GHSA-75jv-vfxf-3865: Assemblyline 4 service client vulnerable to Arbitrary Write through path traversal in Client code

Severity: Critical

CVSS Score: 10

**Path-Traversal -> Arbitrary File Write in Assemblyline Service Client** --- ## 1. Summary The Assemblyline 4 **service client** (`task_handler.py`) accepts a SHA-256 value returned by the service **server** and uses it directly as a local file name. > No validation / sanitisation is performed. A **malicious or compromised server** (or any MITM that can speak to client) can return a path-traversal payload such as `../../../etc/cron.d/evil` and force the client to write the downloaded bytes to an arbitrary location on disk. --- ## 2. Affected Versions | Item | Value | |---|---| | **Component** | `assemblyline-service-client` | | **Repository** | [CybercentreCanada/assemblyline-service-client](https://github.com/CybercentreCanada/assemblyline-service-client) | | **Affected** | **All releases up to master branch.** | --- ## 4. Technical Details | Field | Content | |---|---| | **Location** | `assemblyline_service_client/task_handler.py`, inside `download_file()` | | **Vulnerable Line** | `file_path = os.path.join(self.tasking_dir, sha256)` | | **Root Cause** | The `sha256` string is taken directly from the service-server JSON response and used as a file name without any validation or sanitisation. | | **Exploit Flow** | 1. Attacker (service server) returns HTTP 200 for `GET /api/v1/file/../../../etc/cron.d/evil`.<br>2. Client writes the response body to `/etc/cron.d/evil`.<br>3. Achieves arbitrary file write (code execution if file is executable). | --- ## 5. Impact - **Integrity** – Overwrite any file writable by the service UID (often root). - **Availability** – Corrupt critical files or exhaust disk space. - **Code Execution** – Drop cron jobs, systemd units, or overwrite binaries. --- ## 6. Mitigation / Fix ```python import re _SHA256_RE = re.compile(r'^[0-9a-fA-F]{64}\Z') def download_file(self, sha256: str, sid: str) -> Optional[str]: if not _SHA256_RE.fullmatch(sha256): self.log.error(f"[{sid}] Invalid SHA256: {sha256}") self.status = STATUSES.ERROR_FOUND return None # or your preferred way to check if a string is a shasum. ``` ---