# This Zeek script appends JA3 to ssl.log # Version 1.4 (January 2020) # # Authors: John B. Althouse (jalthouse@salesforce.com) & Jeff Atkinson (jatkinson@salesforce.com) # # Copyright (c) 2017, salesforce.com, inc. # All rights reserved. # Licensed under the BSD 3-Clause license. # For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause module JA3; export { redef enum Log::ID += { LOG }; } type TLSFPStorage: record { client_version: count &default=0 &log; client_ciphers: string &default="" &log; extensions: string &default="" &log; e_curves: string &default="" &log; ec_point_fmt: string &default="" &log; }; redef record connection += { tlsfp: TLSFPStorage &optional; }; redef record SSL::Info += { ja3: string &optional &log; # LOG FIELD VALUES ## # ja3_version: string &optional &log; # ja3_ciphers: string &optional &log; # ja3_extensions: string &optional &log; # ja3_ec: string &optional &log; # ja3_ec_fmt: string &optional &log; }; # Google. https://tools.ietf.org/html/draft-davidben-tls-grease-01 const grease: set[int] = { 2570, 6682, 10794, 14906, 19018, 23130, 27242, 31354, 35466, 39578, 43690, 47802, 51914, 56026, 60138, 64250 }; const sep = "-"; event zeek_init() { Log::create_stream(JA3::LOG,[$columns=TLSFPStorage, $path="tlsfp"]); } event ssl_extension(c: connection, is_orig: bool, code: count, val: string) { if ( is_orig == T ) { if ( code in grease ) { return; } if ( ! c?$tlsfp ){ c$tlsfp=TLSFPStorage(); } if ( c$tlsfp$extensions == "" ) { c$tlsfp$extensions = cat(code); } else { c$tlsfp$extensions = string_cat(c$tlsfp$extensions, sep,cat(code)); } } } event ssl_extension_ec_point_formats(c: connection, is_orig: bool, point_formats: index_vec) { if ( is_orig == T ) { if ( !c?$tlsfp ) c$tlsfp=TLSFPStorage(); for ( i in point_formats ) { if ( point_formats[i] in grease ) { next; } if ( c$tlsfp$ec_point_fmt == "" ) { c$tlsfp$ec_point_fmt += cat(point_formats[i]); } else { c$tlsfp$ec_point_fmt += string_cat(sep,cat(point_formats[i])); } } } } event ssl_extension_elliptic_curves(c: connection, is_orig: bool, curves: index_vec) { if ( !c?$tlsfp ) c$tlsfp=TLSFPStorage(); if ( is_orig == T ) { for ( i in curves ) { if ( curves[i] in grease ) { next; } if ( c$tlsfp$e_curves == "" ) { c$tlsfp$e_curves += cat(curves[i]); } else { c$tlsfp$e_curves += string_cat(sep,cat(curves[i])); } } } } @if ( ( Version::number >= 20600 ) || ( Version::number == 20500 && Version::info$commit >= 944 ) ) event ssl_client_hello(c: connection, version: count, record_version: count, possible_ts: time, client_random: string, session_id: string, ciphers: index_vec, comp_methods: index_vec) &priority=1 @else event ssl_client_hello(c: connection, version: count, possible_ts: time, client_random: string, session_id: string, ciphers: index_vec) &priority=1 @endif { if ( !c?$tlsfp ) c$tlsfp=TLSFPStorage(); c$tlsfp$client_version = version; for ( i in ciphers ) { if ( ciphers[i] in grease ) { next; } if ( c$tlsfp$client_ciphers == "" ) { c$tlsfp$client_ciphers += cat(ciphers[i]); } else { c$tlsfp$client_ciphers += string_cat(sep,cat(ciphers[i])); } } local sep2 = ","; local ja3_string = string_cat(cat(c$tlsfp$client_version),sep2,c$tlsfp$client_ciphers,sep2,c$tlsfp$extensions,sep2,c$tlsfp$e_curves,sep2,c$tlsfp$ec_point_fmt); local tlsfp_1 = md5_hash(ja3_string); c$ssl$ja3 = tlsfp_1; # LOG FIELD VALUES ## #c$ssl$ja3_version = cat(c$tlsfp$client_version); #c$ssl$ja3_ciphers = c$tlsfp$client_ciphers; #c$ssl$ja3_extensions = c$tlsfp$extensions; #c$ssl$ja3_ec = c$tlsfp$e_curves; #c$ssl$ja3_ec_fmt = c$tlsfp$ec_point_fmt; # # FOR DEBUGGING ## #print "JA3: "+tlsfp_1+" Fingerprint String: "+ja3_string; }