yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

83
メモリ不足からリモートコード実行 Yuki Chen (@guhe120) Qihoo 360Vulcan Team

Upload: pacsecjp

Post on 22-Jan-2018

271 views

Category:

Internet


1 download

TRANSCRIPT

Page 1: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

メモリ不足からリモートコード実行

YukiChen(@guhe120)Qihoo360VulcanTeam

Page 2: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

CVE-2014-0290,CVE-2014-0321,CVE-2014-1753,CVE-2014-1769,CVE-2014-1782,CVE-2014-1804,CVE-2014-2768,CVE-2014-2802,CVE-2014-2803,CVE-2014-2824,CVE-2014-4057,CVE-2014-4092,CVE-2014-4091,CVE-2014-4095,CVE-2014-4096,CVE-2014-4097,CVE-2014-4082,CVE-2014-4105,CVE-2014-4129,CVE-2014-6369,CVE-2015-0029,CVE-2015-1745,CVE-2015-1743,CVE-2015-3134,CVE-2015-3135,CVE-2015-4431,CVE-2015-5552,CVE-2015-5553,CVE-2015-5559,CVE-2015-6682,CVE-2015-7635,CVE-2015-7636,CVE-2015-7637,CVE-2015-7638,CVE-2015-7639,CVE-2015-7640,CVE-2015-7641,CVE-2015-7642,CVE-2015-7643,CVE-2015-8454,CVE-2015-8059,CVE-2015-8058,CVE-2015-8055,CVE-2015-8057,CVE-2015-8056,CVE-2015-8061,CVE-2015-8067,CVE-2015-8066,CVE-2015-8062,CVE-2015-8068,CVE-2015-8064,CVE-2015-8065,CVE-2015-8063,CVE-2015-8405,CVE-2015-8404,CVE-2015-8402,CVE-2015-8403,CVE-2015-8071,CVE-2015-8401,CVE-2015-8406,CVE-2015-8069,CVE-2015-8070,CVE-2015-8440,CVE-2015-8409,CVE-2015-8047,CVE-2015-8455,CVE-2015-8045,CVE-2015-8441,CVE-2016-0980,CVE-2016-1015,CVE-2016-1016,CVE-2016-1017,CVE-2016-4120,CVE-2016-4160,CVE-2016-4161,CVE-2016-4162,CVE-2016-4163,CVE-2016-4185CVE-2016-4249,CVE-2016-4180,CVE-2016-4181,CVE-2016-4183,CVE-2016-4184,CVE-2016-4185,CVE-2016-4186,CVE-2016-4187,CVE-2016-4233,CVE-2016-4234,CVE-2016-4235,CVE-2016-4236,CVE-2016-4237,CVE-2016-4238,CVE-2016-4239,CVE-2016-4240,CVE-2016-4241,CVE-2016-4242,CVE-2016-4243,CVE-2016-4244,CVE-2016-4245,CVE-2016-4246,CVE-2016-4182,CVE-2016-3375,CVE-2017-3001,CVE-2017-3002,CVE-2017-3003,CVE-2017-0238,CVE-2017-0236,CVE-2017-8549,CVE-2017-8619

自己紹介バウンティハンター@360VulcanTeam

Page 3: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

自己紹介

ハードコアなアニメ漫画ゲーム        オタク

Page 4: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

360VulcanTeamについて

ü  Qihu360の   セキュリティリサーチャー

ü  Pwn2Ownの実績:ü  Pwn2Own2015IE11ü  Pwn2Own2016Google

Chrome,AdobeFlashü  Pwn2Own2017Edge,Safari,

AdobeFlash,Win10,MacOSXü  “MasterofPwn”Pwn2Own

2017

Page 5: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

アジェンダ

•  ブラウザのメモリ不足(OOM)例外•  メモリ不足からリモートコード実行(RCE)•  メモリ不足からASLRのバイパス•  まとめ

Page 6: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

Webブラウザのメモリ不足(OOM)例外

Page 7: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

