Skip to content

Commit

Permalink
Half working secure re-negotiation
Browse files Browse the repository at this point in the history
- This change does not work fully. It's exploratory work to address
  issue #75. It's messy and hacky
  • Loading branch information
alexmgr committed Nov 18, 2016
1 parent 3062408 commit c8709a0
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 82 deletions.
51 changes: 51 additions & 0 deletions examples/tls_client_with_renegotiation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# -*- coding: utf-8 -*-

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from __future__ import with_statement
from __future__ import print_function

try:
# This import works from the project directory
from scapy_ssl_tls.ssl_tls import *
except ImportError:
# If you installed this package via pip, you just need to execute this
from scapy.layers.ssl_tls import *


tls_version = TLSVersion.TLS_1_2
ciphers = [TLSCipherSuite.ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLSCipherSuite.EMPTY_RENEGOTIATION_INFO_SCSV]
extensions = [TLSExtension() / TLSExtRenegotiationInfo(data="")]


def tls_client(ip):
with TLSSocket(socket.socket(), client=True) as tls_socket:
try:
tls_socket.connect(ip)
tls_ctx = tls_socket.tls_ctx
except socket.timeout:
print("Failed to open connection to server: %s" % (ip,), file=sys.stderr)
else:
print("Connected to server: %s" % (ip,))
try:
server_hello, server_kex = tls_socket.do_handshake(tls_version, ciphers, extensions)
client_verify_data = tls_ctx.client_ctx.verify_data
renegotiation = [TLSExtension() / TLSExtRenegotiationInfo(data=client_verify_data)]
# RSA_WITH_AES_128_CBC_SHA DHE_RSA_WITH_AES_256_CBC_SHA256
server_hello, server_kex = tls_socket.do_secure_renegotiation(tls_version, [TLSCipherSuite.RSA_WITH_AES_128_CBC_SHA], renegotiation)
# client_hello = TLSHandshake() / TLSClientHello(version=tls_version, cipher_suites=, extensions=renegotiation)
# r = tls_socket.do_round_trip(to_raw(client_hello, tls_ctx))
server_kex.show()
# http_response = tls_socket.do_round_trip(to_raw(TLSPlaintext(data="GET / HTTP/1.1\r\nHOST: localhost\r\n\r\n"), tls_socket.tls_ctx))
# http_response.show()
except TLSProtocolError as pe:
print(pe)


if __name__ == "__main__":
if len(sys.argv) > 2:
server = (sys.argv[1], int(sys.argv[2]))
else:
server = ("127.0.0.1", 8443)
tls_client(server)
79 changes: 50 additions & 29 deletions scapy_ssl_tls/ssl_tls.py
Original file line number Diff line number Diff line change
Expand Up @@ -956,11 +956,12 @@ def __getattr__(self, attr):
except AttributeError:
return getattr(self._s, attr)

def sendall(self, pkt, timeout=2):
def sendall(self, pkt, timeout=2, save=True):
prev_timeout = self._s.gettimeout()
self._s.settimeout(timeout)
self._s.sendall(str(pkt))
self.tls_ctx.insert(pkt)
if save:
self.tls_ctx.insert(pkt)
self._s.settimeout(prev_timeout)

def recvall(self, size=8192, timeout=0.5):
Expand Down Expand Up @@ -995,6 +996,9 @@ def do_handshake(self, version, ciphers, extensions=[]):
def do_round_trip(self, pkt, recv=True):
return tls_do_round_trip(self, pkt, recv)

def do_secure_renegotiation(self, version, ciphers, extensions=[]):
return tls_do_secure_renegotiation(self, version, ciphers, extensions)


# entry class
class SSL(Packet):
Expand Down Expand Up @@ -1042,6 +1046,7 @@ def do_dissect(self, raw_bytes):
payload_len = record(raw_bytes[pos:pos + record_header_len]).length
if self.tls_ctx is not None:
payload = record(raw_bytes[pos:pos + record_header_len + payload_len], ctx=self.tls_ctx)
payload = self.do_decrypt(payload)
self.tls_ctx.insert(payload)
else:
payload = record(raw_bytes[pos:pos + record_header_len + payload_len])
Expand All @@ -1053,6 +1058,23 @@ def do_dissect(self, raw_bytes):
# This will always be empty (equivalent to returning "")
return raw_bytes[pos:]

def do_decrypt(self, record):
encrypted_payload, layer = self._get_encrypted_payload(record)
if encrypted_payload is not None:
try:
if self.tls_ctx.client:
cleartext = self.tls_ctx.server_ctx.crypto_ctx.decrypt(encrypted_payload,
record.content_type)
else:
cleartext = self.tls_ctx.client_ctx.crypto_ctx.decrypt(encrypted_payload,
record.content_type)
pkt = layer(cleartext, ctx=self.tls_ctx)
record[self.guessed_next_layer].payload = pkt
# Decryption failed, raise error otherwise we'll be in inconsistent state with sender
except ValueError as ve:
raise ValueError("Decryption failed: %s" % ve)
return record

