問題の詳細と攻撃の仕組み
シリアライズ対象のクラスは、受信側のアプリケーションのクラスパス上にも存在する必要があるので、前述のような攻撃は簡単にはできないと考えるかもしれないが、Javaで内部的に利用されているクラスやCommons Collectionsライブラリに含まれる特定のクラスを使うことで実現が可能である。
以下、報告者によって公開されている攻撃の仕組みについて、詳細に説明する。
Commons Collectionsライブラリにはリフレクションの機能を持つInvokerTransformerクラスがあるが、本クラスを用いて、複数のGadget(メモリに読み込み済みのコード)を連鎖させて、任意のコードを実行するGadget Chainを生成することが可能となる。このような手法はProperty-Oriented-Programingと呼ばれている。
Gadget Chainの例
final String[] execArgs = new String[] { "calc.exe" };
final Transformer[] transformers = new Transformer[] {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[] {
String.class, Class[].class }, new Object[] {
"getRuntime", new Class[0] }),
new InvokerTransformer("invoke", new Class[] {
Object.class, Object[].class }, new Object[] {
null, new Object[0] }),
new InvokerTransformer("exec",
new Class[] { String.class }, execArgs),
new ConstantTransformer(1) };
final ChainedTransformer transformerChain = new ChainedTransformer(transformers);
// 上記transformerChainに対してtransform() を実行すると、Gadget Chain が実行される
Commons Collectionsライブラリは、Java標準のMapを継承したLazyMapというコレクションを提供しているが、LazyMapはInvokerTransformerのインスタンスを保持し、getメソッド内の処理でGadget Chainを実行している。readObjectメソッド内の処理でMapを使っているAnnotationInvocationHandlerクラス(注1)に対し、MapのインスタンスにLazyMapを設定し、getメソッドを呼び出すように細工することで、AnnotationInvocationHandlerのデシリアライズ時にInvokerTransformerを呼び出すことができる。
攻撃者は、前述の仕組みを使用して任意のコマンドを実行するGadget Chainを実行するように細工したうえで、AnnotationInvocationHandlerクラスをシリアライズして送信する。受け取り側でデシリアライズすると、Commons Collectionsライブラリがクラスパス上に存在する環境であれば、攻撃者がGadget Chainで定義した処理が実行され、任意のコード実行が成立してしまう。
なお、公開されている攻撃手法では、InvokerTransformerやLazyMap、AnnotationInvocationHandlerクラスなどが使われているが、他にも任意のコードを実行するGadget Chainが生成可能なクラスが存在し、何らかの方法でデシリアライズ時に呼び出すことが可能な場合は、新しい攻撃手法が公開される可能性も考えられる。
また、本攻撃手法が用いられた場合、デシリアライズの過程で任意のコードが実行されるので、デシリアライズ後にオブジェクトのチェックを行っても、防ぐことができない。攻撃を防ぐためには、デシリアライズ前に意図したクラスであるかを検証する必要があり、これについては連載の次回で説明する。
また、前述した手法で、攻撃用のシリアライズされたデータを簡単に作成できるツールがインターネットに公開されている。攻撃者は、本問題の影響を受けるアプリケーションとの通信をキャプチャリングすることにより、シリアライズされたデータをやりとりしているペイロードを見つけ出し、攻撃用のデータにすり替えることで比較的簡単に攻撃を行える可能性がある。
注1
Java標準のクラスだが、APIとして公開されていない内部的に利用されているクラス。
