安全矩阵

 找回密码
 立即注册
搜索
查看: 2893|回复: 0

java安全 fastjson反序列化基础分析

[复制链接]

181

主题

182

帖子

721

积分

高级会员

Rank: 4

积分
721
发表于 2022-4-5 11:02:59 | 显示全部楼层 |阅读模式
本帖最后由 wangqiang 于 2022-4-7 10:54 编辑

java安全 fastjson反序列化基础分析
原创 Met32 moonsec
2022-04-05 10:00
转载自https://mp.weixin.qq.com/s?__biz=MzAwMjc0NTEzMw==&mid=2653578464&idx=1&sn=d51a2c7134c962df03f26e4b44c70671&chksm=811b72a2b66cfbb42551ae60f1466d3fa61147606b7b1d0b679f97a68c9e18534a836fc197ac&mpshare=1&scene=23&srcid=0405lThrYE7jLFnFYCLZR0vk&sharer_sharetime=1649124437825&sharer_shareid=ee83a55e0b955b99e8343acbb61916b7#rd

fastjson反序列化基础分析

本文主要来讲解fastjson反序列化内容。
依赖包如下:
  1. <dependency><groupId>com.alibaba</groupId>
  2. <artifactId>fastjson</artifactId>
  3. <version>1.2.24</version>
  4. </dependency>
复制代码

在学习该漏洞之前,先整点前置知识。贴出一个User类。主要来发现fastjson的几个小特性​​​​​​​

  1. <font color="#000000"><font style="background-color:white">public class User {</font></font>
  2. <font color="#000000"><font style="background-color:white">
  3. </font></font><font color="#000000"><font style="background-color:white">public String name;</font></font>
  4. <font color="#000000"><font style="background-color:white">public User() {</font></font>
  5. <font color="#000000"><font style="background-color:white">
  6. </font></font><font color="#000000"><font style="background-color:white">System.out.println("构造函数...");</font></font>
  7. <font color="#000000"><font style="background-color:white">}</font></font>
  8. <font color="#000000"><font style="background-color:white">public String getName() {</font></font>
  9. <font color="#000000"><font style="background-color:white">
  10. </font></font><font color="#000000"><font style="background-color:white">System.out.println("调用了get方法");</font></font>
  11. <font color="#000000"><font style="background-color:white">return name;</font></font>
  12. <font color="#000000"><font style="background-color:white">}</font></font>
  13. <font color="#000000"><font style="background-color:white">
  14. </font></font><font color="#000000"><font style="background-color:white">public void setName(String name) {</font></font>
  15. <font color="#000000"><font style="background-color:white">System.out.println("调用了set方法");</font></font>
  16. <font color="#000000"><font style="background-color:white">this.name = name;</font></font>
  17. <font color="#000000"><font style="background-color:white">
  18. </font></font><font color="#000000"><font style="background-color:white">}</font></font>
  19. <font color="#000000"><font style="background-color:white">@Override</font></font>
  20. <font color="#000000"><font style="background-color:white">public String toString() {</font></font>
  21. <font color="#000000"><font style="background-color:white">return "User{" +</font></font>
  22. <font color="#000000"><font style="background-color:white">"name='" + name + '\'' +</font></font>
  23. <font color="#000000"><font style="background-color:white">'}';</font></font>
  24. <font color="#000000"><font style="background-color:white">}</font></font>
  25. <font color="#000000"><font style="background-color:white">}</font></font>
复制代码

在进行toJSONString()的时候,可以发现会调用到get方法。(切记 构造函数和set方法是初始化和赋值时调用)

Object obj = JSON.parseObject(res,User.class);//返回JSONObject 如果加上User.class则是
User类型
​​​​​​​
  1. Object obj2 = JSON.parse(res);//返回类 User
  2. System.out.println(obj.getClass().getName());
  3. System.out.println(obj2.getClass().getName());
复制代码


可以看到在使用parseObject进行反序列化之后,会调用到set方法。而parse没有调用


