Skip to content

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 文件的缓冲区。文件名通过网络包传入后未经任何校验直接用于构造文件路径。

核心漏洞代码 ​

java
// 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 字段,攻击者可随意控制。

调用链 ​

java
// 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() 中两个关键调用:

java
// SchematicBufferManager.java:127 — 先写入
Path file = buffer.writeFile(dir);

// SchematicBufferManager.java:135 — 后读取
LitematicaSchematic schematic = LitematicaSchematic.createFromFile(...)

即使第 135 行的二次读取失败(如 JSON 文件不是 NBT 格式),第 127 行的写入已经完成,文件已存在于磁盘上,不受后续读取失败的影响。

FileType 枚举 ​

java
// 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

攻击使用的数据包类型 ​

java
// 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行,不影响写入)

三、攻击链概述 ​

前置条件 ​

  1. 服务端运行 Fabric + Servux 0.11.1
  2. 任意客户端可连接(无需 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

适用目标 ​

目标文件类型FileTypeFileName 构造
.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 命令。

java
// 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.java46, 49, 99, 101, 115
SchematicBufferManager.java47, 78, 127, 135
LitematicaSchematic.java1196-1197, 1222-1228
ServuxLitematicaPacket.java109-137, 475-499
ServuxLitematicaHandler.java88-129, 131-150
FileType.java60-72
PacketSplitter.java36-61
SchematicPlacingUtils.java96-101, 467-512
EntityUtils.java84-155
NbtUtils.java274-276

免责声明: 本文档仅供安全研究和技术分析使用,不包含可直接用于攻击的完整利用代码。

原初服务器 - 公益 Minecraft 服务器