Kerberos analyzer

This commit is contained in:
Vlad Grigorescu 2014-07-24 16:01:58 -04:00
parent 3cea6ab1eb
commit ca55d203cb
16 changed files with 1432 additions and 0 deletions

View file

@ -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; module GLOBAL;
@load base/bif/event.bif @load base/bif/event.bif

View file

@ -45,6 +45,7 @@
@load base/protocols/ftp @load base/protocols/ftp
@load base/protocols/http @load base/protocols/http
@load base/protocols/irc @load base/protocols/irc
@load base/protocols/krb
@load base/protocols/modbus @load base/protocols/modbus
@load base/protocols/pop3 @load base/protocols/pop3
@load base/protocols/radius @load base/protocols/radius

View file

@ -0,0 +1,2 @@
@load ./main
@load-sigs ./dpd.sig

View file

@ -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",
};
}

View file

@ -0,0 +1,5 @@
signature dpd_krb {
ip-proto == udp
payload /\x6c...\x30...\xa1\x03\x02\x05/
enable "krb"
}

View file

@ -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);
}

View file

@ -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()

View file

@ -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()));
}
}

View file

@ -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

View file

@ -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

View file

@ -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%);

View file

@ -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);
};

View file

@ -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;
%}

View file

@ -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;
};

View file

@ -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

View file

@ -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;