fastjson 与反序列化

前言

这一部分是和parseObject漏洞分开的,可以说是单独的存在,为反序列化服务。

概述

参考:

https://y4tacker.github.io/2023/03/20/year/2023/3/FastJson%E4%B8%8E%E5%8E%9F%E7%94%9F%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/

https://y4tacker.github.io/2023/04/26/year/2023/4/FastJson%E4%B8%8E%E5%8E%9F%E7%94%9F%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96-%E4%BA%8C/

JSONArray#toStringJSONObject#toString可调用对象的getter。

在<= 1.2.48,上述调用getter无限制。

在>= 1.2.49之后,这两个类有了自己的readObject,恢复自身对象时,new 一个安全输入流来readObject。

image-20240301132548701.png

这个输入流实现了resolveClass,在其中调用checkAutoType检查要反序列化的类是否合法。

下面就是针对此处的绕过。

handles

由于黑名单在resolveClass,那么需要找到何时不会调用resolveClass。

大师傅们发现,当反序列化一个引用类型的时候,不会调用resolveClass。

什么是引用类型?

序列化数据前,要先写该数据属于何种类型。若一个数据为对象,则写入TC_OBJECT,后面再跟该对象的数据。若一个数据为引用,则写入TC_REFERENCE

设想,若需要序列化10个相同对象,若每个对象都写入全部数据,则造成很多冗余,此时就该用到引用。

大概作用过程:第一次写入对象A,写入TC_OBJECT+A的数据,将对象A存入handles。第二次写入对象A,在handles里查找到A,说明之前写过了,则写入TC_REFERENCE+A的索引。

当readObject时,遇到TC_REFERENCE,则通过索引查找到实际对象。

这样不会调用resolveClass。

所以绕过思路,准备一个可以装很多对象的容器,先装入恶意对象,再装入JSONArray或JSONObject。其中JSONArray里也有相同的恶意对象。序列化第一个恶意对象时不受限制,序列化第二个恶意对象时,由于读的是TC_REFERENCE,则不调用resolveClass,黑名单不起作用。

exp

public static void bypass() throws Exception{
TemplatesImpl calc = Gadget.getTemplateImpl("calc");
ArrayList<Object> objects = new ArrayList<>();

JSONArray objects1 = new JSONArray();
objects1.add(calc);
BadAttributeValueExpException bd = Gadget.getBadAttributeValueExpException(objects1);
objects.add(calc);
objects.add(bd);
Util.unserialize(Util.serialize(objects));

}

image-20240301140734057.png