0xC0000071 Windows logon failure code Triage: low

0xC0000071 in Event 4625: The Account's Password Has Expired

Published

Windows refused the logon because the account's password has expired and has to be changed — routine at the end of a password cycle for a person, but a broken job when it hits a service account and a lead when it comes from a host the account never uses.

Did the attacker have the password? Cannot tell from this code alone — correlate with the events around it.

Microsoft's definition

STATUS_PASSWORD_EXPIRED

The user account password has expired.

Source: [MS-ERREF] NTSTATUS Values · checked against the published table on every build · look up another code

Where it appears

  • Event 4625 (An account failed to log on): the Status or Sub Status field
  • Event 4776 (credential validation, NTLM) on domain controllers: the Error Code field
  • Microsoft Sentinel / Log Analytics: the Status and SubStatus columns of the SecurityEvent table

Every field of that log: Windows Security event log fields

Where it sits in event 4625: an example

Event 4625 · An account failed to log on

Failure Information:

Status:
0xC000006D STATUS_LOGON_FAILURE
Sub Status:
0xC0000071 STATUS_PASSWORD_EXPIRED This code

Microsoft's event 4625 documentation: Status is “the reason why logon failed”, Sub Status “additional information about logon failure”. In Microsoft Sentinel they are the Status and SubStatus columns of SecurityEvent.

What it means

Event 4625 records a logon that Windows refused, and the Status and Sub Status fields say why. 0xC0000071 is STATUS_PASSWORD_EXPIRED: “The user account password has expired.” The account name was resolved, the account is not what blocked the attempt — the password attached to it has passed its maximum age and has to be changed before it can be used again.

In 4625 the specific reason normally sits in Sub Status alongside the general failure 0xC000006D in Status, the same shape as 0xC000006A. Depending on the package that reported the failure, 0xC0000071 can also show up in Status itself, so when you search, match on either field. Domain controllers validating NTLM credentials put the same value in the Error Code field of event 4776, and the Kerberos path produces 0x17 instead.

Two other fields decide how interesting the entry is: Logon Type (3 network, 10 RemoteInteractive, 8 NetworkCleartext) and Source Network Address / Workstation Name, which tell you whether the attempt came from the person’s own machine, an application server, or somewhere unexpected.

Is it an attack?

Usually not. The ordinary case is a password cycle: an account that nobody has signed into for a while reaches its maximum age, the first attempt after that fails with 0xC0000071, and the user changes the password and gets on with their day. A successful 4624 for the same account shortly afterwards closes the case.

What is worth a second look:

PatternReading
One user, a couple of failures, then a success from the same workstationRoutine password change
One account, failures on a fixed schedule from one internal serverA service, scheduled task or mapped drive holding an expired secret — an availability problem, not an attack
A dormant or leaver account failing this wayThe account should probably be disabled; find out what is still calling it
Failures from an address or workstation the account has never usedSomeone is trying a credential they obtained elsewhere (T1078)
One source producing expired, disabled and wrong-password failures across many accountsA credential list being replayed — compare with 0xC0000072 and 0xC000006A

The value itself does not prove that whoever sent the request knew the current password, so do not escalate on the code alone — escalate on where it came from.

How to investigate

Start by listing which accounts are hitting the expiry and from where:

SecurityEvent
| where TimeGenerated > ago(7d)
| where EventID == 4625
| where Status =~ "0xc0000071" or SubStatus =~ "0xc0000071"
| summarize Failures = count(), FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated),
            Sources = make_set(IpAddress, 10), Workstations = make_set(WorkstationName, 10),
            LogonTypes = make_set(LogonType, 5)
    by TargetUserName, TargetDomainName, Computer
| order by Failures desc

Then separate the broken jobs from the interesting attempts by looking at the rhythm — a service retrying an expired secret produces an even hourly count, a person does not:

SecurityEvent
| where TimeGenerated > ago(14d)
| where EventID == 4625 and (Status =~ "0xc0000071" or SubStatus =~ "0xc0000071")
| summarize Attempts = count() by bin(TimeGenerated, 1h), TargetUserName, IpAddress, LogonType
| order by TargetUserName asc, TimeGenerated asc

For each account, decide which of three cases it is. A person: have them change the password normally. A service or task: find the host from IpAddress / WorkstationName, update the stored credential, and review whether the account should have an expiring password at all. A dormant account: disable it, and check whether the same source also produced failures for other accounts before you close the ticket. If you are working from an exported Security log rather than a SIEM, the free browser analyzer groups 4625 events by source and status code.

Related codes

  • 0xC000006A STATUS_WRONG_PASSWORD The account exists and the password was wrong. One event is a typo; many against one account is brute force; one source failing across many real accounts is password spraying.
  • 0xC000006D STATUS_LOGON_FAILURE The logon was rejected as invalid without saying which half was wrong — it is the general failure result that event 4625 usually carries in Status, with the real reason in Sub Status.
  • 0xC0000072 STATUS_ACCOUNT_DISABLED Windows refused the logon because the account is disabled — the name exists, so the useful question is who or what still tries to use it, from where, and why.
  • 0xC0000234 STATUS_ACCOUNT_LOCKED_OUT The account is locked because the lockout threshold was reached; every further attempt fails with this code until the lockout ends or an administrator unlocks it.
  • 0xC000006E STATUS_ACCOUNT_RESTRICTION The username and the authentication information were valid, but a restriction on the account — Microsoft's example is time-of-day restrictions — stopped the logon before it succeeded.
  • 0xC0000070 STATUS_INVALID_WORKSTATION The account is restricted to a list of computers it may log on from, and this attempt came from one that is not on the list, so Windows rejected it because of where it came from rather than because of the name or the password.

Detect it

MITRE ATT&CK: T1078

Frequently Asked Questions

Does 0xC0000071 mean the password that was typed was correct?
The event does not say. Microsoft's NTSTATUS text for 0xC0000071 is only "The user account password has expired", which describes the state of the account's password rather than the value that was presented. A simple mismatch against an existing account is normally recorded as 0xC000006A, so 0xC0000071 points at the expiry rather than at a wrong entry — but treat that as a strong hint, not a proof, when you are deciding whether a credential is known to an attacker.
Why does a service account produce 0xC0000071?
Because its password is subject to an expiry just like a user's. If the account was never flagged to keep its password indefinitely, or a password policy was applied or tightened, the stored secret in the service, scheduled task or mapped drive keeps being presented after the expiry date and every attempt fails. The giveaway is a steady, machine-like cadence of failures from the same internal host rather than a handful of attempts around someone's working hours.
What is the Kerberos equivalent of 0xC0000071?
On a domain controller the same situation appears as Kerberos result code 0x17, KDC_ERR_KEY_EXPIRED, where the password has expired and must be changed before a ticket is issued. See [0x17](/reference/kerberos-failure-codes/0x17/). If authentication went through NTLM instead, the domain controller records the NTSTATUS value in the Error Code field of event 4776.
Should an expired-password failure from an external address worry me?
Yes, enough to check it. An expired password usually means the account is stale or half-decommissioned, so an attempt from an address or workstation the account has never used suggests someone is working through a list of old credentials. Look at the Logon Type, the source address and whether the same source produced failures for other accounts, then confirm whether the account should still exist at all.

Find 0xC0000071 in your own logs

Load a Windows Security event export into the free browser analyzer to group failures by account, source IP and status code (nothing is uploaded), or run it through LogTriage for threat-intel enrichment and an AI-written incident report.

Investigating something right now? LogTriage checks every address in the file against threat intelligence and writes the incident report: 10 analyses a month free. The incident pack adds the in-depth AI analysis and files up to 100 MB: 25 analyses for $19, one payment, no subscription. Pricing