メモリ不足(Outofmemory)例外“十分なメモリが無い場合に発生する

ランタイム例外” �

Page 8: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

ブラウザOOMの例1•  IE8CAZrArrayの解放済みメモリの使用(use-a[er-free)(cve-2014-1753)•  ファジングにより発見

function f() { var size = 0x8000000; var s = "AAAA"; while (s.length < (size - 2)/2) s += s;

var x = document.createElement("frame"); x.setAttribute("prop2", s.substring(0, (size - 2)/2)); var node = document.body.appendChild(x);

for ( var y = 0; y < 20; ++ y ) { var z = node.cloneNode(); document.body.appendChild(z); }

}

Page 9: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

CloneNode

属性をひとつずつ複製

新しい属性配列の作成

属性複製失敗? �

属性配列の解放

属性配列にアクセス(UAF:use-a[er-free)

コピーする属性が非常に大きい文字列である場合、その属性を複製するとメモリ不足を発生させることができる。最終的に、解放済みメモリの使用(use-a+er-free)が発生する �

Page 10: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

ブラウザOOMの例2•  IEJscirptJSON.parseOOMメモリ破損

var chunksize = 0x2000000; var json = '[' for ( var i = 0; i < chunksize/0x10; ++ i ) json += '1,' json += '1]'; var arr = new Array(); try {

for ( var i = 0; i < 0x1000; ++ i ) arr.push(json.substr(0, (chunksize-2)/2));

} catch (e) {} //大量のメモリを割り当てることにより  IEを低メモリ状態に強制する

while (true) { JSON.parse(json); } �

Page 11: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

jscript!JSONParser::ParseObject+0x3fb: 65b4e9da mov eax,dword ptr [esi+14h] // length of the json array 65b4e9dd mov dword ptr [esp+14h],eax 65b4e9e1 shl eax,4 // alloc size = arr_length * 0x10   65b4e9e4 push eax 65b4e9e5 mov dword ptr [esp+20h],eax 65b4e9e9 call dword ptr [jscript!_imp__malloc (65b740fc)] // もしメモリが十分でない場合、mallocを失敗できる// mallocの失敗をチェックしていないかコンテンツを直接アドレスにコピーする                                          [NULL + arr_size] 65b4ea2fa5movsdwordptres:[edi],dwordptr[esi]es:002b:00777fc0=????????ds:002b:03eb8398=00000003

Page 12: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

OOMバグ –異なるタイプ

•  対処済み/未対処•  制御可能/制御不可能•  32ビット/64ビット•  継続可能/継続不可能

Page 13: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

対処済み/対処無し

•  対処されたOOM– 開発者はコード内にOMMの可能性を意識している– しかし正しく対処されていない(例えば、IECAZrArrayUAFのケース)

•  対処されていないOOM– 開発者はコード内にOMMの可能性を意識しておらず

ノーアイデア(例えば、JSONのケース)– 予期しない実行パスの変更(アーリーリターン、例外)

を引き起こし、悪用可能な状態を引き起こすことができる

Page 14: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

制御可能/制御不可能

•  制御可能– いつでもOOMの例外を確実に発生できる場合

•  大きな割り当て(largeallocadon)の制御•  低メモリ(low-memory)状態の制御

•  制御不可能– ランダムに発生するため制御できない場合

•  小さな割り当て(Smallallocadons)•  制御できない低メモリ(low-memory)状態

Page 15: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

32ビット/64ビット

•  通常、信頼性の高いOOMを見つけるのは64ビットよりも32ビットをターゲットにする方が簡単

•  32ビットのターゲットに対して、プロセスを強制的に低メモリ状態にする方が簡単だから– ブルートフォースアロケーションを使う

Page 16: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

継続可能/継続不可能

•  継続可能–  プログラムがOOMの後も実行を継続できる場合–  エクスプロイト可能

•  継続不可能–  プログラムがOOMの後に実行を継続できない場合

•  悪用できないメモリ破損のために即クラッシュしてしまう(例えば、nullポインタデリファレンス)

•  失敗しない割り当てでもクラッシュしてしまう•  ブラウザのメモリ制限により、メモリの制限を超えた場合にクラッ

シュしてしまう

–  エクスプロイトできない、DOSのみ L

Page 17: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

制御可能または継続可能なOOM例外のみ見つける/フォーカスすること

バグハンターへ:

Page 18: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

OOMのバグの探し方

•  普通のファジング– ランダム値によるファジングで時々OOMが発生する

ことがある

•  低メモリ状態のファジング– ApplicadonVerifierのようなツール– アロケーションAPIのフック– 一部のブラウザにはメモリ不足のシミュレーション用

のテストインターフェースがある(例えば、FireFox)

•  コードの監査

Page 19: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

メモリ不足(OOM)からリモートコード実行(RCE)

Page 20: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

Microso[EdgeのOOMバグを発見するまで

•  Edgeの制御可能なOOMを見つける•  不整合な配列の状態を作るためにトランザク

ション処理をブレイクする•  メモリ破損状態を作り出す•  成功

Page 21: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

Edgeの制御可能なOOMを見つける

•  OOM例外を確実に発生させる必要がある

•  JavaScript配列セグメントの割り当ては良い着眼点

Page 22: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

配列セグメント

•  IE/EdgeのJavaScript配列– 配列セグメントのリンクリスト

長さ

ヘッド

配列

ヘッドセグメント

レフト(le[)

長さ(length)

サイズ(size)

次の位置(next)

要素(element)[0]

要素(element)[N]

セグメント1

レフト(le[)

長さ(length)

サイズ(size)

次の位置(next)

要素(element)[0]

要素(element)[N]

セグメントN…

Page 23: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

vararr=newArray();arr[0]=1;arr[1]=2;arr[2000]=3;arr[5000]=4;

ヘッドセグメント

arr

le#=0length=2element[0]=1element[1]=2

セグメント1

セグメント2

le#=2000length=1element[0]=3

le#=5000length=1element[0]=4

Page 24: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

配列セグメント(.cont)

•  JavaScript配列の要素はセグメントに格納される– 配列セグメントを割り当てるときに、配列内の要

素にメモリ空間が割り当てられる

•  新しい要素を配列に追加するときに既存の領域が無い場合は、新しいセグメントを割り当てるか既存のセグメントを拡大する

Page 25: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

32ビット版Edgedでの配列セグメントの割り当てによるOOM

•  配列セグメントを割り当てるとき、十分なメモリが無い場合は、OOM例外をスローする

Page 26: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

try{for(vari=0x10000000;i<0x18000000;++i)

a[i]=0x0d0d0d0d;}catch(e){}try{

while(true){arr_ab.push(newArrayBuffer(0x02c9dbec*4));//Step1:大容量

のメモリを割り当てることにより、ブラウザを低メモリ状態にする}

}catch(e){}try{

a.reverse();//Step2:配列セグメントの割り当てによって OMM例外をスローする

}catch(e){alert(e);}

32ビット版EdgeでのOOMの発生–例

Page 27: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

32ビット版EdgeでのOOMの発生–例(.cont)

Page 28: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

64ビット版Edgeでの配列セグメントの割り当てによるOOM

•  64ビット版Edgeを強制的にメモリ不足にすることはできない– 64ビットプロセスのメモリ空間が大きいため

•  64ビット版EdgeでOOM例外を発生させるチャンスはまだ残っている– もしアロケーションサイズがオーバーフローした

場合

Page 29: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

アロケーションサイズオーバーフロー時のOOM

Page 30: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

Varaaa=newArray();For(varI=0;I<arrSize.length;++i){

aaa.unshi[.apply(aaa,args);//Step1:大体オーバーフローするまで配列セグメントのサイズを大きくする

aaa[arrSize[i]–1]=1;} try{

aaa.unshi[.apply(aaa,args);//Step2:サイズがオーバーフローしそうな場所で別のセグメントサイズを増やす}catch(e){//Error:OutofMemory}

64ビット版EdgeでのOOMの発生–例

Page 31: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

64ビット版EdgeでのOOMの発生–例(.cont)

Page 32: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

これで32ビット/64ビット版のEdgeで制御可能なOOMができた。次は?

Page 33: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

配列トランザクションの操作

•  配列にはいくつかのインポートフィールドがある– 配列:配列タイプ、長さ、ヘッド、キャッシュされた最後

に使われたセグメント– セグメント:レフト、長さ、サイズ、要素

•  フィールドの1つを変更する場合、配列を有効に保つため他のフィールドも変更する必要がある– 例えば、int配列をfloat配列に変換するとき、セグメン

トのint要素もfloatに変更する必要がある

Page 34: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

配列トランザクション操作(.cont)

•  多くのJavaScript配列のAPIは配列を変更する– shi[、unshi[、splice…– そのようなコードのコア部分では原子性と一貫性

が求められる– データベースのトランザクションと同じように、配

列トランザクション操作と呼んでいる– 配列トランザクション操作の中断は問題を引き起

こすことができる

Page 35: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

配列トランザクション操作–例

•  NadveInt配列からNadveFloat配列への変換

配列内のForeachセグメント:segment->ChangeElementsToFloat()

SetArrayType(NaCveFloatArray)

もし繰り返しの途中でコードが予期せず返ってきた場合、いくつかのFloatセグメントを持つNaHveIntArrayを取得する

Page 36: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

配列トランザクション操作の中断

•  トランザクション操作のコールバック– Edgeのバグの共通パターン

•  コードのフローを中断する例外– メモリ不足(OOM)例外J

Page 37: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

レッツ パーティ!

Page 38: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

リモートコード実行のケース:Array.unshi[

nElemntsToUnshiG=UnshiGする要素の数配列のForeachセグメント:

segment->leG+=nElemntsToUnshiG (1)SetUnshiGElementsToHeadSegment(); (2)Array->length+=nElemntsToUnshiG; (3)

Array.unshi[のトランザクション操作

Page 39: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

nElemntsToUnshiG=UnshiGする要素の数配列のForeachセグメント:

segment->leG+=nElemntsToUnshiG (1)SetUnshiGElementsToHeadSegment(); (2)Array->length+=nElemntsToUnshiG; (3)

中断�

要素をヘッドセグメントに設定すると、ヘッドセグメントの再割り当てが発生し、メモリ不足例外をスローすることができる �

Page 40: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

リモートコード実行のケース:Array.unshi[

•  Array.unshi[トランザクション操作を中断することで– 一貫性の無い配列ができる:

array->lastSegment->le[+array->lastSegment->length

>array->length

– 配列の操作コードと思い込ませる:array->lastSegment->le[+array->lastSegment->length

<=array->length

Page 41: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

リモートコード実行Case:Array.unshi[

•  一貫性のない配列に対してArray.unshi[を再度呼び出す:arr->lastSegment->length>arr->lastSegment->size

•  長さがサイズよりも大きい配列セグメントは、さまざまな場所でヒープオーバーフローを引き起こすことができる–  JavaScriptArray::DirectSetElementAtとか

Page 42: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

リモートコード実行のケース:Array.unshi[

•  配列セグメントのヒープオーバーフローは比較的簡単にエクスプロイトできる

•  オーバーフローされるセグメントの後に別の配列セグメントを割り当てるだけで、次のセグメントの長さとサイズを上書きすることができる

レフト 長さ サイズ

オーバーフロー

0xffffffff 0xffffffff

オーバーフローされるセグメント

次のセグメント

Page 43: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

リモートコード実行のケース:Array.unshi[

•  Pwn2Own2017でEdgeを攻略するために使用•  CVE-2017-0238として修正

デモ

Page 44: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

リモートコード実行のケース:JavascriptNadveIntArray::ToNadveFloatArray

•  この関数は、int配列をfloat配列に変換する

配列のForeachセグメント:seg->size>>=1 (1)if(seg->length>(seg->size>>=1)) seg=AllocateNewSegment (2) Seg->ChangeElementToFloat() (3)Adjust(seg->length) (4)

JavascriptNadveIntArray::ToNadveFloatArrayのトランザクション操作

Page 45: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

配列のForeachセグメント:seg->size>>=1 (1)if(seg->length>(seg->size>>=1)) seg=AllocateNewSegment (2) Seg->ChangeElementToFloat() (3)Adjust(seg->length) (4)

中断�

新しい配列セグメントを割り当てると、メモリ不足例外(OOM)をスローすることができる。この時点で、seg->sizeは2で除算されるが、seg->lengthは変更されない �

Page 46: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

•  ToNadveFloatArrayトランザクション操作を中断することで–  配列セグメントができるSeg->length>Seg->size

•  Array.unshi[と同じような方法でリモートコード実行が

できる–  2017年3月のPwn2Own2017に挑んだときに予備として

持っていたバグの一つ

–  我々はバックアップとして複数の同じようなバグを準備していた(例えば、 Array.splice)

リモートコード実行のケース:JavascriptNadveIntArray::ToNadveFloatArray

Page 47: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

パッチの時間–4月修正

•  このPwn2OwnのバグをMicroso[は4月に修正

•  この修正に少し驚いた– OOMの例外は修正されていなかった– 代わりに我々が使ったエクスプロイトテクニックを

防ごうとしていた•  新しい関数“CheckLengthVsSize”の追加•  “segment->length>segment->size”によって引き起こさ

れるヒープオーバーフローを回避するため

Page 48: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

CheckLengthVsSizeもし“segment->length>segment->size”を検出した場合、すぐにプロセスをクラッシュさせる

Page 49: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

この4月の修正の問題

•  これにより我々のいくつかのOOMのエクスプロイトを防ぐことができる

•  しかし根本原因はまだ修正されていない– 根本原因:OOM例外は配列トランザクション操作

を中断させる

•  また、他のOOMの脆弱性は、 “seg->length>seg->size”を悪用する必要が無い

Page 50: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

パーティーを続けよう

OOMのバグであと10か月は戦える

OOMバグはあと10ヶ月は戦える!

Page 51: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

リモートコード実行のケース:Array.reverseセグメント

解放済みメモリの使用(UseA[erFree)

セグメントリスト全体を反転させる (1)ヘッドがリーフセグメントであれば:

head=ReallocateNonLeafSegment (2)

Array.reverseのトランザクション操作

Page 52: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

リーフセグメント

•  リーフセグメント– 純粋なデータセグメント– GCの次のセグメントはスキャンされない

•  非リーフセグメント– GCの次のセグメントはスキャンされる

非リーフ 非リーフ リーフ

リーフ 非リーフ 非リーフ

ü

X最後の2つのセグメントはGCによってスキャンされずに予期せず解放されて、usea[erfreeの原因となる

Page 53: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

リモートコード実行のケース:Array.reverseセグメント

解放済みメモリの使用(UseA[erFree)

非リーフ

Step1:セグメントリストを逆転する

Step2:リーフセグメントの場合はヘッドを再割り当て

非リーフ リーフ

リーフ 非リーフ 非リーフ

非リーフ 非リーフ 非リーフ

中断�

非リーフセグメントを再割り当てすると、メモリ不足(OOM)の例外を引き起こす

Page 54: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

リモートコード実行のケース:Array.reverseセグメント

解放済みメモリの使用(UseA[erFree)•  Array.reverseのトランザクション操作の中断に

より、 すでに解放されている配列セグメントにアクセスできる

•  完全なリモートコード実行が簡単にできる– 配列セグメントの解放されたメモリを再利用– 偽の配列セグメントを取得(OOMへのアクセスの

実現、型の取り違え、…)

Page 55: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

リモートコード実行のケース:Array.reverseセグメント

解放済みメモリの使用(UseA[erFree)

•  6月のCPUでCVE-2017-8549として修正

•  Edgeのバグバウンティで$15,000ゲット– Microso[に感謝 J

Page 56: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

リモートコード実行のケース:ConvertToVarArray

バッファオーバーフロー•  JavascriptNadveFloatArray::ConvertToVarArrayのバッファオーバーフロー

配列のForeachセグメント:segがリーフセグメントであれば: seg=ReallocateNonLeafSegment()(1)

seg->size*=2 (2)seg>ChangeIntElementsToVar() (3)

Array->ChangeTypeToVarArray() (4)

Page 57: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

Seg->size*=2?

•  32ビット版Edge、sizeof(Var)=4,sizeof(Float)=8•  したがって、floatセグメントをvarセグメントに変換

する場合、セグメントの容量(サイズ)を2倍にすることができる

Float Float

var var var var

Page 58: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

配列のForeachセグメント:segがリーフセグメントであれば: seg=ReallocateNonLeafSegment()(1)

seg->size*=2 (2)seg>ChangeIntElementsToVar() (3)

Array->ChangeTypeToVarArray() (4)

中断�

非リーフセグメントを再割り当てすると、メモリ不足の例外(OOM)が発生することがある。繰り返しの途中(1)で壊れた場合、一部のセグメントのサイズは倍になっているが、倍になったサイズは元に戻されない �

Page 59: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

リモートコード実行の場合:ConvertToVarArrayの型の取り違え

•  JavascriptNadveFloatArray::ConvertToVarArrayのトランザクション操作の中断により– いくつかの倍のサイズのセグメントを持つfloatの

配列ができる

•  セグメントの境界を直接読み書きできる– あとはエクスプロイトを完成させるだけ

Page 60: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

パッチの時間(再び)

•  ようやくMicroso[は根本原因の修正を開始– おそらく、我々がPwn2Ownの後もOOMのバグを

報告し続けたからだろう

•  修正内容– 特定の関数でOOMの例外を検出した場合にプロ

セスをクラッシュさせる

Page 61: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

AutoDisableInterrupt•  コード領域を保護するためのクラス•  保護されたコード領域で例外をスローされる

とプロセスをクラッシュさせる•  根本原因は解決– unshi[、splice、配列変換などの多くのインポート

機能が追加された…– たぶん、いくつかの関数を忘れてる?

Page 62: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

リモートコード実行のケース:Array.reverse(再び)

•  AutoDisableInterruptのパッチの後も– Array.reverseは保護されなかった

•  CVE-2017-8619でOOMセグメントUAFの問題は修正されたと報告

•  その後、別のOOMの問題を同じ関数で発見– CVE-2017-8753– 無効なlastUsedSegmentによって型の取り違えを

引き起こされる

Page 63: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

lastUsedSegment

•  JavaScript配列は最後に使用された配列セグメントをキャッシュし、配列アクセスのスピードアップをしている

•  したがって、セグメントが配列から削除されると、lastUsedSegmentも更新される必要がある。そうでない場合、問題が引き起こされる

Page 64: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

リモートコード実行のケース:Array.reverse(再び)

lastUsedSegment=head; (1)head=AllocateNewHead(); (2)最後のセグメントがリーフでない場合:

ReallocateLastSegmentToNonLeaf();(3)lastUsedSegment=head; (4)

中断�

非リーフセグメントを再割り当てすると、メモリ不足(OOM)の例外が発生することがある。もし(3)で中断した場合、lastUsedSegmentは無効なセグメントを指している �

Page 65: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

無効なlastUsedSegmentの悪用

•  このようなバグの悪用方法について我々のチームメンバ@LiuLongに感謝

•  悪用方法– 配列の型を変更(例えば、int配列->float配列)– 配列の型の変更ではlastUsedSegmentは更新さ

れない– その後でlastUsedSegmentの要素にアクセスする

と、型の取り違えができる

Page 66: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

まだ終わらない…

•  CVE-2017-8753が9月に修正•  EdgeのOOMの悪用は終わり?•  さあ確認してみようJ•  たぶん、デモの時間

Page 67: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

メモリ不足(OOM)からASLRのバイパス

Page 68: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

64ビット版Edgeのメモリを使い切る?

•  通常はできない– メモリを使い切る前にブラウザ(およびシステム

全体)が遅くなったりフリーズする– あまりにも多くのメモリをコミットしてしまうため

•  64ビットブラウザの興味深い機能が見つからない限り

Page 69: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

高速配列バッファ

•  64ビット版Edgeでは、サイズが0x10000(64KB)より大きい配列バッファを割り当てると、それは“高速配列バッファ(fastarraybuffer)”となる– ab=newArrayBuffer(0x10000);//仮想の配列

バッファを作成

•  Edgeは高速配列バッファごとに0x100000000(4GB)バイトを予約する

Page 70: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

バッファごとに4GB?

Page 71: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

どういう意味か

•  64KBの高速配列バッファが割り当てられたとき、実際にコミットされるメモリサイズは64KB

•  しかし、Edgeは4GBの仮想メモリ空間を確保する

•  したがって、64KBのメモリをコミットするだけで4GBのメモリを占有することができる

Page 72: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

64KBのコミット、4GBの確保

Page 73: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

64ビット版Edgeのユーザーモードのメモリ空間

•  Windows10はユーザーモードのメモリとして48ビットを使用

•  ユーザーヒープアドレスは常に0x800000000000以下

•  したがって、ユーザーモードのメモリ空間を使い切るには、0x8000未満の配列バッファをスプレーする必要がある

Page 74: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

64ビット版Edgeのメモリを使い切るための5行

vararr=newArray(0x10000/2);try{ for(i=0;i<arr.length;i++) arr[i]=newArrayBuffer(0x10000);}catch(e){//outofmemoryexcepdon}

Page 75: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

スプレーの最後に

rcx=0000000800000000rcx=0000000700000000rcx=0000000600000000rcx=0000000500000000rcx=0000000400000000rcx=0000000300000000rcx=0000000200000000rcx=0000000100000000

驚いたことに、ほとんどの配列バッファのスプレーが終わると、予測可能なアドレスにメモリを割り当てはじめる

Page 76: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

固定のアドレスで固定のコンテンツを取得

•  スプレーが終了した後、高速配列バッファの1つが固定のアドレス(例えば、0x200000000)に割り当てられる

•  その配列バッファを解放して、興味深いオブジェクト(例えば、JavaScript配列)をスプレーすると、これらのオブジェクトがその固定のアドレスに割り当てられることが分かり、我々はASLRをバイパスすることができた

Page 77: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

0x10000000

0x20000000

0x30000000

高速配列バッファ

高速配列バッファ

高速配列バッファ

高速配列バッファ

高速配列バッファ

0

オブジェクトのスプレー

Page 78: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

デモ

Page 79: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

効果

•  64ビット版Edgeで、制御できるコンテンツを制御できるアドレスに配置できる

•  特定のバグの悪用を容易にする– 任意の場所への書き込み(Write-to-anyonce)– 解放済みメモリの使用(Usea[erfree)– 型の取り違え(Typeconfusion)

Page 80: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

制限

•  スプレー終了までに時間がかかる– ~300秒、私のラップトップでは

•  実際の攻撃には適していない•  Pwn2Ownのようなコンテストで使うには良い

オプションJ

Page 81: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

ASLRのバイパスの先に

•  64ビットのメモリ空間を使い切った後に、32ビットプロセスと同様に制御可能なOOMができる

•  32ビットプロセスのみで悪用可能なOOMの脆弱性は、この問題と組み合わせると64ビットでも悪用できる

Page 82: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

まとめ

•  ブラウザのメモリ不足(Out-of-Memory)の例外は、よく 開発者/バグハンターから無視されていた

•  最新のブラウザで制御可能なものに焦点を当てれば、悪用可能なものを見つけることはまだ可能

•  OOMの問題を真剣に受け止める必要がある

Page 83: Yuki chen from_out_of_memory_to_remote_code_execution_pac_sec2017_final-j

ありがとうございました!