Pre-Windows 2000 computer accounts: delete the dead, rotate the living
Staged computer accounts whose password is the computer name in lowercase, or blank. Delete the inactive ones, rotate the live ones from the machine itself, and fix it with anything except the Reset Account button.
01 The Big PictureFor leadership · no jargon
Okay, so here's a vulnerability that genuinely dates back to the nineties and still shows up on engagements today: a computer account whose password is the computer's own name, lowercase. Tick one compatibility checkbox when creating the account in advance and Windows obligingly sets the password to the name on the label. And the names are not secret: any machine on the network can read them out of the directory. It's the digital version of an office where every desk's spare key is cut to the desk number, and the desk numbers are stencilled on the doors.
The vulnerability comes in two flavours. Predictable: the pre-Windows 2000 checkbox just described. Blank: certain provisioning tools create computer accounts with no password at all. Either way, a pentester who can reach a domain controller can try the computer name (or nothing) as the password, and if the account was staged but never actually used, they're in. What they get is a genuine domain credential: enough to read out every user and machine in the organisation, and on real engagements it has been the first step towards taking over the domain entirely.
Now the good news, because there is some. A computer that actually joins the domain sets itself a fresh password at join time and keeps changing it on its own from then on, so live machines largely look after themselves. The accounts that stay vulnerable are the inactive ones: staged for a rollout, never joined, quietly forgotten. The work is finding them, deleting the dead, nudging the living, and fixing provisioning so it stops minting new ones. Minutes per account and no downtime, with exactly one nasty surprise waiting in the toolbox, which we'll get to.
02 Technical BreakdownFor engineers
When you create a computer account ahead of time in Active Directory Users and Computers (dsa.msc), the wizard offers a checkbox: "Assign this computer account as a pre-Windows 2000 computer". Microsoft's own KB 320187 documents what it does: the account gets "a password that is based on the new computer name"; leave it unticked and you get a random password. The exact recipe, written up by Joe Richards back in 2012 and re-verified by Secura on a fully patched Server 2019 DC: take the sAMAccountName, drop the trailing $, take the first 14 characters, lowercase them. DavesLaptop$ gets the password daveslaptop (TrustedSec's example, and yes, still today).
Two other provisioning paths land in the same place. The legacy net computer API uses the same NT4-era default, and dsadd computer creates the account with a blank password, no change forced. That's both flavours accounted for.
How do you spot the classic case in the directory? A pre-created, never-joined account carries userAccountControl 4128: WORKSTATION_TRUST_ACCOUNT (4096) plus PASSWD_NOTREQD (32). When a machine genuinely joins, the flag drops to 4096 and Netlogon takes over password duty. Rotation is client-driven, by the computer, not the domain (Microsoft's AskDS team is explicit about this), every 30 days by default. Which is exactly why inactive staged accounts are the exposure: nothing is driving them, so the predictable password sits there indefinitely.
Exploitation is embarrassingly cheap. Take a list of hostnames (from DNS, an SMB null session, or any authenticated read of the directory), derive the candidate passwords, and request a Kerberos ticket for each: the pre2k tool and NetExec's pre2k module automate precisely that, one attempt per account, so lockout policy never gets a look in. A correct guess against a never-used account fails with STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT (0xC0000199), which is simultaneously the attacker's success signal and your best detection tell. And a machine account is a real identity: TrustedSec used a inactive pre-created account on a live engagement to enrol for a certificate as Domain Computers via a misconfigured template (ESC1), and Optiv showed the same class of account permits a read-only domain join with netdom, bypassing both the join delegation and a machine account quota of zero.
One more twist, and it's the nasty surprise I promised: Optiv found (lab-verified, reported to MSRC, rated moderate, case closed with no fix) that resetting a computer account, the ADUC "Reset Account" right-click or dsmod -reset, sets the password right back to the lowercase computer name, regardless of how the account was originally created, with no warning and no attribute that reveals it. The obvious remediation re-arms the vulnerability. Windows Server 2025 finally kills the behaviour at the root: its DCs refuse default machine account passwords, and ADUC/ADAC no longer offer the pre-Windows 2000 option at all.
03 Affected
Any domain that pre-stages computer accounts with the checkbox ticked: classic imaging and provisioning workflows, mostly. Also anywhere dsadd computer appears in scripts without a follow-up password (blank), anyone still driving the net computer API, and, awkwardly, any account an admin has ever right-clicked "Reset Account" on, thanks to the reset-to-default behaviour above.
The vulnerable population is the inactive subset: accounts staged and never joined. Live machines rotate out of the predictable password at join time and every 30 days after, so a joined machine showing a stale password is a different mystery (snapshots, clones, or DisablePasswordChange in play). Pre-2025 DCs accept all of this happily; Server 2025 refuses new ones by default.
04 Detection
Before we change anything, let's find them. First pass, the attribute signature of the inactive classics:
Get-ADComputer -LDAPFilter "(&(userAccountControl=4128)(logonCount=0))" -Properties logonCount,pwdLastSet,whenCreated | Select-Object Name,logonCount,pwdLastSet,whenCreated4128 is WORKSTATION_TRUST_ACCOUNT plus PASSWD_NOTREQD, the fingerprint of a staged account; logonCount 0 means it never got used. These are your prime suspects.
Second pass, and this one matters: Optiv's reset finding means attribute queries alone can't be trusted. A reset account shows UAC 4096 and a healthy logonCount while quietly holding the default password ("the only identifying trait is the password itself", in their words). So the complete check is actually testing passwords, exactly as an attacker would, from one low-privilege account:
nxc ldap <dc-ip> -u lowpriv -p 'Password1' -M pre2kNetExec's pre2k module: enumerates computer accounts, tries the derived password against each, and saves a Kerberos ticket for every hit. Any hit is an account holding its predictable password right now, whatever its attributes claim. (The standalone pre2k tool does the same job, and can also work unauthenticated from a bare hostname list.)
For the blank half of the vulnerability, the definitive sweep is a hash audit: any computer$ account showing the empty-password NTLM hash (31d6cfe0d16ae931b73c59d7e0c089c0) in a secretsdump-style export is a blank dsadd special. Heavier to run, since it needs DCSync-level rights, but there's no arguing with it, and it's the approach the r/cybersecurity crowd recommends.
And while you're spraying your own DCs (for good, not evil), this is what the attack looks like in telemetry:
Get-WinEvent -FilterHashtable @{LogName='Security'; Id=4625} -MaxEvents 5000 | Where-Object Message -match '0xC0000199'0xC0000199 is STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT: someone authenticated correctly as a machine that has never logged on. A computer presenting a wrong password should be vanishingly rare under normal circumstances (Secura's detection advice, and it's sound), so a burst of 4625 or 4771 across many computer accounts from one source is computer spraying, full stop. On the Kerberos side the same story shows as 4768 ticket grants for inactive machine accounts.
05 Remediation
01 Triage: dead or alive?
Sort the suspects before touching anything. Inactive staged account, no matching live machine in your inventory: decommission candidate. Joined and alive: it should have rotated at join, so verify it's genuinely live and check nothing is suppressing rotation (DisablePasswordChange, snapshot restores, clones). Powered-on machines rotate regardless of whether anyone logs in, so "it's just never used" is not a state a healthy joined machine stays in.
02 Disable, then delete the dead ones
Disable-ADAccount -Identity OLDSTAGE01Disable first and give it a couple of weeks. If nothing screams (and for a never-joined staged account, nothing will), delete it. A staged account holding a predictable password has no goodwill left to preserve; deleting beats resetting every time.
Remove-ADComputer -Identity OLDSTAGE01The actual deletion, after the cooling-off period.
03 Rotate the live ones, from the machine itself
The non-breaking fix runs on the affected machine (or via your remote tooling: Invoke-Command, Intune, whatever's handy):
Reset-ComputerMachinePassword -Server dc01.contoso.com -Credential (Get-Credential)Rewrites the machine password locally and in AD in one operation; the machine stays joined, no rejoin, no outage. A reboot afterwards is polite, since services holding the old secure channel can throw a wobbly until restarted.
Test-ComputerSecureChannel -Repair -Credential (Get-Credential)The alternative: rebuilds the Netlogon secure channel with a fresh password. Member servers and workstations only; on domain controllers it false-positives, so use netdom or nltest there instead.
04 Do not "Reset Account"
Worth its own section, because it's the button AD puts in front of you. Right-click > Reset Account (or dsmod computer <DN> -reset) does two things: breaks the machine's secure channel so it must rejoin, and, per Optiv's research, sets the password back to the lowercase computer name with no warning. You'd be converting "predictable password" into "predictable password and an offline machine". If a staged account is never going to be joined, delete it; if the machine is live, rotate from the machine as above. The reset button has no role in this remediation.
05 Blank-password accounts
If Detection's hash audit found blank dsadd specials that are somehow still wanted, set a real password first (an admin reset needs no old password):
Set-ADAccountPassword -Identity STAGE02 -Reset -NewPassword (Read-Host -AsSecureString 'New password')Then treat it like any other staged account: get the machine joined, or delete the account. Honestly, deletion is usually the right answer here too.
06 Stop minting new ones
The vulnerability regenerates unless provisioning changes. Retire the checkbox (nothing in your estate predates Windows 2000, and the compatibility it offers is for an OS that can itself be retired), check scripts for bare dsadd computer calls, and if you pre-stage accounts for join delegation, the current domain-join hardening (KB5020276) already expects the joining user to own the account, so stage and join promptly rather than leaving accounts parked. The graceful end state is Windows Server 2025 DCs, which refuse default machine account passwords outright ("Domain controller: Refuse setting default machine account password") and drop the option from the consoles entirely.
Optionally, wire Detection's 0xC0000199 query into your monitoring so a future spraying run, yours or theirs, trips an alarm.
06 Verification
Rotated and deleted? Let's prove it rather than assume it.
Get-ADComputer -LDAPFilter "(&(userAccountControl=4128)(logonCount=0))"The attribute sweep again: empty, or down to a short list you can explain, is the pass mark.
nxc ldap <dc-ip> -u lowpriv -p 'Password1' -M pre2kThe password test again: zero tickets saved means no account, however it got that way, still holds its name as its password. This is the check that matters, so run it even if the attribute sweep looks clean.
Get-ADComputer -Identity FIXED01 -Properties pwdLastSet | Select-Object Name,pwdLastSetFor every machine you rotated: pwdLastSet inside the rotation window.
Test-ComputerSecureChannelOn each rotated machine: True, confirming the domain relationship is healthy after the password change.
Then put the sweep on a schedule. Provisioning habits drift, and this vulnerability is a boomerang.
07 Rollback
The rotation fix is non-breaking, so rollback is mostly about the deletions. Disabling before deleting is your safety net; if you deleted something that turned out to be wanted and the AD Recycle Bin is enabled:
Get-ADObject -Filter 'sAMAccountName -eq "OLDSTAGE01$"' -IncludeDeletedObjects | Restore-ADObjectRestores the account with its SID and group memberships. No Recycle Bin, or past the tombstone lifetime? Recreate the account and rejoin the machine; note that current domain-join hardening requires the joining user to own the account.
If someone did reach for Reset Account and broke a live machine's trust relationship: Test-ComputerSecureChannel -Repair usually rejoins it without drama, with the classic unjoin/rejoin as the fallback. And if a service sulked after a password rotation, a reboot is the fix; that's the known side effect, not damage.
The honest exception: if a legacy provisioning process genuinely cannot stage accounts any other way, scope the exception tightly (dedicated OU, tight ACLs, alert on use), document it as accepted risk, and let the 0xC0000199 monitoring watch it. Not every pentest finding ends in zero; some end in a spreadsheet with an owner.
08 References
- Microsoft KB 320187 (archived): the checkbox assigns a name-based password
- Joe Richards (joeware), 2012: the exact 14-character lowercase derivation
- TrustedSec, Oddvar Moe: Diving into Pre-Created Computer Accounts
- Optiv, Garrett Foster: Diving Deeper (reset-to-default research, MSRC disclosure)
- Secura whitepaper: Timeroasting, Trustroasting and Computer Spraying
- pre2k (garrettfoster13 / Tw1sm)
- NetExec pre2k module docs
- thehacker.recipes: pre-Windows 2000 computers
- Microsoft AskDS: Machine Account Password Process
- Microsoft Learn: Reset-ComputerMachinePassword
- Microsoft Learn: Test-ComputerSecureChannel
- What's new in Windows Server 2025: default machine account passwords blocked
- r/cybersecurity: Computer Objects, Blank Password
- r/sysadmin: Assign this computer account as a pre-Windows 2000 computer, with PowerShell?
If I've got anything wrong here, or you've hit an edge case I haven't covered, do say: corrections and questions are always welcome :)
Ship the fix. Prove it’s closed.
If this runbook doesn’t fit your stack, send the CVE — an operator writes a verified path, usually within a business day. Or contribute your own and get co-authored credit.