
PoC para CVE-2019-5736
PoC para CVE-2019-5736
Creado con ayuda de @singe, @_cablethief, y @feexd
Probado en Ubuntu 18.04, Debian 9 y Arch Linux. Versiones de Docker 18.09.1-ce y 18.03.1-ce. Este PoC actualmente no funciona con Ubuntu 16.04 y CentOS.
Ve a ver el código del exploit de Dragon Sector (las personas que descubrieron la vulnerabilidad) aquí.
Esta es una implementación en Go de CVE-2019-5736, un escape de contenedor para Docker. El exploit funciona sobrescribiendo y ejecutando el binario runc del sistema anfitrión desde dentro del contenedor.
Hay 2 casos de uso para el exploit. El primero (que es lo que es este repositorio), es esencialmente una trampa. Un atacante necesitaría obtener ejecución de comandos dentro de un contenedor e iniciar un binario malicioso que escuche. Cuando alguien (atacante o víctima) usa docker exec para entrar al contenedor, esto activará el exploit que permitirá la ejecución de código como root.

El segundo (que no es lo que es este repositorio), crea una imagen Docker maliciosa. Cuando esa imagen se ejecuta, el exploit se activará. No es necesario hacer exec al contenedor. Vea la parte inferior de este readme para ver un ejemplo en gif.
Para explotar esta vulnerabilidad necesitas tener root (uid 0) dentro del contenedor.
Sí, sobrescribirás tu implementación de runc, lo que hará que tu sistema ya no pueda ejecutar contenedores Docker. Por favor, haz una copia de seguridad de /usr/bin/docker-runc o /usr/bin/runc (dependiendo de cuál tengas; también revisa /usr/sbin).
Modifica el código como consideres adecuado y compílalo con go build main.go. Mueve ese binario al contenedor del que deseas escapar. Ejecuta el binario, y luego la próxima vez que alguien se conecte a él y llame a /bin/sh, tu payload se activará.
Este PoC fue creado usando una excelente explicación de este commit al proyecto lxc (junto con algunos consejos útiles de otras personas).
Como ejemplo, si el binario objetivo fuera /bin/bash, este podría ser reemplazado por un script ejecutable que especifique la ruta del intérprete #!/proc/self/exe (/proc/self/exec es un enlace simbólico creado por el kernel para cada proceso que apunta al binario que se ejecutó para ese proceso). Por lo tanto, cuando /bin/bash se ejecute dentro del contenedor, en su lugar se ejecutará el destino de /proc/self/exe, que apuntará al binario runc en el host.
Implementamos esto sobrescribiendo /bin/sh en el contenedor con #!/proc/self/exe, que apuntará al binario que inició este proceso (el Docker exec).

El atacante puede entonces proceder a escribir en el destino de /proc/self/exe para intentar sobrescribir el binario runc en el host. Sin embargo, en general, esto no tendrá éxito ya que el kernel no permitirá que sea sobrescrito mientras runC se esté ejecutando. Para superar esto, el atacante puede en su lugar abrir un descriptor de archivo a /proc/self/exe usando el flag O_PATH y luego proceder a reabrir el binario como O_WRONLY a través de /proc/self/fd/ e intentar escribir en él en un bucle ocupado desde un proceso separado.
Nota: Algunas partes de la sección anterior no son del todo precisas. No necesitas usar el flag O_PATH al obtener el descriptor de archivo para runcinit. Adicionalmente, no necesitas crear el bucle de escritura en otro proceso. Obtenemos un descriptor de archivo para runcinit obteniendo un manejador de archivo a /proc/PID/exe. Desde allí, usamos ese manejador para obtener un manejador de archivo a /proc/self/fd/FILEDESCRIPTOR. Este es el manejador de archivo que usaremos para escribir.

Finalmente, tendrá éxito cuando el binario runC salga. Después de esto, el binario runC está comprometido y puede ser usado para atacar otros contenedores o el propio host.
Si somos capaces de escribir a ese manejador de archivo, hemos sobrescrito el binario runc en el host. Podemos ejecutar comandos arbitrarios como root.
Este repositorio no contiene este ejemplo, pero puedes encontrarlo aquí. En mi opinión, este es el escenario mucho más peligroso. Simplemente ejecuta la imagen del contenedor malicioso y obtienes ejecución de código como root. Para una explicación detallada, por favor consulta esto.
