Command Injection in yq via -o=shell -r
GHSA-h2cv-65fq-jgrp · CVE assignment pending (MITRE CNA-LR)
Summary
The -o=shell output format in yq is designed to emit shell-escaped values that can be safely eval'd. However, when combined with the -r (raw output) flag, the shell escaping is bypassed and user-controlled input is passed directly to the shell without sanitization.
Affected versions: all versions up to and including v4.54.1. The vulnerability exists in the shellWriter function which processes the -o=shell format.
Impact
- Arbitrary command execution — an attacker who controls the input YAML/JSON (e.g., via a config file, CI/CD pipeline variable, or API response processed by yq) can execute arbitrary shell commands with the privileges of the yq process.
- Attack vector: local/remote — any context where yq processes untrusted input with
-o=shell -rflags. - Real-world scenarios: CI/CD pipelines parsing attacker-controlled YAML, configuration management tools, Kubernetes manifest processing, API response transformation.
Root Cause
The shellWriter in pkg/yqlib/shell_writer.go applies shell escaping unless the -r flag is present. When -r is used, the raw string is written directly without escaping:
// pkg/yqlib/shell_writer.go (simplified)
func (w *shellWriter) writeString(s string) {
if w.rawOutput {
// RAW OUTPUT — NO ESCAPING
w.writer.WriteString(s)
return
}
// Normal path: shell escape the string
w.writer.WriteString(shellEscape(s))
}
The documentation and flag interaction imply -r only removes quotes from output, but in practice it disables the shell escaping entirely for -o=shell.
Proof of Concept
Create a malicious YAML file:
# evil.yaml
payload: "$(id > /tmp/pwned)"
Process it with yq using the vulnerable flags:
yq -o=shell -r '.payload' evil.yaml
# Output: $(id > /tmp/pwned)
# When eval'd: executes 'id > /tmp/pwned'
If the output is used in a shell context (e.g., eval $(yq -o=shell -r ...) or sourced), the command executes immediately. Even without explicit eval, the output is intended to be shell-safe — the -r flag breaks that contract.
Proof-of-Concept Demo
The following video demonstrates the vulnerability: creating a malicious YAML payload, processing it with yq -o=shell -r, and showing the unescaped shell metacharacters in the output that lead to command execution when evaluated.
Video: yq command injection via -o=shell -r (GHSA-h2cv-65fq-jgrp). Full demo on PoC Demos page.
Disclosure Timeline
| Date | Event |
|---|---|
| 2026-10-08 | Vulnerability discovered during source code review |
| 2026-10-08 | Advisory submitted via GitHub Private Vulnerability Reporting |
| 2026-10-23 | Maintainer acknowledged — fixed in v4.54.2 |
| 2026-10-23 | GitHub Security Advisory published: GHSA-h2cv-65fq-jgrp |
| 2026-10-24 | Retest completed — fix verified; CVE request submitted to MITRE (CNA-LR) |
| Pending | CVE assignment via MITRE CNA-LR |
Fix
- Shell escaping is now applied regardless of the
-rflag when-o=shellis used — the-rflag only controls quoting, not escaping. - Updated documentation to clarify that
-o=shelloutput is always shell-escaped.
// pkg/yqlib/shell_writer.go — after fix
func (w *shellWriter) writeString(s string) {
// Always escape for shell output format
escaped := shellEscape(s)
if w.rawOutput {
// rawOutput only controls quoting, not escaping
w.writer.WriteString(escaped)
return
}
w.writer.WriteString(escaped)
}
Fixed in yq v4.54.2 (2026-10-23). Advisory published 2026-10-23; CVE assignment pending via MITRE CNA-LR.