mirror of
https://github.com/zeek/zeek.git
synced 2025-10-09 18:18:19 +00:00
Merge remote-tracking branch 'origin/topic/vladg/kerberos'
* origin/topic/vladg/kerberos: (27 commits) Add Kerberos to NEWS. Add Kerberos memleak btest. Add Kerberos analyzer btest. Update baselines for Kerberos analyzer. Add known ports to krb/main.bro KRB: Clean up krb.log a bit. Kerberos: Remove debugging output. Kerberos: Fix a memleak. Kerberos: A couple small tweaks. Kerberos: Fix parsing of the cipher in tickets, and add it to the log. Kerberos: A couple more formatting fixes. Change krb Info string to success bool Clean up formatting. Documentation update, and rework events a bit. Add support for the SAFE message type. Add support for AP_REQ, AP_REP, PRIV, and CRED message types. Fix parsing error for KRB_Ticket_Sequence Continue clean-up. Some reformatting, removing hard-coded values, documentation, etc. Kerberos analyzer updates: - Split up the (quite length) krb-protocol.pac into krb-protocol, krb-defs, krb-types and krb-padata - Add some supporting types to get rid of awkward and difficult to read case true/false statements - Clean up the conversion code in krb-analyzer.pac Improve Kerberos DPD and fix a few parse errors. ... BIT-1369 #merged
This commit is contained in:
commit
87eb5ef811
37 changed files with 3436 additions and 5 deletions
3
scripts/base/protocols/krb/__load__.bro
Normal file
3
scripts/base/protocols/krb/__load__.bro
Normal file
|
@ -0,0 +1,3 @@
|
|||
@load ./main
|
||||
@load ./files
|
||||
@load-sigs ./dpd.sig
|
99
scripts/base/protocols/krb/consts.bro
Normal file
99
scripts/base/protocols/krb/consts.bro
Normal file
|
@ -0,0 +1,99 @@
|
|||
module KRB;
|
||||
|
||||
export {
|
||||
|
||||
const error_msg: table[count] of string = {
|
||||
[0] = "KDC_ERR_NONE",
|
||||
[1] = "KDC_ERR_NAME_EXP",
|
||||
[2] = "KDC_ERR_SERVICE_EXP",
|
||||
[3] = "KDC_ERR_BAD_PVNO",
|
||||
[4] = "KDC_ERR_C_OLD_MAST_KVNO",
|
||||
[5] = "KDC_ERR_S_OLD_MAST_KVNO",
|
||||
[6] = "KDC_ERR_C_PRINCIPAL_UNKNOWN",
|
||||
[7] = "KDC_ERR_S_PRINCIPAL_UNKNOWN",
|
||||
[8] = "KDC_ERR_PRINCIPAL_NOT_UNIQUE",
|
||||
[9] = "KDC_ERR_NULL_KEY",
|
||||
[10] = "KDC_ERR_CANNOT_POSTDATE",
|
||||
[11] = "KDC_ERR_NEVER_VALID",
|
||||
[12] = "KDC_ERR_POLICY",
|
||||
[13] = "KDC_ERR_BADOPTION",
|
||||
[14] = "KDC_ERR_ETYPE_NOSUPP",
|
||||
[15] = "KDC_ERR_SUMTYPE_NOSUPP",
|
||||
[16] = "KDC_ERR_PADATA_TYPE_NOSUPP",
|
||||
[17] = "KDC_ERR_TRTYPE_NOSUPP",
|
||||
[18] = "KDC_ERR_CLIENT_REVOKED",
|
||||
[19] = "KDC_ERR_SERVICE_REVOKED",
|
||||
[20] = "KDC_ERR_TGT_REVOKED",
|
||||
[21] = "KDC_ERR_CLIENT_NOTYET",
|
||||
[22] = "KDC_ERR_SERVICE_NOTYET",
|
||||
[23] = "KDC_ERR_KEY_EXPIRED",
|
||||
[24] = "KDC_ERR_PREAUTH_FAILED",
|
||||
[25] = "KDC_ERR_PREAUTH_REQUIRED",
|
||||
[26] = "KDC_ERR_SERVER_NOMATCH",
|
||||
[27] = "KDC_ERR_MUST_USE_USER2USER",
|
||||
[28] = "KDC_ERR_PATH_NOT_ACCEPTED",
|
||||
[29] = "KDC_ERR_SVC_UNAVAILABLE",
|
||||
[31] = "KRB_AP_ERR_BAD_INTEGRITY",
|
||||
[32] = "KRB_AP_ERR_TKT_EXPIRED",
|
||||
[33] = "KRB_AP_ERR_TKT_NYV",
|
||||
[34] = "KRB_AP_ERR_REPEAT",
|
||||
[35] = "KRB_AP_ERR_NOT_US",
|
||||
[36] = "KRB_AP_ERR_BADMATCH",
|
||||
[37] = "KRB_AP_ERR_SKEW",
|
||||
[38] = "KRB_AP_ERR_BADADDR",
|
||||
[39] = "KRB_AP_ERR_BADVERSION",
|
||||
[40] = "KRB_AP_ERR_MSG_TYPE",
|
||||
[41] = "KRB_AP_ERR_MODIFIED",
|
||||
[42] = "KRB_AP_ERR_BADORDER",
|
||||
[44] = "KRB_AP_ERR_BADKEYVER",
|
||||
[45] = "KRB_AP_ERR_NOKEY",
|
||||
[46] = "KRB_AP_ERR_MUT_FAIL",
|
||||
[47] = "KRB_AP_ERR_BADDIRECTION",
|
||||
[48] = "KRB_AP_ERR_METHOD",
|
||||
[49] = "KRB_AP_ERR_BADSEQ",
|
||||
[50] = "KRB_AP_ERR_INAPP_CKSUM",
|
||||
[51] = "KRB_AP_PATH_NOT_ACCEPTED",
|
||||
[52] = "KRB_ERR_RESPONSE_TOO_BIG",
|
||||
[60] = "KRB_ERR_GENERIC",
|
||||
[61] = "KRB_ERR_FIELD_TOOLONG",
|
||||
[62] = "KDC_ERROR_CLIENT_NOT_TRUSTED",
|
||||
[63] = "KDC_ERROR_KDC_NOT_TRUSTED",
|
||||
[64] = "KDC_ERROR_INVALID_SIG",
|
||||
[65] = "KDC_ERR_KEY_TOO_WEAK",
|
||||
[66] = "KDC_ERR_CERTIFICATE_MISMATCH",
|
||||
[67] = "KRB_AP_ERR_NO_TGT",
|
||||
[68] = "KDC_ERR_WRONG_REALM",
|
||||
[69] = "KRB_AP_ERR_USER_TO_USER_REQUIRED",
|
||||
[70] = "KDC_ERR_CANT_VERIFY_CERTIFICATE",
|
||||
[71] = "KDC_ERR_INVALID_CERTIFICATE",
|
||||
[72] = "KDC_ERR_REVOKED_CERTIFICATE",
|
||||
[73] = "KDC_ERR_REVOCATION_STATUS_UNKNOWN",
|
||||
[74] = "KDC_ERR_REVOCATION_STATUS_UNAVAILABLE",
|
||||
[75] = "KDC_ERR_CLIENT_NAME_MISMATCH",
|
||||
[76] = "KDC_ERR_KDC_NAME_MISMATCH",
|
||||
};
|
||||
|
||||
const cipher_name: table[count] of string = {
|
||||
[1] = "des-cbc-crc",
|
||||
[2] = "des-cbc-md4",
|
||||
[3] = "des-cbc-md5",
|
||||
[5] = "des3-cbc-md5",
|
||||
[7] = "des3-cbc-sha1",
|
||||
[9] = "dsaWithSHA1-CmsOID",
|
||||
[10] = "md5WithRSAEncryption-CmsOID",
|
||||
[11] = "sha1WithRSAEncryption-CmsOID",
|
||||
[12] = "rc2CBC-EnvOID",
|
||||
[13] = "rsaEncryption-EnvOID",
|
||||
[14] = "rsaES-OAEP-ENV-OID",
|
||||
[15] = "des-ede3-cbc-Env-OID",
|
||||
[16] = "des3-cbc-sha1-kd",
|
||||
[17] = "aes128-cts-hmac-sha1-96",
|
||||
[18] = "aes256-cts-hmac-sha1-96",
|
||||
[23] = "rc4-hmac",
|
||||
[24] = "rc4-hmac-exp",
|
||||
[25] = "camellia128-cts-cmac",
|
||||
[26] = "camellia256-cts-cmac",
|
||||
[65] = "subkey-keymaterial",
|
||||
};
|
||||
|
||||
}
|
26
scripts/base/protocols/krb/dpd.sig
Normal file
26
scripts/base/protocols/krb/dpd.sig
Normal file
|
@ -0,0 +1,26 @@
|
|||
# This is the ASN.1 encoded version and message type headers
|
||||
|
||||
signature dpd_krb_udp_requests {
|
||||
ip-proto == udp
|
||||
payload /(\x6a|\x6c).{1,4}\x30.{1,4}\xa1\x03\x02\x01\x05\xa2\x03\x02\x01/
|
||||
enable "krb"
|
||||
}
|
||||
|
||||
signature dpd_krb_udp_replies {
|
||||
ip-proto == udp
|
||||
payload /(\x6b|\x6d|\x7e).{1,4}\x30.{1,4}\xa0\x03\x02\x01\x05\xa1\x03\x02\x01/
|
||||
enable "krb"
|
||||
}
|
||||
|
||||
signature dpd_krb_tcp_requests {
|
||||
ip-proto == tcp
|
||||
payload /.{4}(\x6a|\x6c).{1,4}\x30.{1,4}\xa1\x03\x02\x01\x05\xa2\x03\x02\x01/
|
||||
enable "krb_tcp"
|
||||
}
|
||||
|
||||
signature dpd_krb_tcp_replies {
|
||||
ip-proto == tcp
|
||||
payload /.{4}(\x6b|\x6d|\x7e).{1,4}\x30.{1,4}\xa0\x03\x02\x01\x05\xa1\x03\x02\x01/
|
||||
enable "krb_tcp"
|
||||
}
|
||||
|
142
scripts/base/protocols/krb/files.bro
Normal file
142
scripts/base/protocols/krb/files.bro
Normal file
|
@ -0,0 +1,142 @@
|
|||
@load ./main
|
||||
@load base/utils/conn-ids
|
||||
@load base/frameworks/files
|
||||
@load base/files/x509
|
||||
|
||||
module KRB;
|
||||
|
||||
export {
|
||||
redef record Info += {
|
||||
# Client certificate
|
||||
client_cert: Files::Info &optional;
|
||||
# Subject of client certificate, if any
|
||||
client_cert_subject: string &log &optional;
|
||||
# File unique ID of client cert, if any
|
||||
client_cert_fuid: string &log &optional;
|
||||
|
||||
# Server certificate
|
||||
server_cert: Files::Info &optional;
|
||||
# Subject of server certificate, if any
|
||||
server_cert_subject: string &log &optional;
|
||||
# File unique ID of server cert, if any
|
||||
server_cert_fuid: string &log &optional;
|
||||
};
|
||||
|
||||
## Default file handle provider for KRB.
|
||||
global get_file_handle: function(c: connection, is_orig: bool): string;
|
||||
|
||||
## Default file describer for KRB.
|
||||
global describe_file: function(f: fa_file): string;
|
||||
}
|
||||
|
||||
function get_file_handle(c: connection, is_orig: bool): string
|
||||
{
|
||||
# Unused. File handles are generated in the analyzer.
|
||||
return "";
|
||||
}
|
||||
|
||||
function describe_file(f: fa_file): string
|
||||
{
|
||||
if ( f$source != "KRB_TCP" && f$source != "KRB" )
|
||||
return "";
|
||||
|
||||
if ( ! f?$info || ! f$info?$x509 || ! f$info$x509?$certificate )
|
||||
return "";
|
||||
|
||||
# It is difficult to reliably describe a certificate - especially since
|
||||
# we do not know when this function is called (hence, if the data structures
|
||||
# are already populated).
|
||||
#
|
||||
# Just return a bit of our connection information and hope that that is good enough.
|
||||
for ( cid in f$conns )
|
||||
{
|
||||
if ( f$conns[cid]?$krb )
|
||||
{
|
||||
local c = f$conns[cid];
|
||||
return cat(c$id$resp_h, ":", c$id$resp_p);
|
||||
}
|
||||
}
|
||||
|
||||
return cat("Serial: ", f$info$x509$certificate$serial, " Subject: ",
|
||||
f$info$x509$certificate$subject, " Issuer: ",
|
||||
f$info$x509$certificate$issuer);
|
||||
}
|
||||
|
||||
event bro_init() &priority=5
|
||||
{
|
||||
Files::register_protocol(Analyzer::ANALYZER_KRB_TCP,
|
||||
[$get_file_handle = KRB::get_file_handle,
|
||||
$describe = KRB::describe_file]);
|
||||
|
||||
Files::register_protocol(Analyzer::ANALYZER_KRB,
|
||||
[$get_file_handle = KRB::get_file_handle,
|
||||
$describe = KRB::describe_file]);
|
||||
}
|
||||
|
||||
event file_over_new_connection(f: fa_file, c: connection, is_orig: bool) &priority=5
|
||||
{
|
||||
if ( f$source != "KRB_TCP" && f$source != "KRB" )
|
||||
return;
|
||||
|
||||
local info: Info;
|
||||
|
||||
if ( ! c?$krb )
|
||||
{
|
||||
info$ts = network_time();
|
||||
info$uid = c$uid;
|
||||
info$id = c$id;
|
||||
}
|
||||
else
|
||||
info = c$krb;
|
||||
|
||||
if ( is_orig )
|
||||
{
|
||||
info$client_cert = f$info;
|
||||
info$client_cert_fuid = f$id;
|
||||
}
|
||||
else
|
||||
{
|
||||
info$server_cert = f$info;
|
||||
info$server_cert_fuid = f$id;
|
||||
}
|
||||
|
||||
c$krb = info;
|
||||
|
||||
Files::add_analyzer(f, Files::ANALYZER_X509);
|
||||
# Always calculate hashes. They are not necessary for base scripts
|
||||
# but very useful for identification, and required for policy scripts
|
||||
Files::add_analyzer(f, Files::ANALYZER_MD5);
|
||||
Files::add_analyzer(f, Files::ANALYZER_SHA1);
|
||||
}
|
||||
|
||||
function fill_in_subjects(c: connection)
|
||||
{
|
||||
if ( !c?$krb )
|
||||
return;
|
||||
|
||||
if ( c$krb?$client_cert && c$krb$client_cert?$x509 && c$krb$client_cert$x509?$certificate )
|
||||
c$krb$client_cert_subject = c$krb$client_cert$x509$certificate$subject;
|
||||
|
||||
if ( c$krb?$server_cert && c$krb$server_cert?$x509 && c$krb$server_cert$x509?$certificate )
|
||||
c$krb$server_cert_subject = c$krb$server_cert$x509$certificate$subject;
|
||||
}
|
||||
|
||||
event krb_error(c: connection, msg: Error_Msg)
|
||||
{
|
||||
fill_in_subjects(c);
|
||||
}
|
||||
|
||||
event krb_as_response(c: connection, msg: KDC_Response)
|
||||
{
|
||||
fill_in_subjects(c);
|
||||
}
|
||||
|
||||
event krb_tgs_response(c: connection, msg: KDC_Response)
|
||||
{
|
||||
fill_in_subjects(c);
|
||||
}
|
||||
|
||||
event connection_state_remove(c: connection)
|
||||
{
|
||||
fill_in_subjects(c);
|
||||
}
|
250
scripts/base/protocols/krb/main.bro
Normal file
250
scripts/base/protocols/krb/main.bro
Normal file
|
@ -0,0 +1,250 @@
|
|||
##! Implements base functionality for KRB analysis. Generates the krb.log file.
|
||||
|
||||
module KRB;
|
||||
|
||||
@load ./consts
|
||||
|
||||
export {
|
||||
redef enum Log::ID += { LOG };
|
||||
|
||||
type Info: record {
|
||||
## Timestamp for when the event happened.
|
||||
ts: time &log;
|
||||
## Unique ID for the connection.
|
||||
uid: string &log;
|
||||
## The connection's 4-tuple of endpoint addresses/ports.
|
||||
id: conn_id &log;
|
||||
|
||||
## Request type - Authentication Service ("AS") or
|
||||
## Ticket Granting Service ("TGS")
|
||||
request_type: string &log &optional;
|
||||
## Client
|
||||
client: string &log &optional;
|
||||
## Service
|
||||
service: string &log;
|
||||
|
||||
## Request result
|
||||
success: bool &log &optional;
|
||||
## Error code
|
||||
error_code: count &optional;
|
||||
## Error message
|
||||
error_msg: string &log &optional;
|
||||
|
||||
## Ticket valid from
|
||||
from: time &log &optional;
|
||||
## Ticket valid till
|
||||
till: time &log &optional;
|
||||
## Ticket encryption type
|
||||
cipher: string &log &optional;
|
||||
|
||||
## Forwardable ticket requested
|
||||
forwardable: bool &log &optional;
|
||||
## Renewable ticket requested
|
||||
renewable: bool &log &optional;
|
||||
|
||||
## We've already logged this
|
||||
logged: bool &default=F;
|
||||
};
|
||||
|
||||
## The server response error texts which are *not* logged.
|
||||
const ignored_errors: set[string] = {
|
||||
# This will significantly increase the noisiness of the log.
|
||||
# However, one attack is to iterate over principals, looking
|
||||
# for ones that don't require preauth, and then performn
|
||||
# an offline attack on that ticket. To detect that attack,
|
||||
# log NEEDED_PREAUTH.
|
||||
"NEEDED_PREAUTH",
|
||||
# This is a more specific version of NEEDED_PREAUTH that's used
|
||||
# by Windows AD Kerberos.
|
||||
"Need to use PA-ENC-TIMESTAMP/PA-PK-AS-REQ",
|
||||
} &redef;
|
||||
|
||||
## Event that can be handled to access the KRB record as it is sent on
|
||||
## to the logging framework.
|
||||
global log_krb: event(rec: Info);
|
||||
}
|
||||
|
||||
redef record connection += {
|
||||
krb: Info &optional;
|
||||
};
|
||||
|
||||
const tcp_ports = { 88/tcp };
|
||||
const udp_ports = { 88/udp };
|
||||
redef likely_server_ports += { tcp_ports, udp_ports };
|
||||
|
||||
event bro_init() &priority=5
|
||||
{
|
||||
Analyzer::register_for_ports(Analyzer::ANALYZER_KRB, udp_ports);
|
||||
Analyzer::register_for_ports(Analyzer::ANALYZER_KRB_TCP, tcp_ports);
|
||||
Log::create_stream(KRB::LOG, [$columns=Info, $ev=log_krb]);
|
||||
}
|
||||
|
||||
event krb_error(c: connection, msg: Error_Msg) &priority=5
|
||||
{
|
||||
local info: Info;
|
||||
|
||||
if ( msg?$error_text && msg$error_text in ignored_errors )
|
||||
{
|
||||
if ( c?$krb ) delete c$krb;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( c?$krb && c$krb$logged )
|
||||
return;
|
||||
|
||||
if ( c?$krb )
|
||||
info = c$krb;
|
||||
|
||||
if ( ! info?$ts )
|
||||
{
|
||||
info$ts = network_time();
|
||||
info$uid = c$uid;
|
||||
info$id = c$id;
|
||||
}
|
||||
|
||||
if ( ! info?$client && ( msg?$client_name || msg?$client_realm ) )
|
||||
info$client = fmt("%s%s", msg?$client_name ? msg$client_name + "/" : "",
|
||||
msg?$client_realm ? msg$client_realm : "");
|
||||
|
||||
info$service = msg$service_name;
|
||||
info$success = F;
|
||||
|
||||
info$error_code = msg$error_code;
|
||||
|
||||
if ( msg?$error_text ) info$error_msg = msg$error_text;
|
||||
else if ( msg$error_code in error_msg ) info$error_msg = error_msg[msg$error_code];
|
||||
|
||||
c$krb = info;
|
||||
}
|
||||
|
||||
event krb_error(c: connection, msg: Error_Msg) &priority=-5
|
||||
{
|
||||
if ( c?$krb )
|
||||
{
|
||||
Log::write(KRB::LOG, c$krb);
|
||||
c$krb$logged = T;
|
||||
}
|
||||
}
|
||||
|
||||
event krb_as_request(c: connection, msg: KDC_Request) &priority=5
|
||||
{
|
||||
if ( c?$krb && c$krb$logged )
|
||||
return;
|
||||
|
||||
local info: Info;
|
||||
|
||||
if ( !c?$krb )
|
||||
{
|
||||
info$ts = network_time();
|
||||
info$uid = c$uid;
|
||||
info$id = c$id;
|
||||
}
|
||||
else
|
||||
info = c$krb;
|
||||
|
||||
info$request_type = "AS";
|
||||
info$client = fmt("%s/%s", msg$client_name, msg$service_realm);
|
||||
info$service = msg$service_name;
|
||||
|
||||
if ( msg?$from )
|
||||
info$from = msg$from;
|
||||
|
||||
info$till = msg$till;
|
||||
|
||||
info$forwardable = msg$kdc_options$forwardable;
|
||||
info$renewable = msg$kdc_options$renewable;
|
||||
|
||||
c$krb = info;
|
||||
}
|
||||
|
||||
event krb_tgs_request(c: connection, msg: KDC_Request) &priority=5
|
||||
{
|
||||
if ( c?$krb && c$krb$logged )
|
||||
return;
|
||||
|
||||
local info: Info;
|
||||
info$ts = network_time();
|
||||
info$uid = c$uid;
|
||||
info$id = c$id;
|
||||
info$request_type = "TGS";
|
||||
info$service = msg$service_name;
|
||||
if ( msg?$from ) info$from = msg$from;
|
||||
info$till = msg$till;
|
||||
|
||||
info$forwardable = msg$kdc_options$forwardable;
|
||||
info$renewable = msg$kdc_options$renewable;
|
||||
|
||||
c$krb = info;
|
||||
}
|
||||
|
||||
event krb_as_response(c: connection, msg: KDC_Response) &priority=5
|
||||
{
|
||||
local info: Info;
|
||||
|
||||
if ( c?$krb && c$krb$logged )
|
||||
return;
|
||||
|
||||
if ( c?$krb )
|
||||
info = c$krb;
|
||||
|
||||
if ( ! info?$ts )
|
||||
{
|
||||
info$ts = network_time();
|
||||
info$uid = c$uid;
|
||||
info$id = c$id;
|
||||
}
|
||||
|
||||
if ( ! info?$client )
|
||||
info$client = fmt("%s/%s", msg$client_name, msg$client_realm);
|
||||
|
||||
info$service = msg$ticket$service_name;
|
||||
info$cipher = cipher_name[msg$ticket$cipher];
|
||||
info$success = T;
|
||||
|
||||
c$krb = info;
|
||||
}
|
||||
|
||||
event krb_as_response(c: connection, msg: KDC_Response) &priority=-5
|
||||
{
|
||||
Log::write(KRB::LOG, c$krb);
|
||||
c$krb$logged = T;
|
||||
}
|
||||
|
||||
event krb_tgs_response(c: connection, msg: KDC_Response) &priority=5
|
||||
{
|
||||
local info: Info;
|
||||
|
||||
if ( c?$krb && c$krb$logged )
|
||||
return;
|
||||
|
||||
if ( c?$krb )
|
||||
info = c$krb;
|
||||
|
||||
if ( ! info?$ts )
|
||||
{
|
||||
info$ts = network_time();
|
||||
info$uid = c$uid;
|
||||
info$id = c$id;
|
||||
}
|
||||
|
||||
if ( ! info?$client )
|
||||
info$client = fmt("%s/%s", msg$client_name, msg$client_realm);
|
||||
|
||||
info$service = msg$ticket$service_name;
|
||||
info$cipher = cipher_name[msg$ticket$cipher];
|
||||
info$success = T;
|
||||
|
||||
c$krb = info;
|
||||
}
|
||||
|
||||
event krb_tgs_response(c: connection, msg: KDC_Response) &priority=-5
|
||||
{
|
||||
Log::write(KRB::LOG, c$krb);
|
||||
c$krb$logged = T;
|
||||
}
|
||||
|
||||
event connection_state_remove(c: connection) &priority=-5
|
||||
{
|
||||
if ( c?$krb && ! c$krb$logged )
|
||||
Log::write(KRB::LOG, c$krb);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue