From 3f91557c3ea03a10f532af327c5d047d19ddd48a Mon Sep 17 00:00:00 2001 From: Johanna Amann Date: Thu, 22 Jun 2023 14:07:18 +0100 Subject: [PATCH] Spicy TLS: raise ssl_established event for TLS 1.3 --- .typos.toml | 1 + src/analyzer/protocol/tls/TLS.evt | 5 +-- src/analyzer/protocol/tls/TLS.spicy | 55 +++++++++++++++++++---------- 3 files changed, 41 insertions(+), 20 deletions(-) diff --git a/.typos.toml b/.typos.toml index cc29d0abb1..5e55e1483f 100644 --- a/.typos.toml +++ b/.typos.toml @@ -67,5 +67,6 @@ uses_seh = "uses_seh" [default.extend-words] caf = "caf" helo = "helo" +inout = "inout" # Seems we use this in the management framework requestor = "requestor" diff --git a/src/analyzer/protocol/tls/TLS.evt b/src/analyzer/protocol/tls/TLS.evt index 365962e406..81f69aa24c 100644 --- a/src/analyzer/protocol/tls/TLS.evt +++ b/src/analyzer/protocol/tls/TLS.evt @@ -25,8 +25,9 @@ on TLS::NewSessionTicket -> event ssl_session_ticket_handshake($conn, self.ticke on TLS::PlaintextRecord::ccs -> event ssl_change_cipher_spec($conn, $is_orig); on TLS::PlaintextRecord::ccs if ( msg.context().ccs_seen == 2 ) -> event ssl_established($conn); -on TLS::PlaintextRecord::appdata if ( self.encrypted == False ) -> event ssl_plaintext_data($conn, TLS::get_direction(sh), msg.record_version, content_type, self.length); -on TLS::PlaintextRecord::appdata if ( self.encrypted == True ) -> event ssl_encrypted_data($conn, TLS::get_direction(sh), msg.record_version, content_type, self.length); +on TLS::PlaintextRecord::trigger_one if ( sh.both_sides_encrypted_first_time == True ) -> event ssl_established($conn); +on TLS::PlaintextRecord::trigger_two if ( self.encrypted == False ) -> event ssl_plaintext_data($conn, TLS::get_direction(sh), msg.record_version, content_type, self.length); +on TLS::PlaintextRecord::trigger_two if ( self.encrypted == True ) -> event ssl_encrypted_data($conn, TLS::get_direction(sh), msg.record_version, content_type, self.length); on TLS::Handshake_message -> event ssl_handshake_message($conn, TLS::get_direction(sh), self.msg_type, self.length); diff --git a/src/analyzer/protocol/tls/TLS.spicy b/src/analyzer/protocol/tls/TLS.spicy index f74898a66b..ad0021e8fa 100644 --- a/src/analyzer/protocol/tls/TLS.spicy +++ b/src/analyzer/protocol/tls/TLS.spicy @@ -568,6 +568,7 @@ type Share = unit { # var skipping: bool; var client_encrypted: bool; var server_encrypted: bool; + var both_sides_encrypted_first_time: bool; on %init { self.ccs_seen = 0; @@ -578,6 +579,7 @@ type Share = unit { self.flip_already_alerted = False; self.server_encrypted = False; self.client_encrypted = False; + self.both_sides_encrypted_first_time = False; } }; @@ -588,7 +590,7 @@ function get_encrypted(sh: Share) : bool { return sh.server_encrypted; } -function startEncryption(handshakesink: sink, alertsink: sink, inout sh: Share) { +function startEncryption(inout handshakesink: sink, inout alertsink: sink, inout sh: Share) { local old_state: bool; if ( get_direction(sh) ) { @@ -605,19 +607,19 @@ function startEncryption(handshakesink: sink, alertsink: sink, inout sh: Share) print "Closing sink"; handshakesink.close(); alertsink.close(); - sh.ccs_seen++; # do we need this? + + if ( sh.client_encrypted && sh.server_encrypted ) { + print "Encrypted first time"; + sh.both_sides_encrypted_first_time = True; + } } } # This function is called several times in certain circumstances. -# If it is called twice, it is first called due to the supported_versions -# field in the server hello - and then again due to the outer version in -# the server hello. So - once we have a version here, let's just stick -# with it. +# If it is called twice, it is first called due to the outer version in +# the server hello - and then again due to the supported_versions +# field in the server hello. function set_version(version: uint16, inout sh: Share) : bool { - if ( sh.parsed_version != UNKNOWN_VERSION ) - return False; - sh.parsed_version = version; if ( version == TLSv13 || version/0xFF == 0x7F ) sh.tls_13 = True; @@ -698,7 +700,10 @@ type DTLSRecordFragment = unit(content_type: uint8, handshakesink: sink, alertsi type PlaintextRecord = unit(content_type: uint8, handshakesink: sink, alertsink: sink, inout msg: Message, inout sh: Share) { length: uint16; - var encrypted: bool = determine_encryption_on(self, handshakesink, alertsink, msg, sh); + var encrypted: bool; + # convenient triggers to hang stuff in the evt file from. Two of them for event ordering :) + trigger_one: bytes &size=0; + trigger_two: bytes &size=0; switch ( ContentType(content_type) ) { ContentType::handshake -> : bytes &size=self.length -> handshakesink; ContentType::application_data -> { @@ -717,6 +722,10 @@ type PlaintextRecord = unit(content_type: uint8, handshakesink: sink, alertsink: print "Unhandled content type", content_type; } + on length { + self.encrypted = determine_encryption_on(self, content_type, handshakesink, alertsink, msg, sh); + } + on ccs { # I know this looks a bit weird. Basically - in TLS 1.3, CCS is meaningless # fluff that just is used to pretend to TLS 1.2 devices listening in that @@ -726,8 +735,14 @@ type PlaintextRecord = unit(content_type: uint8, handshakesink: sink, alertsink: if ( sh.tls_13 ) return; + print "CCS accepted"; + sh.ccs_seen++; # used in TLS.evt startEncryption(handshakesink, alertsink, sh); } + on trigger_two { + if ( sh.both_sides_encrypted_first_time ) + sh.both_sides_encrypted_first_time = False; + } }; ## So - this falls a bit under the envelope of dirty hack - but I don't @@ -750,7 +765,7 @@ type PlaintextRecord = unit(content_type: uint8, handshakesink: sink, alertsink: ## a bit of context here - we can't really say when we get the first packet ## that uses the final cryptographic key material - and will contain content ## data. We just don't have that information available in TLS 1.3 anymore. -function determine_encryption_on(pr: PlaintextRecord, handshakesink: sink, alertsink: sink, inout msg: Message, inout sh: Share) : bool { +function determine_encryption_on(pr: PlaintextRecord, content_type: uint8, handshakesink: sink, alertsink: sink, inout msg: Message, inout sh: Share) : bool { if ( get_encrypted(sh) ) return True; @@ -758,6 +773,9 @@ function determine_encryption_on(pr: PlaintextRecord, handshakesink: sink, alert if ( pr.length == 0 ) return False; + if ( content_type != 23 ) # application_data + return False; + ## in theory, we should check for TLS13 or draft-TLS13 instead of doing the reverse. ## But - people use weird version numbers. And all of those weird version numbers are ## some sort of TLS1.3. So - let's do it this way round instead. @@ -896,7 +914,7 @@ type ClientHello = unit(len: uint64, msg: Message, inout sh: Share) { } }; -# Draft versions of TLS 1.3 had a diffent server hello - distinguish here +# Draft versions of TLS 1.3 had a different server hello - distinguish here type ServerHelloChoice = unit(len: uint64, msg: Message, inout sh: Share) { direction_check: DirectionCheck(sh, False); # should be sent by responder sv : bitfield(16) { @@ -907,8 +925,8 @@ type ServerHelloChoice = unit(len: uint64, msg: Message, inout sh: Share) { var parsed_version: uint16; switch ( self.parsed_version ) { - TLSv13, TLSv13_draft, 0x7F00 -> : ServerHelloOneThree(len, msg, sh, self.sv.server_version); - * -> : ServerHello(len, msg, sh, self.sv.server_version); + TLSv13, TLSv13_draft, 0x7F00 -> sh_one_three : ServerHelloOneThree(len, msg, sh, self.sv.server_version); + * -> sh_normal : ServerHello(len, msg, sh, self.sv.server_version); }; on sv { @@ -927,7 +945,7 @@ type ServerHelloChoice = unit(len: uint64, msg: Message, inout sh: Share) { } }; -# Draft versions of TLS 1.3 had a diffent server hello. +# Draft versions of TLS 1.3 had a different server hello. type ServerHelloOneThree = unit(len: uint64, msg: Message, inout sh: Share, server_version: uint16) { random_bytes: bytes &size=32; gmt_unix_time: uint32 &parse-from=self.random_bytes; @@ -986,7 +1004,7 @@ type Extension = unit(inout sh: Share, client_hello: bool) { }; on code { - print "Extension", self.code; + print "Extension", self.code, client_hello; } on unknown { print "Unknown extension", self.code; @@ -1050,11 +1068,12 @@ type SupportedVersions = unit(sh: Share) { versions: uint16[self.length/2]; }; -# If the server sends it, this is the authorative version. Set it. +# If the server sends it, this is the authoritative version. Set it. type OneSupportedVersion = unit(inout sh: Share) { version: uint16; on version { + print "Setting version to ", self.version; set_version(self.version, sh); } }; @@ -1086,7 +1105,7 @@ type ClientHelloKeyShare = unit(sh: Share) { }; type KeyShare = unit(client_hello: bool, sh: Share, length: uint16) { - switch (client_hello ) { + switch (client_hello) { True -> client_hello_keyshare : ClientHelloKeyShare(sh); False -> server_hello_keyshare : ServerHelloKeyShareChoice(sh, length); };