Explicación del error Heartbleed

    Como habrás escuchado, hay un nuevo error de OpenSSL y es malo. Este no es uno de esos errores o trucos de los que oye hablar en las noticias e ignora con seguridad como siempre lo hace. Afecta a alrededor del 66% de todos los servidores de Internet que existen, lo que probablemente incluye un sitio web que frecuenta o en el que tiene información confidencial.

    Cómo funciona

    Entonces, ¿cuál es el error exactamente? Para describir el error, necesitamos comprender solo algunas partes de la implementación de OpenSSL. La estructura siguiente contiene el registro SSL:

    typedef struct ssl3_record_st
    	{
    /*r */	int type;               /* type of record */
    /*rw*/	unsigned int length;    /* How many bytes available */
    /*r */	unsigned int off;       /* read/write offset into 'buf' */
    /*rw*/	unsigned char *data;    /* pointer to the record data */
    /*rw*/	unsigned char *input;   /* where the decode bytes are */
    /*r */	unsigned char *comp;    /* only used with decompression - malloc()ed */
    /*r */  unsigned long epoch;    /* epoch number, needed by DTLS1 */
    /*r */  PQ_64BIT seq_num;       /* sequence number, needed by DTLS1 */
    /*rw*/	unsigned int orig_len;  /* How many bytes were available before padding
    				   was removed? This is used to implement the
    				   MAC check in constant time for CBC records.
    				 */
    	} SSL3_RECORD;
    

    Esta estructura se utiliza en el dtls1_process_heartbeat(SSL *s) en el archivo d1_both.c para manejar los latidos que mantienen viva la conexión SSL. Este método accede a los datos del registro en la estructura, como se muestra a continuación:

    int
    dtls1_process_heartbeat(SSL *s)
    	{
    	unsigned char *p = &s->s3->rrec.data[0], *pl;
    	unsigned short hbtype;
    	unsigned int payload;
    	unsigned int padding = 16; /* Use minimum padding */
        
        /* Read type and payload length first */
        hbtype = *p++;
        n2s(p, payload);
        pl = p;
    

    El primer byte del registro SSL es el tipo de latido recibido y la macro n2s(c, l) solo toma dos bytes de p, y los pone en payload. Estos bytes son la longitud de la carga útil en el latido. Lo importante a notar aquí es que la longitud en el registro SSL no se comprueba / verifica. La variable pl ahora apunta a los datos de latidos, que fueron enviados por el cliente.

    El hecho de que no se verificara la longitud del registro SSL es un gran problema, ya que más adelante en la misma función, la memoria se asigna usando la misma longitud de carga útil, que se muestra en el siguiente código:

    unsigned char *buffer, *bp;
    int r;
    
    /* Allocate memory for the response, size is 1 byte
     * message type, plus 2 bytes payload length, plus
     * payload, plus padding
     */
    buffer = OPENSSL_malloc(1 + 2 + payload + padding);
    bp = buffer;
    

    Ya que payload se le asignaron los primeros dos bytes de los datos de latido, que es proporcionado por el cliente, eso significa que puede tener un valor máximo de 65535 (o 64k, cuando se refiere a bytes). Entonces, esencialmente, estamos asignando la cantidad de memoria que el cliente quiere que hagamos (hasta 64k), pero no es gran cosa, ¿verdad? Asignar cantidades de datos definidas por el usuario no es muy dañino en sí mismo (aunque definitivamente no es algo bueno), pero aquí es dañino ya que más adelante en la misma función copiamos esa cantidad de memoria en nuestro búfer:

    s2n(payload, bp);
    memcpy(bp, pl, payload);
    

    Aquí, s2n(c, l) mueve el tamaño de la carga útil al bp buffer (opuesto a lo que n2s hizo), y memcpy copia el número de bytes de carga útil de pl a bp. Esto tampoco parece un gran problema hasta que considere el tamaño real de pl. Y si pl es solo unos pocos bytes de tamaño? ¿O incluso solo un byte? Un atacante podría habernos dicho que la carga útil era de 64 kb, cuando solo era de 1 byte. Entonces, ¿de dónde se copiaría el resto de la memoria si pl es mucho más pequeño de lo esperado? Memoria circundante, que puede haber sido liberado recientemente del mismo proceso.

    Potencialmente, hay una gran cantidad de datos confidenciales en la memoria circundante, como nombres de usuario, contraseñas e incluso claves privadas. Esto tiene a mucha gente preocupada ya que un atacante podría enviar continuamente latidos maliciosos a un servidor y seguir recuperando 64 kb de su memoria cada vez. Luego, una simple búsqueda de palabras clave como ‘contraseña’ o ‘tarjeta de crédito’ y listo, el atacante robó sus datos sin que nadie lo supiera. Mark Loman, investigador de seguridad, ya ha mostrado este ataque es posible con Yahoo Mail.

    Sin embargo, por suerte para nosotros, la clave privada probablemente sea segura, como señala Neel Mehta (uno de los investigadores que descubrió el error):

    Los patrones de asignación de montón hacen poco probable la exposición de claves privadas #corazón #dontpanico.

    – Neel Mehta (@neelmehta) 8 de abril de 2014

    Sean Cassidy proporciona información muy útil sobre estos métodos de asignación de montones:

    Hay dos formas en que la memoria se asigna dinámicamente con malloc (en
    menos en Linux): usando sbrk (2) y usando mmap (2). Si la memoria es
    asignado con sbrk, entonces usa las viejas reglas de crecimiento del montón y
    limita lo que se puede encontrar con esto, aunque múltiples solicitudes
    (especialmente al mismo tiempo) todavía podría encontrar algunas cosas divertidas1.

    Las asignaciones para bp no importan en absoluto, en realidad. La asignación
    para pl, sin embargo, importa mucho. Es casi seguro que está asignado
    con sbrk debido al umbral de mmap en malloc. Sin embargo,
    cosas interesantes (como documentos o información de usuario), es muy probable que
    asignado con mmap y puede ser accesible desde pl. Múltiple
    las solicitudes simultáneas también pondrán a disposición algunos datos interesantes.

    ¿Cómo se fija?

    Dado que este error es tan simple de ejecutar, eso significa que también es fácil de solucionar. OpenSSL ya ha publicado una actualización, la versión 1.0.1g. Contiene la siguiente solución:

    /* Read type and payload length first */
    if (1 + 2 + 16 > s->s3->rrec.length)
        return 0; /* silently discard */
    hbtype = *p++;
    n2s(p, payload);
    if (1 + 2 + payload + 16 > s->s3->rrec.length)
        return 0; /* silently discard per RFC 6520 sec. 4 */
    pl = p;
    

    Como puede ver, las diferencias son las verificaciones de longitud, que primero aseguran que el latido no esté vacío (longitud 0) y, en segundo lugar, verifica que la carga útil del latido coincida con la longitud proporcionada por el usuario. Es así de simple. Cuatro líneas de código ahora protegen a más del 66% de los servidores del catastrófico error Heartbleed.

    Cómo está parcheado

    ¿Ejecuta un servidor de cara al público que utiliza SSL? Entonces definitivamente deberías parchearlo. Solo puedo asumir que alguien ya ha configurado un marcador de guerra que verifica cada servidor que puede para detectar este error y luego explota los que lo tienen. Entonces, si está ejecutando Ubuntu, ejecute lo siguiente:

    sudo apt-get update
    sudo apt-get install -y libssl1.0.0 openssl
     
    # Verify that the Build Date is April 7th 2014 or later
    openssl version -a
    

    Luego, asegúrese de reiniciar todos y cada uno de los servicios que usan OpenSSL. Si no está seguro de que el parche haya funcionado, utilice esta herramienta descubrir.

     

    Deja una respuesta

    Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *