T05 · Unauthorized Access and Privilege Escalation
Error
- Location
- __init__.py:105
- Finding
- Unscoped Audio Fallback Can Transcribe Another User's Recording## Vulnerability Details **File Location**: `__init__.py`, lines 105-124 and 177-179 **Vulnerability Type**: Missing resource ownership and request-context validation **Risk Level**: High ### Vulnerable Code ```python def find_voice_files(media_dir: str = None): """Find the newest voice file.""" if media_dir is None: media_dir = os.path.join( os.path.expanduser('~'), '.openclaw', 'media', 'inbound' ) if not os.path.exists(media_dir): return None ogg_files = list(Path(media_dir).glob('*.ogg')) if not ogg_files: return None latest = max(ogg_files, key=lambda f: f.stat().st_mtime) return str(latest) ``` ```python if not voice_file: voice_file = find_voice_files() ``` ### Technical Analysis When the current request has no recognized audio attachment, the skill searches a shared inbound media directory and selects the most recently modified `.ogg` file. It does not verify that the selected file belongs to the current user, conversation, tenant, or request. File recency is not a security boundary. In a shared or concurrent deployment, the newest file may have been uploaded by another user. The selected recording is passed to the transcription function, and the resulting transcript and source path are returned to the caller. ### Attack Path 1. A victim uploads an `.ogg` voice recording, causing it to be stored under `~/.openclaw/media/inbound`. 2. An attacker invokes the skill without supplying an audio attachment. 3. The attachment lookup leaves `voice_file` unset. 4. The fallback calls `find_voice_files()`. 5. The function selects the globally newest `.ogg` file, potentially the victim's recording. 6. The skill transcribes that recording and returns its contents and filesystem path to the attacker. ### Impact Assessment An unauthenticated or lower-privileged caller who ...[truncated 324 chars]
- Remediation
- ## Remediation Suggestions - Remove the global newest-file fallback from the request-facing `main` function. - Require an attachment explicitly associated with the current authenticated request. - Validate attachment ownership, tenant, conversation, and message identifiers before accessing the file. - Resolve the attachment path with `Path.resolve()` and verify that it remains inside a per-request or per-tenant media directory. - Reject symbolic links and files not created or registered by the trusted attachment subsystem. - Return an error when no request-bound recording is available rather than searching a shared directory. - Avoid returning internal filesystem paths to callers unless they are explicitly required and authorized.
