skip to content

A host logs hundreds of Windows Security 4625 failures overnight; what does the sub-status field let you conclude?

level: middleimportance: should knowfreq 61%

answer

  1. the reason lives in a sub-field
  2. wrong password versus no such user
  3. many names versus many passwords
  4. 0xC0000064 against 0xC000006A
  5. the lockout code means it already happened

basics

~10 s

The sub-status names why each logon failed. 0xC000006A is a real account with a wrong password, 0xC0000064 is an account that does not exist, 0xC0000234 is a locked-out account. Those are three different stories.

solid answer

~40 s

Event 4625 records a failed logon, and the generic Status is usually 0xC000006D while the **Sub Status** carries the actual reason. Many 0xC0000064 results (user name does not exist) across many distinct names from one source is enumeration against a guessed list. Many 0xC000006A results (account exists, password wrong) spread thinly across hundreds of accounts is password spraying; concentrated on one account it is brute force, or a service or scheduled task carrying a stale password. 0xC0000234 means the account is already locked out, so the disruptive part has happened. 0xC0000072 (disabled), 0xC0000071 (password expired) and 0xC0000193 (account expired) usually indicate a misconfigured job rather than an adversary. I also read the Logon Type and Source Network Address, and I remember that a broken scheduled task produces the same shape at suspiciously exact intervals.

go deeper

for a junior

Know that 4625 is a failed logon and that the sub-status says why. Be able to separate an account that does not exist from an account whose password was wrong.

for a middle

Explain the shapes: wide-and-shallow is spraying, deep-on-one-account is brute force, perfectly periodic is a misconfigured job. Name the common sub-status codes and what each rules in or out.

for a senior

Demonstrate the pivot from failures to successes, and show you know 4625 does not cover Kerberos failures on a domain controller, so your query has to reach 4771 and 4776 as well.

for a principal

Be ready to argue how much failed-authentication telemetry is worth collecting and retaining across the estate, and what you accept losing when the volume of routine misconfiguration outweighs the detections it supports.

## The record **Windows Security 4625 — "An account failed to log on"** is written by the machine that *rejected* the logon. On its own it is a count, and counts of failures are one of the noisiest things in an estate. The field that turns the count into an inference is the **Sub Status**. The top-level **Status** on 4625 is very often the generic `0xC000006D` ("the attempted logon is invalid"), which tells you nothing. The **Sub Status** is the specific reason: | Sub Status | Meaning | |---|---| | 0xC0000064 | User name does not exist | | 0xC000006A | User name is correct, password is wrong | | 0xC0000234 | Account is currently locked out | | 0xC0000072 | Account is disabled | | 0xC0000071 | Password has expired | | 0xC0000193 | Account has expired | | 0xC0000224 | User is required to change the password at next logon | | 0xC000015B | The account is not granted the requested logon type | | 0xC0000133 | Clock skew between the machines is too large | ## Reading shapes, not counts **Enumeration.** A burst of `0xC0000064` across many *distinct, often implausible* names from one source address is someone testing a user list. Nothing was compromised, but the source now knows which names are real — the failures that came back `0xC000006A` are the ones that exist. **Password spraying.** One or two attempts per account, across hundreds or thousands of accounts, inside a short window, with sub status `0xC000006A`. It is deliberately shaped to stay under per-account lockout thresholds, which is exactly why per-account alerting never sees it. You have to aggregate by **source address and time window**, not by account. And the event that matters is the one that is *not* a 4625: a single 4624 for one of those accounts from the same source. **Brute force on one account.** Hundreds of `0xC000006A` against a single name, typically ending in `0xC0000234` when the lockout threshold trips. **A misconfigured job.** The most common cause of a large 4625 volume in a real estate. A service, scheduled task, mapped drive or application connection string still carries an old password. The signature is regularity: the same account, the same source host, the same logon type (often 4 or 5), at exact intervals, forever. It is a **true positive for "authentication is failing"** and not an attack, and it will also lock the account out repeatedly if the retry rate is high enough. **Disabled, expired, must-change.** `0xC0000072`, `0xC0000193`, `0xC0000224` almost always mean a leaver's account that something is still trying to use, or an unattended job. They are hygiene findings. ## The gap that catches people out 4625 is written by the machine that rejected the logon, and it covers interactive and NTLM paths. **Domain accounts failing Kerberos authentication do not produce 4625 on the domain controller** — they produce **4771 (Kerberos pre-authentication failed)** with its own failure code, and NTLM validation attempts at a domain controller produce **4776** with a matching error code. A hunt written only over 4625 will miss whole classes of failure, and a spray against a domain will look far smaller than it is. ## Reading the surrounding fields - **Logon Type** on the failure: type 3 failures point at network services, type 10 at exposed RDP, type 4 or 5 at jobs and services. - **Source Network Address / Workstation Name**: the network vantage point. Workstation Name is client-supplied and therefore attacker-influenced. - **Account Name**: watch for names that do not exist in the directory at all — that is the enumeration tell. ## What failures cannot tell you A wall of failures says nothing about success. When the failures stop, three explanations are equally consistent with the record: the credential worked, the account locked out, or the source gave up. Only a search for a **successful** logon for the same accounts from the same source, in the same window, separates them. Reporting "the spray stopped" as good news, without that check, is the mistake this record punishes most often.

  • The failures are one attempt per account across nine hundred accounts in an hour. What is that?
    That is the shape of password spraying: a low per-account attempt count to stay beneath lockout thresholds, spread very wide, usually from one or a few source addresses trying a common password. Per-account thresholds never trip, so you have to aggregate by source and time window instead of by account.
  • Where do failed Kerberos logons for domain accounts show up, if not in 4625?
    On the domain controller as 4771, Kerberos pre-authentication failed, with its own failure code; NTLM credential validation at a domain controller appears as 4776 with an error code. 4625 is written by the machine that rejected an interactive or NTLM logon. A search built only on 4625 silently misses those paths.
  • The 4625 burst stops and you see nothing else. Is that good news?
    No. Stopping is equally consistent with the credential working, the account locking out, or the source moving on. Pivot to successful logons for the same accounts from the same source address in the same window. Absence of further failures is never evidence that nothing succeeded.

saying these in an interview costs you the question

  • Treats every 4625 burst as brute force
  • Counts events and never reads the sub-status
  • Thinks 0xC0000064 means a wrong password
  • Calls a stale service-account password an attack without checking the interval
  • Expects domain Kerberos failures to appear as 4625

context