Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ var baseDNMidFlags map[rune]string = map[rune]string{
'X': "HexValue",
'S': "Spacing",
'Q': "DoubleQuotes",
'U': "GUIDFormat",
'I': "SIDFormat",
}

var filterMidFlags map[rune]string = map[rune]string{
Expand All @@ -60,6 +62,8 @@ var filterMidFlags map[rune]string = map[rune]string{
's': "SubstringSplit",
'N': "NamesToANR",
'n': "ANRGarbageSubstring",
'P': "DNAttributesNoise",
'L': "TransitiveEval",
}

var attrListMidFlags map[rune]string = map[rune]string{
Expand Down Expand Up @@ -94,6 +98,8 @@ func SetupMiddlewaresMap() {
"HexValue": basednmid.RandHexValueBaseDNObf(optFloat("BDNHexValueProb")),
"Spacing": basednmid.RandSpacingBaseDNObf(optInt("BDNSpacingMaxElems")),
"DoubleQuotes": basednmid.DoubleQuotesBaseDNObf(),
"GUIDFormat": basednmid.GUIDBaseDNObf(optStr("BDNGuid")),
"SIDFormat": basednmid.SIDBaseDNObf(optStr("BDNSid")),
}

filterMidMap = map[string]filtermid.FilterMiddleware{
Expand All @@ -118,6 +124,8 @@ func SetupMiddlewaresMap() {
"SubstringSplit": filtermid.RandSubstringSplitFilterObf(optFloat("FiltSubstringSplitProb")),
"NamesToANR": filtermid.ANRAttributeFilterObf(ANRSet),
"ANRGarbageSubstring": filtermid.ANRSubstringGarbageFilterObf(optInt("FiltANRSubstringMaxElems"), optStr("FiltGarbageCharset")),
"DNAttributesNoise": filtermid.RandDNAttributesNoiseFilterObf(optFloat("FiltDNAttrNoiseProb")),
"TransitiveEval": filtermid.TransitiveEvalFilterObf(),
}

attrListMidMap = map[string]attrlistmid.AttrListMiddleware{
Expand Down
25 changes: 25 additions & 0 deletions middlewares/basedn/obfuscation.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,31 @@ func DoubleQuotesBaseDNObf() func(string) string {
}
}

// GUIDBaseDNObf replaces the BaseDN with the <GUID=hex> alternative form.
// Per MS-ADTS 3.1.1.3.1.2.4, AD supports <GUID=object_guid> where object_guid
// is the hex representation of the objectGUID attribute. This completely changes
// the BaseDN format, making it unrecognizable as a traditional DN.
func GUIDBaseDNObf(guidHex string) func(string) string {
return func(dn string) string {
if dn == "" || guidHex == "" {
return dn
}
return "<GUID=" + guidHex + ">"
}
}

// SIDBaseDNObf replaces the BaseDN with the <SID=sid> alternative form.
// Per MS-ADTS 3.1.1.3.1.2.4, AD supports <SID=sid> where sid is either
// the string form (S-1-5-21-...) or hex representation of the binary SID.
func SIDBaseDNObf(sid string) func(string) string {
return func(dn string) string {
if dn == "" || sid == "" {
return dn
}
return "<SID=" + sid + ">"
}
}

// RandHexValueBaseDNObf randomly hex encodes characters in BaseDN
func RandHexValueBaseDNObf(prob float64) func(string) string {
return func(dn string) string {
Expand Down
47 changes: 47 additions & 0 deletions middlewares/filter/obfuscation.go
Original file line number Diff line number Diff line change
Expand Up @@ -1279,3 +1279,50 @@ func ReplaceTautologiesFilterObf() func(parser.Filter) parser.Filter {
return filter
})
}

/*
New middlewares based on MS-ADTS research
*/

// Known link attributes that support LDAP_MATCHING_RULE_TRANSITIVE_EVAL
var transitiveLinkAttrs = []string{
"memberof", "member", "manager", "directreports",
"msds-membertransitive", "msds-memberoftransitive",
}

// RandDNAttributesNoiseFilterObf randomly toggles the dnAttributes field
// on extensible match filters. Per MS-ADTS 3.1.1.3.1.3.1, AD always
// ignores this field and treats it as FALSE, so toggling it adds noise
// without affecting query results.
func RandDNAttributesNoiseFilterObf(prob float64) func(parser.Filter) parser.Filter {
return LeafApplierFilterMiddleware(func(filter parser.Filter) parser.Filter {
switch f := filter.(type) {
case *parser.FilterExtensibleMatch:
if rand.Float64() < prob {
f.DNAttributes = !f.DNAttributes
}
}
return filter
})
}

// TransitiveEvalFilterObf converts equality matches on link attributes
// to use LDAP_MATCHING_RULE_TRANSITIVE_EVAL (1.2.840.113556.1.4.1941).
// Per MS-ADTS 3.1.1.3.4.4.3, this rule performs recursive evaluation
// of link attributes. For direct membership queries it produces
// equivalent results while changing the query syntax.
func TransitiveEvalFilterObf() func(parser.Filter) parser.Filter {
return LeafApplierFilterMiddleware(func(filter parser.Filter) parser.Filter {
switch f := filter.(type) {
case *parser.FilterEqualityMatch:
if slices.Contains(transitiveLinkAttrs, strings.ToLower(f.AttributeDesc)) {
return &parser.FilterExtensibleMatch{
MatchingRule: "1.2.840.113556.1.4.1941",
AttributeDesc: f.AttributeDesc,
MatchValue: f.AssertionValue,
}
}
}
return filter
})
}
3 changes: 3 additions & 0 deletions middlewares/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ var DefaultOptions = map[string]string{
"BDNOIDAttributeMaxSpaces": "4",
"BDNOIDAttributeMaxZeros": "4",
"BDNOIDAttributeIncludePrefix": "true",
"BDNGuid": "",
"BDNSid": "",

"FiltSpacingMaxSpaces": "4",
"FiltTimestampGarbageUseComma": "false",
Expand All @@ -25,6 +27,7 @@ var DefaultOptions = map[string]string{
"FiltOIDAttributeMaxSpaces": "4",
"FiltOIDAttributeMaxZeros": "4",
"FiltOIDAttributeIncludePrefix": "true",
"FiltDNAttrNoiseProb": "0.5",

"AttrsDuplicateProb": "0.7",
"AttrsCaseProb": "0.7",
Expand Down