又遇fastjson漏洞

简介: 又是fastjson!又是这家伙!至少经历了2次+这样的场景。我不知道这家伙又得罪了哪位大仙,频繁被“黑”。fastjson到底做错了什么?为什么会被频繁爆出漏洞?但是作为一个技术人(兴趣爱好者),我更关注的是它为什么会频繁被爆漏洞?而其他的Gson却没有。通过对fastjson的releaseNote以及部分源代码进行查阅,发现此现象跟fastjson中的一个AutoType特性有关联。

     又是fastjson!又是这家伙!至少经历了2次+这样的场景。我不知道这家伙又得罪了哪位大仙,频繁被“黑”。fastjson到底做错了什么?为什么会被频繁爆出漏洞?但是作为一个技术人(兴趣爱好者),我更关注的是它为什么会频繁被爆漏洞?而其他的Gson却没有。通过对fastjson的releaseNote以及部分源代码进行查阅,发现此现象跟fastjson中的一个AutoType特性有关联

    Java处理Json数据有三个比较流行的类库,Gson(google维护)、Jackson、以及今天的

主角Fastjson。

     fastjson是阿里巴巴一个开源的json相关的java library,地址:https://github.com/alibaba/fastjson,Fastjson可以将java的对象转换成json的形式,也可以用来将json转换成Java对象,效率较高,被广泛的用在web服务以及android上,它的JSONString()方法可以将Java的对象转换成json格式,同样通过parseObject方法可以将json数据转换成java的对象。这个阿里巴巴的开源一个牛逼的Json解析库,通常被用于将Java Bean和Json字符串之间进行转换。

     这次被工信部通报公司系统存在漏洞,导致了公司各业务线升级fastjson版本,以防止系统安全问题。对部分业务包进行查看,发现当前使用的fastjson版本不一,不过都是在1.2.68之前版本。2020年真是不平凡的一年,fastjson组件库频繁暴露安全漏洞,此漏洞可以绕过autoType开关来实现反序列化远程代码执行并获取服务器访问权限。

     从2019年7月份发布的v1.2.59一直到2020年6月份发布的 v1.2.71 ,每个版本的升级中都有关于AutoType的升级,涉及13个正式版本。fastjson中与AutoType相关的版本历史可参考如下:


1.2.59发布,增强AutoType打开时的安全性 fastjson
1.2.60发布,增加了AutoType黑名单,修复拒绝服务安全问题 fastjson
1.2.61发布,增加AutoType安全黑名单 fastjson
1.2.62发布,增加AutoType黑名单、增强日期反序列化和JSONPath fastjson
1.2.66发布,Bug修复安全加固,并且做安全加固,补充了AutoType黑名单 fastjson
1.2.67发布,Bug修复安全加固,补充了AutoType黑名单 fastjson
1.2.68发布,支持GEOJSON,补充了AutoType黑名单
1.2.69发布,修复新发现高危AutoType开关绕过安全漏洞,补充了AutoType黑名单
1.2.70发布,提升兼容性,补充了AutoType黑名单
1.2.71发布,补充安全黑名单,无新增利用,预防性补充

     关于1.2.68版本的漏洞中,其主要利用方式是利用异常进行攻击,并且重写getMessage方法。其实本质最广泛的是使用AutoCloseable这个接口绕过checkAutoType,网上大多也以这种方式展开分析。因为AutoCloseable构成的payload所需要的条件可能没有那么复杂。之前有文章中说1.2.69已经修复了异常攻击漏洞,但是经过测试似乎并没有。

     然而,截止到2020年11月份发布的v1.2.75版本,依然没有对异常类进行处理,所以仍然存在漏洞,该漏洞的利用条件如下:


      1、危险类必须继承“系统白名单”中任意一个异常类

      2、危险类中的危险方法必须为构造方法或者setter方法,参数可控是最好的。

      以fastjson1.2.68为例,当初产生的主要原因在于,fastjson为了再次避免用户用其AutoType机制进行反序列化而产生漏洞,加入了checkAutoType方法,AutoType机制的标志就是“@type”这个标签。checkAutoType这个方法本意是不想让用户使用AutoType,除非用户自己开启AutoType。然而在checkAutoType方法实现过程中还是没忍住偷偷用了几下(标志是解析了"@type"标志),但加强了限制。在代码审计过程中,我发现,在com.alibaba.fastjson.parser.ParserConfig第1326行


clazz = TypeUtils.getClassFromMapping(typeName);

     其中typeName变量是用户输入的序列化之后的JSON格式数据中,含有类名的字符串,比如{"@type","com.demo.test"}这个json数据,其typeName就是com.demo.test。这行代码的意思就是从TypeUtils这个类中尝试获取json中提到的类。在TypeUtils的静态方法中,执行了addBaseClassMappings函数,其函数代码如下(注意其中含有AutoCloseable接口):


