El año va a comenzar como
terminó 2014, plagado de grandes éxitos. El que hoy vamos a presentar tiene
todos los visos de convertirse en la canción del verano de las veces que vamos
a escucharla en los próximos meses. Dentro de unos días, cuando las listas y
foros de seguridad se llenen de exploits solo tendréis que echarle un ojo a los
registros del IPS para comprobarlo. Con 'shellshock'
tenía su gracia, hacer un grep '() { :; }'
en los del servidor web, sobre todo del IIS. En fin.
Este "gran éxito" ha sido
bautizado como GHOST, tiene su propio logo y de momento (habrá que esperar)
no tiene dominio propio y web para hacer pruebas, cuestión de tiempo, para que
cumpla con todos los requisitos (resumimos: nombre pegadizo, logo y dominio+web
para hacer pruebas). Vamos a lo que nos interesa, quién, qué, cómo y el
diámetro del cráter que podría dejar su explotación.
Lo ha descubierto la empresa Qualys, dándolo
a conocer Amol Sarwate, jefe de ingeniería, en un post en el blog de la empresa.
No se publicarían detalles de GHOST hasta que las principales distribuciones
Linux tuvieran los paquetes con el parche correspondiente fuera del horno. Y
así ha sido, una vez se han sincronizado los repositorios han liberado los
detalles del "fantasma". Un
equilibrio casi perfecto entre el anuncio responsable y la cuota de atención
que van a tener durante estos días. No le quitemos mérito, investigar es
invertir y hasta ahora lo único que han hecho mal es ponerle un nombre antiSEO
a la criatura.
Vamos al qué.
Todas las distribuciones Linux que usen la
librería glibc (y eglibc) versión 2.17 y anteriores son virtualmente
vulnerables. ¿Cómo comprobar la versión?
ldd print | grep libc.so
o directamente ejecutando la librería en la
línea de comandos. Sí, puedes ejecutar una librería en Linux. En el caso de
libc esta dispone de un punto de entrada hacia __libc_main donde llama a
__libc_print_version que no hace otra cosa que imprimir cierta información en
formato humano sobre su compilación.
/lib/i386-linux-gnu$
./libc.so.6
GNU C Library (Ubuntu EGLIBC 2.19-0ubuntu6.5) stable
release version 2.19, by Roland McGrath et al.
Copyright (C) 2014 Free Software Foundation, Inc.
Evidentemente también puedes verificar la
versión instalada buscando su paquete correspondiente, pero esto va a variar en
función de la distribución usada.
Al grano. El fallo reside en la función '__nss_hostname_digits_dots', definida en
el archivo 'gnu/glibc/nss/digits_dots.c'.
Esta función es usada por un grupo de funciones de la familia 'gethostbyname' (de ahí el GHOST) y su
cometido es comprobar si la cadena que se ha pasado a 'gethostbyname' es una IP. De esta forma el sistema se ahorra una
consulta DNS. Básicamente es como si intentases resolver una IP (no una resolución
inversa, ojo), algo que carece de sentido.
Vamos a recorrer la
vulnerabilidad al revés, el fallo podemos verlos en la línea 157 del código de 'digits_dots.c' (sin el parche):
(157) resbuf->h_name = strcpy (hostname, name);
Ya de entrada el uso de 'strcpy' debería
resultar sospechoso, básicamente porque no hace ninguna comprobación sobre los
límites del búfer de destino y terminará sobrescribiendo "hostname" hasta
que encuentre el carácter '\0'
en "name".
Vemos de donde viene el cálculo
del tamaño de "hostname".
(125) hostname = (char *) h_alias_ptr + sizeof (*h_alias_ptr);
'h_alias_ptr' es:
(124) h_alias_ptr = (char **) ((char *) h_addr_ptrs + sizeof (*h_addr_ptrs));
nuevamente, a su vez, 'h_alias_ptrs' es:
(122-123) h_addr_ptrs = (host_addr_list_t *) ((char *) host_addr + sizeof (*host_addr));
seguimos, 'host_addr' es un
simple casting a 'buffer':
(121) host_addr = (host_addr_t *) *buffer;
'buffer' es donde se ha pasado el puntero que
apunta al bloque de memoria reservado en 'new_buf':
(116) *buffer = new_buf;
Ya queda poco. 'new_buf' es donde
está recogido el comienzo de la memoria reservada:
(101) *buffer_size = size_needed;
(102) new_buf = (char *) realloc (*buffer, *buffer_size);
Como podemos ver el tamaño viene
definido por 'buffer_size'
que es definido más arriba, teniendo en cuenta el tamaño de 'host_addr', 'h_addr_ptrs' y 'name'.
(85-86) size_needed = (sizeof (*host_addr) + sizeof (*h_addr_ptrs) + strlen (name) + 1);
Pero, ay, cuando se calcula el
tamaño se hace con la intención de que en 'buffer' se guarden cuatro objetos, dejando un
cuarto fuera del cálculo del tamaño de memoria a reservar: 'h_alias_ptr'.
Esta variable es un puntero a
char. Luego la función 'strcpy'
va a sobrescribir el tamaño, dependiente de la arquitectura, de un puntero a
char. 4 bytes, típicamente, en arquitecturas de 32 bits, 8 bytes en las de 64
bits.
Esta es la cabecera de la función
'__nss_hostname_digits_dots':
int __nss_hostname_digits_dots (const char *name, struct hostent *resbuf, char **buffer, size_t *buffer_size, size_t buflen, struct hostent **result, enum nss_status *status, int af, int *h_errnop)
'name' es el parámetro que puede
controlar el atacante. Cuando el servidor a la escucha necesite efectuar una
llamada a 'gethostbyname' para resolver un nombre de dominio, es ahí
donde comenzará la explotación.
Curiosamente, como apuntan en el anuncio de
seguridad de Qualys, llegar a la línea donde se produce el desbordamiento es un
camino lleno de obstáculos debido a la comprobación de ciertos valores en la
cadena donde se supone que está el nombre del host. Todo un desafío para
escribir un exploit.
Consideraciones
La librería 'libc' es omnipresente en todos los sistemas Linux, incluidos
sistemas empotrados. Prácticamente casi todo el software nativo y no nativo
hace uso de 'libc'. Eso convertiría
en virtualmente vulnerable a casi todos los programas que hagan uso del grupo
de funciones 'gethostbyname'. La
salvedad es la forma en la que se hace uso de dichas funciones y más en el
procesamiento previo que se efectúa sobre el nombre de host. Luego no todo es
vulnerable, hay muchos condicionantes a tener en cuenta.
Curiosamente, este fallo ya fue parcheado en
mayo de 2013, en la versión 2.18 de glibc, aunque en aquel momento no se llegó
a tener en cuenta la "militarización"
del error. Si vemos el parche aplicado en su día:
size_needed = (sizeof (*host_addr) + sizeof (*h_addr_ptrs) + sizeof (*h_alias_ptr) + strlen (name) + 1);Comprobamos como se ha añadido el elemento faltante: 'h_alias_ptr'. Bug neutralizado.
Muchas distribuciones Linux ya
contienen una versión corregida de este fallo desde entonces, 2013 y sin
saberlo. Pero no es suficiente, ahora mismo, ahí afuera, hay cientos de miles
de servicios que podrían estar a tres paquetes TCP de volar por los aires. La
vulnerabilidad está presente desde 2000. Una calma que precede a la tempestad.
Arriad velas, atrincad la carga y preparémonos para el impacto cuando publiquen
los exploits.
Más información:
Qualys Releases Security Advisory for “GHOST”
Vulnerability on Linux Systems
Qualys Security Advisory CVE-2015-0235
GHOST: glibc gethostbyname buffer overflow
funciones "gethostbyname*()"
[SECURITY] [DSA 3142-1] eglibc security update
Critical: glibc security update
USN-2485-1: GNU C Library vulnerability
David García

ja ja ja... últimamente suenas como a locutor de radio... me recordaste a Three Dog de Fallout 3
ResponderEliminar