
El helper writeToFile de Consul Template abría directamente un destino proporcionado por el operador y seguía los componentes de ruta enlazados, permitiendo que la salida renderizada escapara del directorio previsto y sobrescribiera un archivo preexistente.
El helper writeToFile de Consul Template abría directamente un destino proporcionado por el operador y seguía los componentes de ruta con enlaces, lo que permitía que la salida renderizada escapara del directorio previsto y sobrescribiera un archivo preexistente.
Encontré este problema al revisar HashiCorp Consul Template, con una pregunta directa de seguridad del sistema de archivos en mente:
Si writeToFile recibe una ruta que parece estar dentro del directorio previsto, ¿verifica dónde escribirá realmente el sistema de archivos los datos?
En este caso, la respuesta fue no.
El helper de plantilla writeToFile abría directamente la ruta final proporcionada por el usuario mediante os.Create() u os.OpenFile().
Esas operaciones seguían enlaces simbólicos, junctions de directorio y redirecciones equivalentes del sistema de archivos ya presentes en la ruta de destino.
Eso significaba que la cadena de ruta podía permanecer bajo la raíz prevista por el operador mientras que la escritura real aterrizaba en otro lugar.
En mi prueba de concepto controlada, un directorio padre con enlace redirigió la salida renderizada fuera del árbol previsto y provocó que un archivo de destino preexistente fuera sobrescrito.
Ese problema se convirtió en CVE-2026-14361.
Boletín de HashiCorp: HCSEC-2026-20
Boletín de IBM: boletín de seguridad CVE-2026-14361
CVE: CVE-2026-14361
Corregido en: 0.42.1
photo0
el destino proporcionado por el operador aparece dentro de la raíz prevista -> el atacante coloca de antemano un componente padre o final con enlace -> writeToFile abre la ruta directamente -> el sistema de archivos resuelve la escritura fuera del directorio previsto -> el secreto renderizado es redirigido -> un objetivo preexistente puede ser sobrescrito
Consul Template renderiza datos de fuentes como Consul y Vault.
El helper writeToFile permite que una plantilla escriba contenido seleccionado en un archivo local separado mientras aplica un propietario, grupo y modo de permisos solicitados.
La documentación de HashiCorp demuestra específicamente el helper con material PKI:
private key -> writeToFile /my/path/to/cert.key
certificate authority -> writeToFile /my/path/to/cert.pem
certificate -> append to /my/path/to/cert.pem
Eso lo convierte en algo más que un helper de salida ordinario.
El contenido que cruza este límite puede incluir:
La pregunta importante no era si writeToFile podía crear un nombre de archivo solicitado.
La pregunta real era:
¿El proceso escribe en la ubicación del sistema de archivos prevista por el operador, o simplemente en el objeto al que la ruta se resuelve en el momento de la apertura?
En las versiones vulnerables, confiaba en esto último.
Los helpers de escritura son superficies de seguridad de alto valor porque cruzan de los datos de la aplicación a la mutación del sistema de archivos.
Los fallos interesantes a menudo no son el clásico traversal ../.
Son fallos de resolución:
Eso es especialmente importante cuando el proceso se ejecuta con más privilegios del sistema de archivos que el atacante.
Un atacante local con pocos privilegios puede no ser capaz de sobrescribir un archivo sensible directamente.
Pero si puede influir en un componente de ruta bajo un directorio de escritura previsto, un proceso de Consul Template más privilegiado puede realizar la escritura por él.
Ese fue el límite en el que me centré.
No abordé esto como una revisión genérica de path traversal.
La ruta proporcionada no necesitaba segmentos ...
Podía permanecer léxicamente dentro de la raíz prevista todo el tiempo.
La pregunta más sólida era:
¿Se rechazan los componentes de destino con enlaces antes de que se escriba contenido sensible?
Esa pregunta importa para ambos casos:
El segundo caso es especialmente útil porque los registros y la configuración siguen mostrando una ruta de apariencia inocente bajo el directorio esperado.
El sistema de archivos la resuelve en otro lugar.
La causa raíz era la creación directa de archivos basada en rutas sin validación consciente de enlaces.
En la revisión probada, writeToFile() seleccionaba uno de dos caminos de apertura.
El modo de append usaba:
f, err = os.OpenFile(
path,
os.O_APPEND|os.O_WRONLY|os.O_CREATE,
perm,
)
El modo de escritura normal usaba:
dirPath := filepath.Dir(path)
if _, err := os.Stat(dirPath); err != nil {
err := os.MkdirAll(dirPath, os.ModePerm)
if err != nil {
return "", err
}
}
f, err = os.Create(path)
Ninguno de los dos caminos rechazaba componentes de destino con enlaces antes de abrir el archivo.
Eso importa porque:
os.Create(path) sigue las redirecciones existentes del sistema de archivos y trunca el archivo resueltoos.OpenFile(path, ...) sigue los componentes de ruta con enlaces durante el modo appendLa misma suposición basada en rutas continuaba después de la escritura.
La propiedad y los permisos se aplicaban usando la ruta nuevamente:
err = os.Chown(path, uid, gid)
err = os.Chmod(path, perm)
Eso significaba que las operaciones de metadatos también estaban vinculadas a un nombre de ruta mutable en lugar del descriptor de archivo ya abierto.
Porque el atacante puede preparar la redirección antes de que writeToFile se ejecute.
No se requiere una condición de carrera probabilística para el ataque básico.
La secuencia es sencilla:
writeToFile la abre directamenteEsa es toda la vulnerabilidad.
Es cierto que los sistemas operativos normalmente siguen los symlinks durante las aperturas de archivos basadas en rutas.
Eso no hace que este sea un comportamiento seguro de la aplicación.
La pregunta de seguridad no es:
"¿Go se comportó como está documentado?"
La pregunta real es:
¿Un helper que escribe datos sensibles renderizados verificó que el destino resuelto coincidiera con la ubicación prevista por el operador?
En las versiones vulnerables, no lo hizo.
Esa distinción importa porque Consul Template puede ejecutarse: