Hunting Honey Pots: When Never Logged In Means Everything
In red team operations the opening moves decide the engagement. Early reconnaissance is not about pulling the largest possible dataset, it is about pulling the smallest dataset that still answers a question. The question that matters most at this stage is which identities in the environment are real and which ones exist only to catch an intruder. Subtle inconsistencies in identity data routinely expose defensive controls such as honeypots and honeytoken accounts long before any account is touched, and an operator who reads those inconsistencies correctly avoids the trap instead of springing it.
Active Directory exposes most of its structure through LDAP, but leaning on LDAP for everything is a mistake in a mature environment. Directory queries are some of the most heavily instrumented traffic on an internal network. Defenders baseline what normal lookups look like, then alert on the volume, the breadth, and the specific attributes that an attacker tends to request. A single query against the wrong object can be the difference between a quiet foothold and an analyst opening a ticket with your session ID attached to it.
Two native Windows surfaces let you collect account metadata without speaking LDAP at all. The LSA APIs and the Net APIs both return identity information through ordinary system mechanisms that legitimate software calls constantly. LSA functions such as LsaLookupNames2 and LsaEnumerateAccountRights talk to LSASS to resolve security principals and their privileges, while Net functions such as NetUserEnum and NetUserGetInfo return user records over standard SAMR and MS-RPC channels on port 445. Because backup agents, inventory tools, and management consoles hit these same calls all day, the traffic blends into the baseline rather than standing out against it.
That blending is exactly what makes these interfaces valuable for honeypot detection. The goal is to gather just enough signal to separate the decoys from the real population without generating the high confidence alert that interacting with a decoy is designed to produce. Before going further into the directory, it helps to see what this quiet enumeration actually returns, so the screenshot below shows a NetUserEnum sweep pulling the account list straight from SAMR without a single LDAP bind.
Each row in that output came back through the same RPC pipe that a legitimate management tool would use. There is no directory search filter, no paged LDAP result set, and nothing for a query baseline to flag as anomalous. From the defender point of view this looks like routine SAMR activity, which is precisely the cover an operator wants while building the initial list of accounts worth investigating.
The Role of LDAP and Its Visibility
LDAP is still the richest reconnaissance source available once you have a foothold. It exposes structured access to every directory object, users, groups, computers, and service principals, along with the attributes that describe their history and behaviour. Nothing else on an internal network gives you the same density of relationships in a single protocol, which is exactly why defenders watch it so closely.
From a detection standpoint, LDAP enumeration is high signal. Security platforms profile the shape of directory traffic and treat broad attribute sweeps, recursive group expansion, and queries against sensitive objects as strong indicators of reconnaissance. A normal workstation does not enumerate every user object and request servicePrincipalName, adminCount, and userAccountControl in one pass, so when something does, it stands out against the recorded norm.
The risk climbs sharply when honeytoken accounts are present. These accounts are built to be unused and deliberately attractive, often carrying names like svc_backup_adm or sql_da that imply elevated access. Any interaction at all, whether it is a directory lookup that reads their attributes or an authentication attempt against them, can be flagged as suspicious because no legitimate user has any reason to touch them. The decoy does not need to detect a successful compromise, it only needs to detect curiosity, and an LDAP query is curiosity rendered in protocol.
For that reason a careful operator minimises direct LDAP interaction with unknown objects and prefers quieter collection where the same fact can be obtained another way. The directory still gets used, but it gets used surgically and only after lower signal techniques have already narrowed the target set.
A practical way to obtain this same information while staying clear of LDAP detection is to read it through the SAM remote interface instead of the directory. The MS-SAMR protocol exposes the account database over the same RPC transport that ordinary management tooling already relies on, so a short sequence of SamConnect, SamOpenDomain, SamEnumerateUsersInDomain and SamQueryInformationUser returns every samAccountName together with its lastLogon value without issuing a single directory search. Because no LDAP bind and no search filter are ever sent, the query baselines that defenders depend on never register the activity, and the collection blends into the normal SAM traffic that already flows to the controller over port 445. The same calls extend cleanly to the machine accounts that end in a dollar sign, which makes it possible to map both decoy users and decoy computers entirely through the SAM layer. A complete code snippet that implements SAM enumeration and LSA translation APIs can be found using the following link sam_honeypot_enum.c
Understanding the LastLogon Attribute
The lastLogon attribute is one of the most dependable indicators of genuine account activity in Active Directory. It records the last successful interactive or network authentication for a user or computer object and is updated by the domain controller that processed the logon. Stored as a Windows FILETIME, it is a 64 bit count of 100 nanosecond intervals since January 1 1601, which is why a populated value looks like a large meaningless integer until it is converted back into a date.
The detail that makes this attribute so useful is its behaviour at the boundary. When an account has never authenticated, lastLogon is never written, so it stays at its default value of zero. There is no partial state and no ambiguity. An account either carries a real timestamp because someone or something used it, or it carries a flat zero because nothing ever has. That binary quality is what turns a timestamp into an oracle, and the screenshot below shows exactly that contrast against a suspected honeytoken next to a normal user.
The decoy account returns lastLogon set to 0 while the ordinary user returns a full FILETIME that converts to a recent date. One important caveat keeps this honest in a multi controller environment. The lastLogon attribute is not replicated between domain controllers, so each controller maintains its own copy and a busy account can read as zero on a controller it simply never authenticated against. The replicated lastLogonTimestamp attribute smooths this over for auditing, but it is deliberately imprecise and lags by days. For decoy hunting the non replicated lastLogon is still the sharper instrument, provided the value is checked across more than one controller before any conclusion is drawn.
One practical detail matters when reading the output of a tool rather than the raw attribute. Many utilities do not leave the value as a literal zero. Because lastLogon is stored as a Windows FILETIME counting from January 1 1601, a stored zero is the exact start of that epoch, and any tool that blindly converts the number into a calendar date will render it as a timestamp sitting on that origin. In practice it most often appears as 12/31/1600, because the conversion to local time subtracts a timezone offset and pushes the value just behind the 1601 boundary. A displayed date of 12/31/1600, or 1/1/1601 in tools that print it in UTC, is therefore not a real logon from the distant past. It is the cosmetic representation of a lastLogon that was never set, and it should be read as exactly the same honeypot signal as a literal zero.
Honey Pots and the Absence of History
Most honeytoken accounts and decoy computer objects share the same structural weakness. They are created to detect interaction, not to perform real work, so nobody ever logs into them and nothing is ever scheduled to run as them. They are dressed up to look valuable, given plausible names and group memberships, and then left untouched in the directory as bait.
The consequence is that they look completely legitimate at the schema level while carrying no behavioural history whatsoever. There is no authentication trail, no password age that reflects real rotation, no group policy processing, and no logon timestamp. Every attribute that describes who an object is can be forged in advance, but the attributes that describe what an object has done accumulate only through use, and use is the one thing a decoy cannot fake without defeating its own purpose.
This gap between a convincing identity and an empty history is what makes decoys detectable. When you place a suspected honeytoken next to the real population and compare their activity attributes side by side, the decoy stops blending in. The contrast below lines up a genuine account against a honeypot across the attributes that record behaviour rather than identity.
The real account on the left shows a recent lastLogon, a non zero logonCount, and a password that has been changed at a believable point in time. The honeypot on the right shows zeros and defaults across every one of those fields while still presenting an attractive name and an elevated looking group. Identity attributes alone would never separate them, but the behavioural attributes do it immediately.
Using LastLogon as an Oracle
The core inference is simple to state. An account that appears valuable but has never been used is far more likely to be artificial than an oversight, because real privileged accounts get used and real disused accounts rarely keep their privilege. By correlating the displayed importance of an account, its name, its group membership, its apparent role, with the lastLogon attribute, the inconsistencies that betray a decoy become visible without ever authenticating against it.
The technique works because of an asymmetry in effort. Defenders invest heavily in making the structure of a decoy convincing, the name, the description, the membership in a Tier 0 group, but they rarely invest in simulating a long term behavioural pattern, because doing so realistically would mean actually using the account and exposing it to the very compromise it is meant to catch. The structure is cheap to fake and the history is expensive, so the history is where the lie shows.
Treating the absence of a lastLogon value as an oracle lets an operator triage a target list cheaply. Accounts with genuine history move to the top as real candidates, accounts that are valuable on paper but empty in practice get marked as probable decoys and avoided. None of this requires touching the suspected object directly, which is the entire point. The decision to leave an account alone is made from quiet metadata collected through Net and LSA calls, not from the loud directory interaction that would trip the trap.
The Same Tell on Machine Accounts
Everything described so far applies just as cleanly to computer accounts, and in several respects it applies with even greater force. Every domain joined workstation and server holds a machine account in Active Directory, represented by a samAccountName that ends in a dollar sign such as WS014$ or SQLPROD01$. These objects are not cosmetic. The dollar sign account is the identity a machine uses to authenticate to the domain, and it is exercised continuously throughout normal operation rather than sitting idle.
The reason the lastLogon signal is even sharper on computers is the enrollment process itself. A machine cannot become a member of the domain without first talking to a domain controller. When a host is joined, whether through the classic NetJoinDomain call, an unattended provisioning package, or an Offline Domain Join blob, it establishes a Netlogon secure channel and authenticates with its machine account at least once in order to complete enrollment. From that moment the account keeps re-authenticating on a schedule, rotating its machine password roughly every thirty days and requesting Kerberos tickets for the services it consumes. A legitimate computer object therefore cannot carry a lastLogon of zero, because the act of becoming a real computer object is what writes that value in the first place.
That property makes a decoy computer trivial to expose. A defender can pre stage an attractive machine account, give it a server sounding name and a tempting servicePrincipalName, and drop it into the directory as bait, but they cannot manufacture authentication history without actually joining a host and exposing the trap they are trying to set. Running the same enumeration against accounts that end in a dollar sign makes the decoy surface immediately, as the screenshot below shows.
The genuine workstation and servers in that output all carry a recent lastLogon, which is produced only by a live secure channel renewing itself against the controller. The two bait objects present convincing names, one even imitating a backup domain controller, yet both report lastLogon set to zero, a state that is effectively impossible for a computer in a working environment. An account that claims to be a domain joined machine but has never authenticated never finished joining, and an object that never joined is far more likely to be a tripwire than a real host. The same quiet collection used for user accounts therefore doubles as a way to map the decoy machines without ever authenticating against them.
Detection That Matters
Honeypots are a genuinely strong defensive mechanism and they deserve respect from both sides of the engagement. Because any interaction with a true decoy is by definition illegitimate, the alerts they generate carry very high confidence and very low false positive rates, which is rare and valuable in detection engineering. A defender who deploys them well gets a tripwire that fires only when something is actually wrong.
From the offensive side they introduce real risk during reconnaissance and lateral movement, and the cheapest way to manage that risk is to read the signals the decoys cannot hide. The lastLogon attribute, collected carefully and confirmed across controllers, is one of the clearest of those signals and it costs almost nothing to check.
None of this makes honeypots less worthwhile for the defender. They are one layer in a strategy that should also include directory query monitoring, authentication anomaly detection, and tight control over who can read activity attributes in the first place. A defender who assumes a single tripwire is enough will eventually meet an operator patient enough to map the environment without stepping on it, and a defender who layers honeypots with behavioural monitoring forces that same operator to make a mistake somewhere else instead.