sábado, 14 de abril de 2012

Diversión con CRC. Introducción

Una de las áreas probablemente menos trabajadas por los "pentesters" de hoy en día es la criptografía. Sin embargo, poseer habilidades básicas en este tema será cada vez más y más obligatorio para hacer nuestro trabajo de forma adecuada. Este artículo es el primero en una serie dedicada a la criptografía y su aplicación en las pruebas de penetración modernas.

El algoritmo de CRC (Cyclic Redundancy Code) fue standarizado alrededor del año 1975, en la decada que parece ser donde todo lo importante se inventó. El objetivo de sus creadores era contar un código de verificación que permitiera detectar errores de transmisión de datos en las nacientes redes digitales. A pesar de que hoy en día contamos con redes muy confiables en donde errores "ocasionales" no son un verdadero problema, el algoritmo es todavía utilizado con relativa frecuencia y con desastrozas consecuencias para la seguridad.

El punto clave es que los algoritmos de CRC fueron diseñados para detectar modificaciones "no-intencionales." Sus autores nunca pensaron en que tendrían que enfrentarse a una red de redes con atacantes activamente modificando e interviniendo sus comunicaciones. En consecuencia, la utilización de esta clase de algoritmos con la idea de detectar modificaciones maliciosas en nuestra Internet está condenada a ser inefectiva en la mayoría de los casos. El ejemplo más conocido es probablemente el ataque fulminante que recibió WEP en donde la debilidad del CRC32 jugó un papel muy importante. Sin embargo, hay todavía muchos ejemplos vigentes asi que el entendimiento de las debilidades de este algoritmo significa entretenimiento garantizado por todavía un muy buen tiempo :D

La clase de algoritmos CRC están basados en simples operaciones módulo un polinomio (irreducible en muchos casos, aunque primitivo es preferible) en un cuerpo finito, específicamente GF(2). Ésto significa que las sumas se convertien en operaciones "XOR" y los inversos están garantizados cuando el polinomio es irreducible. Básicamente, las operaciones en este cuerpo (o campo) son muy similares (isomorficas de hecho) a las operaciones algebraicas elementales a las que estamos acostumbrados. Pero toda esta teoría es suficiemente aburrida, y esto se supone que sea divertido. La diversión comienza ahora:

Una de las construcciones criptográficas más prominentes en nuestros protocolos de comunicación segura involucra transformar texto claro en cifrado mediante la operación "XOR" del mensaje a proteger con una cadena "aleatoria" y de dificil deducción por parte de nuestro atacante. La razón de usar esta construcción es que nuestras computadoras pueden hacer las operaciones necesarias de forma muy rápida. Por ejemplo, si el mensaje que quiero proteger es "Minas al suroeste" y mi cadena aleatoria es la secuencia de bytes: "506f77735727601cdf3609a8c9d7dc1a9f", entonces lo que tengo que enviar por el canal inseguro es el resultado del siguiente calculo:





A medida que la cadena de cifrado se hace más aleatoria, el trabajo de mi atacante se hace más dificil. Sin embargo, este esquema es muy fácil de vulnerar simplemente aprovechando dos problemáticas muy evidentes: 1) la disponibilidad de la estructura del mensaje y 2) la falta de protección de integridad del texto cifrado. El primer problema estará siempre presente en protocolos estándares y abiertos pues sería inutil pensar que la estructura de mis mensajes permanecera oculta para mi atacante por mucho tiempo.  Sin embargo, el segundo problema es el tema que abordan los "codigos de verificación criptográfica" y es donde se ha intentado utilizar CRC de forma muy poco efectiva. Pero primero veamos cómo mi atacante es capaz de vulnerar el esquema inicial.

Si mi atacante quisiera confundir a mi interlocutor, y modificar el mensaje para que en lugar de decir "Minas al suroeste", dijera, "Minas al noroeste", todo lo que tiene que hacer son unas cuantas simples operaciones con XOR, como siguen:



Ahora, cuando mi interlocutor reciba el texto cifrado modificado por mi atacante, y lo "desencripte" utilizando la cadena aleatoria, obtendra el mensaje "Minas al noroeste" en lugar de obtener el mensaje original.


Nótese que mi atacante no necesitó adivinar la complicada cadena aleatoria. Tódo lo que necesitó fue aprovechar un poco la estructura del mensaje, i.e. la posición donde estaba el mensaje que deseaba modificar, y ese pedazo del texto claro, i.e. "sur". Esto puede parecer un requerimiento demasiado grande, pues el atacante necesita conocer un pedazo del mensaje original ("sur"). Sin embargo, este requermiento no es nada dificil de cumplir en la práctica cuando consideramos lo altamente predecibles que pueden llegar a ser nuestras comunicaciones. Al iniciar una carta, ¿no esperamos siempre un "saludo"? ¿una fecha? ¿una localidad?

