0x18: Kerberos Pre-Authentication Failed (KDC_ERR_PREAUTH_FAILED) in 4771
Published
The domain controller rejected the pre-authentication data sent with a ticket request — Microsoft's listed cause is that the wrong password was provided; repeated 0x18 events for one account are a brute-force indicator.
Did the attacker have the password? No — the password (or username) was wrong.
Microsoft's definition
KDC_ERR_PREAUTH_FAILED
Pre-authentication information was invalid
Source: Microsoft event 4771 Kerberos error codes · checked against the published table on every build · look up another code
Where it appears
- Event 4771 (Kerberos pre-authentication failed) on domain controllers: the Failure Code field
- Microsoft Sentinel / Log Analytics: the Status column of the SecurityEvent table (EventID 4771)
What it means
Kerberos pre-authentication is the step where the client proves it knows the account’s key — normally by encrypting a timestamp with a key derived from the password — before the KDC will issue a TGT. Failure code 0x18, KDC_ERR_PREAUTH_FAILED, means the domain controller could not validate that proof. Microsoft’s “possible causes” text for this code is blunt: “The wrong password was provided.”
The code is carried by event 4771 (Kerberos pre-authentication failed) in the Failure Code field, and only there: the 4768 documentation states that event 4768 “doesn’t generate for Result Codes: 0x10 and 0x18. Event ‘4771: Kerberos pre-authentication failed.’ generates instead.” Microsoft notes for both events that “This event generates only on domain controllers”, so you collect them from DCs, not from member servers or workstations.
Two fields decide how you read the event. Client Address is “IP address of the computer from which the TGT request was received”, written as an IPv6 or IPv4 address, as ::ffff:IPv4_address, or as ::1 for localhost — strip the ::ffff: prefix before comparing against your IPv4 inventory. Account Name (TargetUserName) is the principal whose key failed to validate. Event 4771 also carries ServiceName (typically krbtgt/DOMAIN), IpPort, and certificate fields; for smart-card problems Microsoft points at failure code 0x10 instead, which “can help you to more quickly identify smart-card related problems with Kerberos authentication.”
Is it an attack?
| Pattern | Reading |
|---|---|
| One or two 0x18 for a user, then a 4768 with result 0x0 from the same address | Mistyped password, then a correct one |
| 0x18 for one account every few minutes from one internal host, around the clock | A saved credential — service, scheduled task, mapped drive or mobile mail client — still using the pre-reset password |
| Dozens of 0x18 for one account in minutes | Brute force. Microsoft’s guidance: “N events in last N minutes. This issue can indicate a brute-force attack on the account password, especially for highly critical accounts.” |
| One client address, 0x18 across many distinct accounts, few attempts each | Password spraying over Kerberos |
| 0x18 mixed with 4768 result code 0x6 from the same source | Spraying with a guessed name list — 0x6 is “The username doesn’t exist” |
Kerberos spraying does not have to touch a member server, so it may never produce a 4625. Detections built only on 4625 — such as the Windows 4625 password-spray rule — should be paired with a 4771 view of the domain controllers. In event 4625 and in NTLM credential validation (event 4776), the matching Windows status is 0xC000006A — a wrong password for a real account.
How to investigate
Start by ranking client addresses by how many distinct accounts each one failed against:
SecurityEvent
| where TimeGenerated > ago(1d)
| where EventID == 4771 and Status =~ "0x18"
| extend ClientIp = replace_string(IpAddress, "::ffff:", "")
| summarize Failures = count(), Accounts = dcount(TargetUserName),
AccountList = make_set(TargetUserName, 20),
FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated)
by ClientIp, Computer
| order by Accounts desc, Failures desc
Then look for bursts against a single account, the shape Microsoft calls out for brute force:
SecurityEvent
| where TimeGenerated > ago(1d)
| where EventID == 4771 and Status =~ "0x18"
| extend ClientIp = replace_string(IpAddress, "::ffff:", "")
| summarize Failures = count() by bin(TimeGenerated, 10m), TargetUserName, TargetDomainName, ClientIp
| where Failures >= 10
| order by TimeGenerated asc
The question that sets severity is whether any of those attempts eventually succeeded — a TGT issued with result code 0x0 from the same address, after the failures:
let preauthFails = SecurityEvent
| where TimeGenerated > ago(1d) and EventID == 4771 and Status =~ "0x18"
| extend ClientIp = replace_string(IpAddress, "::ffff:", "")
| summarize FailCount = count(), LastFail = max(TimeGenerated) by TargetUserName, ClientIp;
SecurityEvent
| where TimeGenerated > ago(1d) and EventID == 4768 and Status =~ "0x0"
| extend ClientIp = replace_string(IpAddress, "::ffff:", "")
| join kind=inner preauthFails on TargetUserName, ClientIp
| where TimeGenerated > LastFail
| project TimeGenerated, Computer, TargetUserName, ClientIp, IpPort, FailCount
A success following a burst means the password was guessed: reset the account, pull the service tickets requested afterwards (event 4769 from the same source), and isolate the client if it is a domain-joined host. If the source is one of your own servers and the cadence is regular, hunt for the stored credential instead. Exported Security logs can also be grouped by account and client address in the free browser log analyzer.
Related codes
- 0x6 KDC_ERR_C_PRINCIPAL_UNKNOWN The domain controller could not find the requested account in its database — the name does not exist, and a run of these from one source is account enumeration.
- 0x10 KDC_ERR_PADATA_TYPE_NOSUPP
- 0x12 KDC_ERR_CLIENT_REVOKED The domain controller refused the ticket request because the account itself is barred from logging on — Microsoft's examples are an account that is disabled, expired or locked out — rather than because of anything about the password.
- 0x17 KDC_ERR_KEY_EXPIRED The domain controller refused the ticket request because the account's password has expired and has to be changed before Kerberos will issue a ticket — routine for people, but a problem worth chasing when it hits service accounts or comes from an address you do not recognise.
- 0x20 KRB_AP_ERR_TKT_EXPIRED
Detect it
Frequently Asked Questions
- Does failure code 0x18 mean the account name is valid?
- In practice yes. Microsoft's 4768 documentation lists result code 0x6 as "The username doesn't exist", which is the code you get for an unknown principal; 0x18 is returned after the KDC has found the account and rejected the pre-authentication data it was sent. So the name matched a real security principal and the credential was the problem.
- Why do I see 4771 but no 4768 for these failures?
- Microsoft states that event 4768 "doesn't generate for Result Codes: 0x10 and 0x18. Event '4771: Kerberos pre-authentication failed.' generates instead." If you hunt only on 4768 you will miss every wrong-password attempt against a domain account, so include 4771 in any Kerberos brute-force or spray detection.
- How many 0x18 events should raise an alert?
- Microsoft's monitoring guidance gives a shape rather than a number — "if you see, for example N events in last N minutes. This issue can indicate a brute-force attack on the account password, especially for highly critical accounts." Tune per environment, treating a single source failing against many distinct accounts, or any burst against an administrative or service account, as the highest-value signal.
- What causes 0x18 when there is no attack?
- Stale credentials are the usual answer: a scheduled task, service, mapped drive, saved RDP connection, or a phone or mail client still presenting the old password after a reset. These repeat on a regular interval from one known internal host and always target the same account, which distinguishes them from the irregular, multi-account pattern of an attack.
Find 0x18 in your own logs
Load a domain controller Security event export into the free browser analyzer to group Kerberos failures by account, source address and failure code (nothing is uploaded), or run it through LogTriage for threat-intel enrichment and an AI-written incident report.