martes, julio 19, 2016

OpenSSH: Time-Based Info Leak para enumerar usuarios #Linux #pentest #hacking #OpenSSH

El pasado jueves 14 de Julio se hizo pública una vulnerabilidad que afectaba a la última versión de OpenSSH, en concreto a OpenSSH 7.2p2. Esta ha sido recogida en el expediente CVE-2016-6210 dónde se explica que se pueden enumerar los usuarios en un sistema operativo a través del servicio OpenSSH, gracias a un leak de información basado en el tiempo de cómputo que tiene este tipo de servicios al procesar la criptografía.

Figura 1: OpenSSH. Time-Based Info Leak para enumerar usuarios en GNU/LINUX

Para entender cómo funciona esto, hay que recordar que un servicio OpenSSH 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. Y es en ese caso, cuando se solicita la contraseña, cuando se puede explotar la vulnerabilidad.

¿Dónde se encuentra la vulnerabilidad?

Cuando un usuario remoto envía un usuario y una contraseña al servidor OpenSSH, éste valida en primer lugar el nombre de usuario, y en segundo lugar si la contraseña es correcta o no. En el caso de que el usuario exista, el servidor OpenSSH necesita lidiar con la criptografía de la contraseña y para ello aplica un algoritmo SHA256/SHA512. El proceso de hashing que se lleva a cabo tiene un coste 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. El info leak se produce ya que si el usuario no existe, el software de OpenSSH no hace el procesado criptográfico de la contraseña, por lo que la respuesta es de menor tiempo.

Figura 2: CVE-2016-6210

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 pasa por un proceso de hashing 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 calcular el hashing de la contraseña tanto si el usuario existe como si no, con el fin de igualar tiempos de respuesta en ambos casos.

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 la publicación que se ha hecho en la lista Full Disclosure se puede ver como la contraseña enviada es de 25.000 A’s.  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 en un Kali Linux 2.0 contra un servidor OpenSSH vulnerable 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.

Figura 3: Ejecución de script. El usuario Pablo es el que más tiempo de respuesta da.

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.

Figura 4: Probando usuarios desde un diccionario

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.

Figura 5: Script en Python automatizado con diccionario de datos de entrada

En la imagen superior, 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 OpenSSH. La buena práctica en hardening 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 OpenSSH 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 una sesión SSH.

Figura 6: El usuario root está habilitado. Usuario root2 no está habilitado.

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.

Figura 7: Usuario root y root2 deshabilitados

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.

Figura 8: Fortificación de OpenSSH con Latch en Ubuntu Linux

Que se pueda conocer que el usuario existe o no es un primer paso a tener en cuenta, y por tanto nosotros hemos metido en nuestra plataforma de pentesting persistente Faast esta comprobación, teniendo en cuenta la latencia de red en Internet. Saber la lista de usuarios es el primer paso antes de intentar atacar la contraseña. Si quieres, sabes que puedes configurar Latch en OpenSSH para detectar y bloquear cualquier ataque con existo a una cuenta OpenSSH.

Autor: Pablo González Pérez (@pablogonzalezpe)
Escritor de los libros "Metasploit para Pentesters", "Ethical Hacking", "Got Root" y “Pentesting con Powershell

2 comentarios:

  1. Excelente explicación; a su vez verifique mi versión del ssh y justamente es la versión 7.2p2 que indicas que tiene la vulnerabilidad, ya que utilizo Debian 9 (stretch) y buscando en la pagina web oficial de Debian, observe que en la versión Debian 8 (Jessie) posee otra versión... procedí actualizar mis repositorios y luego la paquetería y como que no esta corregida la vulnerabilidad todavía...

    ResponderEliminar
  2. En mi caso fotifico SSH:
    - login sólo con llave
    - el servicio ssh sólo responde a conexión de IPs autorizadas en /etc/hosts.allow
    - cambio Puerto
    - sólo 1 conexión en simultáneo

    Saludos

    ResponderEliminar