Servux SchematicBuffer 路径遍历漏洞 (CWE-22) 分析
- 漏洞版本: Servux 0.11.1 for Minecraft 26.2 (Fabric)
- 漏洞文件:
SchematicBuffer.java - 漏洞类型: 路径遍历 Path Traversal → 任意文件写入
- 影响范围: 任意连接玩家(无需 OP / 无需创造模式)
- 发布时间: 2026-07-07
一、漏洞定位与根因分析
SchematicBuffer.java 是 Servux 用于接收 Litematica 文件的缓冲区。文件名通过网络包传入后未经任何校验直接用于构造文件路径。
核心漏洞代码
// SchematicBuffer.java:46-58
public Path getFileName()
{
String ext = FileType.getFileExt(this.type);
if (this.name.contains(ext)) { // ← ★ 使用 contains() 而非 endsWith() !
return Path.of(this.name); // ← ★ 零过滤,../ 直接保留
} else {
return Path.of(this.name + ext);
}
}问题: this.name 来自客户端发送的 FileName 字段,攻击者可随意控制。
调用链
// SchematicBufferManager.java:127
Path file = buffer.writeFile(dir);
// → dir = run/schematics/
// → file = dir.resolve(this.getFileName())
// = dir/../ops.json (可被攻击者控制)
// SchematicBuffer.java:101-105 — 任意文件删除
if (Files.exists(file)) { Files.delete(file); }
// SchematicBuffer.java:115-121 — 任意文件写入
try (OutputStream os = Files.newOutputStream(file)) {
for (Slice entry : this.buffer) {
os.write(entry.data(), 0, entry.size()); // 数据由攻击者提供
}
}写入与二次读取的时序问题
finishBuffer() 中两个关键调用:
// SchematicBufferManager.java:127 — 先写入
Path file = buffer.writeFile(dir);
// SchematicBufferManager.java:135 — 后读取
LitematicaSchematic schematic = LitematicaSchematic.createFromFile(...)即使第 135 行的二次读取失败(如 JSON 文件不是 NBT 格式),第 127 行的写入已经完成,文件已存在于磁盘上,不受后续读取失败的影响。
FileType 枚举
// FileType.java:60-72
public static String getFileExt(FileType type) {
return switch (type) {
case LITEMATICA_SCHEMATIC -> ".litematic";
case SCHEMATICA_SCHEMATIC -> ".schematic";
case SPONGE_SCHEMATIC -> ".schem";
case VANILLA_STRUCTURE -> ".nbt";
case JSON -> ".json";
// ...
};
}| FileType 序列化名 | 扩展名 |
|---|---|
"JSON" | .json |
"litematic" | .litematic |
"schematic" | .schematic |
"sponge" | .schem |
"vanilla_nbt" | .nbt |
二、协议分析 (Litematica Transmit)
传输通道
- 通道标识符:
servux:litematics - 通信机制: Fabric
CustomPayload/ServerboundCustomPayloadPacket
攻击使用的数据包类型
// ServuxLitematicaPacket.java
PACKET_C2S_NBT_RESPONSE_DATA = 13 // 客户端→服务端的数据传输包PacketSplitter 分片协议
第一片: [VarInt totalSize][分片数据]
后续片: [分片数据]完整的传输数据在重组后为:
[VarInt transactionType][writeNbt(CompoundTag)]Task 路由
CompoundTag 中的 Task 字段决定处理路径(LitematicaSchematic.java:1194-1247):
"Litematic-TransmitStart"→ 创建文件传输缓冲区"Litematic-TransmitData"→ 接收数据切片"Litematic-TransmitEnd"→ 触发writeFile()将缓冲区写入磁盘"Litematic-TransmitCancel"→ 取消传输
服务端处理流程
PACKET_C2S_NBT_RESPONSE_DATA (13) 到达
→ PacketSplitter.receive() 重组分片
→ handleBulkData()
→ receiveFileTransmit(nbt, player)
├─ "TransmitStart" → createBuffer()
├─ "TransmitData" → receiveSlice()
└─ "TransmitEnd" → finishBuffer()
├─ writeFile(dir) ← 文件写入(第127行)
└─ createFromFile() ← 二次读取(第135行,不影响写入)三、攻击链概述
前置条件
- 服务端运行 Fabric + Servux 0.11.1
- 任意客户端可连接(无需 OP,无需创造模式)
攻击流程
Step 1: 构造含有路径遍历 FileName 的 TransmitStart 包
FileName = "../ops.json"
FileType = "JSON"
Step 2: 构造含有恶意内容的 TransmitData 包
Step 3: 构造 TransmitEnd 包触发 writeFile()
Step 4: writeFile() 将 ../ops.json 写入服务端目录
→ schematics/../ops.json → run/ops.json攻击者可通过控制 FileName 中的 ../ 层级和 FileType 对应的扩展名,将任意内容写入服务端的任意路径(扩展名受限)。
四、绕过技巧 (扩展名校验)
原理
contains() 扩展名校验可以被绕过。攻击者可以在文件名中构造包含目标扩展名的路径段,再用 ../ 解析到实际目标:
FileName = "foo.json/../../target.bat"contains(".json")→true(文件名中存在.json子串)Path.of("foo.json/../../target.bat")→ 不追加额外.json- 文件系统解析后将写入
target.bat
适用目标
| 目标文件类型 | FileType | FileName 构造 |
|---|---|---|
.bat 脚本 | "JSON" | foo.json/../../evil.bat |
.ps1 脚本 | "JSON" | a.json/../../../evil.ps1 |
.jar 文件 | "litematic" | x.litematic/../../mod.jar |
| 任意无扩展名文件 | "JSON" | y.json/../../../etc/cron.d/evil |
五、多场景利用
场景 A: 覆写 ops.json
攻击者将 FileName 设为 ../ops.json、FileType 设为 "JSON",写入包含自己 UUID 和 Level 4 的 ops.json 内容。服务端重启后自动获得 OP 权限。
场景 B: 覆写 whitelist.json
FileName = ../whitelist.json, FileType = "JSON",写入包含自己 UUID 的 whitelist 条目,绕过白名单限制(连接时即时生效)。
场景 C: pasteTo 实体注入
构造包含 CommandBlockMinecart 实体的 .litematic 文件,通过服务端的 pasteTo() 方法(SchematicPlacingUtils.java:467)在服务端生成实体并执行 MC 命令。
// SchematicPlacingUtils.java:467 — 从 NBT 创建实体
Entity entity = EntityUtils.createEntityAndPassengersFromNBT(tag, world);
// SchematicPlacingUtils.java:512 — 生成到世界
EntityUtils.spawnEntityAndPassengersInWorld(entity, world);场景 D: 写入可执行脚本(Windows)
配合扩展名校验绕过,将 FileName 构造为 x.json/../../../Users/Admin/Startup/evil.bat,写入 Windows 启动目录。
七、相关文件引用
(基于 Servux 0.11.1,Minecraft 26.2 Fabric)
| 文件 | 关键行号 |
|---|---|
SchematicBuffer.java | 46, 49, 99, 101, 115 |
SchematicBufferManager.java | 47, 78, 127, 135 |
LitematicaSchematic.java | 1196-1197, 1222-1228 |
ServuxLitematicaPacket.java | 109-137, 475-499 |
ServuxLitematicaHandler.java | 88-129, 131-150 |
FileType.java | 60-72 |
PacketSplitter.java | 36-61 |
SchematicPlacingUtils.java | 96-101, 467-512 |
EntityUtils.java | 84-155 |
NbtUtils.java | 274-276 |
免责声明: 本文档仅供安全研究和技术分析使用,不包含可直接用于攻击的完整利用代码。