解析 `.tar/.zip/.phar` 文件时, 堆边界条件控制不严,导致可能`堆溢出`.
新建一个空文件"aaaa"(0 byte), 打包成 "aaaa.tar"文件,未压缩前`aaaa`的文件大小为`0`。通过`PharFileInfo`对象的`getContent()`方法获取`aaaa`文件的内容,例如: `var_dump($phar['aaaa']->getContent());`
查看`getContent`内部实现源码如下:
```php
ext/phar/phar_object.c:
PHP_METHOD(PharFileInfo, getContent)
{
...snip...
	Z_TYPE_P(return_value) = IS_STRING;
	Z_STRLEN_P(return_value) = php_stream_copy_to_mem(fp, &(Z_STRVAL_P(return_value)), link->uncompressed_filesize, 0);
	if (!Z_STRVAL_P(return_value)) {
		Z_STRVAL_P(return_value) = estrndup("", 0);
	}
...
}
```
`aaaa` 文件大小为`0`, 所以传递给`php_stream_copy_to_mem`函数进行处理,如下:
```php
main/streams/streams.c:
PHPAPI size_t _php_stream_copy_to_mem(php_stream *src, char **buf, size_t maxlen, int persistent STREAMS_DC TSRMLS_DC)
{
...snip...
	if (maxlen == 0) {
		return 0;
	}
...
	if (maxlen > 0) {
		ptr = *buf = pemalloc_rel_orig(maxlen + 1, persistent);
		while ((len < maxlen) && !php_stream_eof(src)) {
			ret = php_stream_read(src, ptr, maxlen - len);
...
}
```
从上面代码可以看出, `If maxlen == 0`, 它将返回 0, 这是ok的, 但是该函数的第二个参数`char **buf`, 将在堆上分配一个空间,从当前文件指针处读数据。
现在返回上一层函数进行分析, 变量 zval `return_value` 是未初始化的变量, `return_value->str.val`将是一个指针,指向之前调用函数分配的堆地址所指向的空间, gdb调试如下:
```
=> 0x817aacc <zim_PharFileInfo_getContent+300>: call   0x8267ad0 <_php_stream_copy_to_mem>
   0x817aad1 <zim_PharFileInfo_getContent+305>: mov    ecx,DWORD PTR [esp+0x54]
   0x817aad5 <zim_PharFileInfo_getContent+309>: mov    DWORD PTR [ecx+0x4],eax
   0x817aad8 <zim_PharFileInfo_getContent+312>: mov    eax,DWORD PTR [ecx]
   0x817aada <zim_PharFileInfo_getContent+314>: test   eax,eax
Guessed arguments:
arg[0]: 0xf7bd9a04 --> 0x8806a40 --> 0x826cad0 (<php_stdiop_write>:     push   ebx)
arg[1]: 0xf7bdd4b8 --> 0xf7bdd56c --> 0x1d 
arg[2]: 0x0 
arg[3]: 0x0 
Breakpoint 2, 0x0817aacc in zim_PharFileInfo_getContent (ht=0x0, return_value=0xf7bdd4b8, return_value_ptr=0xf7bbf094, this_ptr=0xf7bdd49c, return_value_used=0x1) at /root/fuzz/php-5.6.17/ext/phar/phar_object.c:4889
4889            Z_STRLEN_P(return_value) = php_stream_copy_to_mem(fp, &(Z_STRVAL_P(return_value)), link->uncompressed_filesize, 0);
```
从上面可以看出当前`return_value->str.val`就是`0xf7bdd56c`.
正如之前所分析的那样,`maxlen==0`,`_php_stream_copy_to_mem`返回0这是ok的, 但是调用该函数之后,参数`*buf` => `return_value->str.val`保留了函数调用过程中的数据,如下:
```
=> 0x817aad1 <zim_PharFileInfo_getContent+305>: mov    ecx,DWORD PTR [esp+0x54]
   0x817aad5 <zim_PharFileInfo_getContent+309>: mov    DWORD PTR [ecx+0x4],eax
   0x817aad8 <zim_PharFileInfo_getContent+312>: mov    eax,DWORD PTR [ecx]
   0x817aada <zim_PharFileInfo_getContent+314>: test   eax,eax
   0x817aadc <zim_PharFileInfo_getContent+316>: jne    0x817aa21 <zim_PharFileInfo_getContent+129>
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x0817aad1      4889            Z_STRLEN_P(return_value) = php_stream_copy_to_mem(fp, &(Z_STRVAL_P(return_value)), link->uncompressed_filesize, 0);
gdb-peda$ x/10wx 0xf7bdd4b8
0xf7bdd4b8:     0xf7bdd56c      0x088086a0      0x00000001      0x00000006
0xf7bdd4c8:     0x00000000      0x00000010      0x0000001d      0x08820b20
0xf7bdd4d8:     0xf7bd97c4      0x00000091
gdb-peda$ print *(zval*)0xf7bdd4b8
$2 = {
  value = {
    lval = 0xf7bdd56c, 
    dval = 1.0010109254636237e-267, 
    str = {
      val = 0xf7bdd56c "\035", 
      len = 0x88086a0
    }, 
    ht = 0xf7bdd56c, 
    obj = {
      handle = 0xf7bdd56c, 
      handlers = 0x88086a0 <spl_filesystem_object_handlers>
    }, 
    ast = 0xf7bdd56c
  }, 
  refcount__gc = 0x1, 
  type = 0x6, 
  is_ref__gc = 0x0
}
```
`0xf7bdd56c` 仍然保留在`ZVAL(return_value)`.
之后, 这个`str.val`指针将被传递到 `_efree` 去清理 vm stack.
以某种方式, 我能管理`0xf7bdd56c`之前的地址,也绝对能管理 `mm_block(ESI register)` 的下一个地址, 如下:
```
zend_alloc.c:
static void _zend_mm_free_int(zend_mm_heap *heap, void *p ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{
...
	next_block = ZEND_MM_BLOCK_AT(mm_block, size);
	if (ZEND_MM_IS_FREE_BLOCK(next_block)) {
		zend_mm_remove_from_free_list(heap, (zend_mm_free_block *) next_block);
		size += ZEND_MM_FREE_BLOCK_SIZE(next_block);
	}
...
```
我们能接管它的下一个地址和进一步控制堆
                       
                       
        
          
暂无评论