From cbde25f1b8f8885f4135adb423fe88a730bdb10b Mon Sep 17 00:00:00 2001 From: Seth Hall Date: Tue, 9 Aug 2016 10:27:21 -0400 Subject: [PATCH] GSSAPI analyzer now forwards authentication blobs more correctly (and other fixes). Previously, the GSSAPI analyzer blindly forwarded authentication blobs to the NTLM analyzer (which it instantiated too early). Now it waits to instantiate sub analyzers until a blob of a particular type has been seen. It also makes the distinction between krb and ntlm and forwards to the correct analyzer. This required some fixes to the KRB analyzer because KRB over GSSAPI looks slightly different than raw KRB. The KRB analyzer also now includes support for the PA_ENCTYPE_INFO2 pre-auth data type. --- .../protocol/gssapi/gssapi-analyzer.pac | 43 ++++++++++++++++--- .../protocol/gssapi/gssapi-protocol.pac | 1 + src/analyzer/protocol/krb/krb-defs.pac | 1 + src/analyzer/protocol/krb/krb-padata.pac | 33 ++++++++++++-- src/analyzer/protocol/krb/krb-protocol.pac | 2 - 5 files changed, 69 insertions(+), 11 deletions(-) diff --git a/src/analyzer/protocol/gssapi/gssapi-analyzer.pac b/src/analyzer/protocol/gssapi/gssapi-analyzer.pac index a913441f1d..64e8dd5e50 100644 --- a/src/analyzer/protocol/gssapi/gssapi-analyzer.pac +++ b/src/analyzer/protocol/gssapi/gssapi-analyzer.pac @@ -2,10 +2,12 @@ refine connection GSSAPI_Conn += { %member{ analyzer::Analyzer *ntlm; + analyzer::Analyzer *krb5; %} %init{ - ntlm = analyzer_mgr->InstantiateAnalyzer("NTLM", bro_analyzer->Conn()); + ntlm=0; + krb5=0; %} %cleanup{ @@ -13,13 +15,44 @@ refine connection GSSAPI_Conn += { { ntlm->Done(); delete ntlm; + ntlm=0; + } + + if ( krb5 ) + { + krb5->Done(); + delete krb5; + krb5=0; } %} - function forward_ntlm(data: bytestring, is_orig: bool): bool + function forward_blob(val: GSSAPI_NEG_TOKEN_MECH_TOKEN, is_orig: bool): bool %{ - if ( ntlm ) - ntlm->DeliverStream(${data}.length(), ${data}.begin(), is_orig); + if ( ${val.mech_token}.length() >= 7 && + memcmp("NTLMSSP", ${val.mech_token}.begin(), 7) == 0 ) + { + // ntlmssp + if ( ! ntlm ) + ntlm = analyzer_mgr->InstantiateAnalyzer("NTLM", bro_analyzer()->Conn()); + + if ( ntlm ) + ntlm->DeliverStream(${val.mech_token}.length(), ${val.mech_token}.begin(), is_orig); + } + else if ( ${val.mech_token}.length() == 9 && + (memcmp("\x2a\x86\x48\x86\xf7\x12\x01\x02\x02", ${val.mech_token}.begin(), ${val.mech_token}.length()) == 0 || + memcmp("\x2a\x86\x48\x82\xf7\x12\x01\x02\x02", ${val.mech_token}.begin(), ${val.mech_token}.length()) == 0 ) ) + { + // krb5 && ms-krb5 + if ( ! krb5 ) + krb5 = analyzer_mgr->InstantiateAnalyzer("KRB", bro_analyzer()->Conn()); + + // 0x0100 is a special marker + if ( krb5 && memcmp("\x01\x00", ${val.mech_token}.begin(), 2) == 0 ) + { + krb5->DeliverPacket(${val.mech_token}.length()-2, ${val.mech_token}.begin()+2, is_orig, 0, 0, 0); + } + } + return true; %} @@ -37,7 +70,7 @@ refine connection GSSAPI_Conn += { } refine typeattr GSSAPI_NEG_TOKEN_MECH_TOKEN += &let { - fwd: bool = $context.connection.forward_ntlm(mech_token, is_orig); + fwd: bool = $context.connection.forward_blob(this, is_orig); }; refine typeattr GSSAPI_NEG_TOKEN_RESP_Arg += &let { diff --git a/src/analyzer/protocol/gssapi/gssapi-protocol.pac b/src/analyzer/protocol/gssapi/gssapi-protocol.pac index 024d480f35..abd58d7a4d 100644 --- a/src/analyzer/protocol/gssapi/gssapi-protocol.pac +++ b/src/analyzer/protocol/gssapi/gssapi-protocol.pac @@ -53,3 +53,4 @@ type GSSAPI_NEG_TOKEN_MECH_TOKEN(is_orig: bool) = record { meta : ASN1EncodingMeta; mech_token : bytestring &length=meta.length; }; + diff --git a/src/analyzer/protocol/krb/krb-defs.pac b/src/analyzer/protocol/krb/krb-defs.pac index 681f456e6d..8c918bbb82 100644 --- a/src/analyzer/protocol/krb/krb-defs.pac +++ b/src/analyzer/protocol/krb/krb-defs.pac @@ -19,6 +19,7 @@ enum KRBPADataTypes { PA_PW_SALT = 3, PA_PW_AS_REQ = 16, PA_PW_AS_REP = 17, + PA_ENCTYPE_INFO2 = 19, }; # Defined in RFC 4120 diff --git a/src/analyzer/protocol/krb/krb-padata.pac b/src/analyzer/protocol/krb/krb-padata.pac index 829ae48df8..83ec6fba4c 100644 --- a/src/analyzer/protocol/krb/krb-padata.pac +++ b/src/analyzer/protocol/krb/krb-padata.pac @@ -41,6 +41,14 @@ VectorVal* proc_padata(const KRB_PA_Data_Sequence* data, const BroAnalyzer bro_a vv->Assign(vv->Size(), type_val); break; } + case PA_ENCTYPE_INFO2: + { + RecordVal * type_val = new RecordVal(BifType::Record::KRB::Type_Value); + type_val->Assign(0, new Val(element->data_type(), TYPE_COUNT)); + type_val->Assign(1, bytestring_to_val(element->pa_data_element()->pf_enctype_info2()->salt())); + vv->Assign(vv->Size(), type_val); + break; + } case PA_PW_AS_REQ: { const bytestring& cert = element->pa_data_element()->pa_pk_as_req()->cert(); @@ -91,11 +99,11 @@ VectorVal* proc_padata(const KRB_PA_Data_Sequence* data, const BroAnalyzer bro_a } default: { - if ( ! is_error && element->pa_data_element()->unknown().length() ) + if ( ! is_error && element->pa_data_element()->unknown()->meta()->length() > 0 ) { RecordVal * type_val = new RecordVal(BifType::Record::KRB::Type_Value); type_val->Assign(0, new Val(element->data_type(), TYPE_COUNT)); - type_val->Assign(1, bytestring_to_val(element->pa_data_element()->unknown())); + type_val->Assign(1, bytestring_to_val(element->pa_data_element()->unknown()->content())); vv->Assign(vv->Size(), type_val); } break; @@ -165,13 +173,21 @@ type KRB_PA_Data(is_orig: bool, pkt_type: uint8) = record { # Each pre-auth element type KRB_PA_Data_Element(is_orig: bool, type: int64, length: uint64) = case type of { - PA_TGS_REQ -> pa_tgs_req : KRB_AP_REQ(is_orig); + PA_TGS_REQ -> pa_tgs_req : KRB_PA_AP_REQ_wrapper(is_orig); PA_PW_SALT -> pa_pw_salt : ASN1OctetString; PA_PW_AS_REQ -> pa_pk_as_req : KRB_PA_PK_AS_Req &length=length; PA_PW_AS_REP -> pa_pk_as_rep : KRB_PA_PK_AS_Rep &length=length; - default -> unknown : bytestring &length=length; + PA_ENCTYPE_INFO2 -> pf_enctype_info2 : KRB_PA_ENCTYPE_INFO2 &length=length; + default -> unknown : ASN1Encoding &length=length; }; +type KRB_PA_AP_REQ_wrapper(is_orig: bool) = record { + # Not sure what these two field are, but they need to be + # here for pre-auth ap-req messages. + some_meta1 : ASN1EncodingMeta; + some_meta2 : ASN1EncodingMeta; + req : KRB_AP_REQ(is_orig); +}; # The PKINIT certificate structure for a request type KRB_PA_PK_AS_Req = record { @@ -210,3 +226,12 @@ type KRB_PA_PK_AS_Rep = record { : bytestring &restofdata &transient; }; +type KRB_PA_ENCTYPE_INFO2 = record { + some_meta1 : ASN1EncodingMeta; + some_meta2 : ASN1EncodingMeta; + seq_meta1 : ASN1EncodingMeta; + etype : ASN1Encoding; + seq_meta2 : ASN1EncodingMeta; + string_meta : ASN1EncodingMeta; + salt : bytestring &length=string_meta.length; +}; \ No newline at end of file diff --git a/src/analyzer/protocol/krb/krb-protocol.pac b/src/analyzer/protocol/krb/krb-protocol.pac index afb66de973..a237f6b0fa 100644 --- a/src/analyzer/protocol/krb/krb-protocol.pac +++ b/src/analyzer/protocol/krb/krb-protocol.pac @@ -126,8 +126,6 @@ type KRB_KDC_REP(is_orig: bool, pkt_type: uint8) = record { ### AP_REQ type KRB_AP_REQ(is_orig: bool) = record { - string_meta : ASN1EncodingMeta; - app_meta : ASN1EncodingMeta; seq_meta : ASN1EncodingMeta; pvno : SequenceElement(true); msg_type : SequenceElement(true);