Fastjson 1.2.80 反序列化利用链分析

渗透技巧 2年前 (2022) admin
1,122 0 0


 作者:blckder02


前言


该漏洞由浅蓝研究发现,可在特定条件下绕过 AutoType 关闭限制加载远程对象进行反序列化。


影响版本: 特定依赖存在下影响 ≤1.2.80。

漏洞公告:

https://github.com/alibaba/fastjson/wiki/security_update_20220523


Groovy 命令执行


Fastjson

1

搭建

1. Fastjson 1.2.80

2. Groovy 3.0.8

3. JDK 8u12


pom.xml中导入依赖:

<dependency>
      <
groupId>com.alibabagroupId>
      <
artifactId>fastjsonartifactId>
      <
version>1.2.80version>
dependency>

<dependency>
      <
groupId>org.codehaus.groovygroupId>
      <
artifactId>groovy-allartifactId>
      <
version>3.0.8version>
dependency>


复现

构造恶意类,要有@GroovyASTTransformation注解,并且实现ASTTransformation接口;

Fastjson 1.2.80 反序列化利用链分析


起一个服务端,把恶意类的 class 文件放到根目录下,再创建一个META-INF/services/org.codehaus.groovy.transform.ASTTransformation文件,在文件中写入恶意类的名称;

 python -m http.server 8081

Fastjson 1.2.80 反序列化利用链分析


POC,classpathList为远程服务端地址;

Fastjson 1.2.80 反序列化利用链分析


命令执行成功;

Fastjson 1.2.80 反序列化利用链分析


POC 调试

parseObject()中获取到第一个@type字段中的类名,带入checkAutoType()

Fastjson 1.2.80 反序列化利用链分析

mapping中获取到了 clazz,然后返回java.lang.Exception

Fastjson 1.2.80 反序列化利用链分析

因为java.lang.Exception是继承 Throwable 类的,所以获取到的是ThrowableDeserializer类型的deserializer
 这里目的是要利用ThrowableDeserializer.deserializer()中调用的checkAutoType()方法中第二个参数不为 null ,和 1.2.68 版本的绕过原理一样;

Fastjson 1.2.80 反序列化利用链分析

跟进deserialze(),获取到第二个@type字段中的类名,带入checkAutoType()

Fastjson 1.2.80 反序列化利用链分析

跟进,expectClass参数值为Throwable.class,所以expectClassFlag赋为 true;

Fastjson 1.2.80 反序列化利用链分析

然后进行黑名单校验,CompilationFailedException不在黑名单中,能顺利通过校验;

Fastjson 1.2.80 反序列化利用链分析

因为expectClassFlag为 true, 所以在1185 行加载了CompilationFailedException,可以绕过后面对 AutoType 的校验,并且 1190 行把它添加进了 mapping;

Fastjson 1.2.80 反序列化利用链分析

跟进到ThrowableDeserializer.deserialze(),调用了cast(),成员变量类型是ProcessingUnit

Fastjson 1.2.80 反序列化利用链分析

跟进TypeUtils.cast(),调用castToJavaBean();

Fastjson 1.2.80 反序列化利用链分析

获取到JavaBeanDeserializer类型的deserializer

Fastjson 1.2.80 反序列化利用链分析

获取到deserializer之后,把org.codehaus.groovy.control.ProcessingUnit put 进了this.deserializers,这是后面绕过 AutoType 校验的关键!

Fastjson 1.2.80 反序列化利用链分析

1195 行进行实例化,实例化的结果是 null ;

Fastjson 1.2.80 反序列化利用链分析

返回 null 值,调用setValue()进行赋值,由于值为 null ,抛出异常;

Fastjson 1.2.80 反序列化利用链分析

然后进入 POC 的 catch 分支,反序列化 poc2。

获取到第一个@type字段,带入checkAutoType()

Fastjson 1.2.80 反序列化利用链分析

因为前面 把ProcessingUnit put this.deserializers了,所以这里可以直接从this.deserializers中找到ProcessingUnit,返回 clazz;

Fastjson 1.2.80 反序列化利用链分析

获取到JavaBeanDeserializer类型的deserializer,调用deserialze()

Fastjson 1.2.80 反序列化利用链分析

ref是第二个@typeJavaStubCompilationUnitexpectClassProcessingUnit类,带入checkAutoType()

Fastjson 1.2.80 反序列化利用链分析

