T09 · Insecure Skill Coding Practices
Error
- Location
- modules/email_fetcher.py:18
- Finding
- Arbitrary File Write Through an Untrusted Email Attachment Filename<![CDATA[ ## Vulnerability Details **File Location**: `modules/email_fetcher.py:18-28` **Vulnerability Type**: Path traversal and arbitrary file write **Risk Level**: High ### Vulnerable Code ```python for part in msg.walk(): if part.get_content_disposition() == 'attachment': filename = part.get_filename() if filename and filename.endswith(".zip"): path = f"data/downloads/{filename}" with open(path, 'wb') as f: f.write(part.get_payload(decode=True)) file_paths.append(path) ``` ### Technical Analysis The MIME attachment filename is controlled by the email sender and is concatenated directly into a local filesystem path. The implementation does not reduce the value to a basename, reject absolute paths, normalize traversal components, or verify that the resolved destination remains inside `data/downloads`. Consequently, a filename containing path separators or traversal components can cause the attachment to be written outside the intended download directory. The `.zip` suffix check does not prevent path traversal. ### Attack Path 1. An attacker sends an unread email to the monitored mailbox. 2. The email contains an attachment whose MIME filename includes traversal components and ends in `.zip`. 3. `fetch_attachments` concatenates the filename with `data/downloads/`. 4. The process opens the resulting path without a containment check. 5. If the destination is writable, the attachment overwrites or creates a file outside the download directory. ### Impact Assessment The attacker can write files with the privileges of the Skill process. The practical scope includes application data, configuration, or source files writable by that account. Overwriting a Python module or another subsequently loaded file could potentially lead to code execution during a later process invocation. ]]>
- Remediation
- <![CDATA[ ## Remediation Suggestions - Decode the MIME filename safely and reduce it to a basename using `pathlib.Path(filename).name`. - Reject absolute paths, parent-directory components, path separators, NUL characters, and empty filenames. - Resolve both the download root and destination, then verify that the destination is contained within the download root. - Generate a server-controlled random filename rather than trusting the sender's filename. - Create the download directory explicitly with restrictive permissions. - Prevent unintended replacement by opening new files in exclusive-creation mode where appropriate. ]]>
