Merge remote-tracking branch 'origin/topic/awelzel/spicy-ldap-krb-wrap-tokens'

* origin/topic/awelzel/spicy-ldap-krb-wrap-tokens:
  ldap: Remove MessageWrapper with magic 0x30 searching
  ldap: Harden parsing a bit
  ldap: Handle integrity-only KRB wrap tokens
This commit is contained in:
Arne Welzel 2024-07-17 16:45:13 +02:00
commit 2ea3a651bd
18 changed files with 394 additions and 107 deletions

30
CHANGES
View file

@ -1,3 +1,33 @@
7.1.0-dev.16 | 2024-07-17 16:45:13 +0200
* ldap: Remove MessageWrapper with magic 0x30 searching (Arne Welzel, Corelight)
This unit implements a heuristic to search for the 0x30 sequence
byte if Message couldn't readily be parsed. Remove it with the
idea of explicit and predictable support for SASL mechanisms.
* ldap: Harden parsing a bit (Arne Welzel, Corelight)
ASN1Message(True) may go off parsing arbitrary input data as
"something ASN.1" This could be GBs of octet strings or just very
long sequences. Avoid this by open-coding some top-level types expected.
This also tries to avoid some of the &parse-from usages that result
in unnecessary copies of data.
Adds a locally generated PCAP with addRequest/addResponse that we
don't currently handle.
* ldap: Handle integrity-only KRB wrap tokens (Arne Welzel, Corelight)
Mostly staring at the PCAPs and opened a few RFCs. For now, only if the
MS_KRB5 OID is used and accepted in a bind response, start stripping
KRB5 wrap tokens for both, client and server traffic.
Would probably be nice to forward the GSS-API data to the analyzer...
Closes zeek/spicy-ldap#29.
7.1.0-dev.12 | 2024-07-16 10:16:02 -0700 7.1.0-dev.12 | 2024-07-16 10:16:02 -0700
* Bump auxil/spicy to latest development snapshot (Benjamin Bannier, Corelight) * Bump auxil/spicy to latest development snapshot (Benjamin Bannier, Corelight)

6
NEWS
View file

@ -12,9 +12,15 @@ Breaking Changes
New Functionality New Functionality
----------------- -----------------
* The LDAP analyzer now supports handling of non-sealed GSS-API WRAP tokens.
Changed Functionality Changed Functionality
--------------------- ---------------------
* Heuristics for parsing SASL encrypted and signed LDAP traffic have been
made more strict and predictable. Please provide input if this results in
less visibility in your environment.
Removed Functionality Removed Functionality
--------------------- ---------------------

View file

@ -1 +1 @@
7.1.0-dev.12 7.1.0-dev.16

View file