跟进,expectClassFlag为true,在 1185 行加载JavaStubCompilationUnit,并且将它添加进 mapping;

Fastjson 1.2.80 反序列化利用链分析

调用JavaBeanDeserializer.deserialze()

Fastjson 1.2.80 反序列化利用链分析

跟进调用了parseField()

Fastjson 1.2.80 反序列化利用链分析

跟进调用了deserialze(),返回的value是 CompilerConfiguration 类型,并且把传入的远程地址赋给了classpath

Fastjson 1.2.80 反序列化利用链分析

117 行又把整个 CompilerConfiguration 对象赋给JavaStubCompilationUnitconfig参数;

Fastjson 1.2.80 反序列化利用链分析

在 982 行的 for 循环中,从fieldValues中获取参数值存入params[]中,因为我们只给config传了值,所以params[]只有一个参数值;

Fastjson 1.2.80 反序列化利用链分析

1044 行 new 了一个JavaStubCompilationUnit对象;

Fastjson 1.2.80 反序列化利用链分析

JavaStubCompilationUnit 的构造方法有三个参数,第一个参数是我们自定义的,第二个参数是调用setClassLoader()获取的,第三个参数是 null;
 setClassLoader()中 new 了 GroovyClassLoader 对象;

Fastjson 1.2.80 反序列化利用链分析

跟到GroovyClassLoader的构造方法,从config中获取classpath添加进GroovyClassLoader对象;

Fastjson 1.2.80 反序列化利用链分析

返回到CompilationUnit构造方法,184 行调用了addPhaseOperations(),跟进再调用了ASTTransformationVisitor.addPhaseOperations()

Fastjson 1.2.80 反序列化利用链分析

跟进addGlobalTransforms() -> doAddGlobalTransforms()transformLoader是从JavaStubCompilationUnit对象中获取到的GroovyClassLoader
 使用getResource()从加载器中获取META-INF/services/org.codehaus.groovy.transform.ASTTransformation文件下的资源

Fastjson 1.2.80 反序列化利用链分析

跟进getResource(),可以看到,加载资源遵循双亲委派型,会首先委托父类加载器,委托到启动类加载器时,会从 Bootstrap classpath 对应的 jar 包或目录中加载资源;

Fastjson 1.2.80 反序列化利用链分析

getBootstrapClassPath()最终获取到的是bcp字段的值,也就是说从bcp中的 jar 包中加载资源;

Fastjson 1.2.80 反序列化利用链分析

然后调用findResources(),跟进,再调用ucpfindResources()ucp是 URLClassPath 类型,一共会调用三次ucp.findResources(),每次的ucp都不一样;

Fastjson 1.2.80 反序列化利用链分析
Fastjson 1.2.80 反序列化利用链分析
Fastjson 1.2.80 反序列化利用链分析

因为每次的类加载器不一样,所以 ucp 不一样,第三次使用的是GroovyClassLoader,而GroovyClassLoader中含有指定的classpath http://127.0.0.1:8081/,所以会从远程地址加载资源;

Fastjson 1.2.80 反序列化利用链分析

资源加载进来后,就开始逐行读取每个资源的内容,把不以#开头的行放到transformNames中;

Fastjson 1.2.80 反序列化利用链分析

循环到第三次globalServices时,transformNames中已经有四个值了;

Fastjson 1.2.80 反序列化利用链分析

接着探测远程地址的资源,跟进hasMoreElements() -> next() -> 第二次的hasMoreElements() -> next() -> e.hasMoreElements() -> next()

Fastjson 1.2.80 反序列化利用链分析

跟进findResource(),创建了与远程地址资源的 http 连接,设置请求头为HEAD,对远程资源进行探测;

Fastjson 1.2.80 反序列化利用链分析

然后读取远程资源,使用了URLStreams.openUncachedStream()

Fastjson 1.2.80 反序列化利用链分析

跟进可以看到调用getInputStream(),向远程资源发起了 http 请求;

Fastjson 1.2.80 反序列化利用链分析

命令行中可以看到两次请求的记录;

Fastjson 1.2.80 反序列化利用链分析

然后也是将远程资源中的内容读到transformNames中,这样就获取到恶意类的名称;

Fastjson 1.2.80 反序列化利用链分析

接着下面调用addPhaseOperationsForGlobalTransforms()

Fastjson 1.2.80 反序列化利用链分析

跟进,在该方法中依次加载并实例化了transformNames中的所有类;

