初识FastJson
Background
FastJson提供了toJsonString和parseObject方法可用于将一个JavaBean转换为Json数据, 或将Json数据还原为JavaBean对象。
FastJson Version:1.2.24
1 | <!-- pom.xml --> |
Java Bean序列化Json
1 | public class FastJsonLearning { |
输出:
{“age”:19,”name”:”Search?=Null”}
其在序列化对象为Json时会调用Bean对象的get方法(构造方法是实例化对象时调用的, 非序列化所调用):
相反, 在将Json转换为Java对象时也会调用其set方法:
探测FastJson
1 | {"@type":"java.net.InetAddress","val":"dnslog"} |
使用如上Payload当dnslog收到请求时则证明后端使用了FastJson, 为何该Payload能够发起DNS请求。跟随本文从parseObject
开始分析。
com.alibaba.fastjson.JSON#parseObject
接收json数据调用parse方法:
调用parse方法传入json数据和DEFAULT_PARSE_FEATURE(989):
实例化com.alibaba.fastjson.DefaultJSONParser
对象时其会判断JSON数据的第一个是否为{
还是[
, 根据不同结果赋值token, 然后调用DefaultJSONParser#parse
进入case 12的分支实例化JSONObject对象后调用parseObject方法:
在parseObject中的while循环所谓又长又臭(足足400余行代码), 遂只能一行一行琢磨该循环中都做了哪些事。循环的第一行代码便是调用skipWhitespace方法, 在该方法中会判断字符是否为空或换行符等, 因此在JSON数据中加入空格和换行符或许可以规避WAF的检测并正常解析:
1 | // 如下两种形式均可解析发起DNS请求 |
回到循环中继续向下看代码, 如果AllowArbitaryCommas
开启的话(默认开启)并且当前字符为,
号会跳过该字符继续向下解析。因此这部分代码是用于跳过JSON数据中的空格, 换行符, 及逗号的解析。
下表为复现版本中各配置项属性默认值:
属性 | 默认值 |
---|---|
AutoCloseSource | True |
AllowComment | False |
AllowUnQuotedFieldNames | True |
AllowSingleQuotes | True |
InternFieldNames | True |
AllowISO8601DateFormat | False |
AllowArbitraryCommas | True |
UseBigDecimal | True |
IgnoreNotMatch | True |
SortFeidFastMatch | True |
DisableASM | False |
DisableCircularReferenceDetect | False |
InitStringFieldAsEmpty | False |
SupportArrayToBean | False |
OrderedField | False |
DisableSpecialKeyDetect | False |
UseObjectArray | False |
SupportNonPublicField | False |
从代码172行开始就是正式解析JSON数据的部分了, 当前字符为"
时调用scanSymbol
方法获取其双引号中的内容为键, 获得完整的键名后如果其后面的字符不为:
则会抛出异常。
获取到键的值后调用resetStringposition
重置字符串位置为0, 判断key如果是@type
并且DisableSpecialKeyDetect
为关闭情况下, 依旧通过scanSymbol
获取value的值(value是完整类名)。通过TypeUtils.loadClass加载类。
在TypeUtils#loadClass中获取当前线程的类加载器加载类并写入mappings中:
加载完类后nextToken
用于判断当前字符为逗号时跳过逗号继续解析, 接着通过ParserConfig#getDeserializer
方法获取ParserConfig
对象deserializer
属性指定Key的值。
其返回的是一个MiscCodec对象:
最后调用MiscCodec.deserialze(DefaultJSONParser, java.net.InetAddress.class, null)
并return该返回值。对应代码:
1 | thisObj = deserializer.deserialze(this, clazz, fieldName); |
MiscCodec#deserialze
在else分支中判断键的内容为val
后解析其value的内容:
最终在MiscCodec
322行处会调用InetAddress#getByName
发起DNS请求:
总结
- FastJson1.2.24版本默认开启AutoType
- FastJson1.2.24默认会忽略空格 注释符并可以解析十六进制和unicode字符
- FastJson1.2.24可使用白名单的
InetAddress
类进行DNS探测