T09 · Insecure Skill Coding Practices
Error
- Location
- references/advanced_features.md:115
- Finding
- OBS Object Keys Can Escape the Intended Download Directory<![CDATA[ ## Vulnerability Details **File Location**: `references/advanced_features.md:115-125` **Vulnerability Type**: Path traversal leading to arbitrary local file overwrite **Risk Level**: High ### Vulnerable Code ```python object_key = obj['key'] # Calculate the local file path relative_path = object_key[len(obs_prefix):] if object_key.startswith(obs_prefix) else object_key relative_path = relative_path.lstrip('/') local_file_path = os.path.join(local_folder_path, relative_path) # Ensure that the local directory exists os.makedirs(os.path.dirname(local_file_path), exist_ok=True) print(f"Downloading: {object_key} -> {local_file_path}") if download_file(obs_client, bucket_name, object_key, local_file_path): ``` ### Technical Analysis The folder-download implementation derives a local destination directly from an OBS object key. Object keys are remote data and may be controlled by any principal with permission to create or rename objects in the selected bucket. Removing leading `/` characters does not remove `..` path components. Consequently, a key such as `shared/../../app/config.py`, when processed with `obs_prefix='shared/'`, produces the relative path `../../app/config.py`. Passing that value to `os.path.join()` allows the resulting path to resolve outside `local_folder_path`. The implementation then creates the destination's parent directories and calls the download function without verifying that the canonical destination remains inside the intended download root. It also does not require confirmation before overwriting an existing destination. ### Attack Path 1. An attacker obtains permission to upload or rename objects under a bucket or prefix processed by the victim. 2. The attacker creates an object with a traversal key, such as `shared/../../.ssh/authorized_keys`. 3. The victim calls `download_folder()` with `obs_prefix='shared/'` and a local download directory. 4. Prefix removal produces `../../.ssh/authorized_keys`. 5. `os.path.join() ...[truncated 829 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions Treat every object key as untrusted input and verify the canonical destination before creating directories or downloading: ```python root = os.path.realpath(local_folder_path) destination = os.path.realpath(os.path.join(root, relative_path)) if os.path.commonpath([root, destination]) != root: raise ValueError(f"Unsafe object key: {object_key}") ``` Additionally: 1. Reject absolute paths and any key containing `..` path components. 2. Account for both POSIX and Windows path separators and drive-qualified paths. 3. Normalize the prefix and require every downloaded object to match it. 4. Avoid following symlinks within the destination tree, or use directory-relative file APIs with no-follow protections where available. 5. Refuse to overwrite existing files by default; require explicit caller authorization. 6. Download to a safely created temporary file inside the destination directory and atomically rename it after successful validation. 7. Add tests covering `../`, repeated traversal, absolute paths, Windows drive paths, mixed separators, encoded-looking names, and symlink escape scenarios. ]]>
