T05 · Unauthorized Access and Privilege Escalation
- Location
scripts/get_timeline.py:10- Finding
Unvalidated User Identifier Enables Cross-User Credential Access and Path Traversal
- Content
View full analysis
Vulnerability Details
File Location:
scripts/get_timeline.py:10-16; the same credential-loading pattern appears inscripts/get_user_tweets.py:10-16,scripts/like_tweet.py:10-16,scripts/post_tweet.py:14-21,scripts/reply_tweet.py:10-16,scripts/retweet.py:10-16, andscripts/search_tweets.py:11-17
Vulnerability Type: Path traversal and broken tenant authorization
Risk Level: HighVulnerable Code
python def load_credentials(user_id): cred_path = os.path.join(SKILL_DIR, 'credentials', f'{user_id}.json') if not os.path.exists(cred_path): raise Exception(f"No credentials found for user {user_id}. Configure Twitter credentials first.") with open(cred_path, 'r') as f: creds = json.load(f) if 'twitter' not in creds: raise Exception("Twitter credentials not found") return creds['twitter']The identifier is supplied directly through a command-line argument:
python parser.add_argument('user_id')Technical Analysis
Every script constructs a credential path by interpolating the caller-controlled
user_idinto a filename. The value is not restricted to a canonical identifier format, resolved against a trusted root, or checked against the identity of the authenticated caller.This creates two related authorization weaknesses:
- A caller who knows or guesses another tenant's identifier can select that tenant's credential file directly.
- A value containing directory traversal components, such as
../../other/location/account, can cause the resolved path to leave the intendedcredentialsdirectory and access another JSON file whose name ends in.json.
Merely checking whether the resulting path exists does not ensure that it remains inside the credential directory. The scripts then use the selected credentials for X/Twitter API requests. Consequently, exploitation does not require the secret values to be printed: the ...[truncated 1500 chars]
- Remediation
View remediation
Remediation Suggestions
- Validate
user_idagainst a strict allowlist before using it in a path, for example^[A-Za-z0-9_-]+$, and reject empty, absolute, dotted, or separator-containing values. - Resolve the candidate path with
pathlib.Path.resolve()and verify that it is a descendant of the resolved credential directory before opening it. - Do not treat a caller-provided user identifier as authorization. Derive the credential owner from an authenticated execution context or verify that the caller is authorized to use the requested account.
- Open credential files defensively and reject symbolic links where supported.
- Centralize credential loading in one hardened module instead of duplicating the vulnerable implementation across all scripts.
- Record security-relevant access attempts without logging credentials or tokens.
- Add tests covering traversal values, absolute paths, symbolic links, encoded separators, and attempts to select another tenant's identifier.
Example containment validation:
python import re from pathlib import Path USER_ID_PATTERN = re.compile(r"^[A-Za-z0-9_-]+$") def credential_path(user_id): if not USER_ID_PATTERN.fullmatch(user_id): raise ValueError("Invalid user identifier") credential_root = (Path(SKILL_DIR) / "credentials").resolve() candidate = (credential_root / f"{user_id}.json").resolve() if candidate.parent != credential_root: raise ValueError("Credential path escapes the credential directory") return candidate- Validate
