How To Hunt For LDAP Reconnaissance


Security principal reconnaissance (LDAP) on one endpoint


What happened

An actor on Server01 sent suspicious LDAP queries to 2 domain controllers, searching for 2 types of enumeration in

SearchFilter: ( & (objectClass=user) (objectCategory=CN=Computer,CN=Schema,CN=Configuration,DC=prod,DC=tdomaina,DC=com) ( | ( ! (lastLogonTimestamp=*) ) (lastLogonTimestamp=0) (lastLogonTimestamp>=132813001255230000) ) ( | ( ! (pwdLastSet=*) ) (pwdLastSet=0) (pwdLastSet>=132813001255230000) ) )


How do I find this LDAP Query is suspicious or not?

1 Reply
best response confirmed by MichaelJMelone (Microsoft)

Hello @AusSupport180!


Let me break this one out. In the outer parentheses we have an and condition checking four conditions



The first test is looking for user class principals in Active Directory (this is more than just user accounts, it also includes computer accounts)



The second filter reduces the results to look specifically for computer objects. At this point we know the user \ application is searching for computer objects in Active Directory


( | ( ! (lastLogonTimestamp=*) ) (lastLogonTimestamp=0) (lastLogonTimestamp>=132813001255230000) )

I color coded the parenthesis for the third filter to make it easier to read. All three terms are looking at the lastLogonTimestamp attribute which is essentially the last time a user principal performed an interactive logon. Its worth noting that there are a lot of nuances to this attribute that I won't go into here.

This portion of the query is an 'or' query (note the pipe). The first term in red is a not which applies to the blue section that checks that the lastLogonTimestamp is set to any value at all. In other words, this portion is checking for when lastLogonTimestamp is set to nothing,

The term in teal is checking for when lastLogonTimestamp is set to 0, another check to see if it has never been set before.

The last term in purple is checking for when the lastLogonTimestamp is greater than that large number. This value is an integer representing the number of 100 nanosecond intervals since January 1, 1601 UTC. Its usually easier to just find a converter like this one: LDAP, Active Directory & Filetime Timestamp Converter ( This part of the query is checking for lastLogonTimestamp attributes on or after November 13, 2021 18:02:05 UTC. Summing this up, we are checking where the lastLogonTimestamp is null, set to 0, or was after November 13, 2021. 

As I mentioned, the lastLogonTimestamp is a bit confusing as there are conditions where it is never set - such as I think cluster computer accounts (don't hold me to this though). Looks like its looking for active or unused computer accounts so far.


( | ( ! (pwdLastSet=*) ) (pwdLastSet=0) (pwdLastSet>=132813001255230000)

This last one is checking the pwdLastSet attribute which corresponds to when the password was last set on the account. pwdLastSet is pretty straightforward though, unlike lastLogonTimestamp. We're doing the same tests, so this is easy. We're looking for conditions where the accounts password was never set, or the password was last set since November 13, 2021. By default, a computer account changes its password every 30 days (based on the local security policy) so we're looking for computer accounts that are either active since November 2021 or have never set their password.


In summary, this needs to be taken in context. The LDAP filter isn't looking for something inherently malicious unless there is a known piece of malware that uses this exact search filter. What it would yield is all computer accounts in Active Directory that are either active since November 2021 or have never been used.