martes, 15 de abril de 2014

¿Qué demonios está pasando?

Asisto perplejo a las recientes noticias sobre vulnerabilidades encontradas en las implementaciones SSL/TLS tanto de Linux como de iOS. Por si no estáis enterados, os pongo en antecedentes.

A principios de año se liberó una actualización para iOS6, iOS7 y Apple TV 6, y se sabe que también afectaba a OS X 10.9.x. El parche corregía una cagada de las históricas. En post anteriores, os he explicado el mecanismo de funcionamiento de los certificados digitales, su firma y verificación por parte de las CA’s. Pues bien, el problema consistía en el mal funcionamiento de una función, en concreto SSLVerifySignedServerKeyExchange, que, como su nombre bien indica, se encarga de verificar la firma de un mensaje. He aquí el código en cuestión: 

SSLVerifySignedServerKeyExchange(SSLContext *ctx, bool isRsa,
                                 SSLBuffer signedParams, uint8_t *signature,
                                 UInt16 signatureLen)
{
            OSStatus            err;
            ...
            if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0)
                   goto fail;
            if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)
                   goto fail;
                   goto fail;
            if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0)
                   goto fail;
            ...
fail:
            SSLFreeBuffer(&signedHashes);
            SSLFreeBuffer(&hashCtx);
            return err;

No hay que ser programador avanzado para darse cuenta del error. Ese segundo goto fail; que os he resaltado, hace que se salga de la función sin haber terminado todas las verificaciones de la firma, dando un resultado válido, aunque el certificado utilizado para firmar sea falso o incluso no exista. Sorprendente, ¿verdad?

Pues ojiplático me quedé al comprobar, pocas semanas después, ya en Marzo, que GnuTLS padecía del mismo, o similar, fallo de programación. Y para no ser menos que nuestros amigos de Cupertino, esta cagada venía arrastrándose desde el 2005, sin que nadie se hubiera dado cuenta…o no es así, y entonces es para echarse a temblar.

Aquí el fallo venía dado por goto’s erróneos, haciendo que el código de error detectado en un certificado, fuera pasado a la instrucción cleanup, en vez de fail. Así, podía darse el caso de que nuestro sistema detectase un certificado erróneo (entiéndase por erróneo, falso o no verificado) y lo diese por válido. La verdad es que no sé qué cagada es peor. Pero no voy a entrar en la prolífica discusión de si es mas seguro Linux o iOS. Sinceramente, me da igual. Porque cuando ya creía haberlo visto todo, hace su aparición estelar….. TACHAN!!!.....Heartbleed.

Este “palabro” viene a describir el fallo de seguridad conocido más importante hasta la fecha (nótese que he subrayado la palabra “conocido”). Si bien los fallos anteriores eran corregidos rápidamente por una simple actualización o parche en nuestros sistemas, nuestro amigo Heartbleed nos tiene, a día de hoy, y perdonarme la expresión, cogidos por las pelotas. Y os voy a explicar porqué.

Los fallos anteriores afectaban a nuestro lado de la comunicación segura. Así, si era lanzada una web “falsa”, nuestros sistemas podían no darse cuenta del error. Pero no pasaba nada si nos conectábamos a nuestro banco, o comprábamos en nuestra tienda habitual de Internet, o accedíamos a nuestro correo web. Las páginas eran válidas y seguras, y no había posibilidad de robo de credenciales o datos de tarjetas, por poner un ejemplo.

Heartbleed hace que el fallo de seguridad resida en el servidor al que nos conectamos. Da igual que lo hagas con Linux, iOS, Windows, Android, señales de humo o tam tam. Los servidores han sido comprometidos, y gravemente.

En este caso, ha sido un teutón llamado Robin Seggelmann el artífice del caos. El pobre hombre debe estar pasando las de Caín y no estoy de acuerdo en realizar un linchamiento público, ya que, si bien es cierto que el error en el código ha sido suyo, no es menos cierto que el sistema de revisión de dicho código por parte de su supervisor ha fallado.

El fallo se encuentra en la implementación OpenSSL, software instalado en multitud de servidores de Internet, que proveen de acceso seguro a protocolos tales como HTTPs o FTPs, por poner un ejemplo. Vamos a destripar el fallo.

En post anteriores os he descrito el uso de certificados digitales. Vamos a repasarlo de nuevo. Estamos sentados en nuestra casita, tranquilos, y decidimos conectarnos para ver si Hacienda nos ha realizado ya la devolución de los impuestos. Cada vez me devuelven menos, por cierto. Gracias Montoro, ya mismo tendré que pagar por trabajar.

Que me voy, que me voy del tema… Si es que me tiráis de la lengua…

