初识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的内容:

最终在MiscCodec322行处会调用InetAddress#getByName发起DNS请求:

总结
- FastJson1.2.24版本默认开启AutoType
 - FastJson1.2.24默认会忽略空格 注释符并可以解析十六进制和unicode字符
 - FastJson1.2.24可使用白名单的
InetAddress类进行DNS探测