Muchos servicios del sistema operativo requieren privilegios especiales para poder ejecutar sus respectivas tareas. Un error de programación en un servicio que posea tales privilegios abre la puerta para comprometer al sistema mediante la adquisición no autorizada de permisos. En el peor de los casos, un atacante remoto podría alcanzar privilegios de superusuario.La línea del archivo de configuración de sshd que habilita la separación de privilegios es la siguiente:
[Mediante la separación de privilegios], aun cuando existan errores en partes no privilegiadas, los mismos no pueden utilizarse para escalar privilegios. [Este procedimiento] es especialmente útil para servicios del sistema que autentican usuarios.
El principio básico de la separación de privilegios consiste en reducir la cantidad de código que se ejecuta con permisos especiales sin afectar o limitar la funcionalidad del servicio. Esto se logra al dividir una aplicación en dos o más partes: una de ellas (denominada monitor) es ejecutada con privilegios y la/s otra/s (esclavo/s) sin ellos. Esto disminuye la exposición a bugs en el código que es ejecutado con privilegios. Idealmente, la única consecuencia que resulta de un error en un servicio que esté corriendo con separación de privilegios es la denegación del servicio al atacante mismo.
Con esta configuración, estos son los procesos relacionados con ssh:
La salida del comando ps -ef muestra los siguientes datos:
- UID (User ID)
- PID (Process ID)
- PPID (Parent Process ID)
- C (Tiempo de CPU utilizado recientemente por el proceso, en términos de ciclos de reloj, más información aquí)
- STIME (Start TIME)
- TTY (terminal en la que está corriendo el proceso, una ? indica que el proceso es un daemon que está ejecutándose en segundo plano)
- TIME (tiempo de CPU utilizado por el proceso desde que fue iniciado, en términos de minutos y segundos)
- CMD (argumentos de la línea de comandos que iniciaron el proceso)
Este ejemplo muestra dos operaciones que requieren privilegios de superusuarios en la máquina remota:
- Antes de la autenticación, el monitor es la parte de la aplicación que chequea la contraseña provista por el usuario (o la clave pública) contra el archivo de contraseñas (/etc/shadow) o contra la clave pública del cliente previamente guardada en el servidor, según corresponda. Si la autenticación es exitosa, se crea la sesión de usuario; caso contrario, se niega el acceso.
- Después de la autenticación, se crea una pseudo-terminal para la sesión de usuario.
Ahora voy a deshabilitar la separación de privilegios y reiniciar el servidor ssh, para luego verificar los procesos nuevamente:
En este caso podemos ver que tanto sshd como el monitor fueron iniciados directamente por init, y aunque se lanzó la sesión remota de usuario correctamente, no se dispone de la "interfaz" de protección entre las peticiones efectuadas y la aplicación con privilegios. De esta manera, si un atacante lograra encontrar un bug en el código, podría fácilmente explotarla para escalar privilegios sobre el sistema.
Sin embargo, se puede ver en este caso que el monitor retuvo su PID y solamente cambiaron los correspondientes al esclavo y a sshd. Al inspeccionar el estado de los procesos luego de finalizar la conexión anterior e iniciar una nueva, podemos ver lo siguiente:
Por último, vuelvo a editar el archivo /etc/ssh/sshd_config para volver a utilizar separación de privilegios (recordar que más allá de reiniciar el servidor ssh, los cambios recién serán aplicados a la próxima conexión):
Nota: La teoría de este post está basado en el paper "Preventing Privilege Escalation" por Niels Provos, Markus Friedl, y Peter Honeyman, el cual puede descargarse desde este link.