Hello,
I need some help with adjusting an alert for detecting a password spray attack using Auth0 logs in Splunk. What I'm looking for is to not just catch the password spray itself but also get alerted when there's a successful login from the same source right after the spray attempt.
Currently, I have the following query that detects password spray attempts by identifying IPs with more than 10 unique failed login attempts within a 5-minute window:
index = auth0 (data.type IN ("fu", "fp"))
| bucket span=5m _time
| stats dc(data.user_name) AS unique_accounts values(data.user_name) as tried_accounts values(data.client_name) as clientName values(data.type) as failure_reason by data.ip
| where unique_accounts > 10
Is there an way to adjust this query to also detect and alert on successful logins (data.type = "s") from the same IPs that performed the spray attack? I am looking to create an alert that indicates a successful login following the spray, so we can respond accordingly.
Log Event Type Codes (auth0.com)
Thank you
You can use the sub-search method where the sub-search would detect password spray and pass the attempted source_ip and username combination to the main search which detects the successful logins.
index = auth0 <successful login condition here>
[
search index = auth0 <login attempts condition here>
| eventstats dc(data.user_name) as unique_accounts by data.ip
| where unique_accounts>10
| table data.ip,data.user_name
]
| stats count by data.user_name,data.ip,<result,_time>
#NOTE: <...> variable block
You could do something like this
index = auth0 (data.type IN ("fu", "fp", "s"))
| bucket span=5m _time
| stats dc(eval(if('data.type'="s", null(), 'data.user_name'))) AS unique_failed_accounts
dc(eval(if('data.type'="s", 'data.user_name', null()))) AS unique_successful_accounts
values(eval(if('data.type'="s", null(), 'data.user_name'))) as tried_accounts
values(eval(if('data.type'="s", 'data.user_name', null()))) as successful_accounts
values(data.client_name) as clientName
values(eval(if('data.type'="s", null(), 'data.type'))) as failure_reason
latest(eval(if('data.type'="s", 'data.user_name', null()))) as last_successful_account
max(eval(if('data.type'="s", _time, null()))) as last_successful_time
max(eval(if('data.type'="s", null(), _time))) as last_failed_time
by data.ip
| where unique_failed_accounts > 10
Then you can see the latest failed time, successful time and failed and successful accounts and make any decisions needed.
What you're essentially after is the eval() test inside the stats to test what to collect. Make sure you wrap the field names in single quotes in that test, as it's an eval statement and the field names contain . characters.
I've implemented your suggested logic and enhanced it to detect password spray attempts and also alert when there's a successful login from the same source following a spray attempt.
Specifically, I added conditions to detect successful logins from the same source following a spray attempt. Here’s a summary of the changes:
dc(eval(if('data.type'="s", 'data.user_name', null()))) AS unique_successful_accounts
These changes ensure that the query not only detects password spray attempts but also alerts when there's a successful login following the spray attempt.
Thank you so much for your help!