private static void addBaseClassMappings(){
        mappings.put("byte", byte.class);
        mappings.put("short", short.class);
        mappings.put("int", int.class);
        mappings.put("long", long.class);
        mappings.put("float", float.class);
        mappings.put("double", double.class);
        mappings.put("boolean", boolean.class);
        mappings.put("char", char.class);
        mappings.put("[byte", byte[].class);
        mappings.put("[short", short[].class);
        mappings.put("[int", int[].class);
        mappings.put("[long", long[].class);
        mappings.put("[float", float[].class);
        mappings.put("[double", double[].class);
        mappings.put("[boolean", boolean[].class);
        mappings.put("[char", char[].class);
        mappings.put("[B", byte[].class);
        mappings.put("[S", short[].class);
        mappings.put("[I", int[].class);
        mappings.put("[J", long[].class);
        mappings.put("[F", float[].class);
        mappings.put("[D", double[].class);
        mappings.put("[C", char[].class);
        mappings.put("[Z", boolean[].class);
        Class<?>[] classes = new Class[]{
                Object.class,
                java.lang.Cloneable.class,
                loadClass("java.lang.AutoCloseable"),
                java.lang.Exception.class,
                java.lang.RuntimeException.class,
                java.lang.IllegalAccessError.class,
                java.lang.IllegalAccessException.class,
                java.lang.IllegalArgumentException.class,
                java.lang.IllegalMonitorStateException.class,
                java.lang.IllegalStateException.class,
                java.lang.IllegalThreadStateException.class,
                java.lang.IndexOutOfBoundsException.class,
                java.lang.InstantiationError.class,
                java.lang.InstantiationException.class,
                java.lang.InternalError.class,
                java.lang.InterruptedException.class,
                java.lang.LinkageError.class,
                java.lang.NegativeArraySizeException.class,
                java.lang.NoClassDefFoundError.class,
                java.lang.NoSuchFieldError.class,
                java.lang.NoSuchFieldException.class,
                java.lang.NoSuchMethodError.class,
                java.lang.NoSuchMethodException.class,
                java.lang.NullPointerException.class,
                java.lang.NumberFormatException.class,
                java.lang.OutOfMemoryError.class,
                java.lang.SecurityException.class,
                java.lang.StackOverflowError.class,
                java.lang.StringIndexOutOfBoundsException.class,
                java.lang.TypeNotPresentException.class,
                java.lang.VerifyError.class,
                java.lang.StackTraceElement.class,
                java.util.HashMap.class,
                java.util.Hashtable.class,
                java.util.TreeMap.class,
                java.util.IdentityHashMap.class,
                java.util.WeakHashMap.class,
                java.util.LinkedHashMap.class,
                java.util.HashSet.class,
                java.util.LinkedHashSet.class,
                java.util.TreeSet.class,
                java.util.ArrayList.class,
                java.util.concurrent.TimeUnit.class,
                java.util.concurrent.ConcurrentHashMap.class,
                java.util.concurrent.atomic.AtomicInteger.class,
                java.util.concurrent.atomic.AtomicLong.class,
                java.util.Collections.EMPTY_MAP.getClass(),
                java.lang.Boolean.class,
                java.lang.Character.class,
                java.lang.Byte.class,
                java.lang.Short.class,
                java.lang.Integer.class,
                java.lang.Long.class,
                java.lang.Float.class,
                java.lang.Double.class,
                java.lang.Number.class,
                java.lang.String.class,
                java.math.BigDecimal.class,
                java.math.BigInteger.class,
                java.util.BitSet.class,
                java.util.Calendar.class,
                java.util.Date.class,
                java.util.Locale.class,
                java.util.UUID.class,
                java.sql.Time.class,
                java.sql.Date.class,
                java.sql.Timestamp.class,
                java.text.SimpleDateFormat.class,
                com.alibaba.fastjson.JSONObject.class,
                com.alibaba.fastjson.JSONPObject.class,
                com.alibaba.fastjson.JSONArray.class,
        };
        for(Class clazz : classes){
            if(clazz == null){
                continue;
            }
            mappings.put(clazz.getName(), clazz);
        }
    }


     可以发现是系统将这些类加入了mappings中,之前提到的方法显而易见了,即尝试在TypeUtils类中获取typeName值,如果没有获取到,那么clazz为null,若expectClass此时也为null,就会触发异常,告诉用户AutoType不可用。正常情况下AutoType就是被禁止了。然而还有另一种情况,若clazz不为空,则就存在绕过限制的可能,绕过的关键就是TypeUtils.mappings中所包含的类,它们就相当于是“系统的白名单”。

      更详细的:

      1、fastjson会首先从左至右寻找JSON格式中带“@type”的键,若存在,则将其键对应的值(即typeName)通过checkAutoType方法检查是否在"系统的白名单中"和用户自定义的白名单中,当然系统还有自带的黑名单,typeName还不能在黑名单中。

      以下为1.2.68系统设定的黑名单:


