Hagamos un ejercicio mental… imaginemos la arquitectura de Internet a alto nivel. Podríamos empezar por imaginar todos los millones de servidores que componen la web, unos 75 millones de servidores en 2014 (no hace falta imaginar cada uno).
Pensemos en cómo hemos entrado a este post. Algunos habremos puesto el nombre de securityartwork directamente en el navegador, otros habremos entrado por algún enlace, etc. La cuestión es que la gran mayoría hemos accedido a partir del nombre de dominio desde un navegador. Por supuesto, por debajo de esta petición hay una cantidad inmensa de acciones que permiten que con sólo el dominio del servicio, podamos acceder al servicio.
Ahora vamos a añadir un elemento necesario para que las peticiones a dominios lleguen a buen puerto (puerto, jeje).
Para entender la necesidad de este nuevo elemento, es necesario tener claro que el nombre del dominio no sirve como identificador de un servidor, es decir, el servidor que está almacenando este blog no tiene el identificador de securityartwork, sino que tiene una dirección IP única en Internet.
Para obtener esta dirección IP podemos hacer un ping al dominio y ver la IP a la que se ha resuelto el dominio (en el caso de SAW, la 90.161.233.229). Ahora podemos acceder al dominio directamente por la dirección IP del servidor simplemente poniéndola en el navegador.
El nuevo elemento que vamos a añadir a nuestra idea de Internet son los servidores DNS. Los servidores DNS sirven para traducir los nombres de dominio a la dirección IP del servidor que tiene el dominio. Sin embargo, con los cientos de millones de páginas web que existen ahora mismo en Internet, tener un servidor almacenando todas las traducciones de todos los dominios haría que las consultas tardasen mucho en resolverse y que se necesitase una grandísima cantidad de espacio para almacenar todos los nombres en cada servidor.
Es por eso que la arquitectura de los servidores DNS no es una arquitectura de nodos replicados con todos los dominios repetidos en cada servidor, sino que sigue un patrón de jerarquía donde cada nivel de la arquitectura resuelve una parte del dominio.
Dependiendo del nivel, un servidor DNS resuelve un tipo o parte del dominio. Hay servidores DNS que resuelven el país o la región del dominio (“.es” de España), otros que resuelven la organización que tiene registrado el dominio, etc.
Entre los diferentes tipos de DNS están los servidores raíz (root name server). Estos servidores son la parte más crítica de la cadena de resolución de dominios, ya que realizan el primer paso para traducir el dominio a la dirección IP, y es de donde el resto de servidores han obtenido las traducciones de los dominios por las diferentes IPs. En total hay 13 servidores lógicos de nombres raíz y son un punto crítico para el correcto funcionamiento de Internet.
El problema
Hace 2 semanas se publicó un nuevo CVE, mediante el cual, se podía generar un error en BIND, el software para servidores DNS más utilizado en Internet (10 de los 13 servidores raíces lo utilizan). Infobyte es el laboratorio que ha publicado esta vulnerabilidad, junto con una prueba de concepto mostrando cuál sería el resultado en un servidor DNS real.
El error lo detectaron revisando las modificaciones que se estaban haciendo sobre el código del repositorio.
En la parte de código se puede ver la modificación que se añadió al código para controlar el problema que existía. Veamos más a fondo cómo se podría generar el error en la aplicación.
Siguiendo el diagrama, podemos ver los casos que se pueden dar dependiendo del tipo de petición DNS que se realiza y del tamaño de los campos que se envían.
La variable r contiene el buffer en el cual, el programa escribirá la respuesta a una petición DNS recibida. Podemos suponer que el máximo tamaño que puede tomar es de 512 bytes, que es el tamaño máximo permitido para una respuesta DNS.
Si seguimos el código, vemos que la primera comprobación que se hace es para asegurarse de que en el buffer r hay un mínimo de bytes libres para que la cabecera de la respuesta DNS pueda escribirse. En caso de que no quepa, la función devuelve una señal ISC_R_NOSPACE, la cual indica que no existe espacio suficiente para almacenar la respuesta en el buffer de respuesta.
En caso de que la cabecera de respuesta quepa en el buffer, siguiendo el flujo del programa iríamos a comprobar si el mensaje de respuesta DNS cabe en el buffer. Aquí es donde puede aparecer el problema.
La variable msg es la que contiene el mensaje de respuesta que se va a devolver, mientras que el atributo reserved guarda la cantidad de bytes necesarios para almacenar el mensaje, por supuesto, sin contar la cabecera.
Si imaginamos que tenemos un buffer r de 512 bytes (500 de respuesta y 12 de cabeceras por defecto), pero el mensaje de respuesta de 501 bytes (lo que se usa como prueba de concepto en el post publicado en Infobyte), ninguna de las dos condiciones generarían la señal de falta de espacio, ya que no se comprueba que en el buffer quepan tanto la respuesta como la cabecera en conjunto (513 bytes en este supuesto), lo que causa una inconsistencia que hace que la aplicación termine por un error, realizando el DOS.
Cómo controlar el tamaño de msg->reserved
Investigando un poco más a fondo en el código de la aplicación, se dieron cuenta de que el tipo de respuestas que modifican este parámetro son los que requieren de un RR (resource record) extra, los cuales son los tipos OPT, TSIG y SIG(0).
La forma que se usó para la prueba de concepto era introduciendo una firma inválida, en cuyo caso el servidor responde con la misma firma. De esta forma, dependiendo del tamaño de la firma, podemos controlar el tamaño de esta variable.
Cómo defenderse de estos ataques
La principal forma de defenderse es actualizando la aplicación bind9 a la última versión, la cual ya está parcheada (como se puede ver en el fragmento de código).
Si lo que queremos es detectar intentos de ataque de este tipo por medio de reglas, lo que se debería comprobar son peticiones DNS cuya respuesta modifique la varible msg y luego se intente guardar en el buffer de respuesta por medio de la rutina adjunta.
La forma de identificar el ataque sería cazando la petición DNS inicial de cualquiera de los 3 tipos, y calculando el tamaño de la query. En caso de que el tamaño de la query sea superior a 500 bytes, la petición DNS (deliberado o no) causaría el error en la aplicación que no esté actualizada.
Conclusión
Internet no está exento de vulnerabilidades. Si esta vulnerabilidad se hubiese descubierto de otra forma, una organización con el material y los recursos, podría haber dejado sin servicio a una gran parte de la red.
Tenemos para rato.
Referencias:
- Post de infobytesec: http://blog.infobytesec.com/2016/10/a-tale-of-dns-packet-cve-2016-2776.html
- POC y módulo de metasploit: https://github.com/infobyte/CVE-2016-2776
- CVE-2016-2776: http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-2776
- Repositorio de bind9: https://source.isc.org/cgi-bin/gitweb.cgi?p=bind9.git;a=summary