From Code to Coverage (Part 2): The Whitespace Nightmare
Detection stack
- AIDR
- Alert
- ETL
- Query
Summary
The article explains how LDAP filters produced by Impacket tooling are normalized by Active Directory in ways that introduce inconsistent whitespace in Event ID 1644 logs. These formatting shifts can break detections that rely on exact string matching, even when the underlying filter logic is identical. The author shows why basic “contains” logic is fragile and walks through building resilient Sigma detections using regular expressions that tolerate spacing differences, case changes, and operator variations.
Investigation
To validate the issue, the author reviewed real Event 1644 records from production and documented multiple whitespace permutations of the same bitwise LDAP filter (e.g., variants of userAccountControl&524288 with different spacing, parentheses placement, and formatting). Several detection approaches were trialed—starting with static string checks and progressing to increasingly flexible regex patterns—until the rule reliably matched all observed representations without overfiring.
Mitigation
Adopt regex-based detections that allow optional whitespace, support case-insensitive matching, and account for both AND/OR operator styles. To keep performance reasonable, pre-filter on the target attribute name before applying the more expensive regex logic. Finally, continuously validate detections against a curated “wall of shame” corpus that captures every whitespace and formatting variant seen in the field.
Response
When a suspicious LDAP filter match is triggered, notify the SOC to assess potential privilege-discovery or escalation-related enumeration activity. Correlate the event with source host/IP, requesting user, and other LDAP attributes to determine intent and scope. If false positives occur, tune thresholds and rule conditions while preserving coverage for known formatting variants.
Attack Flow
We are still updating this part. Sign up to get notified
Notify MeDetections
Possible Delegation Enumeration via userAccountControl (via directory service)
View
Possible Kerberoasting Enumeration via Disabled Account Exclusion (via directory service)
View
Detection of LDAP Service Layer Bitwise Operations with Whitespace Variations [Microsoft Windows Security Event Log]
View
Simulation Execution
Prerequisite: The Telemetry & Baseline Pre‑flight Check must have passed.
Rationale: This section details the precise execution of the adversary technique (TTP) designed to trigger the detection rule. The commands and narrative MUST directly reflect the TTPs identified and aim to generate the exact telemetry expected by the detection logic.
-
Attack Narrative & Commands:
An adversary with low‑privilege domain credentials wishes to locate privileged accounts for lateral movement. They craft an LDAP filter that performs a bitwise AND onuserAccountControlto isolate accounts with theADMINISTRATORflag (value 524288). To evade simple string matching, they add extra whitespace and parentheses exactly as the Sigma rule expects.- Build the LDAP filter string with whitespace variations.
- Execute the LDAP search using
ldapsearch(via PowerShell) against the domain controller. - Verify that the Security Event Log records Event 1644 containing the crafted filter.
-
Regression Test Script:
# -------------------------------------------------------------- # Simulate LDAP bitwise filter that should trigger the Sigma rule # -------------------------------------------------------------- # Parameters $DomainController = "dc01.example.com" $BaseDN = "DC=example,DC=com" $Filter = "( userAccountControl & 524288 )" $Attributes = "distinguishedName,samAccountName,userAccountControl" # Execute LDAP query try { $result = [ADSI]"LDAP://$DomainController/$BaseDN" $searcher = New-Object System.DirectoryServices.DirectorySearcher($result) $searcher.Filter = $Filter $searcher.PropertiesToLoad.AddRange($Attributes.Split(',')) $searcher.PageSize = 1000 $entries = $searcher.FindAll() foreach ($entry in $entries) { $dn = $entry.Properties["distinguishedname"][0] $sam = $entry.Properties["samaccountname"][0] $uac = $entry.Properties["useraccountcontrol"][0] Write-Output "Found: $sam ($dn) – UAC=$uac" } } catch { Write-Error "LDAP query failed: $_" } -
Cleanup Commands:
# No persistent changes were made; only close the ADSI connection. Write-Output "Cleanup complete."