Fastjson 1.2.80 反序列化利用链分析

校验了加载的类上是否含有@GroovyASTTransformation注解,所以在构造恶意类时要加上注解;
 并且得实现或继承ASTTransformation接口,所以恶意类要实现ASTTransformation,然后实例化触发命令执行。

Fastjson 1.2.80 反序列化利用链分析


jython+spring-context+postgresql 命令

Fastjson

2

搭建

1. Fastjson 1.2.80

2. jython 1.1

3. spring-context 5.0.2.RELEASE

4. postgresql 42.3.0 (42.3.0 < 版本 < 42.3.2)


pom.xml中添加如下依赖:

Fastjson 1.2.80 反序列化利用链分析

复现

利用org.springframework.context.support.ClassPathXmlApplicationContext来加载远程指定的 XML 配置文件,在配置文件中定义一个 bean ,bean 中通过使用 Spring EL 表达式来调用java.lang.ProcessBuilderstart()方法来执行命令。

构造 bean 文件spel.xml

Fastjson 1.2.80 反序列化利用链分析


起一个服务端,把 bean 文件放在根目录下;

构造POC:

Fastjson 1.2.80 反序列化利用链分析


ParseException继承Exception的子类,有一个PyObject类型参数type

Fastjson 1.2.80 反序列化利用链分析

PyConnection继承PyObject,有一个Connection类型的参数connection

Fastjson 1.2.80 反序列化利用链分析

PgConnection实现了Connection的子接口,构造函数中有的五个参数,连接了数据库。

Fastjson 1.2.80 反序列化利用链分析

hostportuserdatabase参数值可以随便传,不影响命令执行;
 socketFactory得是ClassPathXmlApplicationContextsocketFactoryArg是远程 bean 文件地址。


POC 调试

首先对a进行反序列化,原理作用和 Groovy 链一样;
 获取到第一个@type的值java.lang.Exception,带入checkAutoType()
 从 mapping 中获取并返回 clazz

Fastjson 1.2.80 反序列化利用链分析

Exception继承了Throwable,所以下面调用ThrowableDeserializer.deserialze,接着获取到第二个@type的值,带入checkAutoType(),第二个参数值不为 null;

Fastjson 1.2.80 反序列化利用链分析

expectClassFlag为 true ,加载ParseException类并返回;

Fastjson 1.2.80 反序列化利用链分析

获取了type参数的类型,调用cast()

Fastjson 1.2.80 反序列化利用链分析

跟进到castToJavaBean(),把PyObject类型 put 进了this.deserializer,为后面绕过 AutoType 检测做准备;

Fastjson 1.2.80 反序列化利用链分析

返回的value不为 null,所以下面调用setValue()不会报错,所以不用像 Groovy 链那样使用try-catch

Fastjson 1.2.80 反序列化利用链分析

然后接着反序列化b,获取到第一个@type值带入checkAutoType(),能直接从this.deserializer获取到clazz

Fastjson 1.2.80 反序列化利用链分析

PyObject对应的deserializer类型是FastjsonASMDeserializer_1_PyObject,但还是调用的JavaBeanDeserializer.deserialze()

Fastjson 1.2.80 反序列化利用链分析

获取到第二个@type的值带入checkAutoType(),第二个参数不为 null;

Fastjson 1.2.80 反序列化利用链分析

加载并返回PyConnection

Fastjson 1.2.80 反序列化利用链分析

跟进JavaBeanDeserializer.deserialze() -> DefaultFieldDeserializer.parseField() -> getFieldValueDeserilizer(),返回JavaBeanDeserializer类型;

Fastjson 1.2.80 反序列化利用链分析

调用deserialze(),经过一堆循环,获取了下一个@type的值,带入checkAutoType(),第二个参数不为null;

Fastjson 1.2.80 反序列化利用链分析

加载并返回PgConnection

Fastjson 1.2.80 反序列化利用链分析

调用deserialze(),遍历到hostSpecs参数时,逐步调用到getFieldValueDeserilizer(),返回一个ObjectArrayCode类型;

Fastjson 1.2.80 反序列化利用链分析

然后调用ObjectArrayCode.deserialze()

Fastjson 1.2.80 反序列化利用链分析

跟进parserArray(),调用deserialze()

Fastjson 1.2.80 反序列化利用链分析

获取hostport参数值,实例化了一个HostSpec对象并返回;

Fastjson 1.2.80 反序列化利用链分析

