Skip to content

AD honeytokens: honey accounts, honey SPNs and decoys

Deploy honey accounts, honey SPNs, AS-REP decoys, fake GPP passwords and read-audited decoy objects in AD, and alert on them with near-zero false positives.

Florian Amette7 min read

Most AD detections are statistical. Many 4769 requests with RC4, more failed logons than usual, a group change at an unusual hour. They need tuning, and they produce false positives that train analysts to ignore them. Deception flips that model. You plant objects that no legitimate user, service or script ever touches, then treat any interaction with them as an incident. A well-placed honeytoken turns Kerberoasting, AS-REP roasting, password spraying and LDAP reconnaissance into near-certain signals.

This guide builds five decoys: a honey admin account, a honey SPN, an AS-REP roastable decoy, a fake Group Policy Preferences password and a read-audited decoy object. It then covers alerting, verification and what to exclude so your own tooling does not trip them. It assumes the audit policy and SACL groundwork from the auditing and detection pillar guide is in place.

Design principles

A decoy only works if it is attractive, inert and monitored:

  • Attractive. It shows up in the enumeration attackers already run: BloodHound collection, SPN listing, adminCount=1 queries, SYSVOL searches. Names, descriptions and dates should blend in with your naming convention. svc-sql-backup works. honeypot01 does not.
  • Inert. It grants nothing. No real group memberships with rights, no delegation, no logon rights, and a password of 64 or more random characters that is never recorded anywhere.
  • Monitored. Every decoy has a detection rule that has been tested end to end before you rely on it.

Keep a register of decoys, with name, SID, purpose, rule ID and owner, outside AD in your SOC documentation. The fewer people who know which accounts are decoys, the better. That includes most domain admins.

Measure: what attackers see today

Look at your domain the way reconnaissance tooling does, so decoys match the landscape:

PowerShell
# Existing user SPNs (what a Kerberoasting tool lists)
Get-ADUser -Filter 'servicePrincipalName -like "*"' -Properties servicePrincipalName, pwdLastSet, description |
  Select-Object SamAccountName, @{n='SPN';e={$_.servicePrincipalName -join ';'}},
    @{n='PwdLastSet';e={[datetime]::FromFileTime($_.pwdLastSet)}}, Description

# Accounts flagged adminCount=1 (what "privileged account" queries list)
Get-ADUser -LDAPFilter '(adminCount=1)' | Select-Object SamAccountName, DistinguishedName

Mirror the naming, OU placement and description style you find. Real Kerberoastable accounts should be fixed, not just joined by decoys. See Kerberoasting defense.

Build the decoys

Create all decoys in a normal-looking OU, not a dedicated "Decoys" OU. Apply Deny log on locally, Deny log on through Remote Desktop Services and Deny access to this computer from the network to a group containing them. Configure these through a GPO under Computer Configuration > Policies > Windows Settings > Security Settings > Local Policies > User Rights Assignment.

Honey admin account

A decoy that looks like a forgotten administrator:

PowerShell
# Windows PowerShell 5.1: System.Web is available on .NET Framework
Add-Type -AssemblyName System.Web
function New-DecoyPassword {
  ConvertTo-SecureString ([System.Web.Security.Membership]::GeneratePassword(64,10)) -AsPlainText -Force
}
New-ADUser -Name 'adm-jmorel' -SamAccountName 'adm-jmorel' -Path 'OU=Admins,OU=Corp,DC=corp,DC=example,DC=com' `
  -Description 'Infra admin - legacy' -AccountPassword (New-DecoyPassword) -Enabled $true -AccountNotDelegated $true

Put it in a group named like an admin group that holds no rights, or give it adminCount=1 so it appears in "privileged" queries. Do not add it to Domain Admins or any real Tier 0 group. That would make the decoy a real Tier 0 account.

Honey SPN

Attach a plausible SPN to a decoy service account. Any 4769 for that SPN is someone requesting a ticket for a service that does not exist:

PowerShell
New-ADUser -Name 'svc-sqlrpt' -SamAccountName 'svc-sqlrpt' -Path 'OU=Service Accounts,OU=Corp,DC=corp,DC=example,DC=com' `
  -AccountPassword (New-DecoyPassword) -Enabled $true -Description 'SQL Reporting Services'
