
Javaアプリケーションにおけるデシリアライゼーションガジェットチェーンを見つけるためのバイトコードアナライザ
このプロジェクトは、Javaライブラリおよびクラスパス内のガジェットチェーンを検査します。ガジェットチェーンは、デシリアライゼーション脆弱性のエクスプロイトを構築するために使用されます。アプリケーションのクラスパス内で可能なガジェットチェーンを自動的に発見することで、ペネトレーションテスターは迅速にエクスプロイトを構築でき、アプリケーションセキュリティエンジニアはデシリアライゼーション脆弱性の影響を評価し、その修正の優先順位を決定できます。
このプロジェクトはBlack Hat USA 2018で発表されました。詳細はそちらをご覧ください!(リンクは未定)
免責事項: このプロジェクトはアルファ版であり、テストやドキュメントが不足しています。どちらかを追加して、ぜひご協力ください!
システムにJDKがインストールされていることを前提として、./gradlew shadowJar を実行するだけでビルドできます。その後、java -jar build/libs/gadget-inspector-all.jar <引数> でアプリケーションを実行できます。
このアプリケーションは、引数としてwarファイルへのパス(その場合、warが展開され、すべてのクラスとライブラリがクラスパスとして使用されます)または任意の数のjarファイルを受け取ります。
なお、解析はメモリを大量に消費する可能性があります(現時点ではガジェットインスペクタはメモリ消費を抑えるための最適化は一切行われていません)。小さなライブラリの場合でも、少なくとも2GBのヒープサイズ(例:-Xmx2G フラグ)を割り当てることをお勧めします。大規模なアプリケーションでは、可能な限り多くのメモリを使用してください。
このツールキットは、クラスパス検査のいくつかの段階を経て、後続の段階で使用するデータセットを構築します。これらのデータセットは .dat 拡張子のファイルに書き出され、実行後に破棄できます(主に、開発中に初期段階をスキップできるようにするために書き出されます)。
解析が完了すると、gadget-chains.txt ファイルが書き出されます。
以下は、commons-collections-3.2.1.jar に対して実行した例です。例えば、次のコマンドで実行します:
wget http://central.maven.org/maven2/commons-collections/commons-collections/3.2.1/commons-collections-3.2.1.jar
java -Xmx2G -jar build/libs/gadget-inspector-all.jar commons-collections-3.2.1.jar
gadget-chains.txt には次のチェーンがあります:
com/sun/corba/se/spi/orbutil/proxy/CompositeInvocationHandlerImpl.invoke(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object; (-1)
com/sun/corba/se/spi/orbutil/proxy/CompositeInvocationHandlerImpl.invoke(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object; (0)
org/apache/commons/collections/map/DefaultedMap.get(Ljava/lang/Object;)Ljava/lang/Object; (0)
org/apache/commons/collections/functors/InvokerTransformer.transform(Ljava/lang/Object;)Ljava/lang/Object; (0)
java/lang/reflect/Method.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object; (0)
このチェーンのエントリポイントは、JDKの InvocationHandler クラスの実装です。もともとのcommons-collectionsガジェットチェーンと同じテクニックを使用して、このクラスのシリアライズ可能な実装はすべてガジェットチェーンで到達可能なため、発見されたチェーンはここから始まります。このメソッドは classToInvocationHandler.get() を呼び出します。発見されたガジェットチェーンは、classToInvocationHandler が DefaultedMap としてシリアライズ可能であり、その呼び出しが DefaultedMap.get() にジャンプすることを示しています。チェーンの次のステップでは、このメソッドから value.transform() が呼び出されます。このクラスのパラメータ value は InvokerTransformer としてシリアライズ可能です。このクラスの transform メソッド内部では、cls.getMethodName(iMethodName, ...).invoke(...) を呼び出していることがわかります。ガジェットインスペクタは、iMethodName がシリアライズされたメンバーとして攻撃者によって制御可能であると判断し、したがって攻撃者はクラス上で任意のメソッドを実行できます。
このガジェットチェーンは、Frohoffによって発見された完全なcommons-collectionsガジェットチェーンの構成要素です。上記のケースでは、ガジェットインスペクタは AnnotationInvocationHandler と LazyMap ではなく、CompositeInvocationHandlerImpl と DefaultedMap を通じてエントリを発見しましたが、大部分は同じです。
このツールで見つけられるチェーンの種類についてさらに例をお探しの場合は、以下のライブラリにも興味深い結果があります:
ガジェットインスペクタは、完全なアプリケーション(JARまたはWARとしてパッケージ化されたもの)を指定することもできることを忘れないでください。例えば、Zksample2 アプリケーションのwarを解析すると、以下のガジェットチェーンが得られます:
net/sf/jasperreports/charts/design/JRDesignPieDataset.readObject(Ljava/io/ObjectInputStream;)V (1)
org/apache/commons/collections/FastArrayList.add(Ljava/lang/Object;)Z (0)
java/util/ArrayList.clone()Ljava/lang/Object; (0)
org/jfree/data/KeyToGroupMap.clone()Ljava/lang/Object; (0)
org/jfree/data/KeyToGroupMap.clone(Ljava/lang/Object;)Ljava/lang/Object; (0)
java/lang/reflect/Method.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object; (0)
ご覧のように、これはアプリケーションに含まれる複数の異なるライブラリを利用してチェーンを構築しています。
Q: ガジェットインスペクタがガジェットチェーンを発見した場合、それからエクスプロイトを構築できますか?
A: 常にそうとは限りません。この解析ではいくつかの単純化した仮定を使用しており、誤検出(実際には存在しないガジェットチェーン)を報告する可能性があります。簡単な例として、分岐条件の充足可能性を解決しようとはしません。したがって、以下のようなものもガジェットチェーンとして報告されます:
public class MySerializableClass implements Serializable {
public void readObject(ObjectInputStream ois) {
if (false) System.exit(0);
ois.defaultReadObject();
}
}
さらに、ガジェットインスペクタは、注目すべき関数に対してかなり広い条件を持っています。例えば、リフレクション(攻撃者がメソッドを制御できる Method.invoke() への呼び出し)を興味深いものとして扱いますが、しばしば見落とされるアサーションにより、攻撃者は呼び出されるメソッドに影響を与えることはできても、完全な制御はできない場合があります。例えば、攻撃者はどのクラスの "getError()" メソッドを呼び出すことはできても、他のメソッド名までは呼び出せない場合があります。
Q: ガジェットチェーンが見つからなかった場合、アプリケーションはエクスプロイトから安全であることを意味しますか?
A: いいえ! まず、ガジェットインスペクタは「興味深い」副作用があると見なす「シンク」関数のセットが非常に限定されています。これは、リストにない他の興味深いまたは危険な動作が存在しないことを意味するものではありません。
さらに、静的解析には多くの限界があり、ガジェットインスペクタには常に死角が存在します。例えば、ガジェットインスペクタは現在、リフレクション呼び出しを追跡しないため、以下のようなものを見逃します:
public class MySerializableClass implements Serializable {
public void readObject(ObjectInputStream ois) {
System.class.getMethod("exit", int.class).invoke(null, 0);
}
}