@ -126,125 +126,126 @@ public type Result = unit {
# https://tools.ietf.org/html/rfc4511#section-4.1.10 # https://tools.ietf.org/html/rfc4511#section-4.1.10
}; };
# 1.2.840.48018.1.2.2 (MS KRB5 - Microsoft Kerberos 5)
const GSSAPI_MECH_MS_KRB5 = "1.2.840.48018.1.2.2";
# Supported SASL stripping modes.
type SaslStripping = enum {
MS_KRB5 = 1, # Payload starts with a 4 byte length followed by a wrap token that may or may not be sealed.
};
type Ctx = struct {
saslStripping: SaslStripping; # Which mode of SASL stripping to use.
};
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
public type Messages = unit { public type Messages = unit {
: MessageWrapper[]; %context = Ctx;
: SASLStrip(self.context())[];
}; };
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
type SASLLayer = unit { public type SASLStrip = unit(ctx: Ctx&) {
# For the time being (before we support parsing the SASL layer) this unit switch( ctx.saslStripping ) {
# is used by MessageWrapper below to strip it (SASL) so that the parser SaslStripping::Undef -> : Message(ctx);
# can attempt to resume parsing afterward. It also sets the success flag SaslStripping::MS_KRB5 -> : SaslMsKrb5Stripper(ctx);
# if '\x30' is found, otherwise backtracks so that we can deal with encrypted };
# SASL payloads without raising a parse error. };
var success: bool = False;
: bytes &until=b"\x30" {
self.success = True; type KrbWrapToken = unit {
# https://datatracker.ietf.org/doc/html/rfc4121#section-4.2.6.2
# Number of bytes to expect *after* the payload.
var trailer_ec: uint64;
var header_ec: uint64;
ctx_flags: bitfield(8) {
send_by_acceptor: 0;
sealed: 1;
acceptor_subkey: 2;
};
filler: skip b"\xff";
ec: uint16; # extra count
rrc: uint16 { # right rotation count
# Handle rrc == ec or rrc == 0.
if ( self.rrc == self.ec ) {
self.header_ec = self.ec;
} else if ( self.rrc == 0 ) {
self.trailer_ec = self.ec;
} else {
throw "Unhandled rc %s and ec %s" % (self.ec, self.rrc);
}
} }
on %error { snd_seq: uint64;
self.backtrack(); header_e: skip bytes &size=self.header_ec;
}
}; };
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
public type MessageWrapper = unit { type SaslMsKrb5Stripper = unit(ctx: Ctx&) {
# A wrapper around 'Message'. First, we try to parse a Message unit. # This is based on Wireshark output and example traffic we have. There's always
# There are two possible outcomes: # a 4 byte length field followed by the krb5_tok_id field in messages after
# (1) Success -> We consumed all bytes and successfully parsed a Message unit # MS_KRB5 was selected. I haven't read enough specs to understand if it's
# (2) No success -> self.backtrack() is called in the Message unit, # just this one case that works, or others could use the same stripping.
# so effectively we didn't consume any bytes yet. var switch_size: uint64;
# The outcome can be determined by checking the `success` variable of the Message unit
# This success variable is different, because this keeps track of the status for the MessageWrapper object len: uint32;
var success: bool = False; krb5_tok_id: uint16;
var message: Message;
# Here, we try to parse the message... switch ( self.krb5_tok_id ) {
: Message &try { 0x0504 -> krb_wrap_token: KrbWrapToken;
* -> : void;
};
# ... and only if the Message unit successfully parsed, we can set : skip bytes &size=0 {
# the status of this MessageWrapper's success to 'True' self.switch_size = self.len - (self.offset() - 4);
if ( $$.success == True ) { if ( self?.krb_wrap_token )
self.success = True; self.switch_size -= self.krb_wrap_token.trailer_ec;
self.message = $$;
}
} }
# If we failed to parse the message, then we're going to scan the remaining bytes for the '\x30' switch ( self?.krb_wrap_token && ! self.krb_wrap_token.ctx_flags.sealed ) {
# start byte and try to parse a Message starting from that byte. This effectively True -> : Message(ctx)[] &eod;
# strips the SASL layer if SASL Signing was enabled. Until now, I haven't found A * -> : skip bytes &eod;
# better way to scan / determine the exact SASL header length yet, so we'll stick with this } &size=self.switch_size;
# for the time being. If the entire LDAP packet was encrypted with SASL, then we skip parsing for
# now (in the long run we need to be parsing SASL/GSSAPI instead, in which case encrypted payloads
# are just another message type).
# SASLLayer (see unit above) just consumes bytes &until=b"\x30" or backtracks if it isn't found # Consume the wrap token trailer, if any.
# and sets a success flag we can use later to decide if those bytes contain a parsable message. trailer_e: skip bytes &size=self.krb_wrap_token.trailer_ec if (self?.krb_wrap_token);
var sasl_success: bool = False; };
: SASLLayer &try if ( self.success == False ) {
if ( $$.success == True ) {
self.sasl_success = True;
}
}
var remainder: bytes;
# SASLLayer consumes the delimiter ('\x30'), and because this is the first byte of a valid LDAP message
# we should re-add it to the remainder if the delimiter was found. If the delimiter was not found, we
# leave the remainder empty, but note that the bytes must be consumed either way to avoid stalling the
# parser and causing an infinite loop error.
: bytes &eod if ( self.success == False ) {
if ( self.sasl_success == True ) {
self.remainder = b"\x30" + $$;
}
}
# Again, try to parse a Message unit. Be aware that in this will sometimes fail if the '\x30' byte is
# also present in the SASL header.
# Also, we could try to do this recursively or try a few iterations, but for now I would suggest
# to try this extra parsing once to get the best cost/benefit tradeoff.
: Message &try &parse-from=self.remainder if ( self.success == False && self.sasl_success == True ) {
if ( $$.success == True ) {
self.success = True;
self.message = $$;
}
}
# If we still didn't manage to parse a message (so the &try resulted in another backtrack()) then
# this is probably an encrypted LDAP message, so skip it
} &convert=self.message;
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
public type Message = unit { public type Message = unit(ctx: Ctx&) {
var messageID: int64; var messageID: int64;
var opcode: ProtocolOpcode = ProtocolOpcode::Undef; var opcode: ProtocolOpcode = ProtocolOpcode::Undef;
var applicationBytes: bytes;
var unsetResultDefault: Result; var unsetResultDefault: Result;
var result_: Result& = self.unsetResultDefault; var result_: Result& = self.unsetResultDefault;
var obj: string = ""; var obj: string = "";
var arg: string = ""; var arg: string = "";
var success: bool = False; var seqHeaderLen: uint64;
var msgLen: uint64;
: ASN1::ASN1Message(True) { seqHeader: ASN1::ASN1Header &requires=($$.tag.class == ASN1::ASN1Class::Universal && $$.tag.type_ == ASN1::ASN1Type::Sequence) {
if (($$.head.tag.type_ == ASN1::ASN1Type::Sequence) && self.msgLen = $$.len.len;
($$.body?.seq) && }
(|$$.body.seq.submessages| >= 2)) {
if ($$.body.seq.submessages[0].body?.num_value) { # Use offset() to determine how many bytes the seqHeader took. This
self.messageID = $$.body.seq.submessages[0].body.num_value; # needs to be done after the seqHeader field hook.
} : void {
if ($$.body.seq.submessages[1]?.application_id) { self.seqHeaderLen = self.offset();
self.opcode = cast<ProtocolOpcode>(cast<uint8>($$.body.seq.submessages[1].application_id)); }
self.applicationBytes = $$.body.seq.submessages[1].application_data;
} messageID_header: ASN1::ASN1Header &requires=($$.tag.class == ASN1::ASN1Class::Universal && $$.tag.type_ == ASN1::ASN1Type::Integer);
} : ASN1::ASN1Body(self.messageID_header, False) {
self.messageID = $$.num_value;
}
protocolOp: ASN1::ASN1Header &requires=($$.tag.class == ASN1::ASN1Class::Application) {
self.opcode = cast<ProtocolOpcode>(cast<uint8>($$.tag.type_));
} }
switch ( self.opcode ) { switch ( self.opcode ) {
ProtocolOpcode::BIND_REQUEST -> BIND_REQUEST: BindRequest(self); ProtocolOpcode::BIND_REQUEST -> BIND_REQUEST: BindRequest(self);
ProtocolOpcode::BIND_RESPONSE -> BIND_RESPONSE: BindResponse(self); ProtocolOpcode::BIND_RESPONSE -> BIND_RESPONSE: BindResponse(self, ctx);
ProtocolOpcode::UNBIND_REQUEST -> UNBIND_REQUEST: UnbindRequest(self); ProtocolOpcode::UNBIND_REQUEST -> UNBIND_REQUEST: UnbindRequest(self);
ProtocolOpcode::SEARCH_REQUEST -> SEARCH_REQUEST: SearchRequest(self); ProtocolOpcode::SEARCH_REQUEST -> SEARCH_REQUEST: SearchRequest(self);
ProtocolOpcode::SEARCH_RESULT_ENTRY -> SEARCH_RESULT_ENTRY: SearchResultEntry(self); ProtocolOpcode::SEARCH_RESULT_ENTRY -> SEARCH_RESULT_ENTRY: SearchResultEntry(self);
@ -267,17 +268,15 @@ public type Message = unit {
ProtocolOpcode::INTERMEDIATE_RESPONSE -> INTERMEDIATE_RESPONSE: NotImplemented(self); ProtocolOpcode::INTERMEDIATE_RESPONSE -> INTERMEDIATE_RESPONSE: NotImplemented(self);
ProtocolOpcode::MOD_DN_REQUEST -> MOD_DN_REQUEST: NotImplemented(self); ProtocolOpcode::MOD_DN_REQUEST -> MOD_DN_REQUEST: NotImplemented(self);
ProtocolOpcode::SEARCH_RESULT_REFERENCE -> SEARCH_RESULT_REFERENCE: NotImplemented(self); ProtocolOpcode::SEARCH_RESULT_REFERENCE -> SEARCH_RESULT_REFERENCE: NotImplemented(self);
} &parse-from=self.applicationBytes if ( self.opcode ); } &size=self.protocolOp.len.len;
on %error { # Ensure some invariants hold after parsing the command.
self.backtrack(); : void &requires=(self.offset() >= self.seqHeaderLen);
} : void &requires=(self.msgLen >= (self.offset() - self.seqHeaderLen));
on %done { # Eat the controls field if it exists.
self.success = True; : skip bytes &size=self.msgLen - (self.offset() - self.seqHeaderLen);
} };
} &requires=((self?.messageID) && (self?.opcode) && (self.opcode != ProtocolOpcode::Undef));
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# Bind Operation # Bind Operation
@ -288,15 +287,99 @@ public type BindAuthType = enum {
BIND_AUTH_SASL = 3, BIND_AUTH_SASL = 3,
}; };
type GSS_SPNEGO_negTokenInit = unit {
oidHeader: ASN1::ASN1Header &requires=($$.tag.class == ASN1::ASN1Class::Universal && $$.tag.type_ == ASN1::ASN1Type::ObjectIdentifier);
oid: ASN1::ASN1ObjectIdentifier(self.oidHeader.len.len) &requires=(self.oid.oidstring == "1.3.6.1.5.5.2");
# TODO: Parse the rest of negTokenInit.
: skip bytes &eod;
};
# Peak into GSS-SPNEGO payload and ensure it is indeed GSS-SPNEGO.
type GSS_SPNEGO = unit {
# This is the optional octet string in SaslCredentials.
credentialsHeader: ASN1::ASN1Header &requires=($$.tag.type_ == ASN1::ASN1Type::OctetString);
# Now we either have the initial message as specified in RFC2743 or
# a continuation from RFC4178
#
# 60 -> APPLICATION [0] https://datatracker.ietf.org/doc/html/rfc2743#page-81)
# a1 -> CHOICE [1] https://www.rfc-editor.org/rfc/rfc4178#section-4.2
#
gssapiHeader: ASN1::ASN1Header &requires=(
$$.tag.class == ASN1::ASN1Class::Application && $$.tag.type_ == ASN1::ASN1Type(0)
|| $$.tag.class == ASN1::ASN1Class::ContextSpecific && $$.tag.type_ == ASN1::ASN1Type(1)
);
switch ( self.gssapiHeader.tag.type_ ) {
ASN1::ASN1Type(0) -> initial: GSS_SPNEGO_negTokenInit;
* -> : skip bytes &eod;
} &size=self.gssapiHeader.len.len;
};
type SaslCredentials = unit() { type SaslCredentials = unit() {
mechanism: ASN1::ASN1Message(True) &convert=$$.body.str_value; mechanism: ASN1::ASN1Message(False) &convert=$$.body.str_value;
# TODO: if we want to parse the (optional) credentials string
# Peak into GSS-SPNEGO payload if we have any.
switch ( self.mechanism ) {
"GSS-SPNEGO" -> gss_spnego: GSS_SPNEGO;
* -> : skip bytes &eod;
};
};
type NegTokenResp = unit {
var accepted: bool;
var supportedMech: ASN1::ASN1Message;
# Parse the contained Sequence.
seq: ASN1::ASN1Message(True) {
for ( msg in $$.body.seq.submessages ) {
# https://www.rfc-editor.org/rfc/rfc4178#section-4.2.2
if ( msg.application_id == 0 ) {
self.accepted = msg.application_data == b"\x0a\x01\x00";
} else if ( msg.application_id == 1 ) {
self.supportedMech = msg;
} else if ( msg.application_id == 2 ) {
# ignore responseToken
} else if ( msg.application_id == 3 ) {
# ignore mechListMec
} else {
throw "unhandled NegTokenResp id %s" % msg.application_id;
}
}
}
switch ( self?.supportedMech ) {
True -> supportedMechOid: ASN1::ASN1Message(False) &convert=$$.body.str_value;
* -> : void;
} &parse-from=self.supportedMech.application_data;
};
type ServerSaslCreds = unit {
serverSaslCreds: ASN1::ASN1Header &requires=($$.tag.class == ASN1::ASN1Class::ContextSpecific && $$.tag.type_ == ASN1::ASN1Type(7));
# The PCAP missing_ldap_logs.pcapng has a1 81 b6 here for the GSS-SPNEGO response.
#
# This is context-specific ID 1, constructed, and a length of 182 as
# specified by in 4.2 of RFC4178.
#
# https://www.rfc-editor.org/rfc/rfc4178#section-4.2
#
# TODO: This is only valid for a GSS-SPNEGO negTokenResp.
# If you want to support something else, remove the requires
# and add more to the switch below.
choice: ASN1::ASN1Header &requires=($$.tag.class == ASN1::ASN1Class::ContextSpecific);
switch ( self.choice.tag.type_ ) {
ASN1::ASN1Type(1) -> negTokenResp: NegTokenResp;
# ...
} &size=self.choice.len.len;
}; };
# TODO(fox-ds): A helper unit for requests for which no handling has been implemented. # TODO(fox-ds): A helper unit for requests for which no handling has been implemented.
# Eventually all uses of this unit should be replaced with actual parsers so this unit can be removed. # Eventually all uses of this unit should be replaced with actual parsers so this unit can be removed.
type NotImplemented = unit(inout message: Message) { type NotImplemented = unit(inout message: Message) {
# Do nothing : skip bytes &eod;
}; };
type BindRequest = unit(inout message: Message) { type BindRequest = unit(inout message: Message) {
@ -324,14 +407,32 @@ type BindRequest = unit(inout message: Message) {
(|self.authData| > 0)) { (|self.authData| > 0)) {
message.arg = self.saslCreds.mechanism; message.arg = self.saslCreds.mechanism;
} }
} &requires=((self?.authType) && (self.authType != BindAuthType::Undef)); } &requires=(self?.authType && (self.authType != BindAuthType::Undef));
type BindResponse = unit(inout message: Message) { type BindResponse = unit(inout message: Message, ctx: Ctx&) {
: Result { : Result {
message.result_ = $$; message.result_ = $$;
} }
# TODO: if we want to parse SASL credentials returned # Try to parse serverSaslCreds if there's any input remaining. This
# unit is parsed with &size, so &eod here works.
#
# Technically we should be able to tell from the ASN.1 structure
# if the serverSaslCreds field exists or not. But, not sure we can
# check if there's any bytes left at this point outside of passing
# in the length and playing with offset().
serverSaslCreds: ServerSaslCreds[] &eod {
if ( |self.serverSaslCreds| > 0 ) {
if ( self.serverSaslCreds[0]?.negTokenResp ) {
local token = self.serverSaslCreds[0].negTokenResp;
if ( token.accepted && token?.supportedMechOid ) {
if ( token.supportedMechOid == GSSAPI_MECH_MS_KRB5 ) {
ctx.saslStripping = SaslStripping::MS_KRB5;
}
}
}
}
}
}; };
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@ -899,6 +1000,6 @@ type AbandonRequest = unit(inout message: Message) {
# #
# }; # };
on LDAP::MessageWrapper::%done { on LDAP::Message::%done {
spicy::accept_input(); spicy::accept_input();
} }

View file

@ -0,0 +1,11 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path conn
#open XXXX-XX-XX-XX-XX-XX
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p proto service duration orig_bytes resp_bytes conn_state missed_bytes history orig_pkts orig_ip_bytes resp_pkts resp_ip_bytes tunnel_parents
#types time string addr port addr port enum string interval count count string count string count count count count set[string]
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 46160 127.0.1.1 389 tcp ldap_tcp 3.537413 536 42 SF 0 ShADadFf 11 1116 6 362 -
#close XXXX-XX-XX-XX-XX-XX

View file

@ -0,0 +1,14 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path ldap
#open XXXX-XX-XX-XX-XX-XX
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p message_id version opcode result diagnostic_message object argument
#types time string addr port addr port int int string string string string string
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 46160 127.0.1.1 389 1 3 bind simple success - cn=admin,dc=example,dc=com REDACTED
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 46160 127.0.1.1 389 2 - add success - - -
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 46160 127.0.1.1 389 3 - add success - - -
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 46160 127.0.1.1 389 4 - unbind - - - -
#close XXXX-XX-XX-XX-XX-XX

View file

@ -0,0 +1,11 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path conn
#open XXXX-XX-XX-XX-XX-XX
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p proto service duration orig_bytes resp_bytes conn_state missed_bytes history orig_pkts orig_ip_bytes resp_pkts resp_ip_bytes tunnel_parents
#types time string addr port addr port enum string interval count count string count string count count count count set[string]
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 192.168.10.138 63815 192.168.10.186 389 tcp ldap_tcp 0.033404 3046 90400 RSTR 0 ShADdar 14 1733 68 93132 -
#close XXXX-XX-XX-XX-XX-XX

View file

@ -0,0 +1,12 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path ldap
#open XXXX-XX-XX-XX-XX-XX
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p message_id version opcode result diagnostic_message object argument
#types time string addr port addr port int int string string string string string
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 192.168.10.138 63815 192.168.10.186 389 3 3 bind SASL success - - GSS-SPNEGO
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 192.168.10.138 63815 192.168.10.186 389 9 - unbind - - - -
#close XXXX-XX-XX-XX-XX-XX

View file

@ -0,0 +1,14 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path ldap_search
#open XXXX-XX-XX-XX-XX-XX
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p message_id scope deref_aliases base_object result_count result diagnostic_message filter attributes
#types time string addr port addr port int string string string count string string string vector[string]
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 192.168.10.138 63815 192.168.10.186 389 1 base never - 1 success - (objectclass=*) -
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 192.168.10.138 63815 192.168.10.186 389 4 base never - 1 success - (objectClass=*) -
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 192.168.10.138 63815 192.168.10.186 389 6 single never CN=Schema,CN=Configuration,DC=matrix,DC=local 424 success - (&(!(isdefunct=TRUE))(|(|(|(|(|(attributeSyntax=2.5.5.17)(attributeSyntax=2.5.5.10))(attributeSyntax=2.5.5.15))(attributeSyntax=2.5.5.1))(attributeSyntax=2.5.5.7))(attributeSyntax=2.5.5.14))) -
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 192.168.10.138 63815 192.168.10.186 389 8 tree never DC=matrix,DC=local 1 success - (samaccountname=krbtgt) -
#close XXXX-XX-XX-XX-XX-XX

View file

@ -0,0 +1,13 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path conn
#open XXXX-XX-XX-XX-XX-XX
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p proto service duration orig_bytes resp_bytes conn_state missed_bytes history orig_pkts orig_ip_bytes resp_pkts resp_ip_bytes tunnel_parents
#types time string addr port addr port enum string interval count count string count string count count count count set[string]
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 10.199.2.121 59327 10.199.2.111 389 tcp ldap_tcp 63.273503 3963 400107 OTH 0 Dd 12 2595 282 411387 -
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 10.199.2.121 59355 10.199.2.111 389 tcp ldap_tcp 0.007979 2630 3327 OTH 0 Dd 6 990 6 3567 -
XXXXXXXXXX.XXXXXX C4J4Th3PJpwUYZZ6gc 10.199.2.121 59356 10.199.2.111 389 tcp ldap_tcp 0.001925 2183 3436 OTH 0 Dd 4 463 5 3636 -
#close XXXX-XX-XX-XX-XX-XX

View file

@ -0,0 +1,15 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path ldap
#open XXXX-XX-XX-XX-XX-XX
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p message_id version opcode result diagnostic_message object argument
#types time string addr port addr port int int string string string string string
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 10.199.2.121 59327 10.199.2.111 389 3 3 bind SASL success - - GSS-SPNEGO
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 10.199.2.121 59355 10.199.2.111 389 3 3 bind SASL success - - GSS-SPNEGO
XXXXXXXXXX.XXXXXX C4J4Th3PJpwUYZZ6gc 10.199.2.121 59356 10.199.2.111 389 9 3 bind SASL success - - GSS-SPNEGO
XXXXXXXXXX.XXXXXX C4J4Th3PJpwUYZZ6gc 10.199.2.121 59356 10.199.2.111 389 12 - unbind - - - -
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 10.199.2.121 59355 10.199.2.111 389 13 - unbind - - - -
#close XXXX-XX-XX-XX-XX-XX

View file

@ -0,0 +1,27 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path ldap_search
#open XXXX-XX-XX-XX-XX-XX
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p message_id scope deref_aliases base_object result_count result diagnostic_message filter attributes
#types time string addr port addr port int string string string count string string string vector[string]
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 10.199.2.121 59327 10.199.2.111 389 1 base never - 1 success - (objectclass=*) -
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 10.199.2.121 59327 10.199.2.111 389 4 base never - 1 success - (objectClass=*) -
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 10.199.2.121 59327 10.199.2.111 389 5 base never CN=Enrollment Services,CN=Public Key Services,CN=Services,CN=Configuration,DC=DMC,DC=local 1 success - (objectClass=*) -
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 10.199.2.121 59327 10.199.2.111 389 6 base never - 1 success - (objectClass=*) -
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 10.199.2.121 59327 10.199.2.111 389 7 tree never CN=Enrollment Services,CN=Public Key Services,CN=Services,CN=Configuration,DC=DMC,DC=local 2 success - (objectCategory=pKIEnrollmentService) -
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 10.199.2.121 59327 10.199.2.111 389 8 base never - 1 success - (objectClass=*) -
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 10.199.2.121 59327 10.199.2.111 389 9 base never CN=Schema,CN=Configuration,DC=DMC,DC=local 1 success - (objectClass=dMD) -
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 10.199.2.121 59327 10.199.2.111 389 10 base never CN=Schema,CN=Configuration,DC=DMC,DC=local 1 success - (objectClass=dMD) -
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 10.199.2.121 59327 10.199.2.111 389 11 base never CN=Aggregate,CN=Schema,CN=Configuration,DC=DMC,DC=local 1 success - (objectClass=*) -
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 10.199.2.121 59355 10.199.2.111 389 1 base never - 1 success - (objectclass=*) -
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 10.199.2.121 59355 10.199.2.111 389 4 base never CN=WS01,CN=Computers,DC=DMC,DC=local 1 success - (objectclass=*) -
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 10.199.2.121 59355 10.199.2.111 389 5 base never CN=WS01,CN=Computers,DC=DMC,DC=local 1 success - (objectclass=*) -
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 10.199.2.121 59355 10.199.2.111 389 6 base never CN=WS01,CN=Computers,DC=DMC,DC=local 1 success - (objectclass=*) -
XXXXXXXXXX.XXXXXX C4J4Th3PJpwUYZZ6gc 10.199.2.121 59356 10.199.2.111 389 10 base never - 1 success - (ObjectClass=*) -
XXXXXXXXXX.XXXXXX C4J4Th3PJpwUYZZ6gc 10.199.2.121 59356 10.199.2.111 389 11 base never CN=62a0ff2e-97b9-4513-943f-0d221bd30080,CN=Device Registration Configuration,CN=services,CN=Configuration,DC=DMC,DC=local 0 no such object 0000208D: NameErr: DSID-0310028B, problem 2001 (NO_OBJECT), data 0, best match of:??'CN=Services,CN=Configuration,DC=DMC,DC=local'?? (ObjectClass=*) -
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 10.199.2.121 59327 10.199.2.111 389 12 base never CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=DMC,DC=local 1 success - (objectClass=*) -
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 10.199.2.121 59327 10.199.2.111 389 13 tree never CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=DMC,DC=local 38 success - (objectclass=pKICertificateTemplate) -
#close XXXX-XX-XX-XX-XX-XX

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,11 @@
# Copyright (c) 2024 by the Zeek Project. See LICENSE for details.
# @TEST-REQUIRES: have-spicy
# @TEST-EXEC: zeek -C -r ${TRACES}/ldap/ldap-add.pcap %INPUT
# @TEST-EXEC: cat conn.log | zeek-cut -Cn local_orig local_resp > conn.log2 && mv conn.log2 conn.log
# @TEST-EXEC: btest-diff conn.log
# @TEST-EXEC: btest-diff ldap.log
# @TEST-EXEC: ! test -f dpd.log
# @TEST-EXEC: ! test -f analyzer.log
#
# @TEST-DOC: The addRequest/addResponse operation is not implemented, yet we process it.

View file

@ -0,0 +1,11 @@
# Copyright (c) 2024 by the Zeek Project. See LICENSE for details.
# @TEST-REQUIRES: have-spicy
# @TEST-EXEC: zeek -C -r ${TRACES}/ldap/missing_krbtgt_ldap_request.pcapng %INPUT
# @TEST-EXEC: cat conn.log | zeek-cut -Cn local_orig local_resp > conn.log2 && mv conn.log2 conn.log
# @TEST-EXEC: btest-diff conn.log
# @TEST-EXEC: btest-diff ldap.log
# @TEST-EXEC: btest-diff ldap_search.log
# @TEST-EXEC: ! test -f dpd.log
#
# @TEST-DOC: Test LDAP analyzer with GSS-API integrity traffic where we can still peak into LDAP wrapped into WRAP tokens.

View file

@ -0,0 +1,11 @@
# Copyright (c) 2024 by the Zeek Project. See LICENSE for details.
# @TEST-REQUIRES: have-spicy
# @TEST-EXEC: zeek -C -r ${TRACES}/ldap/missing_ldap_logs.pcapng %INPUT
# @TEST-EXEC: cat conn.log | zeek-cut -Cn local_orig local_resp > conn.log2 && mv conn.log2 conn.log
# @TEST-EXEC: btest-diff conn.log
# @TEST-EXEC: btest-diff ldap.log
# @TEST-EXEC: btest-diff ldap_search.log
# @TEST-EXEC: ! test -f dpd.log
#
# @TEST-DOC: Test LDAP analyzer with GSS-API integrity traffic where we can still peak into LDAP wrapped into WRAP tokens.