Detection rules › Splunk

SSH Brute Force detection

Group by
_time, host, res
Source
github.com/anvilogic-forge/armory

Detect possible brute force attempts using SSH in Unix

MITRE ATT&CK coverage

References

Telemetry coverage

Rule body

id: '5453.5608'
title: SSH Brute Force detection
description: 'Detect possible brute force attempts using SSH in Unix. - Threat Actor
  Association: 8220 Gang, Daixin Team, Lancefly, Sandworm (UAC-0165)'
logic_format: Splunk
logic: '`get_endpoint_data` `get_endpoint_data_unix` TERM(ssh) TERM(res=failed) TERM(type=USER_AUTH)
  | rex field=_raw "[^\w]"(?<process_name>"(ssh)")"[^\w]"| eval src_ip=mvfilter( NOT
  match(addr,"\?")), user=mvappend("",acct,user,auid,uid), user=mvfilter(NOT match(user,"\?|\(unknown\)|\d+|unset|^\s$|^$")),
  user_id=mvappend("",auid,uid), user_id=mvfilter(match(user_id,"\d+")) | table _time,
  host, user process, process_*, res, src_ip, user_id | bin _time span=10sec | eventstats
  count by host, _time, res | stats values(*) as * by host, _time, res | where count
  > 3 | bin _time span=10min | stats values(*) as * list(count) as count_list sum(count)
  as count_span by host, _time, res | where mvcount(count_list) > 1 OR count_span
  > 20 | rename count_span as count| lookup dnslookup clientip as src_ip OUTPUT clienthost
  as src_dns | iplocation prefix="src_" src_ip | rename src_Country as src_country '
techniques:
- credential-access:brute force:password guessing
- credential-access:brute force:password spraying
technique_id:
- T1110.001
- T1110.003
data_category:
- Process command-line parameters
- Linux audit logs
references:
- https://linuxhint.com/bruteforce_ssh_ftp/

Stages and Predicates

Stage 1: search

`get_endpoint_data` `get_endpoint_data_unix` TERM(ssh) TERM(res=failed) TERM(type=USER_AUTH)

Stage 2: rex

| rex field=_raw "[^\w]"(?<process_name>"(ssh)")"[^\w]"

The parser skipped this rex command.

Stage 3: eval

| eval src_ip=mvfilter( NOT match(addr,"\?")), user=mvappend("",acct,user,auid,uid), user=mvfilter(NOT match(user,"\?|\(unknown\)|\d+|unset|^\s$|^$")), user_id=mvappend("",auid,uid), user_id=mvfilter(match(user_id,"\d+"))

Stage 4: table

| table _time, host, user process, process_*, res, src_ip, user_id

Stage 5: bucket

| bin _time span=10sec

Stage 6: eventstats

| eventstats count by host, _time, res

Stage 7: stats

| stats values(*) as * by host, _time, res

Stage 8: where

| where count > 3

Stage 9: bucket

| bin _time span=10min

Stage 10: stats

| stats values(*) as * list(count) as count_list sum(count) as count_span by host, _time, res

Stage 11: where

| where mvcount(count_list) > 1 OR count_span > 20

Stage 12: rename

| rename count_span as count

Stage 13: lookup

| lookup dnslookup clientip as src_ip OUTPUT clienthost as src_dns
Lookup table
dnslookup
Key field
clientip as src_ip
Output columns
['clienthost', 'src_dns']

Stage 14: search

| iplocation prefix="src_" src_ip

Stage 15: rename

| rename src_Country as src_country

Indicators

These rows show field, operator, and value matches.

Search terms

These SPL tokens match against raw event text.

StageTerm
1ssh
1"res=failed"
1"type=USER_AUTH"
14iplocation
14src_ip