skip to content

What makes an Active Directory decoy account carrying an SPN believable to an adversary who checks before biting?

level: middleimportance: should knowfreq 44%

answer

  1. it must survive being looked at
  2. clone a real peer's attribute profile
  3. freshness and emptiness are the tells
  4. look valuable, hold no privilege
  5. ageing it creates an exclusion

basics

~20 s

Consistency with its peers: an age band, naming convention, organisational unit, group memberships and description matching real service accounts, plus a service principal name pointing at a host that exists. Bare, brand-new objects get skipped.

solid answer

~50 s

An adversary who enumerates service accounts sorts the results before spending effort, so the decoy has to survive a glance at its attributes. Put it in the same organisational unit as real service accounts, name it to the same convention, give it a password age inside the same band as its peers, populate `description` and `managedBy` the way real ones are populated, add it to a group that real service accounts belong to, and make its service principal name reference a host that resolves. Anything that reads as synthetic — created this morning, never authenticated, sole occupant of an empty container, a description saying "do not use" — gets filtered out. Two rules on top: it must look valuable without being valuable, so never grant it real privilege, and it must not be findable in the vault, the CMDB or a runbook, because the same adversary reads those.

code

text · 18 lines
text
# real peer
distinguishedName: CN=svc-sql-prd-04,OU=ServiceAccounts,DC=corp,DC=example
servicePrincipalName: MSSQLSvc/sql-prd-04.corp.example:1433
description: SQL Server engine account - owned by Data Platform
managedBy: CN=Data Platform Team,OU=Groups,...
memberOf: CN=SQL Service Accounts,OU=Groups,...
pwdLastSet: 2025-11-03
whenCreated: 2023-02-17

# decoy - same OU, same convention, same age band, no real rights
distinguishedName: CN=svc-sql-rpt-02,OU=ServiceAccounts,DC=corp,DC=example
servicePrincipalName: MSSQLSvc/sql-rpt-02.corp.example:1433
description: Reporting extract account - owned by Data Platform
managedBy: CN=Data Platform Team,OU=Groups,...
memberOf: CN=SQL Service Accounts,OU=Groups,...
pwdLastSet: 2025-09-22
whenCreated: 2023-04-05
...

go deeper

for a junior

Be ready to name the obvious tells that give a decoy away: brand-new creation date, never used, blank description, no group memberships, and a name containing words like honeypot or test.

for a middle

Explain how to build the lineage: mirror a real service account's organisational unit, naming convention, password age band, description, owner and group membership, and point the service principal name at a host that resolves.

for a senior

Show the trade you make to age the bait. Refreshing last-logon means authenticating as the decoy and carving that out, so pin the identity, source host and schedule and alert on deviation — or accept a dormant-looking account instead.

for a principal

Own the rule that bait looks valuable and is worthless: no real privilege, a long random password, and no presence in the vault, CMDB or runbook that an intruder enumerates before it reaches the decoy.

