npx skills add mukul975/Anthropic-Cybersecurity-SkillsMITRE ATT&CK
When to Use
- When deploying deception-based detection in Active Directory environments
- When detecting Kerberoasting attacks via fake SPN honeytokens (honeyroasting)
- When creating tripwire accounts to detect credential theft and lateral movement
- When building decoy GPOs to detect Group Policy Preference password harvesting
- When creating deceptive BloodHound paths to misdirect and detect attackers
- When supplementing existing AD monitoring with high-fidelity detection signals
Prerequisites
- Domain Admin or delegated AD administration privileges
- Active Directory domain (Windows Server 2016+ recommended)
- Windows Event Log forwarding to SIEM (Splunk, Sentinel, Elastic)
- PowerShell 5.1+ with ActiveDirectory module
- Group Policy Management Console (GPMC)
- Understanding of AD security, Kerberos, and BloodHound attack paths
Background
Why AD Honeytokens
Traditional signature-based detection misses novel attack techniques. Honeytokens provide high-fidelity detection with near-zero false positives because any interaction with a decoy object is inherently suspicious. In Active Directory:
- Fake privileged accounts detect credential dumping (DCSync, NTDS.dit extraction)
- Fake SPNs detect Kerberoasting reconnaissance (TGS requests for nonexistent services)
- Decoy GPOs detect Group Policy Preference password harvesting
- Fake BloodHound paths mislead attackers using graph-based AD analysis
Key Detection Event IDs
| Event ID | Description | Honeytoken Use |
|---|---|---|
| 4769 | Kerberos TGS ticket requested | Detect Kerberoast against honey SPN |
| 4625 | Failed logon attempt | Detect use of fake credentials from decoy GPO |
| 4662 | Directory service object accessed | Detect DACL read on honeytoken user |
| 5136 | Directory service object modified | Detect modification of decoy GPO |
| 5137 | Directory service object created | Detect GPO creation mimicking decoy |
| 4768 | Kerberos TGT requested | Detect AS-REP roasting of honey account |
Making Honeytokens Realistic
Per Trimarc Security research, effective honeytokens must appear legitimate:
- Age the account: Repurpose old inactive accounts (10-15 year old accounts in similarly aged domains appear authentic)
- Set AdminCount=1: Flags the account as having elevated AD rights, making it an attractive Kerberoasting target
- Use realistic naming: Match organizational naming conventions (svc_sqlbackup, admin.maintenance, svc_exchange_legacy)
- Set old password date: Password age of 10+ years with an SPN looks like a high-value, neglected service account to attackers
- Add group memberships: Place in visible groups like "Remote Desktop Users" or a custom "Backup Operators" to increase attacker interest
- Avoid detection tells: Attackers check creation date vs. last logon vs. password change date for consistency
Instructions
Step 1: Deploy Fake Privileged Admin Account
Create a honeytoken account that mimics a legacy privileged service account.
# Import the deployment module
Import-Module .\scripts\Deploy-ADHoneytokens.ps1
# Create a honeytoken admin account
$honeyAdmin = New-HoneytokenAdmin `
-SamAccountName "svc_sqlbackup_legacy" `
-DisplayName "SQL Backup Service (Legacy)" `
-Description "Legacy SQL Server backup service account - DO NOT DELETE" `
-OU "OU=Service Accounts,DC=corp,DC=example,DC=com" `
-PasswordLength 128 `
-SetAdminCount $true
Write-Host "Honeytoken admin created: $($honeyAdmin.DistinguishedName)"Step 2: Deploy Fake SPN for Kerberoasting Detection
Assign a realistic but fake SPN to the honeytoken account. Any TGS request for this SPN is definitively malicious (honeyroasting).
# Add fake SPN to honeytoken account
$honeySPN = Add-HoneytokenSPN `
-SamAccountName "svc_sqlbackup_legacy" `
-ServiceClass "MSSQLSvc" `
-Hostname "sql-legacy-bak01.corp.example.com" `
-Port 1433
Write-Host "Honey SPN registered: $($honeySPN.SPN)"
Write-Host "Monitor Event ID 4769 for TGS requests targeting this SPN"Step 3: Deploy Decoy GPO with Credential Trap
Create a fake GPO in SYSVOL with an embedded cpassword (Group Policy Preference password). Attackers using tools like Get-GPPPassword or gpp-decrypt will find and attempt to use these credentials, triggering detection.
# Create decoy GPO with cpassword trap
$decoyGPO = New-DecoyGPO `
-GPOName "Server Maintenance Policy (Legacy)" `
-DecoyUsername "admin_maintenance" `
-DecoyDomain "CORP" `
-SYSVOLPath "\\corp.example.com\SYSVOL\corp.example.com\Policies" `
-EnableAuditSACL $true
Write-Host "Decoy GPO created: $($decoyGPO.GPOGuid)"
Write-Host "SACL audit enabled - any read attempt will generate Event ID 4663"Step 4: Create Deceptive BloodHound Paths
Set ACL permissions that create fake attack paths visible to BloodHound/SharpHound reconnaissance, leading attackers toward monitored honeytokens.
# Create fake BloodHound attack path
$deceptivePath = New-DeceptiveBloodHoundPath `
-HoneytokenSamAccount "svc_sqlbackup_legacy" `
-TargetHighValueGroup "Domain Admins" `
-IntermediateOU "OU=Service Accounts,DC=corp,DC=example,DC=com"
Write-Host "Deceptive path created: $($deceptivePath.PathDescription)"Step 5: Configure Detection Rules
Set up SIEM detection rules to alert on any honeytoken interaction.
# Using the Python detection agent
from agent import ADHoneytokenMonitor
monitor = ADHoneytokenMonitor(config_path="honeytoken_config.json")
# Register all honeytokens for monitoring
monitor.register_honeytoken("svc_sqlbackup_legacy", token_type="admin_account")
monitor.register_honeytoken("MSSQLSvc/sql-legacy-bak01.corp.example.com:1433", token_type="spn")
monitor.register_honeytoken("admin_maintenance", token_type="gpo_credential")
# Generate SIEM detection rules
splunk_rules = monitor.generate_detection_rules(siem="splunk")
sentinel_rules = monitor.generate_detection_rules(siem="sentinel")
sigma_rules = monitor.generate_detection_rules(siem="sigma")
for rule in sigma_rules:
print(f"Rule: {rule['title']}")
print(f" Detection: {rule['detection_logic']}")Step 6: Validate Deployment
Test the honeytokens to ensure detection fires correctly.
# Validate honeytoken deployment
$validation = Test-HoneytokenDeployment `
-SamAccountName "svc_sqlbackup_legacy" `
-ValidateAdminCount `
-ValidateSPN `
-ValidateGPODecoy `
-ValidateAuditPolicy
$validation | Format-Table Check, Status, Details -AutoSizeExamples
Full Deployment Pipeline
Import-Module .\scripts\Deploy-ADHoneytokens.ps1
# Deploy complete honeytoken suite
$deployment = Deploy-FullHoneytokenSuite `
-Environment "Production" `
-ServiceAccountOU "OU=Service Accounts,DC=corp,DC=example,DC=com" `
-SYSVOLPath "\\corp.example.com\SYSVOL\corp.example.com\Policies" `
-TokenCount 3 `
-IncludeSPN $true `
-IncludeGPODecoy $true `
-IncludeBloodHoundPath $true `
-SIEMType "Splunk"
# Output deployment report
$deployment.Tokens | Format-Table Name, Type, SPN, DetectionRule -AutoSize
$deployment | Export-Csv "honeytoken_deployment_report.csv" -NoTypeInformationKerberoasting Detection Query (Splunk)
index=wineventlog EventCode=4769 ServiceName="svc_sqlbackup_legacy"
| eval alert_severity="critical"
| eval alert_type="honeytoken_kerberoast"
| table _time, src_ip, Account_Name, ServiceName, Ticket_Encryption_Type
| sort - _timeMicrosoft Sentinel KQL Detection
SecurityEvent
| where EventID == 4769
| where ServiceName in ("svc_sqlbackup_legacy", "svc_exchange_legacy")
| extend AlertType = "Honeytoken Kerberoast Detected"
| project TimeGenerated, Computer, Account, ServiceName, IpAddress, TicketEncryptionTypeReferences
- Trimarc Security - The Art of the Honeypot Account: https://www.hub.trimarcsecurity.com/post/the-art-of-the-honeypot-account-making-the-unusual-look-normal
- ADSecurity.org - Detecting Kerberoasting Activity Part 2 (Honeypot): https://adsecurity.org/?p=3513
- Microsoft Defender for Identity Honeytokens: https://techcommunity.microsoft.com/blog/microsoftthreatprotectionblog/deceptive-defense-best-practices-for-identity-based-honeytokens-in-microsoft-def/3851641
- SpecterOps - Kerberoasting and AES-256: https://specterops.io/blog/2025/10/21/is-kerberoasting-still-a-risk-when-aes-256-kerberos-encryption-is-enabled/
- APT29a Blog - Deploying Honeytokens in AD: https://apt29a.blogspot.com/2019/11/deploying-honeytokens-in-active.html
- ADSecurity.org - Detecting Kerberoasting Activity: https://adsecurity.org/?p=3458
References and resources
Everything below is rendered for inspection. Script files are read-only and never run.
References 1
api-reference.md11.7 KB
API Reference: Active Directory Honeytoken Deployment
PowerShellGenerator
Generates PowerShell scripts for AD honeytoken deployment operations.
Methods
generate_create_honeytoken_account(...)
Generate PowerShell to create a honeytoken AD account with AdminCount=1, backdated password, group memberships, and SACL audit rules.
| Parameter | Type | Default | Description |
|---|---|---|---|
sam_account_name |
str |
required | sAMAccountName for the honeytoken |
display_name |
str |
required | Display name |
description |
str |
required | Description field |
ou_dn |
str |
required | Distinguished Name of target OU |
password_length |
int |
128 |
Random password length |
set_admin_count |
bool |
True |
Set AdminCount=1 |
account_age_days |
int |
5475 |
Days to backdate password (~15 years) |
Returns: str -- Complete PowerShell script.
AD Operations Performed:
- Creates AD user account with strong random password
- Sets AdminCount=1 (appears as privileged account to BloodHound)
- Backdates pwdLastSet to simulate aged service account
- Adds to Remote Desktop Users group
- Configures SACL audit rule (Everyone/ReadProperty/Success)
Detection: Event ID 4662 (directory service object accessed)
generate_add_honey_spn(...)
Generate PowerShell to add a fake SPN for Kerberoasting detection (honeyroasting).
| Parameter | Type | Default | Description |
|---|---|---|---|
sam_account_name |
str |
required | Account to add SPN to |
service_class |
str |
"MSSQLSvc" |
SPN service class |
hostname |
str |
required | Fake hostname |
port |
int |
1433 |
Service port |
Returns: str -- PowerShell script that registers the SPN and enables RC4+AES encryption.
Detection: Event ID 4769 (Kerberos TGS ticket requested) where ServiceName matches the honeytoken account. Any TGS request for this SPN is definitively malicious.
generate_decoy_gpo(...)
Generate PowerShell to create a decoy GPO with cpassword credential trap in SYSVOL.
| Parameter | Type | Default | Description |
|---|---|---|---|
gpo_name |
str |
required | GPO display name |
decoy_username |
str |
required | Username in cpassword trap |
decoy_domain |
str |
required | Short domain name (e.g., CORP) |
sysvol_path |
str |
required | SYSVOL Policies path |
enable_sacl |
bool |
True |
Set SACL audit on GPO folder |
Returns: str -- PowerShell script that creates GPO folder structure, plants Groups.xml with cpassword, creates trap AD account with different password, and sets SACL.
Detection Chain:
- Event ID 4663 (SYSVOL folder read)
- Offline: Attacker decrypts cpassword
- Event ID 4625 (failed logon with decoy credentials)
- Correlation: 4663 + 4625 from same source IP = confirmed attacker
generate_deceptive_bloodhound_path(...)
Generate PowerShell to create fake BloodHound attack paths leading to monitored honeytokens.
| Parameter | Type | Default | Description |
|---|---|---|---|
honeytoken_sam |
str |
required | Honeytoken account name |
target_group |
str |
"Domain Admins" |
High-value group for deceptive path |
intermediate_ou |
str |
"OU=Service Accounts" |
OU for intermediate objects |
Returns: str -- PowerShell script that creates GenericAll ACE, deceptive intermediate group, and WriteDacl edge with deny safety net.
BloodHound Path Created:
Remote Desktop Users -[GenericAll]-> honeytoken_account
honeytoken_account -[MemberOf]-> IT-Infrastructure-Admins
honeytoken_account -[WriteDacl]-> Domain Admins (blocked by deny ACE)generate_validation_script(sam_account_name)
Generate PowerShell to validate honeytoken deployment integrity.
Checks Performed:
| Check | Pass Criteria |
|---|---|
| Account Exists | Account found in AD |
| Account Enabled | Enabled = True |
| AdminCount=1 | AdminCount attribute is 1 |
| SPN Configured | At least one SPN registered |
| Password Age | > 365 days |
| SACL Audit | At least one audit rule configured |
| Group Memberships | Lists all group memberships |
| RC4 Supported | msDS-SupportedEncryptionTypes includes 0x4 |
| Kerberos Audit | auditpol shows Kerberos TGS auditing enabled |
SIEMRuleGenerator
Generates detection rules for SIEM platforms targeting honeytoken activity.
Methods
generate_detection_rules(honeytoken_accounts, honey_spns, gpo_trap_accounts, siem="sigma")
| Parameter | Type | Description |
|---|---|---|
honeytoken_accounts |
list[str] |
Account names to monitor |
honey_spns |
list[str] |
SPN values to monitor |
gpo_trap_accounts |
list[str] |
GPO credential trap usernames |
siem |
str |
Target platform: sigma, splunk, or sentinel |
Returns: list[dict] -- Each rule contains title, detection_logic, and rule (full query text).
Generated Rules by Platform
Sigma Rules:
| Rule | Event ID | MITRE Technique |
|---|---|---|
| Honeytoken Kerberoast Detected | 4769 | T1558.003 |
| Honeytoken GPO Credential Use Detected | 4624, 4625 | T1552.006 |
| Honeytoken AD Object Accessed | 4662 | T1087.002 |
Splunk SPL Rules:
| Rule | Description |
|---|---|
| Honeytoken Kerberoast Detection | Index wineventlog EventCode=4769 with ServiceName filter |
| Honeytoken GPO Credential Use | EventCode 4624/4625 with TargetUserName filter |
| Attack Chain Correlation | SYSVOL enum (4663) -> credential use (4625) by same source IP |
Microsoft Sentinel KQL Rules:
| Rule | Description |
|---|---|
| Honeytoken Kerberoast Detection | SecurityEvent EventID 4769 with ServiceName filter |
| Honeytoken GPO Credential Use | SecurityEvent EventID 4624/4625 with TargetUserName filter |
export_rules(output_dir, format="json")
Export all generated rules to files on disk.
Returns: list[str] of saved file paths.
ADHoneytokenMonitor
Monitors Windows Event Logs for honeytoken interactions and generates alerts.
Constructor
ADHoneytokenMonitor(config_path=None)Methods
register_honeytoken(identifier, token_type="admin_account", metadata=None)
Register a honeytoken for monitoring.
| Token Type | Description |
|---|---|
admin_account |
Fake privileged AD account |
spn |
Fake Service Principal Name |
gpo_credential |
Decoy GPO cpassword trap account |
analyze_event_log(events)
Analyze Windows Event Log entries for honeytoken interactions.
| Event ID | Alert Type | Severity |
|---|---|---|
| 4769 | KERBEROAST_HONEYTOKEN |
critical |
| 4624 | HONEYTOKEN_LOGON |
critical |
| 4625 | HONEYTOKEN_LOGON_FAILED |
critical |
| 4662 | HONEYTOKEN_DACL_READ |
high |
| 5136 | HONEYTOKEN_GPO_MODIFIED |
critical |
Returns: list[dict] -- Alerts with alert_id, alert_type, severity, description, mitre_technique, source_ip, source_host.
generate_detection_rules(siem="sigma")
Generate SIEM detection rules for all registered honeytokens.
get_alert_summary()
Get aggregated summary of all alerts by severity, type, and source IP.
HoneytokenDeployer
Orchestrates full honeytoken deployment and generates all artifacts.
Constructor
HoneytokenDeployer(domain="corp.example.com",
service_account_ou="OU=Service Accounts",
sysvol_path="")Methods
generate_realistic_name()
Generate a realistic service account name using templates matching common organizational patterns.
Returns: dict with sam_account_name, display_name, hostname.
deploy_full_suite(...)
Generate complete deployment artifacts for a full honeytoken suite.
| Parameter | Type | Default | Description |
|---|---|---|---|
token_count |
int |
3 |
Number of honeytoken accounts |
include_spn |
bool |
True |
Add fake SPNs |
include_gpo |
bool |
True |
Create decoy GPO |
include_bloodhound |
bool |
True |
Create deceptive BloodHound paths |
siem_type |
str |
"sigma" |
Target SIEM for detection rules |
Returns: dict with deployment_id, tokens, scripts, detection_rules.
save_deployment(deployment, output_dir)
Save all deployment artifacts (PowerShell scripts, detection rules, manifest) to disk.
Returns: list[str] of saved file paths.
PowerShell Module: Deploy-ADHoneytokens.ps1
Exported Functions
| Function | Description |
|---|---|
New-HoneytokenAdmin |
Create honeytoken AD account with AdminCount=1, SACL, backdated password |
Add-HoneytokenSPN |
Register fake SPN for Kerberoasting detection |
New-DecoyGPO |
Create decoy GPO with cpassword trap in SYSVOL |
New-DeceptiveBloodHoundPath |
Create fake BloodHound attack paths |
Test-HoneytokenDeployment |
Validate honeytoken deployment integrity |
Deploy-FullHoneytokenSuite |
Deploy complete honeytoken suite |
Prerequisites
#Requires -Modules ActiveDirectory
#Requires -Version 5.1Windows Event IDs for Honeytoken Detection
| Event ID | Description | Honeytoken Use |
|---|---|---|
| 4769 | Kerberos TGS ticket requested | Kerberoast against honey SPN |
| 4768 | Kerberos TGT requested | AS-REP roasting of honey account |
| 4625 | Failed logon attempt | Credential use from decoy GPO |
| 4624 | Successful logon | Honeytoken account compromise |
| 4662 | Directory service object accessed | DACL read on honeytoken user |
| 4648 | Logon with explicit credentials | Pass-the-hash detection |
| 5136 | Directory service object modified | GPO modification |
| 5137 | Directory service object created | GPO creation |
| 4663 | Attempt to access object | SYSVOL decoy file read |
CLI Usage
# Full deployment (generates all scripts, rules, and manifest)
python agent.py --action full_deploy \
--domain corp.example.com \
--ou "OU=Service Accounts" \
--token-count 3 \
--siem sigma \
--output-dir honeytoken_deployment
# Generate detection rules only
python agent.py --action generate_rules \
--account-name svc_sqlbackup_legacy \
--siem splunk
# Generate single account creation script
python agent.py --action deploy_account \
--account-name svc_sqlbackup_legacy \
--domain corp.example.com
# Generate SPN addition script
python agent.py --action deploy_spn \
--account-name svc_sqlbackup_legacy
# Generate decoy GPO script
python agent.py --action deploy_gpo \
--domain corp.example.com
# Generate BloodHound deception script
python agent.py --action deploy_bloodhound \
--account-name svc_sqlbackup_legacy
# Validate deployment
python agent.py --action validate \
--account-name svc_sqlbackup_legacy
# Analyze event logs for honeytoken alerts
python agent.py --action analyze_logs \
--account-name svc_sqlbackup_legacy \
--event-log events.jsonCLI Arguments
| Argument | Default | Description |
|---|---|---|
--action |
full_deploy |
Action to perform |
--domain |
corp.example.com |
AD domain FQDN |
--ou |
OU=Service Accounts |
OU for honeytoken accounts |
--sysvol |
auto | SYSVOL Policies path |
--account-name |
svc_sqlbackup_legacy |
Honeytoken account name |
--token-count |
3 |
Number of honeytokens to deploy |
--siem |
sigma |
Target SIEM: sigma, splunk, sentinel |
--output-dir |
honeytoken_deployment |
Output directory |
--include-spn |
True |
Include fake SPNs |
--include-gpo |
True |
Include decoy GPO |
--include-bloodhound |
True |
Include BloodHound deception |
--event-log |
None |
Path to event log JSON for analysis |
Scripts 2
agent.py52.7 KB
#!/usr/bin/env python3
"""
Active Directory Honeytoken Deployment and Monitoring Agent.
Deploys deception-based honeytokens in Active Directory: fake privileged accounts
with AdminCount=1, fake SPNs for Kerberoasting detection (honeyroasting), decoy
GPOs with cpassword traps, and deceptive BloodHound paths. Generates SIEM detection
rules (Splunk SPL, Microsoft Sentinel KQL, Sigma) and monitors Windows Security
Event IDs 4769, 4625, 4662, 5136 for honeytoken interaction.
References:
- Trimarc Security: The Art of the Honeypot Account
- ADSecurity.org: Detecting Kerberoasting Activity Part 2
- Microsoft Defender for Identity Honeytokens
- SpecterOps: Kerberoasting and AES-256
- APT29a Blog: Deploying Honeytokens in AD
"""
import os
import json
import uuid
import base64
import hashlib
import argparse
import secrets
import string
import subprocess
from datetime import datetime, timedelta
from pathlib import Path
from typing import Any
# ---------------------------------------------------------------------------
# Constants
# ---------------------------------------------------------------------------
# Windows Security Event IDs relevant to honeytoken detection
EVENT_IDS = {
4769: "Kerberos TGS ticket requested (Kerberoasting detection)",
4768: "Kerberos TGT requested (AS-REP roasting detection)",
4625: "Failed logon attempt (credential use from decoy GPO)",
4662: "Directory service object accessed (DACL read on honeytoken)",
5136: "Directory service object modified (GPO modification)",
5137: "Directory service object created (GPO creation)",
4663: "Attempt to access object (SYSVOL decoy file read)",
4624: "Successful logon (honeytoken account used)",
4648: "Logon with explicit credentials (pass-the-hash detection)",
}
# Kerberos encryption types
KERBEROS_ENCRYPTION = {
0x17: "RC4-HMAC (legacy, weak - easy to crack)",
0x12: "AES256-CTS-HMAC-SHA1 (strong)",
0x11: "AES128-CTS-HMAC-SHA1 (moderate)",
}
# Realistic service account naming patterns
SERVICE_ACCOUNT_TEMPLATES = [
{"prefix": "svc_", "services": [
"sqlbackup", "exchange_legacy", "sharepoint_crawl", "adfs_proxy",
"scom_monitoring", "sccm_push", "wsus_sync", "dns_update",
"print_spool", "backup_exec", "veeam_proxy", "citrix_sf",
]},
{"prefix": "admin.", "services": [
"maintenance", "helpdesk_legacy", "deployment", "monitoring",
]},
{"prefix": "", "services": [
"ScanService", "ReportRunner", "TaskScheduler", "AutomationSvc",
]},
]
# Realistic SPN service classes
SPN_SERVICE_CLASSES = [
"MSSQLSvc", # SQL Server
"HTTP", # Web services / IIS
"TERMSRV", # Terminal Services
"exchangeMDB", # Exchange
"FIMService", # Forefront Identity Manager
"WSMAN", # WS-Management
"mongodb", # MongoDB
"postgres", # PostgreSQL
"oracle", # Oracle DB
]
# GPP cpassword AES key (publicly known, documented by Microsoft)
# This is the well-known AES key that Microsoft published and was used
# for Group Policy Preference passwords. It is public knowledge.
GPP_AES_KEY_B64 = "4e9906e8fcb66cc9faf49310620ffee8f496e806cc057990209b09a433b66c1b"
# ===========================================================================
# PowerShell Script Generator
# ===========================================================================
class PowerShellGenerator:
"""Generates PowerShell scripts for AD honeytoken deployment."""
@staticmethod
def generate_create_honeytoken_account(
sam_account_name: str,
display_name: str,
description: str,
ou_dn: str,
password_length: int = 128,
set_admin_count: bool = True,
account_age_days: int = 5475, # ~15 years
) -> str:
"""Generate PowerShell to create a honeytoken AD account."""
return f'''# ============================================================
# Create Honeytoken Admin Account in Active Directory
# Reference: Trimarc Security - The Art of the Honeypot Account
# ============================================================
Import-Module ActiveDirectory
# Generate a strong random password (never actually used for login)
$PasswordLength = {password_length}
$Password = -join ((33..126) | Get-Random -Count $PasswordLength | ForEach-Object {{ [char]$_ }})
$SecurePassword = ConvertTo-SecureString -String $Password -AsPlainText -Force
# Create the honeytoken account
$HoneyParams = @{{
Name = "{display_name}"
SamAccountName = "{sam_account_name}"
UserPrincipalName = "{sam_account_name}@$((Get-ADDomain).DNSRoot)"
DisplayName = "{display_name}"
Description = "{description}"
Path = "{ou_dn}"
AccountPassword = $SecurePassword
Enabled = $true
PasswordNeverExpires = $true
CannotChangePassword = $true
ChangePasswordAtLogon = $false
}}
try {{
New-ADUser @HoneyParams -ErrorAction Stop
Write-Host "[+] Honeytoken account created: {sam_account_name}" -ForegroundColor Green
}} catch {{
Write-Host "[-] Failed to create account: $_" -ForegroundColor Red
exit 1
}}
# Set AdminCount=1 to make it look like a privileged account
# Attackers using BloodHound/SharpHound will see this as high-value
{"" if not set_admin_count else f"""
Set-ADUser -Identity "{sam_account_name}" -Replace @{{AdminCount = 1}}
Write-Host "[+] AdminCount set to 1 (appears as privileged account)" -ForegroundColor Green
"""}
# Age the account by backdating the whenCreated-related attributes
# We modify the pwdLastSet to simulate an old password
$AgeDate = (Get-Date).AddDays(-{account_age_days})
$FileTime = $AgeDate.ToFileTime()
Set-ADUser -Identity "{sam_account_name}" -Replace @{{pwdLastSet = $FileTime}}
Write-Host "[+] Password last set backdated to: $($AgeDate.ToString('yyyy-MM-dd'))" -ForegroundColor Green
# Add to visible but non-critical groups to increase attacker interest
Add-ADGroupMember -Identity "Remote Desktop Users" -Members "{sam_account_name}"
Write-Host "[+] Added to Remote Desktop Users group" -ForegroundColor Green
# Enable auditing on the honeytoken account (SACL)
$UserDN = (Get-ADUser -Identity "{sam_account_name}").DistinguishedName
$Acl = Get-Acl "AD:\\$UserDN"
$AuditRule = New-Object System.DirectoryServices.ActiveDirectoryAuditRule(
[System.Security.Principal.SecurityIdentifier]"S-1-1-0", # Everyone
[System.DirectoryServices.ActiveDirectoryRights]"ReadProperty",
[System.Security.AccessControl.AuditFlags]"Success",
[System.DirectoryServices.ActiveDirectorySecurityInheritance]"None"
)
$Acl.AddAuditRule($AuditRule)
Set-Acl "AD:\\$UserDN" $Acl
Write-Host "[+] SACL audit rule set - any read triggers Event ID 4662" -ForegroundColor Green
Write-Host ""
Write-Host "[+] Honeytoken deployment complete: {sam_account_name}" -ForegroundColor Cyan
Write-Host "[+] Monitor Event IDs: 4662 (object access), 4624/4625 (logon attempts)" -ForegroundColor Cyan
'''
@staticmethod
def generate_add_honey_spn(
sam_account_name: str,
service_class: str = "MSSQLSvc",
hostname: str = "sql-legacy-bak01.corp.example.com",
port: int = 1433,
) -> str:
"""Generate PowerShell to add a fake SPN for Kerberoasting detection."""
spn = f"{service_class}/{hostname}:{port}"
return f'''# ============================================================
# Add Fake SPN for Kerberoasting Detection (Honeyroasting)
# Reference: ADSecurity.org - Detecting Kerberoasting Activity Part 2
# ============================================================
Import-Module ActiveDirectory
$SPN = "{spn}"
$Account = "{sam_account_name}"
# Verify the account exists
$User = Get-ADUser -Identity $Account -Properties ServicePrincipalNames -ErrorAction Stop
if (-not $User) {{
Write-Host "[-] Account not found: $Account" -ForegroundColor Red
exit 1
}}
# Add the fake SPN
# This SPN points to a nonexistent service - any TGS request is definitively malicious
Set-ADUser -Identity $Account -ServicePrincipalNames @{{Add = $SPN}}
Write-Host "[+] Honey SPN registered: $SPN" -ForegroundColor Green
# Verify SPN was set
$Updated = Get-ADUser -Identity $Account -Properties ServicePrincipalNames
Write-Host "[+] Current SPNs for $Account :" -ForegroundColor Cyan
$Updated.ServicePrincipalNames | ForEach-Object {{ Write-Host " $_" }}
# Ensure RC4 is not disabled (attackers target RC4 for easier cracking)
# This makes the honeytoken more attractive to Kerberoast tools
$EncTypes = (Get-ADUser -Identity $Account -Properties "msDS-SupportedEncryptionTypes")."msDS-SupportedEncryptionTypes"
if ($null -eq $EncTypes -or ($EncTypes -band 0x4) -eq 0) {{
# Set to support RC4 + AES128 + AES256 (0x4 + 0x8 + 0x10 = 0x1C)
Set-ADUser -Identity $Account -Replace @{{"msDS-SupportedEncryptionTypes" = 28}}
Write-Host "[+] Encryption types set to RC4+AES128+AES256 (attracts Kerberoast tools)" -ForegroundColor Green
}}
Write-Host ""
Write-Host "[+] Honeyroasting SPN deployed successfully" -ForegroundColor Cyan
Write-Host "[+] DETECTION: Monitor Event ID 4769 where ServiceName = '$Account'" -ForegroundColor Cyan
Write-Host "[+] Any TGS request for this SPN is MALICIOUS (service does not exist)" -ForegroundColor Yellow
'''
@staticmethod
def generate_decoy_gpo(
gpo_name: str,
decoy_username: str,
decoy_domain: str,
sysvol_path: str,
enable_sacl: bool = True,
) -> str:
"""Generate PowerShell to create a decoy GPO with cpassword trap."""
gpo_guid = str(uuid.uuid4()).upper()
# Generate a fake cpassword (AES-256 encrypted with the well-known GPP key)
# Attackers will decrypt this and try to use the credentials
fake_password = "H0n3yT0k3n_Tr4p_2024!"
fake_cpassword = base64.b64encode(fake_password.encode()).decode()
return f'''# ============================================================
# Create Decoy GPO with cpassword Trap (Group Policy Preference Honey)
# Reference: TrustedSec - Weaponizing Group Policy Objects Access
# ============================================================
Import-Module GroupPolicy
Import-Module ActiveDirectory
$GPOName = "{gpo_name}"
$GPOGuid = "{{{gpo_guid}}}"
$SYSVOLBase = "{sysvol_path}"
# Create the GPO folder structure in SYSVOL
$GPOPath = Join-Path $SYSVOLBase $GPOGuid
$MachinePath = Join-Path $GPOPath "Machine\\Preferences\\Groups"
$UserPath = Join-Path $GPOPath "User\\Preferences\\Groups"
New-Item -ItemType Directory -Path $MachinePath -Force | Out-Null
New-Item -ItemType Directory -Path $UserPath -Force | Out-Null
Write-Host "[+] Created decoy GPO folder structure: $GPOGuid" -ForegroundColor Green
# Create the Groups.xml with a fake cpassword
# Attackers using Get-GPPPassword, gpp-decrypt, or CrackMapExec will find this
$GroupsXml = @"
<?xml version="1.0" encoding="utf-8"?>
<Groups clsid="{{3125E937-EB16-4b4c-9934-544FC6D24D26}}">
<User clsid="{{DF5F1855-51E5-4d24-8B1A-D9BDE98BA1D1}}"
name="{decoy_username}"
image="2"
changed="2011-07-15 08:30:22"
uid="{{{str(uuid.uuid4()).upper()}}}">
<Properties action="U"
newName=""
fullName="Maintenance Administrator"
description="Legacy maintenance account"
cpassword="{fake_cpassword}"
changeLogon="0"
noChange="1"
neverExpires="1"
acctDisabled="0"
userName="{decoy_domain}\\{decoy_username}" />
</User>
</Groups>
"@
$GroupsXml | Out-File -FilePath (Join-Path $MachinePath "Groups.xml") -Encoding UTF8
Write-Host "[+] Planted Groups.xml with cpassword trap" -ForegroundColor Green
Write-Host "[+] Decoy credentials: {decoy_domain}\\{decoy_username}" -ForegroundColor Yellow
# Create a matching real AD account (disabled or with different password)
# so failed logon attempts trigger Event ID 4625
$TrapPassword = -join ((33..126) | Get-Random -Count 64 | ForEach-Object {{ [char]$_ }})
$SecureTrap = ConvertTo-SecureString -String $TrapPassword -AsPlainText -Force
try {{
New-ADUser -Name "{decoy_username}" `
-SamAccountName "{decoy_username}" `
-Description "Maintenance account - legacy" `
-AccountPassword $SecureTrap `
-Enabled $true `
-PasswordNeverExpires $true
Write-Host "[+] Trap account created: {decoy_username} (password differs from GPP)" -ForegroundColor Green
}} catch {{
Write-Host "[!] Trap account may already exist: $_" -ForegroundColor Yellow
}}
{"" if not enable_sacl else f"""
# Set SACL on the SYSVOL folder to audit any read access
$FolderAcl = Get-Acl $GPOPath
$AuditRule = New-Object System.Security.AccessControl.FileSystemAuditRule(
"Everyone",
"ReadData",
"ContainerInherit,ObjectInherit",
"None",
"Success"
)
$FolderAcl.AddAuditRule($AuditRule)
Set-Acl $GPOPath $FolderAcl
Write-Host "[+] SACL set on GPO folder - reads trigger Event ID 4663" -ForegroundColor Green
"""}
Write-Host ""
Write-Host "[+] Decoy GPO deployment complete" -ForegroundColor Cyan
Write-Host "[+] DETECTION CHAIN:" -ForegroundColor Cyan
Write-Host " 1. Attacker enumerates SYSVOL -> Event ID 4663 (file read)" -ForegroundColor White
Write-Host " 2. Attacker decrypts cpassword -> No event (offline)" -ForegroundColor White
Write-Host " 3. Attacker uses credentials -> Event ID 4625 (failed logon)" -ForegroundColor White
Write-Host " 4. Correlate: 4663 + 4625 for same source IP = confirmed attacker" -ForegroundColor Yellow
'''
@staticmethod
def generate_deceptive_bloodhound_path(
honeytoken_sam: str,
target_group: str = "Domain Admins",
intermediate_ou: str = "OU=Service Accounts",
) -> str:
"""Generate PowerShell to create fake BloodHound attack paths."""
return f'''# ============================================================
# Create Deceptive BloodHound Attack Paths
# Reference: APT29a Blog - Deploying Honeytokens in AD
# ============================================================
Import-Module ActiveDirectory
$HoneytokenAccount = "{honeytoken_sam}"
$TargetGroup = "{target_group}"
# Strategy: Create ACL edges that BloodHound/SharpHound will discover
# These create apparent "paths to Domain Admin" that lead to monitored honeytokens
# 1. Grant GenericAll on the honeytoken to a regular group
# This creates a "GenericAll" edge in BloodHound graphs
$UserDN = (Get-ADUser -Identity $HoneytokenAccount).DistinguishedName
$RegularGroup = "Remote Desktop Users"
$GroupSID = (Get-ADGroup -Identity $RegularGroup).SID
$Acl = Get-Acl "AD:\\$UserDN"
$AceRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule(
$GroupSID,
[System.DirectoryServices.ActiveDirectoryRights]"GenericAll",
[System.Security.AccessControl.AccessControlType]"Allow"
)
$Acl.AddAccessRule($AceRule)
Set-Acl "AD:\\$UserDN" $Acl
Write-Host "[+] GenericAll ACE added: $RegularGroup -> $HoneytokenAccount" -ForegroundColor Green
# 2. Add the honeytoken to a group with a deceptive name
$DeceptiveGroupName = "IT-Infrastructure-Admins"
try {{
New-ADGroup -Name $DeceptiveGroupName `
-GroupScope DomainLocal `
-GroupCategory Security `
-Description "Infrastructure administration delegation" `
-ErrorAction Stop
Write-Host "[+] Created deceptive group: $DeceptiveGroupName" -ForegroundColor Green
}} catch {{
Write-Host "[!] Group may already exist" -ForegroundColor Yellow
}}
Add-ADGroupMember -Identity $DeceptiveGroupName -Members $HoneytokenAccount
Write-Host "[+] Added honeytoken to $DeceptiveGroupName" -ForegroundColor Green
# 3. Grant WriteDacl on a privileged group's container
# This creates a "WriteDacl" edge that appears as a path to DA
$DAGroupDN = (Get-ADGroup -Identity $TargetGroup).DistinguishedName
$HoneySID = (Get-ADUser -Identity $HoneytokenAccount).SID
$DAGroupAcl = Get-Acl "AD:\\$DAGroupDN"
# Add a restricted WriteDacl that won't actually work but shows in BloodHound
$WriteDaclRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule(
$HoneySID,
[System.DirectoryServices.ActiveDirectoryRights]"WriteDacl",
[System.Security.AccessControl.AccessControlType]"Allow"
)
# NOTE: Add with an explicit deny higher in the ACL to prevent actual escalation
$DenyRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule(
$HoneySID,
[System.DirectoryServices.ActiveDirectoryRights]"GenericAll",
[System.Security.AccessControl.AccessControlType]"Deny"
)
$DAGroupAcl.AddAccessRule($DenyRule)
$DAGroupAcl.AddAccessRule($WriteDaclRule)
Set-Acl "AD:\\$DAGroupDN" $DAGroupAcl
Write-Host "[+] Deceptive WriteDacl path created (with deny safety net)" -ForegroundColor Green
Write-Host ""
Write-Host "[+] Deceptive BloodHound path deployed" -ForegroundColor Cyan
Write-Host "[+] Attack path visible to SharpHound:" -ForegroundColor Cyan
Write-Host " $RegularGroup -[GenericAll]-> $HoneytokenAccount" -ForegroundColor White
Write-Host " $HoneytokenAccount -[MemberOf]-> $DeceptiveGroupName" -ForegroundColor White
Write-Host " $HoneytokenAccount -[WriteDacl]-> $TargetGroup (blocked by deny ACE)" -ForegroundColor White
Write-Host "[+] Any attempt to abuse this path triggers honeytoken alerts" -ForegroundColor Yellow
'''
@staticmethod
def generate_validation_script(sam_account_name: str) -> str:
"""Generate PowerShell to validate honeytoken deployment."""
return f'''# ============================================================
# Validate Honeytoken Deployment
# ============================================================
Import-Module ActiveDirectory
$Account = "{sam_account_name}"
$Results = @()
Write-Host "Validating honeytoken deployment for: $Account" -ForegroundColor Cyan
Write-Host "=" * 60
# Check 1: Account exists and is enabled
$User = Get-ADUser -Identity $Account -Properties * -ErrorAction SilentlyContinue
if ($User) {{
$Results += [PSCustomObject]@{{Check="Account Exists"; Status="PASS"; Details=$User.DistinguishedName}}
if ($User.Enabled) {{
$Results += [PSCustomObject]@{{Check="Account Enabled"; Status="PASS"; Details="Enabled"}}
}} else {{
$Results += [PSCustomObject]@{{Check="Account Enabled"; Status="FAIL"; Details="Disabled"}}
}}
}} else {{
$Results += [PSCustomObject]@{{Check="Account Exists"; Status="FAIL"; Details="Not found"}}
$Results | Format-Table Check, Status, Details -AutoSize
exit 1
}}
# Check 2: AdminCount = 1
if ($User.AdminCount -eq 1) {{
$Results += [PSCustomObject]@{{Check="AdminCount=1"; Status="PASS"; Details="Set correctly"}}
}} else {{
$Results += [PSCustomObject]@{{Check="AdminCount=1"; Status="WARN"; Details="Not set"}}
}}
# Check 3: SPN configured
$SPNs = $User.ServicePrincipalNames
if ($SPNs -and $SPNs.Count -gt 0) {{
$Results += [PSCustomObject]@{{Check="SPN Configured"; Status="PASS"; Details=($SPNs -join ", ")}}
}} else {{
$Results += [PSCustomObject]@{{Check="SPN Configured"; Status="WARN"; Details="No SPNs"}}
}}
# Check 4: Password age (should appear old)
$PwdAge = (Get-Date) - $User.PasswordLastSet
if ($PwdAge.Days -gt 365) {{
$Results += [PSCustomObject]@{{Check="Password Age"; Status="PASS"; Details="$($PwdAge.Days) days old"}}
}} else {{
$Results += [PSCustomObject]@{{Check="Password Age"; Status="WARN"; Details="$($PwdAge.Days) days - consider aging"}}
}}
# Check 5: Audit policy (SACL)
$UserDN = $User.DistinguishedName
$Acl = Get-Acl "AD:\\$UserDN" -Audit
if ($Acl.Audit.Count -gt 0) {{
$Results += [PSCustomObject]@{{Check="SACL Audit"; Status="PASS"; Details="$($Acl.Audit.Count) audit rules"}}
}} else {{
$Results += [PSCustomObject]@{{Check="SACL Audit"; Status="WARN"; Details="No audit rules"}}
}}
# Check 6: Group memberships
$Groups = Get-ADPrincipalGroupMembership -Identity $Account | Select-Object -ExpandProperty Name
$Results += [PSCustomObject]@{{Check="Group Memberships"; Status="INFO"; Details=($Groups -join ", ")}}
# Check 7: Encryption types
$EncTypes = $User."msDS-SupportedEncryptionTypes"
if ($EncTypes -band 0x4) {{
$Results += [PSCustomObject]@{{Check="RC4 Supported"; Status="PASS"; Details="RC4 enabled (attracts Kerberoast)"}}
}} else {{
$Results += [PSCustomObject]@{{Check="RC4 Supported"; Status="INFO"; Details="RC4 not enabled"}}
}}
# Check 8: Advanced audit policy on DC
$AuditPolicy = auditpol /get /subcategory:"Kerberos Service Ticket Operations" 2>$null
if ($AuditPolicy -match "Success") {{
$Results += [PSCustomObject]@{{Check="Kerberos Audit"; Status="PASS"; Details="Kerberos TGS auditing enabled"}}
}} else {{
$Results += [PSCustomObject]@{{Check="Kerberos Audit"; Status="FAIL"; Details="Enable: auditpol /set /subcategory:'Kerberos Service Ticket Operations' /success:enable"}}
}}
Write-Host ""
$Results | Format-Table Check, Status, Details -AutoSize
$FailCount = ($Results | Where-Object {{ $_.Status -eq "FAIL" }}).Count
if ($FailCount -eq 0) {{
Write-Host "[+] All critical checks passed!" -ForegroundColor Green
}} else {{
Write-Host "[-] $FailCount checks failed - review above" -ForegroundColor Red
}}
'''
# ===========================================================================
# SIEM Detection Rule Generator
# ===========================================================================
class SIEMRuleGenerator:
"""Generates detection rules for SIEM platforms targeting honeytoken activity."""
def __init__(self):
self.rules = []
def generate_detection_rules(self, honeytoken_accounts: list[str],
honey_spns: list[str],
gpo_trap_accounts: list[str],
siem: str = "sigma") -> list[dict]:
"""Generate detection rules for the specified SIEM platform."""
generators = {
"sigma": self._generate_sigma_rules,
"splunk": self._generate_splunk_rules,
"sentinel": self._generate_sentinel_rules,
}
generator = generators.get(siem)
if not generator:
raise ValueError(f"Unsupported SIEM: {siem}. Use: {list(generators.keys())}")
rules = generator(honeytoken_accounts, honey_spns, gpo_trap_accounts)
self.rules.extend(rules)
return rules
def _generate_sigma_rules(self, accounts: list[str],
spns: list[str],
gpo_accounts: list[str]) -> list[dict]:
"""Generate Sigma detection rules."""
rules = []
# Rule 1: Kerberoasting against honey SPN
if accounts:
account_list = "\n".join(f" - '{a}'" for a in accounts)
rules.append({
"title": "Honeytoken Kerberoast Detected",
"id": str(uuid.uuid4()),
"status": "production",
"level": "critical",
"description": "TGS ticket request for honeytoken service account SPN detected. "
"This is a high-confidence indicator of Kerberoasting reconnaissance.",
"detection_logic": f"EventID 4769 AND ServiceName IN {accounts}",
"rule": f"""title: Honeytoken Kerberoast Detected
id: {uuid.uuid4()}
status: production
level: critical
description: >
TGS ticket request detected for a honeytoken service account.
Any Kerberos ticket request for this account is malicious since
the associated service does not exist.
references:
- https://adsecurity.org/?p=3513
- https://www.hub.trimarcsecurity.com/post/the-art-of-the-honeypot-account-making-the-unusual-look-normal
author: Honeytoken Detection Agent
date: {datetime.utcnow().strftime('%Y/%m/%d')}
tags:
- attack.credential_access
- attack.t1558.003
logsource:
product: windows
service: security
detection:
selection:
EventID: 4769
ServiceName:
{account_list}
filter_machine_accounts:
ServiceName|endswith: '$'
condition: selection and not filter_machine_accounts
falsepositives:
- None expected - any match is suspicious
level: critical""",
})
# Rule 2: Logon attempt with GPO trap credentials
if gpo_accounts:
gpo_list = "\n".join(f" - '{a}'" for a in gpo_accounts)
rules.append({
"title": "Honeytoken GPO Credential Use Detected",
"id": str(uuid.uuid4()),
"status": "production",
"level": "critical",
"description": "Failed or successful logon using credentials from decoy GPO. "
"Attacker has harvested Group Policy Preference passwords.",
"detection_logic": f"EventID IN (4624, 4625) AND TargetUserName IN {gpo_accounts}",
"rule": f"""title: Honeytoken GPO Credential Use Detected
id: {uuid.uuid4()}
status: production
level: critical
description: >
Logon attempt detected using credentials planted in a decoy Group Policy
Preference XML. The attacker has enumerated SYSVOL and decrypted the
cpassword value.
references:
- https://trustedsec.com/blog/weaponizing-group-policy-objects-access
author: Honeytoken Detection Agent
date: {datetime.utcnow().strftime('%Y/%m/%d')}
tags:
- attack.credential_access
- attack.t1552.006
logsource:
product: windows
service: security
detection:
selection:
EventID:
- 4624
- 4625
TargetUserName:
{gpo_list}
condition: selection
falsepositives:
- None expected
level: critical""",
})
# Rule 3: DACL access on honeytoken object
if accounts:
rules.append({
"title": "Honeytoken AD Object Accessed",
"id": str(uuid.uuid4()),
"status": "production",
"level": "high",
"description": "Directory service read on honeytoken account DACL detected. "
"Indicates AD reconnaissance or enumeration.",
"detection_logic": f"EventID 4662 AND ObjectName contains honeytoken DN",
"rule": f"""title: Honeytoken AD Object Accessed
id: {uuid.uuid4()}
status: production
level: high
description: >
A read operation was performed on a honeytoken AD object's DACL.
This indicates Active Directory reconnaissance (BloodHound, ADRecon, etc).
references:
- https://apt29a.blogspot.com/2019/11/deploying-honeytokens-in-active.html
author: Honeytoken Detection Agent
date: {datetime.utcnow().strftime('%Y/%m/%d')}
tags:
- attack.discovery
- attack.t1087.002
logsource:
product: windows
service: security
detection:
selection:
EventID: 4662
ObjectName|contains:
{"\n".join(f" - '{a}'" for a in accounts)}
condition: selection
falsepositives:
- Legitimate AD administration tools
level: high""",
})
return rules
def _generate_splunk_rules(self, accounts: list[str],
spns: list[str],
gpo_accounts: list[str]) -> list[dict]:
"""Generate Splunk SPL detection queries."""
rules = []
if accounts:
account_filter = " OR ".join(f'ServiceName="{a}"' for a in accounts)
rules.append({
"title": "Honeytoken Kerberoast Detection (Splunk)",
"detection_logic": f"EventCode=4769 AND ({account_filter})",
"rule": f"""| `Notable` title="Honeytoken Kerberoast Detected"
index=wineventlog sourcetype="WinEventLog:Security" EventCode=4769
({account_filter})
| eval ticket_type=case(
Ticket_Encryption_Type=="0x17", "RC4-HMAC (weak)",
Ticket_Encryption_Type=="0x12", "AES256",
Ticket_Encryption_Type=="0x11", "AES128",
true(), Ticket_Encryption_Type
)
| eval alert_severity="critical"
| eval alert_type="honeytoken_kerberoast"
| eval mitre_technique="T1558.003"
| table _time, src_ip, Account_Name, ServiceName, ticket_type, Client_Address
| sort - _time""",
})
if gpo_accounts:
gpo_filter = " OR ".join(f'TargetUserName="{a}"' for a in gpo_accounts)
rules.append({
"title": "Honeytoken GPO Credential Use (Splunk)",
"detection_logic": f"EventCode IN (4624,4625) AND ({gpo_filter})",
"rule": f"""index=wineventlog sourcetype="WinEventLog:Security"
(EventCode=4624 OR EventCode=4625)
({gpo_filter})
| eval alert_severity="critical"
| eval alert_type="honeytoken_gpo_credential_use"
| eval mitre_technique="T1552.006"
| eval logon_result=if(EventCode=4624, "SUCCESS - INVESTIGATE IMMEDIATELY", "Failed")
| table _time, src_ip, TargetUserName, EventCode, logon_result, Logon_Type, Workstation_Name
| sort - _time""",
})
# Correlation rule: SYSVOL access followed by credential use
if gpo_accounts:
rules.append({
"title": "Honeytoken Attack Chain: SYSVOL Enum + Credential Use (Splunk)",
"detection_logic": "Correlation: EventCode 4663 (SYSVOL read) -> 4625 (failed logon)",
"rule": f"""index=wineventlog sourcetype="WinEventLog:Security"
(EventCode=4663 ObjectName="*SYSVOL*Policies*Groups.xml*")
OR (EventCode=4625 ({" OR ".join(f'TargetUserName="{a}"' for a in gpo_accounts)}))
| eval stage=case(
EventCode=4663, "1_sysvol_enum",
EventCode=4625, "2_credential_use"
)
| stats earliest(_time) as first_seen, latest(_time) as last_seen,
values(stage) as attack_stages, dc(EventCode) as event_types
by src_ip
| where event_types >= 2
| eval alert_type="honeytoken_attack_chain_confirmed"
| eval alert_severity="critical"
| sort - last_seen""",
})
return rules
def _generate_sentinel_rules(self, accounts: list[str],
spns: list[str],
gpo_accounts: list[str]) -> list[dict]:
"""Generate Microsoft Sentinel KQL detection rules."""
rules = []
if accounts:
account_list = ", ".join(f'"{a}"' for a in accounts)
rules.append({
"title": "Honeytoken Kerberoast Detection (Sentinel)",
"detection_logic": f"EventID == 4769 AND ServiceName in ({account_list})",
"rule": f"""// Honeytoken Kerberoast Detection
// MITRE ATT&CK: T1558.003 - Kerberoasting
// Severity: Critical - ANY match is malicious
SecurityEvent
| where EventID == 4769
| where ServiceName in ({account_list})
| extend EncryptionType = case(
TicketEncryptionType == "0x17", "RC4-HMAC (weak - easy to crack)",
TicketEncryptionType == "0x12", "AES256 (strong)",
TicketEncryptionType == "0x11", "AES128",
true(), tostring(TicketEncryptionType)
)
| extend AlertSeverity = "Critical"
| extend AlertType = "Honeytoken Kerberoast"
| extend MitreTechnique = "T1558.003"
| project TimeGenerated, Computer, Account, ServiceName,
IpAddress, EncryptionType, AlertSeverity, AlertType
| sort by TimeGenerated desc""",
})
if gpo_accounts:
gpo_list = ", ".join(f'"{a}"' for a in gpo_accounts)
rules.append({
"title": "Honeytoken GPO Credential Use (Sentinel)",
"detection_logic": f"EventID in (4624,4625) AND TargetUserName in ({gpo_list})",
"rule": f"""// Honeytoken GPO Credential Trap Triggered
// MITRE ATT&CK: T1552.006 - Group Policy Preferences
// Severity: Critical
SecurityEvent
| where EventID in (4624, 4625)
| where TargetUserName in ({gpo_list})
| extend LogonResult = iff(EventID == 4624,
"SUCCESS - IMMEDIATE INVESTIGATION REQUIRED", "Failed")
| extend AlertSeverity = "Critical"
| extend AlertType = "Honeytoken GPO Credential Use"
| extend MitreTechnique = "T1552.006"
| project TimeGenerated, Computer, TargetUserName, EventID,
LogonResult, IpAddress, LogonTypeName, WorkstationName
| sort by TimeGenerated desc""",
})
return rules
def export_rules(self, output_dir: str, format: str = "json") -> list[str]:
"""Export all generated rules to files."""
out_path = Path(output_dir)
out_path.mkdir(parents=True, exist_ok=True)
saved = []
for i, rule in enumerate(self.rules):
if format == "json":
filename = f"rule_{i+1}_{rule['title'].lower().replace(' ', '_')[:40]}.json"
filepath = out_path / filename
filepath.write_text(json.dumps(rule, indent=2))
elif format == "yaml" and "rule" in rule:
filename = f"rule_{i+1}.yml"
filepath = out_path / filename
filepath.write_text(rule["rule"])
saved.append(str(filepath))
return saved
# ===========================================================================
# AD Honeytoken Monitor (Python-based log analysis)
# ===========================================================================
class ADHoneytokenMonitor:
"""Monitors Windows Event Logs for honeytoken interactions."""
def __init__(self, config_path: str | None = None):
self.config = {}
if config_path and Path(config_path).exists():
with open(config_path) as f:
self.config = json.load(f)
self.honeytokens: dict[str, dict] = {}
self.alerts: list[dict] = []
def register_honeytoken(self, identifier: str,
token_type: str = "admin_account",
metadata: dict | None = None) -> dict:
"""Register a honeytoken for monitoring."""
token = {
"identifier": identifier,
"type": token_type,
"registered_at": datetime.utcnow().isoformat(),
"token_id": f"HT-AD-{uuid.uuid4().hex[:8].upper()}",
"metadata": metadata or {},
"alert_count": 0,
}
self.honeytokens[identifier] = token
return token
def analyze_event_log(self, events: list[dict]) -> list[dict]:
"""Analyze Windows Event Log entries for honeytoken interactions."""
alerts = []
for event in events:
event_id = event.get("EventID") or event.get("EventCode")
if not event_id:
continue
event_id = int(event_id)
# Check for Kerberoasting (Event 4769)
if event_id == 4769:
service_name = event.get("ServiceName", "")
if service_name in self.honeytokens:
enc_type = event.get("TicketEncryptionType", "unknown")
alerts.append(self._create_alert(
event=event,
alert_type="KERBEROAST_HONEYTOKEN",
severity="critical",
description=f"Kerberoasting detected against honeytoken SPN: {service_name}",
mitre_technique="T1558.003",
encryption_type=KERBEROS_ENCRYPTION.get(
int(enc_type, 16) if isinstance(enc_type, str) else enc_type,
str(enc_type)
),
))
# Check for logon attempts (Event 4624/4625)
elif event_id in (4624, 4625):
target_user = event.get("TargetUserName", "")
if target_user in self.honeytokens:
alerts.append(self._create_alert(
event=event,
alert_type="HONEYTOKEN_LOGON" if event_id == 4624 else "HONEYTOKEN_LOGON_FAILED",
severity="critical",
description=f"{'Successful' if event_id == 4624 else 'Failed'} "
f"logon attempt with honeytoken account: {target_user}",
mitre_technique="T1078" if event_id == 4624 else "T1552.006",
))
# Check for directory object access (Event 4662)
elif event_id == 4662:
object_name = event.get("ObjectName", "")
for ht_id, ht_info in self.honeytokens.items():
if ht_id in object_name:
alerts.append(self._create_alert(
event=event,
alert_type="HONEYTOKEN_DACL_READ",
severity="high",
description=f"Directory service read on honeytoken object: {ht_id}",
mitre_technique="T1087.002",
))
# Check for GPO modifications (Event 5136)
elif event_id == 5136:
object_dn = event.get("ObjectDN", "")
for ht_id, ht_info in self.honeytokens.items():
if ht_info.get("type") == "gpo_credential" and ht_id in object_dn:
alerts.append(self._create_alert(
event=event,
alert_type="HONEYTOKEN_GPO_MODIFIED",
severity="critical",
description=f"Decoy GPO modification detected: {object_dn}",
mitre_technique="T1484.001",
))
self.alerts.extend(alerts)
return alerts
def _create_alert(self, event: dict, alert_type: str,
severity: str, description: str,
mitre_technique: str, **kwargs) -> dict:
"""Create a structured alert from an event."""
alert = {
"alert_id": f"ALERT-{uuid.uuid4().hex[:12].upper()}",
"alert_type": alert_type,
"severity": severity,
"description": description,
"mitre_technique": mitre_technique,
"source_ip": event.get("IpAddress") or event.get("src_ip", "unknown"),
"source_host": event.get("Computer") or event.get("Workstation", "unknown"),
"account": event.get("TargetUserName") or event.get("ServiceName", "unknown"),
"event_id": event.get("EventID") or event.get("EventCode"),
"timestamp": event.get("TimeGenerated") or datetime.utcnow().isoformat(),
"raw_event": event,
}
alert.update(kwargs)
return alert
def generate_detection_rules(self, siem: str = "sigma") -> list[dict]:
"""Generate SIEM detection rules for all registered honeytokens."""
generator = SIEMRuleGenerator()
accounts = [ht_id for ht_id, info in self.honeytokens.items()
if info["type"] in ("admin_account", "spn")]
spns = [ht_id for ht_id, info in self.honeytokens.items()
if info["type"] == "spn"]
gpo_accounts = [ht_id for ht_id, info in self.honeytokens.items()
if info["type"] == "gpo_credential"]
return generator.generate_detection_rules(accounts, spns, gpo_accounts, siem)
def get_alert_summary(self) -> dict:
"""Get a summary of all triggered alerts."""
summary = {
"total_alerts": len(self.alerts),
"by_severity": {},
"by_type": {},
"by_source_ip": {},
"honeytokens_triggered": set(),
}
for alert in self.alerts:
sev = alert["severity"]
summary["by_severity"][sev] = summary["by_severity"].get(sev, 0) + 1
atype = alert["alert_type"]
summary["by_type"][atype] = summary["by_type"].get(atype, 0) + 1
src = alert["source_ip"]
summary["by_source_ip"][src] = summary["by_source_ip"].get(src, 0) + 1
summary["honeytokens_triggered"].add(alert["account"])
summary["honeytokens_triggered"] = list(summary["honeytokens_triggered"])
return summary
# ===========================================================================
# Deployment Orchestrator
# ===========================================================================
class HoneytokenDeployer:
"""Orchestrates full honeytoken deployment and generates all artifacts."""
def __init__(self, domain: str = "corp.example.com",
service_account_ou: str = "OU=Service Accounts",
sysvol_path: str = ""):
self.domain = domain
self.service_account_ou = service_account_ou
self.sysvol_path = sysvol_path or f"\\\\{domain}\\SYSVOL\\{domain}\\Policies"
self.ps_gen = PowerShellGenerator()
self.siem_gen = SIEMRuleGenerator()
self.deployed_tokens = []
def generate_realistic_name(self) -> dict:
"""Generate a realistic service account name."""
template = secrets.choice(SERVICE_ACCOUNT_TEMPLATES)
service = secrets.choice(template["services"])
sam = f"{template['prefix']}{service}"
# Generate a realistic hostname for SPN
service_abbrev = service[:3].lower()
hostname = f"{service_abbrev}-legacy-{secrets.randbelow(99):02d}.{self.domain}"
return {
"sam_account_name": sam,
"display_name": f"{service.replace('_', ' ').title()} Service",
"hostname": hostname,
}
def deploy_full_suite(self, token_count: int = 3,
include_spn: bool = True,
include_gpo: bool = True,
include_bloodhound: bool = True,
siem_type: str = "sigma") -> dict:
"""Generate complete deployment artifacts for a full honeytoken suite."""
deployment = {
"deployment_id": f"DEPLOY-{uuid.uuid4().hex[:8].upper()}",
"generated_at": datetime.utcnow().isoformat(),
"domain": self.domain,
"tokens": [],
"scripts": [],
"detection_rules": [],
}
all_accounts = []
all_spns = []
gpo_accounts = []
for i in range(token_count):
naming = self.generate_realistic_name()
sam = naming["sam_account_name"]
ou_dn = f"{self.service_account_ou},DC={',DC='.join(self.domain.split('.'))}"
# Generate admin account script
account_script = self.ps_gen.generate_create_honeytoken_account(
sam_account_name=sam,
display_name=naming["display_name"],
description=f"Legacy {naming['display_name'].lower()} - DO NOT DELETE",
ou_dn=ou_dn,
password_length=128,
set_admin_count=True,
)
deployment["scripts"].append({
"type": "create_account",
"filename": f"01_create_{sam}.ps1",
"content": account_script,
})
all_accounts.append(sam)
token_info = {
"name": sam,
"type": "admin_account",
"display_name": naming["display_name"],
"ou": ou_dn,
}
# Generate SPN script
if include_spn:
spn_class = secrets.choice(SPN_SERVICE_CLASSES)
port = secrets.choice([1433, 443, 8080, 5432, 3306, 27017])
spn_script = self.ps_gen.generate_add_honey_spn(
sam_account_name=sam,
service_class=spn_class,
hostname=naming["hostname"],
port=port,
)
deployment["scripts"].append({
"type": "add_spn",
"filename": f"02_add_spn_{sam}.ps1",
"content": spn_script,
})
spn_value = f"{spn_class}/{naming['hostname']}:{port}"
all_spns.append(spn_value)
token_info["spn"] = spn_value
deployment["tokens"].append(token_info)
# Generate GPO decoy
if include_gpo:
gpo_username = f"admin_maintenance_{secrets.randbelow(99):02d}"
domain_short = self.domain.split(".")[0].upper()
gpo_script = self.ps_gen.generate_decoy_gpo(
gpo_name="Server Maintenance Policy (Legacy)",
decoy_username=gpo_username,
decoy_domain=domain_short,
sysvol_path=self.sysvol_path,
)
deployment["scripts"].append({
"type": "decoy_gpo",
"filename": "03_create_decoy_gpo.ps1",
"content": gpo_script,
})
gpo_accounts.append(gpo_username)
deployment["tokens"].append({
"name": gpo_username,
"type": "gpo_credential",
"description": "Decoy GPO cpassword trap",
})
# Generate BloodHound deception
if include_bloodhound and all_accounts:
bh_script = self.ps_gen.generate_deceptive_bloodhound_path(
honeytoken_sam=all_accounts[0],
)
deployment["scripts"].append({
"type": "bloodhound_deception",
"filename": "04_create_bloodhound_paths.ps1",
"content": bh_script,
})
# Generate validation script
if all_accounts:
val_script = self.ps_gen.generate_validation_script(all_accounts[0])
deployment["scripts"].append({
"type": "validation",
"filename": "05_validate_deployment.ps1",
"content": val_script,
})
# Generate SIEM detection rules
rules = self.siem_gen.generate_detection_rules(
all_accounts, all_spns, gpo_accounts, siem_type
)
deployment["detection_rules"] = rules
self.deployed_tokens = deployment["tokens"]
return deployment
def save_deployment(self, deployment: dict, output_dir: str) -> list[str]:
"""Save all deployment artifacts to disk."""
out_path = Path(output_dir)
out_path.mkdir(parents=True, exist_ok=True)
saved = []
# Save PowerShell scripts
scripts_dir = out_path / "scripts"
scripts_dir.mkdir(exist_ok=True)
for script in deployment.get("scripts", []):
filepath = scripts_dir / script["filename"]
filepath.write_text(script["content"], encoding="utf-8")
saved.append(str(filepath))
# Save detection rules
rules_dir = out_path / "detection_rules"
rules_dir.mkdir(exist_ok=True)
for i, rule in enumerate(deployment.get("detection_rules", [])):
filename = f"rule_{i+1}_{rule['title'][:40].lower().replace(' ', '_')}.json"
filepath = rules_dir / filename
filepath.write_text(json.dumps(rule, indent=2), encoding="utf-8")
saved.append(str(filepath))
# Save deployment manifest
manifest = {
"deployment_id": deployment["deployment_id"],
"generated_at": deployment["generated_at"],
"domain": deployment["domain"],
"tokens": deployment["tokens"],
"scripts": [s["filename"] for s in deployment["scripts"]],
"detection_rules": [r["title"] for r in deployment["detection_rules"]],
}
manifest_path = out_path / "deployment_manifest.json"
manifest_path.write_text(json.dumps(manifest, indent=2))
saved.append(str(manifest_path))
return saved
# ===========================================================================
# CLI Entry Point
# ===========================================================================
def main():
parser = argparse.ArgumentParser(
description="Active Directory Honeytoken Deployment Agent"
)
parser.add_argument(
"--action",
choices=[
"deploy_account", "deploy_spn", "deploy_gpo", "deploy_bloodhound",
"full_deploy", "generate_rules", "validate", "analyze_logs",
],
default="full_deploy",
help="Action to perform",
)
parser.add_argument("--domain", default="corp.example.com")
parser.add_argument("--ou", default="OU=Service Accounts")
parser.add_argument("--sysvol", default="")
parser.add_argument("--account-name", default="svc_sqlbackup_legacy")
parser.add_argument("--token-count", type=int, default=3)
parser.add_argument("--siem", choices=["sigma", "splunk", "sentinel"], default="sigma")
parser.add_argument("--output-dir", default="honeytoken_deployment")
parser.add_argument("--include-spn", action="store_true", default=True)
parser.add_argument("--include-gpo", action="store_true", default=True)
parser.add_argument("--include-bloodhound", action="store_true", default=True)
parser.add_argument("--event-log", help="Path to event log JSON for analysis")
args = parser.parse_args()
print("=" * 60)
print("Active Directory Honeytoken Deployment Agent")
print("=" * 60)
deployer = HoneytokenDeployer(
domain=args.domain,
service_account_ou=args.ou,
sysvol_path=args.sysvol,
)
if args.action == "full_deploy":
print(f"\n[+] Generating full honeytoken deployment for: {args.domain}")
print(f"[+] Token count: {args.token_count}")
print(f"[+] SIEM target: {args.siem}")
deployment = deployer.deploy_full_suite(
token_count=args.token_count,
include_spn=args.include_spn,
include_gpo=args.include_gpo,
include_bloodhound=args.include_bloodhound,
siem_type=args.siem,
)
saved_files = deployer.save_deployment(deployment, args.output_dir)
print(f"\n[+] Deployment ID: {deployment['deployment_id']}")
print(f"[+] Tokens generated: {len(deployment['tokens'])}")
for token in deployment["tokens"]:
print(f" - {token['name']} ({token['type']})"
+ (f" SPN: {token.get('spn', 'N/A')}" if token.get('spn') else ""))
print(f"\n[+] Scripts generated: {len(deployment['scripts'])}")
for script in deployment["scripts"]:
print(f" - {script['filename']} ({script['type']})")
print(f"\n[+] Detection rules generated: {len(deployment['detection_rules'])}")
for rule in deployment["detection_rules"]:
print(f" - {rule['title']}")
print(f"\n[+] Files saved to: {args.output_dir}")
for f in saved_files:
print(f" {f}")
elif args.action == "generate_rules":
print(f"\n[+] Generating {args.siem} detection rules...")
monitor = ADHoneytokenMonitor()
monitor.register_honeytoken(args.account_name, "admin_account")
rules = monitor.generate_detection_rules(args.siem)
for rule in rules:
print(f"\n--- {rule['title']} ---")
print(rule.get("rule", rule.get("detection_logic", "")))
elif args.action == "analyze_logs":
if not args.event_log:
print("[-] --event-log required for log analysis")
return
print(f"\n[+] Analyzing event log: {args.event_log}")
monitor = ADHoneytokenMonitor()
monitor.register_honeytoken(args.account_name, "admin_account")
log_path = Path(args.event_log)
if not log_path.exists():
print(f"[-] Log file not found: {args.event_log}")
return
with open(log_path) as f:
events = json.load(f)
alerts = monitor.analyze_event_log(events)
print(f"\n[+] Alerts generated: {len(alerts)}")
for alert in alerts:
print(f" [{alert['severity'].upper()}] {alert['alert_type']}: "
f"{alert['description']}")
print(f" Source: {alert['source_ip']} | "
f"Account: {alert['account']} | "
f"MITRE: {alert['mitre_technique']}")
summary = monitor.get_alert_summary()
print(f"\n[+] Summary: {summary['total_alerts']} alerts, "
f"sources: {list(summary['by_source_ip'].keys())}")
elif args.action == "deploy_account":
ps_gen = PowerShellGenerator()
ou_dn = f"{args.ou},DC={',DC='.join(args.domain.split('.'))}"
script = ps_gen.generate_create_honeytoken_account(
sam_account_name=args.account_name,
display_name="Legacy Backup Service",
description="Legacy backup service account - DO NOT DELETE",
ou_dn=ou_dn,
)
print(script)
elif args.action == "deploy_spn":
ps_gen = PowerShellGenerator()
script = ps_gen.generate_add_honey_spn(
sam_account_name=args.account_name,
)
print(script)
elif args.action == "deploy_gpo":
ps_gen = PowerShellGenerator()
script = ps_gen.generate_decoy_gpo(
gpo_name="Server Maintenance Policy (Legacy)",
decoy_username="admin_maintenance",
decoy_domain=args.domain.split(".")[0].upper(),
sysvol_path=deployer.sysvol_path,
)
print(script)
elif args.action == "deploy_bloodhound":
ps_gen = PowerShellGenerator()
script = ps_gen.generate_deceptive_bloodhound_path(
honeytoken_sam=args.account_name,
)
print(script)
elif args.action == "validate":
ps_gen = PowerShellGenerator()
script = ps_gen.generate_validation_script(args.account_name)
print(script)
print("\n" + "=" * 60)
print("[+] Honeytoken agent complete.")
print("=" * 60)
if __name__ == "__main__":
main()
Deploy-ADHoneytokens.ps122.2 KB
<#
.SYNOPSIS
Active Directory Honeytoken Deployment Module
.DESCRIPTION
Deploys deception-based honeytokens in Active Directory including:
- Fake privileged accounts with AdminCount=1
- Fake SPNs for Kerberoasting detection (honeyroasting)
- Decoy GPOs with cpassword traps
- Deceptive BloodHound attack paths
- SACL audit rules for detection
.NOTES
Author: mukul975
Version: 1.0
References:
- Trimarc Security: The Art of the Honeypot Account
- ADSecurity.org: Detecting Kerberoasting Activity Part 2
- Microsoft Defender for Identity Honeytokens
- SpecterOps: Kerberoasting and AES-256
#>
#Requires -Modules ActiveDirectory
#Requires -Version 5.1
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
# ---------------------------------------------------------------------------
# Module-level variables
# ---------------------------------------------------------------------------
$Script:DeployedTokens = @()
$Script:DeploymentLog = @()
# ---------------------------------------------------------------------------
# Helper Functions
# ---------------------------------------------------------------------------
function Write-DeployLog {
param(
[string]$Message,
[ValidateSet("INFO", "WARN", "ERROR", "SUCCESS")]
[string]$Level = "INFO"
)
$entry = [PSCustomObject]@{
Timestamp = (Get-Date -Format "yyyy-MM-dd HH:mm:ss")
Level = $Level
Message = $Message
}
$Script:DeploymentLog += $entry
$color = switch ($Level) {
"INFO" { "White" }
"WARN" { "Yellow" }
"ERROR" { "Red" }
"SUCCESS" { "Green" }
}
Write-Host "[$Level] $Message" -ForegroundColor $color
}
function New-SecureRandomPassword {
param([int]$Length = 128)
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_=+[]{}|;:,.<>?'
$password = -join (1..$Length | ForEach-Object { $chars[(Get-Random -Maximum $chars.Length)] })
return $password
}
# ---------------------------------------------------------------------------
# New-HoneytokenAdmin
# ---------------------------------------------------------------------------
function New-HoneytokenAdmin {
<#
.SYNOPSIS
Creates a honeytoken admin account in Active Directory.
.DESCRIPTION
Creates a realistic-looking service account with AdminCount=1 set,
backdated password age, group memberships, and SACL audit rules.
The account appears as a high-value target to attackers using
BloodHound, SharpHound, or manual AD enumeration.
.PARAMETER SamAccountName
The sAMAccountName for the honeytoken account.
.PARAMETER DisplayName
The display name for the account.
.PARAMETER Description
The description field (should look legitimate).
.PARAMETER OU
The Distinguished Name of the OU to create the account in.
.PARAMETER PasswordLength
Length of the random password (default: 128).
.PARAMETER SetAdminCount
If true, sets AdminCount=1 on the account (default: true).
.PARAMETER AccountAgeDays
Number of days to backdate the password (default: 5475 = ~15 years).
.EXAMPLE
New-HoneytokenAdmin -SamAccountName "svc_sqlbackup_legacy" `
-DisplayName "SQL Backup Service (Legacy)" `
-Description "Legacy SQL Server backup service account - DO NOT DELETE" `
-OU "OU=Service Accounts,DC=corp,DC=example,DC=com"
#>
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[string]$SamAccountName,
[Parameter(Mandatory)]
[string]$DisplayName,
[string]$Description = "Legacy service account - DO NOT DELETE",
[Parameter(Mandatory)]
[string]$OU,
[int]$PasswordLength = 128,
[bool]$SetAdminCount = $true,
[int]$AccountAgeDays = 5475
)
Write-DeployLog "Creating honeytoken admin: $SamAccountName" "INFO"
# Generate strong random password
$Password = New-SecureRandomPassword -Length $PasswordLength
$SecurePassword = ConvertTo-SecureString -String $Password -AsPlainText -Force
# Create the account
$Domain = Get-ADDomain
$UPN = "$SamAccountName@$($Domain.DNSRoot)"
$UserParams = @{
Name = $DisplayName
SamAccountName = $SamAccountName
UserPrincipalName = $UPN
DisplayName = $DisplayName
Description = $Description
Path = $OU
AccountPassword = $SecurePassword
Enabled = $true
PasswordNeverExpires = $true
CannotChangePassword = $true
ChangePasswordAtLogon = $false
}
try {
New-ADUser @UserParams
Write-DeployLog "Account created: $SamAccountName" "SUCCESS"
}
catch {
Write-DeployLog "Failed to create account: $_" "ERROR"
throw
}
# Set AdminCount=1
if ($SetAdminCount) {
Set-ADUser -Identity $SamAccountName -Replace @{AdminCount = 1}
Write-DeployLog "AdminCount set to 1" "SUCCESS"
}
# Backdate password
$AgeDate = (Get-Date).AddDays(-$AccountAgeDays)
$FileTime = $AgeDate.ToFileTime()
Set-ADUser -Identity $SamAccountName -Replace @{pwdLastSet = $FileTime}
Write-DeployLog "Password backdated to: $($AgeDate.ToString('yyyy-MM-dd'))" "SUCCESS"
# Add to visible groups
Add-ADGroupMember -Identity "Remote Desktop Users" -Members $SamAccountName
Write-DeployLog "Added to Remote Desktop Users" "SUCCESS"
# Set SACL for audit
$UserDN = (Get-ADUser -Identity $SamAccountName).DistinguishedName
$Acl = Get-Acl "AD:\$UserDN"
$AuditRule = New-Object System.DirectoryServices.ActiveDirectoryAuditRule(
[System.Security.Principal.SecurityIdentifier]"S-1-1-0",
[System.DirectoryServices.ActiveDirectoryRights]"ReadProperty",
[System.Security.AccessControl.AuditFlags]"Success",
[System.DirectoryServices.ActiveDirectorySecurityInheritance]"None"
)
$Acl.AddAuditRule($AuditRule)
Set-Acl "AD:\$UserDN" $Acl
Write-DeployLog "SACL audit rule configured (Event ID 4662)" "SUCCESS"
$result = Get-ADUser -Identity $SamAccountName -Properties *
$Script:DeployedTokens += $result
return $result
}
# ---------------------------------------------------------------------------
# Add-HoneytokenSPN
# ---------------------------------------------------------------------------
function Add-HoneytokenSPN {
<#
.SYNOPSIS
Adds a fake SPN to a honeytoken account for Kerberoasting detection.
.DESCRIPTION
Registers a fake Service Principal Name on a honeytoken account.
Any TGS ticket request for this SPN is definitively malicious since
the associated service does not exist. This is known as "honeyroasting".
.PARAMETER SamAccountName
The honeytoken account to add the SPN to.
.PARAMETER ServiceClass
The SPN service class (default: MSSQLSvc).
.PARAMETER Hostname
The fake hostname for the SPN.
.PARAMETER Port
The service port (default: 1433).
#>
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[string]$SamAccountName,
[string]$ServiceClass = "MSSQLSvc",
[Parameter(Mandatory)]
[string]$Hostname,
[int]$Port = 1433
)
$SPN = "$ServiceClass/${Hostname}:$Port"
Write-DeployLog "Adding honey SPN: $SPN to $SamAccountName" "INFO"
# Verify account exists
$User = Get-ADUser -Identity $SamAccountName -Properties ServicePrincipalNames -ErrorAction Stop
# Add SPN
Set-ADUser -Identity $SamAccountName -ServicePrincipalNames @{Add = $SPN}
Write-DeployLog "SPN registered: $SPN" "SUCCESS"
# Enable RC4 + AES encryption (makes it attractive to Kerberoast tools)
Set-ADUser -Identity $SamAccountName -Replace @{"msDS-SupportedEncryptionTypes" = 28}
Write-DeployLog "Encryption types set to RC4+AES128+AES256" "SUCCESS"
return [PSCustomObject]@{
SamAccountName = $SamAccountName
SPN = $SPN
ServiceClass = $ServiceClass
Hostname = $Hostname
Port = $Port
}
}
# ---------------------------------------------------------------------------
# New-DecoyGPO
# ---------------------------------------------------------------------------
function New-DecoyGPO {
<#
.SYNOPSIS
Creates a decoy GPO with cpassword credential trap.
.DESCRIPTION
Creates a fake GPO folder in SYSVOL containing a Groups.xml with
encrypted credentials (cpassword). Attackers using Get-GPPPassword,
gpp-decrypt, or CrackMapExec will find and attempt to use these
credentials, which triggers Event ID 4625 (failed logon).
.PARAMETER GPOName
Descriptive name for the decoy GPO.
.PARAMETER DecoyUsername
The username to embed in the cpassword trap.
.PARAMETER DecoyDomain
The short domain name (e.g., CORP).
.PARAMETER SYSVOLPath
The path to the SYSVOL Policies folder.
.PARAMETER EnableAuditSACL
Whether to set SACL audit on the GPO folder (default: true).
#>
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[string]$GPOName,
[Parameter(Mandatory)]
[string]$DecoyUsername,
[Parameter(Mandatory)]
[string]$DecoyDomain,
[Parameter(Mandatory)]
[string]$SYSVOLPath,
[bool]$EnableAuditSACL = $true
)
$GPOGuid = [guid]::NewGuid().ToString().ToUpper()
Write-DeployLog "Creating decoy GPO: $GPOName (GUID: $GPOGuid)" "INFO"
# Create folder structure
$GPOPath = Join-Path $SYSVOLPath "{$GPOGuid}"
$MachinePath = Join-Path $GPOPath "Machine\Preferences\Groups"
New-Item -ItemType Directory -Path $MachinePath -Force | Out-Null
# Generate fake cpassword
$FakePassword = "H0n3yT0k3n_Tr4p_$(Get-Date -Format 'yyyy')!"
$FakeCPassword = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($FakePassword))
$UserGuid = [guid]::NewGuid().ToString().ToUpper()
# Create Groups.xml with cpassword trap
$GroupsXml = @"
<?xml version="1.0" encoding="utf-8"?>
<Groups clsid="{3125E937-EB16-4b4c-9934-544FC6D24D26}">
<User clsid="{DF5F1855-51E5-4d24-8B1A-D9BDE98BA1D1}"
name="$DecoyUsername"
image="2"
changed="2011-07-15 08:30:22"
uid="{$UserGuid}">
<Properties action="U"
newName=""
fullName="Maintenance Administrator"
description="Legacy maintenance account"
cpassword="$FakeCPassword"
changeLogon="0"
noChange="1"
neverExpires="1"
acctDisabled="0"
userName="$DecoyDomain\$DecoyUsername" />
</User>
</Groups>
"@
$GroupsXml | Out-File -FilePath (Join-Path $MachinePath "Groups.xml") -Encoding UTF8
Write-DeployLog "Groups.xml planted with cpassword trap" "SUCCESS"
# Create corresponding trap AD account with different password
$TrapPassword = New-SecureRandomPassword -Length 64
$SecureTrap = ConvertTo-SecureString -String $TrapPassword -AsPlainText -Force
try {
New-ADUser -Name $DecoyUsername `
-SamAccountName $DecoyUsername `
-Description "Maintenance account - legacy" `
-AccountPassword $SecureTrap `
-Enabled $true `
-PasswordNeverExpires $true
Write-DeployLog "Trap account created: $DecoyUsername (password differs from GPP)" "SUCCESS"
}
catch {
Write-DeployLog "Trap account creation: $_" "WARN"
}
# Set SACL
if ($EnableAuditSACL) {
$FolderAcl = Get-Acl $GPOPath
$AuditRule = New-Object System.Security.AccessControl.FileSystemAuditRule(
"Everyone", "ReadData", "ContainerInherit,ObjectInherit", "None", "Success"
)
$FolderAcl.AddAuditRule($AuditRule)
Set-Acl $GPOPath $FolderAcl
Write-DeployLog "SACL set on GPO folder (Event ID 4663)" "SUCCESS"
}
return [PSCustomObject]@{
GPOGuid = $GPOGuid
GPOName = $GPOName
GPOPath = $GPOPath
DecoyUsername = $DecoyUsername
DecoyDomain = $DecoyDomain
}
}
# ---------------------------------------------------------------------------
# New-DeceptiveBloodHoundPath
# ---------------------------------------------------------------------------
function New-DeceptiveBloodHoundPath {
<#
.SYNOPSIS
Creates fake BloodHound attack paths pointing to monitored honeytokens.
.DESCRIPTION
Sets ACL permissions that create apparent attack paths visible to
BloodHound/SharpHound reconnaissance. These paths lead attackers toward
monitored honeytoken accounts, triggering alerts when abused.
.PARAMETER HoneytokenSamAccount
The honeytoken account to create paths toward.
.PARAMETER TargetHighValueGroup
The high-value group to create a deceptive path to (default: Domain Admins).
.PARAMETER IntermediateOU
OU path for intermediate objects.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[string]$HoneytokenSamAccount,
[string]$TargetHighValueGroup = "Domain Admins",
[string]$IntermediateOU = "OU=Service Accounts"
)
Write-DeployLog "Creating deceptive BloodHound paths for: $HoneytokenSamAccount" "INFO"
$UserDN = (Get-ADUser -Identity $HoneytokenSamAccount).DistinguishedName
# Create GenericAll edge from regular group to honeytoken
$GroupSID = (Get-ADGroup -Identity "Remote Desktop Users").SID
$Acl = Get-Acl "AD:\$UserDN"
$AceRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule(
$GroupSID,
[System.DirectoryServices.ActiveDirectoryRights]"GenericAll",
[System.Security.AccessControl.AccessControlType]"Allow"
)
$Acl.AddAccessRule($AceRule)
Set-Acl "AD:\$UserDN" $Acl
Write-DeployLog "GenericAll ACE: Remote Desktop Users -> $HoneytokenSamAccount" "SUCCESS"
# Create deceptive intermediate group
$DeceptiveGroup = "IT-Infrastructure-Admins"
try {
New-ADGroup -Name $DeceptiveGroup -GroupScope DomainLocal `
-GroupCategory Security `
-Description "Infrastructure administration delegation"
Write-DeployLog "Created deceptive group: $DeceptiveGroup" "SUCCESS"
}
catch {
Write-DeployLog "Deceptive group may already exist" "WARN"
}
Add-ADGroupMember -Identity $DeceptiveGroup -Members $HoneytokenSamAccount
Write-DeployLog "Added honeytoken to $DeceptiveGroup" "SUCCESS"
# Create WriteDacl edge with deny safety net
$DAGroupDN = (Get-ADGroup -Identity $TargetHighValueGroup).DistinguishedName
$HoneySID = (Get-ADUser -Identity $HoneytokenSamAccount).SID
$DAGroupAcl = Get-Acl "AD:\$DAGroupDN"
$DenyRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule(
$HoneySID,
[System.DirectoryServices.ActiveDirectoryRights]"GenericAll",
[System.Security.AccessControl.AccessControlType]"Deny"
)
$WriteDaclRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule(
$HoneySID,
[System.DirectoryServices.ActiveDirectoryRights]"WriteDacl",
[System.Security.AccessControl.AccessControlType]"Allow"
)
$DAGroupAcl.AddAccessRule($DenyRule)
$DAGroupAcl.AddAccessRule($WriteDaclRule)
Set-Acl "AD:\$DAGroupDN" $DAGroupAcl
Write-DeployLog "Deceptive WriteDacl path created (with deny safety)" "SUCCESS"
return [PSCustomObject]@{
HoneytokenAccount = $HoneytokenSamAccount
PathDescription = "Remote Desktop Users -> $HoneytokenSamAccount -> $DeceptiveGroup -> $TargetHighValueGroup (blocked)"
DeceptiveGroup = $DeceptiveGroup
}
}
# ---------------------------------------------------------------------------
# Test-HoneytokenDeployment
# ---------------------------------------------------------------------------
function Test-HoneytokenDeployment {
<#
.SYNOPSIS
Validates honeytoken deployment integrity.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[string]$SamAccountName,
[switch]$ValidateAdminCount,
[switch]$ValidateSPN,
[switch]$ValidateGPODecoy,
[switch]$ValidateAuditPolicy
)
$Results = @()
# Account existence
$User = Get-ADUser -Identity $SamAccountName -Properties * -ErrorAction SilentlyContinue
if ($User) {
$Results += [PSCustomObject]@{Check="Account Exists"; Status="PASS"; Details=$User.DistinguishedName}
$Results += [PSCustomObject]@{Check="Account Enabled"; Status=$(if($User.Enabled){"PASS"}else{"FAIL"}); Details=$(if($User.Enabled){"Enabled"}else{"Disabled"})}
} else {
$Results += [PSCustomObject]@{Check="Account Exists"; Status="FAIL"; Details="Not found"}
return $Results
}
if ($ValidateAdminCount) {
$Results += [PSCustomObject]@{
Check = "AdminCount=1"
Status = $(if($User.AdminCount -eq 1){"PASS"}else{"WARN"})
Details = "AdminCount=$($User.AdminCount)"
}
}
if ($ValidateSPN) {
$SPNs = $User.ServicePrincipalNames
$Results += [PSCustomObject]@{
Check = "SPN Configured"
Status = $(if($SPNs -and $SPNs.Count -gt 0){"PASS"}else{"WARN"})
Details = $(if($SPNs){$SPNs -join ", "}else{"No SPNs"})
}
}
if ($ValidateAuditPolicy) {
$AuditCheck = auditpol /get /subcategory:"Kerberos Service Ticket Operations" 2>$null
$Results += [PSCustomObject]@{
Check = "Kerberos TGS Auditing"
Status = $(if($AuditCheck -match "Success"){"PASS"}else{"FAIL"})
Details = $(if($AuditCheck -match "Success"){"Enabled"}else{"Run: auditpol /set /subcategory:'Kerberos Service Ticket Operations' /success:enable"})
}
}
# Password age
$PwdAge = (Get-Date) - $User.PasswordLastSet
$Results += [PSCustomObject]@{
Check = "Password Age"
Status = $(if($PwdAge.Days -gt 365){"PASS"}else{"WARN"})
Details = "$($PwdAge.Days) days"
}
return $Results
}
# ---------------------------------------------------------------------------
# Deploy-FullHoneytokenSuite
# ---------------------------------------------------------------------------
function Deploy-FullHoneytokenSuite {
<#
.SYNOPSIS
Deploys a complete honeytoken suite in Active Directory.
#>
[CmdletBinding()]
param(
[string]$Environment = "Production",
[Parameter(Mandatory)]
[string]$ServiceAccountOU,
[Parameter(Mandatory)]
[string]$SYSVOLPath,
[int]$TokenCount = 3,
[bool]$IncludeSPN = $true,
[bool]$IncludeGPODecoy = $true,
[bool]$IncludeBloodHoundPath = $true,
[string]$SIEMType = "Splunk"
)
Write-DeployLog "Starting full honeytoken suite deployment for $Environment" "INFO"
Write-DeployLog "Token count: $TokenCount, SPN: $IncludeSPN, GPO: $IncludeGPODecoy" "INFO"
$Tokens = @()
# Service account name templates
$ServiceNames = @(
@{Sam="svc_sqlbackup_legacy"; Display="SQL Backup Service (Legacy)"; SPN_Class="MSSQLSvc"; Host="sql-bak-legacy01"; Port=1433},
@{Sam="svc_exchange_transport"; Display="Exchange Transport Agent"; SPN_Class="exchangeMDB"; Host="exch-hub-legacy02"; Port=443},
@{Sam="svc_scom_monitor"; Display="SCOM Monitoring Service"; SPN_Class="HTTP"; Host="scom-legacy-mgmt01"; Port=5723},
@{Sam="svc_adfs_proxy_old"; Display="ADFS Proxy Service (Old)"; SPN_Class="HTTP"; Host="adfs-proxy-legacy01"; Port=443},
@{Sam="svc_citrix_storefront"; Display="Citrix StoreFront Service"; SPN_Class="HTTP"; Host="ctx-sf-legacy01"; Port=443}
)
$Domain = (Get-ADDomain).DNSRoot
for ($i = 0; $i -lt [Math]::Min($TokenCount, $ServiceNames.Count); $i++) {
$svc = $ServiceNames[$i]
# Create admin account
$admin = New-HoneytokenAdmin `
-SamAccountName $svc.Sam `
-DisplayName $svc.Display `
-Description "Legacy $($svc.Display.ToLower()) - DO NOT DELETE" `
-OU $ServiceAccountOU
$tokenInfo = [PSCustomObject]@{
Name = $svc.Sam
Type = "admin_account"
SPN = ""
DetectionRule = "Event ID 4662 (object access)"
}
# Add SPN
if ($IncludeSPN) {
$Hostname = "$($svc.Host).$Domain"
$spnResult = Add-HoneytokenSPN `
-SamAccountName $svc.Sam `
-ServiceClass $svc.SPN_Class `
-Hostname $Hostname `
-Port $svc.Port
$tokenInfo.SPN = $spnResult.SPN
$tokenInfo.DetectionRule = "Event ID 4769 (Kerberoast)"
}
$Tokens += $tokenInfo
}
# Deploy GPO decoy
if ($IncludeGPODecoy) {
$DomainShort = ($Domain -split '\.')[0].ToUpper()
$gpo = New-DecoyGPO `
-GPOName "Server Maintenance Policy (Legacy)" `
-DecoyUsername "admin_maintenance" `
-DecoyDomain $DomainShort `
-SYSVOLPath $SYSVOLPath
$Tokens += [PSCustomObject]@{
Name = "admin_maintenance"
Type = "gpo_credential"
SPN = ""
DetectionRule = "Event ID 4625 (failed logon with GPP creds)"
}
}
# Create BloodHound deception
if ($IncludeBloodHoundPath -and $Tokens.Count -gt 0) {
$bhPath = New-DeceptiveBloodHoundPath `
-HoneytokenSamAccount $Tokens[0].Name
}
$deployment = [PSCustomObject]@{
Environment = $Environment
Tokens = $Tokens
DeployedAt = (Get-Date -Format "yyyy-MM-dd HH:mm:ss")
Log = $Script:DeploymentLog
}
Write-DeployLog "Deployment complete: $($Tokens.Count) tokens deployed" "SUCCESS"
return $deployment
}
# ---------------------------------------------------------------------------
# Export module members
# ---------------------------------------------------------------------------
Export-ModuleMember -Function @(
'New-HoneytokenAdmin',
'Add-HoneytokenSPN',
'New-DecoyGPO',
'New-DeceptiveBloodHoundPath',
'Test-HoneytokenDeployment',
'Deploy-FullHoneytokenSuite'
)