T09 · Insecure Skill Coding Practices
- Location
scripts/password_gen.py:66- Finding
Predictable Shared Temporary Cache Allows Passphrase Word-List Poisoning and Symlink Attacks
- Content
View full analysis
Vulnerability Details
File Location:
scripts/password_gen.py, lines 66-86
Vulnerability Type: Unsafe temporary-file handling
Risk Level: MediumVulnerable Code
python EFF_CACHE_PATH = os.path.join(tempfile.gettempdir(), "eff_large_wordlist.txt") def load_eff_wordlist(): """Download and cache the EFF large word list. Falls back to built-in list.""" if os.path.exists(EFF_CACHE_PATH): words = [] with open(EFF_CACHE_PATH, "r") as f: for line in f: parts = line.strip().split("\t") if len(parts) == 2: words.append(parts[1]) if len(words) > 1000: return words try: urllib.request.urlretrieve(EFF_WORDLIST_URL, EFF_CACHE_PATH) return load_eff_wordlist() except Exception: return BUILTIN_WORDSTechnical Analysis
The passphrase generator stores and reads its word-list cache using the fixed filename
/tmp/eff_large_wordlist.txton typical Unix-like systems. A shared temporary directory is generally writable by multiple local users, but the implementation does not verify the file's owner, permissions, type, or cryptographic integrity.Any existing file is trusted if it contains more than 1,000 tab-separated records. An attacker can therefore create a syntactically valid but predictable word list before the victim runs the program. The list may contain repeated or attacker-selected words, while the program still reports entropy based on the number of records:
python entropy = words * math.log2(len(wordlist))Consequently, a malicious list containing duplicate entries can produce highly predictable passphrases while causing the program to report misleadingly high entropy.
The download path also does not protect against symbolic links or use exclusive, atomic file creation. If an attacker places a symbolic link at the cache path, ` ...[truncated 1837 chars]
- Remediation
View remediation
Remediation Suggestions
- Store the cache in a private per-user cache directory, such as
$XDG_CACHE_HOME/password-gen, rather than a shared temporary directory. - Create the directory with permissions that prevent access by other users, such as mode
0700. - Reject symbolic links and non-regular files. Where supported, open files with
O_NOFOLLOW. - Verify that an existing cache is owned by the current user and is not group- or world-writable.
- Download into a securely created temporary file in the private cache directory and atomically rename it after validation.
- Verify the downloaded word list against a pinned SHA-256 digest or another trusted integrity value before accepting it.
- Validate uniqueness and expected content structure rather than checking only that more than 1,000 records were parsed.
- Calculate entropy using the number of unique possible words, not the raw number of list entries.
- Consider making network retrieval explicit and opt-in, with the bundled list used by default.
- Store the cache in a private per-user cache directory, such as
