Splunk Search

Detect Brute Force auth success after multiple failures

gnoriega
Explorer

Hi,

I'm trying to detect brute force activity by detecting multiple auth failures followed by success.  I started with the following search which works and shows when there has been over 20 failures and at least 1 success, but the success can happen anywhere during the search period. It could be 1 success followed by 20 failures or the success can happen in the middle.

index=main sourcetype="wineventlog" (EventCode=4624 OR EventCode=4625) Logon_Type IN (2,3,8,10,11) user!=*$
  | bin _time span=5m as Time 
  | stats count(eval(match(Keywords,"Audit Failure"))) as Failed,
       count(eval(match(Keywords,"Audit Success"))) as Success,
       count(eval(match(lower(Status),"0xc0000224"))) as "PwChangeReq",
       count(eval(match(lower(Sub_Status),"0xc0000071"))) as "Expired",
       count(eval(match(lower(Status),"0xc0000234"))) as "Locked" by Time user src_ip
  | where Success>0 AND Failed>=20 AND PwChangeReq=0 AND Locked=0 AND Expired=0

 

I need the query to only trigger if the success happens after 20 failures. I found some examples using streamstats so I created the following search but it's not working properly because the *reset_after* clears the failure_count for all src_ip. Therefore as long as there is 1 success from any IP address, the failure_count gets reset and I'm not seeing the failure count reach 20.

 

index=main sourcetype="wineventlog" EventCode IN (4624,4625) Logon_Type IN (2,3,8,10,11) 
 | eval action=if(match(Keywords,"Audit Failure"),"failed","success")
 | reverse
 | streamstats window=0 current=true reset_after="("action==\"success\"")" count as failure_count by src_ip
 | where action="success" and failure_count > 20
 | table _time, user, src_ip, action, failure_count

 

Is streamstats the way to go? Or how can I setup a query to detect the success after more than 20 failures?

Labels (1)
0 Karma
1 Solution

gnoriega
Explorer

I found the solution. I had to replace line:

| reverse

with:

| sort src_ip _time

So that streamstats resets the counter each time the action is "success" for each src_ip.

Working code:

index=main sourcetype="wineventlog" EventCode IN (4624,4625) Logon_Type IN (2,3,8,10,11) 
 | eval action=if(match(Keywords,"Audit Failure"),"failed","success")
 | sort src_ip _time
 | streamstats window=0 current=true reset_after="("action==\"success\"")" count as failure_count by src_ip
 | where action="success" and failure_count > 20
 | table _time, user, src_ip, action, failure_count

 

View solution in original post

0 Karma

to4kawa
Ultra Champion

sample:

 

| makeresults count=100000
| eval status="failed", count=1
| eval status = if( count = random() % 20,"success",status), user=mvindex(split("testA,testB,testC",","),random() % 3)
| eval src_ip=mvindex(split("X.X.X.X,Y.Y.Y.Y",","),random() % 2)
| accum count
| eval _time=_time - count 
| sort 0 _time
| streamstats global=f count(eval(status="failed")) as Failed by user src_ip reset_before="status=\"success\""
| table _time src_ip user status Failed
| where Failed > 20

 

try streamstats global=f ...
The data is randomly generated, so the results may not be available.

0 Karma

gnoriega
Explorer

I tried using global=f but I get the same behavior. Once a success is seen and the stats reset they are reset for all src_ip.

0 Karma

to4kawa
Ultra Champion

sample:

| makeresults count=10000
| eval status="failed", count=1
| accum count
| eval status = if( count % 10 = 1,"success",status), user=mvindex(split("testA,testB,testC",","),random() % 3)
| eval src_ip=mvindex(split("X.X.X.X,Y.Y.Y.Y",","),random() % 2)
| eval _time=_time - count 
| sort 0 _time
| streamstats global=f count(eval(status="success")) as session by user src_ip
| streamstats count(eval(status="failed")) as failed_count by session user src_ip
| table _time src_ip user status failed_count
| sort user src_ip _time

how about this?

0 Karma

gnoriega
Explorer

@to4kawa thanks but I'm still not getting the behavior I need. I do see that it properly keeps count of each "session" and only resets the failure_count when there is a success for that user,src_ip which is better.

The issue I have is that I need to filter when there is a success and the failed count had reached over a certain amount. So if for example my threshold is 15, in the screenshot below I see that the success occurred after 19 failures which is what I need to detect. But I don't have a way to detect it.

With the previous "reset_after" command, the row with the success had the failed_count total, so I could do:

| where failed_count>15 AND status="success"

 

Would there be a way for the row with the status="success" to have the failed_count reached so far before resetting?

streamstats1.PNG

0 Karma

gnoriega
Explorer

I found the solution. I had to replace line:

| reverse

with:

| sort src_ip _time

So that streamstats resets the counter each time the action is "success" for each src_ip.

Working code:

index=main sourcetype="wineventlog" EventCode IN (4624,4625) Logon_Type IN (2,3,8,10,11) 
 | eval action=if(match(Keywords,"Audit Failure"),"failed","success")
 | sort src_ip _time
 | streamstats window=0 current=true reset_after="("action==\"success\"")" count as failure_count by src_ip
 | where action="success" and failure_count > 20
 | table _time, user, src_ip, action, failure_count

 

0 Karma
Get Updates on the Splunk Community!

Extending Observability Content to Splunk Cloud

Watch Now!   In this Extending Observability Content to Splunk Cloud Tech Talk, you'll see how to leverage ...

More Control Over Your Monitoring Costs with Archived Metrics!

What if there was a way you could keep all the metrics data you need while saving on storage costs?This is now ...

New in Observability Cloud - Explicit Bucket Histograms

Splunk introduces native support for histograms as a metric data type within Observability Cloud with Explicit ...