1
1
name : Detect Password Spray Attempts
2
2
id : 086ab581-8877-42b3-9aee-4a7ecb0923af
3
- version : 6
3
+ version : 7
4
4
date : ' 2025-02-10'
5
5
author : Dean Luxton
6
6
status : production
@@ -13,41 +13,34 @@ description: This analytic employs the 3-sigma approach to detect an unusual vol
13
13
many different accounts to avoid detection and account lockouts. By utilizing the
14
14
Authentication Data Model, this detection is effective for all CIM-mapped authentication
15
15
events, providing comprehensive coverage and enhancing security against these attacks.
16
- search : >-
17
- | tstats `security_content_summariesonly` values(Authentication.user) AS unique_user_names
18
- dc(Authentication.user) AS unique_accounts values(Authentication.app) as app count(Authentication.user)
19
- as total_failures from datamodel=Authentication.Authentication where Authentication.action="failure"
20
- NOT Authentication.src IN ("-","unknown") by Authentication.action Authentication.app Authentication.authentication_method Authentication.dest
21
- Authentication.signature Authentication.signature_id Authentication.src sourcetype _time span=5m
22
- | `drop_dm_object_name("Authentication")`
23
- ```fill out time buckets for 0-count events during entire search length```
24
- | appendpipe [| timechart limit=0 span=5m count | table _time]
25
- | fillnull value=0 unique_accounts
26
- ``` Create aggregation field & apply to all null events```
27
- | eval counter=src+"__"+sourcetype+"__"+signature_id | eventstats values(counter)
28
- as fnscounter | eval counter=coalesce(counter,fnscounter)
29
- ``` stats version of mvexpand ```
30
- | stats values(app) as app values(unique_user_names) as unique_user_names values(total_failures)
31
- as total_failures values(src) as src values(signature_id) as signature_id values(sourcetype)
32
- as sourcetype count by counter unique_accounts _time
33
- ``` remove duplicate time buckets for each unique source```
34
- | sort - _time unique_accounts
35
- | dedup _time counter
36
- ```Find the outliers```
37
- | eventstats avg(unique_accounts) as comp_avg , stdev(unique_accounts) as comp_std
38
- by counter
39
- | eval upperBound=(comp_avg+comp_std*3)
40
- | eval isOutlier=if(unique_accounts > 30 and unique_accounts >= upperBound, 1, 0)
41
- | replace "::ffff:*" with * in src | where isOutlier=1 | foreach *
42
- [ eval <<FIELD>> = if(<<FIELD>>="null",null(),<<FIELD>>)]
43
- | table _time, src, action, app, unique_accounts, unique_user_names, total_failures,
44
- sourcetype, signature_id, counter
45
- | `detect_password_spray_attempts_filter`
46
- how_to_implement : >-
47
- Ensure in-scope authentication data is CIM mapped and the src field is populated
48
- with the source device. Also ensure fill_nullvalue is set within the macro security_content_summariesonly.
49
- This search opporates best on a 5 minute schedule, looking back over the past 70
50
- minutes. Configure 70 minute throttling on the two fields _time and counter.
16
+ search : " | tstats `security_content_summariesonly` values(Authentication.user) AS\
17
+ \ unique_user_names dc(Authentication.user) AS unique_accounts values(Authentication.app)\
18
+ \ as app count(Authentication.user) as total_failures from datamodel=Authentication.Authentication\
19
+ \ where Authentication.action=\" failure\" NOT Authentication.src IN (\" -\" ,\" unknown\" \
20
+ ) by Authentication.action Authentication.app Authentication.authentication_method\
21
+ \ Authentication.dest \n Authentication.signature Authentication.signature_id Authentication.src\
22
+ \ sourcetype _time span=5m \n | `drop_dm_object_name(\" Authentication\" )`\n ```fill\
23
+ \ out time buckets for 0-count events during entire search length```\n | appendpipe\
24
+ \ [| timechart limit=0 span=5m count | table _time] | fillnull value=0 unique_accounts\n \
25
+ \ ``` Create aggregation field & apply to all null events```\n | eval counter=src+\" \
26
+ __\" +sourcetype+\" __\" +signature_id | eventstats values(counter) as fnscounter\
27
+ \ | eval counter=coalesce(counter,fnscounter) \n ``` stats version of mvexpand\
28
+ \ ```\n | stats values(app) as app values(unique_user_names) as unique_user_names\
29
+ \ values(total_failures) as total_failures values(src) as src values(signature_id)\
30
+ \ as signature_id values(sourcetype) as sourcetype count by counter unique_accounts\
31
+ \ _time\n ``` remove duplicate time buckets for each unique source```\n | sort\
32
+ \ - _time unique_accounts | dedup _time counter\n ```Find the outliers```\n |\
33
+ \ eventstats avg(unique_accounts) as comp_avg , stdev(unique_accounts) as comp_std\
34
+ \ by counter | eval upperBound=(comp_avg+comp_std*3) | eval isOutlier=if(unique_accounts\
35
+ \ > 30 and unique_accounts >= upperBound, 1, 0) | replace \" ::ffff:*\" with * in\
36
+ \ src | where isOutlier=1 | foreach * \n [ eval <<FIELD>> = if(<<FIELD>>=\" \
37
+ null\" ,null(),<<FIELD>>)] \n | table _time, src, action, app, unique_accounts, unique_user_names,\
38
+ \ total_failures, sourcetype, signature_id, counter | `detect_password_spray_attempts_filter`"
39
+ how_to_implement : ' Ensure in-scope authentication data is CIM mapped and the src field
40
+ is populated with the source device. Also ensure fill_nullvalue is set within the
41
+ macro security_content_summariesonly. This search opporates best on a 5 minute schedule,
42
+ looking back over the past 70 minutes. Configure 70 minute throttling on the two
43
+ fields _time and counter. '
51
44
known_false_positives : Unknown
52
45
references :
53
46
- https://attack.mitre.org/techniques/T1110/003/
92
85
tests :
93
86
- name : True Positive Test
94
87
attack_data :
95
- - data :
96
- https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1110.003/purplesharp_invalid_users_kerberos_xml/windows-security.log
88
+ - data : https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1110.003/purplesharp_invalid_users_kerberos_xml/windows-security.log
97
89
source : XmlWinEventLog:Security
98
90
sourcetype : XmlWinEventLog
0 commit comments