In this guest blog, Naveed Krabbe (Senior Operational Intelligence Consultant with Splunk Partner Function1) examines leveraging Splunk to detect brute force attacks. As a leader in the Operational Intelligence and Middleware space, Function1 not only designed the base architecture for some of the largest Splunk deployments in the world today, but also helped to develop the standard for enterprise class governance and data onboarding.
One of the longest-standing and most common challenges to information security and web development teams is the brute force attack. Although this form of attack has been around for many years, it remains 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 irreversible damage to a business’s reputation.
Let’s explore a few brute force detection use cases that can easily be built using Splunk.
Defining Brute Force
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. 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.
1. index=web_idx sourcetype=web_st (AuthType="AuthInvalid" OR AuthType="AuthReject" OR AuthType="AuthAccept OR AuthType="AuthChallenge" OR AuthType="Lockout") 2. | 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 3. | eval invalid_thresh =
4. | eval reject_thresh = 5. | where invalid_count > invalid_thresh OR reject_count > reject_thresh 6. | eval percent_denied = round(((reject_count + attempt_count) / total_count) * 100, 2) 7. | sort 0 - total_count percent_denied 8. | 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).
1. index=web_idx sourcetype=web_st (AuthType="AuthInvalid" OR AuthType="AuthReject" OR AuthType="Lockout") earliest=-30m@m latest=@m 2. | 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 3. | convert timeformat="%Y-%m-%d %T" ctime(earliest_time) AS earliest_time 4. | convert timeformat="%Y-%m-%d %T" ctime(latest_time) AS latest_time 5. | appendcols 6. [ search index=web_idx sourcetype=web_st (AuthType="AuthInvalid" OR AuthType="AuthReject" OR AuthType="Lockout") earliest=-390m@m latest=-360m@m 7. | 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 8. | convert timeformat="%Y-%m-%d %T" ctime(6h_earliest_time) AS 6h_earliest_time 9. | convert timeformat="%Y-%m-%d %T" ctime(6h_latest_time) AS 6h_latest_time ] 10. | eval pct_total_fail_increase = round(((total_fail_count - 6h_total_fail_count) / 6h_total_fail_count) * 100,0) 11. | eval pct_invalid_attempt_increase = round(((invalid_attempt_count - 6h_invalid_attempt_count) / 6h_invalid_attempt_count) * 100,0) 12. | eval pct_lockout_increase = round(((lockout_count - 6h_lockout_count) / 6h_lockout_count) * 100,0) 13. | eval pct_reject_increase = round(((reject_count - 6h_reject_count) / 6h_reject_count) * 100,0) 14. | 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 15. | eval total_fail_thresh =
16. | eval perc_inc_thresh = 17. | 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).
1. index=web_idx sourcetype=web_st (AuthType="AuthReject" OR AuthType="Lockout" OR AuthType="AuthAccept") 2. | 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 3. | eval total_denied = lockout_count + reject_count 4. | eval denied_thresh =
5. | search accept_count>0 AND total_denied>denied_thresh 6. | eval percent_denied = round((total_denied / total_count) * 100, 2) 7. | table userid percent_denied total_count total_denied accept_count lockout_count reject_count count_ips 8. | sort 0 - percent_denied total_denied
We’ve only covered a handful of the many types of searches that can be created in Splunk for brute force detection. Splunk makes it easy for organizations to achieve peace of mind and avoid costly security breaches. For any technical questions about this blog or to learn more about using Splunk for advanced threat detection, feel free to contact us at firstname.lastname@example.org.