T05 · Unauthorized Access and Privilege Escalation
Error
- Location
- index.ts:70
- Finding
- Path Traversal Through Unvalidated Session Identifiers## Vulnerability Details **File Location**: `index.ts:70-91` **Vulnerability Type**: Path traversal and unauthorized filesystem access **Risk Level**: High ### Vulnerable Code ```ts const updatesPath = path.join(SESSIONS_PATH, `${sessionId}.updates`); if (!fs.existsSync(updatesPath)) return { success: false, error: "Session updates log not found" }; const binaryUpdate = Buffer.from(updatePayload, 'base64'); fs.appendFileSync(updatesPath, binaryUpdate); ``` ```ts async function loadTeam(params: any) { const { sessionId } = params; const snapshotPath = path.join(SESSIONS_PATH, `${sessionId}.snapshot`); const updatesPath = path.join(SESSIONS_PATH, `${sessionId}.updates`); ``` ### Technical Analysis The caller-controlled `sessionId` is inserted into filesystem paths without validation or canonical containment checks. `path.join()` normalizes traversal segments but does not guarantee that the resulting path remains under `SESSIONS_PATH`. A value containing components such as `../` can therefore escape `data/sessions`. The `team.sync` action can append data to an existing attacker-selected path whose final name ends in `.updates`. The `team.load` action can attempt to read attacker-selected `.snapshot` and `.updates` files outside the session directory. The filename suffixes constrain the set of reachable files, but they do not prevent directory traversal or access outside the intended storage boundary. ### Attack Path 1. The attacker identifies an existing file outside `data/sessions` ending in `.updates`, or places a compatible `.snapshot` file in another accessible directory. 2. The attacker supplies a traversal identifier such as `../../target` as `sessionId`. 3. `path.join()` resolves the resulting path outside `SESSIONS_PATH`. 4. For `team.sync`, the attacker submits a signed payload and the application appends it to `target.updates`. 5. For `team.load`, the application reads `target.snapshot` ...[truncated 762 chars]
- Remediation
- ## Remediation Suggestions - Validate `sessionId` against the exact server-generated format, for example `^session-[0-9]+-[a-z0-9]{5}$`. - Reject path separators, `.` components, encoded traversal sequences, null bytes, and unexpected characters. - Resolve the final path and verify containment before every filesystem operation: ```ts function sessionFile(sessionId: string, extension: string): string { if (!/^session-[0-9]+-[a-z0-9]{5}$/.test(sessionId)) { throw new Error("Invalid session ID"); } const root = path.resolve(SESSIONS_PATH); const candidate = path.resolve(root, `${sessionId}.${extension}`); if (!candidate.startsWith(root + path.sep)) { throw new Error("Session path escapes storage directory"); } return candidate; } ``` - Open files using restrictive flags and permissions where appropriate. - Run the service under a dedicated, least-privileged operating-system account. - Add tests covering `../`, absolute paths, repeated separators, encoded traversal attempts, and platform-specific separators.
