反序列化漏洞利用链与内存马注入

反序列化漏洞利用链与内存马注入

随着现代应用架构向面向对象编程(OOP)和微服务化发展,反序列化漏洞(Deserialization Vulnerability) 已成为各大语言(Java、PHP、Python 等)中最复杂、最具毁灭性的漏洞之一。

在渗透测试实战中,反序列化漏洞往往是进入企业核心内网的“核武器”。本文将从底层逻辑出发,深度剖析 Java 与 PHP 反序列化的利用链构造,并探讨与之紧密结合的进阶驻留技术——内存马(Memory WebShell)


1. 序列化与反序列化的底层逻辑

序列化是将内存中的对象(包含属性、状态)转换为字节流或特定格式(如 XML, JSON),以便于网络传输或持久化存储。 反序列化则是将这些字节流还原为内存中的对象。

漏洞成因:当应用程序接收了攻击者控制的恶意序列化数据,并在反序列化过程中,自动调用了某些“魔术方法”(Magic Methods),如果这些魔术方法的执行路径(Gadget Chain)最终指向了危险操作(如命令执行、文件写入),漏洞便被触发。


2. Java 反序列化利用链 (Gadget Chain)

Java 体系中的反序列化漏洞是重灾区,著名的如 Apache Commons Collections (CC链)、Fastjson、Shiro 等。

2.1 经典 Gadget Chain: Commons Collections

利用 ysoserial 生成的 CC 链是红队最常用的武器。

  • 触发点 (Kick-off):反序列化入口,通常是重写了 readObject() 方法的类(如 AnnotationInvocationHandler)。
  • 利用链 (Chain):对象间的连续调用。CC 链巧妙利用了 InvokerTransformer 中的反射机制。
  • 执行点 (Sink):最终的执行位置。InvokerTransformer.transform() 可以通过反射调用任意类的任意方法,最终被指向 Runtime.getRuntime().exec() 实现 RCE。

2.2 JNDI 注入与 Fastjson 绕过

Fastjson 漏洞的核心在于其处理 JSON 字符串时,如果启用了 @type,会自动调用指定类的 setter 或 getter 方法。 JNDI (Java Naming and Directory Interface) 注入

  • 攻击者通过 Fastjson 构造 Payload,将 @type 指定为 com.sun.rowset.JdbcRowSetImpl
  • 设置其 dataSourceName 属性为攻击者控制的 RMI/LDAP 服务地址(如 ldap://hacker.com/Exploit)。
  • 当 Fastjson 调用 setAutoCommit() 触发 JNDI 查询时,目标服务器会去黑客的 LDAP 服务器下载并加载恶意的 Exploit.class,从而执行静态代码块中的恶意命令。

2.3 Shiro 反序列化 (CVE-2016-4437)

Apache Shiro 框架使用 Cookie 中的 rememberMe 字段来记录用户登录状态。

  • 机制:Shiro 对该 Cookie 的处理流程是:Base64解码 -> AES解密 -> Java反序列化
  • 漏洞:Shiro 1.2.4 及之前版本使用了硬编码的默认 AES 密钥
  • 利用:红队只需使用 ysoserial 生成恶意的序列化 Payload,使用该硬编码密钥进行 AES 加密和 Base64 编码,塞入 rememberMe Cookie 即可触发 RCE。

3. PHP 反序列化与 Phar 协议利用

PHP 的反序列化通常围绕 unserialize() 函数和魔术方法(如 __wakeup, __destruct, __toString)展开。

3.1 POP 链构造 (Property Oriented Programming)

与 Java 的 Gadget Chain 类似,PHP 也是通过寻找现有代码中的“跳板类”。

  • 攻击者构造一个恶意的序列化字符串。
  • 触发 __destruct(),该方法内部调用了某个对象的属性。
  • 利用 PHP 的多态性,将该属性替换为另一个类的对象,从而触发 __call__toString
  • 最终层层传递,执行到危险函数(如 eval()file_put_contents())。

3.2 Phar 协议反序列化触发

部分开发者认为只要不使用 unserialize() 就是安全的,但 PHP 的 phar:// 伪协议打破了这个认知。 利用原理

  • Phar 文件的元数据(Metadata)部分是以序列化形式存储的。
  • 当 PHP 的任何文件系统函数(如 file_exists(), file_get_contents(), stat())处理 phar:// 协议时,会自动反序列化其 Metadata。
  • 攻击面:如果存在文件上传漏洞但限制了后缀(如只能传 .jpg),可以伪造一个包含恶意 Metadata 的 Phar 文件,后缀改为 .jpg 上传,再配合文件读取漏洞调用 phar://upload/evil.jpg,即可触发反序列化 RCE。

4. 进阶驻留:内存马 (Memory WebShell) 注入

传统的文件型 WebShell 极易被查杀和告警。在通过反序列化漏洞获取 RCE 后,现代红队的标准操作是直接在内存中注入 WebShell(无文件落地)。

4.1 Java 内存马分类

内存马的核心是动态注册恶意组件拦截 HTTP 请求:

  • Filter 内存马:利用 Tomcat/Spring 的 Context 动态注册一个全局 Filter。由于 Filter 的优先级极高,所有请求都会先经过恶意 Filter 判断是否包含指令。
  • Servlet 内存马:动态注册一个隐蔽的 Servlet 路径。
  • Listener 内存马:利用 ServletRequestListener,在每次请求销毁时触发执行命令。

4.2 注入原理 (以 Tomcat Filter 为例)

在反序列化 RCE 的执行阶段(如利用 CC 链执行代码时),我们不执行系统命令,而是执行一段 Java 代码:

  1. 通过反射获取当前的 StandardContext
  2. 实例化一个自定义的、包含执行命令逻辑的恶意 Filter 对象。
  3. 动态调用 FilterDefFilterMap,将该 Filter 映射到 /* 路径。
  4. 将其注入到当前的 Filter 链首部。

至此,一个极其隐蔽、无文件落地、随 Web 进程共存亡的内存马便注入完成,防守方极难通过传统的文件查杀手段发现。


5. 总结

反序列化漏洞是语言底层机制与业务逻辑交织的产物。在红队实战中,熟练掌握不同框架(Spring, Shiro, ThinkPHP)的 Gadget 链构造,结合 JNDI 注入、Phar 触发以及内存马驻留技术,是实施纵深渗透与隐蔽控制的关键能力。