Detect Password Spraying in Google Chronicle
Adversaries may use a single or small list of commonly used passwords against many different accounts to attempt to acquire valid account credentials. Password spraying uses one password (e.g. 'Password01'), or a small list of commonly used passwords, that may match the complexity policy of the domain. Logins are attempted with that password against many different accounts on a network to avoid account lockouts that would normally occur when brute forcing a single account with many passwords. This technique is deliberately throttled to avoid triggering per-account lockout thresholds — the defining characteristic that distinguishes spraying from brute force (T1110.001). Adversaries including APT28, APT29, HAFNIUM, Storm-0940, Chimera, and APT33 have used this technique at scale against OWA, Microsoft 365, VPN portals, SSH, RDP, SMB, and LDAP. Slow-spray variants (approximately 4 attempts per account per hour) are specifically designed to evade detection thresholds, and Kerberos-based spraying is used to avoid generating the high-visibility Event ID 4625 typically alerted on.
MITRE ATT&CK
- Tactic
- Credential Access
- Technique
- T1110 Brute Force
- Sub-technique
- T1110.003 Password Spraying
- Canonical reference
- https://attack.mitre.org/techniques/T1110/003/
YARA-L Detection Query
rule password_spraying_t1110_003 {
meta:
author = "df00tech Detection Engineering"
description = "Password spraying: single source IP with authentication failures against 10+ distinct accounts in a 30-minute window"
mitre_attack_tactic = "TA0006 - Credential Access"
mitre_attack_technique = "T1110.003 - Password Spraying"
severity = "HIGH"
confidence = "HIGH"
false_positives = "Corporate NAT gateways, identity proxy services, authorized red team engagements"
version = "1.0"
events:
$e.metadata.event_type = "USER_LOGIN"
$e.security_result.action = "BLOCK"
$ip = $e.principal.ip
$user = $e.target.user.userid
not $ip = "127.0.0.1"
not $ip = "::1"
$ip != ""
$user != ""
not $user = "ANONYMOUS LOGON"
match:
$ip over 30m
condition:
#e >= 10 and #user >= 10
outcome:
$risk_score = max(85)
$attacker_ip = array_distinct($e.principal.ip)
$affected_accounts = array_distinct($e.target.user.userid)
$target_applications = array_distinct($e.target.application)
$source_hostnames = array_distinct($e.principal.hostname)
$total_failure_count = count($e.metadata.id)
} Chronicle YARA-L 2.0 rule detecting password spraying by matching USER_LOGIN events with action=BLOCK (authentication failure), grouping by source IP over a 30-minute sliding window, and triggering when the source IP targets 10 or more distinct user accounts. The `#user >= 10` condition counts distinct values of the `$user` variable bound in the events block — this is the YARA-L 2.0 mechanism for distinct-value thresholding. Covers Google Workspace, Azure AD, Okta, Windows Security Events, and on-premises AD forwarded via Chronicle agent. Note: internal RFC1918 sources are intentionally not excluded — insider threats and compromised internal hosts are valid spray vectors. Add `not re.regex($ip, \`^(10\\.|172\\.(1[6-9]|2[0-9]|3[01])\\.|192\\.168\\.)\`)` to the events block to restrict alerts to external sources only.
Data Sources
Required Tables
False Positives & Tuning
- Cloud-based zero-trust network access proxies (Zscaler, Prisma Access, Cloudflare Access) where the entire workforce authenticates through a small pool of fixed gateway IPs — failed authentications from many users accumulate under infrastructure addresses and may cross the 10-account threshold during routine credential expiry
- Identity federation services (Azure AD B2C, ADFS proxy, Shibboleth SP) that forward authentication attempts on behalf of users, presenting the service's IP rather than the end-user's source address in UDM principal.ip
- Authorized red team engagements using Chronicle-visible identity providers as spray targets — cross-reference alert timestamp against the security team's active test calendar before escalating to incident response
Other platforms for T1110.003
Testing Methodology
Validate this detection against 4 adversary techniques from Atomic Red Team. Each test below lists the behaviour to exercise and the telemetry you should expect to see. Executable commands and cleanup steps are available with Pro.
- Test 1Local Account Password Spray via Net Use (Windows)
Expected signal: Windows Security Event ID 4625 — one per iteration with LogonType=3, TargetUserName=each account in the list, SubStatus=0xC0000064 (unknown user) for non-existent accounts or 0xC000006A (wrong password) for existing accounts, IpAddress=127.0.0.1. SecurityEvent 4648 (Explicit Credential Logon) may also fire. With 12 accounts in the list, DistinctAccounts=12 will exceed the default threshold of 10.
- Test 2Azure AD Password Spray via PowerShell OAuth Token Request
Expected signal: Azure AD SigninLogs entries (visible in Azure Portal > Azure AD > Sign-in logs within 5-15 minutes) for each request: ResultType=50126 (InvalidUserNameOrPassword) or 50057 (account disabled), IPAddress=your public egress IP, AppDisplayName='Microsoft Azure PowerShell' or 'Azure Active Directory PowerShell', UserAgent containing 'PowerShell'. With 11 accounts in the list, DistinctAccounts=11 exceeds the SprayAccountThreshold=10.
- Test 3Kerberos Password Spray via Rubeus (Low-Visibility Technique)
Expected signal: Windows Security Event ID 4771 on the Domain Controller for each domain account targeted. Fields: Client Address = spray source IP, Account Name = target username, Service Name = krbtgt, Failure Code = 0x18 (KRB_AP_ERR_BAD_INTEGRITY — wrong password) or 0x6 (KDC_ERR_C_PRINCIPAL_UNKNOWN — unknown account). Event ID 4768 (TGT Request) may appear for accounts that receive AS-REQ. The /delay 2000ms and /jitter 30% simulate slow-spray behavior. Main 4625-based detection does NOT fire, demonstrating the detection gap this technique exploits.
- Test 4SMB Password Spray via CrackMapExec Against Subnet
Expected signal: Windows Security Event ID 4625 on the target host for each account attempted: LogonType=3 (Network), SubStatus=0xC000006A (wrong password) for valid accounts or 0xC0000064 (unknown user) for invalid accounts, IpAddress=attacker IP, WorkstationName=blank (common CrackMapExec behavior), AuthenticationPackageName=NTLM. With 10 accounts, DistinctAccounts=10 meets the default detection threshold. EventID 7045 may appear if CrackMapExec uses service-based execution. Sysmon Event ID 3 (Network Connection) visible on attacking host.
References (13)
- https://attack.mitre.org/techniques/T1110/003/
- https://www.trimarcsecurity.com/single-post/2018/05/06/Trimarc-Research-Detecting-Password-Spraying-with-Security-Event-Auditing
- https://www.microsoft.com/en-us/security/blog/2024/10/31/chinese-threat-actor-storm-0940-uses-credentials-from-password-spray-attacks-from-a-covert-network/
- http://www.blackhillsinfosec.com/?p=4645
- https://www.us-cert.gov/ncas/alerts/TA18-086A
- https://github.com/dafthack/MSOLSpray
- https://github.com/dafthack/MailSniper
- https://github.com/ropnop/kerbrute
- https://github.com/GhostPack/Rubeus
- https://learn.microsoft.com/en-us/azure/active-directory/reports-monitoring/concept-sign-ins
- https://learn.microsoft.com/en-us/azure/active-directory/reports-monitoring/reference-sign-ins-error-codes
- https://www.microsoft.com/en-us/security/blog/2021/06/25/nobelium-attacking-more-than-150-organizations/
- https://github.com/byt3bl33d3r/CrackMapExec
Unlock Pro Content
Get the full detection package for T1110.003 including response playbook, investigation guide, and atomic red team tests.