Internet Bug Bounty · Report #110722
Heap BufferOver Flow in escapeshellargs and escapeshellcmd functions
How it was found
- Bug report: https://bugs.php.net/bug.php?id=71270
- Patch submitted and accepted: https://github.com/php/php-src/commit/2871c70efaaaa0f102557a17c727fd4d5204dd4b
---Description---
1)I found this vulnerability using a custom PHP engine fuzzer that I wrote. There exist a heap-based buffer over flow that allows one to write a user tainted data pass an allocated buffer. This vulnerability lies in the following functions: escapeshellarg escapeshellcmd
- On a default php installation, the memory limit is set to 128MB and this vulnerability is not triggerable. My analysis shows that this is triggerable when memory limit is roughly > 1024mb. A quick search on github shows that it's not uncommon to see code like "ini_set('memory_limit', -1);"
3)I've created a POC that triggers the buffer over write with 0x414141414141.....
- A string of 1024mb is created and passed into escapeshellarg. "l" contains the length of this string:
Breakpoint 2, php_escape_shell_arg (str=0x7fffad469028 'A' <repeats 200 times>...) at /home/elaw/php-7.0.0/ext/standard/exec.c:343 343 int x, y = 0, l = (int)strlen(str);
gdb-peda$ print l $43 = 0x40000000 // 1024mb
- This length "l" is then passed into zend_string_alloc as "4 * l + 2" which results in an integer overflow:
Temporary breakpoint 3, php_escape_shell_arg (str=0x7fffad000018 'A' <repeats 200 times>...) at /home/elaw/php-7.0.1/ext/standard/exec.c:348 348 cmd = zend_string_alloc(4 * l + 2, 0); /* worst case */
gdb-peda$ print 4* l + 2 $44 = 0x2 //Overflow
-
Stepping into zend_string_alloc to verify the integer overflow. Notice len=0x2: zend_string_alloc (persistent=0x0, len=0x2) at /home/elaw/php-7.0.0/Zend/zend_string.h:121
121 zend_string *ret = (zend_string *)pemalloc(ZEND_MM_ALIGNED_SIZE(_ZSTR_STRUCT_SIZE(len)), persistent); -
Lets confirm the overflow again in the allocated (zend_string *) cmd. Notice cmd.len=0x2: gdb-peda$ p *cmd $52 = { gc = { refcount = 0x1, u = { v = { type = 0x6, flags = 0x0, gc_info = 0x0 }, type_info = 0x6 } }, h = 0x0, len = 0x2, val = "1" }
-
The loops then writes pass the allocated buffer in
258 for (x = 0, y = 0; x < l; x++) { .... 321 ZSTR_VAL(cmd)[y++] = str[x];
-
Verifying the buffer overflow in gdb-peda$ p (zend_string *)cmd.len $9 = (zend_string *) 0x2 gdb-peda$ x/100b (zend_string *)cmd.val 0x1625a58: 0x27 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x1625a60: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x1625a68: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x1625a70: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x1625a78: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x1625a80: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x1625a88: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x1625a90: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x1625a98: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x1625aa0: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x1625aa8: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x1625ab0: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x1625ab8: 0x41 0x41 0x41 0x41
-
The vulnerability for php_escape_shell_cmd is identical.