En el desarrollo de software , tiempo de verificación a tiempo de uso ( TOCTOU , TOCTTOU o TOC/TOU ) es una clase de errores de software causados por una condición de carrera que implica la verificación del estado de una parte de un sistema (como una credencial de seguridad) y el uso de los resultados de esa verificación.
Las condiciones de carrera TOCTOU son comunes en Unix entre operaciones en el sistema de archivos , [1] pero pueden ocurrir en otros contextos, incluidos los sockets locales y el uso indebido de transacciones de bases de datos . A principios de la década de 1990, la utilidad de correo de BSD 4.3 UNIX tenía una condición de carrera explotable para archivos temporales porque usaba la función mktemp()
[2] . [3]
Las primeras versiones de OpenSSH tenían una condición de carrera explotable para sockets de dominio Unix . [4] Siguen siendo un problema en los sistemas modernos; a partir de 2019, una condición de carrera TOCTOU en Docker permite el acceso root al sistema de archivos de la plataforma host. [5] En la competencia Pwn2Own 2023 en Vancouver, un equipo de piratas informáticos pudo comprometer la puerta de enlace en un Tesla Model 3 actualizado usando este error. [6]
En Unix , el siguiente código C , cuando se utiliza en un setuid
programa, tiene un error TOCTOU:
si ( acceso ( "archivo" , W_OK ) != 0 ) { salir ( 1 );}fd = open ( "archivo" , O_WRONLY ); escribir ( fd , buffer , tamaño de ( buffer ));
Aquí, el acceso tiene como objetivo verificar si el usuario real que ejecutó el setuid
programa normalmente tendría permitido escribir el archivo (es decir, access
verifica el ID de usuario real en lugar del ID de usuario efectivo ).
Esta condición de carrera es vulnerable a un ataque:
En este ejemplo, un atacante puede aprovechar la condición de carrera entre access
y open
para engañar a la setuid
víctima y conseguir que sobrescriba una entrada en la base de datos de contraseñas del sistema. Las carreras TOCTOU se pueden utilizar para la escalada de privilegios y obtener acceso administrativo a una máquina.
Aunque esta secuencia de eventos requiere una sincronización precisa, es posible que un atacante organice tales condiciones sin demasiada dificultad.
La implicación es que las aplicaciones no pueden asumir que el estado administrado por el sistema operativo (en este caso, el espacio de nombres del sistema de archivos) no cambiará entre llamadas del sistema.
Para explotar una condición de carrera TOCTOU se requiere una sincronización precisa para garantizar que las operaciones del atacante se intercalen correctamente con las de la víctima. En el ejemplo anterior, el atacante debe ejecutar la symlink
llamada al sistema con precisión entre access
y open
. Para el ataque más general, el atacante debe estar programado para ejecutarse después de cada operación de la víctima, también conocido como "paso a paso" de la víctima.
En el caso de la utilidad de correo BSD 4.3 y mktemp()
[ 2], el atacante puede simplemente seguir ejecutando la utilidad de correo en un proceso, y seguir adivinando los nombres de los archivos temporales y seguir creando enlaces simbólicos en otro proceso. El ataque normalmente puede tener éxito en menos de un minuto.
Las técnicas para ejecutar paso a paso un programa víctima incluyen laberintos del sistema de archivos [7] y ataques de complejidad algorítmica. [8] En ambos casos, el atacante manipula el estado del sistema operativo para controlar la programación de la víctima.
Los laberintos del sistema de archivos obligan a la víctima a leer una entrada de directorio que no está en la memoria caché del sistema operativo, y el sistema operativo pone a la víctima en modo de suspensión mientras lee el directorio desde el disco. Los ataques de complejidad algorítmica obligan a la víctima a gastar todo su quantum de programación dentro de una única llamada del sistema recorriendo la tabla hash del núcleo de nombres de archivos almacenados en caché. El atacante crea una gran cantidad de archivos con nombres que tienen el mismo valor de hash que el archivo que buscará la víctima.
A pesar de la simplicidad conceptual, las condiciones de carrera TOCTOU son difíciles de evitar y eliminar. Una técnica general es utilizar el manejo de errores en lugar de la comprobación previa, según la filosofía de EAFP («es más fácil pedir perdón que permiso») en lugar de LBYL («mira antes de saltar»). En este caso no hay comprobación y el incumplimiento de los supuestos se indica mediante la devolución de un error. [9]
En el contexto de las condiciones de carrera TOCTOU del sistema de archivos, el desafío fundamental es garantizar que el sistema de archivos no pueda modificarse entre dos llamadas al sistema. En 2004, se publicó un resultado de imposibilidad que mostraba que no existía una técnica determinista y portátil para evitar las condiciones de carrera TOCTOU al utilizar las llamadas al sistema de archivos access
y a UNIX open
. [10]
A partir de esta imposibilidad, los investigadores han propuesto bibliotecas para rastrear descriptores de archivos y garantizar su corrección. [11]
Una solución alternativa propuesta en la comunidad de investigación es que los sistemas UNIX adopten transacciones en el sistema de archivos o en el núcleo del sistema operativo. Las transacciones proporcionan una abstracción de control de concurrencia para el sistema operativo y pueden usarse para evitar carreras TOCTOU. Si bien ningún núcleo UNIX de producción ha adoptado aún las transacciones, se han desarrollado prototipos de investigación de prueba de concepto para Linux, incluido el sistema de archivos Valor [12] y el núcleo TxOS [13] . Microsoft Windows ha agregado transacciones a su sistema de archivos NTFS [14] , pero Microsoft desaconseja su uso y ha indicado que podrían eliminarse en una versión futura de Windows [15] .
El bloqueo de archivos es una técnica común para prevenir condiciones de carrera para un solo archivo, pero no se extiende al espacio de nombres del sistema de archivos ni a otros metadatos, ni funciona bien con sistemas de archivos en red y no puede prevenir condiciones de carrera TOCTOU.
En el caso de setuid
los binarios, una posible solución es utilizar la seteuid()
llamada del sistema para cambiar el usuario efectivo y luego realizar la open()
llamada. Las diferencias setuid()
entre los sistemas operativos pueden ser problemáticas. [16]
{{cite journal}}
: Mantenimiento CS1: fecha y año ( enlace ){{cite book}}
: Mantenimiento de CS1: falta la ubicación del editor ( enlace ){{cite journal}}
: Mantenimiento CS1: fecha y año ( enlace ){{cite web}}
: Mantenimiento CS1: fecha y año ( enlace ){{cite web}}
: Mantenimiento CS1: fecha y año ( enlace )