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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
|
# P7 Spec, Framework Contract & Parser Gap Summary
**Target:** salmon/minecraft-ui
**Date:** 2025-07-29
**Phase:** P7 — Specification, Framework Contract & Parser Differential Analysis
**Mode:** deep
---
## Overview
This report documents gaps between authoritative specifications, framework contracts, and the codebase implementation for salmon/minecraft-ui. The analysis covers:
- **RFC 9106** (Argon2) — password hashing parameter compliance
- **Java `.properties` format** (Oracle spec) — server.properties parsing
- **Source RCON Protocol** (Minecraft community spec — wiki.vg/RCON) — packet handling
- **RFC 6265** (HTTP State Management) — cookie parsing and session handling
- **Axum Framework Contracts** — middleware ordering, extension-based auth, multipart boundary parsing
- **Hidden Control Channels** — Docker environment injection, file path construction, host header trust
- **Deployment Assumptions** — reverse proxy, localhost binding, no TLS
---
## Spec Gap Candidates
### Gap 1: Java `.properties` — Missing Escape Sequence Processing
- **Spec:** Oracle Java `.properties` file format
- **Requirement:** The format specifies escape sequences: `\n`, `\r`, `\t`, `\\`, `\uXXXX`, `\ ` (literal space), `\!`, `\#`, `\=`, `\:`, and multi-line continuation with trailing `\`.
- **Code Path:** `src/properties.rs:13-19` — `read_properties()` splits on `=` or `:` without escaping
- **Gap Type:** parsing | canonicalization
- **Impact:** Values containing `=`, `:`, or Java escape sequences in `server.properties` are misparsed. An attacker who can control `server.properties` values (e.g., via `level-seed` setting) can inject fake key-value pairs or corrupt parsing. The `level-seed` value in `server.properties` is written by `regenerate_world` via a line-replacement loop that creates `level-seed={seed}` — the seed value is not escaped for the `.properties` format.
- **Attack Vector:** Auth'd user sets a seed containing `\nlevel-type=FLAT\n` which, when written to `server.properties` via the naive line-replacement in `regenerate_world`, injects additional properties. On next server start, the injected properties alter server behavior.
- **Exploit Conditions:** Auth'd user; instance not active; access to `regenerate_world` handler.
- **Severity:** MEDIUM
- **Evidence:** `src/properties.rs:13-19` uses `split_once('=')` and `split_once(':')` with no escape processing. Java `.properties` spec requires backslash escaping for separators.
### Gap 2: Argon2id (RFC 9106) — Non-Recommended Default Parameters
- **RFC/Spec:** RFC 9106, Sections 3.1, 4, 7.4
- **Requirement:** RFC 9106 §4 recommends as FIRST option: `Argon2id` with `t=1`, `p=4`, `m=2^21` (2 GiB). §7.4 states "Argon2id variant with t=1 and 2 GiB memory is the FIRST RECOMMENDED option". Salt SHOULD be 16 bytes and SHOULD be unique per password.
- **Code Path:** `src/auth.rs:28` — `Argon2::default().hash_password(password.as_bytes(), &salt)?`
- **Gap Type:** missing-check | parameter-choice
- **Impact:** `Argon2::default()` uses m_cost=19456 KiB (~19 MiB), t_cost=2 iterations, p_cost=1 lane. This is ~100x less memory than RFC 9106 recommends. While not a compliance vulnerability (any parameters are valid per the algorithm spec), the weak defaults reduce brute-force attack resistance relative to the RFC's recommended "default setting for all environments."
- **Attack Vector:** Offline password hash cracking benefits from the 19 MiB vs 2 GiB memory reduction (~100x cheaper per guess).
- **Exploit Conditions:** Attacker obtains password hash from database (SQLite file access or SQL injection).
- **Impact:** Accelerated offline brute-force by ~100x less memory per hash evaluation.
- **Severity:** MEDIUM
- **Evidence:** `src/auth.rs:28` uses `Argon2::default()` (m=19456 KiB, t=2, p=1). RFC 9106 §4 recommends m=2^21 KiB (2 GiB), t=1, p=4 for pass hashing. Source: `/home/salmon/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/argon2-0.5.3/src/params.rs:29` defines DEFAULT_M_COST = 19 * 1024.
### Gap 3: RCON Protocol — Insufficient Response Read After EXECCOMMAND
- **Spec:** Source RCON Protocol (Minecraft community convention — wiki.vg/RCON)
- **Requirement:** After sending `SERVERDATA_EXECCOMMAND` (type 2), the server responds with a `SERVERDATA_RESPONSE_VALUE` (type 0) packet containing the command output, followed by a second `SERVERDATA_RESPONSE_VALUE` (type 0) as an end marker. Multi-packet responses are possible.
- **Code Path:** `src/rcon.rs:72-76` — reads one response, then ignores errors on second
- **Gap Type:** state-machine | error-handling
- **Impact:** Reads only one guaranteed response. Fragmented multi-packet responses are truncated. The comment acknowledges this: "some servers send it, some don't; ignore errors."
- **Attack Vector:** Long RCON command output (> 1400 bytes) gets truncated.
- **Exploit Conditions:** Any RCON command with output exceeding ~1400 bytes.
- **Impact:** Truncated command response (information disclosure).
- **Severity:** LOW — protocol compliance only, no direct security bypass.
- **Evidence:** `src/rcon.rs:72` — `let response = self.read_packet(&mut stream)?;` followed by `let _ = self.read_packet(&mut stream);` with error ignored.
### Gap 4: Java `.properties` — Key Continuation and Whitespace Separator Not Supported
- **Spec:** Oracle Java `.properties` format
- **Code Path:** `src/properties.rs:13-19`
- **Gap Type:** parsing
- **Impact:** Lines with whitespace-only separators (e.g., `key value`) are silently ignored.
- **Severity:** LOW — informational; conventional Minecraft server.properties uses `=`.
### Gap 5: Auth Middleware — Manual Cookie Parsing (RFC 6265)
- **RFC/Spec:** RFC 6265 (HTTP State Management), Sections 4.2.2, 5
- **Code Path:** `src/auth.rs:105-118` — manual `split(';')` then `split_once('=')`
- **Gap Type:** parsing | canonicalization
- **Impact:** The parser does NOT strip OWS around cookie names per RFC 6265 §4.2.2. Does NOT handle quoted cookie values.
- **Severity:** LOW — well-formed browser cookies not affected; no auth bypass possible.
### Gap 6: Framework Contract — Logout Without Auth Middleware
- **Contract:** Axum middleware; all state-changing ops require auth.
- **Code Path:** `src/routes/mod.rs:29` — `/logout` on outer router, not in `protected` group.
- **Gap Type:** middleware-ordering
- **Impact:** Anonymous requests can delete sessions (requires valid session ID — UUIDv4 impractical).
- **Severity:** LOW — theoretical; UUIDv4 unpredictability prevents exploitation.
### Gap 7: Framework Contract — No Server-Side Session TTL Enforcement
- **Contract:** Session cookie Max-Age=86400 but no server-side expiration check.
- **Code Path:** `src/auth.rs:93` — `Max-Age=86400`; `src/auth.rs:108-115` — no `created_at` check.
- **Gap Type:** missing-check
- **Impact:** Session rows remain valid indefinitely in DB. Stolen session ID (via XSS, log access, DB leak) works forever.
- **Attack Vector:** Session ID leak → permanent session hijacking. No server-side TTL means an attacker who obtains a session ID can reuse it indefinitely, long after the Max-Age expires.
- **Exploit Conditions:** Attacker must first obtain a valid session ID.
- **Impact:** Session hijacking window extended indefinitely.
- **Severity:** MEDIUM
- **Evidence:** `src/auth.rs:93` sets `Max-Age=86400` but `src/auth.rs:108-115` does `WHERE s.id = ?1` with no date filter.
### Gap 8: Hidden Control Channel — Instance MOTD Injected Into Docker Env Without Sanitization
- **Contract:** Docker env vars derived from instance settings; Docker env parsing splits on `\n`.
- **Code Path:** `src/docker.rs:103` — `format!("MOTD={}", instance.motd)`
- **Gap Type:** hidden-control-channel | missing-check
- **Impact:** MOTD containing newline characters (`%0a`) injects arbitrary additional env vars into the Docker container. For example, MOTD=`hello\nEULA=FALSE\nONLINE_MODE=true` would override security-critical env vars. All Docker env vars (VERSION, TYPE, MEMORY, MAX_PLAYERS, DIFFICULTY, RCON_PASSWORD, GAMEMODE, LEVEL_TYPE, LEVEL_SEED, PVP, MOTD) are set without sanitization.
- **Attack Vector:** Auth'd user edits instance MOTD → saves → triggers container restart → injected env vars alter Docker container behavior.
- **Exploit Conditions:** Auth'd user; container restarted after instance edit.
- **Impact:** Full Docker container environment variable injection — could override EULA, ONLINE_MODE, RCON settings, or inject arbitrary env vars.
- **Severity:** HIGH
- **Evidence:** `src/docker.rs:103` — `format!("MOTD={}", instance.motd)`. No escaping. MOTD sourced from `src/routes/instances.rs:206` via `Form<EditInstanceForm>` without sanitization.
### Gap 9: Hidden Control Channel — Instance Name Used in File Paths Without Sanitization
- **Contract:** Instance name → filesystem path.
- **Code Path:** `src/routes/instances.rs:173` — `server_directory.join("instances").join(&form.name)`
- **Gap Type:** hidden-control-channel | missing-check
- **Impact:** `std::path::Path::join` DOES traverse on `..` prefix. Instance name `../../evil` results in path outside intended tree. All subsequent operations (mod uploads, world import, backups) land at attacker-chosen locations. Files created: instance data directory, mod JAR files, world files.
- **Attack Vector:** Auth'd user creates instance with `../../evil_name` → data dir created at `server_directory/instances/../../evil_name/data` = `server_directory/evil_name/data`. Then upload mod or import world to write files at traversed location.
- **Exploit Conditions:** Auth'd user.
- **Impact:** Arbitrary file write relative to server_directory (WebUI process user permissions).
- **Severity:** HIGH
- **Evidence:** `src/routes/instances.rs:173` — `let instance_dir = state.config.server_directory.join("instances").join(&form.name).join("data");` No validation of `form.name` for `..` or path separators. Also stored in DB at `src/routes/instances.rs:119` without sanitization.
### Gap 10: Hidden Control Channel — Content-Type Boundary Parsing Trusts Attacker Input
- **Contract:** Multer multipart parser; boundary from Content-Type header.
- **Code Path:** `src/routes/mods.rs:140-149` — boundary parsed without validation
- **Gap Type:** hidden-control-channel | missing-check
- **Impact:** Attacker-controlled boundary value fed to `multer::Multipart::with_constraints`. While multer validates boundaries internally, a crafted boundary could cause field misidentification, size limit bypass, or field-skipping behavior.
- **Attack Vector:** Send crafted Content-Type header with boundary overlapping body content: malformed multipart parsing.
- **Exploit Conditions:** Auth'd user uploading mods.
- **Impact:** Potential DoS or file size limit bypass.
- **Severity:** MEDIUM
- **Evidence:** `src/routes/mods.rs:141-148` — boundary parsed from user-supplied `Content-Type`.
### Gap 11: Hidden Control Channel — All Instance Settings In Docker Env Without Sanitization
- **Contract:** All instance settings → Docker env.
- **Code Path:** `src/docker.rs:87-113`
- **Gap Type:** hidden-control-channel | missing-check
- **Impact:** Beyond MOTD (Gap 8), LEVEL_SEED, LEVEL_TYPE, GAMEMODE, DIFFICULTY, VERSION, RCON_PASSWORD are all injected without sanitization.
- **Severity:** HIGH — combined with Gap 8.
### Gap 12: Framework Contract — RCON Password Exposed to All Authenticated Users
- **Contract:** Supposed role-based access control for sensitive instance data.
- **Code Path:** `src/routes/instances.rs:313` — `rcon_password: row.get(15)?` in `edit_form`
- **Gap Type:** missing-check
- **Impact:** Any authenticated user can view any instance's RCON password by visiting `/instances/{id}/edit`. No superuser or instance-ownership check. RCON password grants full Minecraft server control.
- **Attack Vector:** Auth'd user navigates to `/instances/1/edit` → sees RCON password → uses RCON client to issue arbitrary commands.
- **Exploit Conditions:** Auth'd user.
- **Impact:** Full Minecraft server compromise via RCON — whitelist bypass, data destruction, command execution.
- **Severity:** HIGH
- **Evidence:** `src/routes/instances.rs:313` — `rcon_password` selected and returned. No superuser check. Compare with `src/routes/accounts.rs:35` where checks exist.
---
## Cross-Reference with Phase 3 Domain Attack Research
- **Gap 8/11** (Docker env injection) — NOT covered in Phase 3. New finding.
- **Gap 9** (Instance name path traversal) — NOT specifically covered. Phase 3 identified mod filename traversal (T2) but not instance name as traversal vector.
- **Gap 12** (RCON password exposure) — Covered in Phase 3 as T5.
- **Gap 7** (No session TTL) — Covered in Phase 3 domain research.
- **Gap 2** (Argon2 defaults) — NOT covered in Phase 3.
|