反序列化漏洞利用链与内存马注入
反序列化漏洞利用链与内存马注入
随着现代应用架构向面向对象编程(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 编码,塞入rememberMeCookie 即可触发 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 代码:
- 通过反射获取当前的
StandardContext。 - 实例化一个自定义的、包含执行命令逻辑的恶意
Filter对象。 - 动态调用
FilterDef和FilterMap,将该 Filter 映射到/*路径。 - 将其注入到当前的 Filter 链首部。
至此,一个极其隐蔽、无文件落地、随 Web 进程共存亡的内存马便注入完成,防守方极难通过传统的文件查杀手段发现。
5. 总结
反序列化漏洞是语言底层机制与业务逻辑交织的产物。在红队实战中,熟练掌握不同框架(Spring, Shiro, ThinkPHP)的 Gadget 链构造,结合 JNDI 注入、Phar 触发以及内存马驻留技术,是实施纵深渗透与隐蔽控制的关键能力。