## The check the bait has to survive A decoy service account only works if the adversary spends a request on it. Anyone enumerating the directory for accounts that carry a service principal name gets back a list, and the list is triaged before it is used: which of these look like they belong to a real, running, valuable service? Effort goes to the plausible ones. So the design problem for a deception owner is not "can this be found" but "does this survive being looked at". An account carrying a service principal name is bait specifically because that attribute makes it requestable by ordinary domain users, and the directory records that request — you get a signal without the adversary ever needing to reach a host you own. The attribute is the hook; believability is what makes the hook get taken. ## The attributes that betray a decoy The tells are almost always freshness and isolation: - **Creation date and password age.** An account created this week, or whose password was set the day the sweep runs, is obviously not running anything. Real service accounts in most estates have passwords months or years old. Create the decoy well before you need it, or at least during a batch of real account creations, and let it age. - **Never-used.** An object with no recorded prior authentication reads as abandoned or synthetic. - **Empty context.** No description, no owner, no company or department, no group memberships. Real service accounts accumulate lineage; decoys are usually born bare. - **Wrong neighbourhood.** Sole occupant of a container nobody else lives in, or sitting in the default users container while every real service account lives in a dedicated organisational unit. - **Naming that breaks the convention.** If real accounts read `svc-sql-prd-04`, then `svc_test_account1` is a flag, and anything containing *honey*, *canary*, *decoy* or *trap* is a gift. - **A service principal name pointing nowhere.** If the value names a host that does not resolve or an application nobody has heard of, the account describes a service that does not exist. ## Building the lineage deliberately Mirror a real peer and change only what must change. Same organisational unit, same naming pattern, same description style, same `managedBy`, membership of a group real service accounts belong to — chosen so it confers no meaningful access. Point the service principal name at a host that exists in DNS, ideally one that genuinely runs something of that class. Give the surrounding story a reason to exist: a description referencing a reporting database is more inviting, and more plausible, than a blank field. ## The last-logon problem, and the exclusion it creates Making the account look *used* is the hardest part, because the only way to update its last-logon is to authenticate as it — which is the very event you are detecting. Teams that do this schedule a benign authentication from one known host and carve that exact combination out of the detection. Understand the cost before you agree to it: you have created a documented exclusion (this identity, from this host, on this schedule), and an exclusion is a shape an adversary can operate inside. If you accept it, pin every element of the tuple and alert when any of them deviates, so the carve-out is itself a detection. Many teams decide a plausible age and rich context are enough and leave last-logon alone. ## Look valuable, be worthless The temptation is to make the bait irresistible by giving it real privilege — privileged group membership, rights on a real server. Do not. Bait is an account whose credential material an adversary may try to recover offline, and a decoy that is genuinely privileged converts your tripwire into an access path you handed over. Value should be entirely in the *appearance*: the group name, the description, the service principal name. Set a long random password so recovery is impractical, and keep the account's actual rights at the floor. ## Believable also means undocumented An adversary inside the estate reads what your staff read. If the decoy appears in the password vault, the configuration management database, a monitoring check, a joiners-and-leavers spreadsheet or the SOC's own wiki tagged as a decoy, then the same enumeration that finds the account finds the answer. Deception has to be recorded somewhere — that is a real operational need — but not in the systems an intruder reaches first. ## What a strong answer sounds like "I clone the attribute profile of a real service account: same organisational unit, same naming convention, populated description and owner, a group membership real ones have, a password aged into the same band, and a service principal name for a host that actually resolves. It looks valuable and holds no privilege, and it exists in no vault, CMDB or runbook — because the adversary reads those too."

  • Should the decoy be given a weak password so it is easier to take the bait?
    No. The signal you want fires at the moment the account is targeted, not when a credential is recovered, so weakness buys nothing detective and creates real risk: if the material is ever recovered, an account you designed to look valuable becomes usable. Set a long random password and keep the appearance of value in the group name, description and service principal name.
  • How do you make the decoy look recently used without generating the events you detect on?
    Usually you do not. The only way to refresh last-logon is to authenticate as the account, which forces an exclusion for that identity, host and schedule — a documented shape an adversary can operate inside. If you accept it, pin the whole tuple and alert on any deviation so the carve-out is itself a detection; otherwise rely on a plausible creation date, password age and populated context.
  • Where should the existence of the decoy be recorded?
    Somewhere an intruder in the estate does not reach early — not the password vault, the configuration management database, the monitoring configuration or an AD-authenticated wiki, all of which are enumerated. Keep a minimal record with a named owner outside those systems, and protect the object with directory-level controls rather than relying on staff knowing not to delete it.

saying these in an interview costs you the question

  • Gives the decoy real privileged group membership to make it attractive
  • Names it honeypot, canary or trap in the account name or description
  • Creates it the day the detection goes live with no ageing
  • Leaves description, owner and group memberships empty
  • Stores the decoy credential in the password vault like a real account
  • Points the service principal name at a host that does not resolve

context