flawopen.com/Teardowns/cve-2024-21626-runc-container-breakout-leaky-vessels
CVE-2024-21626: cómo runc corrigió el escape Leaky Vessels
CVE-2024-21626 combinó una fuga interna de descriptores en runc con la falta de una comprobación del límite después de chdir. Según la ruta de inicio, el contenedor podía comenzar con un directorio de trabajo en el namespace del host o alcanzar archivos del host mediante una ruta relacionada con exec.[1], [2], [3]
runc prepara una habitación aislada para el contenedor. Un handle interno filtrado podía hacer que el directorio de trabajo elegido apuntara fuera de esa habitación; la comprobación final del límite y el cierre de handles internos bloquean ese paso.[1], [2]
Conceptos Clave y Términos
runc- El runtime OCI que crea e inicia contenedores.[1]
Descriptor de archivo interno- Handle del sistema operativo para un archivo o directorio abierto. Si un descriptor filtrado sigue abierto al ejecutar un proceso hijo, puede conservar acceso entre procesos.[1], [3]
cwd- Directorio de trabajo del proceso. En Linux, Getwd puede devolver ENOENT cuando queda fuera de la raíz del namespace de montaje.[1], [2]
Escape del contenedor- Ruta por la que un proceso del contenedor alcanza recursos del host que deberían quedar fuera de su aislamiento.[1]
Mapeos de marcos
- Referencia CWE para la debilidad descrita en esta guía: CWE-403
Análisis de Causa Raíz
Antes de runc 1.1.12, algunos descriptores internos podían filtrarse a runc init. Eran internos de runc init; el proceso del contenedor no los heredaba simplemente. En los ataques 1 y 2, el directorio de trabajo configurado o elegido podía resolverse mediante un handle del namespace de montaje del host; runc no comprobaba después de chdir que el directorio final siguiera dentro de la raíz del contenedor. Variantes distintas usaban el manejo de rutas de execve para sobrescribir un binario del host. La versión 1.1.12 añadió cuatro capas: comprobar cwd con el comportamiento Getwd/ENOENT de Linux, cerrar descriptores internos justo antes de execve, corregir fugas concretas y marcar como close-on-exec los descriptores que no son de stdio antes de iniciar runc init.[1], [2], [3], [4]
CVE-2024-21626: variantes de ataque y protección aplicada en runc 1.1.12
| Variante del aviso | Condiciones y límite necesarios | Corrección o medida temporal pertinente |
|---|---|---|
Ataque 1 — imagen maliciosa mediante runc run | Es necesario engañar a un usuario privilegiado para que inicie una imagen no confiable. El directorio de trabajo configurado puede resolverse fuera del namespace de montaje del contenedor. | La comprobación unix.Getwd() posterior a chdir falla si cwd queda fuera de la raíz del namespace. Como medida temporal, permita solo / como cwd para imágenes no confiables. |
Ataque 2 — proceso del contenedor con runc exec | Un proceso dentro del contenedor debe anticipar el cwd distinto de / que elegirá un administrador con runc exec --cwd y preparar la ruta pertinente antes de la ejecución. | La misma comprobación de cwd después de chdir impide la ejecución fuera del contenedor. Hasta actualizar, limite --cwd a /. |
| Ataque 3a — sobrescritura de binario del host al iniciar una imagen | Adapta el ataque 1 y mantiene el requisito de que un usuario privilegiado inicie la imagen maliciosa; esta variante cruza el límite de execve hasta un binario del host. | Cierre los descriptores internos de runc justo antes de execve, corrija las fugas identificadas y active O_CLOEXEC. Actualice a runc 1.1.12 o a una versión corregida por el proveedor. |
Ataque 3b — sobrescritura de binario del host mediante runc exec | Esta variante adapta el ataque 2 y parte de un proceso malicioso en un contenedor activo que el atacante puede afectar; aun así, un administrador con privilegios debe ejecutar una operación normal de runc exec en ese contenedor. El aviso indica que requiere menos interacción del usuario que la ruta mediante imagen. | Las protecciones de cierre antes de execve y close-on-exec cubren esta ruta distinta. Los mantenedores no indican una solución práctica alternativa; actualice. |
Flujo de Ataque Paso a Paso
Impacto y condiciones
El aviso documenta acceso al sistema de archivos y control del host en cuatro variantes. Los ataques mediante imagen requieren que un usuario con privilegios ejecute una imagen no confiable. El ataque 3b aún requiere que un administrador realice una operación normal de runc exec en un contenedor que el atacante pueda afectar.[1]
Código Fuente: Vulnerable vs. Seguro
// Comportamiento pre-fix simplificado; no es código literal de runc
if err := os.Chdir(config.Cwd); err != nil {
return err
}
// Faltaba verificar que el cwd final quedara dentro de la raíz del namespace
// Comprobación de límite de runc 1.1.12 (fragmento upstream)
if wd, err := unix.Getwd(); errors.Is(err, unix.ENOENT) {
return errors.New("cwd fuera de la raíz del namespace de montaje del contenedor")
} else if err != nil {
return fmt.Errorf("no se pudo comprobar la seguridad de cwd: %w", err)
} else if !filepath.IsAbs(wd) {
return fmt.Errorf("cwd no es absoluto: %q", wd)
}
Lista de Verificación de Seguridad para Ingeniería
- ✓Actualice runc a 1.1.12 o una versión posterior admitida por el proveedor y compruebe la versión efectiva en cada imagen de nodo.[1], [4]
- ✓Trate las imágenes no confiables como solicitudes de ejecución de código; limite quién puede iniciarlas mediante Docker, Kubernetes y las canalizaciones de compilación.[1]
- ✓En runtimes, compruebe el cwd después de chdir con la validación de raíz de namespace de la plataforma y falle de forma segura si hay errores.[1], [2]
- ✓Marque los descriptores internos con O_CLOEXEC y cierre los que no sean de stdio justo antes de exec, teniendo en cuenta los descriptores propios del runtime.[1], [3]
- ✓No considere user namespaces un sustituto del parche de runc; úselos como una capa adicional de aislamiento cuando estén disponibles.[1]