Set-ADUser 'svc-sqlrpt' -ServicePrincipalNames @{ Add = 'MSSQLSvc/sqlrpt01.corp.example.com:1433' }

Make sure no DNS record or host named sqlrpt01 exists, so no client ever tries to reach it by accident.

AS-REP roastable decoy

AS-REP roasting targets accounts with Do not require Kerberos preauthentication set. A decoy with this flag produces 4768 with PreAuthType 0 when someone roasts it:

PowerShell
New-ADUser -Name 'svc-scanlegacy' -SamAccountName 'svc-scanlegacy' -Path 'OU=Service Accounts,OU=Corp,DC=corp,DC=example,DC=com' `
  -AccountPassword (New-DecoyPassword) -Enabled $true -Description 'Legacy scanner (Unix)'
Set-ADAccountControl -Identity 'svc-scanlegacy' -DoesNotRequirePreAuth $true

With a 64-character random password, the roasted hash is useless offline. The request itself is the alert.

Fake GPP password

Attackers still search SYSVOL for cpassword values. Removing real GPP passwords comes first. Then a decoy Groups.xml in an unlinked GPO, whose cpassword decrypts to a believable but wrong password for the honey admin account, gives you two alert points:

  1. Event 5145 (Audit Detailed File Share) for access to that specific Groups.xml path on the SYSVOL share, if you can absorb the volume on DCs.
  2. 4771, 4776 or 4625 when the attacker tries the decrypted password against the decoy account.

Use a GPO that is not linked anywhere, so no client ever processes it. Do not reuse a password pattern from your real environment.

Read-audited decoy object

LDAP reconnaissance such as BloodHound or ADExplorer reads every object. A SACL that audits reads on one decoy object turns that sweep into a 4662:

PowerShell
$dn   = 'CN=adm-jmorel,OU=Admins,OU=Corp,DC=corp,DC=example,DC=com'
$acl  = Get-Acl "AD:\$dn"
$rule = New-Object System.DirectoryServices.ActiveDirectoryAuditRule(
    [System.Security.Principal.SecurityIdentifier]'S-1-1-0',
    [System.DirectoryServices.ActiveDirectoryRights]'ReadProperty',
    [System.Security.AccessControl.AuditFlags]'Success')
$acl.AddAuditRule($rule)
Set-Acl -Path "AD:\$dn" -AclObject $acl

This requires DS Access > Audit Directory Service Access (Success) on the domain controllers. Scope the SACL to this single object. Read auditing on anything busy floods the log. For context on reading this kind of attack graph defensively, see attack path management.

Placement and lifecycle

Decoys age like real accounts. A service account whose pwdLastSet is today and that has never logged on looks planted. Create decoys, then let them sit for a few weeks before relying on them. Stagger their creation dates, and avoid creating them all in one change that shows up in the same 4720 burst.

Spread decoys across the places attackers look: at least one in each domain of the forest, one in the OU where your real admins live, and one among service accounts. In large environments, a decoy per major business unit OU helps you tell which part of the network the reconnaissance came from, because the IpAddress field in 4768 and 4769 shows the source host.

Review decoys twice a year. Confirm they still exist, still have their SACLs and SPNs, and still trigger their rules. An object cleanup project that deletes your decoys is a quiet way to lose coverage.

Enforce: alert rules

Write one rule per decoy, keyed on the SID where the event carries it and on the name otherwise:

DecoyEvent IDsCondition
Honey admin4768, 4771, 4776, 4624, 4625TargetUserName equals decoy
Honey SPN4769ServiceName equals decoy account
AS-REP decoy4768TargetUserName equals decoy (any PreAuthType)
Fake GPP5145, 4771, 4776RelativeTargetName ends with the decoy GPO path, or the target is the decoy account
Read-audited object4662ObjectName equals decoy GUID, AccessMask 0x10 (read property)

A local check for testing, or for small environments without a SIEM:

PowerShell
$decoys = 'adm-jmorel','svc-sqlrpt','svc-scanlegacy'
Get-WinEvent -FilterHashtable @{ LogName='Security'; Id=4768,4769,4771,4776; StartTime=(Get-Date).AddHours(-1) } |
  Where-Object {
    $d = ([xml]$_.ToXml()).Event.EventData.Data
    ($d | Where-Object { $_.Name -in 'TargetUserName','ServiceName' }).'#text' | Where-Object { $_ -in $decoys }
  } | Select-Object TimeCreated, Id, MachineName

Route these alerts to paging, not to a daily review queue. If you run Microsoft Defender for Identity, also tag each decoy as a honeytoken under Settings > Identities > Entity tags > Honeytoken, so the sensor raises its own alert even if your SIEM rule breaks. The honeytoken glossary entry summarises the concept for stakeholders.

Honey accounts are only as good as the event coverage behind them. All of the rules above depend on events from every DC reaching the SIEM, which is what Windows Event Forwarding provides.

Verify

Test every decoy from a normal domain-joined workstation, as a standard user, after a change ticket. Warn the SOC first, or run it as a planned purple-team exercise:

PowerShell
# Honey SPN: request a service ticket, then confirm a 4769 reached the SIEM
Add-Type -AssemblyName System.IdentityModel
New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken -ArgumentList 'MSSQLSvc/sqlrpt01.corp.example.com:1433'
klist

# Honey admin: a failed Kerberos logon produces 4771 on the DC
runas /user:CORP\adm-jmorel cmd.exe   # enter a wrong password

For the read-audited object, open it in Active Directory Users and Computers with the Attribute Editor, then confirm a 4662 with your user as SubjectUserName. Record the end-to-end latency, from action to page, for each decoy. Repeat the test quarterly and after any SIEM or collector change.

What it breaks

Decoys do not change production behaviour, but they collide with legitimate tools that read or touch every object:

  • Entra Connect and other sync engines read every object in scope. Keep read-audited decoys in an OU outside the sync scope, or exclude the sync account from the 4662 rule by SID.
  • Assessment and inventory tools (PingCastle, BloodHound collection run by your own team, CMDB connectors, Defender for Identity sensors) trigger read SACLs and may request service tickets. Exclude their service accounts explicitly and document the exclusion, knowing attackers may try to hide behind the same accounts.
  • Password spraying protection and lockout. A decoy locking out through attacker activity is fine, but make sure no helpdesk automation unlocks it or resets its password.
  • Account cleanup scripts that disable accounts inactive for 90 days will disable your decoys. Exclude them by SID in the script, not by a naming pattern an attacker could learn.
  • Auditors and pentesters will trip them. That is a feature, but agree the response process with them before the engagement.

Related reading: the AD security event IDs reference explains every field used above, Kerberoasting defense removes the real roastable accounts the decoys sit among, and the auditing, logging and detection area lists the rest of the series.

Frequently asked questions

Should a honey account be disabled?

Usually not. A disabled account stands out in reconnaissance output and many tools filter it away, so attackers ignore it. Keep the account enabled with a long random password nobody knows, deny it interactive and network logon through user rights assignment and logon hours, and make it look plausible. Any authentication attempt, successful or not, still produces events 4768, 4771 or 4776 on the domain controller, which is what you alert on.

Will a honey SPN catch every Kerberoasting attempt?

No. It catches attackers who request tickets for every SPN in the domain, which is what most tooling does by default. A careful operator who filters for SPNs on privileged or recently used accounts may skip it. Make the decoy attractive with a believable service name, an old password last set date and membership of a group that looks privileged but has no rights, and combine it with volume-based detection on event 4769.

Do honeytokens replace Microsoft Defender for Identity or a SIEM?

No, they sit on top of whatever collects your DC events. Defender for Identity can tag accounts as honeytokens and alert natively on their use, while a SIEM needs a simple rule on 4768, 4769, 4771, 4776, 4624 or 4662 for the decoy's name or SID. The value of honeytokens is that the rule is almost free of false positives, so the alert can page someone rather than land in a queue.

AD honeytokens: honey accounts, honey SPNs and decoys

Related guides

Auditing, Logging & Detection

Windows Event Forwarding for domain controllers

Build a Windows Event Forwarding pipeline for DCs: source-initiated subscriptions, GPO, log access, XPath queries, collector sizing and health checks.

Intermediate