The below will detect a form of brute force which most will miss. Whereas other scripts detect multiple logins against a single account, they fail to detect 4 failed logins against 40 accounts.
This first checks for all accounts having an account login failure of 4 or more, it then checks for the quantity of accounts that have failed by 4 or more (5 in the below example). So if someone attempts to login with 4 or more different passwords unsuccessfully on 5 or more accounts, the alarm will trip.
sourcetype=windows EventCode=4625 OR EventCode=4624 | bin _time span=5m as minute | rex "Security ID:\s*\w*\s*\w*\s*Account Name:\s*(?<username>.*)\s*Account Domain:" | stats count(Keywords) as Attempts, count(eval(match(Keywords,"Audit Failure"))) as Failed, count(eval(match(Keywords,"Audit Success"))) as Success by minute username | where Failed>=4 | stats dc(username) as Total by minute | where Total>5
I like it, but these are the modifications I made to resolve some issues I had and output more information about the accounts involved. Cleans up the time also.
sourcetype=wineventlog EventCode=4625 OR EventCode=4624
| bin _time span=5m as minute
| stats count(Keywords) as Attempts,
count(eval(match(Keywords,”Audit Failure”))) as Failed,
count(eval(match(Keywords,”Audit Success”))) as Success by minute user
| where Failed>=4
|stats values(user) AS userlist dc(user) AS Total BY minute
| where Total>5
| eval minute=strftime(minute,”%m/%d/%y %H:%M:%S”)
How to group by source address? Thank you.
You just need to drill into each user independently and finish with a
| stats count by host
| stats count by Source_Network_Address
It depends where its failing.
| rex “Account For Which Logon Failed:\s*Security ID:\s*(?.*)\s*Account Name:\s*(?.*)\s*Account Domain:\s*(?.*)\s*Fail”
| stats count by host accountName
| sort count desc
This will tell you which account has failed and the machine it failed on. If it was attempting to connect to a network resource change the stats count to “stats count by Source_Network_Address accountName” and that should give you the offending host.
Brute for login attempts against “Known Usernames” vs “Username Guessing” are uniquely different circumstances… because if the “Username” is Not Valid — the Event Code 4625 (Failed Login) can tell you this much more easily via the SubStatus code. Thus significantly reducing false positives an search overhead for other failed logons.
0xC0000064 = user name does not exist
However if you have “Known Usernames” and “Password Guessing”… then the general use case for your search (i.e. multiple failed logon events) is still valid.