if (expectClass == null) {
            expectClassFlag = false;
        } else {
            if (expectClass == Object.class
                    || expectClass == Serializable.class
                    || expectClass == Cloneable.class
                    || expectClass == Closeable.class
                    || expectClass == EventListener.class
                    || expectClass == Iterable.class
                    || expectClass == Collection.class
                    ) {
                expectClassFlag = false;
            } else {
                expectClassFlag = true;
            }
        }

       2、在确定typeName不在黑名单而又在白名单后,会将typeName赋值为expectClass,expectClass可是个好东西啊,相当于一个强大的通行证,他会允许expectClass的儿子们(实现类或子类)不需要通过系统白名单检查直接通过。1.2.68漏洞也因这个“通行证”而产生。

     关于3者Json框架的特性的相关情况,简要如下:

Fastjson

1、速度快
fastjson相对其他JSON库的特点是快,从2011年fastjson发布1.1.x版本之后,其性能一直高于其他类型库
2、使用简单
fastjson的API十分简洁。
3、使用广泛
fastjson在阿里巴巴大规模使用,在数万台服务器上部署,fastjson在业界被广泛接受。

Jackson

1、容易使用 - jackson API提供了一个高层次外观,以简化常用的用例。
2、无需创建映射 - API提供了默认的映射大部分对象序列化。
3、性能高 - 快速,低内存占用,适合大型对象图表或系统。
4、干净的JSON - jackson创建一个干净和紧凑的JSON结果,这是让人很容易阅读。
5、不依赖 - 库不需要任何其他的库,除了JDK。


Gson

1、输出轻量易读的JSON。
2、支持任意复杂的对象。
3、允许自定义对象的表现形式。
4、允许预先存在的不可变的对象转换为JSON或与之相反。
5、提供一种机制,使得将Java对象转换为JSON或相反如使用toString()以及构造器(工厂方法)一样简单。

     相比之下,其他的json框架,如Gson和Jackson,漏洞数量少很多,高危漏洞也比较少。并且,其他2种框架也在努力改进中,各自有各自的优势,从中远期来看,若fastjson漏洞一直无法修复或者避免,Gson未尝不是一个性价比更高的选择呢?


相关文章
|
6月前
|
自然语言处理 JavaScript 前端开发
谁才是真正的协议之王?fastjson2 vs fury
谁才是真正的协议之王?fastjson2 vs fury
182 1
|
9月前
|
安全 Java 开发者
刚折腾完Log4J,又爆Spring RCE核弹级漏洞
继Log4J爆出安全漏洞之后,又在深夜,Spring的github上又更新了一条可能造成RCE(远程命令执行漏洞)的问题代码,随即在国内的安全圈炸开了锅。有安全专家建议升级到JDK 9以上,有些专家又建议回滚到JDK 7以下,一时间小伙伴们不知道该怎么办了。大家来看一段动画演示,怎么改都是“将军"。
85 1
|
12月前
|
分布式计算 JavaScript 前端开发
Fastjson 很快,但不适合我....
Fastjson 很快,但不适合我....
Fastjson 很快,但不适合我....
|
JSON 安全 fastjson
Fastjson漏洞详解
Fastjson漏洞详解
1412 0
|
JSON 安全 搜索推荐
fastjson:我哭了,差点被几个“漏洞”毁了一世英名
我是 fastjson,是个地地道道的杭州土著,但我始终怀揣着一颗走向全世界的雄心。这不,我在 GitHub 上的简介都换成了英文,国际范十足吧?
|
JSON fastjson 数据格式
震惊!fastjson SerializerFeature详解竟然是这个样子
震惊!fastjson SerializerFeature详解竟然是这个样子
230 0
震惊!fastjson SerializerFeature详解竟然是这个样子
|
JSON Java 编译器
你有没有掉进去过这些 Lombok 的 “陷阱“
你有没有掉进去过这些 Lombok 的 “陷阱“
你有没有掉进去过这些 Lombok 的 “陷阱“
|
安全 JavaScript Dubbo
Fastjson再曝反序列化漏洞,网友:Bugson又来了!
Fastjson再曝反序列化漏洞,网友:Bugson又来了!
|
JSON fastjson 数据格式
FastJson 我大意了,我没有闪
FastJson 我大意了,我没有闪
174 0
FastJson 我大意了,我没有闪
|
SQL XML 安全
本以为用的MyBatis框架就万无一失了,没想到还是被黑客注入了,我真的无语了!
本以为用的MyBatis框架就万无一失了,没想到还是被黑客注入了,我真的无语了!
89 0
本以为用的MyBatis框架就万无一失了,没想到还是被黑客注入了,我真的无语了!