ROBOT: El retorno de Bleichenbacher

“Aquellos que no recuerdan el pasado están condenados a repetirlo” (George Santayana)

Hace unos días se ha publicado una vulnerabilidad de ciertas implementaciones de SSL/TLS, conocida como ROBOT. Estas siglas hacen referencia a “Return Of Bleichenbacher’s Oracle Threat” o en castellano “El retorno de la amenaza del oráculo de Bleichenbacher”. En este post intentaremos explicar en qué consiste esta vulnerabilidad.

Lógicamente, si hablamos del retorno del oráculo de Bleichenbacher la primera pregunta que surge es… ¿qué es este oráculo y cómo se puede usar para atacar SSL/TLS?

Cuando el año pasado hablábamos de DROWN (https://www.s21sec.com/es/blog/2016/04/drown-a-fondo-un-nuevo-ataque-al-ssl-parte-i/) decíamos que durante el establecimiento de la conexión SSL/TLS ambos extremos negocian una serie de claves simétricas que serán luego utilizadas para cifrar los datos transmitidos entre ellos. Para negociar estas claves hay varios métodos. Entre ellos se encuentra RSA que es el método atacado tanto por DROWN como por el oráculo de Bleichenbacher. El proceso de intercambio de mensajes se muestra en el siguiente esquema:

 

Resumiendo (si queréis ver el proceso completo lo tenéis en la primera parte del post de DROWN), toda la seguridad se basa en un mensaje (ClientKeyExchange) enviado por el cliente al servidor que transporta un valor conocido como “pre-master-secret”, cifrado con la clave pública RSA del servidor y, por tanto, sólo descifrable por éste. Si un atacante consigue deducir el valor del “pre-master-secret” conseguirá descifrar toda la comunicación.

El punto crítico es que el tamaño del “pre-master-secret” es más pequeño que el tamaño de la clave pública del servidor (algo habitual en el caso del cifrado RSA). Aunque matemáticamente es posible cifrar un valor mucho más pequeño que la clave RSA, es una mala práctica a nivel de seguridad, por lo que se le aplica un relleno para que sea de tamaño comparable a la clave. Este relleno debe cumplir el formato PKCS#1 v1.5. Esto implica que el dato original que se cifra y se envía al servidor debe tener esta forma:

 

Donde los valores en rojo son fijos, los valores en verde son aleatorios pero distintos de 0 y los valores en azul son el “pre-master-secret”.

Por tanto, cuando el servidor recibe el ClientKeyExchange, lo primero que hace es descifrarlo y comprobar si coincide con este formato. Si por lo que sea los dos primeros bytes no son 0x00 0x02 (junto a otras condiciones adicionales, pero esta es la más importante), sabe que ha habido un problema y que la conexión no se llegará a establecer. Las primeras implementaciones de SSL/TLS enviaban una alerta al cliente cuando detectaban esta situación y cerraban la conexión.

Aquí es donde entra en juego el oráculo de Bleichenbacher. En este contexto un oráculo es un mecanismo a través del que descubrimos algo que no deberíamos saber. Por ejemplo, un atacante intenta establecer una conexión enviando un valor arbitrario en el mensaje ClientKeyExchange, en lugar del pre-master-secret cifrado que se espera. Si el servidor continúa con la conexión, esto quiere decir que sus dos primeros bytes (una vez descifrado por parte del servidor) eran 0x00 0x02. Si el servidor envía una alerta, quiere decir que no lo eran.

También es cierto que el servidor puede hacer comprobaciones adicionales que lleven a que, aunque los dos primeros valores sean 0x00 0x02 el valor sea rechazado. Estas comprobaciones son que los valores de P sean distintos de 0 y que el valor entre los P y los M sea exactamente 0. En función del comportamiento del servidor ante fallos en todas estas condiciones, el ataque es más o menos sencillo y se considera que el oráculo es de mejor o peor “calidad”.

El criptógrafo suizo Daniel Bleichenbacher descubrió en 1998 que esta característica puede usarse para deducir el valor del pre-master-secret. El siguiente gráfico describe el comportamiento:

 

En este caso, los valores C y C’ indican el contenido que enviamos como pre-master-secret cifrado dentro del paquete ClientKeyExchange, mientras que los valores que aparecen debajo son el resultado de aplicar el descifrado RSA a esos valores. En el caso de C, al descifrar los dos primeros valores son 0x00 0x02 (no nos importa el valor del resto) y por tanto el servidor lo identifica como un pre-master-secret válido y devuelve su siguiente mensaje, que es ChangeCipherSpec. En el caso de C’, al descifrar los dos primeros bytes son 0xF2 0x13. Por tanto, el servidor lo identifica como un error y genera una alerta. Es decir, para un texto cifrado arbitrario e incluso sin conocer la clave privada del servidor, podemos saber si los dos primeros bytes del texto descifrado son 0x00 0x02.

Para entender cómo funciona el siguiente paso del ataque hay que tener en cuenta que el algoritmo RSA es parcialmente homomórfico (podéis ver la explicación en https://www.s21sec.com/es/blog/2016/04/drown-a-fondo-un-nuevo-ataque-al-ssl-parte-ii/). Esto implica que si cogemos un texto cifrado con RSA y le aplicamos una serie de transformaciones, obtenemos un texto cifrado nuevo que se corresponde con un texto sin cifrar que está relacionado de una forma concreta con el texto sin cifrar original. Sería como decir que aplicando una transformación al texto cifrado podemos poner todas las letras del texto original en mayúsculas, incluso sin saber cuál es el texto original (esto no es posible en RSA pero es una buena metáfora).

El ataque se basa en generar nuevos valores de ClientKeyExchange (usando un sencillo algoritmo que toma como entrada un parámetro “s” que el atacante va cambiando de valor) y comprobar si el valor descifrado empieza con 0x00 0x02. ¿Y cómo hacemos para comprobar ésto si no disponemos de la clave privada? Sencillo: usando el oráculo que hemos visto más arriba.

La mayoría de los intentos resultan rechazados y el servidor envía una alerta (recordemos que el atacante trabaja a ciegas, ya que sólo puede conocer los valores cifrados y no los valores originales a los que corresponden). Pero de vez en cuando uno de los intentos es aceptado por el servidor, lo que implica que sus dos primeros bytes una vez descifrado son 0x00 0x02.

 

Como el atacante conoce la relación entre este ClientKeyExchange aceptado y el original (sabe con qué valor de “s” lo ha generado), esto proporciona al atacante algo de información sobre el valor del ClientKeyExchange original, lo que le permite ir acotando los posibles valores del “pre-master-secret” de la conexión legítima (le proporciona un valor mínimo y un valor máximo entre los cuáles está el “pre-master-secret”). Eventualmente, tras encontrar un elevado número de valores de “s” para los que el ClientKeyExchange modificado se acepta, el atacante obtiene el valor exacto del “pre-master-secret” original, o un rango de valores tan pequeño que es factible atacarlo por fuerza bruta.

Para hacernos una idea de la escala, al oráculo de Bleichenbacher se le conoce también como “el ataque del millón de mensajes” porque se estimaba que era aproximadamente el número de ClientKeyExchange que había que generar hasta conseguir obtener el valor del “pre-master-secret” de la conexión original. En la práctica este valor varía mucho en función de la “calidad” del oráculo.

Para neutralizar el oráculo de Bleichenbacher, en caso de que un servidor detecte un ClientKeyExchange mal formado (es decir, que el resultado después de descifrar no cumple el formato de relleno esperado), lo que hace es generar un valor aleatorio para el pre-master-secret y continuar como si el cliente hubiese proporcionado este valor. El ataque en este caso queda así:

 

Con este mecanismo, el atacante ya no puede distinguir los valores correctos de los incorrectos (el servidor se comporta como si todos fueran correctos), por lo que el oráculo ha quedado neutralizado y el ataque no es posible. Como este mecanismo forma parte del estándar TLS, todas las implementaciones lo incluirán y por tanto nos podemos olvidar del problema.

 

¿Seguro?

Casi 20 años después los autores del estudio en el que se basa ROBOT han decidido comprobar si la protección funciona de forma adecuada en una serie de implementaciones y por tanto el oráculo de Bleichenbacher no es usable a día de hoy. El resultado, desgraciadamente, no ha sido muy alentador. Algunos de los casos más sangrantes son implementaciones que envían alertas o directamente cierran la conexión TCP en caso de un ClientKeyExchange incorrecto, con lo que ni siquiera han implementado las protecciones.

Otro caso sorprendente son implementaciones que se toman la molestia de generar el pre-master-secret aleatorio y continuar con la negociación enviando su ChangeCipherSpec y su Finished… para luego cerrar directamente la conexión sin esperar al ChangeCipherSpec y Finished del cliente. De esta forma, el atacante no tiene más que esperar un poco y si el servidor cierra la conexión de manera abrupta, sabrá que el ClientKeyExchange estaba mal formado. Si el servidor no la cierra, sino que se queda esperando los mensajes del cliente, el ClientKeyExchange estaba bien formado.

En resumen, a pesar de haber pasado 20 años, el oráculo de Bleichenbacher sigue dando guerra, algo que unido a la falta de perfect forward secrecy en el intercambio de secretos RSA justifica aún más si cabe la decisión de eliminarlo en TLS 1.3.

Por otro lado, la gran cantidad de implementaciones diferentes incorrectas nos demuestra algo que es importante recordar: implementar cualquier librería criptográfica no es un juego de niños y es mucho mejor usar librerías establecidas, probadas y auditadas que empezar a implementar nuestro código desde cero.

Si queréis analizar más a fondo este problema, tenéis el paper disponible en https://eprint.iacr.org/2017/1189.pdf

 

Recent Posts

Leave a Comment