diff --git a/scripts/base/init-bare.bro b/scripts/base/init-bare.bro index 65d15dc2cf..309d0aafd4 100644 --- a/scripts/base/init-bare.bro +++ b/scripts/base/init-bare.bro @@ -2971,6 +2971,158 @@ export { }; } +@load base/bif/plugins/Bro_KRB.types.bif + +module KRB; +export { + ## The data from the ERROR_MSG message. See :rfc:`4120`. + type KRB::Error_Msg: record { + ## Protocol version number (5 for KRB5) + pvno: count; + ## The message type (30 for ERROR_MSG) + msg_type: count; + ## Current time on the client + client_time: time &optional; + ## Current time on the server + server_time: time; + ## The specific error code + error_code: count; + ## Realm of the ticket + client_realm: string &optional; + ## Name on the ticket + client_name: string &optional; + ## Realm of the service + service_realm: string; + ## Name of the service + service_name: string; + ## Additional text to explain the error + error_text: string &optional; + }; + + + ## KDC Options. See :rfc:`4120` + type KRB::KDC_Options: record { + ## The ticket to be issued should have its forwardable flag set. + forwardable : bool; + ## A (TGT) request for forwarding. + forwarded : bool; + ## The ticket to be issued should have its proxiable flag set. + proxiable : bool; + ## A request for a proxy. + proxy : bool; + ## The ticket to be issued should have its may-postdate flag set. + allow_postdate : bool; + ## A request for a postdated ticket. + postdated : bool; + ## The ticket to be issued should have its renewable flag set. + renewable : bool; + ## Reserved for opt_hardware_auth + opt_hardware_auth : bool; + ## Request that the KDC not check the transited field of a TGT against + ## the policy of the local realm before it will issue derivative tickets + ## based on the TGT. + disable_transited_check : bool; + ## If a ticket with the requested lifetime cannot be issued, a renewable + ## ticket is acceptable + renewable_ok : bool; + ## The ticket for the end server is to be encrypted in the session key + ## from the additional TGT provided + enc_tkt_in_skey : bool; + ## The request is for a renewal + renew : bool; + ## The request ist to validate a postdated ticket. + validate : bool; + }; + + ## Used in a few places in the Kerberos analyzer for elements + ## that have a type and a string value. + type KRB::Type_Value: record { + ## The data type + data_type : count; + ## The data value + val : string; + }; + + type KRB::Type_Value_Vector: vector of KRB::Type_Value; + + ## A Kerberos ticket. See :rfc:`4120`. + type KRB::Ticket: record { + ## Protocol version number (5 for KRB5) + pvno : count; + ## Realm + realm : string; + ## Name of the service + service_name: string; + ## Cipher the ticket was encrypted with + cipher : count; + }; + + type KRB::Ticket_Vector: vector of KRB::Ticket; + + ## A Kerberos host address See :rfc:`4120`. + type KRB::Host_Address: record { + ## IPv4 or IPv6 address + ip : addr &optional; + ## NetBIOS address + netbios : string &optional; + ## Some other type that we don't support yet + unknown : KRB::Type_Value &optional; + }; + + type KRB::Host_Address_Vector: vector of KRB::Host_Address; + + ## The data from the AS_REQ and TGS_REQ messages. See :rfc:`4120`. + type KRB::KDC_Request: record { + ## Protocol version number (5 for KRB5) + pvno : count; + ## The message type (10 for AS_REQ, 12 for TGS_REQ) + msg_type : count; + ## Optional pre-authentication data + pa_data : vector of KRB::Type_Value &optional; + ## Options specified in the request + kdc_options : KRB::KDC_Options; + ## Name on the ticket + client_name : string &optional; + + ## Realm of the service + service_realm : string; + ## Name of the service + service_name : string &optional; + ## Time the ticket is good from + from : time &optional; + ## Time the ticket is good till + till : time; + ## The requested renew-till time + rtime : time &optional; + + ## A random nonce generated by the client + nonce : count; + ## The desired encryption algorithms, in order of preference + encryption_types : vector of count; + ## Any additional addresses the ticket should be valid for + host_addrs : vector of KRB::Host_Address &optional; + ## Additional tickets may be included for certain transactions + additional_tickets : vector of KRB::Ticket &optional; + }; + + ## The data from the AS_REQ and TGS_REQ messages. See :rfc:`4120`. + type KRB::KDC_Reply: record { + ## Protocol version number (5 for KRB5) + pvno : count; + ## The message type (11 for AS_REP, 13 for TGS_REP) + msg_type : count; + ## Optional pre-authentication data + pa_data : vector of KRB::Type_Value &optional; + ## Realm on the ticket + client_realm : string &optional; + ## Name on the service + client_name : string; + + ## The ticket that was issued + ticket : KRB::Ticket; + }; +} + module GLOBAL; @load base/bif/event.bif diff --git a/scripts/base/init-default.bro b/scripts/base/init-default.bro index 610d205618..f439eae4d4 100644 --- a/scripts/base/init-default.bro +++ b/scripts/base/init-default.bro @@ -45,6 +45,7 @@ @load base/protocols/ftp @load base/protocols/http @load base/protocols/irc +@load base/protocols/krb @load base/protocols/modbus @load base/protocols/pop3 @load base/protocols/radius diff --git a/scripts/base/protocols/krb/__load__.bro b/scripts/base/protocols/krb/__load__.bro new file mode 100644 index 0000000000..9cfbb7a4c4 --- /dev/null +++ b/scripts/base/protocols/krb/__load__.bro @@ -0,0 +1,2 @@ +@load ./main +@load-sigs ./dpd.sig \ No newline at end of file diff --git a/scripts/base/protocols/krb/consts.bro b/scripts/base/protocols/krb/consts.bro new file mode 100644 index 0000000000..b4b60412c8 --- /dev/null +++ b/scripts/base/protocols/krb/consts.bro @@ -0,0 +1,76 @@ +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", + }; + +} diff --git a/scripts/base/protocols/krb/dpd.sig b/scripts/base/protocols/krb/dpd.sig new file mode 100644 index 0000000000..d928f38b81 --- /dev/null +++ b/scripts/base/protocols/krb/dpd.sig @@ -0,0 +1,5 @@ +signature dpd_krb { + ip-proto == udp + payload /\x6c...\x30...\xa1\x03\x02\x05/ + enable "krb" +} diff --git a/scripts/base/protocols/krb/main.bro b/scripts/base/protocols/krb/main.bro new file mode 100644 index 0000000000..97c324e758 --- /dev/null +++ b/scripts/base/protocols/krb/main.bro @@ -0,0 +1,190 @@ +##! 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; + ## Client + client: string &log &optional; + ## Service + service:string &log; + ## Ticket valid from + from: time &log &optional; + ## Ticket valid till + till: time &log &optional; + ## Result + result: string &log &default="unknown"; + ## Error code + error_code: count &log &optional; + ## Error message + error_msg: string &log &optional; + ## We've already logged this + logged: bool &default=F; + }; + + ## Event that can be handled to access the KRB record as it is sent on + ## to the loggin framework. + global log_krb: event(rec: Info); +} + +redef record connection += { + krb: Info &optional; +}; + +const ports = { 88/udp }; + +event bro_init() &priority=5 + { + Log::create_stream(KRB::LOG, [$columns=Info, $ev=log_krb]); + Analyzer::register_for_ports(Analyzer::ANALYZER_KRB, ports); + } + +event krb_error(c: connection, msg: Error_Msg) + { + 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 ) + if ( 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$result = "failed"; + + 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]; + } + + Log::write(KRB::LOG, info); + info$logged = T; + + c$krb = info; + } + +event krb_as_req(c: connection, msg: KDC_Request) + { + if ( c?$krb && c$krb$logged ) + return; + + local info: Info; + info$ts = network_time(); + info$uid = c$uid; + info$id = c$id; + 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; + + c$krb = info; + } + +event krb_tgs_req(c: connection, msg: KDC_Request) + { + if ( c?$krb && c$krb$logged ) + return; + + local info: Info; + info$ts = network_time(); + info$uid = c$uid; + info$id = c$id; + info$service = msg$service_name; + if ( msg?$from ) + info$from = msg$from; + info$till = msg$till; + + c$krb = info; + } + +event krb_as_rep(c: connection, msg: KDC_Reply) + { + 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$result = "success"; + + Log::write(KRB::LOG, info); + info$logged = T; + + c$krb = info; + } + +event krb_tgs_rep(c: connection, msg: KDC_Reply) + { + 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$result = "success"; + + Log::write(KRB::LOG, info); + info$logged = T; + + c$krb = info; + } + +event connection_state_remove(c: connection) + { + if ( c?$krb && ! c$krb$logged ) + Log::write(KRB::LOG, c$krb); + } \ No newline at end of file diff --git a/src/analyzer/protocol/krb/CMakeLists.txt b/src/analyzer/protocol/krb/CMakeLists.txt new file mode 100644 index 0000000000..468364e5d5 --- /dev/null +++ b/src/analyzer/protocol/krb/CMakeLists.txt @@ -0,0 +1,11 @@ + +include(BroPlugin) + +include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) + +bro_plugin_begin(Bro KRB) +bro_plugin_cc(KRB.cc Plugin.cc) +bro_plugin_bif(types.bif) +bro_plugin_bif(events.bif) +bro_plugin_pac(krb.pac krb-protocol.pac krb-analyzer.pac) +bro_plugin_end() diff --git a/src/analyzer/protocol/krb/KRB.cc b/src/analyzer/protocol/krb/KRB.cc new file mode 100644 index 0000000000..e9f91d04db --- /dev/null +++ b/src/analyzer/protocol/krb/KRB.cc @@ -0,0 +1,37 @@ +#include "KRB.h" + +#include "types.bif.h" +#include "events.bif.h" + +using namespace analyzer::krb; + +KRB_Analyzer::KRB_Analyzer(Connection* conn) +: Analyzer("KRB", conn) + { + interp = new binpac::KRB::KRB_Conn(this); + } + +KRB_Analyzer::~KRB_Analyzer() + { + delete interp; + } + +void KRB_Analyzer::Done() + { + Analyzer::Done(); + } + +void KRB_Analyzer::DeliverPacket(int len, const u_char* data, + bool orig, uint64 seq, const IP_Hdr* ip, int caplen) + { + Analyzer::DeliverPacket(len, data, orig, seq, ip, caplen); + try + { + interp->NewData(orig, data, data + len); + } + catch ( const binpac::Exception& e ) + { + printf(fmt("Binpac exception: %s\n", e.c_msg())); + } + } + diff --git a/src/analyzer/protocol/krb/KRB.h b/src/analyzer/protocol/krb/KRB.h new file mode 100644 index 0000000000..905ee43837 --- /dev/null +++ b/src/analyzer/protocol/krb/KRB.h @@ -0,0 +1,28 @@ +#ifndef ANALYZER_PROTOCOL_KRB_KRB_H +#define ANALYZER_PROTOCOL_KRB_KRB_H + +#include "analyzer/protocol/udp/UDP.h" + +#include "krb_pac.h" + +namespace analyzer { namespace krb { + +class KRB_Analyzer : public analyzer::Analyzer { +public: + KRB_Analyzer(Connection* conn); + virtual ~KRB_Analyzer(); + + virtual void Done(); + virtual void DeliverPacket(int len, const u_char* data, bool orig, + uint64 seq, const IP_Hdr* ip, int caplen); + + static analyzer::Analyzer* InstantiateAnalyzer(Connection* conn) + { return new KRB_Analyzer(conn); } + +protected: + binpac::KRB::KRB_Conn* interp; +}; + +} } // namespace analyzer::* + +#endif diff --git a/src/analyzer/protocol/krb/Plugin.cc b/src/analyzer/protocol/krb/Plugin.cc new file mode 100644 index 0000000000..b4bc01f7a3 --- /dev/null +++ b/src/analyzer/protocol/krb/Plugin.cc @@ -0,0 +1,11 @@ + +#include "plugin/Plugin.h" + +#include "KRB.h" + +BRO_PLUGIN_BEGIN(Bro, KRB) + BRO_PLUGIN_DESCRIPTION("Kerberos analyzer"); + BRO_PLUGIN_ANALYZER("KRB", krb::KRB_Analyzer); + BRO_PLUGIN_BIF_FILE(types); + BRO_PLUGIN_BIF_FILE(events); +BRO_PLUGIN_END diff --git a/src/analyzer/protocol/krb/events.bif b/src/analyzer/protocol/krb/events.bif new file mode 100644 index 0000000000..6655f616b8 --- /dev/null +++ b/src/analyzer/protocol/krb/events.bif @@ -0,0 +1,39 @@ +## A Kerberos 5 ``Authentication Server (AS) Request`` as defined +## in :rfc:`4120`. +## +## c: The connection over which this Kerberos message was sent. +## +## msg: A Kerberos KDC request message data structure. +event krb_as_req%(c: connection, msg: KRB::KDC_Request%); + +## A Kerberos 5 ``Ticket-Granting Service (TGS) Request`` as defined +## in :rfc:`4120`. +## +## c: The connection over which this Kerberos message was sent. +## +## msg: A Kerberos KDC request message data structure. +event krb_tgs_req%(c: connection, msg: KRB::KDC_Request%); + +## A Kerberos 5 ``Authentication Server (AS) Reply`` as defined +## in :rfc:`4120`. +## +## c: The connection over which this Kerberos message was sent. +## +## msg: A Kerberos KDC reply message data structure. +event krb_as_rep%(c: connection, msg: KRB::KDC_Reply%); + +## A Kerberos 5 ``Ticket-Granting Service (TGS) Reply`` as defined +## in :rfc:`4120`. +## +## c: The connection over which this Kerberos message was sent. +## +## msg: A Kerberos KDC reply message data structure. +event krb_tgs_rep%(c: connection, msg: KRB::KDC_Reply%); + +## A Kerberos 5 ``ERROR_MSG`` as defined in :rfc:`4120`. +## +## c: The connection over which this Kerberos message was sent. +## +## msg: A Kerberos error message data structure. +event krb_error%(c: connection, msg: KRB::Error_Msg%); + diff --git a/src/analyzer/protocol/krb/krb-analyzer.pac b/src/analyzer/protocol/krb/krb-analyzer.pac new file mode 100644 index 0000000000..9a6804abba --- /dev/null +++ b/src/analyzer/protocol/krb/krb-analyzer.pac @@ -0,0 +1,456 @@ +connection KRB_Conn(bro_analyzer: BroAnalyzer) { + upflow = KRB_Flow(true); + downflow = KRB_Flow(false); +}; + +flow KRB_Flow(is_orig: bool) { + datagram = KRB_PDU withcontext(connection, this); + +}; + +%header{ +Val* GetTimeFromAsn1(const KRB_Time* atime); +Val* GetStringFromPrincipalName(const KRB_Principal_Name* pname); + +Val* asn1_integer_to_val(const ASN1Encoding* i, TypeTag t); +Val* asn1_integer_to_val(const ASN1Integer* i, TypeTag t); + +RecordVal* proc_krb_kdc_options(const KRB_KDC_Options* opts); +%} + +%code{ +Val* GetTimeFromAsn1(const KRB_Time* atime) + { + time_t lResult = 0; + + char lBuffer[16]; + char* pBuffer = lBuffer; + + size_t lTimeLength = atime->time().length(); + char * pString = (char *) atime->time().data(); + + if ( lTimeLength != 15 ) + return 0; + + memcpy(pBuffer, pString, 15); + *(pBuffer+15) = '\0'; + + tm lTime; + lTime.tm_sec = ((lBuffer[12] - '0') * 10) + (lBuffer[13] - '0'); + lTime.tm_min = ((lBuffer[10] - '0') * 10) + (lBuffer[11] - '0'); + lTime.tm_hour = ((lBuffer[8] - '0') * 10) + (lBuffer[9] - '0'); + lTime.tm_mday = ((lBuffer[6] - '0') * 10) + (lBuffer[7] - '0'); + lTime.tm_mon = (((lBuffer[4] - '0') * 10) + (lBuffer[5] - '0')) - 1; + lTime.tm_year = ((lBuffer[0] - '0') * 1000) + ((lBuffer[1] - '0') * 100) + ((lBuffer[2] - '0') * 10) + (lBuffer[3] - '0') - 1900; + + lTime.tm_wday = 0; + lTime.tm_yday = 0; + lTime.tm_isdst = 0; + + lResult = timegm(&lTime); + + if ( !lResult ) + lResult = 0; + + return new Val(double(lResult), TYPE_TIME); +} + +Val* GetStringFromPrincipalName(const KRB_Principal_Name* pname) +{ + if ( pname->data()->size() == 1 ) + return bytestring_to_val(pname->data()[0][0]->encoding()->content()); + if ( pname->data()->size() == 2 ) + return new StringVal(fmt("%s/%s", (char *) pname->data()[0][0]->encoding()->content().begin(), (char *)pname->data()[0][1]->encoding()->content().begin())); + + return new StringVal("unknown"); +} + +Val* asn1_integer_to_val(const ASN1Integer* i, TypeTag t) +{ + return asn1_integer_to_val(i->encoding(), t); +} + +Val* asn1_integer_to_val(const ASN1Encoding* i, TypeTag t) +{ + return new Val(binary_to_int64(i->content()), t); +} + +RecordVal* proc_krb_kdc_options(const KRB_KDC_Options* opts) +{ + RecordVal* rv = new RecordVal(BifType::Record::KRB::KDC_Options); + + rv->Assign(0, new Val(opts->forwardable(), TYPE_BOOL)); + rv->Assign(1, new Val(opts->forwarded(), TYPE_BOOL)); + rv->Assign(2, new Val(opts->proxiable(), TYPE_BOOL)); + rv->Assign(3, new Val(opts->proxy(), TYPE_BOOL)); + rv->Assign(4, new Val(opts->allow_postdate(), TYPE_BOOL)); + rv->Assign(5, new Val(opts->postdated(), TYPE_BOOL)); + rv->Assign(6, new Val(opts->renewable(), TYPE_BOOL)); + rv->Assign(7, new Val(opts->opt_hardware_auth(), TYPE_BOOL)); + rv->Assign(8, new Val(opts->disable_transited_check(), TYPE_BOOL)); + rv->Assign(9, new Val(opts->renewable_ok(), TYPE_BOOL)); + rv->Assign(10, new Val(opts->enc_tkt_in_skey(), TYPE_BOOL)); + rv->Assign(11, new Val(opts->renew(), TYPE_BOOL)); + rv->Assign(12, new Val(opts->validate(), TYPE_BOOL)); + + return rv; +} + +%} + +refine connection KRB_Conn += { + + function proc_krb_kdc_req(msg: KRB_KDC_REQ): bool + %{ + + if ( ( binary_to_int64(${msg.msg_type.data.content}) == 10 ) && ! krb_as_req ) + return false; + + if ( ( binary_to_int64(${msg.msg_type.data.content}) == 12 ) && ! krb_tgs_req ) + return false; + + + RecordVal* rv = new RecordVal(BifType::Record::KRB::KDC_Request); + + rv->Assign(0, asn1_integer_to_val(${msg.pvno.data}, TYPE_COUNT)); + rv->Assign(1, asn1_integer_to_val(${msg.msg_type.data}, TYPE_COUNT)); + + if ( ${msg.has_padata} ) + { + VectorVal* padata = new VectorVal(internal_type("KRB::Type_Value_Vector")->AsVectorType()); + + for ( uint i = 0; i < ${msg.padata.padata_elems}->size(); ++i) + { + switch( ${msg.padata.padata_elems[i].data_type} ) + { + case 1: + // will be generated as separate event + break; + case 2: + // encrypted timestamp is unreadable + break; + case 3: + { + RecordVal * type_val = new RecordVal(BifType::Record::KRB::Type_Value); + type_val->Assign(0, new Val(${msg.padata.padata_elems[i].data_type}, TYPE_COUNT)); + type_val->Assign(1, bytestring_to_val(${msg.padata.padata_elems[i].pa_data_element.pa_pw_salt.encoding.content})); + padata->Assign(padata->Size(), type_val); + break; + } + default: + { + RecordVal * type_val = new RecordVal(BifType::Record::KRB::Type_Value); + type_val->Assign(0, new Val(${msg.padata.padata_elems[i].data_type}, TYPE_COUNT)); + type_val->Assign(1, bytestring_to_val(${msg.padata.padata_elems[i].pa_data_element.unknown})); + padata->Assign(padata->Size(), type_val); + break; + } + } + } + rv->Assign(2, padata); + } + + for ( uint i = 0; i < ${msg.body.args}->size(); ++i ) + { + switch ( ${msg.body.args[i].seq_meta.index} ) + { + case 0: + rv->Assign(3, proc_krb_kdc_options(${msg.body.args[i].data.options})); + break; + case 1: + rv->Assign(4, GetStringFromPrincipalName(${msg.body.args[i].data.principal})); + break; + case 2: + rv->Assign(5, bytestring_to_val(${msg.body.args[i].data.realm.encoding.content})); + break; + case 3: + rv->Assign(6, GetStringFromPrincipalName(${msg.body.args[i].data.sname})); + break; + case 4: + rv->Assign(7, GetTimeFromAsn1(${msg.body.args[i].data.from})); + break; + case 5: + rv->Assign(8, GetTimeFromAsn1(${msg.body.args[i].data.till})); + break; + case 6: + rv->Assign(9, GetTimeFromAsn1(${msg.body.args[i].data.rtime})); + break; + case 7: + rv->Assign(10, asn1_integer_to_val(${msg.body.args[i].data.nonce}, TYPE_COUNT)); + break; + case 8: + if ( ${msg.body.args[i].data.etype.data}->size() ) + { + VectorVal* ciphers = new VectorVal(internal_type("index_vec")->AsVectorType()); + + for ( uint j = 0; j < ${msg.body.args[i].data.etype.data}->size(); ++j ) + ciphers->Assign(ciphers->Size(), asn1_integer_to_val(${msg.body.args[i].data.etype.data[j]}, TYPE_COUNT)); + + rv->Assign(11, ciphers); + } + break; + case 9: + if ( ${msg.body.args[i].data.addrs.addresses}->size() ) + { + VectorVal* addrs = new VectorVal(internal_type("KRB::Host_Address_Vector")->AsVectorType()); + + for ( uint j = 0; j < ${msg.body.args[i].data.addrs.addresses}->size(); ++j ) + { + RecordVal* addr = new RecordVal(BifType::Record::KRB::Host_Address); + switch ( binary_to_int64(${msg.body.args[i].data.addrs.addresses[j].addr_type.data.content}) ) + { + case 2: + addr->Assign(0, new AddrVal(IPAddr(IPv4, (const uint32_t*) c_str(${msg.body.args[i].data.addrs.addresses[j].address.data.content}), IPAddr::Network))); + break; + case 24: + addr->Assign(0, new AddrVal(IPAddr(IPv6, (const uint32_t*) c_str(${msg.body.args[i].data.addrs.addresses[j].address.data.content}), IPAddr::Network))); + break; + case 20: + addr->Assign(1, bytestring_to_val(${msg.body.args[i].data.addrs.addresses[j].address.data.content})); + break; + default: + RecordVal* unk = new RecordVal(BifType::Record::KRB::Type_Value); + unk->Assign(0, asn1_integer_to_val(${msg.body.args[i].data.addrs.addresses[j].addr_type.data}, TYPE_COUNT)); + unk->Assign(1, bytestring_to_val(${msg.body.args[i].data.addrs.addresses[j].address.data.content})); + addr->Assign(2, unk); + break; + } + addrs->Assign(addrs->Size(), addr); + } + + rv->Assign(12, addrs); + } + break; + case 10: + // TODO + break; + case 11: + if ( ${msg.body.args[i].data.addl_tkts.tickets}->size() ) + { + VectorVal* tickets = new VectorVal(internal_type("KRB::Ticket_Vector")->AsVectorType()); + + for ( uint j = 0; j < ${msg.body.args[i].data.addl_tkts.tickets}->size(); ++j ) + { + RecordVal* ticket = new RecordVal(BifType::Record::KRB::Ticket); + + ticket->Assign(0, asn1_integer_to_val(${msg.body.args[i].data.addl_tkts.tickets[j].tkt_vno.data}, TYPE_COUNT)); + ticket->Assign(1, bytestring_to_val(${msg.body.args[i].data.addl_tkts.tickets[j].realm.data.content})); + ticket->Assign(2, GetStringFromPrincipalName(${msg.body.args[i].data.addl_tkts.tickets[j].sname})); + ticket->Assign(3, asn1_integer_to_val(${msg.body.args[i].data.addl_tkts.tickets[j].enc_part.etype.data}, TYPE_COUNT)); + tickets->Assign(tickets->Size(), ticket); + } + rv->Assign(13, tickets); + } + break; + default: + break; + } + } + + if ( ( binary_to_int64(${msg.msg_type.data.content}) == 10 ) ) + BifEvent::generate_krb_as_req(bro_analyzer(), bro_analyzer()->Conn(), rv); + + if ( ( binary_to_int64(${msg.msg_type.data.content}) == 12 ) ) + BifEvent::generate_krb_tgs_req(bro_analyzer(), bro_analyzer()->Conn(), rv); + + return true; + %} + + function proc_krb_kdc_rep(msg: KRB_KDC_REP): bool + %{ + + if ( ( binary_to_int64(${msg.msg_type.data.content}) == 11 ) && ! krb_as_rep ) + return false; + + if ( ( binary_to_int64(${msg.msg_type.data.content}) == 13 ) && ! krb_tgs_rep ) + return false; + + + RecordVal* rv = new RecordVal(BifType::Record::KRB::KDC_Reply); + + rv->Assign(0, asn1_integer_to_val(${msg.pvno.data}, TYPE_COUNT)); + rv->Assign(1, asn1_integer_to_val(${msg.msg_type.data}, TYPE_COUNT)); + + if ( ${msg.has_padata} ) + { + VectorVal* padata = new VectorVal(internal_type("KRB::Type_Value_Vector")->AsVectorType()); + + for ( uint i = 0; i < ${msg.padata.padata_elems}->size(); ++i) + { + switch( ${msg.padata.padata_elems[i].data_type} ) + { + case 1: + // will be generated as separate event + break; + case 2: + // encrypted timestamp is unreadable + break; + case 3: + { + RecordVal * type_val = new RecordVal(BifType::Record::KRB::Type_Value); + type_val->Assign(0, new Val(${msg.padata.padata_elems[i].data_type}, TYPE_COUNT)); + type_val->Assign(1, bytestring_to_val(${msg.padata.padata_elems[i].pa_data_element.pa_pw_salt.encoding.content})); + padata->Assign(padata->Size(), type_val); + break; + } + default: + { + RecordVal * type_val = new RecordVal(BifType::Record::KRB::Type_Value); + type_val->Assign(0, new Val(${msg.padata.padata_elems[i].data_type}, TYPE_COUNT)); + type_val->Assign(1, bytestring_to_val(${msg.padata.padata_elems[i].pa_data_element.unknown})); + padata->Assign(padata->Size(), type_val); + break; + } + } + } + rv->Assign(2, padata); + } + + rv->Assign(3, bytestring_to_val(${msg.client_realm.encoding.content})); + rv->Assign(4, GetStringFromPrincipalName(${msg.client_name})); + + RecordVal* ticket = new RecordVal(BifType::Record::KRB::Ticket); + + ticket->Assign(0, asn1_integer_to_val(${msg.ticket.tkt_vno.data}, TYPE_COUNT)); + ticket->Assign(1, bytestring_to_val(${msg.ticket.realm.data.content})); + ticket->Assign(2, GetStringFromPrincipalName(${msg.ticket.sname})); + ticket->Assign(3, asn1_integer_to_val(${msg.ticket.enc_part.etype.data}, TYPE_COUNT)); + + rv->Assign(5, ticket); + + if ( ( binary_to_int64(${msg.msg_type.data.content}) == 11 ) ) + BifEvent::generate_krb_as_rep(bro_analyzer(), bro_analyzer()->Conn(), rv); + + if ( ( binary_to_int64(${msg.msg_type.data.content}) == 13 ) ) + BifEvent::generate_krb_tgs_rep(bro_analyzer(), bro_analyzer()->Conn(), rv); + + return true; + %} + + function proc_krb_ap_req(msg: KRB_AP_REQ): bool + %{ + // Not implemented + return true; + %} + + function proc_krb_ap_rep(msg: KRB_AP_REP): bool + %{ + // Not implemented + return true; + %} + + function proc_krb_error_msg(msg: KRB_ERROR_MSG): bool + %{ + if ( krb_error ) + { + RecordVal* rv = new RecordVal(BifType::Record::KRB::Error_Msg); + for ( uint i = 0; i < ${msg.args}->size(); i++ ) + { + switch ( ${msg.args[i].seq_meta.index} ) + { + case 0: + rv->Assign(0, asn1_integer_to_val(${msg.args[i].args.pvno}, TYPE_COUNT)); + break; + case 1: + rv->Assign(1, asn1_integer_to_val(${msg.args[i].args.msg_type}, TYPE_COUNT)); + break; + case 2: + rv->Assign(2, GetTimeFromAsn1(${msg.args[i].args.ctime})); + break; + case 3: +// TODO + break; + case 4: + rv->Assign(3, GetTimeFromAsn1(${msg.args[i].args.stime})); + break; + case 5: +// TODO + break; + case 6: + rv->Assign(4, asn1_integer_to_val(${msg.args[i].args.error_code}, TYPE_COUNT)); + break; + case 7: + rv->Assign(5, bytestring_to_val(${msg.args[i].args.crealm.encoding.content})); + break; + case 8: + rv->Assign(6, GetStringFromPrincipalName(${msg.args[i].args.cname})); + break; + case 9: + rv->Assign(7, bytestring_to_val(${msg.args[i].args.realm.encoding.content})); + break; + case 10: + rv->Assign(8, GetStringFromPrincipalName(${msg.args[i].args.sname})); + break; + case 11: + rv->Assign(9, bytestring_to_val(${msg.args[i].args.e_text.encoding.content})); + break; + default: + break; + } + } + BifEvent::generate_krb_error(bro_analyzer(), bro_analyzer()->Conn(), rv); + } + return true; + %} + + function proc_krb_safe_msg(msg: KRB_SAFE_MSG): bool + %{ + // Not implemented + return true; + %} + + function proc_krb_priv_msg(msg: KRB_PRIV_MSG): bool + %{ + // Not implemented + return true; + %} + + function proc_krb_cred_msg(msg: KRB_CRED_MSG): bool + %{ + // Not implemented + return true; + %} + +} + + +refine typeattr KRB_AS_REQ += &let { + proc: bool = $context.connection.proc_krb_kdc_req(data); + }; + +refine typeattr KRB_TGS_REQ += &let { + proc: bool = $context.connection.proc_krb_kdc_req(data); + }; + +refine typeattr KRB_AS_REP += &let { + proc: bool = $context.connection.proc_krb_kdc_rep(data); + }; + +refine typeattr KRB_TGS_REP += &let { + proc: bool = $context.connection.proc_krb_kdc_rep(data); + }; + +refine typeattr KRB_AP_REQ += &let { + proc: bool = $context.connection.proc_krb_ap_req(this); + }; + +refine typeattr KRB_AP_REP += &let { + proc: bool = $context.connection.proc_krb_ap_rep(this); + }; + +refine typeattr KRB_ERROR_MSG += &let { + proc: bool = $context.connection.proc_krb_error_msg(this); + }; + +refine typeattr KRB_SAFE_MSG += &let { + proc: bool = $context.connection.proc_krb_safe_msg(this); + }; + +refine typeattr KRB_PRIV_MSG += &let { + proc: bool = $context.connection.proc_krb_priv_msg(this); + }; + +refine typeattr KRB_CRED_MSG += &let { + proc: bool = $context.connection.proc_krb_cred_msg(this); + }; + diff --git a/src/analyzer/protocol/krb/krb-asn1.pac b/src/analyzer/protocol/krb/krb-asn1.pac new file mode 100644 index 0000000000..e5e2cd3ece --- /dev/null +++ b/src/analyzer/protocol/krb/krb-asn1.pac @@ -0,0 +1,52 @@ +type ASN1Encoding = record { + meta: ASN1EncodingMeta; + content: bytestring &length = meta.length; +}; + +type ASN1EncodingMeta = record { + tag: uint8; + len: uint8; + more_len: bytestring &length = long_len ? len & 0x7f : 0; +} &let { + long_len: bool = len & 0x80; + length: uint64 = long_len ? binary_to_int64(more_len) : len & 0x7f; + index: uint8 = tag - 160; +}; + +type ASN1Integer = record { + encoding: ASN1Encoding; +}; + +type ASN1OctetString = record { + encoding: ASN1Encoding; +}; + +type SequenceElement(grab_content: bool) = record { + index_meta: ASN1EncodingMeta; + have_content: case grab_content of { + true -> data: ASN1Encoding; + false -> meta: ASN1EncodingMeta; + }; +} &let { + index: uint8 = index_meta.index; + length: uint64 = index_meta.length; +}; + +type Array = record { + array_meta: ASN1EncodingMeta; + data: ASN1Encoding[]; +}; + +function binary_to_int64(bs: bytestring): int64 + %{ + int64 rval = 0; + + for ( int i = 0; i < bs.length(); ++i ) + { + uint64 byte = bs[i]; + rval |= byte << (8 * (bs.length() - (i + 1))); + } + + return rval; + %} + diff --git a/src/analyzer/protocol/krb/krb-protocol.pac b/src/analyzer/protocol/krb/krb-protocol.pac new file mode 100644 index 0000000000..1f85ea0e45 --- /dev/null +++ b/src/analyzer/protocol/krb/krb-protocol.pac @@ -0,0 +1,343 @@ +%include krb-asn1.pac + +enum KRBMessageTypes { + AS_REQ = 10, + AS_REP = 11, + TGS_REQ = 12, + TGS_REP = 13, + AP_REQ = 14, + AP_REP = 15, + KRB_SAFE = 20, + KRB_PRIV = 21, + KRB_CRED = 22, + KRB_ERROR = 30, +}; + +type KRB_PDU = record { + app_meta : ASN1EncodingMeta; + msg_type : case (app_meta.tag - 96) of { + AS_REQ -> as_req : KRB_AS_REQ; + AS_REP -> as_rep : KRB_AS_REP; + TGS_REQ -> tgs_req : KRB_TGS_REQ; + TGS_REP -> tgs_rep : KRB_TGS_REP; + AP_REQ -> ap_req : KRB_AP_REQ; + AP_REP -> ap_rep : KRB_AP_REP; + KRB_SAFE -> krb_safe : KRB_SAFE_MSG; + KRB_PRIV -> krb_priv : KRB_PRIV_MSG; + KRB_CRED -> krb_cred : KRB_CRED_MSG; + KRB_ERROR -> krb_error: KRB_ERROR_MSG; + default -> unknown : bytestring &restofdata; + }; +} &byteorder=bigendian; + +type KRB_AS_REQ = record { + data: KRB_KDC_REQ; +}; + +type KRB_TGS_REQ = record { + data: KRB_KDC_REQ; +}; + +type KRB_AS_REP = record { + data: KRB_KDC_REP; +}; + +type KRB_TGS_REP = record { + data: KRB_KDC_REP; +}; + +### KDC_REQ + +type KRB_KDC_REQ = record { + seq_meta : ASN1EncodingMeta; + pvno : SequenceElement(true); + msg_type : SequenceElement(true); + padata_meta: ASN1EncodingMeta; + tmp1 : case has_padata of { + true -> padata : KRB_PA_Data_Sequence &length=padata_meta.length; + false -> n1 : empty; + }; + tmp2 : case has_padata of { + true -> meta2 : ASN1EncodingMeta; + false -> n2 : empty; + }; + body : KRB_REQ_Body &length=body_length; +} &let { + has_padata : bool = padata_meta.index == 3; + body_length: uint8 = has_padata ? meta2.length : padata_meta.length; +}; + +type KRB_PA_Data_Sequence = record { + seq_meta : ASN1EncodingMeta; + padata_elems: KRB_PA_Data[]; +}; + +type KRB_PA_Data = record { + seq_meta : ASN1EncodingMeta; + pa_data_type : SequenceElement(true); + pa_data_elem_meta : ASN1EncodingMeta; + pa_data_element : KRB_PA_Data_Element(data_type); +} &let { + data_type: int64 = binary_to_int64(pa_data_type.data.content); +}; + +type KRB_PA_Data_Element(type: int64) = case type of { + 1 -> pa_tgs_req : KRB_AP_REQ; + 2 -> pa_enc_timestamp : KRB_Encrypted_Data; + 3 -> pa_pw_salt : ASN1OctetString; + default -> unknown : bytestring &restofdata; +}; + +type KRB_REQ_Body = record { + seq_meta : ASN1EncodingMeta; + args : KRB_REQ_Arg[]; +}; + +type KRB_REQ_Arg = record { + seq_meta : ASN1EncodingMeta; + data : KRB_REQ_Arg_Data(seq_meta.index) &length=seq_meta.length; +}; + +type KRB_REQ_Arg_Data(index: uint8) = case index of { + 0 -> options : KRB_KDC_Options; + 1 -> principal : KRB_Principal_Name; + 2 -> realm : ASN1OctetString; + 3 -> sname : KRB_Principal_Name; + 4 -> from : KRB_Time; + 5 -> till : KRB_Time; + 6 -> rtime : KRB_Time; + 7 -> nonce : ASN1Integer; + 8 -> etype : Array; + 9 -> addrs : KRB_Host_Addresses; + 10 -> auth_data : ASN1OctetString; # TODO + 11 -> addl_tkts : KRB_Ticket_Sequence; + default -> unknown : bytestring &restofdata; +}; + +type KRB_KDC_Options = record { + meta : ASN1EncodingMeta; + flags: uint32; +} &let { + reserved : bool = flags & 0x80000000; + forwardable : bool = flags & 0x40000000; + forwarded : bool = flags & 0x20000000; + proxiable : bool = flags & 0x10000000; + proxy : bool = flags & 0x8000000; + allow_postdate : bool = flags & 0x4000000; + postdated : bool = flags & 0x2000000; + unused7 : bool = flags & 0x1000000; + renewable : bool = flags & 0x800000; + unused9 : bool = flags & 0x400000; + unused10 : bool = flags & 0x200000; + opt_hardware_auth : bool = flags & 0x100000; + unused12 : bool = flags & 0x80000; + unused13 : bool = flags & 0x40000; + # ... + unused15 : bool = flags & 0x10000; + # ... + disable_transited_check : bool = flags & 0x10; + renewable_ok : bool = flags & 0x8; + enc_tkt_in_skey : bool = flags & 0x4; + renew : bool = flags & 0x2; + validate : bool = flags & 0x1; +}; + +type KRB_Principal_Name = record { + seq_meta : ASN1EncodingMeta; + name_meta : ASN1EncodingMeta; + name_type : ASN1Integer; + seq_meta_1: ASN1EncodingMeta; + seq_meta_2: ASN1EncodingMeta; + data : ASN1OctetString[] &length=seq_meta_2.length; +}; + +type KRB_Time = record { + meta: ASN1EncodingMeta; + time: bytestring &restofdata; +}; + +type KRB_Host_Addresses = record { + seq_meta : ASN1EncodingMeta; + addresses: KRB_Host_Address[]; +}; + +type KRB_Host_Address = record { + addr_type: SequenceElement(true); + address : SequenceElement(true); +}; + +type KRB_Ticket(in_sequence: bool) = record { + have_seq : case in_sequence of { + true -> meta: ASN1EncodingMeta; + false -> none: empty; + }; + app_meta : ASN1EncodingMeta; + seq_meta : ASN1EncodingMeta; + tkt_vno : SequenceElement(true); + realm : SequenceElement(true); + sname_meta: ASN1EncodingMeta; + sname : KRB_Principal_Name; + enc_part : KRB_Encrypted_Data; +}; + +type KRB_Ticket_Sequence = record { + seq_meta : ASN1EncodingMeta; + tickets : KRB_Ticket(true)[] &length=seq_meta.length; +}; + +type KRB_Encrypted_Data_in_Seq = record { + index_meta : ASN1EncodingMeta; + data : KRB_Encrypted_Data; +}; + +type KRB_Encrypted_Data = record { + seq_meta : ASN1EncodingMeta; + etype : SequenceElement(true); + kvno_meta : ASN1EncodingMeta; + case_kvno : case have_kvno of { + true -> kvno: ASN1Integer; + false -> none: empty; + }; + grab_next_meta : case have_kvno of { + true -> next_meta: ASN1EncodingMeta; + false -> none_meta: empty; + }; + ciphertext : bytestring &length=have_kvno ? next_meta.length : kvno_meta.length; +} &let { + have_kvno : bool = kvno_meta.index == 1; +}; + +### KDC_REP + +type KRB_KDC_REP = record { + seq_meta : ASN1EncodingMeta; + pvno : SequenceElement(true); + msg_type : SequenceElement(true); + padata_meta : ASN1EncodingMeta; + tmp1 : case has_padata of { + true -> padata : KRB_PA_Data_Sequence &length=padata_meta.length; + false -> n1 : empty; + }; + tmp2 : case has_padata of { + true -> meta2 : ASN1EncodingMeta; + false -> n2 : empty; + }; + client_realm: ASN1OctetString &length=realm_length; + client_name : KRB_Principal_Name; + ticket : KRB_Ticket(true); + enc_part : KRB_Encrypted_Data_in_Seq; +} &let { + has_padata : bool = padata_meta.index == 2; + realm_length: uint8 = has_padata ? meta2.length : padata_meta.length; +}; + +### AP_REQ + +type KRB_AP_REQ = record { + string_meta : ASN1EncodingMeta; + app_meta : ASN1EncodingMeta; + seq_meta : ASN1EncodingMeta; + pvno : SequenceElement(true); + msg_type : SequenceElement(true); + ap_options : KRB_AP_Options; + ticket : KRB_Ticket(true); + enc_part : KRB_Encrypted_Data_in_Seq; +}; + +type KRB_AP_Options = record { + meta : SequenceElement(false); + flags : uint32; + : padding[1]; +} &let { + reserved : bool = flags & 0x80000000; + use_session_key : bool = flags & 0x40000000; + mutual_required : bool = flags & 0x20000000; +}; + + +### AP_REP + +type KRB_AP_REP = record { + pvno : SequenceElement(true); + msg_type: SequenceElement(true); + enc_part: KRB_Encrypted_Data_in_Seq; +}; + +### KRB_ERROR + +type KRB_ERROR_MSG = record { + seq_meta: ASN1EncodingMeta; + args : KRB_ERROR_Arg[]; +}; + +type KRB_ERROR_Arg = record { + seq_meta: ASN1EncodingMeta; + args : KRB_ERROR_Arg_Data(seq_meta.index) &length=seq_meta.length; +}; + +type KRB_ERROR_Arg_Data(index: uint8) = case index of { + 0 -> pvno : ASN1Integer; + 1 -> msg_type : ASN1Integer; + 2 -> ctime : KRB_Time; + 3 -> cusec : ASN1Integer; + 4 -> stime : KRB_Time; + 5 -> susec : ASN1Integer; + 6 -> error_code : ASN1Integer; + 7 -> crealm : ASN1OctetString; + 8 -> cname : KRB_Principal_Name; + 9 -> realm : ASN1OctetString; + 10 -> sname : KRB_Principal_Name; + 11 -> e_text : ASN1OctetString; + 12 -> e_data : ASN1OctetString; +}; + +### KRB_SAFE + +type KRB_SAFE_MSG = record { + pvno : SequenceElement(true); + msg_type : SequenceElement(true); + safe_body: KRB_SAFE_Body; + checksum : KRB_Checksum; +}; + +type KRB_SAFE_Body = record { + seq_meta: ASN1EncodingMeta; + args : KRB_SAFE_Arg[]; +}; + +type KRB_SAFE_Arg = record { + seq_meta: ASN1EncodingMeta; + args : KRB_SAFE_Arg_Data(seq_meta.index) &length=seq_meta.length; +}; + +type KRB_SAFE_Arg_Data(index: uint8) = case index of { + 0 -> user_data : ASN1OctetString; + 1 -> timestamp : KRB_Time; + 2 -> usec : ASN1Integer; + 3 -> seq_number : ASN1Integer; + 4 -> sender_addr: KRB_Host_Address; + 5 -> recp_addr : KRB_Host_Address; +}; + +type KRB_Checksum = record { + checksum_type: SequenceElement(true); + checksum : SequenceElement(true); +}; + +### KRB_PRIV + +type KRB_PRIV_MSG = record { + pvno : SequenceElement(true); + msg_type: SequenceElement(true); + enc_part: KRB_Encrypted_Data_in_Seq; +}; + +### KRB_CRED + +type KRB_CRED_MSG = record { + pvno : SequenceElement(true); + msg_type : SequenceElement(true); + tkts_meta: SequenceElement(false); + tickets : KRB_Ticket_Sequence; + enc_part : KRB_Encrypted_Data_in_Seq; +}; \ No newline at end of file diff --git a/src/analyzer/protocol/krb/krb.pac b/src/analyzer/protocol/krb/krb.pac new file mode 100644 index 0000000000..c2e83be19b --- /dev/null +++ b/src/analyzer/protocol/krb/krb.pac @@ -0,0 +1,15 @@ +%include binpac.pac +%include bro.pac + +%extern{ +#include "types.bif.h" +#include "events.bif.h" +%} + +analyzer KRB withcontext { + connection: KRB_Conn; + flow: KRB_Flow; +}; + +%include krb-protocol.pac +%include krb-analyzer.pac diff --git a/src/analyzer/protocol/krb/types.bif b/src/analyzer/protocol/krb/types.bif new file mode 100644 index 0000000000..5464d03510 --- /dev/null +++ b/src/analyzer/protocol/krb/types.bif @@ -0,0 +1,14 @@ +module KRB; + +type Error_Msg: record; + +type KDC_Options: record; +type Type_Value: record; +type Ticket: record; +type Host_Address: record; + +type KDC_Request: record; + +type KDC_Reply: record; + +module GLOBAL;