T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/gphotos.py:22
- Finding
- Unsafe Deserialization of OAuth Token Cache Enables Arbitrary Code Execution## Vulnerability Details **File Location**: `scripts/gphotos.py`, lines 22–24 **Vulnerability Type**: Unsafe Python pickle deserialization **Risk Level**: High ```python if os.path.exists(token_path): with open(token_path, 'rb') as token: creds = pickle.load(token) ``` ### Technical Analysis The application deserializes the user-selected token file with `pickle.load()`. Python pickle data is executable: specially constructed objects can invoke arbitrary callables through reduction operations while they are being deserialized. The `token_path` value is controlled through the documented `--token` command-line option. The file is deserialized before the resulting credentials are validated. Therefore, supplying or replacing a token file with a malicious pickle payload can execute arbitrary Python code as soon as any action invokes `get_credentials()`. This issue does not require the malicious file to contain valid Google credentials. ### Attack Path 1. An attacker creates a malicious pickle whose deserialization routine runs a command or Python callable. 2. The attacker convinces the user to use that file as the token cache, or replaces a token file in a writable or shared location. 3. The user invokes a documented command such as: ```bash ./scripts/gphotos.py --action list \ --credentials /path/to/credentials.json \ --token /path/to/malicious-token.pickle ``` 4. `get_credentials()` finds the file and passes it to `pickle.load()`. 5. The embedded payload executes before credential validity is checked. ### Impact Assessment Successful exploitation provides arbitrary code execution with the privileges of the user running the Skill. The attacker could read or alter files accessible to that user, steal Google OAuth credentials and refresh tokens, access other local secrets, execute additional programs, or perform network operations under the user's identity.
- Remediation
- ## Remediation Suggestions Replace pickle with a non-executable serialization format supported by the Google authentication library: ```python if os.path.exists(token_path): creds = Credentials.from_authorized_user_file(token_path, SCOPES) # After obtaining or refreshing credentials: with open(token_path, "w", encoding="utf-8") as token: token.write(creds.to_json()) ``` Additionally: - Validate the parsed credential fields and expected OAuth scopes. - Require the token file to be owned by the current user. - Reject symbolic links and non-regular files. - Store tokens in a private application directory rather than accepting untrusted token files. - If arbitrary token paths remain supported, clearly treat their contents as untrusted input.