在了解完基础用法之后,我从网上找了一段解释的比较专业的一段话:
为了让开发者更加方便的使用Fastjson的一系列功能,同时也为了方便自省,Fastjson设计了一个叫autoType的功能,也就是网上常⻅的 @type 。
只要JSON字符串中包含 @type ,那么 @type 后的属性,就会被当做是 @type 所指定的类的属性,从而在不传递第二个参数的情况下让Fastjson明确要还原的类。

这是什么意思呢?相信很多小伙伴们复现过fastjson这个漏洞,而下方这条代码,是不是非常像你用到的
“Payload”。用通俗的话来理解,这是一个用来被反序列化的东西,@type后面指定的是类名。
cmd指的是该类的属性,calc为属性的值。

  1. {"@type":"com.example.fastjson.Evil","cmd":"calc"}";<img width="15" _height="15" src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" border="0" alt="">
复制代码


既然有了前面的知识铺垫,接下来准备一个恶意类,该类的set方法会传递参数进去,并通过
Runtime执行

  1. <font color="#000000"><font style="background-color:white">public class Evil {</font></font>
  2. <font color="#000000"><font style="background-color:white">
  3. </font></font><font color="#000000"><font style="background-color:white">String cmd;</font></font>
  4. <font color="#000000"><font style="background-color:white">String a;</font></font>
  5. <font color="#000000"><font style="background-color:white">
  6. </font></font><font color="#000000"><font style="background-color:white">public Evil(){}</font></font>
  7. <font color="#000000"><font style="background-color:white">
  8. </font></font><font color="#000000"><font style="background-color:white">public String getCmd() {</font></font>
  9. <font color="#000000"><font style="background-color:white">
  10. </font></font><font color="#000000"><font style="background-color:white">System.out.println("get方法");</font></font>
  11. <font color="#000000"><font style="background-color:white">
  12. </font></font><font color="#000000"><font style="background-color:white">return cmd;</font></font>
  13. <font color="#000000"><font style="background-color:white">
  14. </font></font><font color="#000000"><font style="background-color:white">}</font></font>
  15. <font color="#000000"><font style="background-color:white">
  16. </font></font><font color="#000000"><font style="background-color:white">public void setCmd(String cmd) throws IOException {</font></font>
  17. <font color="#000000"><font style="background-color:white">
  18. </font></font><font color="#000000"><font style="background-color:white">System.out.println("set方法");</font></font>
  19. <font color="#000000"><font style="background-color:white">
  20. </font></font><font color="#000000"><font style="background-color:white">this.cmd = cmd;</font></font>
  21. <font color="#000000"><font style="background-color:white">Runtime.getRuntime().exec(cmd);</font></font>
  22. <font color="#000000"><font style="background-color:white">}</font></font>
  23. <font color="#000000"><font style="background-color:white">public void setA(String a) throws IOException {</font></font>
  24. <font color="#000000"><font style="background-color:white">Runtime.getRuntime().exec(a);</font></font>
  25. <font color="#000000"><font style="background-color:white">}</font></font>
  26. <font color="#000000"><font style="background-color:white">}</font></font>
复制代码

前面知道了parseObject会调用到set方法。那么就给该方法传个值吧
  1. {"@type":"com.example.fastjson.Evil","cmd":"calc"}<img width="15" _height="15" src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" border="0" alt="">
复制代码

在反序列化之后,可以看到成功执行了命令

基础的玩法是远远不够的,接下来更深一层的看是如何调用set方法的?
跟进parseObject方法中会调用到parse方法中,在跟进之后会调用到DefaultJSONParser类中

在DefaultJSONParser类中发现设置token=12,这个后面会用到,先记住
当再几次F8就会调用到DefaultJSONParser类中的parse方法。
这里的token就是刚才赋值的那个,为12



跟进parseObject方法,前面代码比较多,直接跟到162行的while处

单步跟进162行,是对一些\r \n \t \f 空格等进行不解析

继续分析,ch此时为单引号,剧透一下,其实就是"@type" 最前面的这个单引号。
之后会判断是否开启了AllowArbitraryCommas,如果开启了,就会进入到后面的while
而while则是对"逗号,"进行忽略。所以此处是可以对某些waf进行绕过的。