HostSpec对象转换为数组类型,赋给hostSpecs参数;

Fastjson 1.2.80 反序列化利用链分析

接着调用paseField() ,跟进到 MapDeserializer.deserialze(),调用了parseObject()

Fastjson 1.2.80 反序列化利用链分析

parseObject()中把socketFactorysocketFactoryArg的存入了 map 中;

Fastjson 1.2.80 反序列化利用链分析

然后将info放入了fieldValues,另外几个参数赋值都差不多;

Fastjson 1.2.80 反序列化利用链分析

参数全部赋值完毕后,实例化一个PgConnection对象;

Fastjson 1.2.80 反序列化利用链分析

跟进到PgConnection的构造函数,调用了ConnectionFactory.openConnection()

Fastjson 1.2.80 反序列化利用链分析

逐步跟进到getSocketFactory(),调用了ObjectFactory.instantiate()

Fastjson 1.2.80 反序列化利用链分析

反射获取了ClassPathXmlApplicationContext类和构造函数,最后实例化ClassPathXmlApplicationContext对象;

Fastjson 1.2.80 反序列化利用链分析

加载配置文件,实例化文件中的恶意对象。

Fastjson 1.2.80 反序列化利用链分析


aspectjtools 读文件


Fastjson

3

搭建

1. Fastjson 1.2.80

2. aspectj 1.8.6


pom.xml中导入依赖:

Fastjson 1.2.80 反序列化利用链分析

复现

直接来看看网上普遍使用的POC,要分三次打:

Fastjson 1.2.80 反序列化利用链分析

Fastjson 1.2.80 反序列化利用链分析

由于SourceTypeCollisionExceptionnewAnnotationProcessorUnits参数是接口类型;

Fastjson 1.2.80 反序列化利用链分析


会在TypeUtils.castToJavaBean()中返回一个代理对象,并不会像前两条链一样将ICompilationUnit putthis.deserializer

Fastjson 1.2.80 反序列化利用链分析

所以 jsonStr1 中不添加newAnnotationProcessorUnits参数,它的作用是把SourceTypeCollisionException添加进mapping,以便后面使用。

MiscCodec.deserialze()中调用了toJavaObject(),可以把 JSON对象转换成 Java 对象,然后把该参数类型 put 进this.deserializer
 MapDeserializer.deserialze()可以把传入的各参数的值赋给对应的对象;

jsonStr2 中 各类对应的deserializer如下:


Class

deserializer

java.lang.Class

MiscCodec

java.lang.String

StringCode

java.util.Locale

MiscCodec

com.alibaba.fastjson.JSONObject

MapDeserializer


jsonStr2 的作用就是将org.aspectj.org.eclipse.jdt.internal.compiler.env.ICompilationUnit类型 put 进this.deserializer
 同时构造 json 语法错误,使之抛出异常,进入下一个反序列化。

jsonStr3 的作用就是利用BasicCompilationUnit类中的getContents()方法来读取文件内容;
 “x”:{}的作用是将BasicCompilationUnit对象序列化为 JSON 数据,否则输出结果如下:

Fastjson 1.2.80 反序列化利用链分析

经过分析发现,其实 jsonStr2 简化为如下代码,同样可以实现读文件。

Fastjson 1.2.80 反序列化利用链分析


POC 调试

获取到第一个@type的值,带入checkAutoType(),从mapping中获取并返回Exception

Fastjson 1.2.80 反序列化利用链分析

调用ThrowableDeserializer.deserialze(),获取到第二个@type的值,带入checkAutoType()expectClassFlag为 true,加载并返回SourceTypeCollisionException,还把SourceTypeCollisionException添加进了mapping,这是后面绕过 AutoType 关闭限制的关键;

Fastjson 1.2.80 反序列化利用链分析

然后创建并返回一个 Exception ,结束第一个反序列化。

Fastjson 1.2.80 反序列化利用链分析

开始对第二个字符串进行反序列化,获取到第一个@type的值,带入checkAutoType(),从this.deserializers中获取并返回Class

Fastjson 1.2.80 反序列化利用链分析

然后调用MiscCodec.deserialze()

Fastjson 1.2.80 反序列化利用链分析

获取到第二个@type的值,带入checkAutoType(),从mapping中获取并返回String

Fastjson 1.2.80 反序列化利用链分析

接着调用StringCode.deserialze(),获取到下一个@type的值,带入checkAutoType(),从mapping中获取并返回Locale

