CVE-2016-6210: Enumeración de usuarios en OpenSSH basados en tiempo

El pasado jueves 14 de Julio se hizo pública una vulnerabilidad que afectaba a la última versión de OpenSSH. El CVE-2016-6210 indica que se puede enumerar usuarios en un sistema a través del servicio SSH, gracias al proceso de cómputo que tiene este tipo de servicios. ¿Cómo funciona esto? Un servicio SSH recibe un nombre de usuario y solicita una contraseña para comprobar si debe validar el acceso al usuario remoto o no. Hay que mencionar que la contraseña será solicitada, siempre y cuando este método de autenticación esté habilitado, ya que lo recomendable sería que el método de autenticación fuera de clave pública, sobre todo si el servidor es importante o crítico. 

¿Dónde se encuentra la vulnerabilidad? Cuando un usuario remoto envía un usuario y una contraseña al servidor SSH, éste valida en primer lugar el nombre de usuario, si éste no existe o no tiene acceso remoto, corta la conexión. Este corte de conexión provoca que el tiempo de respuesta del servidor sea muy bajo. En el caso de que el usuario exista, el servidor SSH coge la contraseña y la aplica un algoritmo SHA256/SHA512. El proceso de hashing que se lleva a cabo tiene un conste computacional, por lo que, si la contraseña que el usuario remoto envía es lo suficientemente grande, se producirá un retardo importante en el tiempo de respuesta. 

¿Dónde está el error? Realmente, el error se encuentra en varias partes. Si analizamos un flujo de ejecución se observa que cuando un usuario no existe solo disponemos del tiempo de comprobación de existencia de usuario, lo cual es algo, prácticamente, instantáneo. Cuando el usuario existe, la contraseña es hasheada para llevar a cabo la comprobación. Esto hace que al tiempo de comprobación de existencia de usuario se le sume el tiempo de hashing, el cual es bastante más grande que el primero. Una posible mitigación es insertar un tiempo de “no operación” con el fin de igualar tiempos en ambos casos. 

¿Por qué digo que el error se encuentra en varias partes? Aparte de ver el error en el punto anterior. Otro error de configuración es que el usuario pueda incluir contraseñas enormes, las cuales no tienen sentido. En la prueba de concepto que se ha incluido en el full disclosure se puede ver como la contraseña enviada es de 25.000 A’s. Esto no tiene sentido. Para usar contraseñas tan grandes, lo mejor es cambiar de método de autenticación a una clave pública.

PoC: Enumerando usuarios

El código presentado por los descubridores de la vulnerabilidad está escrito en Python y es muy sencillo de entender. Os lo dejo a continuación.

import paramiko
import time
user=raw_input("user: ")
p='A'*25000
ssh = paramiko.SSHClient()
starttime=time.clock()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
        ssh.connect('127.0.0.1', username=user, password=p)
except:
        endtime=time.clock()
total=endtime-starttime
print(total)

Ejecutando el código anterior contra un servidor SSH observamos que si el usuario existe en la máquina remota el tiempo se encontrará cercano al segundo. En el caso de que el usuario no exista, la respuesta o corte de conexión se realizará en una magnitud de tiempo muy inferior.


Como se puede ver, el usuario “pablo” existe y, por lo tanto, obtenemos unos resultados grandes en lo que a tiempo de respuesta se refiere. El resto de usuarios que fueron probados dan resultados mucho más bajos, por lo que quiere decir que los usuarios no existen.

Esto podría llevarnos a pensar a que podríamos modificar el script de Python ligeramente para leer los nombres de usuario desde un fichero. Cada línea del fichero sería un nombre de usuario e iríamos probando por diccionario que usuarios se encuentran registrados en el sistema.



A continuación, os dejo la pequeña modificación. Simplemente se añade la lectura de un fichero de texto con diferentes nombres de usuario y la posibilidad de indicarle la dirección IP por parámetro de entrada al script. 


Descubriendo si root está habilitado o no

Una de las cosas útiles que pueden ser sacadas de esta enumeración es comprobar si el usuario root está habilitado o no a través del servicio SSH. La buena práctica en fortificación de sistemas GNU/Linux nos dice que no debemos tener habilitado el login como root, para evitar que un usuario se conozca, y que pueda ser utilizados ataques de fuerza bruta contra el sistema. 

Por defecto, los sistemas y configuraciones modernas de SSH no habilitan por defecto el uso de root. Por esta razón, y para esta prueba de concepto, hemos utilizado un sistema Ubuntu con OpenSSH instalado dónde probaremos el estado de root. Para el primer caso configuramos root con posibilidad de ser accedido en remoto a través de SSH. 


Como se puede ver en la imagen, el usuario root existe y el tiempo de respuesta de toda la operación es bastante más grande que la del usuario root2, el cual no existe. Ahora vamos a llevar a cabo la misma prueba, pero deshabilitando previamente el usuario root del sistema SSH de la máquina Ubuntu.


Como se puede ver en la imagen el usuario root tiene unos tiempos cercanos al usuario root2. Esto es debido a que el sistema SSH no ha generado el hash de la contraseña enviada junto al usuario root, ya que por política del servicio el login con root está deshabilitado. Con esto queda demostrado el funcionamiento de la vulnerabilidad y las posibilidades que ofrece en una auditoria o proceso de hacking ético. Se recomienda actualizar la versión de OpenSSH lo antes posible.