T09 · Insecure Skill Coding Practices
- Location
scripts/utils.py:13- Finding
OAuth access and refresh tokens are stored without restrictive filesystem permissions
- Content
View full analysis
None: with AUTH_FILE.open("w", encoding="utf-8") as f: json.dump(data, f, indent=2) ``` ### Technical Analysis The authentication state contains Microsoft Graph access and refresh tokens. The code creates the state directory and authentication file using process-default permissions rather than explicitly enforcing owner-only access. Under a common `umask` of `022`, a newly created file can receive mode `0644`, making it readable by other local users. The containing directory may similarly be created with mode `0755`. A refresh token is particularly sensitive because it can be exchanged repeatedly for new access tokens until revoked. The implementation also writes directly to the final path instead of using a protected temporary file followed by an atomic replacement. It does not verify that the destination is owned by the expected user or reject symbolic links. ### Attack Path 1. A user completes the device-code login flow. 2. `save_auth_state()` writes the access token and refresh token to `state/graph_auth.json`. 3. The host has a permissive default `umask`, resulting in a file readable by another local account. 4. The local attacker reads the authentication file. 5. The attacker submits the refresh token to the Microsoft identity token endpoint using the recorded client and tenant identifiers. 6. The attacker receives a valid Graph access token and invokes APIs covered by the granted scopes. ### Impact Assessment A successful attacker can obtain the same delegated Microsoft Graph privileges as the authenticated user ...[truncated 491 chars]- Remediation
View remediation
None: temp_path = AUTH_FILE.with_suffix(".tmp") fd = os.open(temp_path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600) try: with os.fdopen(fd, "w", encoding="utf-8") as f: json.dump(data, f, indent=2) f.flush() os.fsync(f.fileno()) os.replace(temp_path, AUTH_FILE) os.chmod(AUTH_FILE, 0o600) finally: if temp_path.exists(): temp_path.unlink() ``` Existing installations should immediately change permissions on `state/` and `state/graph_auth.json` and rotate tokens if unauthorized local access may have occurred. ]]>