Fastjson 1.2.80 反序列化利用链分析

调用MiscCodec.deserialze(),获取到下一个@type的值,直接在 310 行获取到com.alibaba.fastjson.JSONObject,不用再调用checkAutoType()
 接着往下就是调用MapDeserializer.deserialze()

Fastjson 1.2.80 反序列化利用链分析

获取到java.lang.String,然后调用StringCode.deserialze(),返回@type

Fastjson 1.2.80 反序列化利用链分析

接着分别获取后面的两个值,存入map

Fastjson 1.2.80 反序列化利用链分析

一直返回到MiscCodec.deserialze(),调用了toJavaObject()

Fastjson 1.2.80 反序列化利用链分析

跟进toJavaObject() -> TypeUtils.cast() -> cast() -> castToJavaBean(),获取SourceTypeCollisionException带入checkAutoType(),从mapping中获取并返回 clazz
 然后又调用castToJavaBean()

Fastjson 1.2.80 反序列化利用链分析

跟进createInstance(),获取到ICompilationUnit[]类型的参数newAnnotationProcessorUnits,调用parseField() -> getFieldValueDeserilizer() -> getDeserializer()
 因为参数是数组类型,所以得到ObjectArrayCode类型的deserializer,然后把它 put 进this.deserializer,这里的type是带[L…;字符的;

Fastjson 1.2.80 反序列化利用链分析

调用ObjectArrayCode.deserialize(),获取到interface org.aspectj.org.eclipse.jdt.internal.compiler.env.ICompilationUnit,带入parseArray()
 跟进到getDeserializer()ICompilationUnit接口类型的deserializer put this.deserializer,这里就和前面两条链的目的一样,为后续绕过 AutoType 做准备;

Fastjson 1.2.80 反序列化利用链分析

然后逐步返回,到结束反序列化时,抛出了一个异常,由于构造的 json 字符串闭合不正确;

Fastjson 1.2.80 反序列化利用链分析

接着就进入到 catch 分支,对第三个 json 数据进行反序列化。

Fastjson 1.2.80 反序列化利用链分析

获取到第一个@type,带入checkAutoType(),从this.deserializer中找到并返回ICompilationUnit类;

Fastjson 1.2.80 反序列化利用链分析

接着调用deserialize(),获取到第二个@type的值,带入checkAutoType(),第二个参数不为 null;

Fastjson 1.2.80 反序列化利用链分析

加载并返回BasicCompilationUnit,然后调用deserialize()

Fastjson 1.2.80 反序列化利用链分析

跟进,在deserialize()中把BasicCompilationUnit类型中不为 null 的参数放到params中,然后实例化该类;

Fastjson 1.2.80 反序列化利用链分析

最后返回JSONObject对象,到这里还没读到文件中的内容;

Fastjson 1.2.80 反序列化利用链分析

继续跟进println(),到MapSerializer.write(),再继续跟进,看不到调试过程了;

Fastjson 1.2.80 反序列化利用链分析

但是一直点,会跳到BasicCompilationUnit.getContents(),这里将D:/1.txt转换为 File 对象,调用了Util.getFileCharContent()

Fastjson 1.2.80 反序列化利用链分析

getFileCharContent()中创建输入流,以字符数组的形式读取文件中的内容;

Fastjson 1.2.80 反序列化利用链分析

然后就是返回、输出文件内容。

Fastjson 1.2.80 反序列化利用链分析


补丁分析



Fastjson

4


更新到 1.2.83 版本。
 checkAutoType()中,在从mapping获取到 java.lang.Execption后,添加了一个校验;
 如果满足expectClass为 null 、clazz不为 null 、clazz实现或继承Throwable.class、未开启 AutoTpye,那把 clazz赋为 null;

Fastjson 1.2.80 反序列化利用链分析

最后只能返回一个 null。

Fastjson 1.2.80 反序列化利用链分析


参考链接


https://github.com/su18/hack-fastjson-1.2.80

https://y4er.com/posts/fastjson-1.2.80

https://github.com/knownsec/KCon/blob/master/2022/Hacking%20JSON%E3%80%90KCon2022%E3%80%91.pdf






原文始发于微信公众号(中孚安全技术研究):Fastjson 1.2.80 反序列化利用链分析

版权声明:admin 发表于 2022年9月29日 上午10:08。
转载请注明:Fastjson 1.2.80 反序列化利用链分析 | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...