Bug #74603 [Opn]: PHP INI Parsing Stack Buffer Overflow Vulnerability
| From: | stas@php.net | Date: | Tue, 20 Jun 2017 05:07:57 +0000 |
| Subject: | Bug #74603 [Opn]: PHP INI Parsing Stack Buffer Overflow Vulnerability | ||
| References: | 1 | Groups: | php.bugs |
| Request: | Send a blank email to php-bugs+get-209599@lists.php.net to get a copy of this message | ||
Edit report at https://bugs.php.net/bug.php?id=74603&edit=1
ID: 74603
Updated by: stas@php.net
Reported by: l dot wei at ntu dot edu dot sg
Summary: PHP INI Parsing Stack Buffer Overflow Vulnerability
Status: Open
Type: Bug
Package: Scripting Engine problem
Operating System: *
PHP Version: 7.1.5
Block user comment: N
Private report: N
New Comment:
Seems to be reproducible only on 32-bit PHP, since i_op* are ints, so it is required that
sizeof(int) == sizeof(long) to reproduce the issue.
Previous Comments:
------------------------------------------------------------------------
[2017-06-20 04:39:58] l dot wei at ntu dot edu dot sg
Hi stas, thanks for the reply.
It is true that php.ini should be protected from tampering, if modified, other issues could arise
too.
However it seems to me that parse_ini_file() and parse_ini_string() are not limited to protected ini
files, as there is no inherent restriction whatsoever that stops these functions from being called
on untrusted external inputs, especially for parse_ini_string(), as a handy PHP API.
So I think maybe this should qualify a security bug ? The implementation bears some assumptions that
ini files/strings should always be protected to the same level as the PHP module, however such
implicit requirements cannot be guaranteed.
------------------------------------------------------------------------
[2017-06-19 23:39:59] stas@php.net
Since .ini files are inherently admin functions, not a security issue.
------------------------------------------------------------------------
[2017-05-28 08:44:45] l dot wei at ntu dot edu dot sg
Affecting Zend, changed package to Scripting Engine. Can also be triggered via ini string (e.g., in
cmdline argument, use -d foor=[bar] with the crasher above).
------------------------------------------------------------------------
[2017-05-17 02:29:46] l dot wei at ntu dot edu dot sg
Description:
------------
A stack buffer overflow exists in the latest stable release of PHP-7.1.5 and PHP-5.6.30 in PHP INI
parsing API, which may accept network / local filesystem input. On malformed inputs, a stack buffer
overflow in zend_ini_do_op() could write 1-byte off a fixed size stack buffer. On installations with
the stack smashing mitigation, this would cause an immediate DoS; upto optimization levels, build
options and stack buffer overflow mitigations, this vulnerability may allow corrupting other local
variables or the frame pointer, potentially allows remotely executing code.
In php-7.1.5/Zend/zend_long.h:
110 #if SIZEOF_ZEND_LONG == 4
111 # define MAX_LENGTH_OF_LONG 11
112 # define LONG_MIN_DIGITS "2147483648"
113 #elif SIZEOF_ZEND_LONG == 8
114 # define MAX_LENGTH_OF_LONG 20
115 # define LONG_MIN_DIGITS "9223372036854775808"
116 #else
117 # error "Unknown SIZEOF_ZEND_LONG"
118 #endif
In php-7.1.5/Zend/zend_ini_parser.c:
123 /* {{{ zend_ini_do_op()
124 */
125 static void zend_ini_do_op(char type, zval *result, zval *op1, zval *op2)
126 {
127 int i_result;
128 int i_op1, i_op2;
129 int str_len;
130 char str_result[MAX_LENGTH_OF_LONG];
131
132 i_op1 = atoi(Z_STRVAL_P(op1));
133 zend_string_free(Z_STR_P(op1));
134 if (op2) {
135 i_op2 = atoi(Z_STRVAL_P(op2));
136 zend_string_free(Z_STR_P(op2));
137 } else {
138 i_op2 = 0;
139 }
140
141 switch (type) {
142 case '|':
143 i_result = i_op1 | i_op2;
144 break;
145 case '&':
146 i_result = i_op1 & i_op2;
147 break;
148 case '^':
149 i_result = i_op1 ^ i_op2;
150 break;
151 case '~':
152 i_result = ~i_op1;
153 break;
154 case '!':
155 i_result = !i_op1;
156 break;
157 default:
158 i_result = 0;
159 break;
160 }
161
162 str_len = zend_sprintf(str_result, "%d", i_result);
163 ZVAL_NEW_STR(result, zend_string_init(str_result, str_len, ZEND_SYSTEM_INI));
164 }
165 /* }}} */
The minimums, "-2147483648" and "-9223372036854775808" are of length 11 and 20
respectively,
the proper definition of str_result[] array would be: str_result[MAX_LENGTH_OF_LONG + 1]. A
crafted ini entry would cause an overflow.
Credit: Wei Lei and Liu Yang of Nanyang Technological University.
Test script:
---------------
$ cat input.ini
0=0&~2000000000
$ cat input.php
<?php
$argc = $_SERVER['argc'];
$argv = $_SERVER['argv'];
$file_loc = dirname(__FILE__)."/".$argv[1];
var_dump(parse_ini_file($file_loc, true, INI_SCANNER_NORMAL));
?>
Expected result:
----------------
No crash.
Actual result:
--------------
$ bin/php input.php input.ini
*** buffer overflow detected ***: bin/php terminated
======= Backtrace: =========
/lib/i386-linux-gnu/libc.so.6(+0x68e4e)[0xb7527e4e]
/lib/i386-linux-gnu/libc.so.6(__fortify_fail+0x6b)[0xb75ba85b]
/lib/i386-linux-gnu/libc.so.6(+0xfa6ea)[0xb75b96ea]
/lib/i386-linux-gnu/libc.so.6(+0xf9e48)[0xb75b8e48]
/lib/i386-linux-gnu/libc.so.6(_IO_default_xsputn+0x8e)[0xb752fc0e]
/lib/i386-linux-gnu/libc.so.6(_IO_vfprintf+0x89b)[0xb7502f3b]
/lib/i386-linux-gnu/libc.so.6(__vsprintf_chk+0xb1)[0xb75b8f01]
/lib/i386-linux-gnu/libc.so.6(__sprintf_chk+0x2f)[0xb75b8e2f]
bin/php[0x82e7aa0]
bin/php[0x82e87d3]
bin/php(zend_parse_ini_file+0x47)[0x82e8b07]
bin/php[0x8255788]
bin/php[0x83631f6]
bin/php(execute_ex+0x22)[0x8353c52]
bin/php(zend_execute+0x13b)[0x83a341b]
bin/php(zend_execute_scripts+0x30)[0x8313010]
bin/php(php_execute_script+0x286)[0x82b3f26]
bin/php[0x83a57de]
bin/php[0x80683b9]
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3)[0xb74d8a83]
bin/php[0x8068444]
======= Memory map: ========
08048000-0888d000 r-xp 00000000 08:01 704181 /home/weilei/php7_gdb/bin/php
0888d000-0888e000 r--p 00844000 08:01 704181 /home/weilei/php7_gdb/bin/php
0888e000-08899000 rw-p 00845000 08:01 704181 /home/weilei/php7_gdb/bin/php
08899000-088b2000 rw-p 00000000 00:00 0
09ae7000-09b9a000 rw-p 00000000 00:00 0 [heap]
b7000000-b7200000 r--p 00000000 08:01 271314 /usr/lib/locale/locale-archive
b7200000-b7400000 rw-p 00000000 00:00 0
b7464000-b7480000 r-xp 00000000 08:01 787579 /lib/i386-linux-gnu/libgcc_s.so.1
b7480000-b7481000 rw-p 0001b000 08:01 787579 /lib/i386-linux-gnu/libgcc_s.so.1
b7496000-b74bf000 rw-p 00000000 00:00 0
b74bf000-b7667000 r-xp 00000000 08:01 787552 /lib/i386-linux-gnu/libc-2.19.so
b7667000-b7669000 r--p 001a8000 08:01 787552 /lib/i386-linux-gnu/libc-2.19.so
b7669000-b766a000 rw-p 001aa000 08:01 787552 /lib/i386-linux-gnu/libc-2.19.so
b766a000-b766d000 rw-p 00000000 00:00 0
b766d000-b7670000 r-xp 00000000 08:01 787569 /lib/i386-linux-gnu/libdl-2.19.so
b7670000-b7671000 r--p 00002000 08:01 787569 /lib/i386-linux-gnu/libdl-2.19.so
b7671000-b7672000 rw-p 00003000 08:01 787569 /lib/i386-linux-gnu/libdl-2.19.so
b7672000-b7673000 rw-p 00000000 00:00 0
b7673000-b76b7000 r-xp 00000000 08:01 787602 /lib/i386-linux-gnu/libm-2.19.so
b76b7000-b76b8000 r--p 00043000 08:01 787602 /lib/i386-linux-gnu/libm-2.19.so
b76b8000-b76b9000 rw-p 00044000 08:01 787602 /lib/i386-linux-gnu/libm-2.19.so
b76b9000-b76cc000 r-xp 00000000 08:01 787678 /lib/i386-linux-gnu/libresolv-2.19.so
b76cc000-b76cd000 ---p 00013000 08:01 787678 /lib/i386-linux-gnu/libresolv-2.19.so
b76cd000-b76ce000 r--p 00013000 08:01 787678 /lib/i386-linux-gnu/libresolv-2.19.so
b76ce000-b76cf000 rw-p 00014000 08:01 787678 /lib/i386-linux-gnu/libresolv-2.19.so
b76cf000-b76e4000 rw-p 00000000 00:00 0
b76e4000-b76e5000 r--s 00000000 08:01 554280 /home/weilei/php7_gdb/input.ini
b76e5000-b76e6000 r--p 00855000 08:01 271314 /usr/lib/locale/locale-archive
b76e6000-b76e8000 rw-p 00000000 00:00 0
b76e8000-b76ea000 r--p 00000000 00:00 0 [vvar]
b76ea000-b76ec000 r-xp 00000000 00:00 0 [vdso]
b76ec000-b770c000 r-xp 00000000 08:01 787528 /lib/i386-linux-gnu/ld-2.19.so
b770c000-b770d000 r--p 0001f000 08:01 787528 /lib/i386-linux-gnu/ld-2.19.so
b770d000-b770e000 rw-p 00020000 08:01 787528 /lib/i386-linux-gnu/ld-2.19.so
bff25000-bff47000 rw-p 00000000 00:00 0 [stack]
Aborted
------------------------------------------------------------------------
--
Edit this bug report at https://bugs.php.net/bug.php?id=74603&edit=1