Spicy TLS: SSLv2 client hello support.

This lets us parse traces that use the old SSLv2 client hello format,
while actually negotiating SSLv3 and above.
This commit is contained in:
Johanna Amann 2024-08-20 16:05:36 +01:00
parent 71d2e8d961
commit 2aae73ea75
3 changed files with 110 additions and 37 deletions

View file

@ -9,6 +9,7 @@ import zeek;
import spicy;
on SSL::ClientHello -> event ssl_client_hello($conn, self.client_version, msg.record_version, cast<time>(self.random.gmt_unix_time), self.random.random_bytes, self.session_id, self.cipher_suites, self.compression_methods);
on SSL::SSL2ClientHello -> event ssl_client_hello($conn, self.client_version, 0, cast<time>(0), self.challenge, self.session_id, self.ciphers, self.compression_methods);
on SSL::ServerHello -> event ssl_server_hello($conn, server_version, msg.record_version, cast<time>(self.gmt_unix_time), self.random_bytes, self.session_id, self.cipher_suite, self.compression_method);
on SSL::ServerHelloOneThree -> event ssl_server_hello($conn, server_version, msg.record_version, cast<time>(self.gmt_unix_time), self.random_bytes, "", self.cipher_suite, 0);

View file

@ -33,6 +33,18 @@ type HandshakeType = enum {
certificate_status = 22, # RFC 3546
};
type SSL2ProtocolMessages = enum {
ssl_error = 0,
ssl_client_hello = 1,
ssl_client_master_key = 2,
ssl_client_finished = 3,
ssl_server_hello = 4,
ssl_server_verify = 5,
ssl_server_finished = 6,
ssl_request_certificate = 7,
ssl_client_certificate = 8,
};
type Extensions = enum {
server_name = 0,
max_fragment_length = 1,
@ -631,7 +643,7 @@ function set_version(version: uint16, inout sh: Share): bool {
# check for partial connection - and at the moment just disable the analyzer
# in that case, for 1:1 equivalence with binpac.
function check_partial(): bool {
if ( is_partial_tcp() ) {
if (is_partial_tcp()) {
zeek::skip_input();
return True;
}
@ -645,9 +657,9 @@ function check_partial(): bool {
# };
#
public type DTLSMessage = unit {
# %context = Share;
#
# m: Message(True);
# %context = Share;
#
# m: Message(True);
: skip bytes &eod;
on %init {
@ -664,6 +676,7 @@ public type Message = unit {
var record_version: uint16;
var dtls: bool = False;
var partial: bool = False;
var first_packet: bool = True; # needed for SSLv2, which sadly is quite stateful.
on %init {
self.handshakesink.connect(new Handshake(self, self.context()));
@ -672,7 +685,7 @@ public type Message = unit {
self.partial = check_partial();
}
: skip bytes &eod if ( self.partial );
: skip bytes &eod if(self.partial);
fragment: RecordFragmentChoice(self.handshakesink, self.alertsink, self, self.context())[];
};
@ -683,13 +696,45 @@ function is_dtls_version(version: uint16): bool {
return False;
}
# Determine if this is SSL or TLS.
type RecordFragmentChoice = unit(handshakesink: sink&, alertsink: sink&, inout msg: Message, inout sh: Share) {
content_type: uint8; # &convert=ContentType($$);
firstbyte: uint8;
# 0x80 in the first byte is a pretty god clue that this is SSLv2 - it can't be anything newer.
# In theory, the SSLv2 header can also have a 0 there if using a 3-byte size field; we don't have
# any practical examples of that, and the old analyzer never supported it even in the times that SSLv2
# existed. So we are ignoring this here.
switch ((self.firstbyte & 0x80) == 0x80) {
True -> ssl2record: SSL2Record(self.firstbyte, msg, sh);
False -> fragment: TLSRecordFragmentChoice(self.firstbyte, handshakesink, alertsink, msg, sh);
};
};
type SSL2Record = unit(lengthone: uint8, inout msg: Message, inout sh: Share) {
lengthtwo: uint8;
var length: uint16;
on lengthtwo {
self.length = (cast<uint16>(lengthone) & 0x7F)<<8 | self.lengthtwo;
}
message_type: uint8;
switch (SSL2ProtocolMessages(self.message_type)) {
SSL2ProtocolMessages::ssl_client_hello -> client_hello: SSL2ClientHello(self.length, msg, sh) &max-size=self.length;
};
on %done {
msg.first_packet = False;
}
};
# For TLS-y protocols - determine how to continue
type TLSRecordFragmentChoice = unit(content_type: uint8, handshakesink: sink&, alertsink: sink&, inout msg: Message, inout sh: Share) {
# content_type: uint8; # &convert=ContentType($$);
version: uint16;
switch (is_dtls_version(self.version)) {
True -> dtlsfragment: DTLSRecordFragment(self.content_type, handshakesink, alertsink, msg, sh);
False -> tlsfragment: TLSRecordFragment(self.content_type, handshakesink, alertsink, msg, sh);
True -> dtlsfragment: DTLSRecordFragment(content_type, handshakesink, alertsink, msg, sh);
False -> tlsfragment: TLSRecordFragment(content_type, handshakesink, alertsink, msg, sh);
};
on version {
@ -852,7 +897,6 @@ type Handshake_message = unit(inout msg: Message, inout sh: Share) {
HandshakeType::certificate_status -> certificate_status: CertificateStatus &max-size=self.length;
* -> unhandled: skip bytes &size=self.length;
};
};
type HelloRequest = unit(inout sh: Share) {
@ -914,6 +958,37 @@ type ClientHelloCookie = unit {
cookie: bytes &size=self.cookie_len;
};
type uint24 = unit {
a: bytes &size=3;
} &convert=$$.a.to_uint(spicy::ByteOrder::Big);
type SSL2ClientHello = unit(len: uint64, msg: Message, inout sh: Share) {
direction_check: DirectionCheck(sh, True); # should be sent by originator
client_version: uint16;
csuite_len: uint16;
session_len: uint16;
chal_len: uint16;
ciphers: uint24[self.csuite_len / 3];
session_id: bytes &size=self.session_len;
challenge: bytes &size=self.chal_len;
# to make the event easier
compression_methods: uint8[0];
on client_version {
if (self.client_version != SSLv2 && self.client_version != SSLv3 && self.client_version != TLSv10 && self.client_version != TLSv11 && self.client_version != TLSv12) {
spicy::decline_input("Invalid version in SSL client hello. Version: %s, self.client_version"); # Version: " + self.client_version);
zeek::skip_input();
}
}
on %init {
if (msg.first_packet == False) {
spicy::decline_input("SSLv2 client hello late in connection");
}
}
};
type ClientHello = unit(len: uint64, msg: Message, inout sh: Share) {
direction_check: DirectionCheck(sh, True); # should be sent by originator
client_version: uint16;
@ -1853,7 +1928,7 @@ on SSL::ServerHello::%done {
}
on SSL::ServerHello::%error(emsg: string) {
spicy::decline_input("error while parsing TLS server hello - " +emsg);
spicy::decline_input("error while parsing TLS server hello - " + emsg);
}
#on SSL::Handshake_message::%error(emsg: string) {

View file

@ -1,6 +1,3 @@
# Does not work in spicy version, due to missing SSLv2 handshake support
# @TEST-REQUIRES: ! grep -q "#define ENABLE_SPICY_SSL" $BUILD/zeek-config.h
# @TEST-EXEC: zeek -b -Cr $TRACES/tls/ecdsa-cert.pcap %INPUT
# @TEST-EXEC: cat intel.log > intel-all.log
# @TEST-EXEC: zeek -b -r $TRACES/tls/ssl.v3.trace %INPUT