En el servidor del banco al que nos conectamos reside su certificado digital, expedido por una CA, con sus claves pública y privada. Al conectarnos, el servidor nos facilita su clave pública, con la que encriptamos la información que le enviamos al Banco. No estoy hablando de credenciales de acceso. Me refiero a que nuestro equipo encripta una serie de información para establecer una comunicación cifrada simétrica con el servidor (recordar que el cifrado simétrico es mucho más rápido que el asimétrico). Así, la información que ciframos con la clave pública del servidor, es descifrada con la clave privada del mismo y se establecen los parámetros para realizar una comunicación cifrada de manera simétrica, utilizando la misma clave de cifrado en ambos lados de la comunicación. Esto es el funcionamiento habitual en una conexión a un servidor seguro. Hasta ahora todo va bien. Y ahora viene la parte complicada. A ver si consigo que todos lo entendamos (yo incluído)

Vamos a hacer una abstracción e imaginar que estamos hablando Frannoe y yo mismo. A ver si así queda claro. Vamos a imaginar que estamos en una conversación telefónica y nos aseguramos de que nuestro interlocutor es realmente quien dice ser.

[Frannoe dixit]
Ya me cogió de conejillo de indias…

[Jose respondere]
Ah… se siente. Haber dedicado el Blog a la cría de gusanos de seda.


F: Hola Jose.
J: Hola Fran
F: La lluvia en Sevilla… (es la clave pública de Fran. Es que es de un poético...)
J: ...buena sombra le cobija (propuesta de clave simétrica, encriptada con la clave pública)
F: Aceptamos barco (aceptada y establecida clave simétrica)

(a partir de aquí va todo cifrado, pero lo dejo en plano)

J: ¿Para cuándo la DMDc 3 en x86, Fran?
F: ¿No has leído mi último post? Que no doy abasto…
J: Si es que te metes en unos berenjenales…
F: Tú tienes parte de culpa, te recuerdo.
J: Espérate Fran, que me hago de vientre…
F: Sin comentarios
.
.
J: Ya he vuelto.
F: Lo siento, ya no estoy seguro de que seas tú realmente. Así que…. La lluvia en Sevilla…
J: (mierda, a empezar de nuevo…)

Para evitar esto, que es una sobrecarga de trabajo enorme para un servidor, se establece un procedimiento por el que le decimos al servidor: “Oye, que soy yo, no me empieces de nuevo la comunicación”. Lógicamente, el procedimiento consiste en el envío de una cierta información desde el cliente, muy similar a un ping. En concreto, lo que se envía desde el cliente al servidor es una estructura de datos llamada TLS1_HB_REQUEST que consiste en dos partes: un mensaje o payload con datos aleatorios y el tamaño de dicho mensaje. Cuando el servidor recibe este mensaje, devuelve una estructura de datos llamada TLS1_HB_RESPONSE, que copia el mensaje recibido y lo envía de vuelta. A este procedimiento se le vino a llamar HEARTBEAT, o latido de corazón, para mantener viva la conexión con el servidor aunque no haya intercambio de datos.

El problema surge aquí. El error consiste en que el servidor no comprueba si los datos enviados en TLS1_HB_REQUEST tienen realmente la longitud indicada en el mensaje. Así que enviamos a nuestro servidor un TLS1_HB_REQUEST de 1 byte, pero indicándole que el tamaño del mismo es de 64 kb (65.535 bytes).

Como el servidor confía ciegamente en que el payload recibido es de 64 kb, procede a generar el TLS1_HB_RESPONSE, copiando el payload original, y completando hasta llenar los 64 kb del mensaje con los datos que en ese momento tiene la memoria RAM del servidor.

[Frannoe dixit]
¿Y qué hay en la memoria RAM del servidor?

[Jose respondere]
Todo, querido amigo. En la RAM del servidor se encuentra ejecutándose OpenSSL, así que tenemos código de OpenSSL, contraseñas, claves de cifrado… Claro, de 64 kb en 64 kb, se tarda en recopilar información, pero potencialmente es infinita.

Así que… ¿en qué situación nos encontramos? Pues para ponernos en el peor de los casos, podemos asumir que todas nuestras claves bancarias y contraseñas de servicios web están comprometidas.

[Frannoe dixit]
Pues las cambio todas y a correr

[Jose respondere]
Es lo mínimo que tienes que hacer...cuando sepas que el servidor al que te conectas ya está parcheado. Te recuerdo que las claves de los certificados de esos servidores pueden haber sido comprometidas. Así que los administradores deberían actualizar dichas claves y/o generar nuevas. Si han sido descubiertas, da igual que cambies tus claves de acceso. Van cifradas con las claves del servidor que han sido comprometidas, ¿recuerdas?.

[Frannoe dixit]
Pero eso ya estará hecho, ¿no? Ha pasado una semana desde que saltó la liebre…

[Jose respondere]
Yo no digo nada, que después todo se sabe… Aquí te dejo un enlace a una herramienta que puedes utilizar para comprobar si el servicio al que te conectas está parcheado o no. Sólo te diré que los que yo he probado (bancos inclusive), me dicen que ya está solucionado (es decir, que ya no se traga el payload de 64 kb), pero advierte que los certificados de los sitios no han sido actualizados….

Y así nos va...


Me parece que voy a dar de baja el Internet, la tele por cable, el móvil y el teléfono fijo… Y vuelta al cartero de toda la vida… y a las postales… qué tiempos...

No hay comentarios:

Publicar un comentario