diff --git a/detections/application/crushftp_server_side_template_injection.yml b/detections/application/crushftp_server_side_template_injection.yml index 4033fd4339..55c018bda9 100644 --- a/detections/application/crushftp_server_side_template_injection.yml +++ b/detections/application/crushftp_server_side_template_injection.yml @@ -7,10 +7,30 @@ data_source: - CrushFTP type: TTP status: production -description: This analytic is designed to identify attempts to exploit a server-side template injection vulnerability in CrushFTP, designated as CVE-2024-4040. This severe vulnerability enables unauthenticated remote attackers to access and read files beyond the VFS Sandbox, circumvent authentication protocols, and execute arbitrary commands on the affected server. The issue impacts all versions of CrushFTP up to 10.7.1 and 11.1.0 on all supported platforms. It is highly recommended to apply patches immediately to prevent unauthorized access to the system and avoid potential data compromises. The search specifically looks for patterns in the raw log data that match the exploitation attempts, including READ or WRITE actions, and extracts relevant information such as the protocol, session ID, user, IP address, HTTP method, and the URI queried. It then evaluates these logs to confirm traces of exploitation based on the presence of specific keywords and the originating IP address, counting and sorting these events for further analysis. -search: '`crushftp` | rex field=_raw "\[(?HTTPS|HTTP):(?[^\:]+):(?[^\:]+):(?\d+\.\d+\.\d+\.\d+)\] (?READ|WROTE): \*(?[A-Z]+) (?[^\s]+) HTTP/[^\*]+\*" | eval message=if(match(_raw, "INCLUDE") and isnotnull(src_ip), "traces of exploitation by " . src_ip, "false") | search message!=false | rename host as dest | stats count by _time, dest, source, message, src_ip, http_method, uri_query, user, action | sort -_time| `crushftp_server_side_template_injection_filter`' -how_to_implement: CrushFTP Session logs, from Windows or Linux, must be ingested to Splunk. Currently, there is no TA for CrushFTP, so the data must be extracted from the raw logs. -known_false_positives: False positives should be limited, however tune or filter as needed. +description: This analytic is designed to identify attempts to exploit a server-side + template injection vulnerability in CrushFTP, designated as CVE-2024-4040. This + severe vulnerability enables unauthenticated remote attackers to access and read + files beyond the VFS Sandbox, circumvent authentication protocols, and execute arbitrary + commands on the affected server. The issue impacts all versions of CrushFTP up to + 10.7.1 and 11.1.0 on all supported platforms. It is highly recommended to apply + patches immediately to prevent unauthorized access to the system and avoid potential + data compromises. The search specifically looks for patterns in the raw log data + that match the exploitation attempts, including READ or WRITE actions, and extracts + relevant information such as the protocol, session ID, user, IP address, HTTP method, + and the URI queried. It then evaluates these logs to confirm traces of exploitation + based on the presence of specific keywords and the originating IP address, counting + and sorting these events for further analysis. +search: '`crushftp` | rex field=_raw "\[(?HTTPS|HTTP):(?[^\:]+):(?[^\:]+):(?\d+\.\d+\.\d+\.\d+)\] + (?READ|WROTE): \*(?[A-Z]+) (?[^\s]+) HTTP/[^\*]+\*" + | eval message=if(match(_raw, "INCLUDE") and isnotnull(src_ip), "traces of exploitation + by " . src_ip, "false") | search message!=false | rename host as dest | stats count + by _time, dest, source, message, src_ip, http_method, uri_query, user, action | + sort -_time| `crushftp_server_side_template_injection_filter`' +how_to_implement: CrushFTP Session logs, from Windows or Linux, must be ingested to + Splunk. Currently, there is no TA for CrushFTP, so the data must be extracted from + the raw logs. +known_false_positives: False positives should be limited, however tune or filter as + needed. references: - https://github.com/airbus-cert/CVE-2024-4040 - https://www.bleepingcomputer.com/news/security/crushftp-warns-users-to-patch-exploited-zero-day-immediately/ @@ -20,7 +40,12 @@ drilldown_searches: earliest_offset: $info_min_time$ latest_offset: $info_max_time$ - name: View risk events for the last 7 days for - "$dest$" - search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$dest$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' + search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$dest$") + starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime + values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) + as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) + as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` + | `security_content_ctime(lastTime)`' earliest_offset: $info_min_time$ latest_offset: $info_max_time$ rba: @@ -29,7 +54,7 @@ rba: risk_objects: - field: dest type: IP Address - risk_score: 64.0 + risk_score: 64 threat_objects: - field: src_ip type: IP Address @@ -62,6 +87,7 @@ tags: tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1190/crushftp/crushftp.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1190/crushftp/crushftp.log sourcetype: crushftp:sessionlogs source: crushftp diff --git a/detections/application/detect_distributed_password_spray_attempts.yml b/detections/application/detect_distributed_password_spray_attempts.yml index 90e29302bc..e6e824ca89 100644 --- a/detections/application/detect_distributed_password_spray_attempts.yml +++ b/detections/application/detect_distributed_password_spray_attempts.yml @@ -7,42 +7,53 @@ status: production type: Hunting data_source: - Azure Active Directory Sign-in activity -description: This analytic employs the 3-sigma approach to identify distributed password spray attacks. A - distributed password spray attack is a type of brute force attack where the attacker attempts a few - common passwords against many different accounts, connecting from multiple IP addresses to avoid detection. - By utilizing the Authentication Data Model, this detection is effective for all CIM-mapped authentication - events, providing comprehensive coverage and enhancing security against these attacks. +description: This analytic employs the 3-sigma approach to identify distributed password + spray attacks. A distributed password spray attack is a type of brute force attack + where the attacker attempts a few common passwords against many different accounts, + connecting from multiple IP addresses to avoid detection. By utilizing the Authentication + Data Model, this detection is effective for all CIM-mapped authentication events, + providing comprehensive coverage and enhancing security against these attacks. search: >- - | tstats `security_content_summariesonly` dc(Authentication.user) AS unique_accounts dc(Authentication.src) as unique_src values(Authentication.app) as app values(Authentication.src) as src count(Authentication.user) as total_failures from datamodel=Authentication.Authentication where Authentication.action="failure" NOT Authentication.src IN ("-","unknown") Authentication.user_agent="*" by Authentication.signature_id, Authentication.user_agent, sourcetype, _time span=10m + | tstats `security_content_summariesonly` dc(Authentication.user) AS unique_accounts + dc(Authentication.src) as unique_src values(Authentication.app) as app values(Authentication.src) + as src count(Authentication.user) as total_failures from datamodel=Authentication.Authentication + where Authentication.action="failure" NOT Authentication.src IN ("-","unknown") + Authentication.user_agent="*" by Authentication.signature_id, Authentication.user_agent, + sourcetype, _time span=10m | `drop_dm_object_name("Authentication")` ```fill out time buckets for 0-count events during entire search length``` | appendpipe [| timechart limit=0 span=10m count | table _time] | fillnull value=0 unique_accounts, unique_src ``` Create aggregation field & apply to all null events``` | eval counter=sourcetype+"__"+signature_id - | eventstats values(counter) as fnscounter | eval counter=coalesce(counter,fnscounter) - | stats values(total_failures) as total_failures values(signature_id) as signature_id values(src) as src values(sourcetype) as sourcetype values(app) as app count by counter unique_accounts unique_src user_agent _time + | eventstats values(counter) as fnscounter | eval counter=coalesce(counter,fnscounter) | + stats values(total_failures) as total_failures values(signature_id) as signature_id + values(src) as src values(sourcetype) as sourcetype values(app) as app count by + counter unique_accounts unique_src user_agent _time ``` remove 0 count rows where counter has data``` | sort - _time unique_accounts | dedup _time counter ``` 3-sigma detection logic ``` - | eventstats avg(unique_accounts) as comp_avg_user , stdev(unique_accounts) as comp_std_user avg(unique_src) as comp_avg_src , stdev(unique_src) as comp_std_src by counter user_agent + | eventstats avg(unique_accounts) as comp_avg_user , stdev(unique_accounts) as comp_std_user + avg(unique_src) as comp_avg_src , stdev(unique_src) as comp_std_src by counter user_agent | eval upperBoundUser=(comp_avg_user+comp_std_user*3), upperBoundsrc=(comp_avg_src+comp_std_src*3) - | eval isOutlier=if((unique_accounts > 30 and unique_accounts >= upperBoundUser) and (unique_src > 30 and unique_src >= upperBoundsrc), 1, 0) + | eval isOutlier=if((unique_accounts > 30 and unique_accounts >= upperBoundUser) + and (unique_src > 30 and unique_src >= upperBoundsrc), 1, 0) | replace "::ffff:*" with * in src | where isOutlier=1 | foreach * [ eval <> = if(<>="null",null(),<>)] - | mvexpand src - | iplocation src - | table _time, unique_src, unique_accounts, total_failures, sourcetype, signature_id, user_agent, src, Country + | mvexpand src | iplocation src | table _time, unique_src, unique_accounts, total_failures, + sourcetype, signature_id, user_agent, src, Country | eval date_wday=strftime(_time,"%a"), date_hour=strftime(_time,"%H") | `detect_distributed_password_spray_attempts_filter` -how_to_implement: Ensure that all relevant authentication data is mapped to the Common Information Model (CIM) - and that the src field is populated with the source device information. Additionally, ensure that - fill_nullvalue is set within the security_content_summariesonly macro to include authentication events from - log sources that do not feature the signature_id field in the results. -known_false_positives: It is common to see a spike of legitimate failed authentication events on monday mornings. +how_to_implement: Ensure that all relevant authentication data is mapped to the Common + Information Model (CIM) and that the src field is populated with the source device + information. Additionally, ensure that fill_nullvalue is set within the security_content_summariesonly + macro to include authentication events from log sources that do not feature the + signature_id field in the results. +known_false_positives: It is common to see a spike of legitimate failed authentication + events on monday mornings. references: - https://attack.mitre.org/techniques/T1110/003/ rba: @@ -74,10 +85,12 @@ tags: - Authentication.user - Authentication.src security_domain: access - manual_test: The dataset & hardcoded timerange doesn't meet the criteria for this detetion. + manual_test: The dataset & hardcoded timerange doesn't meet the criteria for this + detetion. tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1110.003/azure_ad_distributed_spray/azure_ad_distributed_spray.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1110.003/azure_ad_distributed_spray/azure_ad_distributed_spray.log source: azure:monitor:aad sourcetype: azure:monitor:aad diff --git a/detections/application/detect_new_login_attempts_to_routers.yml b/detections/application/detect_new_login_attempts_to_routers.yml index 084e5f29d5..34a5e0ed83 100644 --- a/detections/application/detect_new_login_attempts_to_routers.yml +++ b/detections/application/detect_new_login_attempts_to_routers.yml @@ -5,10 +5,23 @@ date: '2024-10-17' author: Bhavin Patel, Splunk status: experimental type: TTP -description: The following analytic identifies new login attempts to routers. It leverages authentication logs from the ES Assets and Identity Framework, focusing on assets categorized as routers. The detection flags connections that have not been observed in the past 30 days. This activity is significant because unauthorized access to routers can lead to network disruptions or data interception. If confirmed malicious, attackers could gain control over network traffic, potentially leading to data breaches or further network compromise. +description: The following analytic identifies new login attempts to routers. It leverages + authentication logs from the ES Assets and Identity Framework, focusing on assets + categorized as routers. The detection flags connections that have not been observed + in the past 30 days. This activity is significant because unauthorized access to + routers can lead to network disruptions or data interception. If confirmed malicious, + attackers could gain control over network traffic, potentially leading to data breaches + or further network compromise. data_source: [] -search: '| tstats `security_content_summariesonly` count earliest(_time) as earliest latest(_time) as latest from datamodel=Authentication where Authentication.dest_category=router by Authentication.dest Authentication.user| eval isOutlier=if(earliest >= relative_time(now(), "-30d@d"), 1, 0) | where isOutlier=1| `security_content_ctime(earliest)`| `security_content_ctime(latest)` | `drop_dm_object_name("Authentication")` | `detect_new_login_attempts_to_routers_filter`' -how_to_implement: To successfully implement this search, you must ensure the network router devices are categorized as "router" in the Assets and identity table. You must also populate the Authentication data model with logs related to users authenticating to routing infrastructure. +search: '| tstats `security_content_summariesonly` count earliest(_time) as earliest + latest(_time) as latest from datamodel=Authentication where Authentication.dest_category=router + by Authentication.dest Authentication.user| eval isOutlier=if(earliest >= relative_time(now(), + "-30d@d"), 1, 0) | where isOutlier=1| `security_content_ctime(earliest)`| `security_content_ctime(latest)` + | `drop_dm_object_name("Authentication")` | `detect_new_login_attempts_to_routers_filter`' +how_to_implement: To successfully implement this search, you must ensure the network + router devices are categorized as "router" in the Assets and identity table. You + must also populate the Authentication data model with logs related to users authenticating + to routing infrastructure. known_false_positives: Legitimate router connections may appear as new connections references: [] rba: @@ -16,10 +29,10 @@ rba: risk_objects: - field: user type: User - risk_score: 25.0 + risk_score: 25 - field: dest type: Hostname - risk_score: 25.0 + risk_score: 25 threat_objects: [] tags: analytic_story: diff --git a/detections/application/detect_password_spray_attempts.yml b/detections/application/detect_password_spray_attempts.yml index 147136869b..0ca40bd3d9 100644 --- a/detections/application/detect_password_spray_attempts.yml +++ b/detections/application/detect_password_spray_attempts.yml @@ -7,42 +7,48 @@ status: production type: TTP data_source: - Windows Event Log Security 4625 -description: This analytic employs the 3-sigma approach to detect an unusual volume of failed authentication attempts - from a single source. A password spray attack is a type of brute force attack where an attacker tries a few - common passwords across many different accounts to avoid detection and account lockouts. By utilizing the - Authentication Data Model, this detection is effective for all CIM-mapped authentication events, providing - comprehensive coverage and enhancing security against these attacks. +description: This analytic employs the 3-sigma approach to detect an unusual volume + of failed authentication attempts from a single source. A password spray attack + is a type of brute force attack where an attacker tries a few common passwords across + many different accounts to avoid detection and account lockouts. By utilizing the + Authentication Data Model, this detection is effective for all CIM-mapped authentication + events, providing comprehensive coverage and enhancing security against these attacks. search: >- - | tstats `security_content_summariesonly` values(Authentication.user) AS unique_user_names dc(Authentication.user) AS unique_accounts values(Authentication.app) as app count(Authentication.user) as total_failures from datamodel=Authentication.Authentication where Authentication.action="failure" NOT Authentication.src IN ("-","unknown") by Authentication.src, Authentication.action, Authentication.signature_id, sourcetype, _time span=5m - | `drop_dm_object_name("Authentication")` + | tstats `security_content_summariesonly` values(Authentication.user) AS unique_user_names + dc(Authentication.user) AS unique_accounts values(Authentication.app) as app count(Authentication.user) + as total_failures from datamodel=Authentication.Authentication where Authentication.action="failure" + NOT Authentication.src IN ("-","unknown") by Authentication.src, Authentication.action, + Authentication.signature_id, sourcetype, _time span=5m | `drop_dm_object_name("Authentication")` ```fill out time buckets for 0-count events during entire search length``` | appendpipe [| timechart limit=0 span=5m count | table _time] | fillnull value=0 unique_accounts ``` Create aggregation field & apply to all null events``` - | eval counter=src+"__"+sourcetype+"__"+signature_id - | eventstats values(counter) as fnscounter - | eval counter=coalesce(counter,fnscounter) + | eval counter=src+"__"+sourcetype+"__"+signature_id | eventstats values(counter) + as fnscounter | eval counter=coalesce(counter,fnscounter) ``` stats version of mvexpand ``` - | stats values(app) as app values(unique_user_names) as unique_user_names values(total_failures) as total_failures values(src) as src values(signature_id) as signature_id values(sourcetype) as sourcetype count by counter unique_accounts _time + | stats values(app) as app values(unique_user_names) as unique_user_names values(total_failures) + as total_failures values(src) as src values(signature_id) as signature_id values(sourcetype) + as sourcetype count by counter unique_accounts _time ``` remove duplicate time buckets for each unique source``` | sort - _time unique_accounts | dedup _time counter ```Find the outliers``` - | eventstats avg(unique_accounts) as comp_avg , stdev(unique_accounts) as comp_std by counter + | eventstats avg(unique_accounts) as comp_avg , stdev(unique_accounts) as comp_std + by counter | eval upperBound=(comp_avg+comp_std*3) | eval isOutlier=if(unique_accounts > 30 and unique_accounts >= upperBound, 1, 0) - | replace "::ffff:*" with * in src - | where isOutlier=1 - | foreach * + | replace "::ffff:*" with * in src | where isOutlier=1 | foreach * [ eval <> = if(<>="null",null(),<>)] - | table _time, src, action, app, unique_accounts, unique_user_names, total_failures, sourcetype, signature_id, counter + | table _time, src, action, app, unique_accounts, unique_user_names, total_failures, + sourcetype, signature_id, counter | `detect_password_spray_attempts_filter` how_to_implement: >- - Ensure in-scope authentication data is CIM mapped and the src field is populated with the source device. - Also ensure fill_nullvalue is set within the macro security_content_summariesonly. + Ensure in-scope authentication data is CIM mapped and the src field is populated + with the source device. Also ensure fill_nullvalue is set within the macro security_content_summariesonly. + - This search opporates best on a 5 minute schedule, looking back over the past 70 minutes. - Configure 70 minute throttling on the two fields _time and counter. + This search opporates best on a 5 minute schedule, looking back over the past 70 + minutes. Configure 70 minute throttling on the two fields _time and counter. known_false_positives: Unknown references: - https://attack.mitre.org/techniques/T1110/003/ @@ -52,7 +58,12 @@ drilldown_searches: earliest_offset: $info_min_time$ latest_offset: $info_max_time$ - name: View risk events for the last 7 days for - "$sourcetype$" - search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$sourcetype$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' + search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$sourcetype$") + starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime + values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) + as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) + as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` + | `security_content_ctime(lastTime)`' earliest_offset: $info_min_time$ latest_offset: $info_max_time$ rba: @@ -61,7 +72,7 @@ rba: risk_objects: - field: unique_user_names type: User - risk_score: 49.0 + risk_score: 49 threat_objects: - field: src type: Endpoint @@ -90,6 +101,7 @@ tags: tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1110.003/purplesharp_invalid_users_kerberos_xml/windows-security.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1110.003/purplesharp_invalid_users_kerberos_xml/windows-security.log source: XmlWinEventLog:Security sourcetype: XmlWinEventLog diff --git a/detections/application/email_attachments_with_lots_of_spaces.yml b/detections/application/email_attachments_with_lots_of_spaces.yml index 272dd9c1ed..c09cda524f 100644 --- a/detections/application/email_attachments_with_lots_of_spaces.yml +++ b/detections/application/email_attachments_with_lots_of_spaces.yml @@ -5,14 +5,34 @@ date: '2024-10-17' author: David Dorsey, Splunk status: experimental type: Anomaly -description: The following analytic detects email attachments with an unusually high number of spaces in their file names, which is a common tactic used by attackers to obfuscate file extensions. It leverages the Email data model to identify attachments where the ratio of spaces to the total file name length exceeds 10%. This behavior is significant as it may indicate an attempt to bypass security filters and deliver malicious payloads. If confirmed malicious, this activity could lead to the execution of harmful code or unauthorized access to sensitive information within the recipient's environment. +description: The following analytic detects email attachments with an unusually high + number of spaces in their file names, which is a common tactic used by attackers + to obfuscate file extensions. It leverages the Email data model to identify attachments + where the ratio of spaces to the total file name length exceeds 10%. This behavior + is significant as it may indicate an attempt to bypass security filters and deliver + malicious payloads. If confirmed malicious, this activity could lead to the execution + of harmful code or unauthorized access to sensitive information within the recipient's + environment. data_source: [] -search: '| tstats `security_content_summariesonly` count values(All_Email.recipient) as recipient_address min(_time) as firstTime max(_time) as lastTime from datamodel=Email where All_Email.file_name="*" by All_Email.src_user, All_Email.file_name All_Email.message_id | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | `drop_dm_object_name("All_Email")` | eval space_ratio = (mvcount(split(file_name," "))-1)/len(file_name) | search space_ratio >= 0.1 | rex field=recipient_address "(?.*)@" | `email_attachments_with_lots_of_spaces_filter`' -how_to_implement: 'You need to ingest data from emails. Specifically, the sender''s address and the file names of any attachments must be mapped to the Email data model. The threshold ratio is set to 10%, but this value can be configured to suit each environment. - - **Splunk Phantom Playbook Integration** - - If Splunk Phantom is also configured in your environment, a playbook called "Suspicious Email Attachment Investigate and Delete" can be configured to run when any results are found by this detection search. To use this integration, install the Phantom App for Splunk `https://splunkbase.splunk.com/app/3411/` and add the correct hostname to the "Phantom Instance" field in the Adaptive Response Actions when configuring this detection search. The notable event will be sent to Phantom and the playbook will gather further information about the file attachment and its network behaviors. If Phantom finds malicious behavior and an analyst approves of the results, the email will be deleted from the user''s inbox.' +search: '| tstats `security_content_summariesonly` count values(All_Email.recipient) + as recipient_address min(_time) as firstTime max(_time) as lastTime from datamodel=Email + where All_Email.file_name="*" by All_Email.src_user, All_Email.file_name All_Email.message_id + | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | `drop_dm_object_name("All_Email")` + | eval space_ratio = (mvcount(split(file_name," "))-1)/len(file_name) | search space_ratio + >= 0.1 | rex field=recipient_address "(?.*)@" | `email_attachments_with_lots_of_spaces_filter`' +how_to_implement: "You need to ingest data from emails. Specifically, the sender's + address and the file names of any attachments must be mapped to the Email data model. + The threshold ratio is set to 10%, but this value can be configured to suit each + environment.\n**Splunk Phantom Playbook Integration**\nIf Splunk Phantom is also + configured in your environment, a playbook called \"Suspicious Email Attachment + Investigate and Delete\" can be configured to run when any results are found by + this detection search. To use this integration, install the Phantom App for Splunk + `https://splunkbase.splunk.com/app/3411/` and add the correct hostname to the \"\ + Phantom Instance\" field in the Adaptive Response Actions when configuring this + detection search. The notable event will be sent to Phantom and the playbook will + gather further information about the file attachment and its network behaviors. + If Phantom finds malicious behavior and an analyst approves of the results, the + email will be deleted from the user's inbox." known_false_positives: None at this time references: [] rba: @@ -20,7 +40,7 @@ rba: risk_objects: - field: user type: User - risk_score: 25.0 + risk_score: 25 threat_objects: [] tags: analytic_story: diff --git a/detections/application/email_files_written_outside_of_the_outlook_directory.yml b/detections/application/email_files_written_outside_of_the_outlook_directory.yml index 0b48b801d7..8c070d2b44 100644 --- a/detections/application/email_files_written_outside_of_the_outlook_directory.yml +++ b/detections/application/email_files_written_outside_of_the_outlook_directory.yml @@ -5,19 +5,39 @@ date: '2024-10-17' author: Bhavin Patel, Splunk status: experimental type: TTP -description: The following analytic detects email files (.pst or .ost) being created outside the standard Outlook directories. It leverages the Endpoint.Filesystem data model to identify file creation events and filters for email files not located in "C:\Users\*\My Documents\Outlook Files\*" or "C:\Users\*\AppData\Local\Microsoft\Outlook*". This activity is significant as it may indicate data exfiltration or unauthorized access to email data. If confirmed malicious, an attacker could potentially access sensitive email content, leading to data breaches or further exploitation within the network. +description: The following analytic detects email files (.pst or .ost) being created + outside the standard Outlook directories. It leverages the Endpoint.Filesystem data + model to identify file creation events and filters for email files not located in + "C:\Users\*\My Documents\Outlook Files\*" or "C:\Users\*\AppData\Local\Microsoft\Outlook*". + This activity is significant as it may indicate data exfiltration or unauthorized + access to email data. If confirmed malicious, an attacker could potentially access + sensitive email content, leading to data breaches or further exploitation within + the network. data_source: - Sysmon EventID 11 -search: '| tstats `security_content_summariesonly` count values(Filesystem.file_path) as file_path min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Filesystem where (Filesystem.file_name=*.pst OR Filesystem.file_name=*.ost) Filesystem.file_path != "C:\\Users\\*\\My Documents\\Outlook Files\\*" Filesystem.file_path!="C:\\Users\\*\\AppData\\Local\\Microsoft\\Outlook*" by Filesystem.action Filesystem.process_id Filesystem.file_name Filesystem.dest | `drop_dm_object_name("Filesystem")` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`| `email_files_written_outside_of_the_outlook_directory_filter`' -how_to_implement: To successfully implement this search, you must be ingesting data that records the file-system activity from your hosts to populate the Endpoint.Filesystem data model node. This is typically populated via endpoint detection-and-response product, such as Carbon Black, or by other endpoint data sources, such as Sysmon. The data used for this search is typically generated via logs that report file-system reads and writes. -known_false_positives: Administrators and users sometimes prefer backing up their email data by moving the email files into a different folder. These attempts will be detected by the search. +search: '| tstats `security_content_summariesonly` count values(Filesystem.file_path) + as file_path min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Filesystem + where (Filesystem.file_name=*.pst OR Filesystem.file_name=*.ost) Filesystem.file_path + != "C:\\Users\\*\\My Documents\\Outlook Files\\*" Filesystem.file_path!="C:\\Users\\*\\AppData\\Local\\Microsoft\\Outlook*" + by Filesystem.action Filesystem.process_id Filesystem.file_name Filesystem.dest + | `drop_dm_object_name("Filesystem")` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`| + `email_files_written_outside_of_the_outlook_directory_filter`' +how_to_implement: To successfully implement this search, you must be ingesting data + that records the file-system activity from your hosts to populate the Endpoint.Filesystem + data model node. This is typically populated via endpoint detection-and-response + product, such as Carbon Black, or by other endpoint data sources, such as Sysmon. + The data used for this search is typically generated via logs that report file-system + reads and writes. +known_false_positives: Administrators and users sometimes prefer backing up their + email data by moving the email files into a different folder. These attempts will + be detected by the search. references: [] rba: message: tbd risk_objects: - field: dest type: Hostname - risk_score: 25.0 + risk_score: 25 threat_objects: [] tags: analytic_story: diff --git a/detections/application/email_servers_sending_high_volume_traffic_to_hosts.yml b/detections/application/email_servers_sending_high_volume_traffic_to_hosts.yml index 9de0e451b6..bfab9ff19b 100644 --- a/detections/application/email_servers_sending_high_volume_traffic_to_hosts.yml +++ b/detections/application/email_servers_sending_high_volume_traffic_to_hosts.yml @@ -5,18 +5,45 @@ date: '2024-10-17' author: Bhavin Patel, Splunk status: experimental type: Anomaly -description: The following analytic identifies a significant increase in data transfers from your email server to client hosts. It leverages the Network_Traffic data model to monitor outbound traffic from email servers, using statistical analysis to detect anomalies based on average and standard deviation metrics. This activity is significant as it may indicate a malicious actor exfiltrating data via your email server. If confirmed malicious, this could lead to unauthorized data access and potential data breaches, compromising sensitive information and impacting organizational security. +description: The following analytic identifies a significant increase in data transfers + from your email server to client hosts. It leverages the Network_Traffic data model + to monitor outbound traffic from email servers, using statistical analysis to detect + anomalies based on average and standard deviation metrics. This activity is significant + as it may indicate a malicious actor exfiltrating data via your email server. If + confirmed malicious, this could lead to unauthorized data access and potential data + breaches, compromising sensitive information and impacting organizational security. data_source: [] -search: '| tstats `security_content_summariesonly` sum(All_Traffic.bytes_out) as bytes_out from datamodel=Network_Traffic where All_Traffic.src_category=email_server by All_Traffic.dest_ip _time span=1d | `drop_dm_object_name("All_Traffic")` | eventstats avg(bytes_out) as avg_bytes_out stdev(bytes_out) as stdev_bytes_out | eventstats count as num_data_samples avg(eval(if(_time < relative_time(now(), "@d"), bytes_out, null))) as per_source_avg_bytes_out stdev(eval(if(_time < relative_time(now(), "@d"), bytes_out, null))) as per_source_stdev_bytes_out by dest_ip | eval minimum_data_samples = 4, deviation_threshold = 3 | where num_data_samples >= minimum_data_samples AND bytes_out > (avg_bytes_out + (deviation_threshold * stdev_bytes_out)) AND bytes_out > (per_source_avg_bytes_out + (deviation_threshold * per_source_stdev_bytes_out)) AND _time >= relative_time(now(), "@d") | eval num_standard_deviations_away_from_server_average = round(abs(bytes_out - avg_bytes_out) / stdev_bytes_out, 2), num_standard_deviations_away_from_client_average = round(abs(bytes_out - per_source_avg_bytes_out) / per_source_stdev_bytes_out, 2) | table dest_ip, _time, bytes_out, avg_bytes_out, per_source_avg_bytes_out, num_standard_deviations_away_from_server_average, num_standard_deviations_away_from_client_average | `email_servers_sending_high_volume_traffic_to_hosts_filter`' -how_to_implement: This search requires you to be ingesting your network traffic and populating the Network_Traffic data model. Your email servers must be categorized as "email_server" for the search to work, as well. You may need to adjust the deviation_threshold and minimum_data_samples values based on the network traffic in your environment. The "deviation_threshold" field is a multiplying factor to control how much variation you're willing to tolerate. The "minimum_data_samples" field is the minimum number of connections of data samples required for the statistic to be valid. -known_false_positives: The false-positive rate will vary based on how you set the deviation_threshold and data_samples values. Our recommendation is to adjust these values based on your network traffic to and from your email servers. +search: '| tstats `security_content_summariesonly` sum(All_Traffic.bytes_out) as bytes_out + from datamodel=Network_Traffic where All_Traffic.src_category=email_server by All_Traffic.dest_ip + _time span=1d | `drop_dm_object_name("All_Traffic")` | eventstats avg(bytes_out) + as avg_bytes_out stdev(bytes_out) as stdev_bytes_out | eventstats count as num_data_samples + avg(eval(if(_time < relative_time(now(), "@d"), bytes_out, null))) as per_source_avg_bytes_out + stdev(eval(if(_time < relative_time(now(), "@d"), bytes_out, null))) as per_source_stdev_bytes_out + by dest_ip | eval minimum_data_samples = 4, deviation_threshold = 3 | where num_data_samples + >= minimum_data_samples AND bytes_out > (avg_bytes_out + (deviation_threshold * + stdev_bytes_out)) AND bytes_out > (per_source_avg_bytes_out + (deviation_threshold + * per_source_stdev_bytes_out)) AND _time >= relative_time(now(), "@d") | eval num_standard_deviations_away_from_server_average + = round(abs(bytes_out - avg_bytes_out) / stdev_bytes_out, 2), num_standard_deviations_away_from_client_average + = round(abs(bytes_out - per_source_avg_bytes_out) / per_source_stdev_bytes_out, + 2) | table dest_ip, _time, bytes_out, avg_bytes_out, per_source_avg_bytes_out, num_standard_deviations_away_from_server_average, + num_standard_deviations_away_from_client_average | `email_servers_sending_high_volume_traffic_to_hosts_filter`' +how_to_implement: This search requires you to be ingesting your network traffic and + populating the Network_Traffic data model. Your email servers must be categorized + as "email_server" for the search to work, as well. You may need to adjust the deviation_threshold + and minimum_data_samples values based on the network traffic in your environment. + The "deviation_threshold" field is a multiplying factor to control how much variation + you're willing to tolerate. The "minimum_data_samples" field is the minimum number + of connections of data samples required for the statistic to be valid. +known_false_positives: The false-positive rate will vary based on how you set the + deviation_threshold and data_samples values. Our recommendation is to adjust these + values based on your network traffic to and from your email servers. references: [] rba: message: tbd risk_objects: - field: dest type: Hostname - risk_score: 25.0 + risk_score: 25 threat_objects: [] tags: analytic_story: diff --git a/detections/application/ivanti_vtm_new_account_creation.yml b/detections/application/ivanti_vtm_new_account_creation.yml index 199c71a26a..e8428623d5 100644 --- a/detections/application/ivanti_vtm_new_account_creation.yml +++ b/detections/application/ivanti_vtm_new_account_creation.yml @@ -7,10 +7,24 @@ data_source: - Ivanti VTM Audit type: TTP status: production -description: This analytic detects potential exploitation of the Ivanti Virtual Traffic Manager (vTM) authentication bypass vulnerability (CVE-2024-7593) to create new administrator accounts. The vulnerability allows unauthenticated remote attackers to bypass authentication on the admin panel and create new admin users. This detection looks for suspicious new account creation events in the Ivanti vTM audit logs that lack expected authentication details, which may indicate exploitation attempts. -search: '`ivanti_vtm_audit` OPERATION="adduser" MODGROUP="admin" IP="!!ABSENT!!" | stats count min(_time) as firstTime max(_time) as lastTime by IP, MODUSER, OPERATION, MODGROUP, AUTH | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | `ivanti_vtm_new_account_creation_filter`' -how_to_implement: To implement this detection, ensure that Ivanti vTM audit logs are being ingested into Splunk. Configure the Ivanti vTM to send its audit logs to Splunk via syslog or by monitoring the log files directly. The sourcetype should be set to "ivanti_vtm_audit" or a similar custom sourcetype for these logs. -known_false_positives: Legitimate new account creation by authorized administrators will generate similar log entries. However, those should include proper authentication details. Verify any detected events against expected administrative activities and authorized user lists. +description: This analytic detects potential exploitation of the Ivanti Virtual Traffic + Manager (vTM) authentication bypass vulnerability (CVE-2024-7593) to create new + administrator accounts. The vulnerability allows unauthenticated remote attackers + to bypass authentication on the admin panel and create new admin users. This detection + looks for suspicious new account creation events in the Ivanti vTM audit logs that + lack expected authentication details, which may indicate exploitation attempts. +search: '`ivanti_vtm_audit` OPERATION="adduser" MODGROUP="admin" IP="!!ABSENT!!" | + stats count min(_time) as firstTime max(_time) as lastTime by IP, MODUSER, OPERATION, + MODGROUP, AUTH | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` + | `ivanti_vtm_new_account_creation_filter`' +how_to_implement: To implement this detection, ensure that Ivanti vTM audit logs are + being ingested into Splunk. Configure the Ivanti vTM to send its audit logs to Splunk + via syslog or by monitoring the log files directly. The sourcetype should be set + to "ivanti_vtm_audit" or a similar custom sourcetype for these logs. +known_false_positives: Legitimate new account creation by authorized administrators + will generate similar log entries. However, those should include proper authentication + details. Verify any detected events against expected administrative activities and + authorized user lists. references: - https://www.ivanti.com/security/security-advisories/ivanti-virtual-traffic-manager-vtm-cve-2024-7593 - https://nvd.nist.gov/vuln/detail/CVE-2024-7593 @@ -20,7 +34,12 @@ drilldown_searches: earliest_offset: $info_min_time$ latest_offset: $info_max_time$ - name: View risk events for the last 7 days for - "$MODUSER$" - search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$MODUSER$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' + search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$MODUSER$") + starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime + values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) + as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) + as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` + | `security_content_ctime(lastTime)`' earliest_offset: $info_min_time$ latest_offset: $info_max_time$ rba: @@ -29,7 +48,7 @@ rba: risk_objects: - field: MODUSER type: User - risk_score: 72.0 + risk_score: 72 threat_objects: [] tags: analytic_story: @@ -57,6 +76,7 @@ tags: tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1190/ivanti/ivanti_vtm_audit.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1190/ivanti/ivanti_vtm_audit.log sourcetype: ivanti_vtm_audit source: ivanti_vtm diff --git a/detections/application/monitor_email_for_brand_abuse.yml b/detections/application/monitor_email_for_brand_abuse.yml index 572dd92e49..57e6d20387 100644 --- a/detections/application/monitor_email_for_brand_abuse.yml +++ b/detections/application/monitor_email_for_brand_abuse.yml @@ -5,10 +5,25 @@ date: '2024-10-17' author: David Dorsey, Splunk status: experimental type: TTP -description: The following analytic identifies emails claiming to be sent from a domain similar to one you are monitoring for potential abuse. It leverages email header data, specifically the sender's address, and cross-references it with a lookup table of known domain permutations generated by the "ESCU - DNSTwist Domain Names" search. This activity is significant as it can indicate phishing attempts or brand impersonation, which are common tactics used in social engineering attacks. If confirmed malicious, this could lead to unauthorized access, data theft, or reputational damage. +description: The following analytic identifies emails claiming to be sent from a domain + similar to one you are monitoring for potential abuse. It leverages email header + data, specifically the sender's address, and cross-references it with a lookup table + of known domain permutations generated by the "ESCU - DNSTwist Domain Names" search. + This activity is significant as it can indicate phishing attempts or brand impersonation, + which are common tactics used in social engineering attacks. If confirmed malicious, + this could lead to unauthorized access, data theft, or reputational damage. data_source: [] -search: '| tstats `security_content_summariesonly` values(All_Email.recipient) as recipients, min(_time) as firstTime, max(_time) as lastTime from datamodel=Email by All_Email.src_user, All_Email.message_id | `drop_dm_object_name("All_Email")` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | eval temp=split(src_user, "@") | eval email_domain=mvindex(temp, 1) | lookup update=true brandMonitoring_lookup domain as email_domain OUTPUT domain_abuse | search domain_abuse=true | table message_id, src_user, email_domain, recipients, firstTime, lastTime | `monitor_email_for_brand_abuse_filter`' -how_to_implement: You need to ingest email header data. Specifically the sender's address (src_user) must be populated. You also need to have run the search "ESCU - DNSTwist Domain Names", which creates the permutations of the domain that will be checked for. +search: '| tstats `security_content_summariesonly` values(All_Email.recipient) as + recipients, min(_time) as firstTime, max(_time) as lastTime from datamodel=Email + by All_Email.src_user, All_Email.message_id | `drop_dm_object_name("All_Email")` + | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | eval + temp=split(src_user, "@") | eval email_domain=mvindex(temp, 1) | lookup update=true + brandMonitoring_lookup domain as email_domain OUTPUT domain_abuse | search domain_abuse=true + | table message_id, src_user, email_domain, recipients, firstTime, lastTime | `monitor_email_for_brand_abuse_filter`' +how_to_implement: You need to ingest email header data. Specifically the sender's + address (src_user) must be populated. You also need to have run the search "ESCU + - DNSTwist Domain Names", which creates the permutations of the domain that will + be checked for. known_false_positives: None at this time references: [] rba: @@ -16,7 +31,7 @@ rba: risk_objects: - field: user type: User - risk_score: 25.0 + risk_score: 25 threat_objects: [] tags: analytic_story: diff --git a/detections/application/no_windows_updates_in_a_time_frame.yml b/detections/application/no_windows_updates_in_a_time_frame.yml index 715b4c5729..aaf1286181 100644 --- a/detections/application/no_windows_updates_in_a_time_frame.yml +++ b/detections/application/no_windows_updates_in_a_time_frame.yml @@ -5,10 +5,28 @@ date: '2024-10-17' author: Bhavin Patel, Splunk status: experimental type: Hunting -description: The following analytic identifies Windows endpoints that have not generated an event indicating a successful Windows update in the last 60 days. It leverages the 'Update' data model in Splunk, specifically looking for the latest 'Installed' status events from Microsoft Windows. This activity is significant for a SOC because endpoints that are not regularly patched are vulnerable to known exploits and security vulnerabilities. If confirmed malicious, this could indicate a compromised endpoint that is intentionally being kept unpatched, potentially allowing attackers to exploit unpatched vulnerabilities and gain unauthorized access or control. +description: The following analytic identifies Windows endpoints that have not generated + an event indicating a successful Windows update in the last 60 days. It leverages + the 'Update' data model in Splunk, specifically looking for the latest 'Installed' + status events from Microsoft Windows. This activity is significant for a SOC because + endpoints that are not regularly patched are vulnerable to known exploits and security + vulnerabilities. If confirmed malicious, this could indicate a compromised endpoint + that is intentionally being kept unpatched, potentially allowing attackers to exploit + unpatched vulnerabilities and gain unauthorized access or control. data_source: [] -search: '| tstats `security_content_summariesonly` max(_time) as lastTime from datamodel=Updates where Updates.status=Installed Updates.vendor_product="Microsoft Windows" by Updates.dest Updates.status Updates.vendor_product | rename Updates.dest as Host | rename Updates.status as "Update Status" | rename Updates.vendor_product as Product | eval isOutlier=if(lastTime <= relative_time(now(), "-60d@d"), 1, 0) | `security_content_ctime(lastTime)` | search isOutlier=1 | rename lastTime as "Last Update Time", | table Host, "Update Status", Product, "Last Update Time" | `no_windows_updates_in_a_time_frame_filter`' -how_to_implement: To successfully implement this search, it requires that the 'Update' data model is being populated. This can be accomplished by ingesting Windows events or the Windows Update log via a universal forwarder on the Windows endpoints you wish to monitor. The Windows add-on should be also be installed and configured to properly parse Windows events in Splunk. There may be other data sources which can populate this data model, including vulnerability management systems. +search: '| tstats `security_content_summariesonly` max(_time) as lastTime from datamodel=Updates + where Updates.status=Installed Updates.vendor_product="Microsoft Windows" by Updates.dest + Updates.status Updates.vendor_product | rename Updates.dest as Host | rename Updates.status + as "Update Status" | rename Updates.vendor_product as Product | eval isOutlier=if(lastTime + <= relative_time(now(), "-60d@d"), 1, 0) | `security_content_ctime(lastTime)` | + search isOutlier=1 | rename lastTime as "Last Update Time", | table Host, "Update + Status", Product, "Last Update Time" | `no_windows_updates_in_a_time_frame_filter`' +how_to_implement: To successfully implement this search, it requires that the 'Update' + data model is being populated. This can be accomplished by ingesting Windows events + or the Windows Update log via a universal forwarder on the Windows endpoints you + wish to monitor. The Windows add-on should be also be installed and configured to + properly parse Windows events in Splunk. There may be other data sources which can + populate this data model, including vulnerability management systems. known_false_positives: None identified references: [] rba: @@ -16,7 +34,7 @@ rba: risk_objects: - field: dest type: Hostname - risk_score: 25.0 + risk_score: 25 threat_objects: [] tags: analytic_story: diff --git a/detections/application/okta_authentication_failed_during_mfa_challenge.yml b/detections/application/okta_authentication_failed_during_mfa_challenge.yml index 9ad16fb122..a79174057d 100644 --- a/detections/application/okta_authentication_failed_during_mfa_challenge.yml +++ b/detections/application/okta_authentication_failed_during_mfa_challenge.yml @@ -7,10 +7,27 @@ data_source: - Okta type: TTP status: production -description: The following analytic identifies failed authentication attempts during the Multi-Factor Authentication (MFA) challenge in an Okta tenant. It uses the Authentication datamodel to detect specific failed events where the authentication signature is `user.authentication.auth_via_mfa`. This activity is significant as it may indicate an adversary attempting to authenticate with compromised credentials on an account with MFA enabled. If confirmed malicious, this could suggest an ongoing attempt to bypass MFA protections, potentially leading to unauthorized access and further compromise of the affected account. -search: '| tstats `security_content_summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Authentication.app) as app values(Authentication.reason) as reason values(Authentication.signature) as signature values(Authentication.method) as method from datamodel=Authentication where Authentication.signature=user.authentication.auth_via_mfa Authentication.action = failure by _time Authentication.src Authentication.user Authentication.dest Authentication.action | `drop_dm_object_name("Authentication")` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`| iplocation src | `okta_authentication_failed_during_mfa_challenge_filter`' -how_to_implement: The analytic leverages Okta OktaIm2 logs to be ingested using the Splunk Add-on for Okta Identity Cloud (https://splunkbase.splunk.com/app/6553). -known_false_positives: A user may have accidentally entered the wrong credentials during the MFA challenge. If the user is new to MFA, they may have trouble authenticating. Ensure that the user is aware of the MFA process and has the correct credentials. +description: The following analytic identifies failed authentication attempts during + the Multi-Factor Authentication (MFA) challenge in an Okta tenant. It uses the Authentication + datamodel to detect specific failed events where the authentication signature is + `user.authentication.auth_via_mfa`. This activity is significant as it may indicate + an adversary attempting to authenticate with compromised credentials on an account + with MFA enabled. If confirmed malicious, this could suggest an ongoing attempt + to bypass MFA protections, potentially leading to unauthorized access and further + compromise of the affected account. +search: '| tstats `security_content_summariesonly` count min(_time) as firstTime max(_time) + as lastTime values(Authentication.app) as app values(Authentication.reason) as + reason values(Authentication.signature) as signature values(Authentication.method) + as method from datamodel=Authentication where Authentication.signature=user.authentication.auth_via_mfa + Authentication.action = failure by _time Authentication.src Authentication.user + Authentication.dest Authentication.action | `drop_dm_object_name("Authentication")` + | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`| iplocation + src | `okta_authentication_failed_during_mfa_challenge_filter`' +how_to_implement: The analytic leverages Okta OktaIm2 logs to be ingested using the + Splunk Add-on for Okta Identity Cloud (https://splunkbase.splunk.com/app/6553). +known_false_positives: A user may have accidentally entered the wrong credentials + during the MFA challenge. If the user is new to MFA, they may have trouble authenticating. + Ensure that the user is aware of the MFA process and has the correct credentials. references: - https://sec.okta.com/everythingisyes - https://splunkbase.splunk.com/app/6553 @@ -20,7 +37,12 @@ drilldown_searches: earliest_offset: $info_min_time$ latest_offset: $info_max_time$ - name: View risk events for the last 7 days for - "$user$" - search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' + search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$") + starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime + values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) + as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) + as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` + | `security_content_ctime(lastTime)`' earliest_offset: $info_min_time$ latest_offset: $info_max_time$ rba: @@ -28,7 +50,7 @@ rba: risk_objects: - field: user type: User - risk_score: 48.0 + risk_score: 48 threat_objects: - field: src type: IP Address @@ -63,6 +85,7 @@ tags: tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1621/okta_mfa_login_failed/okta_mfa_login_failed.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1621/okta_mfa_login_failed/okta_mfa_login_failed.log source: okta_log sourcetype: OktaIM2:log diff --git a/detections/application/okta_idp_lifecycle_modifications.yml b/detections/application/okta_idp_lifecycle_modifications.yml index a302ee268b..66797b5078 100644 --- a/detections/application/okta_idp_lifecycle_modifications.yml +++ b/detections/application/okta_idp_lifecycle_modifications.yml @@ -7,10 +7,26 @@ data_source: - Okta type: Anomaly status: production -description: The following analytic identifies modifications to Okta Identity Provider (IDP) lifecycle events, including creation, activation, deactivation, and deletion of IDP configurations. It uses OktaIm2 logs ingested via the Splunk Add-on for Okta Identity Cloud. Monitoring these events is crucial for maintaining the integrity and security of authentication mechanisms. Unauthorized or anomalous changes could indicate potential security breaches or misconfigurations. If confirmed malicious, attackers could manipulate authentication processes, potentially gaining unauthorized access or disrupting identity management systems. -search: '`okta` eventType IN ("system.idp.lifecycle.activate","system.idp.lifecycle.create","system.idp.lifecycle.delete","system.idp.lifecycle.deactivate") | stats count min(_time) as firstTime max(_time) as lastTime values(target{}.id) as target_id values(target{}.type) as target_modified by src dest src_user_id user user_agent command description | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | `okta_idp_lifecycle_modifications_filter`' -how_to_implement: The analytic leverages Okta OktaIm2 logs to be ingested using the Splunk Add-on for Okta Identity Cloud (https://splunkbase.splunk.com/app/6553). -known_false_positives: It's possible for legitimate administrative actions or automated processes to trigger this detection, especially if there are bulk modifications to Okta IDP lifecycle events. Review the context of the modification, such as the user making the change and the specific lifecycle event modified, to determine if it aligns with expected behavior. +description: The following analytic identifies modifications to Okta Identity Provider + (IDP) lifecycle events, including creation, activation, deactivation, and deletion + of IDP configurations. It uses OktaIm2 logs ingested via the Splunk Add-on for Okta + Identity Cloud. Monitoring these events is crucial for maintaining the integrity + and security of authentication mechanisms. Unauthorized or anomalous changes could + indicate potential security breaches or misconfigurations. If confirmed malicious, + attackers could manipulate authentication processes, potentially gaining unauthorized + access or disrupting identity management systems. +search: '`okta` eventType IN ("system.idp.lifecycle.activate","system.idp.lifecycle.create","system.idp.lifecycle.delete","system.idp.lifecycle.deactivate") + | stats count min(_time) as firstTime max(_time) as lastTime values(target{}.id) + as target_id values(target{}.type) as target_modified by src dest src_user_id user + user_agent command description | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` + | `okta_idp_lifecycle_modifications_filter`' +how_to_implement: The analytic leverages Okta OktaIm2 logs to be ingested using the + Splunk Add-on for Okta Identity Cloud (https://splunkbase.splunk.com/app/6553). +known_false_positives: It's possible for legitimate administrative actions or automated + processes to trigger this detection, especially if there are bulk modifications + to Okta IDP lifecycle events. Review the context of the modification, such as the + user making the change and the specific lifecycle event modified, to determine if + it aligns with expected behavior. references: - https://www.obsidiansecurity.com/blog/behind-the-breach-cross-tenant-impersonation-in-okta/ - https://splunkbase.splunk.com/app/6553 @@ -20,7 +36,12 @@ drilldown_searches: earliest_offset: $info_min_time$ latest_offset: $info_max_time$ - name: View risk events for the last 7 days for - "$user$" - search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' + search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$") + starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime + values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) + as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) + as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` + | `security_content_ctime(lastTime)`' earliest_offset: $info_min_time$ latest_offset: $info_max_time$ rba: @@ -29,7 +50,7 @@ rba: risk_objects: - field: user type: User - risk_score: 81.0 + risk_score: 81 threat_objects: - field: src type: IP Address @@ -61,6 +82,7 @@ tags: tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1556/okta_idp/okta.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1556/okta_idp/okta.log source: Okta sourcetype: OktaIM2:log diff --git a/detections/application/okta_mfa_exhaustion_hunt.yml b/detections/application/okta_mfa_exhaustion_hunt.yml index d1c81d5d18..5362c6a63c 100644 --- a/detections/application/okta_mfa_exhaustion_hunt.yml +++ b/detections/application/okta_mfa_exhaustion_hunt.yml @@ -5,12 +5,33 @@ date: '2024-10-17' author: Michael Haag, Marissa Bower, Mauricio Velazco, Splunk status: production type: Hunting -description: The following analytic detects patterns of successful and failed Okta MFA push attempts to identify potential MFA exhaustion attacks. It leverages Okta event logs, specifically focusing on push verification events, and uses statistical evaluations to determine suspicious activity. This activity is significant as it may indicate an attacker attempting to bypass MFA by overwhelming the user with push notifications. If confirmed malicious, this could lead to unauthorized access, compromising the security of the affected accounts and potentially the entire environment. +description: The following analytic detects patterns of successful and failed Okta + MFA push attempts to identify potential MFA exhaustion attacks. It leverages Okta + event logs, specifically focusing on push verification events, and uses statistical + evaluations to determine suspicious activity. This activity is significant as it + may indicate an attacker attempting to bypass MFA by overwhelming the user with + push notifications. If confirmed malicious, this could lead to unauthorized access, + compromising the security of the affected accounts and potentially the entire environment. data_source: - Okta -search: '`okta` eventType=system.push.send_factor_verify_push OR ((legacyEventType=core.user.factor.attempt_success) AND (debugContext.debugData.factor=OKTA_VERIFY_PUSH)) OR ((legacyEventType=core.user.factor.attempt_fail) AND (debugContext.debugData.factor=OKTA_VERIFY_PUSH)) | stats count(eval(legacyEventType="core.user.factor.attempt_success")) as successes count(eval(legacyEventType="core.user.factor.attempt_fail")) as failures count(eval(eventType="system.push.send_factor_verify_push")) as pushes by user,_time | stats latest(_time) as lasttime earliest(_time) as firsttime sum(successes) as successes sum(failures) as failures sum(pushes) as pushes by user | eval seconds=lasttime-firsttime | eval lasttime=strftime(lasttime, "%c") | search (pushes>1) | eval totalattempts=successes+failures | eval finding="Normal authentication pattern" | eval finding=if(failures==pushes AND pushes>1,"Authentication attempts not successful because multiple pushes denied",finding) | eval finding=if(totalattempts==0,"Multiple pushes sent and ignored",finding) | eval finding=if(successes>0 AND pushes>3,"Probably should investigate. Multiple pushes sent, eventual successful authentication!",finding) | `okta_mfa_exhaustion_hunt_filter`' -how_to_implement: The analytic leverages Okta OktaIm2 logs to be ingested using the Splunk Add-on for Okta Identity Cloud (https://splunkbase.splunk.com/app/6553). -known_false_positives: False positives may be present. Tune Okta and tune the analytic to ensure proper fidelity. Modify risk score as needed. Drop to anomaly until tuning is complete. +search: '`okta` eventType=system.push.send_factor_verify_push OR ((legacyEventType=core.user.factor.attempt_success) + AND (debugContext.debugData.factor=OKTA_VERIFY_PUSH)) OR ((legacyEventType=core.user.factor.attempt_fail) + AND (debugContext.debugData.factor=OKTA_VERIFY_PUSH)) | stats count(eval(legacyEventType="core.user.factor.attempt_success")) as + successes count(eval(legacyEventType="core.user.factor.attempt_fail")) as failures + count(eval(eventType="system.push.send_factor_verify_push")) as pushes by user,_time + | stats latest(_time) as lasttime earliest(_time) as firsttime sum(successes) as + successes sum(failures) as failures sum(pushes) as pushes by user | eval seconds=lasttime-firsttime + | eval lasttime=strftime(lasttime, "%c") | search (pushes>1) | eval totalattempts=successes+failures + | eval finding="Normal authentication pattern" | eval finding=if(failures==pushes + AND pushes>1,"Authentication attempts not successful because multiple pushes denied",finding) + | eval finding=if(totalattempts==0,"Multiple pushes sent and ignored",finding) | + eval finding=if(successes>0 AND pushes>3,"Probably should investigate. Multiple + pushes sent, eventual successful authentication!",finding) | `okta_mfa_exhaustion_hunt_filter`' +how_to_implement: The analytic leverages Okta OktaIm2 logs to be ingested using the + Splunk Add-on for Okta Identity Cloud (https://splunkbase.splunk.com/app/6553). +known_false_positives: False positives may be present. Tune Okta and tune the analytic + to ensure proper fidelity. Modify risk score as needed. Drop to anomaly until tuning + is complete. references: - https://developer.okta.com/docs/reference/api/event-types/?q=user.acount.lock - https://sec.okta.com/everythingisyes @@ -20,7 +41,7 @@ rba: risk_objects: - field: user type: User - risk_score: 18.0 + risk_score: 18 threat_objects: [] tags: analytic_story: @@ -46,6 +67,7 @@ tags: tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1621/okta_multiple_failed_mfa_pushes/okta_multiple_failed_mfa_pushes.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1621/okta_multiple_failed_mfa_pushes/okta_multiple_failed_mfa_pushes.log source: Okta sourcetype: OktaIM2:log diff --git a/detections/application/okta_mismatch_between_source_and_response_for_verify_push_request.yml b/detections/application/okta_mismatch_between_source_and_response_for_verify_push_request.yml index 975b1e9934..660f55e0c4 100644 --- a/detections/application/okta_mismatch_between_source_and_response_for_verify_push_request.yml +++ b/detections/application/okta_mismatch_between_source_and_response_for_verify_push_request.yml @@ -7,10 +7,38 @@ type: TTP status: experimental data_source: - Okta -description: The following analytic identifies discrepancies between the source and response events for Okta Verify Push requests, indicating potential suspicious behavior. It leverages Okta System Log events, specifically `system.push.send_factor_verify_push` and `user.authentication.auth_via_mfa` with the factor "OKTA_VERIFY_PUSH." The detection groups events by SessionID, calculates the ratio of successful sign-ins to push requests, and checks for session roaming and new device/IP usage. This activity is significant as it may indicate push spam or unauthorized access attempts. If confirmed malicious, attackers could bypass MFA, leading to unauthorized access to sensitive systems. -search: '`okta` eventType IN (system.push.send_factor_verify_push) OR (eventType IN (user.authentication.auth_via_mfa) debugContext.debugData.factor="OKTA_VERIFY_PUSH") | eval groupby="authenticationContext.externalSessionId" | eval group_push_time=_time | bin span=2s group_push_time | fillnull value=NULL | stats min(_time) as _time by authenticationContext.externalSessionId eventType debugContext.debugData.factor outcome.result actor.alternateId client.device client.ipAddress client.userAgent.rawUserAgent debugContext.debugData.behaviors group_push_time groupby | iplocation client.ipAddress | fields - lat, lon, group_push_time | stats min(_time) as _time dc(client.ipAddress) as dc_ip sum(eval(if(eventType="system.push.send_factor_verify_push" AND "outcome.result"="SUCCESS",1,0))) as total_pushes sum(eval(if(eventType="user.authentication.auth_via_mfa" AND "outcome.result"="SUCCESS",1,0))) as total_successes sum(eval(if(eventType="user.authentication.auth_via_mfa" AND "outcome.result"="FAILURE",1,0))) as total_rejected sum(eval(if(eventType="system.push.send_factor_verify_push" AND "debugContext.debugData.behaviors" LIKE "%New Device=POSITIVE%",1,0))) as suspect_device_from_source sum(eval(if(eventType="system.push.send_factor_verify_push" AND "debugContext.debugData.behaviors" LIKE "%New IP=POSITIVE%",0,0))) as suspect_ip_from_source values(eval(if(eventType="system.push.send_factor_verify_push","client.ipAddress",""))) as src values(eval(if(eventType="user.authentication.auth_via_mfa","client.ipAddress",""))) as dest values(*) as * by groupby | eval ratio = round(total_successes/total_pushes,2) | search ((ratio < 0.5 AND total_pushes > 1) OR (total_rejected > 0)) AND dc_ip > 1 AND suspect_device_from_source > 0 AND suspect_ip_from_source > 0 | `okta_mismatch_between_source_and_response_for_verify_push_request_filter`' -how_to_implement: The analytic leverages Okta OktaIm2 logs to be ingested using the Splunk Add-on for Okta Identity Cloud (https://splunkbase.splunk.com/app/6553). -known_false_positives: False positives may be present based on organization size and configuration of Okta. Monitor, tune and filter as needed. +description: The following analytic identifies discrepancies between the source and + response events for Okta Verify Push requests, indicating potential suspicious behavior. + It leverages Okta System Log events, specifically `system.push.send_factor_verify_push` + and `user.authentication.auth_via_mfa` with the factor "OKTA_VERIFY_PUSH." The detection + groups events by SessionID, calculates the ratio of successful sign-ins to push + requests, and checks for session roaming and new device/IP usage. This activity + is significant as it may indicate push spam or unauthorized access attempts. If + confirmed malicious, attackers could bypass MFA, leading to unauthorized access + to sensitive systems. +search: '`okta` eventType IN (system.push.send_factor_verify_push) OR (eventType IN + (user.authentication.auth_via_mfa) debugContext.debugData.factor="OKTA_VERIFY_PUSH") + | eval groupby="authenticationContext.externalSessionId" | eval group_push_time=_time + | bin span=2s group_push_time | fillnull value=NULL | stats min(_time) as _time + by authenticationContext.externalSessionId eventType debugContext.debugData.factor + outcome.result actor.alternateId client.device client.ipAddress client.userAgent.rawUserAgent + debugContext.debugData.behaviors group_push_time groupby | iplocation client.ipAddress + | fields - lat, lon, group_push_time | stats min(_time) as _time dc(client.ipAddress) + as dc_ip sum(eval(if(eventType="system.push.send_factor_verify_push" AND "outcome.result"="SUCCESS",1,0))) + as total_pushes sum(eval(if(eventType="user.authentication.auth_via_mfa" AND "outcome.result"="SUCCESS",1,0))) + as total_successes sum(eval(if(eventType="user.authentication.auth_via_mfa" AND + "outcome.result"="FAILURE",1,0))) as total_rejected sum(eval(if(eventType="system.push.send_factor_verify_push" + AND "debugContext.debugData.behaviors" LIKE "%New Device=POSITIVE%",1,0))) as suspect_device_from_source + sum(eval(if(eventType="system.push.send_factor_verify_push" AND "debugContext.debugData.behaviors" + LIKE "%New IP=POSITIVE%",0,0))) as suspect_ip_from_source values(eval(if(eventType="system.push.send_factor_verify_push","client.ipAddress",""))) + as src values(eval(if(eventType="user.authentication.auth_via_mfa","client.ipAddress",""))) + as dest values(*) as * by groupby | eval ratio = round(total_successes/total_pushes,2) + | search ((ratio < 0.5 AND total_pushes > 1) OR (total_rejected > 0)) AND dc_ip + > 1 AND suspect_device_from_source > 0 AND suspect_ip_from_source > 0 | `okta_mismatch_between_source_and_response_for_verify_push_request_filter`' +how_to_implement: The analytic leverages Okta OktaIm2 logs to be ingested using the + Splunk Add-on for Okta Identity Cloud (https://splunkbase.splunk.com/app/6553). +known_false_positives: False positives may be present based on organization size and + configuration of Okta. Monitor, tune and filter as needed. references: - https://attack.mitre.org/techniques/T1621 - https://splunkbase.splunk.com/app/6553 @@ -20,7 +48,7 @@ rba: risk_objects: - field: actor.alternateId type: User - risk_score: 64.0 + risk_score: 64 threat_objects: [] tags: analytic_story: diff --git a/detections/application/okta_multi_factor_authentication_disabled.yml b/detections/application/okta_multi_factor_authentication_disabled.yml index 443c20a4b0..5b7827ba9c 100644 --- a/detections/application/okta_multi_factor_authentication_disabled.yml +++ b/detections/application/okta_multi_factor_authentication_disabled.yml @@ -7,10 +7,24 @@ data_source: - Okta type: TTP status: production -description: The following analytic identifies an attempt to disable multi-factor authentication (MFA) for an Okta user. It leverages OktaIM2 logs to detect when the 'user.mfa.factor.deactivate' command is executed. This activity is significant because disabling MFA can allow an adversary to maintain persistence within the environment using a compromised valid account. If confirmed malicious, this action could enable attackers to bypass additional security layers, potentially leading to unauthorized access to sensitive information and prolonged undetected presence in the network. -search: '| tstats `security_content_summariesonly` count max(_time) as lastTime, min(_time) as firstTime from datamodel=Change where sourcetype="OktaIM2:log" All_Changes.object_category=User AND All_Changes.action=modified All_Changes.command=user.mfa.factor.deactivate by All_Changes.user All_Changes.result All_Changes.command sourcetype All_Changes.src | `drop_dm_object_name("All_Changes")` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | `okta_multi_factor_authentication_disabled_filter`' -how_to_implement: The analytic leverages Okta OktaIm2 logs to be ingested using the Splunk Add-on for Okta Identity Cloud (https://splunkbase.splunk.com/app/6553). -known_false_positives: Legitimate use case may require for users to disable MFA. Filter lightly and monitor for any unusual activity. +description: The following analytic identifies an attempt to disable multi-factor + authentication (MFA) for an Okta user. It leverages OktaIM2 logs to detect when + the 'user.mfa.factor.deactivate' command is executed. This activity is significant + because disabling MFA can allow an adversary to maintain persistence within the + environment using a compromised valid account. If confirmed malicious, this action + could enable attackers to bypass additional security layers, potentially leading + to unauthorized access to sensitive information and prolonged undetected presence + in the network. +search: '| tstats `security_content_summariesonly` count max(_time) as lastTime, min(_time) + as firstTime from datamodel=Change where sourcetype="OktaIM2:log" All_Changes.object_category=User + AND All_Changes.action=modified All_Changes.command=user.mfa.factor.deactivate by + All_Changes.user All_Changes.result All_Changes.command sourcetype All_Changes.src + | `drop_dm_object_name("All_Changes")` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` + | `okta_multi_factor_authentication_disabled_filter`' +how_to_implement: The analytic leverages Okta OktaIm2 logs to be ingested using the + Splunk Add-on for Okta Identity Cloud (https://splunkbase.splunk.com/app/6553). +known_false_positives: Legitimate use case may require for users to disable MFA. Filter + lightly and monitor for any unusual activity. references: - https://attack.mitre.org/techniques/T1556/ - https://splunkbase.splunk.com/app/6553 @@ -20,7 +34,12 @@ drilldown_searches: earliest_offset: $info_min_time$ latest_offset: $info_max_time$ - name: View risk events for the last 7 days for - "$user$" - search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' + search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$") + starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime + values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) + as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) + as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` + | `security_content_ctime(lastTime)`' earliest_offset: $info_min_time$ latest_offset: $info_max_time$ rba: @@ -29,7 +48,7 @@ rba: risk_objects: - field: user type: User - risk_score: 30.0 + risk_score: 30 threat_objects: - field: src type: IP Address @@ -60,6 +79,7 @@ tags: tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1556.006/okta_mfa_method_disabled/okta_mfa_method_disabled.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1556.006/okta_mfa_method_disabled/okta_mfa_method_disabled.log source: Okta sourcetype: OktaIM2:log diff --git a/detections/application/okta_multiple_accounts_locked_out.yml b/detections/application/okta_multiple_accounts_locked_out.yml index 69e82d58c8..b10cbad765 100644 --- a/detections/application/okta_multiple_accounts_locked_out.yml +++ b/detections/application/okta_multiple_accounts_locked_out.yml @@ -7,10 +7,23 @@ data_source: - Okta type: Anomaly status: production -description: The following analytic detects multiple Okta accounts being locked out within a short period. It uses the user.account.lock event from Okta logs, aggregated over a 5-minute window, to identify this behavior. This activity is significant as it may indicate a brute force or password spraying attack, where an adversary attempts to guess passwords, leading to account lockouts. If confirmed malicious, this could result in potential account takeovers or unauthorized access to sensitive Okta accounts, posing a significant security risk. -search: '| tstats `security_content_summariesonly` count max(_time) as lastTime, min(_time) as firstTime values(All_Changes.user) as user from datamodel=Change where All_Changes.change_type=AAA All_Changes.object_category=User AND All_Changes.action=lockout AND All_Changes.command=user.account.lock by _time span=5m All_Changes.result All_Changes.command sourcetype All_Changes.src | where count > 5 | `drop_dm_object_name("All_Changes")` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | `okta_multiple_accounts_locked_out_filter`' -how_to_implement: The analytic leverages Okta OktaIm2 logs to be ingested using the Splunk Add-on for Okta Identity Cloud (https://splunkbase.splunk.com/app/6553). -known_false_positives: Multiple account lockouts may be also triggered by an application malfunction. Filter as needed, and monitor for any unusual activity. +description: The following analytic detects multiple Okta accounts being locked out + within a short period. It uses the user.account.lock event from Okta logs, aggregated + over a 5-minute window, to identify this behavior. This activity is significant + as it may indicate a brute force or password spraying attack, where an adversary + attempts to guess passwords, leading to account lockouts. If confirmed malicious, + this could result in potential account takeovers or unauthorized access to sensitive + Okta accounts, posing a significant security risk. +search: '| tstats `security_content_summariesonly` count max(_time) as lastTime, min(_time) + as firstTime values(All_Changes.user) as user from datamodel=Change where All_Changes.change_type=AAA + All_Changes.object_category=User AND All_Changes.action=lockout AND All_Changes.command=user.account.lock + by _time span=5m All_Changes.result All_Changes.command sourcetype All_Changes.src + | where count > 5 | `drop_dm_object_name("All_Changes")` | `security_content_ctime(firstTime)` + | `security_content_ctime(lastTime)` | `okta_multiple_accounts_locked_out_filter`' +how_to_implement: The analytic leverages Okta OktaIm2 logs to be ingested using the + Splunk Add-on for Okta Identity Cloud (https://splunkbase.splunk.com/app/6553). +known_false_positives: Multiple account lockouts may be also triggered by an application + malfunction. Filter as needed, and monitor for any unusual activity. references: - https://attack.mitre.org/techniques/T1110/ - https://splunkbase.splunk.com/app/6553 @@ -20,7 +33,12 @@ drilldown_searches: earliest_offset: $info_min_time$ latest_offset: $info_max_time$ - name: View risk events for the last 7 days for - "$user$" - search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' + search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$") + starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime + values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) + as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) + as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` + | `security_content_ctime(lastTime)`' earliest_offset: $info_min_time$ latest_offset: $info_max_time$ rba: @@ -29,7 +47,7 @@ rba: risk_objects: - field: user type: User - risk_score: 49.0 + risk_score: 49 threat_objects: - field: src type: IP Address @@ -59,6 +77,7 @@ tags: tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1110/okta_multiple_accounts_lockout/okta_multiple_accounts_lockout.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1110/okta_multiple_accounts_lockout/okta_multiple_accounts_lockout.log source: Okta sourcetype: OktaIM2:log diff --git a/detections/application/okta_multiple_failed_mfa_requests_for_user.yml b/detections/application/okta_multiple_failed_mfa_requests_for_user.yml index 0aa7c57310..7929fda2c6 100644 --- a/detections/application/okta_multiple_failed_mfa_requests_for_user.yml +++ b/detections/application/okta_multiple_failed_mfa_requests_for_user.yml @@ -7,10 +7,23 @@ data_source: - Okta type: Anomaly status: production -description: The following analytic identifies multiple failed multi-factor authentication (MFA) requests for a single user within an Okta tenant. It triggers when more than 10 MFA attempts fail within 5 minutes, using Okta event logs to detect this pattern. This activity is significant as it may indicate an adversary attempting to bypass MFA by bombarding the user with repeated authentication requests, a technique used by threat actors like Lapsus and APT29. If confirmed malicious, this could lead to unauthorized access, potentially compromising sensitive information and systems. -search: '`okta` eventType=user.authentication.auth_via_mfa outcome.result=FAILURE debugContext.debugData.factor!=PASSWORD_AS_FACTOR | bucket _time span=5m | stats count min(_time) as firstTime max(_time) as lastTime values(displayMessage) values(src_ip) as src_ip values(debugContext.debugData.factor) by _time src_user | where count >= 10 | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | `okta_multiple_failed_mfa_requests_for_user_filter`' -how_to_implement: The analytic leverages Okta OktaIm2 logs to be ingested using the Splunk Add-on for Okta Identity Cloud (https://splunkbase.splunk.com/app/6553). -known_false_positives: Multiple Failed MFA requests may also be a sign of authentication or application issues. Filter as needed and monitor for any unusual activity. +description: The following analytic identifies multiple failed multi-factor authentication + (MFA) requests for a single user within an Okta tenant. It triggers when more than + 10 MFA attempts fail within 5 minutes, using Okta event logs to detect this pattern. + This activity is significant as it may indicate an adversary attempting to bypass + MFA by bombarding the user with repeated authentication requests, a technique used + by threat actors like Lapsus and APT29. If confirmed malicious, this could lead + to unauthorized access, potentially compromising sensitive information and systems. +search: '`okta` eventType=user.authentication.auth_via_mfa outcome.result=FAILURE + debugContext.debugData.factor!=PASSWORD_AS_FACTOR | bucket _time span=5m | stats + count min(_time) as firstTime max(_time) as lastTime values(displayMessage) values(src_ip) + as src_ip values(debugContext.debugData.factor) by _time src_user | where count + >= 10 | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` + | `okta_multiple_failed_mfa_requests_for_user_filter`' +how_to_implement: The analytic leverages Okta OktaIm2 logs to be ingested using the + Splunk Add-on for Okta Identity Cloud (https://splunkbase.splunk.com/app/6553). +known_false_positives: Multiple Failed MFA requests may also be a sign of authentication + or application issues. Filter as needed and monitor for any unusual activity. references: - https://attack.mitre.org/techniques/T1621/ drilldown_searches: @@ -19,7 +32,12 @@ drilldown_searches: earliest_offset: $info_min_time$ latest_offset: $info_max_time$ - name: View risk events for the last 7 days for - "$src_user$" - search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$src_user$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' + search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$src_user$") + starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime + values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) + as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) + as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` + | `security_content_ctime(lastTime)`' earliest_offset: $info_min_time$ latest_offset: $info_max_time$ rba: @@ -27,7 +45,7 @@ rba: risk_objects: - field: src_user type: User - risk_score: 42.0 + risk_score: 42 threat_objects: - field: src_ip type: IP Address @@ -56,6 +74,7 @@ tags: tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1621/okta_multiple_failed_mfa_requests/okta_multiple_failed_mfa_requests.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1621/okta_multiple_failed_mfa_requests/okta_multiple_failed_mfa_requests.log source: Okta sourcetype: OktaIM2:log diff --git a/detections/application/okta_multiple_failed_requests_to_access_applications.yml b/detections/application/okta_multiple_failed_requests_to_access_applications.yml index 1ef5fdd8d6..52d320b7fe 100644 --- a/detections/application/okta_multiple_failed_requests_to_access_applications.yml +++ b/detections/application/okta_multiple_failed_requests_to_access_applications.yml @@ -7,10 +7,34 @@ type: Hunting status: experimental data_source: - Okta -description: The following analytic detects multiple failed attempts to access applications in Okta, potentially indicating the reuse of a stolen web session cookie. It leverages Okta logs to evaluate policy and SSO events, aggregating data by user, session, and IP. The detection triggers when more than half of the app sign-on attempts are unsuccessful across multiple applications. This activity is significant as it may indicate an attempt to bypass authentication mechanisms. If confirmed malicious, it could lead to unauthorized access to sensitive applications and data, posing a significant security risk. -search: '`okta` target{}.type=AppInstance (eventType=policy.evaluate_sign_on outcome.result=CHALLENGE) OR (eventType=user.authentication.sso outcome.result=SUCCESS) | eval targets=mvzip(''target{}.type'', ''target{}.displayName'', ": ") | eval targets=mvfilter(targets LIKE "AppInstance%") | stats count min(_time) as _time values(outcome.result) as outcome.result dc(eval(if(eventType="policy.evaluate_sign_on",targets,NULL))) as total_challenges sum(eval(if(eventType="user.authentication.sso",1,0))) as total_successes by authenticationContext.externalSessionId targets actor.alternateId client.ipAddress | search total_challenges > 0 | stats min(_time) as _time values(*) as * sum(total_challenges) as total_challenges sum(total_successes) as total_successes values(eval(if("outcome.result"="SUCCESS",targets,NULL))) as success_apps values(eval(if(":outcome.result"!="SUCCESS",targets,NULL))) as no_success_apps by authenticationContext.externalSessionId actor.alternateId client.ipAddress | fillnull | eval ratio=round(total_successes/total_challenges,2), severity="HIGH", mitre_technique_id="T1538", description="actor.alternateId". " from " . "client.ipAddress" . " seen opening " . total_challenges . " chiclets/apps with " . total_successes . " challenges successfully passed" | fields - count, targets | search ratio < 0.5 total_challenges > 2 | `okta_multiple_failed_requests_to_access_applications_filter`' -how_to_implement: This analytic is specific to Okta and requires Okta:im2 logs to be ingested. -known_false_positives: False positives may be present based on organization size and configuration of Okta. +description: The following analytic detects multiple failed attempts to access applications + in Okta, potentially indicating the reuse of a stolen web session cookie. It leverages + Okta logs to evaluate policy and SSO events, aggregating data by user, session, + and IP. The detection triggers when more than half of the app sign-on attempts are + unsuccessful across multiple applications. This activity is significant as it may + indicate an attempt to bypass authentication mechanisms. If confirmed malicious, + it could lead to unauthorized access to sensitive applications and data, posing + a significant security risk. +search: "`okta` target{}.type=AppInstance (eventType=policy.evaluate_sign_on outcome.result=CHALLENGE) + OR (eventType=user.authentication.sso outcome.result=SUCCESS) | eval targets=mvzip('target{}.type', + 'target{}.displayName', \": \") | eval targets=mvfilter(targets LIKE \"AppInstance%\"\ + ) | stats count min(_time) as _time values(outcome.result) as outcome.result dc(eval(if(eventType=\"\ + policy.evaluate_sign_on\",targets,NULL))) as total_challenges sum(eval(if(eventType=\"\ + user.authentication.sso\",1,0))) as total_successes by authenticationContext.externalSessionId + targets actor.alternateId client.ipAddress | search total_challenges > 0 | stats + min(_time) as _time values(*) as * sum(total_challenges) as total_challenges sum(total_successes) + as total_successes values(eval(if(\"outcome.result\"=\"SUCCESS\",targets,NULL))) + as success_apps values(eval(if(\":outcome.result\"!=\"SUCCESS\",targets,NULL))) + as no_success_apps by authenticationContext.externalSessionId actor.alternateId + client.ipAddress | fillnull | eval ratio=round(total_successes/total_challenges,2), + severity=\"HIGH\", mitre_technique_id=\"T1538\", description=\"actor.alternateId\"\ + . \" from \" . \"client.ipAddress\" . \" seen opening \" . total_challenges . \"\ + \ chiclets/apps with \" . total_successes . \" challenges successfully passed\" + | fields - count, targets | search ratio < 0.5 total_challenges > 2 | `okta_multiple_failed_requests_to_access_applications_filter`" +how_to_implement: This analytic is specific to Okta and requires Okta:im2 logs to + be ingested. +known_false_positives: False positives may be present based on organization size and + configuration of Okta. references: - https://attack.mitre.org/techniques/T1538 - https://attack.mitre.org/techniques/T1550/004 @@ -19,7 +43,7 @@ rba: risk_objects: - field: actor.alternateId type: User - risk_score: 56.0 + risk_score: 56 threat_objects: [] tags: analytic_story: diff --git a/detections/application/okta_multiple_users_failing_to_authenticate_from_ip.yml b/detections/application/okta_multiple_users_failing_to_authenticate_from_ip.yml index 55f66abc0c..a529c2e2b0 100644 --- a/detections/application/okta_multiple_users_failing_to_authenticate_from_ip.yml +++ b/detections/application/okta_multiple_users_failing_to_authenticate_from_ip.yml @@ -7,10 +7,25 @@ data_source: - Okta type: Anomaly status: production -description: The following analytic identifies instances where more than 10 unique user accounts have failed to authenticate from a single IP address within a 5-minute window in an Okta tenant. This detection uses OktaIm2 logs ingested via the Splunk Add-on for Okta Identity Cloud. Such activity is significant as it may indicate brute-force attacks or password spraying attempts. If confirmed malicious, this behavior suggests an external entity is attempting to compromise multiple user accounts, potentially leading to unauthorized access to organizational resources and data breaches. -search: '| tstats `security_content_summariesonly` count max(_time) as lastTime, min(_time) as firstTime dc(Authentication.user) as unique_accounts values(Authentication.signature) as signature values(Authentication.user) as user values(Authentication.app) as app values(Authentication.authentication_method) as authentication_method from datamodel=Authentication where Authentication.action="failure" AND Authentication.signature=user.session.start by _time span=5m Authentication.src sourcetype | where unique_accounts > 9 | `drop_dm_object_name("Authentication")` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | `okta_multiple_users_failing_to_authenticate_from_ip_filter`' -how_to_implement: The analytic leverages Okta OktaIm2 logs to be ingested using the Splunk Add-on for Okta Identity Cloud (https://splunkbase.splunk.com/app/6553). -known_false_positives: A source Ip failing to authenticate with multiple users in a short period of time is not common legitimate behavior. +description: The following analytic identifies instances where more than 10 unique + user accounts have failed to authenticate from a single IP address within a 5-minute + window in an Okta tenant. This detection uses OktaIm2 logs ingested via the Splunk + Add-on for Okta Identity Cloud. Such activity is significant as it may indicate + brute-force attacks or password spraying attempts. If confirmed malicious, this + behavior suggests an external entity is attempting to compromise multiple user accounts, + potentially leading to unauthorized access to organizational resources and data + breaches. +search: '| tstats `security_content_summariesonly` count max(_time) as lastTime, min(_time) + as firstTime dc(Authentication.user) as unique_accounts values(Authentication.signature) + as signature values(Authentication.user) as user values(Authentication.app) as app + values(Authentication.authentication_method) as authentication_method from datamodel=Authentication + where Authentication.action="failure" AND Authentication.signature=user.session.start + by _time span=5m Authentication.src sourcetype | where unique_accounts > 9 | `drop_dm_object_name("Authentication")` + | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | `okta_multiple_users_failing_to_authenticate_from_ip_filter`' +how_to_implement: The analytic leverages Okta OktaIm2 logs to be ingested using the + Splunk Add-on for Okta Identity Cloud (https://splunkbase.splunk.com/app/6553). +known_false_positives: A source Ip failing to authenticate with multiple users in + a short period of time is not common legitimate behavior. references: - https://attack.mitre.org/techniques/T1110/003/ - https://splunkbase.splunk.com/app/6553 @@ -20,7 +35,12 @@ drilldown_searches: earliest_offset: $info_min_time$ latest_offset: $info_max_time$ - name: View risk events for the last 7 days for - "$user$" - search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' + search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$") + starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime + values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) + as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) + as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` + | `security_content_ctime(lastTime)`' earliest_offset: $info_min_time$ latest_offset: $info_max_time$ rba: @@ -29,7 +49,7 @@ rba: risk_objects: - field: user type: User - risk_score: 54.0 + risk_score: 54 threat_objects: - field: src type: IP Address @@ -59,6 +79,7 @@ tags: tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1110.003/okta_multiple_users_from_ip/okta_multiple_users_from_ip.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1110.003/okta_multiple_users_from_ip/okta_multiple_users_from_ip.log source: Okta sourcetype: OktaIM2:log diff --git a/detections/application/okta_new_api_token_created.yml b/detections/application/okta_new_api_token_created.yml index 99ec1a9653..ac2bb78559 100644 --- a/detections/application/okta_new_api_token_created.yml +++ b/detections/application/okta_new_api_token_created.yml @@ -5,12 +5,25 @@ date: '2024-09-30' author: Michael Haag, Mauricio Velazco, Splunk status: production type: TTP -description: The following analytic detects the creation of a new API token within an Okta tenant. It uses OktaIm2 logs ingested via the Splunk Add-on for Okta Identity Cloud to identify events where the `system.api_token.create` command is executed. This activity is significant because creating a new API token can indicate potential account takeover attempts or unauthorized access, allowing an adversary to maintain persistence. If confirmed malicious, this could enable attackers to execute API calls, access sensitive data, and perform administrative actions within the Okta environment. +description: The following analytic detects the creation of a new API token within + an Okta tenant. It uses OktaIm2 logs ingested via the Splunk Add-on for Okta Identity + Cloud to identify events where the `system.api_token.create` command is executed. + This activity is significant because creating a new API token can indicate potential + account takeover attempts or unauthorized access, allowing an adversary to maintain + persistence. If confirmed malicious, this could enable attackers to execute API + calls, access sensitive data, and perform administrative actions within the Okta + environment. data_source: - Okta -search: '| tstats `security_content_summariesonly` count max(_time) as lastTime, min(_time) as firstTime from datamodel=Change where All_Changes.action=created AND All_Changes.command=system.api_token.create by _time span=5m All_Changes.user All_Changes.result All_Changes.command sourcetype All_Changes.src All_Changes.action All_Changes.object_category | `drop_dm_object_name("All_Changes")` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | `okta_new_api_token_created_filter`' -how_to_implement: The analytic leverages Okta OktaIm2 logs to be ingested using the Splunk Add-on for Okta Identity Cloud (https://splunkbase.splunk.com/app/6553). -known_false_positives: False positives may be present. Tune Okta and tune the analytic to ensure proper fidelity. Modify risk score as needed. +search: '| tstats `security_content_summariesonly` count max(_time) as lastTime, min(_time) + as firstTime from datamodel=Change where All_Changes.action=created AND All_Changes.command=system.api_token.create + by _time span=5m All_Changes.user All_Changes.result All_Changes.command sourcetype + All_Changes.src All_Changes.action All_Changes.object_category | `drop_dm_object_name("All_Changes")` + | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | `okta_new_api_token_created_filter`' +how_to_implement: The analytic leverages Okta OktaIm2 logs to be ingested using the + Splunk Add-on for Okta Identity Cloud (https://splunkbase.splunk.com/app/6553). +known_false_positives: False positives may be present. Tune Okta and tune the analytic + to ensure proper fidelity. Modify risk score as needed. references: - https://developer.okta.com/docs/reference/api/event-types/?q=security.threat.detected - https://splunkbase.splunk.com/app/6553 @@ -20,7 +33,12 @@ drilldown_searches: earliest_offset: $info_min_time$ latest_offset: $info_max_time$ - name: View risk events for the last 7 days for - "$user$" - search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' + search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$") + starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime + values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) + as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) + as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` + | `security_content_ctime(lastTime)`' earliest_offset: $info_min_time$ latest_offset: $info_max_time$ rba: @@ -29,7 +47,7 @@ rba: risk_objects: - field: user type: User - risk_score: 64.0 + risk_score: 64 threat_objects: [] tags: analytic_story: @@ -62,6 +80,7 @@ tags: tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1098.001/okta_new_api_token_created/okta_new_api_token_created.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1098.001/okta_new_api_token_created/okta_new_api_token_created.log source: Okta sourcetype: OktaIM2:log diff --git a/detections/application/okta_new_device_enrolled_on_account.yml b/detections/application/okta_new_device_enrolled_on_account.yml index cdfac3a591..795eb96e5f 100644 --- a/detections/application/okta_new_device_enrolled_on_account.yml +++ b/detections/application/okta_new_device_enrolled_on_account.yml @@ -5,12 +5,25 @@ date: '2024-09-30' author: Michael Haag, Mauricio Velazco, Splunk status: production type: TTP -description: The following analytic identifies when a new device is enrolled on an Okta account. It uses OktaIm2 logs ingested via the Splunk Add-on for Okta Identity Cloud to detect the creation of new device enrollments. This activity is significant as it may indicate a legitimate user setting up a new device or an adversary adding a device to maintain unauthorized access. If confirmed malicious, this could lead to potential account takeover, unauthorized access, and persistent control over the compromised Okta account. Monitoring this behavior is crucial for detecting and mitigating unauthorized access attempts. +description: The following analytic identifies when a new device is enrolled on an + Okta account. It uses OktaIm2 logs ingested via the Splunk Add-on for Okta Identity + Cloud to detect the creation of new device enrollments. This activity is significant + as it may indicate a legitimate user setting up a new device or an adversary adding + a device to maintain unauthorized access. If confirmed malicious, this could lead + to potential account takeover, unauthorized access, and persistent control over + the compromised Okta account. Monitoring this behavior is crucial for detecting + and mitigating unauthorized access attempts. data_source: - Okta -search: '| tstats `security_content_summariesonly` count max(_time) as lastTime, min(_time) as firstTime from datamodel=Change where All_Changes.action=created All_Changes.command=device.enrollment.create by _time span=5m All_Changes.user All_Changes.result All_Changes.command sourcetype All_Changes.src All_Changes.action All_Changes.object_category | `drop_dm_object_name("All_Changes")` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | `okta_new_device_enrolled_on_account_filter`' -how_to_implement: The analytic leverages Okta OktaIm2 logs to be ingested using the Splunk Add-on for Okta Identity Cloud (https://splunkbase.splunk.com/app/6553). -known_false_positives: It is possible that the user has legitimately added a new device to their account. Please verify this activity. +search: '| tstats `security_content_summariesonly` count max(_time) as lastTime, min(_time) + as firstTime from datamodel=Change where All_Changes.action=created All_Changes.command=device.enrollment.create + by _time span=5m All_Changes.user All_Changes.result All_Changes.command sourcetype + All_Changes.src All_Changes.action All_Changes.object_category | `drop_dm_object_name("All_Changes")` + | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | `okta_new_device_enrolled_on_account_filter`' +how_to_implement: The analytic leverages Okta OktaIm2 logs to be ingested using the + Splunk Add-on for Okta Identity Cloud (https://splunkbase.splunk.com/app/6553). +known_false_positives: It is possible that the user has legitimately added a new device + to their account. Please verify this activity. references: - https://attack.mitre.org/techniques/T1098/005/ - https://developer.okta.com/docs/reference/api/event-types/?q=device.enrollment.create @@ -20,7 +33,12 @@ drilldown_searches: earliest_offset: $info_min_time$ latest_offset: $info_max_time$ - name: View risk events for the last 7 days for - "$user$" - search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' + search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$") + starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime + values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) + as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) + as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` + | `security_content_ctime(lastTime)`' earliest_offset: $info_min_time$ latest_offset: $info_max_time$ rba: @@ -29,7 +47,7 @@ rba: risk_objects: - field: user type: User - risk_score: 24.0 + risk_score: 24 threat_objects: [] tags: analytic_story: @@ -58,6 +76,7 @@ tags: tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1098.005/okta_new_device_enrolled/okta_new_device_enrolled.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1098.005/okta_new_device_enrolled/okta_new_device_enrolled.log source: Okta sourcetype: OktaIM2:log diff --git a/detections/application/okta_phishing_detection_with_fastpass_origin_check.yml b/detections/application/okta_phishing_detection_with_fastpass_origin_check.yml index 742735b32e..c6a36e463e 100644 --- a/detections/application/okta_phishing_detection_with_fastpass_origin_check.yml +++ b/detections/application/okta_phishing_detection_with_fastpass_origin_check.yml @@ -7,10 +7,23 @@ type: TTP status: experimental data_source: - Okta -description: The following analytic identifies failed user authentication attempts in Okta due to FastPass declining a phishing attempt. It leverages Okta logs, specifically looking for events where multi-factor authentication (MFA) fails with the reason "FastPass declined phishing attempt." This activity is significant as it indicates that attackers are targeting users with real-time phishing proxies, attempting to capture credentials. If confirmed malicious, this could lead to unauthorized access to user accounts, potentially compromising sensitive information and furthering lateral movement within the organization. -search: '`okta` eventType="user.authentication.auth_via_mfa" AND result="FAILURE" AND outcome.reason="FastPass declined phishing attempt" | stats count min(_time) as firstTime max(_time) as lastTime values(displayMessage) by user eventType client.userAgent.rawUserAgent client.userAgent.browser outcome.reason | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | `okta_phishing_detection_with_fastpass_origin_check_filter`' -how_to_implement: This search is specific to Okta and requires Okta logs to be ingested in your Splunk deployment. -known_false_positives: Fidelity of this is high as Okta is specifying malicious infrastructure. Filter and modify as needed. +description: The following analytic identifies failed user authentication attempts + in Okta due to FastPass declining a phishing attempt. It leverages Okta logs, specifically + looking for events where multi-factor authentication (MFA) fails with the reason + "FastPass declined phishing attempt." This activity is significant as it indicates + that attackers are targeting users with real-time phishing proxies, attempting to + capture credentials. If confirmed malicious, this could lead to unauthorized access + to user accounts, potentially compromising sensitive information and furthering + lateral movement within the organization. +search: '`okta` eventType="user.authentication.auth_via_mfa" AND result="FAILURE" + AND outcome.reason="FastPass declined phishing attempt" | stats count min(_time) + as firstTime max(_time) as lastTime values(displayMessage) by user eventType client.userAgent.rawUserAgent + client.userAgent.browser outcome.reason | `security_content_ctime(firstTime)` | + `security_content_ctime(lastTime)` | `okta_phishing_detection_with_fastpass_origin_check_filter`' +how_to_implement: This search is specific to Okta and requires Okta logs to be ingested + in your Splunk deployment. +known_false_positives: Fidelity of this is high as Okta is specifying malicious infrastructure. + Filter and modify as needed. references: - https://sec.okta.com/fastpassphishingdetection rba: @@ -18,7 +31,7 @@ rba: risk_objects: - field: user type: User - risk_score: 100.0 + risk_score: 100 threat_objects: [] tags: analytic_story: diff --git a/detections/application/okta_risk_threshold_exceeded.yml b/detections/application/okta_risk_threshold_exceeded.yml index 68192b0861..d074e658f5 100644 --- a/detections/application/okta_risk_threshold_exceeded.yml +++ b/detections/application/okta_risk_threshold_exceeded.yml @@ -5,12 +5,35 @@ date: '2024-09-30' author: Michael Haag, Bhavin Patel, Splunk status: production type: Correlation -description: The following correlation identifies when a user exceeds a risk threshold based on multiple suspicious Okta activities. It leverages the Risk Framework from Enterprise Security, aggregating risk events from "Suspicious Okta Activity," "Okta Account Takeover," and "Okta MFA Exhaustion" analytic stories. This detection is significant as it highlights potentially compromised user accounts exhibiting multiple tactics, techniques, and procedures (TTPs) within a 24-hour period. If confirmed malicious, this activity could indicate a serious security breach, allowing attackers to gain unauthorized access, escalate privileges, or persist within the environment. +description: The following correlation identifies when a user exceeds a risk threshold + based on multiple suspicious Okta activities. It leverages the Risk Framework from + Enterprise Security, aggregating risk events from "Suspicious Okta Activity," "Okta + Account Takeover," and "Okta MFA Exhaustion" analytic stories. This detection is + significant as it highlights potentially compromised user accounts exhibiting multiple + tactics, techniques, and procedures (TTPs) within a 24-hour period. If confirmed + malicious, this activity could indicate a serious security breach, allowing attackers + to gain unauthorized access, escalate privileges, or persist within the environment. data_source: - Okta -search: '| tstats `security_content_summariesonly` values(All_Risk.analyticstories) as analyticstories sum(All_Risk.calculated_risk_score) as risk_score, count(All_Risk.calculated_risk_score) as risk_event_count,values(All_Risk.annotations.mitre_attack.mitre_tactic_id) as annotations.mitre_attack.mitre_tactic_id, dc(All_Risk.annotations.mitre_attack.mitre_tactic_id) as mitre_tactic_id_count, values(All_Risk.annotations.mitre_attack.mitre_technique_id) as annotations.mitre_attack.mitre_technique_id, dc(All_Risk.annotations.mitre_attack.mitre_technique_id) as mitre_technique_id_count, values(All_Risk.tag) as tag, values(source) as source, dc(source) as source_count from datamodel=Risk.All_Risk where All_Risk.risk_object_type = user All_Risk.analyticstories IN ("Okta Account Takeover", "Suspicious Okta Activity","Okta MFA Exhaustion") by All_Risk.risk_object,All_Risk.risk_object_type | `drop_dm_object_name("All_Risk")` | search mitre_technique_id_count > 5 | `okta_risk_threshold_exceeded_filter`' -how_to_implement: This search leverages the Risk Framework from Enterprise Security. Ensure that "Suspicious Okta Activity", "Okta Account Takeover", and "Okta MFA Exhaustion" analytic stories are enabled. TTPs may be set to Notables for point detections; anomalies should not be notables but rather risk generators. The correlation relies on risk before generating a notable. Modify the value as needed. -known_false_positives: False positives will be limited to the number of events generated by the analytics tied to the stories. Analytics will need to be tested and tuned, and the risk score reduced as needed based on the organization. +search: '| tstats `security_content_summariesonly` values(All_Risk.analyticstories) + as analyticstories sum(All_Risk.calculated_risk_score) as risk_score, count(All_Risk.calculated_risk_score) + as risk_event_count,values(All_Risk.annotations.mitre_attack.mitre_tactic_id) as + annotations.mitre_attack.mitre_tactic_id, dc(All_Risk.annotations.mitre_attack.mitre_tactic_id) + as mitre_tactic_id_count, values(All_Risk.annotations.mitre_attack.mitre_technique_id) + as annotations.mitre_attack.mitre_technique_id, dc(All_Risk.annotations.mitre_attack.mitre_technique_id) + as mitre_technique_id_count, values(All_Risk.tag) as tag, values(source) as source, + dc(source) as source_count from datamodel=Risk.All_Risk where All_Risk.risk_object_type + = user All_Risk.analyticstories IN ("Okta Account Takeover", "Suspicious Okta Activity","Okta + MFA Exhaustion") by All_Risk.risk_object,All_Risk.risk_object_type | `drop_dm_object_name("All_Risk")` + | search mitre_technique_id_count > 5 | `okta_risk_threshold_exceeded_filter`' +how_to_implement: This search leverages the Risk Framework from Enterprise Security. + Ensure that "Suspicious Okta Activity", "Okta Account Takeover", and "Okta MFA Exhaustion" + analytic stories are enabled. TTPs may be set to Notables for point detections; + anomalies should not be notables but rather risk generators. The correlation relies + on risk before generating a notable. Modify the value as needed. +known_false_positives: False positives will be limited to the number of events generated + by the analytics tied to the stories. Analytics will need to be tested and tuned, + and the risk score reduced as needed based on the organization. references: - https://developer.okta.com/docs/reference/api/event-types - https://sec.okta.com/everythingisyes @@ -20,7 +43,12 @@ drilldown_searches: earliest_offset: $info_min_time$ latest_offset: $info_max_time$ - name: View risk events for the last 7 days for - "$risk_object$" - search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$risk_object$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' + search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$risk_object$") + starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime + values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) + as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) + as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` + | `security_content_ctime(lastTime)`' earliest_offset: $info_min_time$ latest_offset: $info_max_time$ rba: @@ -29,7 +57,7 @@ rba: risk_objects: - field: risk_object type: User - risk_score: 56.0 + risk_score: 56 threat_objects: [] tags: analytic_story: @@ -60,6 +88,7 @@ tags: tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/suspicious_behaviour/okta_account_takeover_risk_events/okta_risk.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/suspicious_behaviour/okta_account_takeover_risk_events/okta_risk.log source: risk_data sourcetype: stash diff --git a/detections/application/okta_successful_single_factor_authentication.yml b/detections/application/okta_successful_single_factor_authentication.yml index f48ff7cfaa..84de178845 100644 --- a/detections/application/okta_successful_single_factor_authentication.yml +++ b/detections/application/okta_successful_single_factor_authentication.yml @@ -7,10 +7,24 @@ data_source: - Okta type: Anomaly status: production -description: The following analytic identifies successful single-factor authentication events against the Okta Dashboard for accounts without Multi-Factor Authentication (MFA) enabled. It detects this activity by analyzing Okta logs for successful authentication events where "Okta Verify" is not used. This behavior is significant as it may indicate a misconfiguration, policy violation, or potential account takeover. If confirmed malicious, an attacker could gain unauthorized access to the account, potentially leading to data breaches or further exploitation within the environment. -search: '`okta` action=success src_user_type = User eventType = user.authentication.verify OR eventType = user.authentication.auth_via_mfa| stats dc(eventType) values(eventType) as eventType values(target{}.displayName) as targets values(debugContext.debugData.url) min(_time) as firstTime max(_time) as lastTime values(authentication_method) by src_ip user action | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | search targets !="Okta Verify" | `okta_successful_single_factor_authentication_filter`' -how_to_implement: This detection utilizes logs from Okta environments and requires the ingestion of OktaIm2 logs through the Splunk Add-on for Okta Identity Cloud (https://splunkbase.splunk.com/app/6553). -known_false_positives: Although not recommended, certain users may be exempt from multi-factor authentication. Adjust the filter as necessary. +description: The following analytic identifies successful single-factor authentication + events against the Okta Dashboard for accounts without Multi-Factor Authentication + (MFA) enabled. It detects this activity by analyzing Okta logs for successful authentication + events where "Okta Verify" is not used. This behavior is significant as it may indicate + a misconfiguration, policy violation, or potential account takeover. If confirmed + malicious, an attacker could gain unauthorized access to the account, potentially + leading to data breaches or further exploitation within the environment. +search: '`okta` action=success src_user_type = User eventType = user.authentication.verify + OR eventType = user.authentication.auth_via_mfa| stats dc(eventType) values(eventType) + as eventType values(target{}.displayName) as targets values(debugContext.debugData.url) + min(_time) as firstTime max(_time) as lastTime values(authentication_method) by + src_ip user action | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` + | search targets !="Okta Verify" | `okta_successful_single_factor_authentication_filter`' +how_to_implement: This detection utilizes logs from Okta environments and requires + the ingestion of OktaIm2 logs through the Splunk Add-on for Okta Identity Cloud + (https://splunkbase.splunk.com/app/6553). +known_false_positives: Although not recommended, certain users may be exempt from + multi-factor authentication. Adjust the filter as necessary. references: - https://sec.okta.com/everythingisyes - https://attack.mitre.org/techniques/T1078/004/ @@ -20,7 +34,12 @@ drilldown_searches: earliest_offset: $info_min_time$ latest_offset: $info_max_time$ - name: View risk events for the last 7 days for - "$user$" - search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' + search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$") + starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime + values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) + as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) + as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` + | `security_content_ctime(lastTime)`' earliest_offset: $info_min_time$ latest_offset: $info_max_time$ rba: @@ -29,7 +48,7 @@ rba: risk_objects: - field: user type: User - risk_score: 48.0 + risk_score: 48 threat_objects: [] tags: analytic_story: @@ -63,6 +82,7 @@ tags: tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1078.004/okta_single_factor_auth/okta_single_factor_auth.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1078.004/okta_single_factor_auth/okta_single_factor_auth.log source: okta_log sourcetype: OktaIM2:log diff --git a/detections/application/okta_suspicious_activity_reported.yml b/detections/application/okta_suspicious_activity_reported.yml index 03f184be08..adf3ec4860 100644 --- a/detections/application/okta_suspicious_activity_reported.yml +++ b/detections/application/okta_suspicious_activity_reported.yml @@ -5,12 +5,27 @@ date: '2024-09-30' author: Michael Haag, Splunk status: production type: TTP -description: The following analytic identifies when an associate reports a login attempt as suspicious via an email from Okta. It leverages Okta Identity Management logs, specifically the `user.account.report_suspicious_activity_by_enduser` event type. This activity is significant as it indicates potential unauthorized access attempts, warranting immediate investigation to prevent possible security breaches. If confirmed malicious, the attacker could gain unauthorized access to sensitive systems and data, leading to data theft, privilege escalation, or further compromise of the environment. +description: The following analytic identifies when an associate reports a login attempt + as suspicious via an email from Okta. It leverages Okta Identity Management logs, + specifically the `user.account.report_suspicious_activity_by_enduser` event type. + This activity is significant as it indicates potential unauthorized access attempts, + warranting immediate investigation to prevent possible security breaches. If confirmed + malicious, the attacker could gain unauthorized access to sensitive systems and + data, leading to data theft, privilege escalation, or further compromise of the + environment. data_source: - Okta -search: '`okta` eventType=user.account.report_suspicious_activity_by_enduser | stats count min(_time) as firstTime max(_time) as lastTime values(displayMessage) by user eventType client.userAgent.rawUserAgent client.userAgent.browser client.geographicalContext.city client.geographicalContext.country | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | `okta_suspicious_activity_reported_filter`' -how_to_implement: This detection utilizes logs from Okta Identity Management (IM) environments. It requires the ingestion of OktaIm2 logs through the Splunk Add-on for Okta Identity Cloud (https://splunkbase.splunk.com/app/6553). Additionally, it necessitates the activation of suspicious activity reporting and training for associates to report such activities. -known_false_positives: False positives should be minimal, given the high fidelity of this detection. marker. +search: '`okta` eventType=user.account.report_suspicious_activity_by_enduser | stats + count min(_time) as firstTime max(_time) as lastTime values(displayMessage) by user + eventType client.userAgent.rawUserAgent client.userAgent.browser client.geographicalContext.city client.geographicalContext.country + | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | `okta_suspicious_activity_reported_filter`' +how_to_implement: This detection utilizes logs from Okta Identity Management (IM) + environments. It requires the ingestion of OktaIm2 logs through the Splunk Add-on + for Okta Identity Cloud (https://splunkbase.splunk.com/app/6553). Additionally, + it necessitates the activation of suspicious activity reporting and training for + associates to report such activities. +known_false_positives: False positives should be minimal, given the high fidelity + of this detection. marker. references: - https://help.okta.com/en-us/Content/Topics/Security/suspicious-activity-reporting.htm drilldown_searches: @@ -19,7 +34,12 @@ drilldown_searches: earliest_offset: $info_min_time$ latest_offset: $info_max_time$ - name: View risk events for the last 7 days for - "$user$" - search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' + search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$") + starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime + values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) + as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) + as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` + | `security_content_ctime(lastTime)`' earliest_offset: $info_min_time$ latest_offset: $info_max_time$ rba: @@ -28,7 +48,7 @@ rba: risk_objects: - field: user type: User - risk_score: 25.0 + risk_score: 25 threat_objects: [] tags: analytic_story: @@ -57,6 +77,7 @@ tags: tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1078/okta_suspicious_activity_reported_by_user/okta_suspicious_activity_reported_by_user.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1078/okta_suspicious_activity_reported_by_user/okta_suspicious_activity_reported_by_user.log source: Okta sourcetype: OktaIM2:log diff --git a/detections/application/okta_suspicious_use_of_a_session_cookie.yml b/detections/application/okta_suspicious_use_of_a_session_cookie.yml index 500a3f19a1..8e1bda8e50 100644 --- a/detections/application/okta_suspicious_use_of_a_session_cookie.yml +++ b/detections/application/okta_suspicious_use_of_a_session_cookie.yml @@ -7,10 +7,27 @@ type: Anomaly status: production data_source: - Okta -description: The following analytic identifies suspicious use of a session cookie by detecting multiple client values (IP, User Agent, etc.) changing for the same Device Token associated with a specific user. It leverages policy evaluation events from successful authentication logs in Okta. This activity is significant as it may indicate an adversary attempting to reuse a stolen web session cookie, potentially bypassing authentication mechanisms. If confirmed malicious, this could allow unauthorized access to user accounts, leading to data breaches or further exploitation within the environment. -search: '`okta` eventType IN (policy.evaluate_sign_on) outcome.result IN (ALLOW, SUCCESS) | stats earliest(_time) as _time, values(client.ipAddress) as src_ip, values(client.userAgent.rawUserAgent) as user_agent, values(client.userAgent.os) as userAgentOS_list, values(client.geographicalContext.city) as city, values(client.userAgent.browser) as userAgentBrowser_list, values(device.os_platform) as okta_device_os, dc(client.userAgent.browser) as dc_userAgentBrowser, dc(client.userAgent.os) as dc_userAgentOS, dc(client.ipAddress) as dc_src_ip, values(outcome.reason) as reason by debugContext.debugData.dtHash, user | where dc_src_ip>1 AND (dc_userAgentOS>1 OR dc_userAgentBrowser>1) | `okta_suspicious_use_of_a_session_cookie_filter`' -how_to_implement: This detection utilizes logs from Okta Identity Management (IM) environments. It requires the ingestion of OktaIm2 logs through the Splunk Add-on for Okta Identity Cloud (https://splunkbase.splunk.com/app/6553). -known_false_positives: False positives may occur, depending on the organization's size and the configuration of Okta. +description: The following analytic identifies suspicious use of a session cookie + by detecting multiple client values (IP, User Agent, etc.) changing for the same + Device Token associated with a specific user. It leverages policy evaluation events + from successful authentication logs in Okta. This activity is significant as it + may indicate an adversary attempting to reuse a stolen web session cookie, potentially + bypassing authentication mechanisms. If confirmed malicious, this could allow unauthorized + access to user accounts, leading to data breaches or further exploitation within + the environment. +search: '`okta` eventType IN (policy.evaluate_sign_on) outcome.result IN (ALLOW, SUCCESS) + | stats earliest(_time) as _time, values(client.ipAddress) as src_ip, values(client.userAgent.rawUserAgent) + as user_agent, values(client.userAgent.os) as userAgentOS_list, values(client.geographicalContext.city) + as city, values(client.userAgent.browser) as userAgentBrowser_list, values(device.os_platform) + as okta_device_os, dc(client.userAgent.browser) as dc_userAgentBrowser, dc(client.userAgent.os) + as dc_userAgentOS, dc(client.ipAddress) as dc_src_ip, values(outcome.reason) as + reason by debugContext.debugData.dtHash, user | where dc_src_ip>1 AND (dc_userAgentOS>1 + OR dc_userAgentBrowser>1) | `okta_suspicious_use_of_a_session_cookie_filter`' +how_to_implement: This detection utilizes logs from Okta Identity Management (IM) + environments. It requires the ingestion of OktaIm2 logs through the Splunk Add-on + for Okta Identity Cloud (https://splunkbase.splunk.com/app/6553). +known_false_positives: False positives may occur, depending on the organization's + size and the configuration of Okta. references: - https://attack.mitre.org/techniques/T1539/ drilldown_searches: @@ -19,7 +36,12 @@ drilldown_searches: earliest_offset: $info_min_time$ latest_offset: $info_max_time$ - name: View risk events for the last 7 days for - "$user$" - search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' + search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$") + starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime + values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) + as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) + as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` + | `security_content_ctime(lastTime)`' earliest_offset: $info_min_time$ latest_offset: $info_max_time$ rba: @@ -28,7 +50,7 @@ rba: risk_objects: - field: user type: User - risk_score: 56.0 + risk_score: 56 threat_objects: [] tags: analytic_story: @@ -58,6 +80,7 @@ tags: tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1539/okta_web_session_multiple_ip/okta_web_session_multiple_ip.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1539/okta_web_session_multiple_ip/okta_web_session_multiple_ip.log source: Okta sourcetype: OktaIM2:log diff --git a/detections/application/okta_threatinsight_threat_detected.yml b/detections/application/okta_threatinsight_threat_detected.yml index 96d656c19d..36df94a080 100644 --- a/detections/application/okta_threatinsight_threat_detected.yml +++ b/detections/application/okta_threatinsight_threat_detected.yml @@ -5,12 +5,26 @@ date: '2024-09-30' author: Michael Haag, Mauricio Velazco, Splunk status: production type: Anomaly -description: The following analytic identifies threats detected by Okta ThreatInsight, such as password spraying, login failures, and high counts of unknown user login attempts. It leverages Okta Identity Management logs, specifically focusing on security.threat.detected events. This activity is significant for a SOC as it highlights potential unauthorized access attempts and credential-based attacks. If confirmed malicious, these activities could lead to unauthorized access, data breaches, and further exploitation of compromised accounts, posing a significant risk to the organization's security posture. +description: The following analytic identifies threats detected by Okta ThreatInsight, + such as password spraying, login failures, and high counts of unknown user login + attempts. It leverages Okta Identity Management logs, specifically focusing on security.threat.detected + events. This activity is significant for a SOC as it highlights potential unauthorized + access attempts and credential-based attacks. If confirmed malicious, these activities + could lead to unauthorized access, data breaches, and further exploitation of compromised + accounts, posing a significant risk to the organization's security posture. data_source: - Okta -search: '`okta` eventType = security.threat.detected | rename client.geographicalContext.country as country, client.geographicalContext.state as state, client.geographicalContext.city as city | stats count min(_time) as firstTime max(_time) as lastTime by app src_ip signature eventType displayMessage client.device city state country user_agent outcome.reason outcome.result severity | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | `okta_threatinsight_threat_detected_filter`' -how_to_implement: This detection utilizes logs from Okta Identity Management (IM) environments. It requires the ingestion of OktaIm2 logs through the Splunk Add-on for Okta Identity Cloud (https://splunkbase.splunk.com/app/6553). -known_false_positives: False positives may occur. It is recommended to fine-tune Okta settings and the analytic to ensure high fidelity. Adjust the risk score as necessary. +search: '`okta` eventType = security.threat.detected | rename client.geographicalContext.country + as country, client.geographicalContext.state as state, client.geographicalContext.city + as city | stats count min(_time) as firstTime max(_time) as lastTime by app src_ip + signature eventType displayMessage client.device city state country user_agent outcome.reason + outcome.result severity | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` + | `okta_threatinsight_threat_detected_filter`' +how_to_implement: This detection utilizes logs from Okta Identity Management (IM) + environments. It requires the ingestion of OktaIm2 logs through the Splunk Add-on + for Okta Identity Cloud (https://splunkbase.splunk.com/app/6553). +known_false_positives: False positives may occur. It is recommended to fine-tune Okta + settings and the analytic to ensure high fidelity. Adjust the risk score as necessary. references: - https://developer.okta.com/docs/reference/api/event-types/?q=security.threat.detected drilldown_searches: @@ -19,7 +33,12 @@ drilldown_searches: earliest_offset: $info_min_time$ latest_offset: $info_max_time$ - name: View risk events for the last 7 days for - "$app$" - search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$app$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' + search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$app$") + starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime + values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) + as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) + as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` + | `security_content_ctime(lastTime)`' earliest_offset: $info_min_time$ latest_offset: $info_max_time$ rba: @@ -28,7 +47,7 @@ rba: risk_objects: - field: app type: Endpoint - risk_score: 25.0 + risk_score: 25 threat_objects: - field: src_ip type: IP Address @@ -63,6 +82,7 @@ tags: tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1078.004/okta_threatinsight_threat_detected/okta_threatinsight_threat_detected.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1078.004/okta_threatinsight_threat_detected/okta_threatinsight_threat_detected.log source: Okta sourcetype: OktaIM2:log diff --git a/detections/application/okta_unauthorized_access_to_application.yml b/detections/application/okta_unauthorized_access_to_application.yml index 7af46ae384..b25d850330 100644 --- a/detections/application/okta_unauthorized_access_to_application.yml +++ b/detections/application/okta_unauthorized_access_to_application.yml @@ -7,10 +7,25 @@ data_source: - Okta type: Anomaly status: production -description: The following analytic identifies attempts by users to access Okta applications that have not been assigned to them. It leverages Okta Identity Management logs, specifically focusing on failed access attempts to unassigned applications. This activity is significant for a SOC as it may indicate potential unauthorized access attempts, which could lead to exposure of sensitive information or disruption of services. If confirmed malicious, such activity could result in data breaches, non-compliance with data protection laws, and overall compromise of the IT environment. -search: '| tstats values(Authentication.app) as app values(Authentication.action) as action values(Authentication.user) as user values(Authentication.reason) as reason from datamodel=Authentication where Authentication.signature=app.generic.unauth_app_access_attempt Authentication.action="failure" by _time Authentication.src Authentication.user | `drop_dm_object_name("Authentication")` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | iplocation src | `okta_unauthorized_access_to_application_filter`' -how_to_implement: This detection utilizes logs from Okta Identity Management (IM) environments and requires the ingestion of OktaIm2 logs through the Splunk Add-on for Okta Identity Cloud (https://splunkbase.splunk.com/app/6553). -known_false_positives: There is a possibility that a user may accidentally click on the wrong application, which could trigger this event. It is advisable to verify the location from which this activity originates. +description: The following analytic identifies attempts by users to access Okta applications + that have not been assigned to them. It leverages Okta Identity Management logs, + specifically focusing on failed access attempts to unassigned applications. This + activity is significant for a SOC as it may indicate potential unauthorized access + attempts, which could lead to exposure of sensitive information or disruption of + services. If confirmed malicious, such activity could result in data breaches, non-compliance + with data protection laws, and overall compromise of the IT environment. +search: '| tstats values(Authentication.app) as app values(Authentication.action) + as action values(Authentication.user) as user values(Authentication.reason) as reason + from datamodel=Authentication where Authentication.signature=app.generic.unauth_app_access_attempt + Authentication.action="failure" by _time Authentication.src Authentication.user + | `drop_dm_object_name("Authentication")` | `security_content_ctime(firstTime)` + | `security_content_ctime(lastTime)` | iplocation src | `okta_unauthorized_access_to_application_filter`' +how_to_implement: This detection utilizes logs from Okta Identity Management (IM) + environments and requires the ingestion of OktaIm2 logs through the Splunk Add-on + for Okta Identity Cloud (https://splunkbase.splunk.com/app/6553). +known_false_positives: There is a possibility that a user may accidentally click on + the wrong application, which could trigger this event. It is advisable to verify + the location from which this activity originates. references: - https://attack.mitre.org/techniques/T1110/003/ drilldown_searches: @@ -19,7 +34,12 @@ drilldown_searches: earliest_offset: $info_min_time$ latest_offset: $info_max_time$ - name: View risk events for the last 7 days for - "$user$" - search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' + search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$") + starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime + values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) + as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) + as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` + | `security_content_ctime(lastTime)`' earliest_offset: $info_min_time$ latest_offset: $info_max_time$ rba: @@ -28,7 +48,7 @@ rba: risk_objects: - field: user type: User - risk_score: 81.0 + risk_score: 81 threat_objects: - field: src type: IP Address @@ -59,6 +79,7 @@ tags: tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1087.004/okta_unauth_access/okta_unauth_access.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1087.004/okta_unauth_access/okta_unauth_access.log source: Okta sourcetype: OktaIM2:log diff --git a/detections/application/okta_user_logins_from_multiple_cities.yml b/detections/application/okta_user_logins_from_multiple_cities.yml index 40cb2a8c0a..4000de3eed 100644 --- a/detections/application/okta_user_logins_from_multiple_cities.yml +++ b/detections/application/okta_user_logins_from_multiple_cities.yml @@ -7,10 +7,29 @@ data_source: - Okta type: Anomaly status: production -description: The following analytic identifies instances where the same Okta user logs in from different cities within a 24-hour period. This detection leverages Okta Identity Management logs, analyzing login events and their geographic locations. Such behavior is significant as it may indicate a compromised account, with an attacker attempting unauthorized access from multiple locations. If confirmed malicious, this activity could lead to account takeovers and data breaches, allowing attackers to access sensitive information and potentially escalate their privileges within the environment. -search: '| tstats `security_content_summariesonly` values(Authentication.app) as app values(Authentication.action) as action values(Authentication.user) as user values(Authentication.reason) as reason values(Authentication.dest) as dest values(Authentication.signature) as signature values(Authentication.method) as method from datamodel=Authentication where Authentication.signature=user.session.start by _time Authentication.src | `drop_dm_object_name("Authentication")` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | iplocation src | stats count min(_time) as firstTime max(_time) as lastTime dc(src) as distinct_src dc(City) as distinct_city values(src) as src values(City) as City values(Country) as Country values(action) as action by user | where distinct_city > 1 | `okta_user_logins_from_multiple_cities_filter`' -how_to_implement: This detection utilizes logs from Okta Identity Management (IM) environments. It requires the ingestion of OktaIm2 logs through the Splunk Add-on for Okta Identity Cloud (https://splunkbase.splunk.com/app/6553). -known_false_positives: It is uncommon for a user to log in from multiple cities simultaneously, which may indicate a false positive. +description: The following analytic identifies instances where the same Okta user + logs in from different cities within a 24-hour period. This detection leverages + Okta Identity Management logs, analyzing login events and their geographic locations. + Such behavior is significant as it may indicate a compromised account, with an attacker + attempting unauthorized access from multiple locations. If confirmed malicious, + this activity could lead to account takeovers and data breaches, allowing attackers + to access sensitive information and potentially escalate their privileges within + the environment. +search: '| tstats `security_content_summariesonly` values(Authentication.app) as + app values(Authentication.action) as action values(Authentication.user) as user + values(Authentication.reason) as reason values(Authentication.dest) as dest values(Authentication.signature) + as signature values(Authentication.method) as method from datamodel=Authentication + where Authentication.signature=user.session.start by _time Authentication.src | + `drop_dm_object_name("Authentication")` | `security_content_ctime(firstTime)` | + `security_content_ctime(lastTime)` | iplocation src | stats count min(_time) as + firstTime max(_time) as lastTime dc(src) as distinct_src dc(City) as distinct_city + values(src) as src values(City) as City values(Country) as Country values(action) + as action by user | where distinct_city > 1 | `okta_user_logins_from_multiple_cities_filter`' +how_to_implement: This detection utilizes logs from Okta Identity Management (IM) + environments. It requires the ingestion of OktaIm2 logs through the Splunk Add-on + for Okta Identity Cloud (https://splunkbase.splunk.com/app/6553). +known_false_positives: It is uncommon for a user to log in from multiple cities simultaneously, + which may indicate a false positive. references: - https://attack.mitre.org/techniques/T1110/003/ drilldown_searches: @@ -19,7 +38,12 @@ drilldown_searches: earliest_offset: $info_min_time$ latest_offset: $info_max_time$ - name: View risk events for the last 7 days for - "$user$" - search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' + search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$") + starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime + values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) + as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) + as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` + | `security_content_ctime(lastTime)`' earliest_offset: $info_min_time$ latest_offset: $info_max_time$ rba: @@ -28,7 +52,7 @@ rba: risk_objects: - field: user type: User - risk_score: 81.0 + risk_score: 81 threat_objects: - field: src type: IP Address @@ -59,6 +83,7 @@ tags: tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1586.003/okta_multiple_city/okta_multiple_city_im2.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1586.003/okta_multiple_city/okta_multiple_city_im2.log source: Okta sourcetype: OktaIM2:log diff --git a/detections/application/pingid_mismatch_auth_source_and_verification_response.yml b/detections/application/pingid_mismatch_auth_source_and_verification_response.yml index 44784fb8b3..50564f00a4 100644 --- a/detections/application/pingid_mismatch_auth_source_and_verification_response.yml +++ b/detections/application/pingid_mismatch_auth_source_and_verification_response.yml @@ -5,12 +5,38 @@ date: '2024-09-30' author: Steven Dick status: production type: TTP -description: The following analytic identifies discrepancies between the IP address of an authentication event and the IP address of the verification response event, focusing on differences in the originating countries. It leverages JSON logs from PingID, comparing the 'auth_Country' and 'verify_Country' fields. This activity is significant as it may indicate suspicious sign-in behavior, such as account compromise or unauthorized access attempts. If confirmed malicious, this could allow attackers to bypass authentication mechanisms, potentially leading to unauthorized access to sensitive systems and data. +description: The following analytic identifies discrepancies between the IP address + of an authentication event and the IP address of the verification response event, + focusing on differences in the originating countries. It leverages JSON logs from + PingID, comparing the 'auth_Country' and 'verify_Country' fields. This activity + is significant as it may indicate suspicious sign-in behavior, such as account compromise + or unauthorized access attempts. If confirmed malicious, this could allow attackers + to bypass authentication mechanisms, potentially leading to unauthorized access + to sensitive systems and data. data_source: - PingID -search: '`pingid` ("result.status" IN ("SUCCESS*","FAIL*","UNSUCCESSFUL*") NOT "result.message" IN ("*pair*","*create*","*delete*")) | eval user = upper(''actors{}.name''), session_id = ''resources{}.websession'', dest = ''resources{}.ipaddress'', reason = ''result.message'', object = ''resources{}.devicemodel'', status = ''result.status'' | join user session_id [ search `pingid` ("result.status" IN ("POLICY") AND "resources{}.ipaddress"=*) AND "result.message" IN("*Action: Authenticate*","*Action: Approve*","*Action: Allowed*") | rex field=result.message "IP Address: (?:N\/A)?(?.+)?\n" | rex field=result.message "Action: (?:N\/A)?(?.+)?\n" | rex field=result.message "Requested Application Name: (?:N\/A)?(?.+)?\n" | rex field=result.message "Requested Application ID: (?:N\/A)?(?.+)?\n" | eval user = upper(''actors{}.name''), session_id = ''resources{}.websession'', src = coalesce(''resources{}.ipaddress'',policy_ipaddress), app = coalesce(Requested_Application_ID,Requested_Application_Name) | fields app, user, session_id, src, signature ] | iplocation prefix=auth_ dest | iplocation prefix=verify_ src | stats count min(_time) as firstTime max(_time) as lastTime values(app) as app values(session_id) as session_id by user, dest, auth_Country, src, verify_Country, object, signature, status, reason | where auth_Country != verify_Country | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | `pingid_mismatch_auth_source_and_verification_response_filter`' -how_to_implement: Target environment must ingest JSON logging from a PingID(PingOne) enterprise environment, either via Webhook or Push Subscription. -known_false_positives: False positives may be generated by users working out the geographic region where the organizations services or technology is hosted. +search: "`pingid` (\"result.status\" IN (\"SUCCESS*\",\"FAIL*\",\"UNSUCCESSFUL*\" + ) NOT \"result.message\" IN (\"*pair*\",\"*create*\",\"*delete*\")) | eval user + = upper('actors{}.name'), session_id = 'resources{}.websession', dest = 'resources{}.ipaddress', + reason = 'result.message', object = 'resources{}.devicemodel', status = 'result.status' + | join user session_id [ search `pingid` (\"result.status\" IN (\"POLICY\") AND + \"resources{}.ipaddress\"=*) AND \"result.message\" IN(\"*Action: Authenticate*\"\ + ,\"*Action: Approve*\",\"*Action: Allowed*\") | rex field=result.message \"IP Address: + (?:N\\/A)?(?.+)?\\n\" | rex field=result.message \"Action: (?:N\\\ + /A)?(?.+)?\\n\" | rex field=result.message \"Requested Application Name: + (?:N\\/A)?(?.+)?\\n\" | rex field=result.message \" + Requested Application ID: (?:N\\/A)?(?.+)?\\n\" | eval + user = upper('actors{}.name'), session_id = 'resources{}.websession', src = coalesce('resources{}.ipaddress',policy_ipaddress), + app = coalesce(Requested_Application_ID,Requested_Application_Name) | fields app, + user, session_id, src, signature ] | iplocation prefix=auth_ dest | iplocation prefix=verify_ + src | stats count min(_time) as firstTime max(_time) as lastTime values(app) as + app values(session_id) as session_id by user, dest, auth_Country, src, verify_Country, + object, signature, status, reason | where auth_Country != verify_Country | `security_content_ctime(firstTime)` + | `security_content_ctime(lastTime)` | `pingid_mismatch_auth_source_and_verification_response_filter`" +how_to_implement: Target environment must ingest JSON logging from a PingID(PingOne) + enterprise environment, either via Webhook or Push Subscription. +known_false_positives: False positives may be generated by users working out the geographic + region where the organizations services or technology is hosted. references: - https://twitter.com/jhencinski/status/1618660062352007174 - https://attack.mitre.org/techniques/T1098/005/ @@ -22,7 +48,12 @@ drilldown_searches: earliest_offset: $info_min_time$ latest_offset: $info_max_time$ - name: View risk events for the last 7 days for - "$user$" and "$src$" - search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$", "$src$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' + search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$", + "$src$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as + lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" + values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" + values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object + | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' earliest_offset: $info_min_time$ latest_offset: $info_max_time$ rba: @@ -31,10 +62,10 @@ rba: risk_objects: - field: user type: User - risk_score: 25.0 + risk_score: 25 - field: src type: IP Address - risk_score: 25.0 + risk_score: 25 threat_objects: - field: object type: Other @@ -65,7 +96,8 @@ tags: tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1621/pingid/pingid.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1621/pingid/pingid.log source: PINGID sourcetype: _json update_timestamp: true diff --git a/detections/application/pingid_multiple_failed_mfa_requests_for_user.yml b/detections/application/pingid_multiple_failed_mfa_requests_for_user.yml index 34f5ea4f83..5d17cf5881 100644 --- a/detections/application/pingid_multiple_failed_mfa_requests_for_user.yml +++ b/detections/application/pingid_multiple_failed_mfa_requests_for_user.yml @@ -5,12 +5,25 @@ date: '2024-09-30' author: Steven Dick status: production type: TTP -description: The following analytic identifies multiple failed multi-factor authentication (MFA) requests for a single user within a PingID environment. It triggers when 10 or more MFA prompts fail within 10 minutes, using JSON logs from PingID. This activity is significant as it may indicate an adversary attempting to bypass MFA by bombarding the user with repeated authentication requests. If confirmed malicious, this could lead to unauthorized access, as the user might eventually accept the fraudulent request, compromising the security of the account and potentially the entire network. +description: The following analytic identifies multiple failed multi-factor authentication + (MFA) requests for a single user within a PingID environment. It triggers when 10 + or more MFA prompts fail within 10 minutes, using JSON logs from PingID. This activity + is significant as it may indicate an adversary attempting to bypass MFA by bombarding + the user with repeated authentication requests. If confirmed malicious, this could + lead to unauthorized access, as the user might eventually accept the fraudulent + request, compromising the security of the account and potentially the entire network. data_source: - PingID -search: '`pingid` "result.status" IN ("FAILURE,authFail","UNSUCCESSFUL_ATTEMPT") | eval time = _time, src = coalesce(''resources{}.ipaddress'',''resources{}.devicemodel''), user = upper(''actors{}.name''), object = ''resources{}.devicemodel'', reason = ''result.message''| bucket span=10m _time | stats dc(_raw) AS mfa_prompts min(time) as firstTime, max(time) as lastTime values(src) as src by user, reason, _time | `security_content_ctime(firstTime)`| `security_content_ctime(lastTime)` | where mfa_prompts >= 10 | `pingid_multiple_failed_mfa_requests_for_user_filter`' -how_to_implement: Target environment must ingest JSON logging from a PingID(PingOne) enterprise environment, either via Webhook or Push Subscription. -known_false_positives: False positives may be generated by normal provisioning workflows for user device registration. +search: "`pingid` \"result.status\" IN (\"FAILURE,authFail\",\"UNSUCCESSFUL_ATTEMPT\"\ + ) | eval time = _time, src = coalesce('resources{}.ipaddress','resources{}.devicemodel'), + user = upper('actors{}.name'), object = 'resources{}.devicemodel', reason = 'result.message'| + bucket span=10m _time | stats dc(_raw) AS mfa_prompts min(time) as firstTime, max(time) + as lastTime values(src) as src by user, reason, _time | `security_content_ctime(firstTime)`| + `security_content_ctime(lastTime)` | where mfa_prompts >= 10 | `pingid_multiple_failed_mfa_requests_for_user_filter`" +how_to_implement: Target environment must ingest JSON logging from a PingID(PingOne) + enterprise environment, either via Webhook or Push Subscription. +known_false_positives: False positives may be generated by normal provisioning workflows + for user device registration. references: - https://therecord.media/russian-hackers-bypass-2fa-by-annoying-victims-with-repeated-push-notifications/ - https://attack.mitre.org/techniques/T1621/ @@ -23,7 +36,12 @@ drilldown_searches: earliest_offset: $info_min_time$ latest_offset: $info_max_time$ - name: View risk events for the last 7 days for - "$user$" - search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' + search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$") + starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime + values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) + as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) + as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` + | `security_content_ctime(lastTime)`' earliest_offset: $info_min_time$ latest_offset: $info_max_time$ rba: @@ -32,7 +50,7 @@ rba: risk_objects: - field: user type: User - risk_score: 50.0 + risk_score: 50 threat_objects: [] tags: analytic_story: @@ -60,6 +78,7 @@ tags: tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1621/pingid/pingid.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1621/pingid/pingid.log source: PINGID sourcetype: _json diff --git a/detections/application/pingid_new_mfa_method_after_credential_reset.yml b/detections/application/pingid_new_mfa_method_after_credential_reset.yml index 6087ad5635..b6e1d1b4c9 100644 --- a/detections/application/pingid_new_mfa_method_after_credential_reset.yml +++ b/detections/application/pingid_new_mfa_method_after_credential_reset.yml @@ -5,12 +5,34 @@ date: '2024-09-30' author: Steven Dick status: production type: TTP -description: The following analytic identifies the provisioning of a new MFA device shortly after a password reset. It detects this activity by correlating Windows Event Log events for password changes (EventID 4723, 4724) with PingID logs indicating device pairing. This behavior is significant as it may indicate a social engineering attack where a threat actor impersonates a valid user to reset credentials and add a new MFA device. If confirmed malicious, this activity could allow an attacker to gain persistent access to the compromised account, bypassing traditional security measures. +description: The following analytic identifies the provisioning of a new MFA device + shortly after a password reset. It detects this activity by correlating Windows + Event Log events for password changes (EventID 4723, 4724) with PingID logs indicating + device pairing. This behavior is significant as it may indicate a social engineering + attack where a threat actor impersonates a valid user to reset credentials and add + a new MFA device. If confirmed malicious, this activity could allow an attacker + to gain persistent access to the compromised account, bypassing traditional security + measures. data_source: - PingID -search: '`pingid` "result.message" = "*Device Paired*" | rex field=result.message "Device (Unp)?(P)?aired (?.+)" | eval src = coalesce(''resources{}.ipaddress'',''resources{}.devicemodel''), user = upper(''actors{}.name''), reason = ''result.message'' | eval object=CASE(ISNOTNULL(''resources{}.devicemodel''),''resources{}.devicemodel'',true(),device_extract) | eval action=CASE(match(''result.message'',"Device Paired*"),"created",match(''result.message'', "Device Unpaired*"),"deleted") | stats count min(_time) as firstTime, max(_time) as lastTime, values(reason) as reason by src,user,action,object | join type=outer user [| search `wineventlog_security` EventID IN(4723,4724) | eval PW_Change_Time = _time, user = upper(user) | fields user,src_user,EventID,PW_Change_Time] | eval timeDiffRaw = round(lastTime - PW_Change_Time) | eval timeDiff = replace(tostring(abs(timeDiffRaw) ,"duration"),"(\d*)\+*(\d+):(\d+):(\d+)","\2 hours \3 minutes") | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | `security_content_ctime(PW_Change_Time)` | where timeDiffRaw > 0 AND timeDiffRaw < 3600 | `pingid_new_mfa_method_after_credential_reset_filter`' -how_to_implement: Target environment must ingest Windows Event Log and PingID(PingOne) data sources. Specifically from logs from Active Directory Domain Controllers and JSON logging from a PingID(PingOne) enterprise environment, either via Webhook or Push Subscription. -known_false_positives: False positives may be generated by normal provisioning workflows that generate a password reset followed by a device registration. +search: "`pingid` \"result.message\" = \"*Device Paired*\" | rex field=result.message + \"Device (Unp)?(P)?aired (?.+)\" | eval src = coalesce('resources{}.ipaddress','resources{}.devicemodel'), + user = upper('actors{}.name'), reason = 'result.message' | eval object=CASE(ISNOTNULL('resources{}.devicemodel'),'resources{}.devicemodel',true(),device_extract) + | eval action=CASE(match('result.message',\"Device Paired*\"),\"created\",match('result.message', + \"Device Unpaired*\"),\"deleted\") | stats count min(_time) as firstTime, max(_time) + as lastTime, values(reason) as reason by src,user,action,object | join type=outer + user [| search `wineventlog_security` EventID IN(4723,4724) | eval PW_Change_Time + = _time, user = upper(user) | fields user,src_user,EventID,PW_Change_Time] | eval + timeDiffRaw = round(lastTime - PW_Change_Time) | eval timeDiff = replace(tostring(abs(timeDiffRaw) + ,\"duration\"),\"(\\d*)\\+*(\\d+):(\\d+):(\\d+)\",\"\\2 hours \\3 minutes\") | `security_content_ctime(firstTime)` + | `security_content_ctime(lastTime)` | `security_content_ctime(PW_Change_Time)` + | where timeDiffRaw > 0 AND timeDiffRaw < 3600 | `pingid_new_mfa_method_after_credential_reset_filter`" +how_to_implement: Target environment must ingest Windows Event Log and PingID(PingOne) + data sources. Specifically from logs from Active Directory Domain Controllers and + JSON logging from a PingID(PingOne) enterprise environment, either via Webhook or + Push Subscription. +known_false_positives: False positives may be generated by normal provisioning workflows + that generate a password reset followed by a device registration. references: - https://techcommunity.microsoft.com/t5/microsoft-entra-azure-ad-blog/defend-your-users-from-mfa-fatigue-attacks/ba-p/2365677 - https://www.bleepingcomputer.com/news/security/mfa-fatigue-hackers-new-favorite-tactic-in-high-profile-breaches/ @@ -23,7 +45,12 @@ drilldown_searches: earliest_offset: $info_min_time$ latest_offset: $info_max_time$ - name: View risk events for the last 7 days for - "$user$" - search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' + search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$") + starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime + values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) + as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) + as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` + | `security_content_ctime(lastTime)`' earliest_offset: $info_min_time$ latest_offset: $info_max_time$ rba: @@ -32,7 +59,7 @@ rba: risk_objects: - field: user type: User - risk_score: 50.0 + risk_score: 50 threat_objects: - field: object type: Other @@ -65,9 +92,11 @@ tags: tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1621/pingid/windows_pw_reset.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1621/pingid/windows_pw_reset.log source: XmlWinEventLog:Security sourcetype: XmlWinEventLog - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1621/pingid/pingid.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1621/pingid/pingid.log source: PINGID sourcetype: _json diff --git a/detections/application/pingid_new_mfa_method_registered_for_user.yml b/detections/application/pingid_new_mfa_method_registered_for_user.yml index 9e57ef3d6d..cd208c6b5f 100644 --- a/detections/application/pingid_new_mfa_method_registered_for_user.yml +++ b/detections/application/pingid_new_mfa_method_registered_for_user.yml @@ -5,12 +5,27 @@ date: '2024-09-30' author: Steven Dick status: production type: TTP -description: The following analytic detects the registration of a new Multi-Factor Authentication (MFA) method for a PingID (PingOne) account. It leverages JSON logs from PingID, specifically looking for successful device pairing events. This activity is significant as adversaries who gain unauthorized access to a user account may register a new MFA method to maintain persistence. If confirmed malicious, this could allow attackers to bypass existing security measures, maintain long-term access, and potentially escalate their privileges within the compromised environment. +description: The following analytic detects the registration of a new Multi-Factor + Authentication (MFA) method for a PingID (PingOne) account. It leverages JSON logs + from PingID, specifically looking for successful device pairing events. This activity + is significant as adversaries who gain unauthorized access to a user account may + register a new MFA method to maintain persistence. If confirmed malicious, this + could allow attackers to bypass existing security measures, maintain long-term access, + and potentially escalate their privileges within the compromised environment. data_source: - PingID -search: '`pingid` "result.message"="Device Paired*" result.status="SUCCESS" | rex field=result.message "Device (Unp)?(P)?aired (?.+)" | eval src = coalesce(''resources{}.ipaddress'',''resources{}.devicemodel''), user = upper(''actors{}.name''), reason = ''result.message'' | eval object=CASE(ISNOTNULL(''resources{}.devicemodel''),''resources{}.devicemodel'',true(),device_extract) | eval action=CASE(match(''result.message'',"Device Paired*"),"created",match(''result.message'', "Device Unpaired*"),"deleted") | stats count min(_time) as firstTime, max(_time) as lastTime by src,user,object,action,reason | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | `pingid_new_mfa_method_registered_for_user_filter`' -how_to_implement: Target environment must ingest JSON logging from a PingID(PingOne) enterprise environment, either via Webhook or Push Subscription. -known_false_positives: False positives may be generated by normal provisioning workflows for user device registration. +search: "`pingid` \"result.message\"=\"Device Paired*\" result.status=\"SUCCESS\"\ + \ | rex field=result.message \"Device (Unp)?(P)?aired (?.+)\" + | eval src = coalesce('resources{}.ipaddress','resources{}.devicemodel'), user = + upper('actors{}.name'), reason = 'result.message' | eval object=CASE(ISNOTNULL('resources{}.devicemodel'),'resources{}.devicemodel',true(),device_extract) + | eval action=CASE(match('result.message',\"Device Paired*\"),\"created\",match('result.message', + \"Device Unpaired*\"),\"deleted\") | stats count min(_time) as firstTime, max(_time) + as lastTime by src,user,object,action,reason | `security_content_ctime(firstTime)` + | `security_content_ctime(lastTime)` | `pingid_new_mfa_method_registered_for_user_filter`" +how_to_implement: Target environment must ingest JSON logging from a PingID(PingOne) + enterprise environment, either via Webhook or Push Subscription. +known_false_positives: False positives may be generated by normal provisioning workflows + for user device registration. references: - https://twitter.com/jhencinski/status/1618660062352007174 - https://attack.mitre.org/techniques/T1098/005/ @@ -22,7 +37,12 @@ drilldown_searches: earliest_offset: $info_min_time$ latest_offset: $info_max_time$ - name: View risk events for the last 7 days for - "$user$" and "$src$" - search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$", "$src$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' + search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$", + "$src$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as + lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" + values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" + values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object + | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' earliest_offset: $info_min_time$ latest_offset: $info_max_time$ rba: @@ -31,10 +51,10 @@ rba: risk_objects: - field: user type: User - risk_score: 10.0 + risk_score: 10 - field: src type: IP Address - risk_score: 10.0 + risk_score: 10 threat_objects: - field: object type: Other @@ -64,7 +84,8 @@ tags: tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1621/pingid/pingid.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1621/pingid/pingid.log source: PINGID sourcetype: _json update_timestamp: true diff --git a/detections/application/suspicious_email_attachment_extensions.yml b/detections/application/suspicious_email_attachment_extensions.yml index b0515740d0..1eea97df43 100644 --- a/detections/application/suspicious_email_attachment_extensions.yml +++ b/detections/application/suspicious_email_attachment_extensions.yml @@ -5,14 +5,31 @@ date: '2024-10-17' author: David Dorsey, Splunk status: experimental type: Anomaly -description: The following analytic detects emails containing attachments with suspicious file extensions. It leverages the Email data model in Splunk, using the tstats command to identify emails where the attachment filename is not empty. This detection is significant for SOC analysts as it highlights potential phishing or malware delivery attempts, which are common vectors for data breaches and malware infections. If confirmed malicious, this activity could lead to unauthorized access to sensitive information, system compromise, or data exfiltration. Immediate review and analysis of the identified emails and attachments are crucial to mitigate these risks. +description: The following analytic detects emails containing attachments with suspicious + file extensions. It leverages the Email data model in Splunk, using the tstats command + to identify emails where the attachment filename is not empty. This detection is + significant for SOC analysts as it highlights potential phishing or malware delivery + attempts, which are common vectors for data breaches and malware infections. If + confirmed malicious, this activity could lead to unauthorized access to sensitive + information, system compromise, or data exfiltration. Immediate review and analysis + of the identified emails and attachments are crucial to mitigate these risks. data_source: [] -search: '| tstats `security_content_summariesonly` count min(_time) as firstTime max(_time) as lastTime from datamodel=Email where All_Email.file_name="*" by All_Email.src_user, All_Email.file_name All_Email.message_id | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | `drop_dm_object_name("All_Email")` | `suspicious_email_attachments` | `suspicious_email_attachment_extensions_filter`' -how_to_implement: 'You need to ingest data from emails. Specifically, the sender''s address and the file names of any attachments must be mapped to the Email data model. - - **Splunk Phantom Playbook Integration** - - If Splunk Phantom is also configured in your environment, a Playbook called "Suspicious Email Attachment Investigate and Delete" can be configured to run when any results are found by this detection search. To use this integration, install the Phantom App for Splunk `https://splunkbase.splunk.com/app/3411/`, and add the correct hostname to the "Phantom Instance" field in the Adaptive Response Actions when configuring this detection search. The notable event will be sent to Phantom and the playbook will gather further information about the file attachment and its network behaviors. If Phantom finds malicious behavior and an analyst approves of the results, the email will be deleted from the user''s inbox.''' +search: '| tstats `security_content_summariesonly` count min(_time) as firstTime max(_time) + as lastTime from datamodel=Email where All_Email.file_name="*" by All_Email.src_user, + All_Email.file_name All_Email.message_id | `security_content_ctime(firstTime)` | + `security_content_ctime(lastTime)` | `drop_dm_object_name("All_Email")` | `suspicious_email_attachments` + | `suspicious_email_attachment_extensions_filter`' +how_to_implement: "You need to ingest data from emails. Specifically, the sender's + address and the file names of any attachments must be mapped to the Email data model.\n + **Splunk Phantom Playbook Integration**\nIf Splunk Phantom is also configured in + your environment, a Playbook called \"Suspicious Email Attachment Investigate and + Delete\" can be configured to run when any results are found by this detection search. + To use this integration, install the Phantom App for Splunk `https://splunkbase.splunk.com/app/3411/`, + and add the correct hostname to the \"Phantom Instance\" field in the Adaptive Response + Actions when configuring this detection search. The notable event will be sent to + Phantom and the playbook will gather further information about the file attachment + and its network behaviors. If Phantom finds malicious behavior and an analyst approves + of the results, the email will be deleted from the user's inbox.'" known_false_positives: None identified references: [] rba: @@ -20,7 +37,7 @@ rba: risk_objects: - field: user type: User - risk_score: 25.0 + risk_score: 25 threat_objects: [] tags: analytic_story: diff --git a/detections/application/suspicious_java_classes.yml b/detections/application/suspicious_java_classes.yml index 56feb02dfe..d1ec90e4bf 100644 --- a/detections/application/suspicious_java_classes.yml +++ b/detections/application/suspicious_java_classes.yml @@ -5,10 +5,24 @@ date: '2024-10-17' author: Jose Hernandez, Splunk status: experimental type: Anomaly -description: The following analytic identifies suspicious Java classes often used for remote command execution exploits in Java frameworks like Apache Struts. It detects this activity by analyzing HTTP POST requests with specific content patterns using Splunk's `stream_http` data source. This behavior is significant because it may indicate an attempt to exploit vulnerabilities in web applications, potentially leading to unauthorized remote code execution. If confirmed malicious, this activity could allow attackers to execute arbitrary commands on the server, leading to data breaches, system compromise, and further network infiltration. +description: The following analytic identifies suspicious Java classes often used + for remote command execution exploits in Java frameworks like Apache Struts. It + detects this activity by analyzing HTTP POST requests with specific content patterns + using Splunk's `stream_http` data source. This behavior is significant because it + may indicate an attempt to exploit vulnerabilities in web applications, potentially + leading to unauthorized remote code execution. If confirmed malicious, this activity + could allow attackers to execute arbitrary commands on the server, leading to data + breaches, system compromise, and further network infiltration. data_source: [] -search: '`stream_http` http_method=POST http_content_length>1 | regex form_data="(?i)java\.lang\.(?:runtime|processbuilder)" | rename src_ip as src | stats count earliest(_time) as firstTime, latest(_time) as lastTime, values(url) as uri, values(status) as status, values(http_user_agent) as http_user_agent by src, dest | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | `suspicious_java_classes_filter`' -how_to_implement: In order to properly run this search, Splunk needs to ingest data from your web-traffic appliances that serve or sit in the path of your Struts application servers. This can be accomplished by indexing data from a web proxy, or by using network traffic-analysis tools, such as Splunk Stream or Bro. +search: '`stream_http` http_method=POST http_content_length>1 | regex form_data="(?i)java\.lang\.(?:runtime|processbuilder)" + | rename src_ip as src | stats count earliest(_time) as firstTime, latest(_time) + as lastTime, values(url) as uri, values(status) as status, values(http_user_agent) + as http_user_agent by src, dest | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` + | `suspicious_java_classes_filter`' +how_to_implement: In order to properly run this search, Splunk needs to ingest data + from your web-traffic appliances that serve or sit in the path of your Struts application + servers. This can be accomplished by indexing data from a web proxy, or by using + network traffic-analysis tools, such as Splunk Stream or Bro. known_false_positives: There are no known false positives. references: [] rba: @@ -16,10 +30,10 @@ rba: risk_objects: - field: user type: User - risk_score: 25.0 + risk_score: 25 - field: dest type: Hostname - risk_score: 25.0 + risk_score: 25 threat_objects: [] tags: analytic_story: diff --git a/detections/application/web_servers_executing_suspicious_processes.yml b/detections/application/web_servers_executing_suspicious_processes.yml index ef854d71f4..5c837bdb49 100644 --- a/detections/application/web_servers_executing_suspicious_processes.yml +++ b/detections/application/web_servers_executing_suspicious_processes.yml @@ -5,22 +5,43 @@ date: '2024-10-17' author: David Dorsey, Splunk status: experimental type: TTP -description: The following analytic detects the execution of suspicious processes on systems identified as web servers. It leverages the Splunk data model "Endpoint.Processes" to search for specific process names such as "whoami", "ping", "iptables", "wget", "service", and "curl". This activity is significant because these processes are often used by attackers for reconnaissance, persistence, or data exfiltration. If confirmed malicious, this could lead to data theft, deployment of additional malware, or even ransomware attacks. Immediate investigation is required to determine the legitimacy of the activity and mitigate potential threats. +description: The following analytic detects the execution of suspicious processes + on systems identified as web servers. It leverages the Splunk data model "Endpoint.Processes" + to search for specific process names such as "whoami", "ping", "iptables", "wget", + "service", and "curl". This activity is significant because these processes are + often used by attackers for reconnaissance, persistence, or data exfiltration. If + confirmed malicious, this could lead to data theft, deployment of additional malware, + or even ransomware attacks. Immediate investigation is required to determine the + legitimacy of the activity and mitigate potential threats. data_source: - Sysmon EventID 1 -search: '| tstats `security_content_summariesonly` count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where Processes.dest_category="web_server" AND (Processes.process="*whoami*" OR Processes.process="*ping*" OR Processes.process="*iptables*" OR Processes.process="*wget*" OR Processes.process="*service*" OR Processes.process="*curl*") by Processes.process Processes.process_name, Processes.dest Processes.user| `drop_dm_object_name(Processes)` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | `web_servers_executing_suspicious_processes_filter`' -how_to_implement: The detection is based on data that originates from Endpoint Detection and Response (EDR) agents. These agents are designed to provide security-related telemetry from the endpoints where the agent is installed. To implement this search, you must ingest logs that contain the process GUID, process name, and parent process. Additionally, you must ingest complete command-line executions. These logs must be processed using the appropriate Splunk Technology Add-ons that are specific to the EDR product. The logs must also be mapped to the `Processes` node of the `Endpoint` data model. Use the Splunk Common Information Model (CIM) to normalize the field names and speed up the data modeling process. -known_false_positives: Some of these processes may be used legitimately on web servers during maintenance or other administrative tasks. +search: '| tstats `security_content_summariesonly` count min(_time) as firstTime max(_time) + as lastTime from datamodel=Endpoint.Processes where Processes.dest_category="web_server" + AND (Processes.process="*whoami*" OR Processes.process="*ping*" OR Processes.process="*iptables*" + OR Processes.process="*wget*" OR Processes.process="*service*" OR Processes.process="*curl*") + by Processes.process Processes.process_name, Processes.dest Processes.user| `drop_dm_object_name(Processes)` + | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | `web_servers_executing_suspicious_processes_filter`' +how_to_implement: The detection is based on data that originates from Endpoint Detection + and Response (EDR) agents. These agents are designed to provide security-related + telemetry from the endpoints where the agent is installed. To implement this search, + you must ingest logs that contain the process GUID, process name, and parent process. + Additionally, you must ingest complete command-line executions. These logs must + be processed using the appropriate Splunk Technology Add-ons that are specific to + the EDR product. The logs must also be mapped to the `Processes` node of the `Endpoint` + data model. Use the Splunk Common Information Model (CIM) to normalize the field + names and speed up the data modeling process. +known_false_positives: Some of these processes may be used legitimately on web servers + during maintenance or other administrative tasks. references: [] rba: message: tbd risk_objects: - field: user type: User - risk_score: 25.0 + risk_score: 25 - field: dest type: Hostname - risk_score: 25.0 + risk_score: 25 threat_objects: [] tags: analytic_story: diff --git a/detections/application/windows_ad_add_self_to_group.yml b/detections/application/windows_ad_add_self_to_group.yml index 066354ed66..c784172651 100644 --- a/detections/application/windows_ad_add_self_to_group.yml +++ b/detections/application/windows_ad_add_self_to_group.yml @@ -7,8 +7,16 @@ status: production type: TTP data_source: - Windows Event Log Security 4728 -description: This analytic detects instances where a user adds themselves to an Active Directory (AD) group. This activity is a common indicator of privilege escalation, where a user attempts to gain unauthorized access to higher privileges or sensitive resources. By monitoring AD logs, this detection identifies such suspicious behavior, which could be part of a larger attack strategy aimed at compromising critical systems and data. -search: '`wineventlog_security` EventCode IN (4728) | where user=src_user | stats min(_time) as _time dc(user) as usercount, values(user) as user values(user_category) as user_category values(src_user_category) as src_user_category values(dvc) as dvc by signature, Group_Name, src_user | `windows_ad_add_self_to_group_filter`' +description: This analytic detects instances where a user adds themselves to an Active + Directory (AD) group. This activity is a common indicator of privilege escalation, + where a user attempts to gain unauthorized access to higher privileges or sensitive + resources. By monitoring AD logs, this detection identifies such suspicious behavior, + which could be part of a larger attack strategy aimed at compromising critical systems + and data. +search: '`wineventlog_security` EventCode IN (4728) | where user=src_user | stats + min(_time) as _time dc(user) as usercount, values(user) as user values(user_category) + as user_category values(src_user_category) as src_user_category values(dvc) as dvc + by signature, Group_Name, src_user | `windows_ad_add_self_to_group_filter`' how_to_implement: This analytic requires eventCode 4728 to be ingested. known_false_positives: Unknown references: [] @@ -18,7 +26,12 @@ drilldown_searches: earliest_offset: $info_min_time$ latest_offset: $info_max_time$ - name: View risk events for the last 7 days for - "$user$" - search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' + search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$") + starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime + values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) + as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) + as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` + | `security_content_ctime(lastTime)`' earliest_offset: $info_min_time$ latest_offset: $info_max_time$ rba: @@ -26,7 +39,7 @@ rba: risk_objects: - field: user type: User - risk_score: 50.0 + risk_score: 50 threat_objects: [] tags: analytic_story: @@ -52,7 +65,8 @@ tags: tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1098/account_manipulation/xml-windows-security.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1098/account_manipulation/xml-windows-security.log source: XmlWinEventLog:Security sourcetype: XmlWinEventLog update_timestamp: true diff --git a/detections/application/windows_ad_dangerous_deny_acl_modification.yml b/detections/application/windows_ad_dangerous_deny_acl_modification.yml index fdef1326e9..a21c0b2418 100644 --- a/detections/application/windows_ad_dangerous_deny_acl_modification.yml +++ b/detections/application/windows_ad_dangerous_deny_acl_modification.yml @@ -7,9 +7,40 @@ status: production type: TTP data_source: - Windows Security 5136 -description: This detection identifies an Active Directory access-control list (ACL) modification event, which applies permissions that deny the ability to enumerate permissions of the object. -search: '`wineventlog_security` EventCode=5136 | stats min(_time) as _time values(eval(if(OperationType=="%%14675",AttributeValue,null))) as old_value values(eval(if(OperationType=="%%14674",AttributeValue,null))) as new_value values(OperationType) as OperationType by ObjectClass ObjectDN OpCorrelationID src_user SubjectLogonId | rex field=old_value max_match=10000 "\((?P.*?)\)" | rex field=new_value max_match=10000 "\((?P.*?)\)" | mvexpand new_ace | where NOT new_ace IN (old_values) | rex field=new_ace "(?P.*?);(?P.*?);(?P.*?);(?P.*?);(?P.*?);(?P.*?)$" | rex max_match=100 field=aceAccessRights "(?P[A-Z]{2})" | rex max_match=100 field=aceFlags "(?P[A-Z]{2})" | lookup msad_guid_lookup guid as aceObjectGuid OUTPUT displayName as ControlAccessRights | lookup ace_access_rights_lookup access_rights_string as AccessRights OUTPUT access_rights_value | lookup ace_type_lookup ace_type_string as aceType OUTPUT ace_type_value as aceType | lookup ace_flag_lookup flag_string as aceFlags OUTPUT flag_value as ace_flag_value ``` Optional SID resolution lookups | lookup identity_lookup_expanded objectSid as aceSid OUTPUT downLevelDomainName as user | lookup admon_groups_def objectSid as aceSid OUTPUT cn as group ``` | lookup builtin_groups_lookup builtin_group_string as aceSid OUTPUT builtin_group_name as builtin_group | eval aceType=coalesce(ace_type_value,aceType), aceFlags=coalesce(ace_flag_value,"This object only"), aceAccessRights=if(aceAccessRights="CCDCLCSWRPWPDTLOCRSDRCWDWO","Full control",coalesce(access_rights_value,AccessRights)), aceControlAccessRights=coalesce(ControlAccessRights,aceObjectGuid), user=coalesce(user, group, builtin_group, aceSid) | stats values(aceType) as aceType values(aceFlags) as aceFlags values(aceControlAccessRights) as aceControlAccessRights values(aceAccessRights) as aceAccessRights values(new_ace) as new_ace values(aceInheritedTypeGuid) as aceInheritedTypeGuid by _time ObjectClass ObjectDN src_user SubjectLogonId user OpCorrelationID | eval aceControlAccessRights=if(mvcount(aceControlAccessRights)=1 AND aceControlAccessRights="","All rights",''aceControlAccessRights'') | search aceType IN ("Access denied",D) AND aceAccessRights IN ("Full control","Read permissions",RC) | `windows_ad_dangerous_deny_acl_modification_filter`' -how_to_implement: Ensure you are ingesting Active Directory audit logs - specifically event 5136. See lantern article in references for further on how to onboard AD audit data. Ensure the wineventlog_security macro is configured with the correct indexes and include lookups for SID resolution if evt_resolve_ad_obj is set to 0. +description: This detection identifies an Active Directory access-control list (ACL) + modification event, which applies permissions that deny the ability to enumerate + permissions of the object. +search: "`wineventlog_security` EventCode=5136 | stats min(_time) as _time values(eval(if(OperationType==\"\ + %%14675\",AttributeValue,null))) as old_value values(eval(if(OperationType==\"%%14674\"\ + ,AttributeValue,null))) as new_value values(OperationType) as OperationType by ObjectClass + ObjectDN OpCorrelationID src_user SubjectLogonId | rex field=old_value max_match=10000 + \"\\((?P.*?)\\)\" | rex field=new_value max_match=10000 \"\\((?P.*?)\\\ + )\" | mvexpand new_ace | where NOT new_ace IN (old_values) | rex field=new_ace + \"(?P.*?);(?P.*?);(?P.*?);(?P.*?);(?P.*?);(?P.*?)$\"\ + \ | rex max_match=100 field=aceAccessRights \"(?P[A-Z]{2})\" | rex + max_match=100 field=aceFlags \"(?P[A-Z]{2})\" | lookup msad_guid_lookup + guid as aceObjectGuid OUTPUT displayName as ControlAccessRights | lookup ace_access_rights_lookup + access_rights_string as AccessRights OUTPUT access_rights_value | lookup ace_type_lookup + ace_type_string as aceType OUTPUT ace_type_value as aceType | lookup ace_flag_lookup + flag_string as aceFlags OUTPUT flag_value as ace_flag_value ``` Optional SID resolution + lookups | lookup identity_lookup_expanded objectSid as aceSid OUTPUT downLevelDomainName + as user | lookup admon_groups_def objectSid as aceSid OUTPUT cn as group ``` | + lookup builtin_groups_lookup builtin_group_string as aceSid OUTPUT builtin_group_name + as builtin_group | eval aceType=coalesce(ace_type_value,aceType), aceFlags=coalesce(ace_flag_value,\"\ + This object only\"), aceAccessRights=if(aceAccessRights=\"CCDCLCSWRPWPDTLOCRSDRCWDWO\"\ + ,\"Full control\",coalesce(access_rights_value,AccessRights)), aceControlAccessRights=coalesce(ControlAccessRights,aceObjectGuid), + user=coalesce(user, group, builtin_group, aceSid) | stats values(aceType) as aceType + values(aceFlags) as aceFlags values(aceControlAccessRights) as aceControlAccessRights + values(aceAccessRights) as aceAccessRights values(new_ace) as new_ace values(aceInheritedTypeGuid) + as aceInheritedTypeGuid by _time ObjectClass ObjectDN src_user SubjectLogonId user + OpCorrelationID | eval aceControlAccessRights=if(mvcount(aceControlAccessRights)=1 + AND aceControlAccessRights=\"\",\"All rights\",'aceControlAccessRights') | search + aceType IN (\"Access denied\",D) AND aceAccessRights IN (\"Full control\",\"Read + permissions\",RC) | `windows_ad_dangerous_deny_acl_modification_filter`" +how_to_implement: Ensure you are ingesting Active Directory audit logs - specifically + event 5136. See lantern article in references for further on how to onboard AD audit + data. Ensure the wineventlog_security macro is configured with the correct indexes + and include lookups for SID resolution if evt_resolve_ad_obj is set to 0. known_false_positives: None. references: - https://happycamper84.medium.com/sneaky-persistence-via-hidden-objects-in-ad-1c91fc37bf54 @@ -21,7 +52,12 @@ drilldown_searches: earliest_offset: $info_min_time$ latest_offset: $info_max_time$ - name: View risk events for the last 7 days for - "$user$" and "$src_user$" - search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$", "$src_user$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' + search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$", + "$src_user$") starthoursago=168 | stats count min(_time) as firstTime max(_time) + as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk + Message" values(analyticstories) as "Analytic Stories" values(annotations._all) + as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" + by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' earliest_offset: $info_min_time$ latest_offset: $info_max_time$ rba: @@ -30,10 +66,10 @@ rba: risk_objects: - field: user type: User - risk_score: 100.0 + risk_score: 100 - field: src_user type: User - risk_score: 100.0 + risk_score: 100 threat_objects: [] tags: analytic_story: @@ -65,6 +101,7 @@ tags: tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1222.001/dacl_abuse/hidden_object_windows-security-xml.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1222.001/dacl_abuse/hidden_object_windows-security-xml.log source: XmlWinEventLog:Security sourcetype: XmlWinEventLog diff --git a/detections/application/windows_ad_dangerous_group_acl_modification.yml b/detections/application/windows_ad_dangerous_group_acl_modification.yml index da25be9dc8..aeb723317f 100644 --- a/detections/application/windows_ad_dangerous_group_acl_modification.yml +++ b/detections/application/windows_ad_dangerous_group_acl_modification.yml @@ -7,9 +7,48 @@ status: production type: TTP data_source: - Windows Security 5136 -description: 'This detection monitors the addition of the following ACLs to an Active Directory group object: "Full control", "All extended rights", "All validated writes", "Create all child objects", "Delete all child objects", "Delete subtree", "Delete", "Modify permissions", "Modify owner", and "Write all properties". Such modifications can indicate potential privilege escalation or malicious activity. Immediate investigation is recommended upon alert.' -search: '`wineventlog_security` EventCode=5136 ObjectClass=group | stats min(_time) as _time values(eval(if(OperationType=="%%14675",AttributeValue,null))) as old_value values(eval(if(OperationType=="%%14674",AttributeValue,null))) as new_value values(OperationType) as OperationType by ObjectClass ObjectDN OpCorrelationID src_user SubjectLogonId | rex field=old_value max_match=10000 "\((?P.*?)\)" | rex field=new_value max_match=10000 "\((?P.*?)\)" | mvexpand new_ace | where NOT new_ace IN (old_values) | rex field=new_ace "(?P.*?);(?P.*?);(?P.*?);(?P.*?);(?P.*?);(?P.*?)$" | rex max_match=100 field=aceAccessRights "(?P[A-Z]{2})" | rex max_match=100 field=aceFlags "(?P[A-Z]{2})" | lookup ace_type_lookup ace_type_string as aceType OUTPUT ace_type_value as aceType | lookup ace_flag_lookup flag_string as aceFlags OUTPUT flag_value as ace_flag_value | lookup ace_access_rights_lookup access_rights_string as AccessRights OUTPUT access_rights_value | lookup msad_guid_lookup guid as aceObjectGuid OUTPUT displayName as ControlAccessRights ``` Optional SID resolution lookups | lookup identity_lookup_expanded objectSid as aceSid OUTPUT downLevelDomainName as user | lookup admon_groups_def objectSid as aceSid OUTPUT cn as group ``` | lookup builtin_groups_lookup builtin_group_string as aceSid OUTPUT builtin_group_name as builtin_group | eval aceType=coalesce(ace_type_value,aceType), aceInheritance=coalesce(ace_flag_value,"This object only"), aceAccessRights=if(aceAccessRights="CCDCLCSWRPWPDTLOCRSDRCWDWO","Full control",coalesce(access_rights_value,AccessRights)), aceControlAccessRights=if((ControlAccessRights="Write member" OR aceObjectGuid="bf9679c0-0de6-11d0-a285-00aa003049e2") AND (aceAccessRights="All validated writes" OR AccessRights="SW"),"Add/remove self as member",coalesce(ControlAccessRights,aceObjectGuid)), user=coalesce(user, group, builtin_group, aceSid) | stats values(aceType) as aceType values(aceInheritance) as aceInheritance values(aceControlAccessRights) as aceControlAccessRights values(aceAccessRights) as aceAccessRights values(new_ace) as new_ace values(aceInheritedTypeGuid) as aceInheritedTypeGuid by _time ObjectClass ObjectDN src_user SubjectLogonId user OpCorrelationID | eval aceControlAccessRights=if(mvcount(aceControlAccessRights)=1 AND aceControlAccessRights="","All rights",''aceControlAccessRights'') | search NOT aceType IN ("*denied*","D","OD","XD") AND aceAccessRights IN ("Full control","All extended rights","All validated writes","Create all child objects","Delete all child objects","Delete subtree","Delete","Modify permissions","Modify owner","Write all properties",CC,CR,DC,DT,SD,SW,WD,WO,WP) | `windows_ad_dangerous_group_acl_modification_filter`' -how_to_implement: Ensure you are ingesting Active Directory audit logs - specifically event 5136. See lantern article in references for further on how to onboard AD audit data. Ensure the wineventlog_security macro is configured with the correct indexes and include lookups for SID resolution if evt_resolve_ad_obj is set to 0. +description: 'This detection monitors the addition of the following ACLs to an Active + Directory group object: "Full control", "All extended rights", "All validated writes", "Create + all child objects", "Delete all child objects", "Delete subtree", "Delete", "Modify + permissions", "Modify owner", and "Write all properties". Such modifications can + indicate potential privilege escalation or malicious activity. Immediate investigation + is recommended upon alert.' +search: "`wineventlog_security` EventCode=5136 ObjectClass=group | stats min(_time) + as _time values(eval(if(OperationType==\"%%14675\",AttributeValue,null))) as old_value + values(eval(if(OperationType==\"%%14674\",AttributeValue,null))) as new_value values(OperationType) + as OperationType by ObjectClass ObjectDN OpCorrelationID src_user SubjectLogonId\ + \ | rex field=old_value max_match=10000 \"\\((?P.*?)\\)\" | rex field=new_value + max_match=10000 \"\\((?P.*?)\\)\" | mvexpand new_ace | where NOT new_ace + IN (old_values) | rex field=new_ace \"(?P.*?);(?P.*?);(?P.*?);(?P.*?);(?P.*?);(?P.*?)$\"\ + \ | rex max_match=100 field=aceAccessRights \"(?P[A-Z]{2})\" | rex + max_match=100 field=aceFlags \"(?P[A-Z]{2})\" | lookup ace_type_lookup + ace_type_string as aceType OUTPUT ace_type_value as aceType | lookup ace_flag_lookup + flag_string as aceFlags OUTPUT flag_value as ace_flag_value | lookup ace_access_rights_lookup + access_rights_string as AccessRights OUTPUT access_rights_value | lookup msad_guid_lookup + guid as aceObjectGuid OUTPUT displayName as ControlAccessRights ``` Optional SID + resolution lookups | lookup identity_lookup_expanded objectSid as aceSid OUTPUT + downLevelDomainName as user | lookup admon_groups_def objectSid as aceSid OUTPUT + cn as group ``` | lookup builtin_groups_lookup builtin_group_string as aceSid OUTPUT + builtin_group_name as builtin_group | eval aceType=coalesce(ace_type_value,aceType), + aceInheritance=coalesce(ace_flag_value,\"This object only\"), aceAccessRights=if(aceAccessRights=\"\ + CCDCLCSWRPWPDTLOCRSDRCWDWO\",\"Full control\",coalesce(access_rights_value,AccessRights)), + aceControlAccessRights=if((ControlAccessRights=\"Write member\" OR aceObjectGuid=\"\ + bf9679c0-0de6-11d0-a285-00aa003049e2\") AND (aceAccessRights=\"All validated writes\"\ + \ OR AccessRights=\"SW\"),\"Add/remove self as member\",coalesce(ControlAccessRights,aceObjectGuid)), + user=coalesce(user, group, builtin_group, aceSid) | stats values(aceType) as aceType + values(aceInheritance) as aceInheritance values(aceControlAccessRights) as aceControlAccessRights + values(aceAccessRights) as aceAccessRights values(new_ace) as new_ace values(aceInheritedTypeGuid) + as aceInheritedTypeGuid by _time ObjectClass ObjectDN src_user SubjectLogonId user + OpCorrelationID | eval aceControlAccessRights=if(mvcount(aceControlAccessRights)=1 + AND aceControlAccessRights=\"\",\"All rights\",'aceControlAccessRights') | search + NOT aceType IN (\"*denied*\",\"D\",\"OD\",\"XD\") AND aceAccessRights IN (\"Full + control\",\"All extended rights\",\"All validated writes\",\"Create all child objects\"\ + ,\"Delete all child objects\",\"Delete subtree\",\"Delete\",\"Modify permissions\"\ + ,\"Modify owner\",\"Write all properties\",CC,CR,DC,DT,SD,SW,WD,WO,WP) | `windows_ad_dangerous_group_acl_modification_filter`" +how_to_implement: Ensure you are ingesting Active Directory audit logs - specifically + event 5136. See lantern article in references for further on how to onboard AD audit + data. Ensure the wineventlog_security macro is configured with the correct indexes + and include lookups for SID resolution if evt_resolve_ad_obj is set to 0. known_false_positives: Unknown references: - https://learn.microsoft.com/en-us/windows/win32/secauthz/ace-strings @@ -22,7 +61,12 @@ drilldown_searches: earliest_offset: $info_min_time$ latest_offset: $info_max_time$ - name: View risk events for the last 7 days for - "$user$" and "$src_user$" - search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$", "$src_user$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' + search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$", + "$src_user$") starthoursago=168 | stats count min(_time) as firstTime max(_time) + as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk + Message" values(analyticstories) as "Analytic Stories" values(annotations._all) + as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" + by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' earliest_offset: $info_min_time$ latest_offset: $info_max_time$ rba: @@ -31,10 +75,10 @@ rba: risk_objects: - field: user type: User - risk_score: 100.0 + risk_score: 100 - field: src_user type: User - risk_score: 100.0 + risk_score: 100 threat_objects: [] tags: analytic_story: @@ -66,6 +110,7 @@ tags: tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1222.001/dacl_abuse/group_dacl_mod_windows-security-xml.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1222.001/dacl_abuse/group_dacl_mod_windows-security-xml.log source: XmlWinEventLog:Security sourcetype: XmlWinEventLog diff --git a/detections/application/windows_ad_dangerous_user_acl_modification.yml b/detections/application/windows_ad_dangerous_user_acl_modification.yml index b5d7556e53..1adb07c243 100644 --- a/detections/application/windows_ad_dangerous_user_acl_modification.yml +++ b/detections/application/windows_ad_dangerous_user_acl_modification.yml @@ -7,9 +7,45 @@ status: production type: TTP data_source: - Windows Security 5136 -description: 'This detection monitors the addition of the following ACLs to an Active Directory user object: "Full control","All extended rights","All validated writes", "Create all child objects","Delete all child objects","Delete subtree","Delete","Modify permissions","Modify owner","Write all properties". Such modifications can indicate potential privilege escalation or malicious activity. Immediate investigation is recommended upon alert.' -search: '`wineventlog_security` EventCode=5136 ObjectClass=user | stats min(_time) as _time values(eval(if(OperationType=="%%14675",AttributeValue,null))) as old_value values(eval(if(OperationType=="%%14674",AttributeValue,null))) as new_value values(OperationType) as OperationType by ObjectClass ObjectDN OpCorrelationID src_user SubjectLogonId | rex field=old_value max_match=10000 "\((?P.*?)\)" | rex field=new_value max_match=10000 "\((?P.*?)\)" | mvexpand new_ace | where NOT new_ace IN (old_values) | rex field=new_ace "(?P.*?);(?P.*?);(?P.*?);(?P.*?);(?P.*?);(?P.*?)$" | rex max_match=100 field=aceAccessRights "(?P[A-Z]{2})" | rex max_match=100 field=aceFlags "(?P[A-Z]{2})" | lookup msad_guid_lookup guid as aceObjectGuid OUTPUT displayName as ControlAccessRights | lookup ace_access_rights_lookup access_rights_string as AccessRights OUTPUT access_rights_value | lookup ace_type_lookup ace_type_string as aceType OUTPUT ace_type_value as aceType | lookup ace_flag_lookup flag_string as aceFlags OUTPUT flag_value as ace_flag_value ``` Optional SID resolution lookups | lookup identity_lookup_expanded objectSid as aceSid OUTPUT downLevelDomainName as user | lookup admon_groups_def objectSid as aceSid OUTPUT cn as group ``` | lookup builtin_groups_lookup builtin_group_string as aceSid OUTPUT builtin_group_name as builtin_group | eval aceType=coalesce(ace_type_value,aceType), aceFlags=coalesce(ace_flag_value,"This object only"), aceAccessRights=if(aceAccessRights="CCDCLCSWRPWPDTLOCRSDRCWDWO","Full control",coalesce(access_rights_value,AccessRights)), aceControlAccessRights=coalesce(ControlAccessRights,aceObjectGuid), user=coalesce(user, group, builtin_group, aceSid) | stats values(aceType) as aceType values(aceFlags) as aceFlags values(aceControlAccessRights) as aceControlAccessRights values(aceAccessRights) as aceAccessRights values(new_ace) as new_ace values(aceInheritedTypeGuid) as aceInheritedTypeGuid by _time ObjectClass ObjectDN src_user SubjectLogonId user OpCorrelationID | eval aceControlAccessRights=if(mvcount(aceControlAccessRights)=1 AND aceControlAccessRights="","All rights",''aceControlAccessRights'') | search NOT aceType IN (*denied*,D,OD,XD) AND aceAccessRights IN ("Full control","All extended rights","All validated writes","Create all child objects","Delete all child objects","Delete subtree","Delete","Modify permissions","Modify owner","Write all properties",CC,CR,DC,DT,SD,SW,WD,WO,WP) | `windows_ad_dangerous_user_acl_modification_filter`' -how_to_implement: Ensure you are ingesting Active Directory audit logs - specifically event 5136. See lantern article in references for further on how to onboard AD audit data. Ensure the wineventlog_security macro is configured with the correct indexes and include lookups for SID resolution if evt_resolve_ad_obj is set to 0. +description: 'This detection monitors the addition of the following ACLs to an Active + Directory user object: "Full control","All extended rights","All validated writes", + "Create all child objects","Delete all child objects","Delete subtree","Delete","Modify + permissions","Modify owner","Write all properties". Such modifications can indicate + potential privilege escalation or malicious activity. Immediate investigation is + recommended upon alert.' +search: "`wineventlog_security` EventCode=5136 ObjectClass=user | stats min(_time) + as _time values(eval(if(OperationType==\"%%14675\",AttributeValue,null))) as old_value + values(eval(if(OperationType==\"%%14674\",AttributeValue,null))) as new_value values(OperationType) + as OperationType by ObjectClass ObjectDN OpCorrelationID src_user SubjectLogonId\ + \ | rex field=old_value max_match=10000 \"\\((?P.*?)\\)\" | rex field=new_value + max_match=10000 \"\\((?P.*?)\\)\" | mvexpand new_ace | where NOT new_ace + IN (old_values) | rex field=new_ace \"(?P.*?);(?P.*?);(?P.*?);(?P.*?);(?P.*?);(?P.*?)$\"\ + \ | rex max_match=100 field=aceAccessRights \"(?P[A-Z]{2})\" | rex + max_match=100 field=aceFlags \"(?P[A-Z]{2})\" | lookup msad_guid_lookup + guid as aceObjectGuid OUTPUT displayName as ControlAccessRights | lookup ace_access_rights_lookup + access_rights_string as AccessRights OUTPUT access_rights_value | lookup ace_type_lookup + ace_type_string as aceType OUTPUT ace_type_value as aceType | lookup ace_flag_lookup + flag_string as aceFlags OUTPUT flag_value as ace_flag_value ``` Optional SID resolution + lookups | lookup identity_lookup_expanded objectSid as aceSid OUTPUT downLevelDomainName + as user | lookup admon_groups_def objectSid as aceSid OUTPUT cn as group ``` | + lookup builtin_groups_lookup builtin_group_string as aceSid OUTPUT builtin_group_name + as builtin_group | eval aceType=coalesce(ace_type_value,aceType), aceFlags=coalesce(ace_flag_value,\"\ + This object only\"), aceAccessRights=if(aceAccessRights=\"CCDCLCSWRPWPDTLOCRSDRCWDWO\"\ + ,\"Full control\",coalesce(access_rights_value,AccessRights)), aceControlAccessRights=coalesce(ControlAccessRights,aceObjectGuid), + user=coalesce(user, group, builtin_group, aceSid) | stats values(aceType) as aceType + values(aceFlags) as aceFlags values(aceControlAccessRights) as aceControlAccessRights + values(aceAccessRights) as aceAccessRights values(new_ace) as new_ace values(aceInheritedTypeGuid) + as aceInheritedTypeGuid by _time ObjectClass ObjectDN src_user SubjectLogonId user + OpCorrelationID | eval aceControlAccessRights=if(mvcount(aceControlAccessRights)=1 + AND aceControlAccessRights=\"\",\"All rights\",'aceControlAccessRights') | search + NOT aceType IN (*denied*,D,OD,XD) AND aceAccessRights IN (\"Full control\",\"All + extended rights\",\"All validated writes\",\"Create all child objects\",\"Delete + all child objects\",\"Delete subtree\",\"Delete\",\"Modify permissions\",\"Modify + owner\",\"Write all properties\",CC,CR,DC,DT,SD,SW,WD,WO,WP) | `windows_ad_dangerous_user_acl_modification_filter`" +how_to_implement: Ensure you are ingesting Active Directory audit logs - specifically + event 5136. See lantern article in references for further on how to onboard AD audit + data. Ensure the wineventlog_security macro is configured with the correct indexes + and include lookups for SID resolution if evt_resolve_ad_obj is set to 0. known_false_positives: Unknown references: - https://learn.microsoft.com/en-us/windows/win32/secauthz/ace-strings @@ -22,7 +58,12 @@ drilldown_searches: earliest_offset: $info_min_time$ latest_offset: $info_max_time$ - name: View risk events for the last 7 days for - "$user$" and "$src_user$" - search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$", "$src_user$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' + search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$", + "$src_user$") starthoursago=168 | stats count min(_time) as firstTime max(_time) + as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk + Message" values(analyticstories) as "Analytic Stories" values(annotations._all) + as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" + by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' earliest_offset: $info_min_time$ latest_offset: $info_max_time$ rba: @@ -31,10 +72,10 @@ rba: risk_objects: - field: user type: User - risk_score: 100.0 + risk_score: 100 - field: src_user type: User - risk_score: 100.0 + risk_score: 100 threat_objects: [] tags: analytic_story: @@ -66,6 +107,7 @@ tags: tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1222.001/dacl_abuse/user_dacl_mod_windows-security-xml.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1222.001/dacl_abuse/user_dacl_mod_windows-security-xml.log source: XmlWinEventLog:Security sourcetype: XmlWinEventLog diff --git a/detections/application/windows_ad_dcshadow_privileges_acl_addition.yml b/detections/application/windows_ad_dcshadow_privileges_acl_addition.yml index 21a2c72f0c..5f0f013ed3 100644 --- a/detections/application/windows_ad_dcshadow_privileges_acl_addition.yml +++ b/detections/application/windows_ad_dcshadow_privileges_acl_addition.yml @@ -7,9 +7,41 @@ status: production type: TTP data_source: - Windows Security 5136 -description: This detection identifies an Active Directory access-control list (ACL) modification event, which applies the minimum required extended rights to perform the DCShadow attack. -search: '`wineventlog_security` EventCode=5136 ObjectClass=domainDNS | stats min(_time) as _time values(eval(if(OperationType=="%%14675",AttributeValue,null))) as old_value values(eval(if(OperationType=="%%14674",AttributeValue,null))) as new_value values(OperationType) as OperationType by ObjectClass ObjectDN OpCorrelationID src_user SubjectLogonId | rex field=old_value max_match=10000 "\((?P.*?)\)" | rex field=new_value max_match=10000 "\((?P.*?)\)" | mvexpand new_ace | where NOT new_ace IN (old_values) | rex field=new_ace "(?P.*?);(?P.*?);(?P.*?);(?P.*?);;(?P.*?)$" | search aceObjectGuid IN ("9923a32a-3607-11d2-b9be-0000f87a36b2","1131f6ab-9c07-11d1-f79f-00c04fc2dcd2","1131f6ac-9c07-11d1-f79f-00c04fc2dcd2") | rex max_match=100 field=aceAccessRights "(?P[A-Z]{2})" | rex max_match=100 field=aceFlags "(?P[A-Z]{2})" | lookup msad_guid_lookup guid as aceObjectGuid OUTPUT displayName as ControlAccessRights | lookup ace_access_rights_lookup access_rights_string as AccessRights OUTPUT access_rights_value | lookup ace_type_lookup ace_type_string as aceType OUTPUT ace_type_value | lookup ace_flag_lookup flag_string as aceFlags OUTPUT flag_value as ace_flag_value ``` Optional SID resolution lookups | lookup identity_lookup_expanded objectSid as aceSid OUTPUT downLevelDomainName as user | lookup admon_groups_def objectSid as aceSid OUTPUT cn as group ``` | lookup builtin_groups_lookup builtin_group_string as aceSid OUTPUT builtin_group_name as builtin_group | eval aceType=coalesce(ace_type_value,aceType), aceFlags=coalesce(ace_flag_value,"This object only"), aceAccessRights=if(aceAccessRights="CCDCLCSWRPWPDTLOCRSDRCWDWO","Full control",coalesce(access_rights_value,AccessRights)), aceControlAccessRights=coalesce(ControlAccessRights,aceObjectGuid), user=coalesce(user, group, builtin_group, aceSid) | stats min(_time) as _time values(aceType) as aceType values(aceFlags) as aceFlags(inheritance) values(aceControlAccessRights) as aceControlAccessRights values(aceAccessRights) as aceAccessRights values(new_ace) as new_ace values(SubjectLogonId) as SubjectLogonId by ObjectClass ObjectDN src_user user | search (aceControlAccessRights="Add/Remove Replica In Domain" AND aceControlAccessRights="Manage Replication Topology" AND aceControlAccessRights="Replication Synchronization") OR (aceControlAccessRights="9923a32a-3607-11d2-b9be-0000f87a36b2" AND aceControlAccessRights="1131f6ab-9c07-11d1-f79f-00c04fc2dcd2" AND aceControlAccessRights="1131f6ac-9c07-11d1-f79f-00c04fc2dcd2") | `windows_ad_dcshadow_privileges_acl_addition_filter`' -how_to_implement: Ensure you are ingesting Active Directory audit logs - specifically event 5136. See lantern article in references for further on how to onboard AD audit data. Ensure the wineventlog_security macro is configured with the correct indexes and include lookups for SID resolution if evt_resolve_ad_obj is set to 0. +description: This detection identifies an Active Directory access-control list (ACL) + modification event, which applies the minimum required extended rights to perform + the DCShadow attack. +search: '`wineventlog_security` EventCode=5136 ObjectClass=domainDNS | stats min(_time) + as _time values(eval(if(OperationType=="%%14675",AttributeValue,null))) as old_value + values(eval(if(OperationType=="%%14674",AttributeValue,null))) as new_value values(OperationType) + as OperationType by ObjectClass ObjectDN OpCorrelationID src_user SubjectLogonId | + rex field=old_value max_match=10000 "\((?P.*?)\)" | rex field=new_value + max_match=10000 "\((?P.*?)\)" | mvexpand new_ace | where NOT new_ace IN + (old_values) | rex field=new_ace "(?P.*?);(?P.*?);(?P.*?);(?P.*?);;(?P.*?)$" + | search aceObjectGuid IN ("9923a32a-3607-11d2-b9be-0000f87a36b2","1131f6ab-9c07-11d1-f79f-00c04fc2dcd2","1131f6ac-9c07-11d1-f79f-00c04fc2dcd2") + | rex max_match=100 field=aceAccessRights "(?P[A-Z]{2})" | rex max_match=100 + field=aceFlags "(?P[A-Z]{2})" | lookup msad_guid_lookup guid as aceObjectGuid + OUTPUT displayName as ControlAccessRights | lookup ace_access_rights_lookup access_rights_string + as AccessRights OUTPUT access_rights_value | lookup ace_type_lookup ace_type_string + as aceType OUTPUT ace_type_value | lookup ace_flag_lookup flag_string as aceFlags + OUTPUT flag_value as ace_flag_value ``` Optional SID resolution lookups | lookup + identity_lookup_expanded objectSid as aceSid OUTPUT downLevelDomainName as user | + lookup admon_groups_def objectSid as aceSid OUTPUT cn as group ``` | lookup builtin_groups_lookup + builtin_group_string as aceSid OUTPUT builtin_group_name as builtin_group | eval + aceType=coalesce(ace_type_value,aceType), aceFlags=coalesce(ace_flag_value,"This + object only"), aceAccessRights=if(aceAccessRights="CCDCLCSWRPWPDTLOCRSDRCWDWO","Full + control",coalesce(access_rights_value,AccessRights)), aceControlAccessRights=coalesce(ControlAccessRights,aceObjectGuid), + user=coalesce(user, group, builtin_group, aceSid) | stats min(_time) as _time values(aceType) + as aceType values(aceFlags) as aceFlags(inheritance) values(aceControlAccessRights) + as aceControlAccessRights values(aceAccessRights) as aceAccessRights values(new_ace) + as new_ace values(SubjectLogonId) as SubjectLogonId by ObjectClass ObjectDN src_user + user | search (aceControlAccessRights="Add/Remove Replica In Domain" AND aceControlAccessRights="Manage + Replication Topology" AND aceControlAccessRights="Replication Synchronization") + OR (aceControlAccessRights="9923a32a-3607-11d2-b9be-0000f87a36b2" AND aceControlAccessRights="1131f6ab-9c07-11d1-f79f-00c04fc2dcd2" + AND aceControlAccessRights="1131f6ac-9c07-11d1-f79f-00c04fc2dcd2") | `windows_ad_dcshadow_privileges_acl_addition_filter`' +how_to_implement: Ensure you are ingesting Active Directory audit logs - specifically + event 5136. See lantern article in references for further on how to onboard AD audit + data. Ensure the wineventlog_security macro is configured with the correct indexes + and include lookups for SID resolution if evt_resolve_ad_obj is set to 0. known_false_positives: Unknown references: - https://www.labofapenetrationtester.com/2018/04/dcshadow.html @@ -22,7 +54,12 @@ drilldown_searches: earliest_offset: $info_min_time$ latest_offset: $info_max_time$ - name: View risk events for the last 7 days for - "$user$" and "$src_user$" - search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$", "$src_user$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' + search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$", + "$src_user$") starthoursago=168 | stats count min(_time) as firstTime max(_time) + as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk + Message" values(analyticstories) as "Analytic Stories" values(annotations._all) + as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" + by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' earliest_offset: $info_min_time$ latest_offset: $info_max_time$ rba: @@ -31,10 +68,10 @@ rba: risk_objects: - field: user type: User - risk_score: 100.0 + risk_score: 100 - field: src_user type: User - risk_score: 100.0 + risk_score: 100 threat_objects: [] tags: analytic_story: @@ -64,6 +101,7 @@ tags: tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1484/DCShadowPermissions/windows-security-xml.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1484/DCShadowPermissions/windows-security-xml.log source: XmlWinEventLog:Security sourcetype: XmlWinEventLog diff --git a/detections/application/windows_ad_domain_root_acl_deletion.yml b/detections/application/windows_ad_domain_root_acl_deletion.yml index 7e188f468e..8a4a418f61 100644 --- a/detections/application/windows_ad_domain_root_acl_deletion.yml +++ b/detections/application/windows_ad_domain_root_acl_deletion.yml @@ -7,9 +7,38 @@ status: production type: TTP data_source: - Windows Security 5136 -description: ACL deletion performed on the domain root object, significant AD change with high impact. Following MS guidance all changes at this level should be reviewed. Drill into the logonID within EventCode 4624 for information on the source device during triage. -search: '`wineventlog_security` EventCode=5136 ObjectClass=domainDNS | stats min(_time) as _time values(eval(if(OperationType=="%%14675",AttributeValue,null))) as old_value values(eval(if(OperationType=="%%14674",AttributeValue,null))) as new_value values(OperationType) as OperationType by ObjectClass ObjectDN OpCorrelationID src_user SubjectLogonId | rex field=old_value max_match=10000 "\((?P.*?)\)" | rex field=new_value max_match=10000 "\((?P.*?)\)" | mvexpand old_values | where NOT old_values IN (new_values) | rex field=old_values "(?P.*?);(?P.*?);(?P.*?);(?P.*?);;(?P.*?)$" | rex max_match=100 field=aceAccessRights "(?P[A-Z]{2})" | rex max_match=100 field=aceFlags "(?P[A-Z]{2})" | lookup msad_guid_lookup guid as aceObjectGuid OUTPUT displayName as ControlAccessRights | lookup ace_access_rights_lookup access_rights_string as AccessRights OUTPUT access_rights_value | lookup ace_type_lookup ace_type_string as aceType OUTPUT ace_type_value | lookup ace_flag_lookup flag_string as aceFlags OUTPUT flag_value as ace_flag_value ``` Optional SID resolution lookups | lookup identity_lookup_expanded objectSid as aceSid OUTPUT downLevelDomainName as user | lookup admon_groups_def objectSid as aceSid OUTPUT cn as group ``` | lookup builtin_groups_lookup builtin_group_string as aceSid OUTPUT builtin_group_name as builtin_group | eval aceType=coalesce(ace_type_value,aceType), aceFlags=coalesce(ace_flag_value,"This object only"), aceAccessRights=if(aceAccessRights="CCDCLCSWRPWPDTLOCRSDRCWDWO","Full control",coalesce(access_rights_value,AccessRights)), aceControlAccessRights=coalesce(ControlAccessRights,aceObjectGuid), user=coalesce(user, group, builtin_group, aceSid) | stats values(aceType) as aceType values(aceFlags) as aceFlags(inheritance) values(aceControlAccessRights) as aceControlAccessRights values(aceAccessRights) as aceAccessRights values(old_values) as old_values by _time ObjectClass ObjectDN src_user SubjectLogonId user OpCorrelationID | eval aceControlAccessRights=if(mvcount(aceControlAccessRights)=1 AND aceControlAccessRights="","All rights",''aceControlAccessRights'') | `windows_ad_domain_root_acl_deletion_filter`' -how_to_implement: Ensure you are ingesting Active Directory audit logs - specifically event 5136. See lantern article in references for further on how to onboard AD audit data. Ensure the wineventlog_security macro is configured with the correct indexes and include lookups for SID resolution if evt_resolve_ad_obj is set to 0. +description: ACL deletion performed on the domain root object, significant AD change + with high impact. Following MS guidance all changes at this level should be reviewed. + Drill into the logonID within EventCode 4624 for information on the source device + during triage. +search: "`wineventlog_security` EventCode=5136 ObjectClass=domainDNS | stats min(_time) + as _time values(eval(if(OperationType==\"%%14675\",AttributeValue,null))) as old_value + values(eval(if(OperationType==\"%%14674\",AttributeValue,null))) as new_value values(OperationType) + as OperationType by ObjectClass ObjectDN OpCorrelationID src_user SubjectLogonId\ + \ | rex field=old_value max_match=10000 \"\\((?P.*?)\\)\" | rex field=new_value + max_match=10000 \"\\((?P.*?)\\)\" | mvexpand old_values | where NOT + old_values IN (new_values) | rex field=old_values \"(?P.*?);(?P.*?);(?P.*?);(?P.*?);;(?P.*?)$\"\ + \ | rex max_match=100 field=aceAccessRights \"(?P[A-Z]{2})\" | rex + max_match=100 field=aceFlags \"(?P[A-Z]{2})\" | lookup msad_guid_lookup + guid as aceObjectGuid OUTPUT displayName as ControlAccessRights | lookup ace_access_rights_lookup + access_rights_string as AccessRights OUTPUT access_rights_value | lookup ace_type_lookup + ace_type_string as aceType OUTPUT ace_type_value | lookup ace_flag_lookup flag_string + as aceFlags OUTPUT flag_value as ace_flag_value ``` Optional SID resolution lookups + | lookup identity_lookup_expanded objectSid as aceSid OUTPUT downLevelDomainName + as user | lookup admon_groups_def objectSid as aceSid OUTPUT cn as group ``` | + lookup builtin_groups_lookup builtin_group_string as aceSid OUTPUT builtin_group_name + as builtin_group | eval aceType=coalesce(ace_type_value,aceType), aceFlags=coalesce(ace_flag_value,\"\ + This object only\"), aceAccessRights=if(aceAccessRights=\"CCDCLCSWRPWPDTLOCRSDRCWDWO\"\ + ,\"Full control\",coalesce(access_rights_value,AccessRights)), aceControlAccessRights=coalesce(ControlAccessRights,aceObjectGuid), + user=coalesce(user, group, builtin_group, aceSid) | stats values(aceType) as aceType + values(aceFlags) as aceFlags(inheritance) values(aceControlAccessRights) as aceControlAccessRights + values(aceAccessRights) as aceAccessRights values(old_values) as old_values by _time + ObjectClass ObjectDN src_user SubjectLogonId user OpCorrelationID | eval aceControlAccessRights=if(mvcount(aceControlAccessRights)=1 + AND aceControlAccessRights=\"\",\"All rights\",'aceControlAccessRights') | `windows_ad_domain_root_acl_deletion_filter`" +how_to_implement: Ensure you are ingesting Active Directory audit logs - specifically + event 5136. See lantern article in references for further on how to onboard AD audit + data. Ensure the wineventlog_security macro is configured with the correct indexes + and include lookups for SID resolution if evt_resolve_ad_obj is set to 0. known_false_positives: Unknown references: - https://learn.microsoft.com/en-us/windows/win32/secauthz/ace-strings @@ -22,7 +51,12 @@ drilldown_searches: earliest_offset: $info_min_time$ latest_offset: $info_max_time$ - name: View risk events for the last 7 days for - "$user$" and "$src_user$" - search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$", "$src_user$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' + search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$", + "$src_user$") starthoursago=168 | stats count min(_time) as firstTime max(_time) + as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk + Message" values(analyticstories) as "Analytic Stories" values(annotations._all) + as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" + by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' earliest_offset: $info_min_time$ latest_offset: $info_max_time$ rba: @@ -31,10 +65,10 @@ rba: risk_objects: - field: user type: User - risk_score: 100.0 + risk_score: 100 - field: src_user type: User - risk_score: 100.0 + risk_score: 100 threat_objects: [] tags: analytic_story: @@ -66,6 +100,7 @@ tags: tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1222.001/dacl_abuse/domain_root_acl_deletion_windows-security-xml.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1222.001/dacl_abuse/domain_root_acl_deletion_windows-security-xml.log source: XmlWinEventLog:Security sourcetype: XmlWinEventLog diff --git a/detections/application/windows_ad_domain_root_acl_modification.yml b/detections/application/windows_ad_domain_root_acl_modification.yml index a0ca3b3bee..c3bc752df9 100644 --- a/detections/application/windows_ad_domain_root_acl_modification.yml +++ b/detections/application/windows_ad_domain_root_acl_modification.yml @@ -7,9 +7,38 @@ status: production type: TTP data_source: - Windows Security 5136 -description: ACL modification performed on the domain root object, significant AD change with high impact. Following MS guidance all changes at this level should be reviewed. Drill into the logonID within EventCode 4624 for information on the source device during triage. -search: '`wineventlog_security` EventCode=5136 ObjectClass=domainDNS | stats min(_time) as _time values(eval(if(OperationType=="%%14675",AttributeValue,null))) as old_value values(eval(if(OperationType=="%%14674",AttributeValue,null))) as new_value values(OperationType) as OperationType by ObjectClass ObjectDN OpCorrelationID src_user SubjectLogonId | rex field=old_value max_match=10000 "\((?P.*?)\)" | rex field=new_value max_match=10000 "\((?P.*?)\)" | mvexpand new_ace | where NOT new_ace IN (old_values) | rex field=new_ace "(?P.*?);(?P.*?);(?P.*?);(?P.*?);;(?P.*?)$" | rex max_match=100 field=aceAccessRights "(?P[A-Z]{2})" | rex max_match=100 field=aceFlags "(?P[A-Z]{2})" | lookup msad_guid_lookup guid as aceObjectGuid OUTPUT displayName as ControlAccessRights | lookup ace_access_rights_lookup access_rights_string as AccessRights OUTPUT access_rights_value | lookup ace_type_lookup ace_type_string as aceType OUTPUT ace_type_value | lookup ace_flag_lookup flag_string as aceFlags OUTPUT flag_value as ace_flag_value ``` Optional SID resolution lookups | lookup identity_lookup_expanded objectSid as aceSid OUTPUT downLevelDomainName as user | lookup admon_groups_def objectSid as aceSid OUTPUT cn as group ``` | lookup builtin_groups_lookup builtin_group_string as aceSid OUTPUT builtin_group_name as builtin_group | eval aceAccessRights=if(aceAccessRights="CCDCLCSWRPWPDTLOCRSDRCWDWO","Full control",''access_rights_value''), aceType=ace_type_value, aceFlags=coalesce(ace_flag_value,"This object only"), aceControlAccessRights=ControlAccessRights, user=coalesce(user, group, builtin_group, aceSid) | stats values(aceType) as aceType values(aceFlags) as aceFlags(inheritance) values(aceControlAccessRights) as aceControlAccessRights values(aceAccessRights) as aceAccessRights values(new_ace) as new_ace by _time ObjectClass ObjectDN src_user SubjectLogonId user OpCorrelationID | eval aceControlAccessRights=if(mvcount(aceControlAccessRights)=1 AND aceControlAccessRights="","All rights",''aceControlAccessRights'') | `windows_ad_domain_root_acl_modification_filter`' -how_to_implement: Ensure you are ingesting Active Directory audit logs - specifically event 5136. See lantern article in references for further on how to onboard AD audit data. Ensure the wineventlog_security macro is configured with the correct indexes and include lookups for SID resolution if evt_resolve_ad_obj is set to 0. +description: ACL modification performed on the domain root object, significant AD + change with high impact. Following MS guidance all changes at this level should + be reviewed. Drill into the logonID within EventCode 4624 for information on the + source device during triage. +search: "`wineventlog_security` EventCode=5136 ObjectClass=domainDNS | stats min(_time) + as _time values(eval(if(OperationType==\"%%14675\",AttributeValue,null))) as old_value + values(eval(if(OperationType==\"%%14674\",AttributeValue,null))) as new_value values(OperationType) + as OperationType by ObjectClass ObjectDN OpCorrelationID src_user SubjectLogonId\ + \ | rex field=old_value max_match=10000 \"\\((?P.*?)\\)\" | rex field=new_value + max_match=10000 \"\\((?P.*?)\\)\" | mvexpand new_ace | where NOT new_ace + IN (old_values) | rex field=new_ace \"(?P.*?);(?P.*?);(?P.*?);(?P.*?);;(?P.*?)$\"\ + \ | rex max_match=100 field=aceAccessRights \"(?P[A-Z]{2})\" | rex + max_match=100 field=aceFlags \"(?P[A-Z]{2})\" | lookup msad_guid_lookup + guid as aceObjectGuid OUTPUT displayName as ControlAccessRights | lookup ace_access_rights_lookup + access_rights_string as AccessRights OUTPUT access_rights_value | lookup ace_type_lookup + ace_type_string as aceType OUTPUT ace_type_value | lookup ace_flag_lookup flag_string + as aceFlags OUTPUT flag_value as ace_flag_value ``` Optional SID resolution lookups + | lookup identity_lookup_expanded objectSid as aceSid OUTPUT downLevelDomainName + as user | lookup admon_groups_def objectSid as aceSid OUTPUT cn as group ``` | + lookup builtin_groups_lookup builtin_group_string as aceSid OUTPUT builtin_group_name + as builtin_group | eval aceAccessRights=if(aceAccessRights=\"CCDCLCSWRPWPDTLOCRSDRCWDWO\"\ + ,\"Full control\",'access_rights_value'), aceType=ace_type_value, aceFlags=coalesce(ace_flag_value,\"\ + This object only\"), aceControlAccessRights=ControlAccessRights, user=coalesce(user, + group, builtin_group, aceSid) | stats values(aceType) as aceType values(aceFlags) + as aceFlags(inheritance) values(aceControlAccessRights) as aceControlAccessRights + values(aceAccessRights) as aceAccessRights values(new_ace) as new_ace by _time ObjectClass + ObjectDN src_user SubjectLogonId user OpCorrelationID | eval aceControlAccessRights=if(mvcount(aceControlAccessRights)=1 + AND aceControlAccessRights=\"\",\"All rights\",'aceControlAccessRights') | `windows_ad_domain_root_acl_modification_filter`" +how_to_implement: Ensure you are ingesting Active Directory audit logs - specifically + event 5136. See lantern article in references for further on how to onboard AD audit + data. Ensure the wineventlog_security macro is configured with the correct indexes + and include lookups for SID resolution if evt_resolve_ad_obj is set to 0. known_false_positives: Unknown references: - https://learn.microsoft.com/en-us/windows/win32/secauthz/ace-strings @@ -22,7 +51,12 @@ drilldown_searches: earliest_offset: $info_min_time$ latest_offset: $info_max_time$ - name: View risk events for the last 7 days for - "$user$" and "$src_user$" - search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$", "$src_user$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' + search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$", + "$src_user$") starthoursago=168 | stats count min(_time) as firstTime max(_time) + as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk + Message" values(analyticstories) as "Analytic Stories" values(annotations._all) + as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" + by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' earliest_offset: $info_min_time$ latest_offset: $info_max_time$ rba: @@ -31,10 +65,10 @@ rba: risk_objects: - field: user type: User - risk_score: 100.0 + risk_score: 100 - field: src_user type: User - risk_score: 100.0 + risk_score: 100 threat_objects: [] tags: analytic_story: @@ -66,6 +100,7 @@ tags: tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1222.001/dacl_abuse/domain_root_acl_mod_windows-security-xml.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1222.001/dacl_abuse/domain_root_acl_mod_windows-security-xml.log source: XmlWinEventLog:Security sourcetype: XmlWinEventLog diff --git a/detections/application/windows_ad_gpo_deleted.yml b/detections/application/windows_ad_gpo_deleted.yml index 186af9ce40..4d526ee3b2 100644 --- a/detections/application/windows_ad_gpo_deleted.yml +++ b/detections/application/windows_ad_gpo_deleted.yml @@ -7,9 +7,24 @@ status: production type: TTP data_source: - Windows Security 5136 -description: This detection identifies when an Active Directory Group Policy is deleted using the Group Policy Management Console. -search: '`wineventlog_security` EventCode=5136 AttributeLDAPDisplayName=gpLink | eval ObjectDN=upper(ObjectDN) | stats min(_time) as eventTime values(eval(if(OperationType=="%%14675",AttributeValue,null))) as old_value values(eval(if(OperationType=="%%14674",AttributeValue,null))) as new_value values(OperationType) as OperationType values(src_user) as src_user by OpCorrelationID ObjectDN SubjectLogonId | rex field=old_value max_match=10000 "(?i)LDAP://(?Pcn.*?);(?P\d)\]" | rex field=new_value max_match=10000 "(?i)LDAP://(?Pcn.*?);(?P\d)\]" | mvexpand old_dn | where NOT old_dn IN (new_dn) | eval ObjectDN=upper(old_dn) | join ObjectDN type=outer [| search `admon` objectCategory="CN=Group-Policy-Container*" admonEventType=Update | eval ObjectDN=upper(distinguishedName) | stats latest(displayName) as displayName by ObjectDN ] | stats min(eventTime) as _time values(OpCorrelationID) as OpCorrelationID values(displayName) as policyName values(src_user) as src_user by ObjectDN SubjectLogonId | `windows_ad_gpo_deleted_filter`' -how_to_implement: Ensure you are ingesting Active Directory audit logs - specifically event 5136, admon data is also used to display the display name of the GPO. See lantern article in references for further on how to onboard AD audit data. Ensure the wineventlog_security and admon macros are configured with the correct indexes. +description: This detection identifies when an Active Directory Group Policy is deleted + using the Group Policy Management Console. +search: '`wineventlog_security` EventCode=5136 AttributeLDAPDisplayName=gpLink | eval ObjectDN=upper(ObjectDN) + | stats min(_time) as eventTime values(eval(if(OperationType=="%%14675",AttributeValue,null))) + as old_value values(eval(if(OperationType=="%%14674",AttributeValue,null))) as new_value + values(OperationType) as OperationType values(src_user) as src_user by OpCorrelationID + ObjectDN SubjectLogonId | rex field=old_value max_match=10000 "(?i)LDAP://(?Pcn.*?);(?P\d)\]" + | rex field=new_value max_match=10000 "(?i)LDAP://(?Pcn.*?);(?P\d)\]" + | mvexpand old_dn | where NOT old_dn IN (new_dn) | eval ObjectDN=upper(old_dn) | + join ObjectDN type=outer [| search `admon` objectCategory="CN=Group-Policy-Container*" + admonEventType=Update | eval ObjectDN=upper(distinguishedName) | stats latest(displayName) + as displayName by ObjectDN ] | stats min(eventTime) as _time values(OpCorrelationID) + as OpCorrelationID values(displayName) as policyName values(src_user) as src_user + by ObjectDN SubjectLogonId | `windows_ad_gpo_deleted_filter`' +how_to_implement: Ensure you are ingesting Active Directory audit logs - specifically + event 5136, admon data is also used to display the display name of the GPO. See + lantern article in references for further on how to onboard AD audit data. Ensure + the wineventlog_security and admon macros are configured with the correct indexes. known_false_positives: Unknown references: - https://lantern.splunk.com/Security/Product_Tips/Enterprise_Security/Enabling_an_audit_trail_from_Active_Directory @@ -19,7 +34,12 @@ drilldown_searches: earliest_offset: $info_min_time$ latest_offset: $info_max_time$ - name: View risk events for the last 7 days for - "$src_user$" - search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$src_user$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' + search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$src_user$") + starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime + values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) + as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) + as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` + | `security_content_ctime(lastTime)`' earliest_offset: $info_min_time$ latest_offset: $info_max_time$ rba: @@ -27,7 +47,7 @@ rba: risk_objects: - field: src_user type: User - risk_score: 64.0 + risk_score: 64 threat_objects: [] tags: analytic_story: @@ -58,9 +78,11 @@ tags: tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1484.001/group_policy_deleted/windows-security.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1484.001/group_policy_deleted/windows-security.log source: XmlWinEventLog:Security sourcetype: XmlWinEventLog - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1484.001/group_policy_deleted/windows-admon.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1484.001/group_policy_deleted/windows-admon.log source: ActiveDirectory sourcetype: ActiveDirectory diff --git a/detections/application/windows_ad_gpo_disabled.yml b/detections/application/windows_ad_gpo_disabled.yml index 4d6813a444..3757baf876 100644 --- a/detections/application/windows_ad_gpo_disabled.yml +++ b/detections/application/windows_ad_gpo_disabled.yml @@ -7,9 +7,22 @@ status: production type: TTP data_source: - Windows Security 5136 -description: This detection identifies when an Active Directory Group Policy is disabled using the Group Policy Management Console. -search: '`wineventlog_security` EventCode=5136 AttributeLDAPDisplayName=flags OperationType="%%14674" AttributeValue!=0 | eval AttributeValueExp=case(AttributeValue==0,"Enabled",AttributeValue==1,"User configuration settings disabled",AttributeValue==2,"Computer configuration settings disabled",AttributeValue==3,"Disabled"), ObjectDN=upper(ObjectDN) | join ObjectDN type=inner [| search `admon` objectCategory="CN=Group-Policy-Container*" admonEventType=Update | eval ObjectDN=upper(distinguishedName) | stats latest(displayName) as displayName by ObjectDN ] | stats min(_time) as _time values(AttributeValue) as AttributeValue values(AttributeValueExp) as AttributeValueExp values(OpCorrelationID) as OpCorrelationID values(displayName) as policyName values(src_user) as src_user by ObjectDN SubjectLogonId | `windows_ad_gpo_disabled_filter`' -how_to_implement: Ensure you are ingesting Active Directory audit logs - specifically event 5136, admon data is also used to display the display name of the GPO. See lantern article in references for further on how to onboard AD audit data. Ensure the wineventlog_security and admon macros are configured with the correct indexes. +description: This detection identifies when an Active Directory Group Policy is disabled + using the Group Policy Management Console. +search: '`wineventlog_security` EventCode=5136 AttributeLDAPDisplayName=flags OperationType="%%14674" + AttributeValue!=0 | eval AttributeValueExp=case(AttributeValue==0,"Enabled",AttributeValue==1,"User + configuration settings disabled",AttributeValue==2,"Computer configuration settings + disabled",AttributeValue==3,"Disabled"), ObjectDN=upper(ObjectDN) | join ObjectDN + type=inner [| search `admon` objectCategory="CN=Group-Policy-Container*" admonEventType=Update + | eval ObjectDN=upper(distinguishedName) | stats latest(displayName) as displayName + by ObjectDN ] | stats min(_time) as _time values(AttributeValue) as AttributeValue + values(AttributeValueExp) as AttributeValueExp values(OpCorrelationID) as OpCorrelationID + values(displayName) as policyName values(src_user) as src_user by ObjectDN SubjectLogonId + | `windows_ad_gpo_disabled_filter`' +how_to_implement: Ensure you are ingesting Active Directory audit logs - specifically + event 5136, admon data is also used to display the display name of the GPO. See + lantern article in references for further on how to onboard AD audit data. Ensure + the wineventlog_security and admon macros are configured with the correct indexes. known_false_positives: Unknown references: - https://lantern.splunk.com/Security/Product_Tips/Enterprise_Security/Enabling_an_audit_trail_from_Active_Directory @@ -19,7 +32,12 @@ drilldown_searches: earliest_offset: $info_min_time$ latest_offset: $info_max_time$ - name: View risk events for the last 7 days for - "$src_user$" - search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$src_user$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' + search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$src_user$") + starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime + values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) + as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) + as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` + | `security_content_ctime(lastTime)`' earliest_offset: $info_min_time$ latest_offset: $info_max_time$ rba: @@ -27,7 +45,7 @@ rba: risk_objects: - field: src_user type: User - risk_score: 64.0 + risk_score: 64 threat_objects: [] tags: analytic_story: @@ -58,9 +76,11 @@ tags: tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1484.001/group_policy_disabled/windows-security.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1484.001/group_policy_disabled/windows-security.log source: XmlWinEventLog:Security sourcetype: XmlWinEventLog - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1484.001/group_policy_disabled/windows-admon.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1484.001/group_policy_disabled/windows-admon.log source: ActiveDirectory sourcetype: ActiveDirectory diff --git a/detections/application/windows_ad_gpo_new_cse_addition.yml b/detections/application/windows_ad_gpo_new_cse_addition.yml index 826c3827f9..f63080a5fc 100644 --- a/detections/application/windows_ad_gpo_new_cse_addition.yml +++ b/detections/application/windows_ad_gpo_new_cse_addition.yml @@ -7,10 +7,31 @@ status: production type: TTP data_source: - Windows Security 5136 -description: This detection identifies when a a new client side extension is added to an Active Directory Group Policy using the Group Policy Management Console. -search: '`wineventlog_security` EventCode=5136 ObjectClass=groupPolicyContainer AttributeLDAPDisplayName=gPCMachineExtensionNames | stats min(_time) as _time values(eval(if(OperationType=="%%14675",AttributeValue,null))) as old_value values(eval(if(OperationType=="%%14674",AttributeValue,null))) as new_value values(OperationType) as OperationType by ObjectClass ObjectDN OpCorrelationID src_user SubjectLogonId | rex field=old_value max_match=10000 "(?P\{.*?\})" | rex field=new_value max_match=10000 "(?P\{.*?\})" | rex field=ObjectDN max_match=10000 "CN=(?P\{.*?\})" | mvexpand new_values | where NOT new_values IN (old_values,"{00000000-0000-0000-0000-000000000000}",policy_guid) AND match(new_values, "^\{[A-Z|\d]+\-[A-Z|\d]+\-[A-Z|\d]+\-[A-Z|\d]+\-[A-Z|\d]+\}") | lookup msad_guid_lookup guid as new_values OUTPUTNEW displayName as policyType | eval newPolicy=if(policyType like "%",policyType,new_values) | join ObjectDN [| search `admon` objectCategory="CN=Group-Policy-Container*" admonEventType=Update | stats latest(displayName) as displayName by distinguishedName | eval ObjectDN=upper(distinguishedName)] | stats values(OpCorrelationID) as OpCorrelationID values(src_user) as src_user values(SubjectLogonId) as SubjectLogonId values(newPolicy) as newPolicy values(displayName) as policyName by ObjectDN | `windows_ad_gpo_new_cse_addition_filter`' -how_to_implement: Ensure you are ingesting Active Directory audit logs - specifically event 5136, admon data is also used to display the display name of the GPO. See lantern article in references for further on how to onboard AD audit data. Ensure the wineventlog_security and admon macros are configured with the correct indexes. -known_false_positives: General usage of group policy will trigger this detection, also please not GPOs modified using tools such as SharpGPOAbuse will not generate the AD audit events which enable this detection. +description: This detection identifies when a a new client side extension is added + to an Active Directory Group Policy using the Group Policy Management Console. +search: '`wineventlog_security` EventCode=5136 ObjectClass=groupPolicyContainer AttributeLDAPDisplayName=gPCMachineExtensionNames + | stats min(_time) as _time values(eval(if(OperationType=="%%14675",AttributeValue,null))) + as old_value values(eval(if(OperationType=="%%14674",AttributeValue,null))) as new_value + values(OperationType) as OperationType by ObjectClass ObjectDN OpCorrelationID src_user + SubjectLogonId | rex field=old_value max_match=10000 "(?P\{.*?\})" | + rex field=new_value max_match=10000 "(?P\{.*?\})" | rex field=ObjectDN + max_match=10000 "CN=(?P\{.*?\})" | mvexpand new_values | where NOT + new_values IN (old_values,"{00000000-0000-0000-0000-000000000000}",policy_guid) + AND match(new_values, "^\{[A-Z|\d]+\-[A-Z|\d]+\-[A-Z|\d]+\-[A-Z|\d]+\-[A-Z|\d]+\}") + | lookup msad_guid_lookup guid as new_values OUTPUTNEW displayName as policyType + | eval newPolicy=if(policyType like "%",policyType,new_values) | join ObjectDN [| + search `admon` objectCategory="CN=Group-Policy-Container*" admonEventType=Update + | stats latest(displayName) as displayName by distinguishedName | eval ObjectDN=upper(distinguishedName)] + | stats values(OpCorrelationID) as OpCorrelationID values(src_user) as src_user + values(SubjectLogonId) as SubjectLogonId values(newPolicy) as newPolicy values(displayName) + as policyName by ObjectDN | `windows_ad_gpo_new_cse_addition_filter`' +how_to_implement: Ensure you are ingesting Active Directory audit logs - specifically + event 5136, admon data is also used to display the display name of the GPO. See + lantern article in references for further on how to onboard AD audit data. Ensure + the wineventlog_security and admon macros are configured with the correct indexes. +known_false_positives: General usage of group policy will trigger this detection, + also please not GPOs modified using tools such as SharpGPOAbuse will not generate + the AD audit events which enable this detection. references: - https://wald0.com/?p=179 - https://learn.microsoft.com/en-gb/archive/blogs/mempson/group-policy-client-side-extension-list @@ -22,7 +43,12 @@ drilldown_searches: earliest_offset: $info_min_time$ latest_offset: $info_max_time$ - name: View risk events for the last 7 days for - "$src_user$" - search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$src_user$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' + search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$src_user$") + starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime + values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) + as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) + as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` + | `security_content_ctime(lastTime)`' earliest_offset: $info_min_time$ latest_offset: $info_max_time$ rba: @@ -31,7 +57,7 @@ rba: risk_objects: - field: src_user type: User - risk_score: 100.0 + risk_score: 100 threat_objects: [] tags: analytic_story: @@ -64,9 +90,11 @@ tags: tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1484.001/group_policy_new_cse/windows-security.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1484.001/group_policy_new_cse/windows-security.log source: XmlWinEventLog:Security sourcetype: XmlWinEventLog - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1484.001/group_policy_new_cse/windows-admon.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1484.001/group_policy_new_cse/windows-admon.log source: ActiveDirectory sourcetype: ActiveDirectory diff --git a/detections/application/windows_ad_hidden_ou_creation.yml b/detections/application/windows_ad_hidden_ou_creation.yml index 94ab235095..04bd88dc09 100644 --- a/detections/application/windows_ad_hidden_ou_creation.yml +++ b/detections/application/windows_ad_hidden_ou_creation.yml @@ -7,9 +7,40 @@ status: production type: TTP data_source: - Windows Security 5136 -description: This analytic is looking for when an ACL is applied to an OU which denies listing the objects residing in the OU. This activity combined with modifying the owner of the OU will hide AD objects even from domain administrators. -search: '`wineventlog_security` EventCode=5136 ObjectClass=organizationalUnit | stats min(_time) as _time values(eval(if(OperationType=="%%14675",AttributeValue,null))) as old_value values(eval(if(OperationType=="%%14674",AttributeValue,null))) as new_value values(OperationType) as OperationType by ObjectClass ObjectDN OpCorrelationID src_user SubjectLogonId | rex field=old_value max_match=10000 "\((?P.*?)\)" | rex field=new_value max_match=10000 "\((?P.*?)\)" | mvexpand new_ace | where NOT new_ace IN (old_values) | rex field=new_ace "(?P.*?);(?P.*?);(?P.*?);(?P.*?);(?P.*?);(?P.*?)$" | rex max_match=100 field=aceAccessRights "(?P[A-Z]{2})" | rex max_match=100 field=aceFlags "(?P[A-Z]{2})" | lookup msad_guid_lookup guid as aceObjectGuid OUTPUT displayName as ControlAccessRights | lookup ace_access_rights_lookup access_rights_string as AccessRights OUTPUT access_rights_value | lookup ace_type_lookup ace_type_string as aceType OUTPUT ace_type_value as aceType | lookup ace_flag_lookup flag_string as aceFlags OUTPUT flag_value as ace_flag_value ``` Optional SID resolution lookups | lookup identity_lookup_expanded objectSid as aceSid OUTPUT downLevelDomainName as user | lookup admon_groups_def objectSid as aceSid OUTPUT cn as group ``` | lookup builtin_groups_lookup builtin_group_string as aceSid OUTPUT builtin_group_name as builtin_group | eval aceType=coalesce(ace_type_value,aceType), aceFlags=coalesce(ace_flag_value,"This object only"), aceAccessRights=if(aceAccessRights="CCDCLCSWRPWPDTLOCRSDRCWDWO","Full control",coalesce(access_rights_value,AccessRights)), aceControlAccessRights=coalesce(ControlAccessRights,aceObjectGuid), user=coalesce(user, group, builtin_group, aceSid) | stats values(aceType) as aceType values(aceFlags) as aceFlags values(aceControlAccessRights) as aceControlAccessRights values(aceAccessRights) as aceAccessRights values(new_ace) as new_ace values(aceInheritedTypeGuid) as aceInheritedTypeGuid by _time ObjectClass ObjectDN src_user SubjectLogonId user OpCorrelationID | eval aceControlAccessRights=if(mvcount(aceControlAccessRights)=1 AND aceControlAccessRights="","All rights",''aceControlAccessRights'') | search aceType IN ("Access denied",D) AND aceAccessRights IN ("List contents","List objects",LC,LO) | `windows_ad_hidden_ou_creation_filter`' -how_to_implement: Ensure you are ingesting Active Directory audit logs - specifically event 5136. See lantern article in references for further on how to onboard AD audit data. Ensure the wineventlog_security macro is configured with the correct indexes and include lookups for SID resolution if evt_resolve_ad_obj is set to 0. +description: This analytic is looking for when an ACL is applied to an OU which denies + listing the objects residing in the OU. This activity combined with modifying the + owner of the OU will hide AD objects even from domain administrators. +search: "`wineventlog_security` EventCode=5136 ObjectClass=organizationalUnit | stats + min(_time) as _time values(eval(if(OperationType==\"%%14675\",AttributeValue,null))) + as old_value values(eval(if(OperationType==\"%%14674\",AttributeValue,null))) as + new_value values(OperationType) as OperationType by ObjectClass ObjectDN OpCorrelationID + src_user SubjectLogonId | rex field=old_value max_match=10000 \"\\((?P.*?)\\\ + )\" | rex field=new_value max_match=10000 \"\\((?P.*?)\\)\" | mvexpand + new_ace | where NOT new_ace IN (old_values) | rex field=new_ace \"(?P.*?);(?P.*?);(?P.*?);(?P.*?);(?P.*?);(?P.*?)$\"\ + \ | rex max_match=100 field=aceAccessRights \"(?P[A-Z]{2})\" | rex + max_match=100 field=aceFlags \"(?P[A-Z]{2})\" | lookup msad_guid_lookup + guid as aceObjectGuid OUTPUT displayName as ControlAccessRights | lookup ace_access_rights_lookup + access_rights_string as AccessRights OUTPUT access_rights_value | lookup ace_type_lookup + ace_type_string as aceType OUTPUT ace_type_value as aceType | lookup ace_flag_lookup + flag_string as aceFlags OUTPUT flag_value as ace_flag_value ``` Optional SID resolution + lookups | lookup identity_lookup_expanded objectSid as aceSid OUTPUT downLevelDomainName + as user | lookup admon_groups_def objectSid as aceSid OUTPUT cn as group ``` | + lookup builtin_groups_lookup builtin_group_string as aceSid OUTPUT builtin_group_name + as builtin_group | eval aceType=coalesce(ace_type_value,aceType), aceFlags=coalesce(ace_flag_value,\"\ + This object only\"), aceAccessRights=if(aceAccessRights=\"CCDCLCSWRPWPDTLOCRSDRCWDWO\"\ + ,\"Full control\",coalesce(access_rights_value,AccessRights)), aceControlAccessRights=coalesce(ControlAccessRights,aceObjectGuid), + user=coalesce(user, group, builtin_group, aceSid) | stats values(aceType) as aceType + values(aceFlags) as aceFlags values(aceControlAccessRights) as aceControlAccessRights + values(aceAccessRights) as aceAccessRights values(new_ace) as new_ace values(aceInheritedTypeGuid) + as aceInheritedTypeGuid by _time ObjectClass ObjectDN src_user SubjectLogonId user + OpCorrelationID | eval aceControlAccessRights=if(mvcount(aceControlAccessRights)=1 + AND aceControlAccessRights=\"\",\"All rights\",'aceControlAccessRights') | search + aceType IN (\"Access denied\",D) AND aceAccessRights IN (\"List contents\",\"List + objects\",LC,LO) | `windows_ad_hidden_ou_creation_filter`" +how_to_implement: Ensure you are ingesting Active Directory audit logs - specifically + event 5136. See lantern article in references for further on how to onboard AD audit + data. Ensure the wineventlog_security macro is configured with the correct indexes + and include lookups for SID resolution if evt_resolve_ad_obj is set to 0. known_false_positives: None. references: - https://happycamper84.medium.com/sneaky-persistence-via-hidden-objects-in-ad-1c91fc37bf54 @@ -20,7 +51,12 @@ drilldown_searches: earliest_offset: $info_min_time$ latest_offset: $info_max_time$ - name: View risk events for the last 7 days for - "$user$" and "$src_user$" - search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$", "$src_user$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' + search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$", + "$src_user$") starthoursago=168 | stats count min(_time) as firstTime max(_time) + as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk + Message" values(analyticstories) as "Analytic Stories" values(annotations._all) + as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" + by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' earliest_offset: $info_min_time$ latest_offset: $info_max_time$ rba: @@ -28,10 +64,10 @@ rba: risk_objects: - field: user type: User - risk_score: 100.0 + risk_score: 100 - field: src_user type: User - risk_score: 100.0 + risk_score: 100 threat_objects: [] tags: analytic_story: @@ -67,6 +103,7 @@ tags: tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1222.001/dacl_abuse/hidden_ou_windows-security-xml.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1222.001/dacl_abuse/hidden_ou_windows-security-xml.log source: XmlWinEventLog:Security sourcetype: XmlWinEventLog diff --git a/detections/application/windows_ad_object_owner_updated.yml b/detections/application/windows_ad_object_owner_updated.yml index e74ee9995b..bcbe8a25c0 100644 --- a/detections/application/windows_ad_object_owner_updated.yml +++ b/detections/application/windows_ad_object_owner_updated.yml @@ -7,9 +7,30 @@ status: production type: TTP data_source: - Windows Security 5136 -description: AD Object Owner Updated. The owner provides Full control level privileges over the target AD Object. This event has significant impact alone and is also a precursor activity for hiding an AD object. -search: '`wineventlog_security` EventCode=5136 | stats min(_time) as _time values(eval(if(OperationType=="%%14675",AttributeValue,null))) as old_value values(eval(if(OperationType=="%%14674",AttributeValue,null))) as new_value values(OperationType) as OperationType by ObjectClass ObjectDN OpCorrelationID src_user SubjectLogonId DSName | rex field=old_value "O:(?P.*?)G:" | rex field=new_value "O:(?P.*?)G:" | where old_owner!=new_owner ``` optional SID resolution lookups | lookup identity_lookup_expanded objectSid as new_owner OUTPUT downLevelDomainName as new_owner_user | lookup admon_groups_def objectSid as new_owner OUTPUT cn as new_owner_group | lookup identity_lookup_expanded objectSid as old_owner OUTPUT downLevelDomainName as old_owner_user | lookup admon_groups_def objectSid as old_owner OUTPUT cn as old_owner_group ``` | lookup builtin_groups_lookup builtin_group_string as new_owner_group OUTPUT builtin_group_name as new_owner_group_builtin_group | lookup builtin_groups_lookup builtin_group_string as old_owner OUTPUT builtin_group_name as old_owner_group_builtin_group | eval user=coalesce(new_owner_user, new_owner_group, new_owner_group_builtin_group, new_owner), previousOwner=coalesce(old_owner_user, old_owner_group, old_owner_group_builtin_group, old_owner) | stats values(previousOwner) as previousOwner values(user) as user values(SubjectLogonId) as SubjectLogonId by _time ObjectClass ObjectDN src_user OpCorrelationID DSName | `windows_ad_object_owner_updated_filter`' -how_to_implement: Ensure you are ingesting Active Directory audit logs - specifically event 5136. See lantern article in references for further on how to onboard AD audit data. Ensure the wineventlog_security macro is configured with the correct indexes and include lookups for SID resolution if evt_resolve_ad_obj is set to 0. +description: AD Object Owner Updated. The owner provides Full control level privileges + over the target AD Object. This event has significant impact alone and is also a + precursor activity for hiding an AD object. +search: '`wineventlog_security` EventCode=5136 | stats min(_time) as _time values(eval(if(OperationType=="%%14675",AttributeValue,null))) + as old_value values(eval(if(OperationType=="%%14674",AttributeValue,null))) as new_value + values(OperationType) as OperationType by ObjectClass ObjectDN OpCorrelationID src_user + SubjectLogonId DSName | rex field=old_value "O:(?P.*?)G:" | rex field=new_value + "O:(?P.*?)G:" | where old_owner!=new_owner ``` optional SID resolution + lookups | lookup identity_lookup_expanded objectSid as new_owner OUTPUT downLevelDomainName + as new_owner_user | lookup admon_groups_def objectSid as new_owner OUTPUT cn as + new_owner_group | lookup identity_lookup_expanded objectSid as old_owner OUTPUT + downLevelDomainName as old_owner_user | lookup admon_groups_def objectSid as old_owner + OUTPUT cn as old_owner_group ``` | lookup builtin_groups_lookup builtin_group_string as + new_owner_group OUTPUT builtin_group_name as new_owner_group_builtin_group | lookup + builtin_groups_lookup builtin_group_string as old_owner OUTPUT builtin_group_name + as old_owner_group_builtin_group | eval user=coalesce(new_owner_user, new_owner_group, + new_owner_group_builtin_group, new_owner), previousOwner=coalesce(old_owner_user, + old_owner_group, old_owner_group_builtin_group, old_owner) | stats values(previousOwner) + as previousOwner values(user) as user values(SubjectLogonId) as SubjectLogonId by + _time ObjectClass ObjectDN src_user OpCorrelationID DSName | `windows_ad_object_owner_updated_filter`' +how_to_implement: Ensure you are ingesting Active Directory audit logs - specifically + event 5136. See lantern article in references for further on how to onboard AD audit + data. Ensure the wineventlog_security macro is configured with the correct indexes + and include lookups for SID resolution if evt_resolve_ad_obj is set to 0. known_false_positives: Unknown references: - https://learn.microsoft.com/en-us/windows/win32/secauthz/ace-strings @@ -22,7 +43,12 @@ drilldown_searches: earliest_offset: $info_min_time$ latest_offset: $info_max_time$ - name: View risk events for the last 7 days for - "$user$" and "$src_user$" - search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$", "$src_user$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' + search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$", + "$src_user$") starthoursago=168 | stats count min(_time) as firstTime max(_time) + as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk + Message" values(analyticstories) as "Analytic Stories" values(annotations._all) + as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" + by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' earliest_offset: $info_min_time$ latest_offset: $info_max_time$ rba: @@ -30,10 +56,10 @@ rba: risk_objects: - field: user type: User - risk_score: 100.0 + risk_score: 100 - field: src_user type: User - risk_score: 100.0 + risk_score: 100 threat_objects: [] tags: analytic_story: @@ -65,6 +91,7 @@ tags: tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1222.001/dacl_abuse/owner_updated_windows-security-xml.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1222.001/dacl_abuse/owner_updated_windows-security-xml.log source: XmlWinEventLog:Security sourcetype: XmlWinEventLog diff --git a/detections/application/windows_ad_privileged_group_modification.yml b/detections/application/windows_ad_privileged_group_modification.yml index 0b62fac218..ec710dba8b 100644 --- a/detections/application/windows_ad_privileged_group_modification.yml +++ b/detections/application/windows_ad_privileged_group_modification.yml @@ -8,8 +8,15 @@ type: TTP data_source: - Windows Event Log Security 4728 description: Detect users added to privileged AD Groups. -search: '`wineventlog_security` EventCode IN (4728) | stats min(_time) as _time dc(user) as usercount, values(user) as user values(user_category) as user_category values(src_user_category) as src_user_category values(dvc) as dvc by signature, Group_Name,src_user | lookup admon_groups_def cn as Group_Name OUTPUT category | where category="privileged" | `windows_ad_privileged_group_modification_filter`' -how_to_implement: This analytic requires eventCode 4728 to be ingested along with the admon_groups_def lookup being configured to include a list of AD groups along with a category to identify privileged groups. See splunkbase app listed in the references for further details. +search: '`wineventlog_security` EventCode IN (4728) | stats min(_time) as _time dc(user) + as usercount, values(user) as user values(user_category) as user_category values(src_user_category) + as src_user_category values(dvc) as dvc by signature, Group_Name,src_user | lookup + admon_groups_def cn as Group_Name OUTPUT category | where category="privileged" + | `windows_ad_privileged_group_modification_filter`' +how_to_implement: This analytic requires eventCode 4728 to be ingested along with + the admon_groups_def lookup being configured to include a list of AD groups along + with a category to identify privileged groups. See splunkbase app listed in the + references for further details. known_false_positives: None references: - https://splunkbase.splunk.com/app/6853 @@ -18,7 +25,7 @@ rba: risk_objects: - field: user type: User - risk_score: 50.0 + risk_score: 50 threat_objects: [] tags: analytic_story: @@ -42,10 +49,12 @@ tags: - Group_Name - dest security_domain: identity - manual_test: This search uses a lookup provided by Enterprise Security and needs to be manually tested. + manual_test: This search uses a lookup provided by Enterprise Security and needs + to be manually tested. tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1098/account_manipulation/xml-windows-security.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1098/account_manipulation/xml-windows-security.log source: XmlWinEventLog:Security sourcetype: XmlWinEventLog diff --git a/detections/application/windows_ad_self_dacl_assignment.yml b/detections/application/windows_ad_self_dacl_assignment.yml index 4c85346d1f..54b0fa125a 100644 --- a/detections/application/windows_ad_self_dacl_assignment.yml +++ b/detections/application/windows_ad_self_dacl_assignment.yml @@ -8,8 +8,39 @@ type: TTP data_source: - Windows Security 5136 description: Detect when a user creates a new DACL in AD for their own AD object. -search: '`wineventlog_security` EventCode=5136 | stats min(_time) as _time values(eval(if(OperationType=="%%14675",AttributeValue,null))) as old_value values(eval(if(OperationType=="%%14674",AttributeValue,null))) as new_value values(OperationType) as OperationType by ObjectClass ObjectDN OpCorrelationID src_user SubjectLogonId | rex field=old_value max_match=10000 "\((?P.*?)\)" | rex field=new_value max_match=10000 "\((?P.*?)\)" | mvexpand new_ace | where NOT new_ace IN (old_values) | rex field=new_ace "(?P.*?);(?P.*?);(?P.*?);(?P.*?);(?P.*?);(?P.*?)$" | rex max_match=100 field=aceAccessRights "(?P[A-Z]{2})" | rex max_match=100 field=aceFlags "(?P[A-Z]{2})" | lookup ace_type_lookup ace_type_string as aceType OUTPUT ace_type_value as aceType | lookup ace_flag_lookup flag_string as aceFlags OUTPUT flag_value as ace_flag_value | lookup ace_access_rights_lookup access_rights_string as AccessRights OUTPUT access_rights_value | lookup msad_guid_lookup guid as aceObjectGuid OUTPUT displayName as ControlAccessRights ``` Optional SID resolution lookups | lookup identity_lookup_expanded objectSid as aceSid OUTPUT downLevelDomainName as user | lookup admon_groups_def objectSid as aceSid OUTPUT cn as group ``` | lookup builtin_groups_lookup builtin_group_string as aceSid OUTPUT builtin_group_name as builtin_group | eval aceType=coalesce(ace_type_value,aceType), aceInheritance=coalesce(ace_flag_value,"This object only"), aceAccessRights=if(aceAccessRights="CCDCLCSWRPWPDTLOCRSDRCWDWO","Full control",coalesce(access_rights_value,AccessRights)), aceControlAccessRights=if((ControlAccessRights="Write member" OR aceObjectGuid="bf9679c0-0de6-11d0-a285-00aa003049e2") AND (aceAccessRights="All validated writes" OR AccessRights="SW"),"Add/remove self as member",coalesce(ControlAccessRights,aceObjectGuid)), user=coalesce(user, group, builtin_group, aceSid) | stats values(aceType) as aceType values(aceInheritance) as aceInheritance values(aceControlAccessRights) as aceControlAccessRights values(aceAccessRights) as aceAccessRights values(new_ace) as new_ace values(aceInheritedTypeGuid) as aceInheritedTypeGuid by _time ObjectClass ObjectDN src_user SubjectLogonId user OpCorrelationID | eval aceControlAccessRights=if(mvcount(aceControlAccessRights)=1 AND aceControlAccessRights="","All rights",''aceControlAccessRights'') | rex field=user "\\\(?P.*?)$" | where lower(src_user)=lower(nt_user) | `windows_ad_self_dacl_assignment_filter`' -how_to_implement: Ensure you are ingesting Active Directory audit logs - specifically event 5136. See lantern article in references for further on how to onboard AD audit data. Ensure the wineventlog_security macro is configured with the correct indexes and include lookups for SID resolution if evt_resolve_ad_obj is set to 0. +search: "`wineventlog_security` EventCode=5136 | stats min(_time) as _time values(eval(if(OperationType==\"\ + %%14675\",AttributeValue,null))) as old_value values(eval(if(OperationType==\"%%14674\"\ + ,AttributeValue,null))) as new_value values(OperationType) as OperationType by ObjectClass + ObjectDN OpCorrelationID src_user SubjectLogonId | rex field=old_value max_match=10000 + \"\\((?P.*?)\\)\" | rex field=new_value max_match=10000 \"\\((?P.*?)\\\ + )\" | mvexpand new_ace | where NOT new_ace IN (old_values) | rex field=new_ace + \"(?P.*?);(?P.*?);(?P.*?);(?P.*?);(?P.*?);(?P.*?)$\"\ + \ | rex max_match=100 field=aceAccessRights \"(?P[A-Z]{2})\" | rex + max_match=100 field=aceFlags \"(?P[A-Z]{2})\" | lookup ace_type_lookup + ace_type_string as aceType OUTPUT ace_type_value as aceType | lookup ace_flag_lookup + flag_string as aceFlags OUTPUT flag_value as ace_flag_value | lookup ace_access_rights_lookup + access_rights_string as AccessRights OUTPUT access_rights_value | lookup msad_guid_lookup + guid as aceObjectGuid OUTPUT displayName as ControlAccessRights ``` Optional SID + resolution lookups | lookup identity_lookup_expanded objectSid as aceSid OUTPUT + downLevelDomainName as user | lookup admon_groups_def objectSid as aceSid OUTPUT + cn as group ``` | lookup builtin_groups_lookup builtin_group_string as aceSid OUTPUT + builtin_group_name as builtin_group | eval aceType=coalesce(ace_type_value,aceType), + aceInheritance=coalesce(ace_flag_value,\"This object only\"), aceAccessRights=if(aceAccessRights=\"\ + CCDCLCSWRPWPDTLOCRSDRCWDWO\",\"Full control\",coalesce(access_rights_value,AccessRights)), + aceControlAccessRights=if((ControlAccessRights=\"Write member\" OR aceObjectGuid=\"\ + bf9679c0-0de6-11d0-a285-00aa003049e2\") AND (aceAccessRights=\"All validated writes\"\ + \ OR AccessRights=\"SW\"),\"Add/remove self as member\",coalesce(ControlAccessRights,aceObjectGuid)), + user=coalesce(user, group, builtin_group, aceSid) | stats values(aceType) as aceType + values(aceInheritance) as aceInheritance values(aceControlAccessRights) as aceControlAccessRights + values(aceAccessRights) as aceAccessRights values(new_ace) as new_ace values(aceInheritedTypeGuid) + as aceInheritedTypeGuid by _time ObjectClass ObjectDN src_user SubjectLogonId user + OpCorrelationID | eval aceControlAccessRights=if(mvcount(aceControlAccessRights)=1 + AND aceControlAccessRights=\"\",\"All rights\",'aceControlAccessRights') | rex field=user + \"\\\\\\(?P.*?)$\" | where lower(src_user)=lower(nt_user) | `windows_ad_self_dacl_assignment_filter`" +how_to_implement: Ensure you are ingesting Active Directory audit logs - specifically + event 5136. See lantern article in references for further on how to onboard AD audit + data. Ensure the wineventlog_security macro is configured with the correct indexes + and include lookups for SID resolution if evt_resolve_ad_obj is set to 0. known_false_positives: Unknown references: - https://lantern.splunk.com/Security/Product_Tips/Enterprise_Security/Enabling_an_audit_trail_from_Active_Directory @@ -19,7 +50,12 @@ drilldown_searches: earliest_offset: $info_min_time$ latest_offset: $info_max_time$ - name: View risk events for the last 7 days for - "$src_user$" - search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$src_user$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' + search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$src_user$") + starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime + values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) + as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) + as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` + | `security_content_ctime(lastTime)`' earliest_offset: $info_min_time$ latest_offset: $info_max_time$ rba: @@ -28,7 +64,7 @@ rba: risk_objects: - field: src_user type: User - risk_score: 80.0 + risk_score: 80 threat_objects: [] tags: analytic_story: @@ -59,6 +95,7 @@ tags: tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1484/aclmodification/windows-security-xml.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1484/aclmodification/windows-security-xml.log source: XmlWinEventLog:Security sourcetype: XmlWinEventLog diff --git a/detections/application/windows_ad_suspicious_attribute_modification.yml b/detections/application/windows_ad_suspicious_attribute_modification.yml index cfac3afdf4..0367580f98 100644 --- a/detections/application/windows_ad_suspicious_attribute_modification.yml +++ b/detections/application/windows_ad_suspicious_attribute_modification.yml @@ -7,10 +7,28 @@ status: production type: TTP data_source: - Windows Security 5136 -description: 'This detection monitors changes to the following Active Directory attributes: "msDS-AllowedToDelegateTo", "msDS-AllowedToActOnBehalfOfOtherIdentity", "msDS-KeyCredentialLink", "scriptPath", and "msTSInitialProgram". Modifications to these attributes can indicate potential malicious activity or privilege escalation attempts. Immediate investigation is recommended upon alert.' -search: '`wineventlog_security` EventCode=5136 AttributeLDAPDisplayName IN ("msDS-AllowedToDelegateTo","msDS-AllowedToActOnBehalfOfOtherIdentity","scriptPath","msTSInitialProgram") OperationType=%%14674 ```Changes to the attribute "msDS-KeyCredentialLink" are also worth moniroting, however tuning will need to be applied``` | table _time ObjectClass ObjectDN OpCorrelationID src_user SubjectLogonId DSName AttributeValue AttributeLDAPDisplayName | rename SubjectLogonId as TargetLogonId, src_user as initiator, _time as eventTime | appendpipe [| map search="search `wineventlog_security` EventCode=4624 TargetLogonId=$TargetLogonId$"] | stats min(eventTime) as _time values(initiator) as src_user, values(DSName) as targetDomain, values(ObjectDN) as ObjectDN, values(ObjectClass) as ObjectClass, values(src_category) as src_category, values(src_ip) as src_ip values(LogonType) as LogonType values(AttributeValue) as AttributeValue values(AttributeLDAPDisplayName) as AttributeLDAPDisplayName by TargetLogonId | rex field=ObjectDN "^CN=(?P.*?),[A-Z]{2}\=" | eval dest=if(ObjectClass="computer",cn,null), user=if(ObjectClass="user",cn,null) | fields - cn | `windows_ad_suspicious_attribute_modification_filter`' -how_to_implement: Ensure you are ingesting Active Directory audit logs - specifically event 5136. See lantern article in references for further on how to onboard AD audit data. Ensure the wineventlog_security macro is configured with the correct indexes. -known_false_positives: If key credentials are regularly assigned to users, these events will need to be tuned out. +description: 'This detection monitors changes to the following Active Directory attributes: + "msDS-AllowedToDelegateTo", "msDS-AllowedToActOnBehalfOfOtherIdentity", "msDS-KeyCredentialLink", + "scriptPath", and "msTSInitialProgram". Modifications to these attributes can indicate + potential malicious activity or privilege escalation attempts. Immediate investigation + is recommended upon alert.' +search: '`wineventlog_security` EventCode=5136 AttributeLDAPDisplayName IN ("msDS-AllowedToDelegateTo","msDS-AllowedToActOnBehalfOfOtherIdentity","scriptPath","msTSInitialProgram") + OperationType=%%14674 ```Changes to the attribute "msDS-KeyCredentialLink" are + also worth moniroting, however tuning will need to be applied``` | table _time ObjectClass + ObjectDN OpCorrelationID src_user SubjectLogonId DSName AttributeValue AttributeLDAPDisplayName | + rename SubjectLogonId as TargetLogonId, src_user as initiator, _time as eventTime | + appendpipe [| map search="search `wineventlog_security` EventCode=4624 TargetLogonId=$TargetLogonId$"] | + stats min(eventTime) as _time values(initiator) as src_user, values(DSName) as targetDomain, + values(ObjectDN) as ObjectDN, values(ObjectClass) as ObjectClass, values(src_category) + as src_category, values(src_ip) as src_ip values(LogonType) as LogonType values(AttributeValue) + as AttributeValue values(AttributeLDAPDisplayName) as AttributeLDAPDisplayName by + TargetLogonId | rex field=ObjectDN "^CN=(?P.*?),[A-Z]{2}\=" | eval dest=if(ObjectClass="computer",cn,null), + user=if(ObjectClass="user",cn,null) | fields - cn | `windows_ad_suspicious_attribute_modification_filter`' +how_to_implement: Ensure you are ingesting Active Directory audit logs - specifically + event 5136. See lantern article in references for further on how to onboard AD audit + data. Ensure the wineventlog_security macro is configured with the correct indexes. +known_false_positives: If key credentials are regularly assigned to users, these events + will need to be tuned out. references: - https://trustedsec.com/blog/a-hitchhackers-guide-to-dacl-based-detections-part-1-a - https://lantern.splunk.com/Security/Product_Tips/Enterprise_Security/Enabling_an_audit_trail_from_Active_Directory @@ -20,7 +38,12 @@ drilldown_searches: earliest_offset: $info_min_time$ latest_offset: $info_max_time$ - name: View risk events for the last 7 days for - "$src_user$" and "$dest$" - search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$src_user$", "$dest$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' + search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$src_user$", + "$dest$") starthoursago=168 | stats count min(_time) as firstTime max(_time) + as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk + Message" values(analyticstories) as "Analytic Stories" values(annotations._all) + as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" + by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' earliest_offset: $info_min_time$ latest_offset: $info_max_time$ rba: @@ -29,10 +52,10 @@ rba: risk_objects: - field: src_user type: User - risk_score: 100.0 + risk_score: 100 - field: dest type: Hostname - risk_score: 100.0 + risk_score: 100 threat_objects: [] tags: analytic_story: @@ -64,6 +87,7 @@ tags: tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1222.001/dacl_abuse/suspicious_acl_modification-windows-security-xml.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1222.001/dacl_abuse/suspicious_acl_modification-windows-security-xml.log source: XmlWinEventLog:Security sourcetype: XmlWinEventLog diff --git a/detections/application/windows_ad_suspicious_gpo_modification.yml b/detections/application/windows_ad_suspicious_gpo_modification.yml index c244e89924..6d19c89bb0 100644 --- a/detections/application/windows_ad_suspicious_gpo_modification.yml +++ b/detections/application/windows_ad_suspicious_gpo_modification.yml @@ -8,10 +8,45 @@ type: TTP data_source: - Windows Security 5136 - Windows Security 5145 -description: This analytic looks for a the creation of potentially harmful GPO which could lead to persistence or code execution on remote hosts. Note, this analyic is looking for the absence of the corresponding 5136 events which is evidence of the GPOs being manually edited (using a tool like PowerView) or potentially missing logs. -search: "`wineventlog_security` EventCode=5145 ShareName=\"\\\\\\\\*\\\\SYSVOL\" RelativeTargetName IN (*\\\\ScheduledTasks.xml, *\\\\Groups.xml, *\\\\Registry.xml, *\\\\Services.xml, *\\\\Scripts\\\\*) NOT RelativeTargetName=*\\\\Scripts\\\\scripts.ini AccessMask=0x2 | rex field=AccessList max_match=0 \"(?P%%\\d+)\" | table _time AccessMask src_ip src_user RelativeTargetName Logon_ID dvc | rex field=RelativeTargetName \"Policies\\\\\\(?P{.*?})\\\\\\(?P\\w+?)\\\\\\(\\w+)\\\\\\(?P\\w+)\\\\\\(?P\\w+\\.\\w+)$\" | eval src=if(match(src_ip, \"(?i)^fe80:\"),dvc,src_ip), folder=case(RelativeTargetName like \"%\\\\Scripts\\\\%\",\"Scripts\",folder=\"Groups\",\"Local users and groups\",1=1,folder) | appendpipe \n [| map search=\"search `wineventlog_security` EventCode=5136 ObjectClass=groupPolicyContainer AttributeLDAPDisplayName=gPCMachineExtensionNames $gpo_guid$\" \n | stats min(_time) as _time values(eval(if(OperationType==\"%%14675\",AttributeValue,null))) as old_value values(eval(if(OperationType==\"%%14674\",AttributeValue,null))) as new_value values(OperationType) as OperationType by ObjectClass ObjectDN OpCorrelationID src_user SubjectLogonId \n | rex field=old_value max_match=10000 \"(?P\\{.*?\\})\" \n | rex field=new_value max_match=10000 \"(?P\\{.*?\\})\" \n | rex field=ObjectDN max_match=10000 \"CN=(?P\\{.*?\\})\" \n | mvexpand new_values \n | where NOT new_values IN (old_values,\"{00000000-0000-0000-0000-000000000000}\",policy_guid) AND match(new_values, \"^\\{[A-Z|\\d]+\\-[A-Z|\\d]+\\-[A-Z|\\d]+\\-[A-Z|\\d]+\\-[A-Z|\\d]+\\}\") \n | lookup msad_guid_lookup guid as new_values OUTPUTNEW displayName as policyType \n | eval newPolicy=if(policyType like \"%\",policyType,new_values) \n | stats values(OpCorrelationID) as OpCorrelationID values(newPolicy) as newPolicy by ObjectDN \n | rex field=ObjectDN max_match=10000 \"CN=(?P\\{.*?\\})\" \n | fields - ObjectDN] \n| stats values(AccessMask) as AccessMask values(src) as src values(src_user) as src_user values(RelativeTargetName) as RelativeTargetName values(Logon_ID) as Logon_ID values(newPolicy) as newPolicy values(OpCorrelationID) as OpCorrelationID values(folder) as folder values(file) as file by gpo_guid | mvexpand folder | where NOT folder IN (newPolicy) | `windows_ad_suspicious_gpo_modification_filter`" -how_to_implement: Ingest EventCodes 5145 and 5136 from domain controllers. Additional SACLs required to capture EventCode 5136, see references for further information on how to configure this. The Group Policy - Audit Detailed File Share will need to be enabled on the DCs to generate event code 5145, this event is very noisy on DCs, consider tuning out sysvol events which do not match access mask 0x2. -known_false_positives: When a GPO is manually edited and 5136 events are not logging to Splunk. +description: This analytic looks for a the creation of potentially harmful GPO which + could lead to persistence or code execution on remote hosts. Note, this analyic + is looking for the absence of the corresponding 5136 events which is evidence of + the GPOs being manually edited (using a tool like PowerView) or potentially missing + logs. +search: "`wineventlog_security` EventCode=5145 ShareName=\"\\\\\\\\*\\\\SYSVOL\" RelativeTargetName + IN (*\\\\ScheduledTasks.xml, *\\\\Groups.xml, *\\\\Registry.xml, *\\\\Services.xml, + *\\\\Scripts\\\\*) NOT RelativeTargetName=*\\\\Scripts\\\\scripts.ini AccessMask=0x2\ + \ | rex field=AccessList max_match=0 \"(?P%%\\d+)\" | table _time + AccessMask src_ip src_user RelativeTargetName Logon_ID dvc | rex field=RelativeTargetName + \"Policies\\\\\\(?P{.*?})\\\\\\(?P\\w+?)\\\\\\(\\w+)\\\\\\(?P\\\ + w+)\\\\\\(?P\\w+\\.\\w+)$\" | eval src=if(match(src_ip, \"(?i)^fe80:\"),dvc,src_ip), + folder=case(RelativeTargetName like \"%\\\\Scripts\\\\%\",\"Scripts\",folder=\"\ + Groups\",\"Local users and groups\",1=1,folder) | appendpipe \n [| map search=\"\ + search `wineventlog_security` EventCode=5136 ObjectClass=groupPolicyContainer AttributeLDAPDisplayName=gPCMachineExtensionNames + $gpo_guid$\" \n | stats min(_time) as _time values(eval(if(OperationType==\"%%14675\"\ + ,AttributeValue,null))) as old_value values(eval(if(OperationType==\"%%14674\",AttributeValue,null))) + as new_value values(OperationType) as OperationType by ObjectClass ObjectDN OpCorrelationID + src_user SubjectLogonId \n | rex field=old_value max_match=10000 \"(?P\\\ + {.*?\\})\" \n | rex field=new_value max_match=10000 \"(?P\\{.*?\\})\"\ + \ \n | rex field=ObjectDN max_match=10000 \"CN=(?P\\{.*?\\})\" \n\ + \ | mvexpand new_values \n | where NOT new_values IN (old_values,\"{00000000-0000-0000-0000-000000000000}\"\ + ,policy_guid) AND match(new_values, \"^\\{[A-Z|\\d]+\\-[A-Z|\\d]+\\-[A-Z|\\d]+\\\ + -[A-Z|\\d]+\\-[A-Z|\\d]+\\}\") \n | lookup msad_guid_lookup guid as new_values + OUTPUTNEW displayName as policyType \n | eval newPolicy=if(policyType like \"%\"\ + ,policyType,new_values) \n | stats values(OpCorrelationID) as OpCorrelationID values(newPolicy) + as newPolicy by ObjectDN \n | rex field=ObjectDN max_match=10000 \"CN=(?P\\\ + {.*?\\})\" \n | fields - ObjectDN] \n| stats values(AccessMask) as AccessMask values(src) + as src values(src_user) as src_user values(RelativeTargetName) as RelativeTargetName + values(Logon_ID) as Logon_ID values(newPolicy) as newPolicy values(OpCorrelationID) + as OpCorrelationID values(folder) as folder values(file) as file by gpo_guid | + mvexpand folder | where NOT folder IN (newPolicy) | `windows_ad_suspicious_gpo_modification_filter`" +how_to_implement: Ingest EventCodes 5145 and 5136 from domain controllers. Additional + SACLs required to capture EventCode 5136, see references for further information + on how to configure this. The Group Policy - Audit Detailed File Share will need + to be enabled on the DCs to generate event code 5145, this event is very noisy on + DCs, consider tuning out sysvol events which do not match access mask 0x2. +known_false_positives: When a GPO is manually edited and 5136 events are not logging + to Splunk. references: - https://github.com/PowerShellMafia/PowerSploit/blob/26a0757612e5654b4f792b012ab8f10f95d391c9/Recon/PowerView.ps1#L5907-L6122 - https://github.com/X-C3LL/GPOwned @@ -25,10 +60,10 @@ rba: risk_objects: - field: user type: User - risk_score: 80.0 + risk_score: 80 - field: src_user type: User - risk_score: 80.0 + risk_score: 80 threat_objects: [] tags: analytic_story: @@ -61,6 +96,7 @@ tags: tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1484.001/gpo_new_cse/windows-security.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1484.001/gpo_new_cse/windows-security.log source: XmlWinEventLog:Security sourcetype: XmlWinEventLog diff --git a/detections/application/windows_increase_in_group_or_object_modification_activity.yml b/detections/application/windows_increase_in_group_or_object_modification_activity.yml index 2e8ff08473..90ce747fdf 100644 --- a/detections/application/windows_increase_in_group_or_object_modification_activity.yml +++ b/detections/application/windows_increase_in_group_or_object_modification_activity.yml @@ -7,8 +7,18 @@ status: production type: TTP data_source: - Windows Event Log Security 4663 -description: This analytic detects an increase in modifications to AD groups or objects. Frequent changes to AD groups or objects can indicate potential security risks, such as unauthorized access attempts, impairing defences or establishing persistence. By monitoring AD logs for unusual modification patterns, this detection helps identify suspicious behavior that could compromise the integrity and security of the AD environment. -search: '`wineventlog_security` EventCode IN (4670,4727,4731,4734,4735,4764) | bucket span=5m _time | stats values(object) as object, dc(object) as objectCount, values(src_user_category) as src_user_category, values(dest) as dest, values(dest_category) as dest_category by _time, src_user, signature, status | eventstats avg(objectCount) as comp_avg, stdev(objectCount) as comp_std by src_user, signature | eval upperBound=(comp_avg+comp_std) | eval isOutlier=if(objectCount > 10 and (objectCount >= upperBound), 1, 0) | search isOutlier=1 | `windows_increase_in_group_or_object_modification_activity_filter`' +description: This analytic detects an increase in modifications to AD groups or objects. + Frequent changes to AD groups or objects can indicate potential security risks, + such as unauthorized access attempts, impairing defences or establishing persistence. + By monitoring AD logs for unusual modification patterns, this detection helps identify + suspicious behavior that could compromise the integrity and security of the AD environment. +search: '`wineventlog_security` EventCode IN (4670,4727,4731,4734,4735,4764) | bucket + span=5m _time | stats values(object) as object, dc(object) as objectCount, values(src_user_category) + as src_user_category, values(dest) as dest, values(dest_category) as dest_category + by _time, src_user, signature, status | eventstats avg(objectCount) as comp_avg, + stdev(objectCount) as comp_std by src_user, signature | eval upperBound=(comp_avg+comp_std) + | eval isOutlier=if(objectCount > 10 and (objectCount >= upperBound), 1, 0) | search + isOutlier=1 | `windows_increase_in_group_or_object_modification_activity_filter`' how_to_implement: Run this detection looking over a 7 day timeframe for best results. known_false_positives: Unknown references: [] @@ -18,7 +28,12 @@ drilldown_searches: earliest_offset: $info_min_time$ latest_offset: $info_max_time$ - name: View risk events for the last 7 days for - "$src_user$" - search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$src_user$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' + search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$src_user$") + starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime + values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) + as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) + as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` + | `security_content_ctime(lastTime)`' earliest_offset: $info_min_time$ latest_offset: $info_max_time$ rba: @@ -26,7 +41,7 @@ rba: risk_objects: - field: src_user type: User - risk_score: 8.0 + risk_score: 8 threat_objects: [] tags: analytic_story: @@ -50,6 +65,7 @@ tags: tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1098/account_manipulation/xml-windows-security.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1098/account_manipulation/xml-windows-security.log source: XmlWinEventLog:Security sourcetype: XmlWinEventLog diff --git a/detections/application/windows_increase_in_user_modification_activity.yml b/detections/application/windows_increase_in_user_modification_activity.yml index ff56ac0edd..b070c5ccca 100644 --- a/detections/application/windows_increase_in_user_modification_activity.yml +++ b/detections/application/windows_increase_in_user_modification_activity.yml @@ -7,8 +7,22 @@ status: production type: TTP data_source: - Windows Event Log Security 4720 -description: This analytic detects an increase in modifications to AD user objects. A large volume of changes to user objects can indicate potential security risks, such as unauthorized access attempts, impairing defences or establishing persistence. By monitoring AD logs for unusual modification patterns, this detection helps identify suspicious behavior that could compromise the integrity and security of the AD environment. -search: '`wineventlog_security` EventCode IN (4720,4722,4723,4724,4725,4726,4728,4732,4733,4738,4743,4780) | bucket span=5m _time | stats values(TargetDomainName) as TargetDomainName, values(user) as user, dc(user) as userCount, values(user_category) as user_category, values(src_user_category) as src_user_category, values(dest) as dest, values(dest_category) as dest_category by _time, src_user, signature, status | eventstats avg(userCount) as comp_avg , stdev(userCount) as comp_std by src_user, signature | eval upperBound=(comp_avg+comp_std*3) | eval isOutlier=if(userCount > 10 and userCount >= upperBound, 1, 0) | search isOutlier=1 | stats values(TargetDomainName) as TargetDomainName, values(user) as user, dc(user) as userCount, values(user_category) as user_category, values(src_user_category) as src_user_category, values(dest) as dest, values(dest_category) as dest_category values(signature) as signature by _time, src_user, status | `windows_increase_in_user_modification_activity_filter`' +description: This analytic detects an increase in modifications to AD user objects. + A large volume of changes to user objects can indicate potential security risks, + such as unauthorized access attempts, impairing defences or establishing persistence. + By monitoring AD logs for unusual modification patterns, this detection helps identify + suspicious behavior that could compromise the integrity and security of the AD environment. +search: '`wineventlog_security` EventCode IN (4720,4722,4723,4724,4725,4726,4728,4732,4733,4738,4743,4780) + | bucket span=5m _time | stats values(TargetDomainName) as TargetDomainName, values(user) + as user, dc(user) as userCount, values(user_category) as user_category, values(src_user_category) + as src_user_category, values(dest) as dest, values(dest_category) as dest_category + by _time, src_user, signature, status | eventstats avg(userCount) as comp_avg , + stdev(userCount) as comp_std by src_user, signature | eval upperBound=(comp_avg+comp_std*3) + | eval isOutlier=if(userCount > 10 and userCount >= upperBound, 1, 0) | search + isOutlier=1 | stats values(TargetDomainName) as TargetDomainName, values(user) as + user, dc(user) as userCount, values(user_category) as user_category, values(src_user_category) + as src_user_category, values(dest) as dest, values(dest_category) as dest_category + values(signature) as signature by _time, src_user, status | `windows_increase_in_user_modification_activity_filter`' how_to_implement: Run this detection looking over a 7 day timeframe for best results. known_false_positives: Genuine activity references: [] @@ -18,7 +32,12 @@ drilldown_searches: earliest_offset: $info_min_time$ latest_offset: $info_max_time$ - name: View risk events for the last 7 days for - "$src_user$" - search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$src_user$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' + search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$src_user$") + starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime + values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) + as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) + as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` + | `security_content_ctime(lastTime)`' earliest_offset: $info_min_time$ latest_offset: $info_max_time$ rba: @@ -26,7 +45,7 @@ rba: risk_objects: - field: src_user type: User - risk_score: 8.0 + risk_score: 8 threat_objects: [] tags: analytic_story: @@ -50,6 +69,7 @@ tags: tests: - name: True Positive Test attack_data: - - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1098/account_manipulation/xml-windows-security.log + - data: + https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1098/account_manipulation/xml-windows-security.log source: XmlWinEventLog:Security sourcetype: XmlWinEventLog