Ahora bien, aceptemos por un momento que es imposible eliminar la redundancia de nuestras comunicaciones de tal forma que asumamos que nuestro atacante siempre tendrá un pedazo de información con la cual perpretar este ataque. ¿Acaso no podemos hacer nada para defendernos? Intentemos usar el chequeo CRC. Específicamente, utilizaremos el algoritmo CRC32 que es el más usado. La utilización de este algoritmo en este tipo de situaciones y las formas en que puede abusarse para perpetrar ataques altamente efectivos serán el tema de los siguientes artículos de esta serie. Sin embargo, para terminar esta introducción al tema, veamos cómo se calcula y como se usa el CRC32.

El CRC32 más popular está basado en el polinomio:

x³² + x²⁶ + x²³ + x²² + x¹⁶ + x¹² + x¹¹ + x¹⁰ + x⁸ + x⁷ + x⁵ + x⁴ + x² + x + 1

El cual representaremos con la secuencia de bytes: 0xEDB88320. A cada mensaje, le corresponde un código CRC32 cuyo cálculo detallado es un poco involucrado, pero en el corazón del algoritmo está la determinación del residuo de la división de nuestro mensaje por este polinomio, asumiendo coeficientes módulo 2. La mejor forma de describirlo para nosotros los informáticos es un algoritmo. La implementación en python que presento aquí es muy utilizada, y está basada en una tabla cuya generación veremos a continuación:

def CRC(mensaje,INITXOR,OUTXOR):
    r = INITXOR
    for c in mensaje:
        r = crc32_table[(r ^ ord(c)) & 0xFF] ^ (r >> 8)
    r ^= OUTXOR

La función anterior utiliza dos parametros adicionales (INITXOR y OUTXOR) que no hemos mencionado todavía, pero que para el caso CRC32, los valores son simplemente 0xFFFFFFFF en ambos casos. Ahora bien, hace falta conocer los valores de crc32_table. Esta tabla se puede generar utilizando la siguiente función:

def gen_crc_table(CRCPOLY):
    res = [0 for i in xrange(256)]
    for n in xrange(256):
        c = n
        for k in xrange(8):
            if ((c & 1) != 0):
                c = CRCPOLY ^ (c >> 1)
            else:
                c = c >> 1
    return res

Para el caso de CRC32 el parámetro CRCPOLY de la función anterior es simplemente el valor 0xEDB88320 mencionado anteriormente. Esto es todo. Veamos como funciona esto en la consola de python:



Ahora que sabemos como se calcula el código CRC32 para un mensaje, estamos listos para intentar usarlo y prevenir que nuestro atacante envíe a nuestra tropa al campo minado del noroeste. La idea es muy natural, en lugar de enviar sólamente mi mensaje encriptado con mi cadena de bytes aleatoria, lo que voy a enviar es la encripción de la concatenación del mensaje original con el código CRC32 recien calculado. La idea sería que cuando mi atacante modifique el mensaje encritpado, mis interlocutores se den cuenta e ignoren el mensaje falso. El código CRC de mi mensaje es: 0xf60ebe68, pero lo concatenamos al mensaje con los bits menos significativos primero. Esta concatenación la podemos ver en la figura anterior. Por lo tanto, lo que enviaré a la tropa es:

El texto cifrado es básicamente el mismo, pero concatenado con el cifrado del código CRC32 que calculamos. Nuestro atacante puede hacer ahora exactamente el mismo procedimiento anterior. Sin embargo, ahora cuando la tropa obtenga el texto falsificado, tendremos un código CRC32 con que validarlo:



y al calcular el CRC32 del mensaje que modificó mi atacante, encontrará:


El cual no es igual al CRC que enviamos en el texto cifrado. La tropa descartará el mensaje y todos felices. Sin embargo, resulta que por las propiedades del algoritmo de CRC, mi atacante tiene una gran cantidad de posibilidades para atacar este nuevo esquema. El detalle de algunas de estas fórmulas de ataque serán el tema de los siguientes artículos de esta serie.

2 comentarios:

  1. Excelente artículo. bueno para entender de que se trata. Muestra conocimiento de lo que está diciendo en el texto

    ResponderEliminar
  2. Yo tuve el gusto de escucharlo en una conferencia el 2013 en Lima Perú, la verdad fue el expositor que mas llamo mi atención me gustaría hacerle muchas preguntas sobre su carrera espero que lea mi comentario saludos y felicitaciones.

    ResponderEliminar