代码审计-fastjson1.2.68分析

本文详细分析了Fastjson 1.2.68版本的安全问题,重点探讨了如何利用`ThrowableDeserializer`和`JavaBeanDeserializer`进行潜在的代码注入。通过分析`checkAutoType`方法和构造特定的反序列化器,揭示了如何在特定条件下触发危险操作。虽然限制较多,但文章提供了多个利用场景,包括Mysql JDBC RCE和文件操作。此外,还介绍了如何通过`AutoCloseable`接口进行深入利用,并给出了利用链挖掘的方法和示例。
摘要由CSDN通过智能技术生成

前言

1.2.68有safeMode,但是默认不是开启的,所以还是有风险

分析1

根据网上信息的描述,这次问题点主要是在checkAutoType参数期望类这个地方

看看哪些地方会调用checkAutoType方法并使用到期望类这个参数

发现主要是2个地方会使用到

  1. com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer#deserialze

  2. com.alibaba.fastjson.parser.deserializer.ThrowableDeserializer#deserialze

那哪些地方会使用到这两个类的对应的deserialze方法呢?

发现这个地方刚好是常规的@type进行checkAutoType检查后进行反序列化的时候会调用到;

先构造反序列化器,也就是说如果我们@type的值对应的类构造的反序列化器是JavaBeanDeserializer或者ThrowableDeserializer,就会触发deserialze,同时有希望触发带有期望类参数的checkAutoType达到我们的目的

总结成一句话就是:寻找怎么才能调用到带有expectClass参数的checkAutoType方法

分析2

那这俩个反序列化器是怎么构造出来的呢?

我们跟一下config.getDeserializer(clazz)

重载,经过一系列的各种class的判断,到了这

如果clazz是Throwable的子类,那么就返回ThrowableDeserializer

如果所有条件都不满足,那么就会调用createJavaBeanDeserializer去新建JavaBeanDeserializer

跟进新建函数,发现是接口的情况,asmEnable为false,可以创建javaBeanDeserializer对象,否则调用的asmFactory.createJavaBeanDeserializer进行创建,不是我们想要的

ThrowableDeserializer

分析

要使用到com.alibaba.fastjson.parser.deserializer.ThrowableDeserializer这个反序列化器,根据上面的分析,那么我们@type传入的就应该是Throwable的子类

所以poc(这里就直接用的他本身了)

因为java.lang.Throwable不在mapping和可信任的map中,所以这里要手动开启autoTypeSupport

ParserConfig.getGlobalInstance().setAutoTypeSupport(true);

{"@type":"java.lang.Throwable", "a":"b"}

运行,跟,可以看到这个时候我们的反序列化器deserializer确实是我们预计的ThrowableDeserializer类

进入deserialize,然后一直F8,发现payload后续参数满足条件键值对的键是@type,就可以调用带有期望类参数的checkAutoType

所以修改一下payload

{"@type":"java.lang.Throwable", "@type":"org.example.App"}

然后跟到刚才的地方

key为@type,exClass为我们输入的@type对应的值org.example.App,进入checkAutoType(期望类为Throwable.class,平时一般为null)

通过各种黑白名单的检查,一直到这,loadClass(因为这里开启了autoTypeSupport,所以cacheClass为true)

跟进看看,其实就是给它加到mapping中

再返回到checkAutoType继续往下,如果传入的clazz是期望类的子类,就通过autoType检查返回clazz

也就是说,我们的第二个@type对应的类必须是期望类java.lang.Throwable的子类,我们这里是自己随便写的一个类明显不是Throwable的子类,会抛出异常,所以找一个它的子类改一下payload

{"@type":"java.lang.Throwable", "@type":"java.lang.Error"}

运行到刚才的地方,成功返回clazz

如果payload中还有其他的参数,关键参数如message会被用作后续的构造函数的参数等,otherValues就是传入的其他参数,比如"a":"b"这种,在创建实例后会进行setValue操作

再往后就是创建一个实例,用上刚才传入的参数啥的

跟进就是通过反射获取构造函数再创建实例

有参数的情况下会执行setValue操作,也就是会调用setXXX方法

利用

通过上述的分析,开启ast的情况下,如果我们能找到一个java.lang.Throwable的子类,且其的setter或者getter能执行危险操作,就有可利用的嫌疑

  1. 限定了可以利用的类必须是Throwable的子类,不过异常类很少使用高危函数。。。所以很鸡肋吧

  2. 需要开启AST,更鸡肋了,随便找个不在黑名单的类都可以利用了

举个例子:

  • 恶意类

package org.example;
import lombok.Data;

@Data

public class User extends Error{

   private String test;

   public void setTest(String test) {

       System.out.println("call setTest");

       System.out.println("test value: " + test);

       this.test = test;

   }

}

  • payload

{"@type":"java.lang.Throwable", "@type":"org.example.User", "test":"hahahaha"}

JavaBeanDeserializer

分析

在获取反序列化器的时候,如果是一个接口,且里面所有的判断都不满足,就会返回JavaBeanDeserializer

我们随便创建一个接口

package org.example;
public interface Test {

}

payload

{"@type":"org.example.Test", "test":"hahahaha"}

运行,一直到获取了反序列化器进行反序列化

跟进,看看里面的判断条件,一阵F8后,看到了熟悉的东西

也就是说和刚才那个一样,还得需要一个@type,修改payload

{"@type":"org.example.Test", "@type":"org.example.Test1", "test":"hahahaha"}

熟悉的味道

往下,进入checkAutoType,expectClass为我们传入的第一个接口

又一直F8,来到了熟悉的地方,loadClass,给我们的传入的第二个@type的类加入到mapping中

再往后,这几行基本杜绝了JNDI注入的风险

再继续往下,clazz必须是expectClass的子类,和上面那个类似

我们把接口Test1变成Test的子类,然后继续

通过验证,ok,返回clazz

返回就是常规的setValue了

利用

和上面那个差不多一样,只不过这个应用更广泛,只需要找一个接口,然后找一个实现了这个接口的类,类中有可以利用的点即可;最好是可以绕过autoTypeSupport

于是大佬们找到了java.lang.AutoCloseable这个接口,这个接口位于默认的mapping中,有很多子类,不开启autoTypeSupport也可以用(大佬们真牛)

本地先测试下,证明我们的猜想是不是正确的,编写个恶意的类,实现java.lang.AutoCloseable接口

package org.example;
public class User implements AutoCloseable{

   private String test;

   public void setTest(String test) {

       System.out.println("call setTest");

       System.out.println("test value: " + test);

       this.test = test;

   }

   @Override

   public void close() throws Exception {

   }

}

payload(不开启autoTypeSupport)

{"@type":"java.lang.AutoCloseable", "@type":"org.example.User", "test":"hahahaha"}

Bingo!!!

AutoCloseable深入使用

小知识1

fastjson除了使用setXXX的方法赋值外,也可以直接对构造函数进行传值反序列为对象,比如

public User(String test){
   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值