Brute Force Attacks: Splunk Detection and Analytics
One of the longest-standing and most common challenges to both information security and web development teams is the brute force attack. Although this form of attack has been around for many years, it is still one of the most popular and widely used password-cracking methods. In terms of impact, brute force attacks are a very serious threat capable of affecting millions of accounts. If these attacks are not detected and addressed in a timely manner they can lead to theft of intellectual property and personally identifiable information, significant financial losses, and even damage to a business’s reputation.
In this blog, we will explore some of the common brute force detection use cases that can be easily built using Splunk.
Brute Force: Definition
A brute force attack is a trial and error method used to discover a password by systematically trying every possible combination of letters, numbers, and symbols until the correct combination is found. Various types of automated software and cracking tools are used to generate a large number of consecutive guesses. Some brute force attacks utilize dictionary words and lists of common passwords in order to increase their success rates.
The only true requirement is having the necessary data ingested (and correctly parsed) in your Splunk Enterprise deployment. These use cases can be built using the standard Splunk SPL and presented as dashboards or saved as scheduled searches that trigger alerts. Of course, if your organization utilizes Splunk Enterprise Security, these use cases would work great as correlation searches that trigger notable events.
This, of course, will be dependent upon the your organization's specific use case goals and the technologies and applications being used. In general, web application logs are a good place to start. Specifically, these logs should include all authentication related events for your website/web applications. At a minimum, they should contain the timestamp, source IP, user account, type of authentication event and the result. Additionally having the channel (web or mobile) and device information (cookie or hardware ID) can provide extra insight for reporting.
Example Web Application Log Format (CA SiteMinder)
[categoryid][AuthType][reason][hostname][timestamp][agentname][sessionid][userid, ou1, ou2, dc1, dc2][domainoid][realmname][realmoid][client_ip][resource][action][authdirname][authdirserver][authdirnamespace][transactionid][statusmsg][domainname][impersonatorname][impersonatordirname][objname][objoid][fielddesc]
[Auth][AuthReject][host.abcd.org][11/Apr/2017:09:47:31 -0400][signonagent][asdf][uid=abcd-efgh-1234-5678,ou=customers,ou=people,dc=abcd,dc=org][99-88a-77b-66c][abcd][00-11z-22x-33y][12.345.67.89][/abcd/][GET][abcd Login Directory][ldap1.abcd.org:9800][LDAP:][abcd]
Brute Force IP Detected
This use case is intended to detect source IPs that are exceeding a high threshold of rejected and/or invalid logins.
index=web_idx sourcetype=web_st (AuthType="AuthInvalid" OR AuthType="AuthReject" OR AuthType="AuthAccept OR AuthType="AuthChallenge" OR AuthType="Lockout") | stats count as total_count count(eval(AuthType=="AuthInvalid")) as invalid_count count(eval(AuthType=="AuthReject")) as reject_count count(eval(AuthType =="AuthAccept")) as accept_count count(eval(AuthType=="AuthChallenge")) as challenge_count count(eval(AuthType==“Lockout")) as lockout_count by client_ip | eval invalid_thresh = <value> | eval reject_thresh = <value> | where invalid_count > invalid_thresh OR reject_count > reject_thresh | eval percent_denied = round(((reject_count + attempt_count) / total_count) * 100, 2) | sort 0 - total_count percent_denied | rename client_ip as "Client IP", total_count as "Total Count", invalid_count as "Invalid Username Attempts", reject_count as "Valid Username / Invalid Password Attempts", accept_count as "Successful Logins", challenge_count as "Amount of Challenges", lockout_count as “Locked Attempts”, percent_denied as "Percent Denied"
Brute Force Increase Percentage
This use case detects large increase percentages in various brute force statistics over different periods of time (the last 30 minutes, compared to a 30 minute period 6 hours ago).
index=web_idx sourcetype=web_st (AuthType="AuthInvalid" OR AuthType="AuthReject" OR AuthType="Lockout") earliest=-30m@m latest=@m | stats count as total_fail_count count(eval(AuthType=="AuthInvalid")) as invalid_count count(eval(AuthType=="AuthReject")) as reject_count count(eval(AuthType==“Lockout")) as lockout_count earliest(_time) as earliest_time latest(_time) as latest_time | convert timeformat="%Y-%m-%d %T" ctime(earliest_time) AS earliest_time | convert timeformat="%Y-%m-%d %T" ctime(latest_time) AS latest_time | appendcols [ search index=web_idx sourcetype=web_st (AuthType="AuthInvalid" OR AuthType="AuthReject" OR AuthType="Lockout") earliest=-390m@m latest=-360m@m | stats count as 6h_total_fail_count count(eval(AuthType=="AuthInvalid")) as 6h_invalid_count count(eval(AuthType=="AuthReject")) as 6h_reject_count count(eval(AuthType==“Lockout")) as 6h_lockout_count earliest(_time) as 6h_earliest_time latest(_time) as 6h_latest_time | convert timeformat="%Y-%m-%d %T" ctime(6h_earliest_time) AS 6h_earliest_time | convert timeformat="%Y-%m-%d %T" ctime(6h_latest_time) AS 6h_latest_time ] | eval pct_total_fail_increase = round(((total_fail_count - 6h_total_fail_count) / 6h_total_fail_count) * 100,0) | eval pct_invalid_attempt_increase = round(((invalid_attempt_count - 6h_invalid_attempt_count) / 6h_invalid_attempt_count) * 100,0) | eval pct_lockout_increase = round(((lockout_count - 6h_lockout_count) / 6h_lockout_count) * 100,0) | eval pct_reject_increase = round(((reject_count - 6h_reject_count) / 6h_reject_count) * 100,0) | table pct_total_fail_increase pct_invalid_attempt_increase pct_lockout_increase pct_reject_increase earliest_time latest_time total_fail_count invalid_attempt_count lockout_count reject_count 6h_earliest_time 6h_latest_time 6h_total_fail_count 6h_invalid_attempt_count 6h_lockout_count 6h_reject_count | eval total_fail_thresh = <value> | eval perc_inc_thresh = <value> | where (total_fail_count > total_fail_thresh) AND ((pct_total_fail_increase > perc_inc_thresh) OR (pct_invalid_attempt_increase > perc_inc_thresh) OR (pct_lockout_increase > perc_inc_thresh))
Brute Force Potentially Compromised Accounts
This use case detects accounts that have shown brute force behavior (high amount of failed logins with one successful login).
index=web_idx sourcetype=web_st (AuthType="AuthReject" OR AuthType="Lockout" OR AuthType="AuthAccept") | stats count as total_count count(eval(AuthType=="Lockout")) as lockout_count count(eval(AuthType=="AuthReject")) as reject_count count(eval(AuthType=="AuthAccept")) as accept_count dc(client_ip) as count_ips by userid | eval total_denied = lockout_count + reject_count | eval denied_thresh = <value> | search accept_count>0 AND total_denied>denied_thresh | eval percent_denied = round((total_denied / total_count) * 100, 2) | table userid percent_denied total_count total_denied accept_count lockout_count reject_count count_ips | sort 0 - percent_denied total_denied
These use cases are only a few examples of the various types of searches that can be created in Splunk for brute force detection. Reach out to us here or by sending an email to firstname.lastname@example.org if your organization is looking for assistance in improving its security detection and analytics using Splunk.