Inyección de comandos en yq mediante -o=shell -r
GHSA-h2cv-65fq-jgrp · Asignación CVE pendiente (MITRE CNA-LR)
Resumen
El formato de salida -o=shell en yq está diseñado para emitir valores con escape de shell que pueden evaluarse de forma segura. Sin embargo, cuando se combina con el flag -r (salida cruda), el escape de shell se omite y la entrada controlada por el usuario se pasa directamente al shell sin sanitización.
Versiones afectadas: todas las versiones hasta e incluyendo v4.54.1. La vulnerabilidad existe en la función shellWriter que procesa el formato -o=shell.
Impacto
- Ejecución arbitraria de comandos — un atacante que controle el YAML/JSON de entrada (p.ej., mediante un archivo de configuración, variable de CI/CD, o respuesta de API procesada por yq) puede ejecutar comandos de shell arbitrarios con los privilegios del proceso yq.
- Vector de ataque: local/remoto — cualquier contexto donde yq procese entrada no confiable con los flags
-o=shell -r. - Escenarios reales: pipelines CI/CD parseando YAML controlado por atacante, herramientas de gestión de configuración, procesamiento de manifiestos Kubernetes, transformación de respuestas de API.
Causa Raíz
El shellWriter en pkg/yqlib/shell_writer.go aplica escape de shell a menos que esté presente el flag -r. Cuando se usa -r, la cadena cruda se escribe directamente sin escape:
// pkg/yqlib/shell_writer.go (simplificado)
func (w *shellWriter) writeString(s string) {
if w.rawOutput {
// SALIDA CRUDA — SIN ESCAPE
w.writer.WriteString(s)
return
}
// Ruta normal: escape de shell de la cadena
w.writer.WriteString(shellEscape(s))
}
La documentación y la interacción de flags implican que -r solo elimina comillas de la salida, pero en la práctica deshabilita el escape de shell completamente para -o=shell.
Prueba de Concepto
Crear un archivo YAML malicioso:
# evil.yaml
payload: "$(id > /tmp/pwned)"
Procesarlo con yq usando los flags vulnerables:
yq -o=shell -r '.payload' evil.yaml
# Salida: $(id > /tmp/pwned)
# Al evaluarse: ejecuta 'id > /tmp/pwned'
Si la salida se usa en un contexto de shell (p.ej., eval $(yq -o=shell -r ...) o source), el comando se ejecuta inmediatamente. Incluso sin eval explícito, la salida está destinada a ser segura para shell — el flag -r rompe ese contrato.
Demostración de Prueba de Concepto
El siguiente video demuestra la vulnerabilidad: crear una carga maliciosa YAML, procesarla con yq -o=shell -r, y mostrar los metacaracteres de shell sin escape en la salida que conducen a la ejecución de comandos cuando se evalúan.
Video: inyección de comandos en yq mediante -o=shell -r (GHSA-h2cv-65fq-jgrp). Demo completa en la página de PoCs.
Cronología de Divulgación
| Fecha | Evento |
|---|---|
| 2026-10-08 | Vulnerabilidad descubierta durante revisión de código fuente |
| 2026-10-08 | Advisory enviado vía GitHub Private Vulnerability Reporting |
| 2026-10-23 | Mantenedor confirmó — corregido en v4.54.2 |
| 2026-10-23 | GitHub Security Advisory publicado: GHSA-h2cv-65fq-jgrp |
| 2026-10-24 | Retest completado — fix verificado; solicitud CVE enviada a MITRE (CNA-LR) |
| Pendiente | Asignación CVE vía MITRE CNA-LR |
Corrección
- El escape de shell ahora se aplica independientemente del flag
-rcuando se usa-o=shell— el flag-rsolo controla el entrecomillado, no el escape. - Documentación actualizada para aclarar que la salida
-o=shellsiempre tiene escape de shell.
// pkg/yqlib/shell_writer.go — tras la corrección
func (w *shellWriter) writeString(s string) {
// Siempre escape para formato de salida shell
escaped := shellEscape(s)
if w.rawOutput {
// rawOutput solo controla entrecomillado, no escape
w.writer.WriteString(escaped)
return
}
w.writer.WriteString(escaped)
}
Corregido en yq v4.54.2 (2026-10-23). Advisory publicado 2026-10-23; asignación CVE pendiente vía MITRE CNA-LR.