From Code to Coverage (Part 6): What netlogon.log Sees That Event 1644 Never Will
Part 6 of Huntress's "From Code to Coverage" series reverse-engineers ldapnomnom's LDAP Ping (cLDAP-style) Active Directory username enumeration technique, tracing its Go source down to the Windows Filtering Platform to test the tool's claim of generating "no Windows audit logs." Lab testing shows Event 1644 never fires for LDAP Ping (it's processed by netlogon.dll, not the ntdsa.dll LDAP engine it monitors), but netlogon.log captures every queried username including disabled-account detail attackers can't see, while Event 5156 attributes source IP for ldapnomnom's TCP connections — a gap that disappears entirely if an attacker switches to true UDP cLDAP (as demonstrated with a custom cldap_ping.py), leaving only MDI's threshold-based external ID 2437 as network-layer coverage. The piece also audits the ldeep tool's enum_users transport and provides a PowerShell correlation script joining netlogon.log and Event 5156 within a 5-second window.
What it is
ldapnomnom is an anonymous, unauthenticated AD username bruteforcer that abuses LDAP Ping (cLDAP) against Domain Controllers. Its README claims it generates "No Windows audit logs." The Huntress post traces the code path to test that claim and finds it wrong: the technique has a real gap (Event 1644 blindness), but the tool as shipped is more detectable than advertised.
How it works
DCs process LDAP over port 389 through three distinct stacks. Regular authenticated LDAP search (BloodHound, PowerView) and ADWS both reach the ntdsa.dll LDAP engine in LSASS — the engine that generates Event 1644. True UDP cLDAP / LDAP Ping is instead dispatched to the netlogon.dll handler (also in LSASS, different code path). The two share the wire format (BER-encoded SearchRequest) and port, but nothing in the query-processing path.
LDAP Ping does not query the directory tree. The filter (&(NtVer=...)(AAC=...)(User=...)) goes to Netlogon, which does a single targeted lookup of the sAMAccountName against the directory NC, applies an AAC bitmask test, and returns a hardcoded NETLOGON_SAM_LOGON_RESPONSE_EX blob. No filter parsing, no candidate walk, so Event 1644 cannot fire. This is architectural, not a logging-config problem — no registry key, verbosity setting, or Sigma rule makes 1644 see it.
flowchart TD
Q[LDAP query :389] --> D{path}
D -->|authenticated search| N[ntdsa.dll LDAP engine] --> E[Event 1644 fires]
D -->|ADWS| N
D -->|UDP cLDAP / LDAP Ping| M[netlogon.dll handler] --> X[no Event 1644]What to do
Netlogon debug logging captures inbound pings as [MAILSLOT] entries — this is the detection the tool's README omits. Enable it with:
nltest /dbflag:0x2080FFFFIt is off by default and, outside active account-lockout troubleshooting, rarely enabled, so this requires deliberate action.
Caveats
- The technique's Event 1644 gap is genuine and unfixable at the LDAP-auditing layer — detection depends on Netlogon debug logging (or network-layer telemetry the excerpt hints at but doesn't detail here).
- Defender asymmetry: the attacker receives the same
UnknownOperationCode whether the account doesn't exist, exists-but-disabled, or exists-but-fails-the-AAC-mask. The DC holds the distinguishing information; the wire response does not carry it. - LDAP Ping is pre-credential (enumerates who exists); everything after — group membership, Kerberoastability — needs authentication and is a separate detection story covered elsewhere in the series.
- References for the response-code and AAC/disabled routing logic: [MS-ADTS] §6.3.1.3 and §6.3.3.2.
Organisations named here
- Event 1644
- Event 5156
- Event 5807
- Lars Karlslund
- MDI external ID 2437
- MS-ADTS
- NETLOGON_SAM_LOGON_RESPONSE_EX
- RFC 1798
- Sapir Federovsky
- Synacktiv
- TROOPERS24
Technologies named here
- Active Directory Domain Controllers
- ldapnomnom
- lkarlslund/ldap library
- Windows Server
- Microsoft Defender for Identity
- cldap_ping.py
- ldeep
- BloodHound
- PowerView
- SOAPHound
- Get-LDAPPingDetection.ps1
- nltest
- Active Directory
- Windows
- PowerShell
- Go
- Python
- LDAP
Techniques named here
- LDAP Ping username enumeration
- Anonymous rootDSE query
- True UDP cLDAP enumeration
- Throttling/evasion via --throttle and --maxrequests flags
- Response opcode analysis for account state
- Password spray reconnaissance
Related in Blue Team & Detection
AI-Coded Malware | Analyzing Vibe-Coded AD Enumeration | Huntress