def _get_encrypted_payload(self, record):
encrypted_payload = None
decrypted_type = None
Expand All @@ -1074,38 +1096,15 @@ def _get_encrypted_payload(self, record):
decrypted_type = TLSPlaintext
return encrypted_payload, decrypted_type

def post_dissect(self, s):
if self.tls_ctx is not None:
for record in self.records:
encrypted_payload, layer = self._get_encrypted_payload(record)
if encrypted_payload is not None:
try:
if self.tls_ctx.client:
cleartext = self.tls_ctx.server_ctx.crypto_ctx.decrypt(encrypted_payload,
record.content_type)
else:
cleartext = self.tls_ctx.client_ctx.crypto_ctx.decrypt(encrypted_payload,
record.content_type)
pkt = layer(cleartext, ctx=self.tls_ctx)
original_record = record
record[self.guessed_next_layer].payload = pkt
# If the encrypted is in the history packet list, update it with the unencrypted version
if original_record in self.tls_ctx.history:
record_index = self.tls_ctx.history.index(original_record)
self.tls_ctx.history[record_index] = record
# Decryption failed, raise error otherwise we'll be in inconsistent state with sender
except ValueError as ve:
raise ValueError("Decryption failed: %s" % ve)
return s

TLS = SSL

cleartext_handler = {TLSPlaintext: lambda pkt, tls_ctx: (TLSContentType.APPLICATION_DATA, pkt[TLSPlaintext].data),
TLSFinished: lambda pkt, tls_ctx: (TLSContentType.HANDSHAKE,
str(TLSHandshake(type=TLSHandshakeType.FINISHED) /
tls_ctx.get_verify_data())),
TLSChangeCipherSpec: lambda pkt, tls_ctx: (TLSContentType.CHANGE_CIPHER_SPEC, str(pkt)),
TLSAlert: lambda pkt, tls_ctx: (TLSContentType.ALERT, str(pkt))}
TLSAlert: lambda pkt, tls_ctx: (TLSContentType.ALERT, str(pkt)),
TLSHandshake: lambda pkt, tls_ctx: (TLSContentType.HANDSHAKE, str(pkt))}


def to_raw(pkt, tls_ctx, include_record=True, compress_hook=None, pre_encrypt_hook=None, encrypt_hook=None):
Expand Down Expand Up @@ -1167,10 +1166,10 @@ def __init__(self, *args, **kwargs):
Exception.__init__(self, args[0], **kwargs)


def tls_do_round_trip(tls_socket, pkt, recv=True):
def tls_do_round_trip(tls_socket, pkt, recv=True, save=True):
resp = TLS()
try:
tls_socket.sendall(pkt)
tls_socket.sendall(pkt, save)
if recv:
resp = tls_socket.recvall()
if resp.haslayer(TLSAlert):
Expand All @@ -1184,6 +1183,7 @@ def tls_do_round_trip(tls_socket, pkt, recv=True):


def tls_do_handshake(tls_socket, version, ciphers, extensions=[]):
print(version, ciphers)
client_hello = TLSRecord(version=version) / TLSHandshake() / TLSClientHello(version=version, cipher_suites=ciphers, extensions=extensions)
resp1 = tls_do_round_trip(tls_socket, client_hello)

Expand All @@ -1195,6 +1195,27 @@ def tls_do_handshake(tls_socket, version, ciphers, extensions=[]):
return resp1, resp2


def tls_do_secure_renegotiation(tls_socket, version, ciphers, extensions=[]):
# This is hacky, depends on insertion sequence
tls_ctx = tls_socket.tls_ctx
client_hello = TLSHandshake() / TLSClientHello(version=version, cipher_suites=ciphers, extensions=extensions)
tls_ctx.insert(client_hello)
resp1 = tls_do_round_trip(tls_socket, to_raw(client_hello, tls_ctx), save=False)

client_key_exchange = TLSHandshake() / tls_ctx.get_client_kex_data()
client_ccs = TLSChangeCipherSpec()

print(tls_ctx)
tls_do_round_trip(tls_socket, to_raw(client_key_exchange, tls_ctx), False, save=False)
tls_ctx.insert(client_key_exchange)
tls_do_round_trip(tls_socket, to_raw(client_ccs, tls_ctx), False, save=False)
tls_ctx.insert(client_ccs)

print(tls_ctx)
resp2 = tls_do_round_trip(tls_socket, to_raw(TLSFinished(), tls_socket.tls_ctx))
return resp1, resp2


def tls_fragment_payload(pkt, record=None, size=2**14):
if size <= 0:
raise ValueError("Fragment size must be strictly positive")
Expand Down
Loading

0 comments on commit c8709a0

Please sign in to comment.