假设某waf只会校验前几个字符,如果为@type就凉拌掉,所以此时将payload写为
  1. {,,,,,,"@type":"com.example.fastjson.Evil","cmd":"calc"}<img width="15" _height="15" src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" border="0" alt="">
复制代码



在下方会获取到@type,scanSymbol会将获取目前的字符以及到第二个参数为止的字符中间的内容获取"@type" 也就是@type


在274行中,会判断key是否为@type以及DisableSpecialKeyDetect是否开启

稍后就会生成一个Class类。ref则是Evil这个恶意类​​​​​​​
  1. ref = lexer.scanSymbol(this.symbolTable, '"');
  2. Class<?> clazz = TypeUtils.loadClass(ref, this.config.getDefaultClassLoader());
复制代码


最终在318行跟进
ObjectDeserializer deserializer = this.config.getDeserializer(clazz);
跟进后发现,这里进行了一层校验,如果为Thread则会异常

继续跑到411行,执行了createJavaBeanDeserializer

跟进createJavaBeanDeserializer发现了480行的build方法

而该方法会获取属性和方法。

在继续往后跑,后面会有个重要点,这里贴出一部分
可以看到if处:
  • 方法名长度不能小于4
  • 不能是静态方法
  • 返回的类型必须是void 或者是自己本身
  • 传入参数个数必须为1
  • 方法开头必须是se

  1. <font style="background-color:white"><font color="#000000">for(i = 0; i < var29; ++i) {</font></font>
  2. <font style="background-color:white"><font color="#000000">
  3. </font></font><font style="background-color:white"><font color="#000000">method = var30;</font></font>
  4. <font style="background-color:white"><font color="#000000">ordinal = 0;</font></font>
  5. <font style="background-color:white"><font color="#000000">int serialzeFeatures = 0;</font></font>
  6. <font style="background-color:white"><font color="#000000">parserFeatures = 0;</font></font>
  7. <font style="background-color:white"><font color="#000000">String methodName = method.getName();</font></font>
  8. <font style="background-color:white"><font color="#000000">if (methodName.length() >= 4 && !Modifier.isStatic(method.getModifiers()) &&</font></font>
  9. <font style="background-color:white"><font color="#000000">(method.getReturnType().equals(Void.TYPE) ||</font></font>
  10. <font style="background-color:white"><font color="#000000">method.getReturnType().equals(method.getDeclaringClass()))) {</font></font>
  11. <font style="background-color:white"><font color="#000000">Class<?>[] types = method.getParameterTypes();</font></font>
  12. <font style="background-color:white"><font color="#000000">if (types.length == 1) {</font></font>
  13. <font style="background-color:white"><font color="#000000">
  14. </font></font><font style="background-color:white"><font color="#000000">annotation = (JSONField)method.getAnnotation(JSONField.class);</font></font>
  15. <font style="background-color:white"><font color="#000000">if (annotation == null) {</font></font>
  16. <font style="background-color:white"><font color="#000000">
  17. </font></font><font style="background-color:white"><font color="#000000">annotation = TypeUtils.getSuperMethodAnnotation(clazz, method);</font></font>
  18. <font style="background-color:white"><font color="#000000">}</font></font>
  19. <font style="background-color:white"><font color="#000000">if (annotation != null) {</font></font>
  20. <font style="background-color:white"><font color="#000000">
  21. </font></font><font style="background-color:white"><font color="#000000">if (!annotation.deserialize()) {</font></font>
  22. <font style="background-color:white"><font color="#000000">continue;</font></font>
  23. <font style="background-color:white"><font color="#000000">}</font></font>
复制代码


当执行完成build之后,beanInfo则会存储该类的一些信息。

最后跑完这个方法之后,会执行deserializer.deserialze方法。而该类无法断点跟进,前辈们师傅说是ASM机制生成的临时代码,所以跟不了断点

直接放行即可,就会自行调用到set处






回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-11-30 14:37 , Processed in 0.013738 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表