T05 · Unauthorized Access and Privilege Escalation
- Location
scripts/track.py:20- Finding
Path Traversal Through Unvalidated Session Identifier
- Content
View full analysis
list: """Load session history.""" ensure_track_dir() path = os.path.join(TRACK_DIR, f"{session_id}.json") if os.path.exists(path): with open(path, "r", encoding="utf-8") as f: return json.load(f) return [] def save_session(session_id: str, history: list): """Save session history.""" ensure_track_dir() path = os.path.join(TRACK_DIR, f"{session_id}.json") with open(path, "w", encoding="utf-8") as f: json.dump(history, f, ensure_ascii=False, indent=2) ``` ### Technical Analysis The externally supplied `session_id` is inserted directly into a filesystem path without validation, normalization, or containment checks: ```python os.path.join(TRACK_DIR, f"{session_id}.json") ``` An identifier containing parent-directory components such as `../../target` causes the resulting path to escape `/tmp/sentiment_tracking`. Both `load_session()` and `save_session()` use the unsafe path, providing read and write primitives for JSON files accessible to the process. The automatically appended `.json` suffix restricts targets to paths ending in `.json`, but it does not prevent traversal. For successful history processing, an existing target must contain a JSON list because the program subsequently invokes `history.append(...)`. An attacker can also create a new JSON file when its parent directory exists and is writable by the process. No canonical path comparison is performed, and regular `open()` calls follow symbolic links. ### Attack Path 1. The attacker supplies a malicious session identifier through the command-line interface: ```bash python3 scripts/track.py --session-id "../../target" "attacker-controlled message" ``` 2. The program constructs: ...[truncated 1363 chars]- Remediation
View remediation
str: if not SESSION_ID_PATTERN.fullmatch(session_id): raise ValueError("Invalid session identifier") return session_id ``` 2. Prefer deriving filenames from a cryptographic hash rather than using identifiers directly: ```python import hashlib filename = hashlib.sha256(session_id.encode("utf-8")).hexdigest() + ".json" ``` 3. Resolve and verify the final path before access: ```python base = os.path.realpath(TRACK_DIR) path = os.path.realpath(os.path.join(base, filename)) if os.path.commonpath([base, path]) != base: raise ValueError("Session path escapes tracking directory") ``` 4. Reject or safely handle symbolic links. On supported platforms, open files with no-follow semantics such as `os.O_NOFOLLOW`. 5. Create files atomically and avoid time-of-check/time-of-use sequences involving `exists()` followed by `open()`. 6. Validate loaded JSON data before use, including confirming that the root value is a list and that every entry has the expected schema. 7. Run the tracker under a dedicated, minimally privileged operating-system account so that any remaining filesystem flaw has limited scope. ]]>
