diff --git a/bin/doc_gen.py b/bin/doc_gen.py
index 36e22b69d4..0e5ba698ec 100644
--- a/bin/doc_gen.py
+++ b/bin/doc_gen.py
@@ -348,6 +348,12 @@ def generate_doc_detections(REPO_PATH, OUTPUT_DIR, TEMPLATE_PATH, attack, messag
detection_lookups.append(lookup)
detection_yaml['lookups'] = detection_lookups
detection_yaml['lookups'] = detection_lookups
+
+ # sort macros and lookups
+ sorted_macros = sorted(detection_yaml['macros'], key=lambda i: i['name'])
+ detection_yaml['macros'] = sorted_macros
+ sorted_lookups = sorted(detection_yaml['lookups'], key=lambda i: i['name'])
+ detection_yaml['lookups'] = sorted_lookups
# sort macros and lookups
sorted_macros = sorted(detection_yaml['macros'], key=lambda i: i['name'])
diff --git a/bin/jinja2_templates/doc_index.j2 b/bin/jinja2_templates/doc_index.j2
index 68c229da49..d9e8235d66 100644
--- a/bin/jinja2_templates/doc_index.j2
+++ b/bin/jinja2_templates/doc_index.j2
@@ -28,7 +28,7 @@ feature_row:
- image_path: /static/feature_playbooks.png
alt: "100% free"
title: "Playbooks"
- excerpt: "See all **{{ playbook_count }}** sets of steps 🐾 to automatically response to a threat."
+ excerpt: "See all **{{ playbook_count }}** automated investigation 🔭 and response 🛠 playbooks "
url: "/playbooks"
btn_class: "btn--primary"
btn_label: "Explore"
@@ -39,7 +39,7 @@ feature_row:
# Welcome to Splunk Security Content
-This project gives you access to our repository of Analytic Stories that are security guides which provide background on TTPs, mapped to the MITRE framework, the Lockheed Martin Kill Chain, and CIS controls. They include Splunk searches, machine-learning algorithms, and Splunk Phantom playbooks (where available)—all designed to work together to detect, investigate, and respond to threats.
+This project gives you access to our repository of Analytic Stories that are security guides which provide background on TTPs, mapped to the MITRE framework, the Lockheed Martin Kill Chain, and CIS controls. They include Splunk searches, machine-learning algorithms, and Splunk SOAR playbooks (where available)—all designed to work together to detect, investigate, and respond to threats.
[Try in Splunk Security Cloud](https://www.splunk.com/en_us/cyber-security.html){: .btn .btn--success}
diff --git a/bin/jinja2_templates/doc_playbooks.j2 b/bin/jinja2_templates/doc_playbooks.j2
index c3c51f6958..f8e310aacd 100644
--- a/bin/jinja2_templates/doc_playbooks.j2
+++ b/bin/jinja2_templates/doc_playbooks.j2
@@ -11,6 +11,9 @@ tags:
{%- for app in playbook.app_list %}
- {{ app }}
{%- endfor %}
+{%- for tag in playbook.tags.platform_tags %}
+ - {{ tag }}
+{%- endfor %}
---
[Try in Splunk SOAR](https://www.splunk.com/en_us/software/splunk-security-orchestration-and-automation.html){: .btn .btn--success}
diff --git a/docs/_pages/adversary_tactics.md b/docs/_pages/adversary_tactics.md
index 229a2b585a..6a9d33dae5 100644
--- a/docs/_pages/adversary_tactics.md
+++ b/docs/_pages/adversary_tactics.md
@@ -29,7 +29,7 @@ sidebar:
| [HAFNIUM Group](/stories/hafnium_group/) | [Server Software Component](/tags/#server-software-component), [Web Shell](/tags/#web-shell) | [Persistence](/tags/#persistence) |
| [Ingress Tool Transfer](/stories/ingress_tool_transfer/) | [Ingress Tool Transfer](/tags/#ingress-tool-transfer) | [Command And Control](/tags/#command-and-control) |
| [Linux Persistence Techniques](/stories/linux_persistence_techniques/) | [Sudo and Sudo Caching](/tags/#sudo-and-sudo-caching), [Abuse Elevation Control Mechanism](/tags/#abuse-elevation-control-mechanism) | [Privilege Escalation](/tags/#privilege-escalation) |
-| [Linux Privilege Escalation](/stories/linux_privilege_escalation/) | [Sudo and Sudo Caching](/tags/#sudo-and-sudo-caching), [Abuse Elevation Control Mechanism](/tags/#abuse-elevation-control-mechanism) | [Privilege Escalation](/tags/#privilege-escalation) |
+| [Linux Privilege Escalation](/stories/linux_privilege_escalation/) | [Exploitation for Privilege Escalation](/tags/#exploitation-for-privilege-escalation) | [Privilege Escalation](/tags/#privilege-escalation) |
| [Log4Shell CVE-2021-44228](/stories/log4shell_cve-2021-44228/) | [Exploit Public-Facing Application](/tags/#exploit-public-facing-application) | [Initial Access](/tags/#initial-access) |
| [Malicious PowerShell](/stories/malicious_powershell/) | [Gather Victim Host Information](/tags/#gather-victim-host-information) | [Reconnaissance](/tags/#reconnaissance) |
| [Masquerading - Rename System Utilities](/stories/masquerading_-_rename_system_utilities/) | [Masquerading](/tags/#masquerading), [Rename System Utilities](/tags/#rename-system-utilities), [Signed Binary Proxy Execution](/tags/#signed-binary-proxy-execution), [InstallUtil](/tags/#installutil) | [Defense Evasion](/tags/#defense-evasion) |
diff --git a/docs/_pages/detections.md b/docs/_pages/detections.md
index 422416bc0b..e8a6284a70 100644
--- a/docs/_pages/detections.md
+++ b/docs/_pages/detections.md
@@ -61,8 +61,8 @@ sidebar:
| [Attempt To Delete Services](/endpoint/attempt_to_delete_services/) | [Service Stop](/tags/#service-stop), [Create or Modify System Process](/tags/#create-or-modify-system-process), [Windows Service](/tags/#windows-service) | TTP |
| [Attempt To Disable Services](/endpoint/attempt_to_disable_services/) | [Service Stop](/tags/#service-stop) | TTP |
| [Attempt To Stop Security Service](/endpoint/attempt_to_stop_security_service/) | [Disable or Modify Tools](/tags/#disable-or-modify-tools), [Impair Defenses](/tags/#impair-defenses) | TTP |
-| [Attempted Credential Dump From Registry via Reg exe](/endpoint/attempted_credential_dump_from_registry_via_reg_exe/) | [OS Credential Dumping](/tags/#os-credential-dumping), [Security Account Manager](/tags/#security-account-manager) | TTP |
| [Attempted Credential Dump From Registry via Reg exe](/endpoint/attempted_credential_dump_from_registry_via_reg_exe/) | [Security Account Manager](/tags/#security-account-manager), [OS Credential Dumping](/tags/#os-credential-dumping) | TTP |
+| [Attempted Credential Dump From Registry via Reg exe](/endpoint/attempted_credential_dump_from_registry_via_reg_exe/) | [OS Credential Dumping](/tags/#os-credential-dumping), [Security Account Manager](/tags/#security-account-manager) | TTP |
| [Auto Admin Logon Registry Entry](/endpoint/auto_admin_logon_registry_entry/) | [Credentials in Registry](/tags/#credentials-in-registry), [Unsecured Credentials](/tags/#unsecured-credentials) | TTP |
| [BCDEdit Failure Recovery Modification](/endpoint/bcdedit_failure_recovery_modification/) | [Inhibit System Recovery](/tags/#inhibit-system-recovery) | TTP |
| [BCDEdit Failure Recovery Modification](/endpoint/bcdedit_failure_recovery_modification/) | [Inhibit System Recovery](/tags/#inhibit-system-recovery) | TTP |
@@ -413,6 +413,7 @@ sidebar:
| [Linux Sudo OR Su Execution](/endpoint/linux_sudo_or_su_execution/) | [Sudo and Sudo Caching](/tags/#sudo-and-sudo-caching), [Abuse Elevation Control Mechanism](/tags/#abuse-elevation-control-mechanism) | Hunting |
| [Linux Sudoers Tmp File Creation](/endpoint/linux_sudoers_tmp_file_creation/) | [Sudo and Sudo Caching](/tags/#sudo-and-sudo-caching), [Abuse Elevation Control Mechanism](/tags/#abuse-elevation-control-mechanism) | Anomaly |
| [Linux Visudo Utility Execution](/endpoint/linux_visudo_utility_execution/) | [Sudo and Sudo Caching](/tags/#sudo-and-sudo-caching), [Abuse Elevation Control Mechanism](/tags/#abuse-elevation-control-mechanism) | Anomaly |
+| [Linux pkexec Privilege Escalation](/endpoint/linux_pkexec_privilege_escalation/) | [Exploitation for Privilege Escalation](/tags/#exploitation-for-privilege-escalation) | TTP |
| [Loading Of Dynwrapx Module](/endpoint/loading_of_dynwrapx_module/) | [Process Injection](/tags/#process-injection), [Dynamic-link Library Injection](/tags/#dynamic-link-library-injection) | TTP |
| [Local Account Discovery With Wmic](/endpoint/local_account_discovery_with_wmic/) | [Account Discovery](/tags/#account-discovery), [Local Account](/tags/#local-account) | Hunting |
| [Local Account Discovery with Net](/endpoint/local_account_discovery_with_net/) | [Account Discovery](/tags/#account-discovery), [Local Account](/tags/#local-account) | Hunting |
@@ -573,7 +574,6 @@ sidebar:
| [Resize Shadowstorage Volume](/endpoint/resize_shadowstorage_volume/) | [Service Stop](/tags/#service-stop) | TTP |
| [Revil Common Exec Parameter](/endpoint/revil_common_exec_parameter/) | [User Execution](/tags/#user-execution) | TTP |
| [Revil Registry Entry](/endpoint/revil_registry_entry/) | [Modify Registry](/tags/#modify-registry) | TTP |
-| [RunDLL Loading DLL By Ordinal](/endpoint/rundll_loading_dll_by_ordinal/) | [Signed Binary Proxy Execution](/tags/#signed-binary-proxy-execution), [Rundll32](/tags/#rundll32) | TTP |
| [Runas Execution in CommandLine](/endpoint/runas_execution_in_commandline/) | [Access Token Manipulation](/tags/#access-token-manipulation), [Token Impersonation/Theft](/tags/#token-impersonation/theft) | Hunting |
| [Rundll32 Control RunDLL Hunt](/endpoint/rundll32_control_rundll_hunt/) | [Signed Binary Proxy Execution](/tags/#signed-binary-proxy-execution), [Rundll32](/tags/#rundll32) | Hunting |
| [Rundll32 Control RunDLL World Writable Directory](/endpoint/rundll32_control_rundll_world_writable_directory/) | [Signed Binary Proxy Execution](/tags/#signed-binary-proxy-execution), [Rundll32](/tags/#rundll32) | TTP |
@@ -652,7 +652,6 @@ sidebar:
| [Suspicious Reg exe Process](/endpoint/suspicious_reg_exe_process/) | [Modify Registry](/tags/#modify-registry) | TTP |
| [Suspicious Regsvr32 Register Suspicious Path](/endpoint/suspicious_regsvr32_register_suspicious_path/) | [Signed Binary Proxy Execution](/tags/#signed-binary-proxy-execution), [Regsvr32](/tags/#regsvr32) | TTP |
| [Suspicious Rundll32 PluginInit](/endpoint/suspicious_rundll32_plugininit/) | [Signed Binary Proxy Execution](/tags/#signed-binary-proxy-execution), [Rundll32](/tags/#rundll32) | TTP |
-| [Suspicious Rundll32 Rename](/endpoint/suspicious_rundll32_rename/) | [Signed Binary Proxy Execution](/tags/#signed-binary-proxy-execution), [Masquerading](/tags/#masquerading), [Rundll32](/tags/#rundll32), [Rename System Utilities](/tags/#rename-system-utilities) | Hunting |
| [Suspicious Rundll32 StartW](/endpoint/suspicious_rundll32_startw/) | [Signed Binary Proxy Execution](/tags/#signed-binary-proxy-execution), [Rundll32](/tags/#rundll32) | TTP |
| [Suspicious Rundll32 dllregisterserver](/endpoint/suspicious_rundll32_dllregisterserver/) | [Signed Binary Proxy Execution](/tags/#signed-binary-proxy-execution), [Rundll32](/tags/#rundll32) | TTP |
| [Suspicious Rundll32 no Command Line Arguments](/endpoint/suspicious_rundll32_no_command_line_arguments/) | [Signed Binary Proxy Execution](/tags/#signed-binary-proxy-execution), [Rundll32](/tags/#rundll32) | TTP |
diff --git a/docs/_pages/playbooks.md b/docs/_pages/playbooks.md
index eb535f5e60..892ea13122 100644
--- a/docs/_pages/playbooks.md
+++ b/docs/_pages/playbooks.md
@@ -10,6 +10,8 @@ sidebar:
| Name | Detections | Type |
| --------| ---------- | ----------- |
+| [AWS Disable User Accounts](/playbooks/aws_disable_user_accounts/)| None | Response |
+| [AWS Find Inactive Users](/playbooks/aws_find_inactive_users/)| None | Investigation |
| [Active Directory Reset password](/playbooks/active_directory_reset_password/)| None | Response |
| [Block Indicators](/playbooks/block_indicators/)| None | Response |
| [Crowdstrike Malware Triage](/playbooks/crowdstrike_malware_triage/)| None | Response |
@@ -20,8 +22,8 @@ sidebar:
| [Internal Host SSH Log4j Response](/playbooks/internal_host_ssh_log4j_response/)| None | Response |
| [Internal Host WinRM Investigate](/playbooks/internal_host_winrm_investigate/)| None | Investigation |
| [Internal Host WinRM Log4j Investigate](/playbooks/internal_host_winrm_log4j_investigate/)| None | Investigation |
-| [Internal Host WinRM Response](/playbooks/internal_host_winrm_response/)| None | Investigation |
-| [Log4j Investigate](/playbooks/log4j_investigate/)|[Curl Download and Bash Execution](/endpoint/curl_download_and_bash_execution/)[Wget Download and Bash Execution](/endpoint/wget_download_and_bash_execution/)[Linux Java Spawning Shell](/endpoint/linux_java_spawning_shell/)[Java Class File download by Java User Agent](/endpoint/java_class_file_download_by_java_user_agent/)[Outbound Network Connection from Java Using Default Ports](/endpoint/outbound_network_connection_from_java_using_default_ports/)[Log4Shell JNDI Payload Injection Attempt](/web/log4shell_jndi_payload_injection_attempt/)[Log4Shell JNDI Payload Injection with Outbound Connection](/web/log4shell_jndi_payload_injection_with_outbound_connection/)[Detect Outbound LDAP Traffic](/network/detect_outbound_ldap_traffic/)| Investigate |
+| [Internal Host WinRM Response](/playbooks/internal_host_winrm_response/)| None | Response |
+| [Log4j Investigate](/playbooks/log4j_investigate/)|[Curl Download and Bash Execution](/endpoint/curl_download_and_bash_execution/)[Wget Download and Bash Execution](/endpoint/wget_download_and_bash_execution/)[Linux Java Spawning Shell](/endpoint/linux_java_spawning_shell/)[Java Class File download by Java User Agent](/endpoint/java_class_file_download_by_java_user_agent/)[Outbound Network Connection from Java Using Default Ports](/endpoint/outbound_network_connection_from_java_using_default_ports/)[Log4Shell JNDI Payload Injection Attempt](/web/log4shell_jndi_payload_injection_attempt/)[Log4Shell JNDI Payload Injection with Outbound Connection](/web/log4shell_jndi_payload_injection_with_outbound_connection/)[Detect Outbound LDAP Traffic](/network/detect_outbound_ldap_traffic/)| Investigation |
| [Log4j Respond](/playbooks/log4j_respond/)|[Curl Download and Bash Execution](/endpoint/curl_download_and_bash_execution/)[Wget Download and Bash Execution](/endpoint/wget_download_and_bash_execution/)[Linux Java Spawning Shell](/endpoint/linux_java_spawning_shell/)[Java Class File download by Java User Agent](/endpoint/java_class_file_download_by_java_user_agent/)[Outbound Network Connection from Java Using Default Ports](/endpoint/outbound_network_connection_from_java_using_default_ports/)[Log4Shell JNDI Payload Injection Attempt](/web/log4shell_jndi_payload_injection_attempt/)[Log4Shell JNDI Payload Injection with Outbound Connection](/web/log4shell_jndi_payload_injection_with_outbound_connection/)[Detect Outbound LDAP Traffic](/network/detect_outbound_ldap_traffic/)| Response |
| [Log4j Splunk Investigation](/playbooks/log4j_splunk_investigation/)| None | Investigation |
| [Malware Hunt and Contain](/playbooks/malware_hunt_and_contain/)| None | Response |
@@ -36,3 +38,6 @@ sidebar:
| [Risk Notable Protect Assets and Users](/playbooks/risk_notable_protect_assets_and_users/)| None | Response |
| [Risk Notable Review Indicators](/playbooks/risk_notable_review_indicators/)| None | Response |
| [Risk Notable Verdict](/playbooks/risk_notable_verdict/)| None | Response |
+| [Start Investigation](/playbooks/start_investigation/)| None | Investigation |
+| [Threat Intel Investigate](/playbooks/threat_intel_investigate/)| None | Investigation |
+| [TruSTAR Enrich Indicators](/playbooks/trustar_enrich_indicators/)| None | Investigation |
diff --git a/docs/_pages/stories.md b/docs/_pages/stories.md
index ef3cc4d3ec..6cb21c61dd 100644
--- a/docs/_pages/stories.md
+++ b/docs/_pages/stories.md
@@ -58,7 +58,7 @@ sidebar:
| [Kubernetes Scanning Activity](kubernetes_scanning_activity) | [Cloud Service Discovery](/tags/#cloud-service-discovery) | [Discovery](/tags/#discovery) |
| [Kubernetes Sensitive Object Access Activity]() | None | None |
| [Linux Persistence Techniques](linux_persistence_techniques) | [Sudo and Sudo Caching](/tags/#sudo-and-sudo-caching), [Abuse Elevation Control Mechanism](/tags/#abuse-elevation-control-mechanism) | [Privilege Escalation](/tags/#privilege-escalation) |
-| [Linux Privilege Escalation](linux_privilege_escalation) | [Sudo and Sudo Caching](/tags/#sudo-and-sudo-caching), [Abuse Elevation Control Mechanism](/tags/#abuse-elevation-control-mechanism) | [Privilege Escalation](/tags/#privilege-escalation) |
+| [Linux Privilege Escalation](linux_privilege_escalation) | [Exploitation for Privilege Escalation](/tags/#exploitation-for-privilege-escalation) | [Privilege Escalation](/tags/#privilege-escalation) |
| [Log4Shell CVE-2021-44228](log4shell_cve-2021-44228) | [Exploit Public-Facing Application](/tags/#exploit-public-facing-application) | [Initial Access](/tags/#initial-access) |
| [Malicious PowerShell](malicious_powershell) | [Gather Victim Host Information](/tags/#gather-victim-host-information) | [Reconnaissance](/tags/#reconnaissance) |
| [Masquerading - Rename System Utilities](masquerading_-_rename_system_utilities) | [Masquerading](/tags/#masquerading), [Rename System Utilities](/tags/#rename-system-utilities), [Signed Binary Proxy Execution](/tags/#signed-binary-proxy-execution), [InstallUtil](/tags/#installutil) | [Defense Evasion](/tags/#defense-evasion) |
diff --git a/docs/_playbooks/aws_disable_user_accounts.md b/docs/_playbooks/aws_disable_user_accounts.md
new file mode 100644
index 0000000000..0116fd599d
--- /dev/null
+++ b/docs/_playbooks/aws_disable_user_accounts.md
@@ -0,0 +1,46 @@
+---
+title: "AWS Disable User Accounts"
+last_modified_at: 2021-11-01
+toc: true
+toc_label: ""
+tags:
+ - Response
+ - Splunk SOAR
+ - AWS IAM
+ - Cloud
+---
+
+[Try in Splunk SOAR](https://www.splunk.com/en_us/software/splunk-security-orchestration-and-automation.html){: .btn .btn--success}
+
+#### Description
+
+Disable a list of AWS IAM user accounts. After checking the list of accounts against an allowlist and confirming with an analyst, each account is disabled. The change can be reversed with the `enable user` action.
+
+- **Type**: Response
+- **Product**: Splunk SOAR
+- **Apps**: [AWS IAM](https://splunkbase.splunk.com/apps/#/search/AWS IAM/product/soar)
+- **Last Updated**: 2021-11-01
+- **Author**: Philip Royer, Splunk
+- **ID**: fc0edc75-ff2b-48c0-5f6f-63da6423fd63
+
+#### Associated Detections
+
+
+#### How To Implement
+This playbook works with the community playbook aws_find_inactive_users using the usernames discovered by that playbook. Change the prompt block from admin to the correct analyst user or role. You should create a custom list called aws_inactive_user_allowlist. Any user names in that list will be ignored by this playbook.
+
+#### Playbooks
+
+
+#### Required field
+* aws_username
+
+
+#### Reference
+
+* [https://www.splunk.com/en_us/blog/security/splunk-soar-playbooks-finding-and-disabling-inactive-users-on-aws.html](https://www.splunk.com/en_us/blog/security/splunk-soar-playbooks-finding-and-disabling-inactive-users-on-aws.html)
+
+
+
+
+[*source*](https://github.com/splunk/security_content/tree/develop/playbooks/aws_disable_user_accounts.yml) \| *version*: **1**
\ No newline at end of file
diff --git a/docs/_playbooks/aws_find_inactive_users.md b/docs/_playbooks/aws_find_inactive_users.md
new file mode 100644
index 0000000000..2b87dec545
--- /dev/null
+++ b/docs/_playbooks/aws_find_inactive_users.md
@@ -0,0 +1,46 @@
+---
+title: "AWS Find Inactive Users"
+last_modified_at: 2021-11-01
+toc: true
+toc_label: ""
+tags:
+ - Investigation
+ - Splunk SOAR
+ - AWS IAM
+ - Phantom
+ - Cloud
+---
+
+[Try in Splunk SOAR](https://www.splunk.com/en_us/software/splunk-security-orchestration-and-automation.html){: .btn .btn--success}
+
+#### Description
+
+Find AWS accounts that have not been used for a long time (90 days by default). For each unused account, gather additional group and policy information and create an artifact to enable further automation or manual action.
+
+- **Type**: Investigation
+- **Product**: Splunk SOAR
+- **Apps**: [AWS IAM](https://splunkbase.splunk.com/apps/#/search/AWS IAM/product/soar), [Phantom](https://splunkbase.splunk.com/apps/#/search/Phantom/product/soar)
+- **Last Updated**: 2021-11-01
+- **Author**: Philip Royer, Splunk
+- **ID**: fc0edc76-ff2b-48b0-5f6f-63da6423fd63
+
+#### Associated Detections
+
+
+#### How To Implement
+This playbook is meant to run on a Timer, such as once per week. To adjust the lookback period away from the default, change the number of days to a different negative number in the 'calculate_start_time' block. Note that this playbook will ignore accounts where the password has never been used. These could be unused human accounts or they could be API accounts where the access keys are actively used.
+
+#### Playbooks
+
+
+#### Required field
+
+
+#### Reference
+
+* [https://www.splunk.com/en_us/blog/security/splunk-soar-playbooks-finding-and-disabling-inactive-users-on-aws.html](https://www.splunk.com/en_us/blog/security/splunk-soar-playbooks-finding-and-disabling-inactive-users-on-aws.html)
+
+
+
+
+[*source*](https://github.com/splunk/security_content/tree/develop/playbooks/aws_find_inactive_users.yml) \| *version*: **1**
\ No newline at end of file
diff --git a/docs/_playbooks/delete_detected_files.md b/docs/_playbooks/delete_detected_files.md
index 030af20a60..a8e87a9108 100644
--- a/docs/_playbooks/delete_detected_files.md
+++ b/docs/_playbooks/delete_detected_files.md
@@ -774,7 +774,6 @@ This playbook acts upon events where a file has been determined to be malicious
-
#### How To Implement
diff --git a/docs/_playbooks/internal_host_winrm_response.md b/docs/_playbooks/internal_host_winrm_response.md
index e0ae315068..cc6f261e37 100644
--- a/docs/_playbooks/internal_host_winrm_response.md
+++ b/docs/_playbooks/internal_host_winrm_response.md
@@ -4,7 +4,7 @@ last_modified_at: 2021-12-14
toc: true
toc_label: ""
tags:
- - Investigation
+ - Response
- Splunk SOAR
- Windows Remote Management
---
@@ -15,7 +15,7 @@ tags:
Published in response to CVE-2021-44228, this playbook accepts a list of hosts and filenames to remediate on the endpoint. If filenames are provided, the endpoints will be searched and then the user can approve deletion. Then the user is prompted to quarantine the endpoint.
-- **Type**: Investigation
+- **Type**: Response
- **Product**: Splunk SOAR
- **Apps**: [Windows Remote Management](https://splunkbase.splunk.com/apps/#/search/Windows Remote Management/product/soar)
- **Last Updated**: 2021-12-14
diff --git a/docs/_playbooks/log4j_investigate.md b/docs/_playbooks/log4j_investigate.md
index 33a0c299ff..78edf26f80 100644
--- a/docs/_playbooks/log4j_investigate.md
+++ b/docs/_playbooks/log4j_investigate.md
@@ -4,8 +4,9 @@ last_modified_at: 2021-12-14
toc: true
toc_label: ""
tags:
- - Investigate
+ - Investigation
- Splunk SOAR
+ - Log4J
---
[Try in Splunk SOAR](https://www.splunk.com/en_us/software/splunk-security-orchestration-and-automation.html){: .btn .btn--success}
@@ -14,7 +15,7 @@ tags:
Published in response to CVE-2021-44228, this playbook and its sub-playbooks can be used to investigate and respond to attacks against hosts running vulnerable Java applications which use log4j. Between the parent playbook and seven sub-playbooks, each potentially compromised host found in Splunk Enteprise can be investigated and the risk can be mitigated using SSH for unix systems and WinRM for Windows systems.
-- **Type**: Investigate
+- **Type**: Investigation
- **Product**: Splunk SOAR
- **Apps**:
- **Last Updated**: 2021-12-14
@@ -1470,8 +1471,6 @@ Published in response to CVE-2021-44228, this playbook and its sub-playbooks can
-
-
@@ -3385,8 +3384,6 @@ Published in response to CVE-2021-44228, this playbook and its sub-playbooks can
-
-
@@ -4936,7 +4933,6 @@ Published in response to CVE-2021-44228, this playbook and its sub-playbooks can
-
* [Log4Shell JNDI Payload Injection Attempt](/web/log4shell_jndi_payload_injection_attempt/)
@@ -5689,7 +5685,6 @@ Published in response to CVE-2021-44228, this playbook and its sub-playbooks can
-
* [Log4Shell JNDI Payload Injection with Outbound Connection](/web/log4shell_jndi_payload_injection_with_outbound_connection/)
@@ -6186,8 +6181,6 @@ Published in response to CVE-2021-44228, this playbook and its sub-playbooks can
-
-
@@ -6788,7 +6781,6 @@ Published in response to CVE-2021-44228, this playbook and its sub-playbooks can
-
#### How To Implement
diff --git a/docs/_playbooks/log4j_respond.md b/docs/_playbooks/log4j_respond.md
index e0d3abe546..f8c67f79b8 100644
--- a/docs/_playbooks/log4j_respond.md
+++ b/docs/_playbooks/log4j_respond.md
@@ -6,6 +6,7 @@ toc_label: ""
tags:
- Response
- Splunk SOAR
+ - Log4J
---
[Try in Splunk SOAR](https://www.splunk.com/en_us/software/splunk-security-orchestration-and-automation.html){: .btn .btn--success}
@@ -1470,8 +1471,6 @@ Published in response to CVE-2021-44228, this playbook is meant to be launched a
-
-
@@ -3385,8 +3384,6 @@ Published in response to CVE-2021-44228, this playbook is meant to be launched a
-
-
@@ -4936,7 +4933,6 @@ Published in response to CVE-2021-44228, this playbook is meant to be launched a
-
* [Log4Shell JNDI Payload Injection Attempt](/web/log4shell_jndi_payload_injection_attempt/)
@@ -5689,7 +5685,6 @@ Published in response to CVE-2021-44228, this playbook is meant to be launched a
-
* [Log4Shell JNDI Payload Injection with Outbound Connection](/web/log4shell_jndi_payload_injection_with_outbound_connection/)
@@ -6186,8 +6181,6 @@ Published in response to CVE-2021-44228, this playbook is meant to be launched a
-
-
@@ -6788,7 +6781,6 @@ Published in response to CVE-2021-44228, this playbook is meant to be launched a
-
#### How To Implement
diff --git a/docs/_playbooks/ransomware_investigate_and_contain.md b/docs/_playbooks/ransomware_investigate_and_contain.md
index 8abf8e815a..fa7b2d2847 100644
--- a/docs/_playbooks/ransomware_investigate_and_contain.md
+++ b/docs/_playbooks/ransomware_investigate_and_contain.md
@@ -11,6 +11,7 @@ tags:
- Palo Alto Networks Firewall
- WildFire
- Cylance
+ - Ransomware
---
[Try in Splunk SOAR](https://www.splunk.com/en_us/software/splunk-security-orchestration-and-automation.html){: .btn .btn--success}
@@ -778,7 +779,6 @@ This playbook investigates and contains ransomware detected on endpoints.
-
#### How To Implement
diff --git a/docs/_playbooks/risk_notable_block_indicators.md b/docs/_playbooks/risk_notable_block_indicators.md
index c7f9d98dad..3b75e9b955 100644
--- a/docs/_playbooks/risk_notable_block_indicators.md
+++ b/docs/_playbooks/risk_notable_block_indicators.md
@@ -7,6 +7,7 @@ tags:
- Response
- Splunk SOAR
- None
+ - Risk Notable
---
[Try in Splunk SOAR](https://www.splunk.com/en_us/software/splunk-security-orchestration-and-automation.html){: .btn .btn--success}
diff --git a/docs/_playbooks/risk_notable_enrich.md b/docs/_playbooks/risk_notable_enrich.md
index 2fba096826..fba05b8ab2 100644
--- a/docs/_playbooks/risk_notable_enrich.md
+++ b/docs/_playbooks/risk_notable_enrich.md
@@ -7,6 +7,7 @@ tags:
- Investigation
- Splunk SOAR
- None
+ - Risk Notable
---
[Try in Splunk SOAR](https://www.splunk.com/en_us/software/splunk-security-orchestration-and-automation.html){: .btn .btn--success}
diff --git a/docs/_playbooks/risk_notable_import_data.md b/docs/_playbooks/risk_notable_import_data.md
index 3fc22126b8..a05f5ef449 100644
--- a/docs/_playbooks/risk_notable_import_data.md
+++ b/docs/_playbooks/risk_notable_import_data.md
@@ -7,6 +7,7 @@ tags:
- Investigation
- Splunk SOAR
- Splunk
+ - Risk Notable
---
[Try in Splunk SOAR](https://www.splunk.com/en_us/software/splunk-security-orchestration-and-automation.html){: .btn .btn--success}
diff --git a/docs/_playbooks/risk_notable_investigate.md b/docs/_playbooks/risk_notable_investigate.md
index 2214f57c80..2ccd662d03 100644
--- a/docs/_playbooks/risk_notable_investigate.md
+++ b/docs/_playbooks/risk_notable_investigate.md
@@ -7,6 +7,7 @@ tags:
- Investigation
- Splunk SOAR
- None
+ - Risk Notable
---
[Try in Splunk SOAR](https://www.splunk.com/en_us/software/splunk-security-orchestration-and-automation.html){: .btn .btn--success}
diff --git a/docs/_playbooks/risk_notable_merge_events.md b/docs/_playbooks/risk_notable_merge_events.md
index bd9e41ec0d..19e4abac0b 100644
--- a/docs/_playbooks/risk_notable_merge_events.md
+++ b/docs/_playbooks/risk_notable_merge_events.md
@@ -7,6 +7,7 @@ tags:
- Investigation
- Splunk SOAR
- None
+ - Risk Notable
---
[Try in Splunk SOAR](https://www.splunk.com/en_us/software/splunk-security-orchestration-and-automation.html){: .btn .btn--success}
diff --git a/docs/_playbooks/risk_notable_mitigate.md b/docs/_playbooks/risk_notable_mitigate.md
index 454009f5de..a502c418d4 100644
--- a/docs/_playbooks/risk_notable_mitigate.md
+++ b/docs/_playbooks/risk_notable_mitigate.md
@@ -10,6 +10,7 @@ tags:
- o
- n
- e
+ - Risk Notable
---
[Try in Splunk SOAR](https://www.splunk.com/en_us/software/splunk-security-orchestration-and-automation.html){: .btn .btn--success}
diff --git a/docs/_playbooks/risk_notable_preprocess.md b/docs/_playbooks/risk_notable_preprocess.md
index c03661f7f2..dee8ffe476 100644
--- a/docs/_playbooks/risk_notable_preprocess.md
+++ b/docs/_playbooks/risk_notable_preprocess.md
@@ -7,6 +7,7 @@ tags:
- Investigation
- Splunk SOAR
- Splunk
+ - Risk Notable
---
[Try in Splunk SOAR](https://www.splunk.com/en_us/software/splunk-security-orchestration-and-automation.html){: .btn .btn--success}
diff --git a/docs/_playbooks/risk_notable_protect_assets_and_users.md b/docs/_playbooks/risk_notable_protect_assets_and_users.md
index ebb421de69..1cb23f13ed 100644
--- a/docs/_playbooks/risk_notable_protect_assets_and_users.md
+++ b/docs/_playbooks/risk_notable_protect_assets_and_users.md
@@ -7,6 +7,7 @@ tags:
- Response
- Splunk SOAR
- None
+ - Risk Notable
---
[Try in Splunk SOAR](https://www.splunk.com/en_us/software/splunk-security-orchestration-and-automation.html){: .btn .btn--success}
diff --git a/docs/_playbooks/risk_notable_review_indicators.md b/docs/_playbooks/risk_notable_review_indicators.md
index 05d1ca7376..dfae32bd39 100644
--- a/docs/_playbooks/risk_notable_review_indicators.md
+++ b/docs/_playbooks/risk_notable_review_indicators.md
@@ -7,6 +7,7 @@ tags:
- Response
- Splunk SOAR
- None
+ - Risk Notable
---
[Try in Splunk SOAR](https://www.splunk.com/en_us/software/splunk-security-orchestration-and-automation.html){: .btn .btn--success}
diff --git a/docs/_playbooks/risk_notable_verdict.md b/docs/_playbooks/risk_notable_verdict.md
index b2a5261272..82185a47e2 100644
--- a/docs/_playbooks/risk_notable_verdict.md
+++ b/docs/_playbooks/risk_notable_verdict.md
@@ -7,6 +7,7 @@ tags:
- Response
- Splunk SOAR
- None
+ - Risk Notable
---
[Try in Splunk SOAR](https://www.splunk.com/en_us/software/splunk-security-orchestration-and-automation.html){: .btn .btn--success}
diff --git a/docs/_playbooks/start_investigation.md b/docs/_playbooks/start_investigation.md
new file mode 100644
index 0000000000..7be4450648
--- /dev/null
+++ b/docs/_playbooks/start_investigation.md
@@ -0,0 +1,40 @@
+---
+title: "Start Investigation"
+last_modified_at: 2021-10-07
+toc: true
+toc_label: ""
+tags:
+ - Investigation
+ - Splunk SOAR
+---
+
+[Try in Splunk SOAR](https://www.splunk.com/en_us/software/splunk-security-orchestration-and-automation.html){: .btn .btn--success}
+
+#### Description
+
+Handle cases in Splunk SOAR with consistency that only automation can provide. This playbook ensures that cases are being assigned to analysts, and follow on work gets started.
+
+- **Type**: Investigation
+- **Product**: Splunk SOAR
+- **Apps**:
+- **Last Updated**: 2021-10-07
+- **Author**: Kelby Shelton, Splunk
+- **ID**: fc5adc76-f3ab-4cb0-5f6f-63bc3493fd46
+
+#### Associated Detections
+
+
+#### How To Implement
+This is a playbook that is designed to be recommended within a workbook. If used in this manner, the playbook will assign the user that launched the playbook as the owner of the event, move the event status to "Open", and complete the workbook task where this playbook appears. If there is a task after the one where the playbook appears (within the same phase), it will set the next task to "In Progress."
+
+#### Playbooks
+
+
+#### Required field
+
+
+#### Reference
+
+
+
+[*source*](https://github.com/splunk/security_content/tree/develop/playbooks/start_investigation.yml) \| *version*: **1**
\ No newline at end of file
diff --git a/docs/_playbooks/threat_intel_investigate.md b/docs/_playbooks/threat_intel_investigate.md
new file mode 100644
index 0000000000..f5b99eb9ec
--- /dev/null
+++ b/docs/_playbooks/threat_intel_investigate.md
@@ -0,0 +1,44 @@
+---
+title: "Threat Intel Investigate"
+last_modified_at: 2021-11-30
+toc: true
+toc_label: ""
+tags:
+ - Investigation
+ - Splunk SOAR
+ - threat_intel
+---
+
+[Try in Splunk SOAR](https://www.splunk.com/en_us/software/splunk-security-orchestration-and-automation.html){: .btn .btn--success}
+
+#### Description
+
+This parent playbook collects data and launches appropriate child playbooks to gather threat intelligence information about indicators. After the child playbooks have run, this playbook posts the notes to the container and prompts the analyst to add tags to each enriched indicator based on the intelligence provided.
+
+- **Type**: Investigation
+- **Product**: Splunk SOAR
+- **Apps**:
+- **Last Updated**: 2021-11-30
+- **Author**: Philip Royer, Splunk
+- **ID**: fc5adc76-fd2b-48b0-5f6f-63bc3493fd46
+
+#### Associated Detections
+
+
+#### How To Implement
+The prompt is currently sent to the Administrator role, but should be changed to the appropriate user and role. The "list_investigate_playbooks" block fetches playbooks from the local repository with the tags "investigate" and "threat_intel" by default. The playbook "trustar_enrich_indicators" is meant to be used by this playbook, and others can be created to replace it or work alongside it. To add a new input playbook, copy it to the local repository and give it the necessary tags. Define a playbook input with the name "indicators" and the data type matching the types of indicators the playbook can process. To add a new tag to the preconfigured list, add it to the "choices" array in the "threat_intel_indicator_review" prompt block, and add it to the "response_to_tag_map" in "process_indicators".
+
+#### Playbooks
+
+
+#### Required field
+
+
+#### Reference
+
+* [https://www.splunk.com/en_us/blog/security/TruSTAR-Enrich-Indicators-soar-in-seconds.html](https://www.splunk.com/en_us/blog/security/TruSTAR-Enrich-Indicators-soar-in-seconds.html)
+
+
+
+
+[*source*](https://github.com/splunk/security_content/tree/develop/playbooks/threat_intel_investigate.yml) \| *version*: **1**
\ No newline at end of file
diff --git a/docs/_playbooks/trustar_enrich_indicators.md b/docs/_playbooks/trustar_enrich_indicators.md
new file mode 100644
index 0000000000..08c9847a7c
--- /dev/null
+++ b/docs/_playbooks/trustar_enrich_indicators.md
@@ -0,0 +1,47 @@
+---
+title: "TruSTAR Enrich Indicators"
+last_modified_at: 2021-11-24
+toc: true
+toc_label: ""
+tags:
+ - Investigation
+ - Splunk SOAR
+ - TruSTAR
+ - threat_intel
+ - risk_notable
+---
+
+[Try in Splunk SOAR](https://www.splunk.com/en_us/software/splunk-security-orchestration-and-automation.html){: .btn .btn--success}
+
+#### Description
+
+Use TruSTAR to gather threat information about indicators in a SOAR event. Tag the indicators with the normalized priority score from TruSTAR and summarize the findings in an analyst note. This playbook is meant to be used as a child playbook executed by a parent playbook such as "threat_intel_investigate".
+
+- **Type**: Investigation
+- **Product**: Splunk SOAR
+- **Apps**: [TruSTAR](https://splunkbase.splunk.com/apps/#/search/TruSTAR/product/soar)
+- **Last Updated**: 2021-11-24
+- **Author**: Philip Royer, Splunk
+- **ID**: fc5adc76-fd2b-48b0-5f6f-63da6423fd63
+
+#### Associated Detections
+
+
+#### How To Implement
+To use this playbook as a sub-playbook of "threat_intel_investigate", copy it to the local git repository and make sure it has the tags "investigate" and "threat_intel". To use this playbook as a sub-playbook of "risk_notable_enrich", copy it to local and make sure it has the tags "investigate" and "risk_notable" To control the types of indicators processed by this playbook, change the data types of the "indicators" input"
+
+#### Playbooks
+
+
+#### Required field
+* indicators
+
+
+#### Reference
+
+* [https://www.splunk.com/en_us/blog/security/TruSTAR-Enrich-Indicators-soar-in-seconds.html](https://www.splunk.com/en_us/blog/security/TruSTAR-Enrich-Indicators-soar-in-seconds.html)
+
+
+
+
+[*source*](https://github.com/splunk/security_content/tree/develop/playbooks/trustar_enrich_indicators.yml) \| *version*: **1**
\ No newline at end of file
diff --git a/docs/_posts/2020-11-30-rundll_loading_dll_by_ordinal.md b/docs/_posts/2020-11-30-rundll_loading_dll_by_ordinal.md
deleted file mode 100644
index fc40d2250b..0000000000
--- a/docs/_posts/2020-11-30-rundll_loading_dll_by_ordinal.md
+++ /dev/null
@@ -1,109 +0,0 @@
----
-title: "RunDLL Loading DLL By Ordinal"
-excerpt: "Signed Binary Proxy Execution, Rundll32"
-categories:
- - Endpoint
-last_modified_at: 2020-11-30
-toc: true
-toc_label: ""
-tags:
- - Signed Binary Proxy Execution
- - Defense Evasion
- - Rundll32
- - Defense Evasion
- - Splunk Enterprise
- - Splunk Enterprise Security
- - Splunk Cloud
- - Endpoint
----
-
-
-
-[Try in Splunk Security Cloud](https://www.splunk.com/en_us/cyber-security.html){: .btn .btn--success}
-
-#### Description
-
-This search looks for executing scripts with rundll32. Adversaries may abuse rundll32.exe to proxy execution of malicious code. Using rundll32.exe, vice executing directly, may avoid triggering security tools that may not monitor execution of the rundll32.exe process because of allowlists or false positives from normal operations.
-
-- **Type**: [TTP](https://github.com/splunk/security_content/wiki/Detection-Analytic-Types)
-- **Product**: Splunk Enterprise, Splunk Enterprise Security, Splunk Cloud
-- **Datamodel**: [Endpoint](https://docs.splunk.com/Documentation/CIM/latest/User/Endpoint)
-- **Last Updated**: 2020-11-30
-- **Author**: David Dorsey, Splunk
-- **ID**: 6c135f8d-5e60-454e-80b7-c56eed739833
-
-
-#### [ATT&CK](https://attack.mitre.org/)
-
-| ID | Technique | Tactic |
-| -------------- | ---------------- |-------------------- |
-| [T1218](https://attack.mitre.org/techniques/T1218/) | Signed Binary Proxy Execution | Defense Evasion |
-
-| [T1218.011](https://attack.mitre.org/techniques/T1218/011/) | Rundll32 | Defense Evasion |
-
-#### Search
-
-```
-
-| tstats `security_content_summariesonly` values(Processes.process) as process min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where `process_rundll32` by Processes.process_name Processes.parent_process_name Processes.original_file_name Processes.process Processes.dest Processes.user
-| `drop_dm_object_name(Processes)`
-| `security_content_ctime(firstTime)`
-| `security_content_ctime(lastTime)`
-| `rundll_loading_dll_by_ordinal_filter`
-```
-
-#### Macros
-The SPL above uses the following Macros:
-* [process_rundll32](https://github.com/splunk/security_content/blob/develop/macros/process_rundll32.yml)
-* [security_content_ctime](https://github.com/splunk/security_content/blob/develop/macros/security_content_ctime.yml)
-* [security_content_summariesonly](https://github.com/splunk/security_content/blob/develop/macros/security_content_summariesonly.yml)
-
-Note that `rundll_loading_dll_by_ordinal_filter` is a empty macro by default. It allows the user to filter out any results (false positives) without editing the SPL.
-
-#### Required field
-* _time
-* Processes.dest
-* Processes.user
-* Processes.parent_process_name
-* Processes.parent_process
-* Processes.original_file_name
-* Processes.process_name
-* Processes.process
-* Processes.process_id
-* Processes.parent_process_path
-* Processes.process_path
-* Processes.parent_process_id
-
-
-#### How To Implement
-To successfully implement this search you need to be ingesting information on process that include the name of the process responsible for the changes from your endpoints into the `Endpoint` datamodel in the `Processes` node. In addition, confirm the latest CIM App 4.20 or higher is installed and the latest TA for the endpoint product.
-
-#### Known False Positives
-While not common, loading a DLL under %AppData% and calling a function by ordinal is possible by a legitimate process
-
-#### Kill Chain Phase
-* Installation
-
-
-
-#### RBA
-
-| Risk Score | Impact | Confidence | Message |
-| ----------- | ----------- |--------------|--------------|
-| 70.0 | 70 | 100 | A rundll32 process $process_name$ with ordinal parameter like this process commandline $process$ in host $dest$ |
-
-
-
-
-#### Reference
-
-
-#### Test Dataset
-Replay any dataset to Splunk Enterprise by using our [`replay.py`](https://github.com/splunk/attack_data#using-replaypy) tool or the [UI](https://github.com/splunk/attack_data#using-ui).
-Alternatively you can replay a dataset into a [Splunk Attack Range](https://github.com/splunk/attack_range#replay-dumps-into-attack-range-splunk-server)
-
-* [https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1218.011/atomic_red_team/windows-sysmon.log](https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1218.011/atomic_red_team/windows-sysmon.log)
-
-
-
-[*source*](https://github.com/splunk/security_content/tree/develop/detections/endpoint/rundll_loading_dll_by_ordinal.yml) \| *version*: **5**
\ No newline at end of file
diff --git a/docs/_posts/2021-02-04-suspicious_rundll32_rename.md b/docs/_posts/2021-02-04-suspicious_rundll32_rename.md
deleted file mode 100644
index dbc01c5cb1..0000000000
--- a/docs/_posts/2021-02-04-suspicious_rundll32_rename.md
+++ /dev/null
@@ -1,122 +0,0 @@
----
-title: "Suspicious Rundll32 Rename"
-excerpt: "Signed Binary Proxy Execution, Masquerading, Rundll32, Rename System Utilities"
-categories:
- - Endpoint
-last_modified_at: 2021-02-04
-toc: true
-toc_label: ""
-tags:
- - Signed Binary Proxy Execution
- - Defense Evasion
- - Masquerading
- - Defense Evasion
- - Rundll32
- - Defense Evasion
- - Rename System Utilities
- - Defense Evasion
- - Splunk Enterprise
- - Splunk Enterprise Security
- - Splunk Cloud
- - Endpoint
----
-
-
-
-[Try in Splunk Security Cloud](https://www.splunk.com/en_us/cyber-security.html){: .btn .btn--success}
-
-#### Description
-
-The following analytic identifies renamed instances of rundll32.exe executing. rundll32.exe is natively found in C:\Windows\system32 and C:\Windows\syswow64. During investigation, validate it is the legitimate rundll32.exe executing and what script content it is loading. This query relies on the original filename or internal name from the PE meta data. Expand the query as needed by looking for specific command line arguments outlined in other analytics.
-
-- **Type**: [Hunting](https://github.com/splunk/security_content/wiki/Detection-Analytic-Types)
-- **Product**: Splunk Enterprise, Splunk Enterprise Security, Splunk Cloud
-- **Datamodel**: [Endpoint](https://docs.splunk.com/Documentation/CIM/latest/User/Endpoint)
-- **Last Updated**: 2021-02-04
-- **Author**: Michael Haag, Splunk
-- **ID**: 7360137f-abad-473e-8189-acbdaa34d114
-
-
-#### [ATT&CK](https://attack.mitre.org/)
-
-| ID | Technique | Tactic |
-| -------------- | ---------------- |-------------------- |
-| [T1218](https://attack.mitre.org/techniques/T1218/) | Signed Binary Proxy Execution | Defense Evasion |
-
-| [T1036](https://attack.mitre.org/techniques/T1036/) | Masquerading | Defense Evasion |
-
-| [T1218.011](https://attack.mitre.org/techniques/T1218/011/) | Rundll32 | Defense Evasion |
-
-| [T1036.003](https://attack.mitre.org/techniques/T1036/003/) | Rename System Utilities | Defense Evasion |
-
-#### Search
-
-```
-
-| tstats `security_content_summariesonly` count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes where `process_rundll32` by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.process Processes.process_id Processes.parent_process_id Processes.original_file_name
-| `drop_dm_object_name(Processes)`
-| `security_content_ctime(firstTime)`
-| `security_content_ctime(lastTime)`
-| `suspicious_rundll32_rename_filter`
-```
-
-#### Macros
-The SPL above uses the following Macros:
-* [process_rundll32](https://github.com/splunk/security_content/blob/develop/macros/process_rundll32.yml)
-* [security_content_ctime](https://github.com/splunk/security_content/blob/develop/macros/security_content_ctime.yml)
-* [security_content_summariesonly](https://github.com/splunk/security_content/blob/develop/macros/security_content_summariesonly.yml)
-
-Note that `suspicious_rundll32_rename_filter` is a empty macro by default. It allows the user to filter out any results (false positives) without editing the SPL.
-
-#### Required field
-* _time
-* Processes.dest
-* Processes.user
-* Processes.parent_process_name
-* Processes.parent_process
-* Processes.original_file_name
-* Processes.process_name
-* Processes.process
-* Processes.process_id
-* Processes.parent_process_path
-* Processes.process_path
-* Processes.parent_process_id
-
-
-#### How To Implement
-To successfully implement this search you need to be ingesting information on process that include the name of the process responsible for the changes from your endpoints into the `Endpoint` datamodel in the `Processes` node. In addition, confirm the latest CIM App 4.20 or higher is installed and the latest TA for the endpoint product.
-
-#### Known False Positives
-Although unlikely, some legitimate applications may use a moved copy of rundll32, triggering a false positive.
-
-#### Kill Chain Phase
-* Actions on Objectives
-
-
-
-#### RBA
-
-| Risk Score | Impact | Confidence | Message |
-| ----------- | ----------- |--------------|--------------|
-| 63.0 | 70 | 90 | Suspicious renamed rundll32.exe binary ran on $dest$ by $user$ |
-
-
-
-
-#### Reference
-
-* [https://attack.mitre.org/techniques/T1218/011/](https://attack.mitre.org/techniques/T1218/011/)
-* [https://github.com/redcanaryco/atomic-red-team/blob/master/atomics/T1218.011/T1218.011.md](https://github.com/redcanaryco/atomic-red-team/blob/master/atomics/T1218.011/T1218.011.md)
-* [https://lolbas-project.github.io/lolbas/Binaries/Rundll32](https://lolbas-project.github.io/lolbas/Binaries/Rundll32)
-
-
-
-#### Test Dataset
-Replay any dataset to Splunk Enterprise by using our [`replay.py`](https://github.com/splunk/attack_data#using-replaypy) tool or the [UI](https://github.com/splunk/attack_data#using-ui).
-Alternatively you can replay a dataset into a [Splunk Attack Range](https://github.com/splunk/attack_range#replay-dumps-into-attack-range-splunk-server)
-
-* [https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1218.011/atomic_red_team/windows-sysmon.log](https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1218.011/atomic_red_team/windows-sysmon.log)
-
-
-
-[*source*](https://github.com/splunk/security_content/tree/develop/detections/endpoint/suspicious_rundll32_rename.yml) \| *version*: **3**
\ No newline at end of file
diff --git a/docs/_posts/2021-12-21-linux_setuid_using_chmod_utility.md b/docs/_posts/2021-12-21-linux_setuid_using_chmod_utility.md
index 9d06eed015..653de10ac3 100644
--- a/docs/_posts/2021-12-21-linux_setuid_using_chmod_utility.md
+++ b/docs/_posts/2021-12-21-linux_setuid_using_chmod_utility.md
@@ -47,7 +47,7 @@ This analytic looks for suspicious chmod utility execution to enable SUID bit. T
```
-| tstats `security_content_summariesonly` count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes WHERE (Processes.process_name = chmod OR Processes.process = "*chmod *") AND Processes.process IN("* g+s *", "* u+s *", "* 4777 *", "* 4577 *", "* 777 *") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.process Processes.process_id Processes.parent_process_id Processes.process_guid
+| tstats `security_content_summariesonly` count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes WHERE (Processes.process_name = chmod OR Processes.process = "*chmod *") AND Processes.process IN("* g+s *", "* u+s *", "* 4777 *", "* 4577 *") by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.process Processes.process_id Processes.parent_process_id Processes.process_guid
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| `security_content_ctime(lastTime)`
diff --git a/docs/_posts/2022-01-20-powershell_remove_windows_defender_directory.md b/docs/_posts/2022-01-20-powershell_remove_windows_defender_directory.md
index 207c8be5a7..ba0de913dc 100644
--- a/docs/_posts/2022-01-20-powershell_remove_windows_defender_directory.md
+++ b/docs/_posts/2022-01-20-powershell_remove_windows_defender_directory.md
@@ -44,7 +44,7 @@ This analytic will identify a suspicious PowerShell command used to delete the W
#### Search
```
-`powershell` EventCode=4104 Message = "* rmdir *" OR Message = "*\\Microsoft\\Windows Defender*"
+`powershell` EventCode=4104 Message = "* rmdir *" AND Message = "*\\Microsoft\\Windows Defender*"
| stats count min(_time) as firstTime max(_time) as lastTime by EventCode Message ComputerName User
| `security_content_ctime(firstTime)`
| `security_content_ctime(lastTime)`
diff --git a/docs/_posts/2022-01-28-linux_pkexec_privilege_escalation.md b/docs/_posts/2022-01-28-linux_pkexec_privilege_escalation.md
new file mode 100644
index 0000000000..f8dd1479bd
--- /dev/null
+++ b/docs/_posts/2022-01-28-linux_pkexec_privilege_escalation.md
@@ -0,0 +1,119 @@
+---
+title: "Linux pkexec Privilege Escalation"
+excerpt: "Exploitation for Privilege Escalation"
+categories:
+ - Endpoint
+last_modified_at: 2022-01-28
+toc: true
+toc_label: ""
+tags:
+ - Exploitation for Privilege Escalation
+ - Privilege Escalation
+ - Splunk Enterprise
+ - Splunk Enterprise Security
+ - Splunk Cloud
+ - CVE-2021-4034
+ - Endpoint
+---
+
+
+
+[Try in Splunk Security Cloud](https://www.splunk.com/en_us/cyber-security.html){: .btn .btn--success}
+
+#### Description
+
+The following analytic identifies `pkexec` spawning with no command-line arguments. A vulnerability in Polkit's pkexec component identified as CVE-2021-4034 (PwnKit) which is present in the default configuration of all major Linux distributions and can be exploited to gain full root privileges on the system.
+
+- **Type**: [TTP](https://github.com/splunk/security_content/wiki/Detection-Analytic-Types)
+- **Product**: Splunk Enterprise, Splunk Enterprise Security, Splunk Cloud
+- **Datamodel**: [Endpoint](https://docs.splunk.com/Documentation/CIM/latest/User/Endpoint)
+- **Last Updated**: 2022-01-28
+- **Author**: Michael Haag, Splunk
+- **ID**: 03e22c1c-8086-11ec-ac2e-acde48001122
+
+
+#### [ATT&CK](https://attack.mitre.org/)
+
+| ID | Technique | Tactic |
+| -------------- | ---------------- |-------------------- |
+| [T1068](https://attack.mitre.org/techniques/T1068/) | Exploitation for Privilege Escalation | Privilege Escalation |
+
+#### Search
+
+```
+
+| tstats `security_content_summariesonly` count FROM datamodel=Endpoint.Processes where Processes.process_name=pkexec by _time Processes.dest Processes.process_id Processes.parent_process_name Processes.process_name Processes.process Processes.process_path
+| `drop_dm_object_name(Processes)`
+| `security_content_ctime(firstTime)`
+| `security_content_ctime(lastTime)`
+| regex process="(^.{1}$)"
+| `linux_pkexec_privilege_escalation_filter`
+```
+
+#### Macros
+The SPL above uses the following Macros:
+* [security_content_ctime](https://github.com/splunk/security_content/blob/develop/macros/security_content_ctime.yml)
+* [security_content_summariesonly](https://github.com/splunk/security_content/blob/develop/macros/security_content_summariesonly.yml)
+
+Note that `linux_pkexec_privilege_escalation_filter` is a empty macro by default. It allows the user to filter out any results (false positives) without editing the SPL.
+
+#### Required field
+* _time
+* Processes.dest
+* Processes.user
+* Processes.parent_process_name
+* Processes.parent_process
+* Processes.original_file_name
+* Processes.process_name
+* Processes.process
+* Processes.process_id
+* Processes.parent_process_path
+* Processes.process_path
+* Processes.parent_process_id
+
+
+#### How To Implement
+Depending on the EDR product in use, there are multiple ways to "null" the command-line field, Processes.process. Two that may be useful `process="(^.{0}$)"` or `| where isnull(process)`. To generate data for this behavior, Sysmon for Linux was utilized. To successfully implement this search you need to be ingesting information on process that include the name of the process responsible for the changes from your endpoints into the `Endpoint` datamodel in the `Processes` node. In addition, confirm the latest CIM App 4.20 or higher is installed and the latest TA for the endpoint product.
+
+#### Known False Positives
+False positives may be present, filter as needed.
+
+#### Kill Chain Phase
+* Exploitation
+
+
+
+#### RBA
+
+| Risk Score | Impact | Confidence | Message |
+| ----------- | ----------- |--------------|--------------|
+| 56.0 | 80 | 70 | An instance of $parent_process_name$ spawning $process_name$ was identified on endpoint $dest$ by user $user$ related to a local privilege escalation in polkit pkexec. |
+
+
+
+#### CVE
+
+| ID | Summary | [CVSS](https://nvd.nist.gov/vuln-metrics/cvss) |
+| ----------- | ----------- | -------------- |
+| [CVE-2021-4034](https://nvd.nist.gov/vuln/detail/CVE-2021-4034) | A local privilege escalation vulnerability was found on polkit's pkexec utility. The pkexec application is a setuid tool designed to allow unprivileged users to run commands as privileged users according predefined policies. The current version of pkexec doesn't handle the calling parameters count correctly and ends trying to execute environment variables as commands. An attacker can leverage this by crafting environment variables in such a way it'll induce pkexec to execute arbitrary code. When successfully executed the attack can cause a local privilege escalation given unprivileged users administrative rights on the target machine. | 7.2 |
+
+
+
+#### Reference
+
+* [https://www.reddit.com/r/crowdstrike/comments/sdfeig/20220126_cool_query_friday_hunting_pwnkit_local/](https://www.reddit.com/r/crowdstrike/comments/sdfeig/20220126_cool_query_friday_hunting_pwnkit_local/)
+* [https://linux.die.net/man/1/pkexec](https://linux.die.net/man/1/pkexec)
+* [https://www.bleepingcomputer.com/news/security/linux-system-service-bug-gives-root-on-all-major-distros-exploit-released/](https://www.bleepingcomputer.com/news/security/linux-system-service-bug-gives-root-on-all-major-distros-exploit-released/)
+* [https://access.redhat.com/security/security-updates/#/?q=polkit&p=1&sort=portal_publication_date%20desc&rows=10&portal_advisory_type=Security%20Advisory&documentKind=PortalProduct](https://access.redhat.com/security/security-updates/#/?q=polkit&p=1&sort=portal_publication_date%20desc&rows=10&portal_advisory_type=Security%20Advisory&documentKind=PortalProduct)
+
+
+
+#### Test Dataset
+Replay any dataset to Splunk Enterprise by using our [`replay.py`](https://github.com/splunk/attack_data#using-replaypy) tool or the [UI](https://github.com/splunk/attack_data#using-ui).
+Alternatively you can replay a dataset into a [Splunk Attack Range](https://github.com/splunk/attack_range#replay-dumps-into-attack-range-splunk-server)
+
+* [https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1068/zoom_child_process/linux-sysmon.log](https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1068/zoom_child_process/linux-sysmon.log)
+
+
+
+[*source*](https://github.com/splunk/security_content/tree/develop/detections/endpoint/linux_pkexec_privilege_escalation.yml) \| *version*: **1**
\ No newline at end of file
diff --git a/docs/_stories/credential_dumping.md b/docs/_stories/credential_dumping.md
index a3f454b5e4..99b0828fa0 100644
--- a/docs/_stories/credential_dumping.md
+++ b/docs/_stories/credential_dumping.md
@@ -35,8 +35,8 @@ The detection searches in this Analytic Story monitor access to the Local Securi
| Name | Technique | Type |
| ----------- | ----------- |--------------|
| [Access LSASS Memory for Dump Creation](/endpoint/access_lsass_memory_for_dump_creation/) | [LSASS Memory](/tags/#lsass-memory), [OS Credential Dumping](/tags/#os-credential-dumping) | TTP |
-| [Attempted Credential Dump From Registry via Reg exe](/endpoint/attempted_credential_dump_from_registry_via_reg_exe/) | [OS Credential Dumping](/tags/#os-credential-dumping), [Security Account Manager](/tags/#security-account-manager) | TTP |
| [Attempted Credential Dump From Registry via Reg exe](/endpoint/attempted_credential_dump_from_registry_via_reg_exe/) | [Security Account Manager](/tags/#security-account-manager), [OS Credential Dumping](/tags/#os-credential-dumping) | TTP |
+| [Attempted Credential Dump From Registry via Reg exe](/endpoint/attempted_credential_dump_from_registry_via_reg_exe/) | [OS Credential Dumping](/tags/#os-credential-dumping), [Security Account Manager](/tags/#security-account-manager) | TTP |
| [Create Remote Thread into LSASS](/endpoint/create_remote_thread_into_lsass/) | [LSASS Memory](/tags/#lsass-memory), [OS Credential Dumping](/tags/#os-credential-dumping) | TTP |
| [Creation of Shadow Copy](/endpoint/creation_of_shadow_copy/) | [NTDS](/tags/#ntds), [OS Credential Dumping](/tags/#os-credential-dumping) | TTP |
| [Creation of Shadow Copy with wmic and powershell](/endpoint/creation_of_shadow_copy_with_wmic_and_powershell/) | [NTDS](/tags/#ntds), [OS Credential Dumping](/tags/#os-credential-dumping) | TTP |
diff --git a/docs/_stories/linux_privilege_escalation.md b/docs/_stories/linux_privilege_escalation.md
index 56481b77b9..6854233057 100644
--- a/docs/_stories/linux_privilege_escalation.md
+++ b/docs/_stories/linux_privilege_escalation.md
@@ -62,6 +62,7 @@ Privilege escalation is a "land-and-expand" technique, wherein an adversary gain
| [Linux Sudo OR Su Execution](/endpoint/linux_sudo_or_su_execution/) | [Sudo and Sudo Caching](/tags/#sudo-and-sudo-caching), [Abuse Elevation Control Mechanism](/tags/#abuse-elevation-control-mechanism) | Hunting |
| [Linux Sudoers Tmp File Creation](/endpoint/linux_sudoers_tmp_file_creation/) | [Sudo and Sudo Caching](/tags/#sudo-and-sudo-caching), [Abuse Elevation Control Mechanism](/tags/#abuse-elevation-control-mechanism) | Anomaly |
| [Linux Visudo Utility Execution](/endpoint/linux_visudo_utility_execution/) | [Sudo and Sudo Caching](/tags/#sudo-and-sudo-caching), [Abuse Elevation Control Mechanism](/tags/#abuse-elevation-control-mechanism) | Anomaly |
+| [Linux pkexec Privilege Escalation](/endpoint/linux_pkexec_privilege_escalation/) | [Exploitation for Privilege Escalation](/tags/#exploitation-for-privilege-escalation) | TTP |
#### Reference
diff --git a/docs/_stories/masquerading_-_rename_system_utilities.md b/docs/_stories/masquerading_-_rename_system_utilities.md
index 02ad6b8023..83798bb57e 100644
--- a/docs/_stories/masquerading_-_rename_system_utilities.md
+++ b/docs/_stories/masquerading_-_rename_system_utilities.md
@@ -36,7 +36,6 @@ There will be false positives as some native Windows processes are moved or ran
| [Execution of File with Multiple Extensions](/endpoint/execution_of_file_with_multiple_extensions/) | [Masquerading](/tags/#masquerading), [Rename System Utilities](/tags/#rename-system-utilities) | TTP |
| [Sdelete Application Execution](/endpoint/sdelete_application_execution/) | [Data Destruction](/tags/#data-destruction), [File Deletion](/tags/#file-deletion), [Indicator Removal on Host](/tags/#indicator-removal-on-host) | TTP |
| [Suspicious MSBuild Rename](/endpoint/suspicious_msbuild_rename/) | [Masquerading](/tags/#masquerading), [Trusted Developer Utilities Proxy Execution](/tags/#trusted-developer-utilities-proxy-execution), [Rename System Utilities](/tags/#rename-system-utilities), [MSBuild](/tags/#msbuild) | TTP |
-| [Suspicious Rundll32 Rename](/endpoint/suspicious_rundll32_rename/) | [Signed Binary Proxy Execution](/tags/#signed-binary-proxy-execution), [Masquerading](/tags/#masquerading), [Rundll32](/tags/#rundll32), [Rename System Utilities](/tags/#rename-system-utilities) | Hunting |
| [Suspicious microsoft workflow compiler rename](/endpoint/suspicious_microsoft_workflow_compiler_rename/) | [Masquerading](/tags/#masquerading), [Trusted Developer Utilities Proxy Execution](/tags/#trusted-developer-utilities-proxy-execution), [Rename System Utilities](/tags/#rename-system-utilities) | Hunting |
| [Suspicious msbuild path](/endpoint/suspicious_msbuild_path/) | [Masquerading](/tags/#masquerading), [Trusted Developer Utilities Proxy Execution](/tags/#trusted-developer-utilities-proxy-execution), [Rename System Utilities](/tags/#rename-system-utilities), [MSBuild](/tags/#msbuild) | TTP |
| [System Process Running from Unexpected Location](/endpoint/system_process_running_from_unexpected_location/) | [Masquerading](/tags/#masquerading) | Anomaly |
diff --git a/docs/_stories/suspicious_rundll32_activity.md b/docs/_stories/suspicious_rundll32_activity.md
index 445e490895..fd9bebf848 100644
--- a/docs/_stories/suspicious_rundll32_activity.md
+++ b/docs/_stories/suspicious_rundll32_activity.md
@@ -37,7 +37,6 @@ One common adversary tactic is to bypass application control solutions via the r
| [Rundll32 Control RunDLL Hunt](/endpoint/rundll32_control_rundll_hunt/) | [Signed Binary Proxy Execution](/tags/#signed-binary-proxy-execution), [Rundll32](/tags/#rundll32) | Hunting |
| [Rundll32 Control RunDLL World Writable Directory](/endpoint/rundll32_control_rundll_world_writable_directory/) | [Signed Binary Proxy Execution](/tags/#signed-binary-proxy-execution), [Rundll32](/tags/#rundll32) | TTP |
| [Rundll32 with no Command Line Arguments with Network](/endpoint/rundll32_with_no_command_line_arguments_with_network/) | [Signed Binary Proxy Execution](/tags/#signed-binary-proxy-execution), [Rundll32](/tags/#rundll32) | TTP |
-| [Suspicious Rundll32 Rename](/endpoint/suspicious_rundll32_rename/) | [Signed Binary Proxy Execution](/tags/#signed-binary-proxy-execution), [Masquerading](/tags/#masquerading), [Rundll32](/tags/#rundll32), [Rename System Utilities](/tags/#rename-system-utilities) | Hunting |
| [Suspicious Rundll32 StartW](/endpoint/suspicious_rundll32_startw/) | [Signed Binary Proxy Execution](/tags/#signed-binary-proxy-execution), [Rundll32](/tags/#rundll32) | TTP |
| [Suspicious Rundll32 dllregisterserver](/endpoint/suspicious_rundll32_dllregisterserver/) | [Signed Binary Proxy Execution](/tags/#signed-binary-proxy-execution), [Rundll32](/tags/#rundll32) | TTP |
| [Suspicious Rundll32 no Command Line Arguments](/endpoint/suspicious_rundll32_no_command_line_arguments/) | [Signed Binary Proxy Execution](/tags/#signed-binary-proxy-execution), [Rundll32](/tags/#rundll32) | TTP |
diff --git a/docs/_stories/unusual_processes.md b/docs/_stories/unusual_processes.md
index 397120f593..4dd5c0cb17 100644
--- a/docs/_stories/unusual_processes.md
+++ b/docs/_stories/unusual_processes.md
@@ -38,7 +38,6 @@ In the event an unusual process is identified, it is imperative to better unders
| [Detect processes used for System Network Configuration Discovery](/endpoint/detect_processes_used_for_system_network_configuration_discovery/) | [System Network Configuration Discovery](/tags/#system-network-configuration-discovery) | TTP |
| [First time seen command line argument](/endpoint/first_time_seen_command_line_argument/) | [Command and Scripting Interpreter](/tags/#command-and-scripting-interpreter), [Indirect Command Execution](/tags/#indirect-command-execution) | Anomaly |
| [Rare Parent-Child Process Relationship](/endpoint/rare_parent-child_process_relationship/) | [Exploitation for Client Execution](/tags/#exploitation-for-client-execution), [Command and Scripting Interpreter](/tags/#command-and-scripting-interpreter), [Scheduled Task/Job](/tags/#scheduled-task/job), [Software Deployment Tools](/tags/#software-deployment-tools) | Anomaly |
-| [RunDLL Loading DLL By Ordinal](/endpoint/rundll_loading_dll_by_ordinal/) | [Signed Binary Proxy Execution](/tags/#signed-binary-proxy-execution), [Rundll32](/tags/#rundll32) | TTP |
| [Rundll32 Shimcache Flush](/endpoint/rundll32_shimcache_flush/) | [Modify Registry](/tags/#modify-registry) | TTP |
| [Suspicious Copy on System32](/endpoint/suspicious_copy_on_system32/) | [Rename System Utilities](/tags/#rename-system-utilities), [Masquerading](/tags/#masquerading) | TTP |
| [System Processes Run From Unexpected Locations](/endpoint/system_processes_run_from_unexpected_locations/) | [Masquerading](/tags/#masquerading), [Rename System Utilities](/tags/#rename-system-utilities) | TTP |
diff --git a/docs/index.markdown b/docs/index.markdown
index 3e11bdf01e..db6b78b036 100644
--- a/docs/index.markdown
+++ b/docs/index.markdown
@@ -9,12 +9,12 @@ header:
actions:
- label: "Download"
url: "https://splunkbase.splunk.com/app/3449/"
-excerpt: "Get the latest **FREE** Enterprise Security Content Update (ESCU) App with **750** detections for Splunk."
+excerpt: "Get the latest **FREE** Enterprise Security Content Update (ESCU) App with **749** detections for Splunk."
feature_row:
- image_path: /static/feature_detection.png
alt: "customizable"
title: "Detections"
- excerpt: "See all **750** Splunk Analytics built to find evil 😈."
+ excerpt: "See all **749** Splunk Analytics built to find evil 😈."
url: "/detections"
btn_class: "btn--primary"
btn_label: "Explore"
@@ -28,7 +28,7 @@ feature_row:
- image_path: /static/feature_playbooks.png
alt: "100% free"
title: "Playbooks"
- excerpt: "See all **26** sets of steps 🐾 to automatically response to a threat."
+ excerpt: "See all **31** automated investigation 🔭 and response 🛠 playbooks "
url: "/playbooks"
btn_class: "btn--primary"
btn_label: "Explore"
@@ -39,7 +39,7 @@ feature_row:
# Welcome to Splunk Security Content
-This project gives you access to our repository of Analytic Stories that are security guides which provide background on TTPs, mapped to the MITRE framework, the Lockheed Martin Kill Chain, and CIS controls. They include Splunk searches, machine-learning algorithms, and Splunk Phantom playbooks (where available)—all designed to work together to detect, investigate, and respond to threats.
+This project gives you access to our repository of Analytic Stories that are security guides which provide background on TTPs, mapped to the MITRE framework, the Lockheed Martin Kill Chain, and CIS controls. They include Splunk searches, machine-learning algorithms, and Splunk SOAR playbooks (where available)—all designed to work together to detect, investigate, and respond to threats.
[Try in Splunk Security Cloud](https://www.splunk.com/en_us/cyber-security.html){: .btn .btn--success}
diff --git a/playbooks/activedirectory_reset_password.yml b/playbooks/activedirectory_reset_password.yml
index 103fee5823..93bf24ddb3 100644
--- a/playbooks/activedirectory_reset_password.yml
+++ b/playbooks/activedirectory_reset_password.yml
@@ -11,8 +11,7 @@ references: []
app_list:
- "LDAP"
tags:
- platform_tags:
- - Response
+ platform_tags: []
playbook_fields:
- compromisedUserName
product:
diff --git a/playbooks/aws_disable_user_accounts.json b/playbooks/aws_disable_user_accounts.json
new file mode 100644
index 0000000000..fecc5e822e
--- /dev/null
+++ b/playbooks/aws_disable_user_accounts.json
@@ -0,0 +1,337 @@
+{
+ "blockly": false,
+ "blockly_xml": "",
+ "category": "Cloud",
+ "coa": {
+ "data": {
+ "description": "Disable a list of AWS IAM user accounts. After checking the list of accounts against an allowlist and confirming with an analyst, each account is disabled. The change can be reversed with the \"enable user\" action.",
+ "edges": [
+ {
+ "id": "port_0_to_port_2",
+ "sourceNode": "0",
+ "sourcePort": "0_out",
+ "targetNode": "2",
+ "targetPort": "2_in"
+ },
+ {
+ "conditions": [
+ {
+ "index": 1
+ }
+ ],
+ "id": "port_2_to_port_3",
+ "sourceNode": "2",
+ "sourcePort": "2_out",
+ "targetNode": "3",
+ "targetPort": "3_in"
+ },
+ {
+ "id": "port_3_to_port_4",
+ "sourceNode": "3",
+ "sourcePort": "3_out",
+ "targetNode": "4",
+ "targetPort": "4_in"
+ },
+ {
+ "conditions": [
+ {
+ "index": 0
+ }
+ ],
+ "id": "port_4_to_port_5",
+ "sourceNode": "4",
+ "sourcePort": "4_out",
+ "targetNode": "5",
+ "targetPort": "5_in"
+ },
+ {
+ "id": "port_5_to_port_1",
+ "sourceNode": "5",
+ "sourcePort": "5_out",
+ "targetNode": "1",
+ "targetPort": "1_in"
+ },
+ {
+ "conditions": [
+ {
+ "index": 0
+ }
+ ],
+ "id": "port_2_to_port_9",
+ "sourceNode": "2",
+ "sourcePort": "2_out",
+ "targetNode": "9",
+ "targetPort": "9_in"
+ }
+ ],
+ "hash": "5f38423da066d88fdb15072fc93346ea05c4fafe",
+ "nodes": {
+ "0": {
+ "data": {
+ "advanced": {
+ "join": []
+ },
+ "functionName": "on_start",
+ "id": "0",
+ "type": "start"
+ },
+ "errors": {},
+ "id": "0",
+ "type": "start",
+ "x": 150,
+ "y": 0
+ },
+ "1": {
+ "data": {
+ "advanced": {
+ "join": []
+ },
+ "functionId": 1,
+ "functionName": "on_finish",
+ "id": "1",
+ "type": "end"
+ },
+ "errors": {},
+ "id": "1",
+ "type": "end",
+ "userCode": "\n # This function is called after all actions are completed.\n # summary of all the action and/or all details of actions\n # can be collected here.\n\n # summary_json = phantom.get_summary()\n # if 'result' in summary_json:\n # for action_result in summary_json['result']:\n # if 'action_run_id' in action_result:\n # action_results = phantom.get_action_results(action_run_id=action_result['action_run_id'], result_data=False, flatten=False)\n # phantom.debug(action_results)\n\n",
+ "x": 20,
+ "y": 836
+ },
+ "2": {
+ "data": {
+ "advanced": {
+ "join": []
+ },
+ "conditions": [
+ {
+ "comparisons": [
+ {
+ "conditionIndex": 0,
+ "op": "in",
+ "param": "playbook_input:aws_username",
+ "value": "custom_list:aws_inactive_user_allowlist"
+ }
+ ],
+ "conditionIndex": 0,
+ "customName": "user in allowlist",
+ "logic": "and"
+ },
+ {
+ "comparisons": [
+ {
+ "conditionIndex": 1,
+ "op": "not in",
+ "param": "playbook_input:aws_username",
+ "value": "custom_list:aws_inactive_user_allowlist"
+ }
+ ],
+ "conditionIndex": 1,
+ "customName": "user not in allowlist",
+ "logic": "and"
+ }
+ ],
+ "functionId": 1,
+ "functionName": "filter_1",
+ "id": "2",
+ "type": "filter"
+ },
+ "errors": {},
+ "id": "2",
+ "type": "filter",
+ "x": 190,
+ "y": 148
+ },
+ "3": {
+ "data": {
+ "advanced": {
+ "customName": "aws_disable_user_check",
+ "customNameId": 0,
+ "join": []
+ },
+ "approver": "proyer",
+ "functionId": 1,
+ "functionName": "aws_disable_user_check",
+ "id": "3",
+ "message": "The following AWS user(s) were found to be inactive:\n\n```\n{0}\n```",
+ "parameters": [
+ "filtered-data:filter_1:condition_2:playbook_input:aws_username"
+ ],
+ "responseTime": 30,
+ "responses": [
+ {
+ "responseOptions": [
+ "Yes",
+ "No"
+ ],
+ "responsePrompt": "Should those user account(s) be disabled?",
+ "responseType": "yes/no"
+ }
+ ],
+ "type": "prompt"
+ },
+ "errors": {},
+ "id": "3",
+ "type": "prompt",
+ "x": 80,
+ "y": 328
+ },
+ "4": {
+ "data": {
+ "advanced": {
+ "join": []
+ },
+ "conditions": [
+ {
+ "comparisons": [
+ {
+ "conditionIndex": 0,
+ "op": "==",
+ "param": "aws_disable_user_check:action_result.summary.responses.0",
+ "value": "Yes"
+ }
+ ],
+ "conditionIndex": 0,
+ "display": "If",
+ "logic": "and",
+ "type": "if"
+ }
+ ],
+ "functionId": 1,
+ "functionName": "decision_1",
+ "id": "4",
+ "type": "decision"
+ },
+ "errors": {},
+ "id": "4",
+ "type": "decision",
+ "x": 80,
+ "y": 508
+ },
+ "5": {
+ "data": {
+ "action": "disable user",
+ "actionType": "contain",
+ "advanced": {
+ "join": []
+ },
+ "connector": "AWS IAM",
+ "connectorConfigs": [
+ "aws_iam"
+ ],
+ "connectorId": 46,
+ "connectorVersion": "v1",
+ "functionId": 1,
+ "functionName": "disable_user_1",
+ "id": "5",
+ "parameters": {
+ "disable_access_keys": true,
+ "username": "filtered-data:filter_1:condition_2:playbook_input:aws_username"
+ },
+ "requiredParameters": [
+ "username"
+ ],
+ "type": "action"
+ },
+ "errors": {},
+ "id": "5",
+ "type": "action",
+ "x": 0,
+ "y": 688
+ },
+ "9": {
+ "data": {
+ "advanced": {
+ "join": []
+ },
+ "customFunction": {
+ "draftMode": false,
+ "name": "indicator_tag",
+ "repoName": "community"
+ },
+ "functionId": 4,
+ "functionName": "indicator_tag_4",
+ "id": "9",
+ "selectMore": false,
+ "type": "utility",
+ "utilities": {
+ "indicator_tag": {
+ "description": "Tag an existing indicator record. Tags can be overwritten or appended.",
+ "fields": [
+ {
+ "dataTypes": [
+ "*"
+ ],
+ "description": "Specifies the indicator which the tag will be added to. Supports a string indicator value or an indicator id.",
+ "inputType": "item",
+ "label": "indicator",
+ "name": "indicator",
+ "placeholder": "my_indicator",
+ "renderType": "datapath",
+ "required": false
+ },
+ {
+ "dataTypes": [
+ "*"
+ ],
+ "description": "Comma separated list of tags. Tags should only contain characters Aa-Zz, 0-9, '-', and '_'.",
+ "inputType": "item",
+ "label": "tags",
+ "name": "tags",
+ "placeholder": "tag1,tag2,...,tagk",
+ "renderType": "datapath",
+ "required": false
+ },
+ {
+ "dataTypes": [],
+ "description": "Optional input. Either \"true\" or \"false\" with default as \"false\". If set to \"true\", existing tags on the indicator record will be replaced by the provided input. If set to \"false\", the new tags will be appended to the existing indicator tags.",
+ "inputType": "item",
+ "label": "overwrite",
+ "name": "overwrite",
+ "placeholder": "false",
+ "renderType": "datapath",
+ "required": false
+ }
+ ],
+ "label": "indicator_tag",
+ "name": "indicator_tag"
+ }
+ },
+ "utilityType": "custom_function",
+ "values": {
+ "indicator_tag": {
+ "indicator": "filtered-data:filter_1:condition_1:playbook_input:aws_username",
+ "overwrite": null,
+ "tags": "aws_inactive_user_allowlist"
+ }
+ }
+ },
+ "errors": {},
+ "id": "9",
+ "type": "utility",
+ "x": 260,
+ "y": 344
+ }
+ },
+ "notes": "This playbook uses the following Apps:\n - AWS IAM (disable user) [asset name = aws_iam] - disable AWS user accounts\n\nDeployment Notes:\n - works with the community playbook \"aws_find_inactive_users\" using the usernames discovered by that playbook\n - change the prompt block from admin to the correct analyst user or role\n - create a custom list called aws_inactive_user_allowlist. Any user names in that list will be ignored by this playbook."
+ },
+ "input_spec": [
+ {
+ "contains": [],
+ "description": "One or more AWS account usernames to disable",
+ "name": "aws_username"
+ }
+ ],
+ "output_spec": null,
+ "playbook_type": "data",
+ "python_version": "3",
+ "schema": "5.0.3",
+ "version": "5.0.1.66250"
+ },
+ "create_time": "2021-11-01T20:24:56.751674+00:00",
+ "draft_mode": false,
+ "labels": [
+ "*"
+ ],
+ "tags": []
+}
\ No newline at end of file
diff --git a/playbooks/aws_disable_user_accounts.png b/playbooks/aws_disable_user_accounts.png
new file mode 100644
index 0000000000..14d1760f5f
Binary files /dev/null and b/playbooks/aws_disable_user_accounts.png differ
diff --git a/playbooks/aws_disable_user_accounts.py b/playbooks/aws_disable_user_accounts.py
new file mode 100644
index 0000000000..201b4a0c54
--- /dev/null
+++ b/playbooks/aws_disable_user_accounts.py
@@ -0,0 +1,183 @@
+"""
+Disable a list of AWS IAM user accounts. After checking the list of accounts against an allowlist and confirming with an analyst, each account is disabled. The change can be reversed with the "enable user" action.
+"""
+
+
+import phantom.rules as phantom
+import json
+from datetime import datetime, timedelta
+
+
+def on_start(container):
+ phantom.debug('on_start() called')
+
+ # call 'filter_1' block
+ filter_1(container=container)
+
+ return
+
+def filter_1(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
+ phantom.debug("filter_1() called")
+
+ # collect filtered artifact ids and results for 'if' condition 1
+ matched_artifacts_1, matched_results_1 = phantom.condition(
+ container=container,
+ conditions=[
+ ["playbook_input:aws_username", "in", "custom_list:aws_inactive_user_allowlist"]
+ ],
+ name="filter_1:condition_1")
+
+ # call connected blocks if filtered artifacts or results
+ if matched_artifacts_1 or matched_results_1:
+ indicator_tag_4(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=matched_artifacts_1, filtered_results=matched_results_1)
+
+ # collect filtered artifact ids and results for 'if' condition 2
+ matched_artifacts_2, matched_results_2 = phantom.condition(
+ container=container,
+ conditions=[
+ ["playbook_input:aws_username", "not in", "custom_list:aws_inactive_user_allowlist"]
+ ],
+ name="filter_1:condition_2")
+
+ # call connected blocks if filtered artifacts or results
+ if matched_artifacts_2 or matched_results_2:
+ aws_disable_user_check(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=matched_artifacts_2, filtered_results=matched_results_2)
+
+ return
+
+
+def aws_disable_user_check(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
+ phantom.debug("aws_disable_user_check() called")
+
+ # set user and message variables for phantom.prompt call
+
+ user = "proyer"
+ message = """The following AWS user(s) were found to be inactive:\n\n```\n{0}\n```"""
+
+ # parameter list for template variable replacement
+ parameters = [
+ "filtered-data:filter_1:condition_2:playbook_input:aws_username"
+ ]
+
+ # responses
+ response_types = [
+ {
+ "prompt": "Should those user account(s) be disabled?",
+ "options": {
+ "type": "list",
+ "choices": [
+ "Yes",
+ "No"
+ ],
+ },
+ }
+ ]
+
+ phantom.prompt2(container=container, user=user, message=message, respond_in_mins=30, name="aws_disable_user_check", parameters=parameters, response_types=response_types, callback=decision_1)
+
+ return
+
+
+def decision_1(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
+ phantom.debug("decision_1() called")
+
+ # check for 'if' condition 1
+ found_match_1 = phantom.decision(
+ container=container,
+ conditions=[
+ ["aws_disable_user_check:action_result.summary.responses.0", "==", "Yes"]
+ ])
+
+ # call connected blocks if condition 1 matched
+ if found_match_1:
+ disable_user_1(action=action, success=success, container=container, results=results, handle=handle)
+ return
+
+ return
+
+
+def disable_user_1(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
+ phantom.debug("disable_user_1() called")
+
+ # phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED')))
+
+ filtered_input_0_aws_username = phantom.collect2(container=container, datapath=["filtered-data:filter_1:condition_2:playbook_input:aws_username"])
+
+ parameters = []
+
+ # build parameters list for 'disable_user_1' call
+ for filtered_input_0_aws_username_item in filtered_input_0_aws_username:
+ if filtered_input_0_aws_username_item[0] is not None:
+ parameters.append({
+ "username": filtered_input_0_aws_username_item[0],
+ "disable_access_keys": True,
+ })
+
+ ################################################################################
+ ## Custom Code Start
+ ################################################################################
+
+ # Write your custom code here...
+
+ ################################################################################
+ ## Custom Code End
+ ################################################################################
+
+ phantom.act("disable user", parameters=parameters, name="disable_user_1", assets=["aws_iam"])
+
+ return
+
+
+def indicator_tag_4(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
+ phantom.debug("indicator_tag_4() called")
+
+ filtered_input_0_aws_username = phantom.collect2(container=container, datapath=["filtered-data:filter_1:condition_1:playbook_input:aws_username"])
+
+ parameters = []
+
+ # build parameters list for 'indicator_tag_4' call
+ for filtered_input_0_aws_username_item in filtered_input_0_aws_username:
+ parameters.append({
+ "indicator": filtered_input_0_aws_username_item[0],
+ "tags": "aws_inactive_user_allowlist",
+ "overwrite": None,
+ })
+
+ ################################################################################
+ ## Custom Code Start
+ ################################################################################
+
+ # Write your custom code here...
+
+ ################################################################################
+ ## Custom Code End
+ ################################################################################
+
+ phantom.custom_function(custom_function="community/indicator_tag", parameters=parameters, name="indicator_tag_4")
+
+ return
+
+
+def on_finish(container, summary):
+ phantom.debug("on_finish() called")
+
+ ################################################################################
+ ## Custom Code Start
+ ################################################################################
+
+ # This function is called after all actions are completed.
+ # summary of all the action and/or all details of actions
+ # can be collected here.
+
+ # summary_json = phantom.get_summary()
+ # if 'result' in summary_json:
+ # for action_result in summary_json['result']:
+ # if 'action_run_id' in action_result:
+ # action_results = phantom.get_action_results(action_run_id=action_result['action_run_id'], result_data=False, flatten=False)
+ # phantom.debug(action_results)
+
+ ################################################################################
+ ## Custom Code End
+ ################################################################################
+
+ return
\ No newline at end of file
diff --git a/playbooks/aws_disable_user_accounts.yml b/playbooks/aws_disable_user_accounts.yml
new file mode 100644
index 0000000000..c09f103fed
--- /dev/null
+++ b/playbooks/aws_disable_user_accounts.yml
@@ -0,0 +1,20 @@
+name: AWS Disable User Accounts
+id: fc0edc75-ff2b-48c0-5f6f-63da6423fd63
+version: 1
+date: '2021-11-01'
+author: Philip Royer, Splunk
+type: Response
+description: "Disable a list of AWS IAM user accounts. After checking the list of accounts against an allowlist and confirming with an analyst, each account is disabled. The change can be reversed with the `enable user` action."
+playbook: aws_disable_user_accounts
+how_to_implement: "This playbook works with the community playbook aws_find_inactive_users using the usernames discovered by that playbook. Change the prompt block from admin to the correct analyst user or role. You should create a custom list called aws_inactive_user_allowlist. Any user names in that list will be ignored by this playbook."
+references:
+- https://www.splunk.com/en_us/blog/security/splunk-soar-playbooks-finding-and-disabling-inactive-users-on-aws.html
+app_list:
+- "AWS IAM"
+tags:
+ platform_tags:
+ - Cloud
+ playbook_fields:
+ - aws_username
+ product:
+ - Splunk SOAR
\ No newline at end of file
diff --git a/playbooks/aws_find_inactive_users.json b/playbooks/aws_find_inactive_users.json
new file mode 100644
index 0000000000..9d92624dc5
--- /dev/null
+++ b/playbooks/aws_find_inactive_users.json
@@ -0,0 +1,505 @@
+{
+ "blockly": false,
+ "blockly_xml": "",
+ "category": "Cloud",
+ "coa": {
+ "data": {
+ "description": "Find AWS accounts that have not been used for a long time (90 days by default). For each unused account, gather additional group and policy information and create an artifact to enable further automation or manual action.",
+ "edges": [
+ {
+ "id": "port_0_to_port_2",
+ "sourceNode": "0",
+ "sourcePort": "0_out",
+ "targetNode": "2",
+ "targetPort": "2_in"
+ },
+ {
+ "id": "port_0_to_port_3",
+ "sourceNode": "0",
+ "sourcePort": "0_out",
+ "targetNode": "3",
+ "targetPort": "3_in"
+ },
+ {
+ "id": "port_2_to_port_4",
+ "sourceNode": "2",
+ "sourcePort": "2_out",
+ "targetNode": "4",
+ "targetPort": "4_in"
+ },
+ {
+ "id": "port_3_to_port_4",
+ "sourceNode": "3",
+ "sourcePort": "3_out",
+ "targetNode": "4",
+ "targetPort": "4_in"
+ },
+ {
+ "conditions": [
+ {
+ "index": 0
+ }
+ ],
+ "id": "port_4_to_port_5",
+ "sourceNode": "4",
+ "sourcePort": "4_out",
+ "targetNode": "5",
+ "targetPort": "5_in"
+ },
+ {
+ "id": "port_5_to_port_6",
+ "sourceNode": "5",
+ "sourcePort": "5_out",
+ "targetNode": "6",
+ "targetPort": "6_in"
+ },
+ {
+ "id": "port_6_to_port_8",
+ "sourceNode": "6",
+ "sourcePort": "6_out",
+ "targetNode": "8",
+ "targetPort": "8_in"
+ },
+ {
+ "id": "port_8_to_port_1",
+ "sourceNode": "8",
+ "sourcePort": "8_out",
+ "targetNode": "1",
+ "targetPort": "1_in"
+ }
+ ],
+ "nodes": {
+ "0": {
+ "data": {
+ "advanced": {
+ "join": []
+ },
+ "functionName": "on_start",
+ "id": "0",
+ "type": "start"
+ },
+ "errors": {},
+ "id": "0",
+ "type": "start",
+ "x": 190,
+ "y": 0
+ },
+ "1": {
+ "data": {
+ "advanced": {
+ "join": []
+ },
+ "functionId": 1,
+ "functionName": "on_finish",
+ "id": "1",
+ "type": "end"
+ },
+ "errors": {},
+ "id": "1",
+ "type": "end",
+ "userCode": "\n # This function is called after all actions are completed.\n # summary of all the action and/or all details of actions\n # can be collected here.\n\n # summary_json = phantom.get_summary()\n # if 'result' in summary_json:\n # for action_result in summary_json['result']:\n # if 'action_run_id' in action_result:\n # action_results = phantom.get_action_results(action_run_id=action_result['action_run_id'], result_data=False, flatten=False)\n # phantom.debug(action_results)\n\n",
+ "x": 190,
+ "y": 1007
+ },
+ "2": {
+ "data": {
+ "action": "list users",
+ "actionType": "investigate",
+ "advanced": {
+ "customName": "list all accounts",
+ "customNameId": 0,
+ "description": "List all AWS IAM accounts, which will include the PasswordLastUsed field for us to filter on.",
+ "join": [],
+ "note": "List all AWS IAM accounts, which will include the PasswordLastUsed field for us to filter on."
+ },
+ "connector": "AWS IAM",
+ "connectorConfigs": [
+ "aws_iam"
+ ],
+ "connectorId": 20,
+ "connectorVersion": "v1",
+ "functionId": 1,
+ "functionName": "list_all_accounts",
+ "id": "2",
+ "requiredParameters": [],
+ "tab": "byAction",
+ "type": "action"
+ },
+ "errors": {},
+ "id": "2",
+ "type": "action",
+ "x": 0,
+ "y": 148
+ },
+ "3": {
+ "data": {
+ "advanced": {
+ "customName": "calculate start time",
+ "customNameId": 0,
+ "join": []
+ },
+ "customFunction": {
+ "draftMode": false,
+ "name": "datetime_modify",
+ "repoName": "community"
+ },
+ "functionId": 1,
+ "functionName": "calculate_start_time",
+ "id": "3",
+ "selectMore": false,
+ "type": "utility",
+ "utilities": {
+ "datetime_modify": {
+ "description": "Change a timestamp by adding or subtracting minutes, hours, or days.",
+ "fields": [
+ {
+ "dataTypes": [
+ ""
+ ],
+ "description": "The datetime to modify, which should be provided in a string format determined by input_format_string",
+ "inputType": "item",
+ "label": "input_datetime",
+ "name": "input_datetime",
+ "placeholder": "2020-06-27T14:53:08.219016Z",
+ "renderType": "datapath",
+ "required": false
+ },
+ {
+ "dataTypes": [],
+ "description": "The format string to use for the input according to the Python's datetime.strptime() formatting rules. If none is provided the default will be '%Y-%m-%dT%H:%M:%S.%fZ'. In addition to strptime() formats, the special format \"epoch\" can be used to accept unix epoch timestamps.",
+ "inputType": "item",
+ "label": "input_format_string",
+ "name": "input_format_string",
+ "placeholder": "%Y-%m-%dT%H:%M:%S.%fZ",
+ "renderType": "datapath",
+ "required": false
+ },
+ {
+ "dataTypes": [
+ ""
+ ],
+ "description": "Choose a unit to modify the date by, which must be either seconds, minutes, hours, or days. If none is provided the default will be 'minutes'",
+ "inputType": "item",
+ "label": "modification_unit",
+ "name": "modification_unit",
+ "placeholder": "minutes",
+ "renderType": "datapath",
+ "required": false
+ },
+ {
+ "dataTypes": [],
+ "description": "The number of seconds, minutes, hours, or days to add or subtract. Use a negative number such as -1.5 to subtract time. Defaults to zero.",
+ "inputType": "item",
+ "label": "amount_to_modify",
+ "name": "amount_to_modify",
+ "placeholder": "0",
+ "renderType": "datapath",
+ "required": false
+ },
+ {
+ "dataTypes": [],
+ "description": "The format string to use for the output according to the Python's datetime.strftime() formatting rules. If none is provided the default will be '%Y-%m-%dT%H:%M:%S.%fZ'.",
+ "inputType": "item",
+ "label": "output_format_string",
+ "name": "output_format_string",
+ "placeholder": "%Y-%m-%dT%H:%M:%S.%fZ",
+ "renderType": "datapath",
+ "required": false
+ }
+ ],
+ "label": "datetime_modify",
+ "name": "datetime_modify"
+ }
+ },
+ "utilityType": "custom_function",
+ "values": {
+ "datetime_modify": {
+ "amount_to_modify": "-90",
+ "input_datetime": null,
+ "input_format_string": null,
+ "modification_unit": "days",
+ "output_format_string": "%Y-%m-%dT%H:%M:%SZ"
+ }
+ }
+ },
+ "errors": {},
+ "id": "3",
+ "type": "utility",
+ "x": 340,
+ "y": 162.5
+ },
+ "4": {
+ "data": {
+ "advanced": {
+ "customName": "filter inactive accounts",
+ "customNameId": 0,
+ "description": "Compare the PasswordLastUsed field to the calculated start time to find unused accounts. Ignore accounts with no value for PasswordLastUsed. This will ignore all accounts with no passwords, such as accounts that only use API access keys.",
+ "join": [],
+ "note": "Compare the PasswordLastUsed field to the calculated start time to find unused accounts. Ignore accounts with no value for PasswordLastUsed. This will ignore all accounts with no passwords, such as accounts that only use API access keys."
+ },
+ "conditions": [
+ {
+ "comparisons": [
+ {
+ "conditionIndex": 0,
+ "op": "<",
+ "param": "list_all_accounts:action_result.data.*.PasswordLastUsed",
+ "value": "calculate_start_time:custom_function_result.data.datetime_string"
+ },
+ {
+ "conditionIndex": 0,
+ "op": "!=",
+ "param": "list_all_accounts:action_result.data.*.PasswordLastUsed",
+ "value": ""
+ }
+ ],
+ "conditionIndex": 0,
+ "logic": "and"
+ }
+ ],
+ "functionId": 1,
+ "functionName": "filter_inactive_accounts",
+ "id": "4",
+ "type": "filter"
+ },
+ "errors": {},
+ "id": "4",
+ "type": "filter",
+ "x": 230,
+ "y": 325
+ },
+ "5": {
+ "data": {
+ "action": "get user",
+ "actionType": "investigate",
+ "advanced": {
+ "customName": "get unused account information",
+ "customNameId": 0,
+ "description": "Use the \"get user\" action to gather more information about the unused accounts, including group membership and policy assignments.",
+ "join": [],
+ "note": "Use the \"get user\" action to gather more information about the unused accounts, including group membership and policy assignments."
+ },
+ "connector": "AWS IAM",
+ "connectorConfigs": [
+ "aws_iam"
+ ],
+ "connectorId": 20,
+ "connectorVersion": "v1",
+ "functionId": 1,
+ "functionName": "get_unused_account_information",
+ "id": "5",
+ "parameters": {
+ "username": "filtered-data:filter_inactive_accounts:condition_1:list_all_accounts:action_result.data.*.UserName"
+ },
+ "requiredParameters": [
+ "username"
+ ],
+ "tab": "byAction",
+ "type": "action"
+ },
+ "errors": {},
+ "id": "5",
+ "type": "action",
+ "x": 170,
+ "y": 505
+ },
+ "6": {
+ "data": {
+ "advanced": {
+ "customName": "save to artifacts",
+ "customNameId": 0,
+ "join": []
+ },
+ "customFunction": {
+ "draftMode": false,
+ "name": "artifact_create",
+ "repoName": "community"
+ },
+ "functionId": 2,
+ "functionName": "save_to_artifacts",
+ "id": "6",
+ "selectMore": false,
+ "type": "utility",
+ "utilities": {
+ "artifact_create": {
+ "description": "Create a new artifact with the specified attributes.",
+ "fields": [
+ {
+ "dataTypes": [
+ "phantom container id"
+ ],
+ "description": "Container which the artifact will be added to.",
+ "inputType": "item",
+ "label": "container",
+ "name": "container",
+ "placeholder": "container:id",
+ "renderType": "datapath",
+ "required": false
+ },
+ {
+ "dataTypes": [],
+ "description": "The name of the new artifact, which is optional and defaults to \"artifact\".",
+ "inputType": "item",
+ "label": "name",
+ "name": "name",
+ "placeholder": "artifact",
+ "renderType": "datapath",
+ "required": false
+ },
+ {
+ "dataTypes": [],
+ "description": "The label of the new artifact, which is optional and defaults to \"events\"",
+ "inputType": "item",
+ "label": "label",
+ "name": "label",
+ "placeholder": "events",
+ "renderType": "datapath",
+ "required": false
+ },
+ {
+ "dataTypes": [
+ ""
+ ],
+ "description": "The severity of the new artifact, which is optional and defaults to \"Medium\". Typically this is either \"High\", \"Medium\", or \"Low\".",
+ "inputType": "item",
+ "label": "severity",
+ "name": "severity",
+ "placeholder": "Medium",
+ "renderType": "datapath",
+ "required": false
+ },
+ {
+ "dataTypes": [],
+ "description": "The name of the CEF field to populate in the artifact, such as \"destinationAddress\" or \"sourceDnsDomain\". Required only if cef_value is provided.",
+ "inputType": "item",
+ "label": "cef_field",
+ "name": "cef_field",
+ "placeholder": "destinationAddress",
+ "renderType": "datapath",
+ "required": false
+ },
+ {
+ "dataTypes": [
+ "*"
+ ],
+ "description": "The value of the CEF field to populate in the artifact, such as the IP address, domain name, or file hash. Required only if cef_field is provided.",
+ "inputType": "item",
+ "label": "cef_value",
+ "name": "cef_value",
+ "placeholder": "192.0.2.192",
+ "renderType": "datapath",
+ "required": false
+ },
+ {
+ "dataTypes": [],
+ "description": "The CEF data type of the data in cef_value. For example, this could be \"ip\", \"hash\", or \"domain\". Optional.",
+ "inputType": "item",
+ "label": "cef_data_type",
+ "name": "cef_data_type",
+ "placeholder": "ip",
+ "renderType": "datapath",
+ "required": false
+ },
+ {
+ "dataTypes": [],
+ "description": "A comma-separated list of tags to apply to the created artifact, which is optional.",
+ "inputType": "item",
+ "label": "tags",
+ "name": "tags",
+ "placeholder": "tag1, tag2, tag3",
+ "renderType": "datapath",
+ "required": false
+ },
+ {
+ "dataTypes": [],
+ "description": "Either \"true\" or \"false\", depending on whether or not the new artifact should trigger the execution of any playbooks that are set to active on the label of the container the artifact will be added to. Optional and defaults to \"false\".",
+ "inputType": "item",
+ "label": "run_automation",
+ "name": "run_automation",
+ "placeholder": "false",
+ "renderType": "datapath",
+ "required": false
+ },
+ {
+ "dataTypes": [],
+ "description": "Optional parameter to modify any extra attributes of the artifact. Input_json will be merged with other inputs. In the event of a conflict, input_json will take precedence.",
+ "inputType": "item",
+ "label": "input_json",
+ "name": "input_json",
+ "placeholder": "{\"source_data_identifier\": \"1234\", \"data\": \"5678\"}",
+ "renderType": "datapath",
+ "required": false
+ }
+ ],
+ "label": "artifact_create",
+ "name": "artifact_create"
+ }
+ },
+ "utilityType": "custom_function",
+ "values": {
+ "artifact_create": {
+ "cef_data_type": "aws iam user name",
+ "cef_field": "awsUserName",
+ "cef_value": "get_unused_account_information:action_result.parameter.username",
+ "container": "container:id",
+ "input_json": null,
+ "label": "user",
+ "name": "Unused AWS Account",
+ "run_automation": "false",
+ "severity": null,
+ "tags": null
+ }
+ }
+ },
+ "errors": {},
+ "id": "6",
+ "type": "utility",
+ "x": 170,
+ "y": 697
+ },
+ "8": {
+ "data": {
+ "advanced": {
+ "join": []
+ },
+ "functionId": 1,
+ "functionName": "playbook_aws_disable_user_accounts_1",
+ "id": "8",
+ "inputs": {
+ "aws_username": {
+ "datapaths": [
+ "get_unused_account_information:action_result.parameter.username"
+ ],
+ "deduplicate": false
+ }
+ },
+ "playbookName": "aws_disable_user_accounts",
+ "playbookRepo": 1,
+ "playbookRepoName": "community",
+ "playbookType": "data",
+ "synchronous": false,
+ "type": "playbook"
+ },
+ "errors": {},
+ "id": "8",
+ "type": "playbook",
+ "x": 170,
+ "y": 845
+ }
+ },
+ "notes": "This playbook uses the following Apps:\n - AWS IAM (list users, get user) [asset name = aws_iam] - gather information about AWS IAM user accounts\n\nDeployment Notes:\n - this playbook is meant to run on a Timer, such as once per week\n - to adjust the lookback period away from the default, change the number of days to a different negative number in the \"calculate_start_time\" block\n - note that this playbook will ignore accounts where the password has never been used. These could be unused human accounts or they could be API accounts where the access keys are actively used."
+ },
+ "input_spec": null,
+ "output_spec": null,
+ "playbook_type": "automation",
+ "python_version": "3",
+ "schema": "5.0.3",
+ "version": "5.0.1.66250"
+ },
+ "create_time": "2021-11-01T20:36:59.131793+00:00",
+ "draft_mode": false,
+ "labels": [
+ "aws find inactive users"
+ ],
+ "tags": []
+}
\ No newline at end of file
diff --git a/playbooks/aws_find_inactive_users.png b/playbooks/aws_find_inactive_users.png
new file mode 100644
index 0000000000..9b33cb96ff
Binary files /dev/null and b/playbooks/aws_find_inactive_users.png differ
diff --git a/playbooks/aws_find_inactive_users.py b/playbooks/aws_find_inactive_users.py
new file mode 100644
index 0000000000..13f277f0a9
--- /dev/null
+++ b/playbooks/aws_find_inactive_users.py
@@ -0,0 +1,235 @@
+"""
+Find AWS accounts that have not been used for a long time (90 days by default). For each unused account, gather additional group and policy information and create an artifact to enable further automation or manual action.
+"""
+
+
+import phantom.rules as phantom
+import json
+from datetime import datetime, timedelta
+
+
+def on_start(container):
+ phantom.debug('on_start() called')
+
+ # call 'list_all_accounts' block
+ list_all_accounts(container=container)
+ # call 'calculate_start_time' block
+ calculate_start_time(container=container)
+
+ return
+
+def list_all_accounts(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
+ phantom.debug("list_all_accounts() called")
+
+ # phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED')))
+
+ ################################################################################
+ # List all AWS IAM accounts, which will include the PasswordLastUsed field for
+ # us to filter on.
+ ################################################################################
+
+ parameters = []
+
+ ################################################################################
+ ## Custom Code Start
+ ################################################################################
+
+ # Write your custom code here...
+
+ ################################################################################
+ ## Custom Code End
+ ################################################################################
+
+ phantom.act("list users", parameters=parameters, name="list_all_accounts", assets=["aws_iam"], callback=join_filter_inactive_accounts)
+
+ return
+
+
+def calculate_start_time(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
+ phantom.debug("calculate_start_time() called")
+
+ parameters = []
+
+ parameters.append({
+ "input_datetime": None,
+ "input_format_string": None,
+ "modification_unit": "days",
+ "amount_to_modify": -90,
+ "output_format_string": "%Y-%m-%dT%H:%M:%SZ",
+ })
+
+ ################################################################################
+ ## Custom Code Start
+ ################################################################################
+
+ # Write your custom code here...
+
+ ################################################################################
+ ## Custom Code End
+ ################################################################################
+
+ phantom.custom_function(custom_function="community/datetime_modify", parameters=parameters, name="calculate_start_time", callback=join_filter_inactive_accounts)
+
+ return
+
+
+def join_filter_inactive_accounts(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
+ phantom.debug("join_filter_inactive_accounts() called")
+
+ if phantom.completed(action_names=["list_all_accounts"], custom_function_names=["calculate_start_time"]):
+ # call connected block "filter_inactive_accounts"
+ filter_inactive_accounts(container=container, handle=handle)
+
+ return
+
+
+def filter_inactive_accounts(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
+ phantom.debug("filter_inactive_accounts() called")
+
+ ################################################################################
+ # Compare the PasswordLastUsed field to the calculated start time to find unused
+ # accounts. Ignore accounts with no value for PasswordLastUsed. This will ignore
+ # all accounts with no passwords, such as accounts that only use API access keys.
+ ################################################################################
+
+ # collect filtered artifact ids and results for 'if' condition 1
+ matched_artifacts_1, matched_results_1 = phantom.condition(
+ container=container,
+ logical_operator="and",
+ conditions=[
+ ["list_all_accounts:action_result.data.*.PasswordLastUsed", "<", "calculate_start_time:custom_function_result.data.datetime_string"],
+ ["list_all_accounts:action_result.data.*.PasswordLastUsed", "!=", ""]
+ ],
+ name="filter_inactive_accounts:condition_1")
+
+ # call connected blocks if filtered artifacts or results
+ if matched_artifacts_1 or matched_results_1:
+ get_unused_account_information(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=matched_artifacts_1, filtered_results=matched_results_1)
+
+ return
+
+
+def get_unused_account_information(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
+ phantom.debug("get_unused_account_information() called")
+
+ # phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED')))
+
+ ################################################################################
+ # Use the "get user" action to gather more information about the unused accounts,
+ # including group membership and policy assignments.
+ ################################################################################
+
+ filtered_result_0_data_filter_inactive_accounts = phantom.collect2(container=container, datapath=["filtered-data:filter_inactive_accounts:condition_1:list_all_accounts:action_result.data.*.UserName"])
+
+ parameters = []
+
+ # build parameters list for 'get_unused_account_information' call
+ for filtered_result_0_item_filter_inactive_accounts in filtered_result_0_data_filter_inactive_accounts:
+ if filtered_result_0_item_filter_inactive_accounts[0] is not None:
+ parameters.append({
+ "username": filtered_result_0_item_filter_inactive_accounts[0],
+ })
+
+ ################################################################################
+ ## Custom Code Start
+ ################################################################################
+
+ # Write your custom code here...
+
+ ################################################################################
+ ## Custom Code End
+ ################################################################################
+
+ phantom.act("get user", parameters=parameters, name="get_unused_account_information", assets=["aws_iam"], callback=save_to_artifacts)
+
+ return
+
+
+def save_to_artifacts(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
+ phantom.debug("save_to_artifacts() called")
+
+ id_value = container.get("id", None)
+ get_unused_account_information_result_data = phantom.collect2(container=container, datapath=["get_unused_account_information:action_result.parameter.username","get_unused_account_information:action_result.parameter.context.artifact_id"], action_results=results)
+
+ parameters = []
+
+ # build parameters list for 'save_to_artifacts' call
+ for get_unused_account_information_result_item in get_unused_account_information_result_data:
+ parameters.append({
+ "container": id_value,
+ "name": "Unused AWS Account",
+ "label": "user",
+ "severity": None,
+ "cef_field": "awsUserName",
+ "cef_value": get_unused_account_information_result_item[0],
+ "cef_data_type": "aws iam user name",
+ "tags": None,
+ "run_automation": "false",
+ "input_json": None,
+ })
+
+ ################################################################################
+ ## Custom Code Start
+ ################################################################################
+
+ # Write your custom code here...
+
+ ################################################################################
+ ## Custom Code End
+ ################################################################################
+
+ phantom.custom_function(custom_function="community/artifact_create", parameters=parameters, name="save_to_artifacts", callback=playbook_aws_disable_user_accounts_1)
+
+ return
+
+
+def playbook_aws_disable_user_accounts_1(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
+ phantom.debug("playbook_aws_disable_user_accounts_1() called")
+
+ get_unused_account_information_result_data = phantom.collect2(container=container, datapath=["get_unused_account_information:action_result.parameter.username"], action_results=results)
+
+ get_unused_account_information_parameter_username = [item[0] for item in get_unused_account_information_result_data]
+
+ inputs = {
+ "aws_username": get_unused_account_information_parameter_username,
+ }
+
+ ################################################################################
+ ## Custom Code Start
+ ################################################################################
+
+ # Write your custom code here...
+
+ ################################################################################
+ ## Custom Code End
+ ################################################################################
+
+ # call playbook "community/aws_disable_user_accounts", returns the playbook_run_id
+ playbook_run_id = phantom.playbook("community/aws_disable_user_accounts", container=container, inputs=inputs)
+
+ return
+
+
+def on_finish(container, summary):
+ phantom.debug("on_finish() called")
+
+ ################################################################################
+ ## Custom Code Start
+ ################################################################################
+
+ # This function is called after all actions are completed.
+ # summary of all the action and/or all details of actions
+ # can be collected here.
+
+ # summary_json = phantom.get_summary()
+ # if 'result' in summary_json:
+ # for action_result in summary_json['result']:
+ # if 'action_run_id' in action_result:
+ # action_results = phantom.get_action_results(action_run_id=action_result['action_run_id'], result_data=False, flatten=False)
+ # phantom.debug(action_results)
+
+ ################################################################################
+ ## Custom Code End
+ ################################################################################
+
+ return
\ No newline at end of file
diff --git a/playbooks/aws_find_inactive_users.yml b/playbooks/aws_find_inactive_users.yml
new file mode 100644
index 0000000000..7e0fabfdc3
--- /dev/null
+++ b/playbooks/aws_find_inactive_users.yml
@@ -0,0 +1,20 @@
+name: AWS Find Inactive Users
+id: fc0edc76-ff2b-48b0-5f6f-63da6423fd63
+version: 1
+date: '2021-11-01'
+author: Philip Royer, Splunk
+type: Investigation
+description: "Find AWS accounts that have not been used for a long time (90 days by default). For each unused account, gather additional group and policy information and create an artifact to enable further automation or manual action."
+playbook: aws_find_inactive_users
+how_to_implement: "This playbook is meant to run on a Timer, such as once per week. To adjust the lookback period away from the default, change the number of days to a different negative number in the 'calculate_start_time' block. Note that this playbook will ignore accounts where the password has never been used. These could be unused human accounts or they could be API accounts where the access keys are actively used."
+references:
+- https://www.splunk.com/en_us/blog/security/splunk-soar-playbooks-finding-and-disabling-inactive-users-on-aws.html
+app_list:
+- "AWS IAM"
+- "Phantom"
+tags:
+ platform_tags:
+ - Cloud
+ playbook_fields: []
+ product:
+ - Splunk SOAR
\ No newline at end of file
diff --git a/playbooks/block_indicators.yml b/playbooks/block_indicators.yml
index 9caf9a527c..b1686ff23d 100644
--- a/playbooks/block_indicators.yml
+++ b/playbooks/block_indicators.yml
@@ -13,8 +13,7 @@ app_list:
- "CarbonBlack Response"
- "OpenDNS Umbrella"
tags:
- platform_tags:
- - Response
+ platform_tags: []
playbook_fields:
- destinationDnsDomain
- destinationAddress
diff --git a/playbooks/crowdstrike_malware_triage.yml b/playbooks/crowdstrike_malware_triage.yml
index d24da6eed5..292b830119 100644
--- a/playbooks/crowdstrike_malware_triage.yml
+++ b/playbooks/crowdstrike_malware_triage.yml
@@ -11,8 +11,7 @@ references: []
app_list:
- "Crowdstrike OAuth"
tags:
- platform_tags:
- - Response
+ platform_tags: []
playbook_fields:
- filePath
- destinationAddress
diff --git a/playbooks/delete_detected_files.yml b/playbooks/delete_detected_files.yml
index db95e2d059..3e41aae6f5 100644
--- a/playbooks/delete_detected_files.yml
+++ b/playbooks/delete_detected_files.yml
@@ -15,8 +15,7 @@ tags:
- Active Directory Lateral Movement
detections:
- Executable File Written in Administrative SMB Share
- platform_tags:
- - Response
+ platform_tags: []
playbook_fields:
- filePath
- destinationAddress
diff --git a/playbooks/email_notification_for_malware.yml b/playbooks/email_notification_for_malware.yml
index a6c5b70531..6afbc2d9f8 100644
--- a/playbooks/email_notification_for_malware.yml
+++ b/playbooks/email_notification_for_malware.yml
@@ -14,8 +14,7 @@ app_list:
- "CarbonBlack Response"
- "SMTP"
tags:
- platform_tags:
- - Response
+ platform_tags: []
playbook_fields:
- fileHash
- vaultId
diff --git a/playbooks/internal_host_splunk_investigate_log4j.yml b/playbooks/internal_host_splunk_investigate_log4j.yml
index 2ca6f06546..59c4fec939 100644
--- a/playbooks/internal_host_splunk_investigate_log4j.yml
+++ b/playbooks/internal_host_splunk_investigate_log4j.yml
@@ -14,8 +14,7 @@ app_list:
tags:
analytic_story:
- Log4Shell CVE-2021-44228
- platform_tags:
- - Response
+ platform_tags: []
playbook_fields:
- hostName
- destinationAddress
diff --git a/playbooks/internal_host_ssh_investigate.yml b/playbooks/internal_host_ssh_investigate.yml
index e29776f7a3..0604c74c52 100644
--- a/playbooks/internal_host_ssh_investigate.yml
+++ b/playbooks/internal_host_ssh_investigate.yml
@@ -11,8 +11,7 @@ references: ["https://github.com/Neo23x0/Fenrir/blob/master/fenrir.sh"]
app_list:
- "SSH"
tags:
- platform_tags:
- - Response
+ platform_tags: []
playbook_fields: []
product:
- Splunk SOAR
diff --git a/playbooks/internal_host_ssh_log4j_investigate.yml b/playbooks/internal_host_ssh_log4j_investigate.yml
index cc5c6c3669..8d63eb9780 100644
--- a/playbooks/internal_host_ssh_log4j_investigate.yml
+++ b/playbooks/internal_host_ssh_log4j_investigate.yml
@@ -11,8 +11,7 @@ references: ["https://github.com/Neo23x0/Fenrir/blob/master/fenrir.sh"]
app_list:
- "SSH"
tags:
- platform_tags:
- - Response
+ platform_tags: []
playbook_fields: []
product:
- Splunk SOAR
diff --git a/playbooks/internal_host_ssh_log4j_respond.yml b/playbooks/internal_host_ssh_log4j_respond.yml
index bc5a2ee28e..e42634649b 100644
--- a/playbooks/internal_host_ssh_log4j_respond.yml
+++ b/playbooks/internal_host_ssh_log4j_respond.yml
@@ -11,8 +11,7 @@ references: ["https://github.com/Neo23x0/Fenrir/blob/master/fenrir.sh"]
app_list:
- "SSH"
tags:
- platform_tags:
- - Response
+ platform_tags: []
playbook_fields: []
product:
- Splunk SOAR
diff --git a/playbooks/internal_host_winrm_investigate.yml b/playbooks/internal_host_winrm_investigate.yml
index 55d778ae2c..8db5966975 100644
--- a/playbooks/internal_host_winrm_investigate.yml
+++ b/playbooks/internal_host_winrm_investigate.yml
@@ -11,8 +11,7 @@ references: []
app_list:
- "Windows Remote Management"
tags:
- platform_tags:
- - Response
+ platform_tags: []
playbook_fields: []
product:
- Splunk SOAR
diff --git a/playbooks/internal_host_winrm_log4j_investigate.yml b/playbooks/internal_host_winrm_log4j_investigate.yml
index 7c126b6e90..d90fc2c6f8 100644
--- a/playbooks/internal_host_winrm_log4j_investigate.yml
+++ b/playbooks/internal_host_winrm_log4j_investigate.yml
@@ -11,8 +11,7 @@ references: ["https://twitter.com/CyberRaiju/status/1469505677580124160"]
app_list:
- "Windows Remote Management"
tags:
- platform_tags:
- - Response
+ platform_tags: []
playbook_fields: []
product:
- Splunk SOAR
diff --git a/playbooks/internal_host_winrm_log4j_respond.yml b/playbooks/internal_host_winrm_log4j_respond.yml
index b5f3982f9a..1987ff3685 100644
--- a/playbooks/internal_host_winrm_log4j_respond.yml
+++ b/playbooks/internal_host_winrm_log4j_respond.yml
@@ -3,7 +3,7 @@ id: 32fd9db5-5201-4b2f-b2c2-9299c7b3495d
version: 1
date: '2021-12-14'
author: Kelby Shelton, Splunk
-type: Investigation
+type: Response
description: Published in response to CVE-2021-44228, this playbook accepts a list of hosts and filenames to remediate on the endpoint. If filenames are provided, the endpoints will be searched and then the user can approve deletion. Then the user is prompted to quarantine the endpoint.
playbook: internal_host_winrm_log4j_respond
how_to_implement: The winrm asset requires Administrator access to gather certain files.
@@ -11,8 +11,7 @@ references: []
app_list:
- "Windows Remote Management"
tags:
- platform_tags:
- - Response
+ platform_tags: []
playbook_fields: []
product:
- Splunk SOAR
diff --git a/playbooks/log4j_investigate.yml b/playbooks/log4j_investigate.yml
index 85d6586a42..01d648e0a4 100644
--- a/playbooks/log4j_investigate.yml
+++ b/playbooks/log4j_investigate.yml
@@ -3,7 +3,7 @@ id: e609d729-0076-421a-b8f7-9e545d000381
version: 2
date: '2021-12-14'
author: Philip Royer, Splunk
-type: Investigate
+type: Investigation
description: Published in response to CVE-2021-44228, this playbook and its sub-playbooks can be used to investigate and respond to attacks against hosts running vulnerable Java applications which use log4j. Between the parent playbook and seven sub-playbooks, each potentially compromised host found in Splunk Enteprise can be investigated and the risk can be mitigated using SSH for unix systems and WinRM for Windows systems.
playbook: log4j_investigate
how_to_implement: >
@@ -16,8 +16,7 @@ references:
app_list: []
tags:
platform_tags:
- - Response
- - log4j
+ - Log4J
analytic_story:
- Log4Shell CVE-2021-44228
detections:
diff --git a/playbooks/log4j_respond.yml b/playbooks/log4j_respond.yml
index f7021b753c..ddbb3e6c45 100644
--- a/playbooks/log4j_respond.yml
+++ b/playbooks/log4j_respond.yml
@@ -16,8 +16,7 @@ references:
app_list: []
tags:
platform_tags:
- - Response
- - log4j
+ - Log4J
analytic_story:
- Log4Shell CVE-2021-44228
detections:
diff --git a/playbooks/malware_hunt_and_contain.yml b/playbooks/malware_hunt_and_contain.yml
index fda6970320..9d60cffa84 100644
--- a/playbooks/malware_hunt_and_contain.yml
+++ b/playbooks/malware_hunt_and_contain.yml
@@ -14,8 +14,7 @@ app_list:
- "CarbonBlack Response"
- "VirusTotal"
tags:
- platform_tags:
- - Response
+ platform_tags: []
playbook_fields:
- fileHash
product:
diff --git a/playbooks/ransomware_investigate_and_contain.yml b/playbooks/ransomware_investigate_and_contain.yml
index daea66234a..49aaebbb68 100644
--- a/playbooks/ransomware_investigate_and_contain.yml
+++ b/playbooks/ransomware_investigate_and_contain.yml
@@ -22,7 +22,6 @@ tags:
- Conti Common Exec parameter
platform_tags:
- Ransomware
- - Response
playbook_fields:
- ComputerName
- Username
diff --git a/playbooks/start_investigation.json b/playbooks/start_investigation.json
new file mode 100644
index 0000000000..3ca8f38960
--- /dev/null
+++ b/playbooks/start_investigation.json
@@ -0,0 +1,350 @@
+{
+ "blockly": false,
+ "blockly_xml": "",
+ "category": "Use Cases",
+ "coa": {
+ "data": {
+ "description": "This is a playbook that is designed to be recommended within a workbook. If used in this manner, the playbook will assign the user that launched the playbook as the owner of the event, move the event status to \"Open\", and complete the workbook task where this playbook appears. If there is a task after the one where the playbook appears (within the same phase), it will set the next task to \"In Progress.\"",
+ "edges": [
+ {
+ "id": "port_0_to_port_2",
+ "sourceNode": "0",
+ "sourcePort": "0_out",
+ "targetNode": "2",
+ "targetPort": "2_in"
+ },
+ {
+ "id": "port_2_to_port_4",
+ "sourceNode": "2",
+ "sourcePort": "2_out",
+ "targetNode": "4",
+ "targetPort": "4_in"
+ },
+ {
+ "id": "port_6_to_port_7",
+ "sourceNode": "6",
+ "sourcePort": "6_out",
+ "targetNode": "7",
+ "targetPort": "7_in"
+ },
+ {
+ "conditions": [
+ {
+ "index": 0
+ }
+ ],
+ "id": "port_7_to_port_8",
+ "sourceNode": "7",
+ "sourcePort": "7_out",
+ "targetNode": "8",
+ "targetPort": "8_in"
+ },
+ {
+ "id": "port_8_to_port_9",
+ "sourceNode": "8",
+ "sourcePort": "8_out",
+ "targetNode": "9",
+ "targetPort": "9_in"
+ },
+ {
+ "conditions": [
+ {
+ "index": 1
+ }
+ ],
+ "id": "port_7_to_port_9",
+ "sourceNode": "7",
+ "sourcePort": "7_out",
+ "targetNode": "9",
+ "targetPort": "9_in"
+ },
+ {
+ "id": "port_9_to_port_1",
+ "sourceNode": "9",
+ "sourcePort": "9_out",
+ "targetNode": "1",
+ "targetPort": "1_in"
+ },
+ {
+ "conditions": [
+ {
+ "index": 0
+ }
+ ],
+ "id": "port_4_to_port_6",
+ "sourceNode": "4",
+ "sourcePort": "4_out",
+ "targetNode": "6",
+ "targetPort": "6_in"
+ }
+ ],
+ "hash": "486d29f43a4378a9e142395cf537970427d36ce8",
+ "nodes": {
+ "0": {
+ "data": {
+ "advanced": {
+ "join": []
+ },
+ "functionName": "on_start",
+ "id": "0",
+ "type": "start"
+ },
+ "errors": {},
+ "id": "0",
+ "type": "start",
+ "x": 100,
+ "y": 59.999999999999915
+ },
+ "1": {
+ "data": {
+ "advanced": {
+ "join": []
+ },
+ "functionId": 1,
+ "functionName": "on_finish",
+ "id": "1",
+ "type": "end"
+ },
+ "errors": {},
+ "id": "1",
+ "type": "end",
+ "userCode": "\n # This function is called after all actions are completed.\n # summary of all the action and/or all details of actions\n # can be collected here.\n\n # summary_json = phantom.get_summary()\n # if 'result' in summary_json:\n # for action_result in summary_json['result']:\n # if 'action_run_id' in action_result:\n # action_results = phantom.get_action_results(action_run_id=action_result['action_run_id'], result_data=False, flatten=False)\n # phantom.debug(action_results)\n\n",
+ "x": 80,
+ "y": 1060
+ },
+ "2": {
+ "data": {
+ "advanced": {
+ "customName": "get effective user",
+ "customNameId": 0,
+ "description": "Find the user and user type that launched this playbook.",
+ "join": [],
+ "note": "Find the user and user type that launched this playbook."
+ },
+ "functionId": 1,
+ "functionName": "get_effective_user",
+ "id": "2",
+ "inputParameters": [],
+ "outputVariables": [
+ "user_id",
+ "user_type"
+ ],
+ "type": "code"
+ },
+ "errors": {},
+ "id": "2",
+ "type": "code",
+ "userCode": "\n effective_user_id = phantom.get_effective_user()\n url = phantom.build_phantom_rest_url('ph_user', effective_user_id)\n response_json = phantom.requests.get(url, verify=False).json()\n \n get_effective_user__user_type = response_json['type']\n get_effective_user__user_id = effective_user_id\n \n\n",
+ "x": 80,
+ "y": 180
+ },
+ "4": {
+ "data": {
+ "advanced": {
+ "customName": "user decision",
+ "customNameId": 0,
+ "join": []
+ },
+ "conditions": [
+ {
+ "comparisons": [
+ {
+ "conditionIndex": 0,
+ "op": "!=",
+ "param": "get_effective_user:custom_function:user_type",
+ "value": "automation"
+ }
+ ],
+ "conditionIndex": 0,
+ "customName": "normal user",
+ "display": "If",
+ "logic": "and",
+ "type": "if"
+ }
+ ],
+ "functionId": 1,
+ "functionName": "user_decision",
+ "id": "4",
+ "type": "decision"
+ },
+ "errors": {},
+ "id": "4",
+ "type": "decision",
+ "x": 160,
+ "y": 320
+ },
+ "6": {
+ "data": {
+ "advanced": {
+ "customName": "set owner",
+ "customNameId": 0,
+ "description": "Sets the owner of the container as the user that launched this playbook",
+ "join": [],
+ "note": "Sets the owner of the container as the user that launched this playbook"
+ },
+ "functionId": 2,
+ "functionName": "set_owner",
+ "id": "6",
+ "inputParameters": [
+ "get_effective_user:custom_function:user_id"
+ ],
+ "outputVariables": [],
+ "type": "code"
+ },
+ "errors": {},
+ "id": "6",
+ "type": "code",
+ "userCode": "\n phantom.set_owner(container=container, user=get_effective_user__user_id)\n\n",
+ "x": 80,
+ "y": 500
+ },
+ "7": {
+ "data": {
+ "advanced": {
+ "customName": "status decision",
+ "customNameId": 0,
+ "description": "Determine if the status of the container should be changed.",
+ "join": [],
+ "note": "Determine if the status of the container should be changed."
+ },
+ "conditions": [
+ {
+ "comparisons": [
+ {
+ "conditionIndex": 0,
+ "op": "==",
+ "param": "container:status",
+ "value": "new"
+ }
+ ],
+ "conditionIndex": 0,
+ "customName": "set status",
+ "display": "If",
+ "logic": "and",
+ "type": "if"
+ },
+ {
+ "comparisons": [
+ {
+ "conditionIndex": 1,
+ "op": "==",
+ "param": "",
+ "value": ""
+ }
+ ],
+ "conditionIndex": 1,
+ "customName": "move on",
+ "display": "Else",
+ "logic": "and",
+ "type": "else"
+ }
+ ],
+ "functionId": 3,
+ "functionName": "status_decision",
+ "id": "7",
+ "type": "decision"
+ },
+ "errors": {},
+ "id": "7",
+ "type": "decision",
+ "x": 160,
+ "y": 640
+ },
+ "8": {
+ "data": {
+ "advanced": {
+ "join": [],
+ "refreshNotableData": true
+ },
+ "functionId": 1,
+ "functionName": "set_status_1",
+ "id": "8",
+ "selectMore": false,
+ "tab": "apis",
+ "type": "utility",
+ "utilities": {
+ "set_status": {
+ "description": "",
+ "fields": [
+ {
+ "choices": "notableStatus",
+ "description": "",
+ "label": "status",
+ "name": "status",
+ "placeholder": "Select status",
+ "renderType": "dropdown",
+ "required": true
+ },
+ {
+ "hidden": true,
+ "name": "container",
+ "required": false
+ },
+ {
+ "hidden": true,
+ "name": "trace",
+ "required": false
+ }
+ ],
+ "label": "set status",
+ "name": "set_status",
+ "refreshNotableData": true
+ }
+ },
+ "utilityType": "api",
+ "values": {
+ "set_status": {
+ "_internal": [
+ "container",
+ "trace"
+ ],
+ "status": "open"
+ }
+ }
+ },
+ "errors": {},
+ "id": "8",
+ "type": "utility",
+ "x": 0,
+ "y": 800
+ },
+ "9": {
+ "data": {
+ "advanced": {
+ "customName": "update workbook tasks",
+ "customNameId": 0,
+ "description": "Custom code to determine which task this playbook occurs in, complete that task, and set the status of the next task in the workbook (within the same phase) to \"In Progress\".",
+ "join": [],
+ "note": "Custom code to determine which task this playbook occurs in, complete that task, and set the status of the next task in the workbook (within the same phase) to \"In Progress\"."
+ },
+ "functionId": 3,
+ "functionName": "update_workbook_tasks",
+ "id": "9",
+ "inputParameters": [],
+ "outputVariables": [],
+ "type": "code"
+ },
+ "errors": {},
+ "id": "9",
+ "type": "code",
+ "userCode": "\n # Get current repo and playbook name\n current_scm = phantom.get_playbook_info()[0]['repo_name']\n current_playbook = phantom.get_playbook_info()[0]['name']\n task_order = None\n \n # Iterate through tasks on the current container\n for task in phantom.get_tasks(container=container):\n playbooks = task.get('data').get('suggestions').get('playbooks')\n if playbooks:\n for playbook in playbooks:\n # Check if the current container tasks contain a reference to this playbook.\n # If so, this is the task phase you want to mark as current\n if playbook['playbook'] == current_playbook and playbook['scm'] == current_scm:\n task_order = task['data']['order']\n status = task['data']['status']\n url = phantom.build_phantom_rest_url('workflow_task') + '/{}'.format(task['data']['id'])\n # If status is not started (statud id 0), move to in progress (status id 2) before moving to complete (status id 1)\n if status == 0:\n data = {'status': 2}\n phantom.requests.post(url, data=json.dumps(data), verify=False)\n data = {'status': 1} \n phantom.set_phase(container=container, phase=task['data']['phase']) \n phantom.requests.post(url, data=json.dumps(data), verify=False)\n \n # Iterate through the other tasks on the current container if a task was updated as indicated by the presence of \"task_order\" \n if task_order:\n for task in phantom.get_tasks(container=container):\n # If another task matches the updated task's order + 1, then update it as well\n if task['data']['order'] == task_order + 1:\n data = {'status': 2}\n url = phantom.build_phantom_rest_url('workflow_task') + '/{}'.format(task['data']['id'])\n phantom.requests.post(url, data=json.dumps(data), verify=False)\n\n\n",
+ "x": 60,
+ "y": 920
+ }
+ },
+ "notes": ""
+ },
+ "input_spec": null,
+ "output_spec": null,
+ "playbook_type": "automation",
+ "python_version": "3",
+ "schema": "5.0.3",
+ "version": "5.0.1.66250"
+ },
+ "create_time": "2021-10-07T16:24:36.298613+00:00",
+ "draft_mode": false,
+ "labels": [
+ "*"
+ ],
+ "tags": []
+}
\ No newline at end of file
diff --git a/playbooks/start_investigation.png b/playbooks/start_investigation.png
new file mode 100644
index 0000000000..e244853acb
Binary files /dev/null and b/playbooks/start_investigation.png differ
diff --git a/playbooks/start_investigation.py b/playbooks/start_investigation.py
new file mode 100644
index 0000000000..17771f6843
--- /dev/null
+++ b/playbooks/start_investigation.py
@@ -0,0 +1,229 @@
+"""
+This is a playbook that is designed to be recommended within a workbook. If used in this manner, the playbook will assign the user that launched the playbook as the owner of the event, move the event status to "Open", and complete the workbook task where this playbook appears. If there is a task after the one where the playbook appears (within the same phase), it will set the next task to "In Progress."
+"""
+
+
+import phantom.rules as phantom
+import json
+from datetime import datetime, timedelta
+
+
+def on_start(container):
+ phantom.debug('on_start() called')
+
+ # call 'get_effective_user' block
+ get_effective_user(container=container)
+
+ return
+
+def get_effective_user(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
+ phantom.debug("get_effective_user() called")
+
+ ################################################################################
+ # Find the user and user type that launched this playbook.
+ ################################################################################
+
+ get_effective_user__user_id = None
+ get_effective_user__user_type = None
+
+ ################################################################################
+ ## Custom Code Start
+ ################################################################################
+
+ effective_user_id = phantom.get_effective_user()
+ url = phantom.build_phantom_rest_url('ph_user', effective_user_id)
+ response_json = phantom.requests.get(url, verify=False).json()
+
+ get_effective_user__user_type = response_json['type']
+ get_effective_user__user_id = effective_user_id
+
+
+ ################################################################################
+ ## Custom Code End
+ ################################################################################
+
+ phantom.save_run_data(key="get_effective_user:user_id", value=json.dumps(get_effective_user__user_id))
+ phantom.save_run_data(key="get_effective_user:user_type", value=json.dumps(get_effective_user__user_type))
+
+ user_decision(container=container)
+
+ return
+
+
+def user_decision(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
+ phantom.debug("user_decision() called")
+
+ # check for 'if' condition 1
+ found_match_1 = phantom.decision(
+ container=container,
+ conditions=[
+ ["get_effective_user:custom_function:user_type", "!=", "automation"]
+ ])
+
+ # call connected blocks if condition 1 matched
+ if found_match_1:
+ set_owner(action=action, success=success, container=container, results=results, handle=handle)
+ return
+
+ return
+
+
+def set_owner(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
+ phantom.debug("set_owner() called")
+
+ ################################################################################
+ # Sets the owner of the container as the user that launched this playbook
+ ################################################################################
+
+ get_effective_user__user_id = json.loads(phantom.get_run_data(key="get_effective_user:user_id"))
+
+ ################################################################################
+ ## Custom Code Start
+ ################################################################################
+
+ phantom.set_owner(container=container, user=get_effective_user__user_id)
+
+ ################################################################################
+ ## Custom Code End
+ ################################################################################
+
+ status_decision(container=container)
+
+ return
+
+
+def status_decision(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
+ phantom.debug("status_decision() called")
+
+ ################################################################################
+ # Determine if the status of the container should be changed.
+ ################################################################################
+
+ status_value = container.get("status", None)
+
+ # check for 'if' condition 1
+ found_match_1 = phantom.decision(
+ container=container,
+ conditions=[
+ [status_value, "==", "new"]
+ ])
+
+ # call connected blocks if condition 1 matched
+ if found_match_1:
+ set_status_1(action=action, success=success, container=container, results=results, handle=handle)
+ return
+
+ # check for 'else' condition 2
+ join_update_workbook_tasks(action=action, success=success, container=container, results=results, handle=handle)
+
+ return
+
+
+def set_status_1(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
+ phantom.debug("set_status_1() called")
+
+ ################################################################################
+ ## Custom Code Start
+ ################################################################################
+
+ # Write your custom code here...
+
+ ################################################################################
+ ## Custom Code End
+ ################################################################################
+
+ phantom.set_status(container=container, status="open")
+
+ container = phantom.get_container(container.get('id', None))
+
+ join_update_workbook_tasks(container=container)
+
+ return
+
+
+def join_update_workbook_tasks(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
+ phantom.debug("join_update_workbook_tasks() called")
+
+ # call connected block "update_workbook_tasks"
+ update_workbook_tasks(container=container, handle=handle)
+
+ return
+
+
+def update_workbook_tasks(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
+ phantom.debug("update_workbook_tasks() called")
+
+ ################################################################################
+ # Custom code to determine which task this playbook occurs in, complete that task,
+ # and set the status of the next task in the workbook (within the same phase)
+ # to "In Progress".
+ ################################################################################
+
+ ################################################################################
+ ## Custom Code Start
+ ################################################################################
+
+ # Get current repo and playbook name
+ current_scm = phantom.get_playbook_info()[0]['repo_name']
+ current_playbook = phantom.get_playbook_info()[0]['name']
+ task_order = None
+
+ # Iterate through tasks on the current container
+ for task in phantom.get_tasks(container=container):
+ playbooks = task.get('data').get('suggestions').get('playbooks')
+ if playbooks:
+ for playbook in playbooks:
+ # Check if the current container tasks contain a reference to this playbook.
+ # If so, this is the task phase you want to mark as current
+ if playbook['playbook'] == current_playbook and playbook['scm'] == current_scm:
+ task_order = task['data']['order']
+ status = task['data']['status']
+ url = phantom.build_phantom_rest_url('workflow_task') + '/{}'.format(task['data']['id'])
+ # If status is not started (statud id 0), move to in progress (status id 2) before moving to complete (status id 1)
+ if status == 0:
+ data = {'status': 2}
+ phantom.requests.post(url, data=json.dumps(data), verify=False)
+ data = {'status': 1}
+ phantom.set_phase(container=container, phase=task['data']['phase'])
+ phantom.requests.post(url, data=json.dumps(data), verify=False)
+
+ # Iterate through the other tasks on the current container if a task was updated as indicated by the presence of "task_order"
+ if task_order:
+ for task in phantom.get_tasks(container=container):
+ # If another task matches the updated task's order + 1, then update it as well
+ if task['data']['order'] == task_order + 1:
+ data = {'status': 2}
+ url = phantom.build_phantom_rest_url('workflow_task') + '/{}'.format(task['data']['id'])
+ phantom.requests.post(url, data=json.dumps(data), verify=False)
+
+
+ ################################################################################
+ ## Custom Code End
+ ################################################################################
+
+ return
+
+
+def on_finish(container, summary):
+ phantom.debug("on_finish() called")
+
+ ################################################################################
+ ## Custom Code Start
+ ################################################################################
+
+ # This function is called after all actions are completed.
+ # summary of all the action and/or all details of actions
+ # can be collected here.
+
+ # summary_json = phantom.get_summary()
+ # if 'result' in summary_json:
+ # for action_result in summary_json['result']:
+ # if 'action_run_id' in action_result:
+ # action_results = phantom.get_action_results(action_run_id=action_result['action_run_id'], result_data=False, flatten=False)
+ # phantom.debug(action_results)
+
+ ################################################################################
+ ## Custom Code End
+ ################################################################################
+
+ return
\ No newline at end of file
diff --git a/playbooks/start_investigation.yml b/playbooks/start_investigation.yml
new file mode 100644
index 0000000000..d6f4890ba2
--- /dev/null
+++ b/playbooks/start_investigation.yml
@@ -0,0 +1,16 @@
+name: Start Investigation
+id: fc5adc76-f3ab-4cb0-5f6f-63bc3493fd46
+version: 1
+date: '2021-10-07'
+author: Kelby Shelton, Splunk
+type: Investigation
+description: Handle cases in Splunk SOAR with consistency that only automation can provide. This playbook ensures that cases are being assigned to analysts, and follow on work gets started.
+playbook: start_investigation
+how_to_implement: "This is a playbook that is designed to be recommended within a workbook. If used in this manner, the playbook will assign the user that launched the playbook as the owner of the event, move the event status to \"Open\", and complete the workbook task where this playbook appears. If there is a task after the one where the playbook appears (within the same phase), it will set the next task to \"In Progress.\""
+references: []
+app_list: []
+tags:
+ platform_tags: []
+ playbook_fields: []
+ product:
+ - Splunk SOAR
\ No newline at end of file
diff --git a/playbooks/threat_intel_investigate.json b/playbooks/threat_intel_investigate.json
new file mode 100644
index 0000000000..8fbd06b8fb
--- /dev/null
+++ b/playbooks/threat_intel_investigate.json
@@ -0,0 +1,629 @@
+{
+ "blockly": false,
+ "blockly_xml": "",
+ "category": "Use Cases",
+ "coa": {
+ "data": {
+ "description": "This parent playbook collects data and launches appropriate child playbooks to gather threat intelligence information about indicators. After the child playbooks have run, this playbook posts the notes to the container and prompts the analyst to add tags to each enriched indicator based on the intelligence provided.",
+ "edges": [
+ {
+ "id": "port_0_to_port_2",
+ "sourceNode": "0",
+ "sourcePort": "0_out",
+ "targetNode": "2",
+ "targetPort": "2_in"
+ },
+ {
+ "id": "port_2_to_port_3",
+ "sourceNode": "2",
+ "sourcePort": "2_out",
+ "targetNode": "3",
+ "targetPort": "3_in"
+ },
+ {
+ "conditions": [
+ {
+ "index": 0
+ }
+ ],
+ "id": "port_3_to_port_4",
+ "sourceNode": "3",
+ "sourcePort": "3_out",
+ "targetNode": "4",
+ "targetPort": "4_in"
+ },
+ {
+ "id": "port_4_to_port_5",
+ "sourceNode": "4",
+ "sourcePort": "4_out",
+ "targetNode": "5",
+ "targetPort": "5_in"
+ },
+ {
+ "id": "port_5_to_port_6",
+ "sourceNode": "5",
+ "sourcePort": "5_out",
+ "targetNode": "6",
+ "targetPort": "6_in"
+ },
+ {
+ "id": "port_6_to_port_8",
+ "sourceNode": "6",
+ "sourcePort": "6_out",
+ "targetNode": "8",
+ "targetPort": "8_in"
+ },
+ {
+ "id": "port_8_to_port_9",
+ "sourceNode": "8",
+ "sourcePort": "8_out",
+ "targetNode": "9",
+ "targetPort": "9_in"
+ },
+ {
+ "id": "port_9_to_port_11",
+ "sourceNode": "9",
+ "sourcePort": "9_out",
+ "targetNode": "11",
+ "targetPort": "11_in"
+ },
+ {
+ "id": "port_11_to_port_1",
+ "sourceNode": "11",
+ "sourcePort": "11_out",
+ "targetNode": "1",
+ "targetPort": "1_in"
+ }
+ ],
+ "hash": "75eb82d5a8ba447e780d5fce7569bcacc368b579",
+ "nodes": {
+ "0": {
+ "data": {
+ "advanced": {
+ "join": []
+ },
+ "functionName": "on_start",
+ "id": "0",
+ "type": "start"
+ },
+ "errors": {},
+ "id": "0",
+ "type": "start",
+ "x": 19.999999999999986,
+ "y": -4.618527782440651e-14
+ },
+ "1": {
+ "data": {
+ "advanced": {
+ "join": []
+ },
+ "functionId": 1,
+ "functionName": "on_finish",
+ "id": "1",
+ "type": "end"
+ },
+ "errors": {},
+ "id": "1",
+ "type": "end",
+ "userCode": "\n # This function is called after all actions are completed.\n # summary of all the action and/or all details of actions\n # can be collected here.\n\n # summary_json = phantom.get_summary()\n # if 'result' in summary_json:\n # for action_result in summary_json['result']:\n # if 'action_run_id' in action_result:\n # action_results = phantom.get_action_results(action_run_id=action_result['action_run_id'], result_data=False, flatten=False)\n # phantom.debug(action_results)\n\n",
+ "x": 19.999999999999986,
+ "y": 1060
+ },
+ "11": {
+ "data": {
+ "advanced": {
+ "customName": "tag indicators",
+ "customNameId": 0,
+ "join": []
+ },
+ "customFunction": {
+ "draftMode": false,
+ "name": "indicator_tag",
+ "repoName": "community"
+ },
+ "functionId": 5,
+ "functionName": "tag_indicators",
+ "id": "11",
+ "selectMore": false,
+ "type": "utility",
+ "utilities": {
+ "indicator_tag": {
+ "description": "Tag an existing indicator record. Tags can be overwritten or appended.",
+ "fields": [
+ {
+ "dataTypes": [
+ "*"
+ ],
+ "description": "Specifies the indicator which the tag will be added to. Supports a string indicator value or an indicator id.",
+ "inputType": "item",
+ "label": "indicator",
+ "name": "indicator",
+ "placeholder": "my_indicator",
+ "renderType": "datapath",
+ "required": false
+ },
+ {
+ "dataTypes": [
+ "*"
+ ],
+ "description": "Comma separated list of tags. Tags should only contain characters Aa-Zz, 0-9, '-', and '_'.",
+ "inputType": "item",
+ "label": "tags",
+ "name": "tags",
+ "placeholder": "tag1,tag2,...,tagk",
+ "renderType": "datapath",
+ "required": false
+ },
+ {
+ "dataTypes": [],
+ "description": "Optional input. Either \"true\" or \"false\" with default as \"false\". If set to \"true\", existing tags on the indicator record will be replaced by the provided input. If set to \"false\", the new tags will be appended to the existing indicator tags.",
+ "inputType": "item",
+ "label": "overwrite",
+ "name": "overwrite",
+ "placeholder": "false",
+ "renderType": "datapath",
+ "required": false
+ }
+ ],
+ "label": "indicator_tag",
+ "name": "indicator_tag"
+ }
+ },
+ "utilityType": "custom_function",
+ "values": {
+ "indicator_tag": {
+ "indicator": "process_responses:custom_function_result.data",
+ "overwrite": null,
+ "tags": "process_responses:custom_function_result.data"
+ }
+ }
+ },
+ "errors": {},
+ "id": "11",
+ "type": "utility",
+ "userCode": " \n # overwrite the parameters, extracting the indicator and tag for each result from process_responses\n parameters = []\n for item in process_responses__result:\n parameters.append({\n \"indicator\": item[0][0]['item'],\n \"tags\": item[0][1]['item'],\n \"overwrite\": None\n })\n\n",
+ "x": -1.4210854715202004e-14,
+ "y": 960
+ },
+ "2": {
+ "data": {
+ "advanced": {
+ "customName": "list investigate playbooks",
+ "customNameId": 0,
+ "join": []
+ },
+ "customFunction": {
+ "draftMode": false,
+ "name": "playbooks_list",
+ "repoName": "community"
+ },
+ "functionId": 1,
+ "functionName": "list_investigate_playbooks",
+ "id": "2",
+ "selectMore": false,
+ "type": "utility",
+ "utilities": {
+ "playbooks_list": {
+ "description": "List all playbooks matching the provided name, category, and tags. If no filters are provided, list all playbooks.",
+ "fields": [
+ {
+ "dataTypes": [],
+ "description": "Only return playbooks with the provided name.",
+ "inputType": "item",
+ "label": "name",
+ "name": "name",
+ "placeholder": "Playbook Name",
+ "renderType": "datapath",
+ "required": false
+ },
+ {
+ "dataTypes": [],
+ "description": "Only returns playbooks that match the provided category.",
+ "inputType": "item",
+ "label": "category",
+ "name": "category",
+ "placeholder": "Playbook Category",
+ "renderType": "datapath",
+ "required": false
+ },
+ {
+ "dataTypes": [],
+ "description": "Only return playbooks that contain ALL the provided tags. Multiple tags must be a comma-separated list.",
+ "inputType": "item",
+ "label": "tags",
+ "name": "tags",
+ "placeholder": "tag1,tag2,tag3",
+ "renderType": "datapath",
+ "required": false
+ },
+ {
+ "dataTypes": [],
+ "description": "Only return playbooks that exist in this repo.",
+ "inputType": "item",
+ "label": "repo",
+ "name": "repo",
+ "placeholder": "local",
+ "renderType": "datapath",
+ "required": false
+ },
+ {
+ "dataTypes": [],
+ "description": "Only return playbooks that match the provided type. Accepts 'automation', 'input' or 'data.'",
+ "inputType": "item",
+ "label": "playbook_type",
+ "name": "playbook_type",
+ "placeholder": "automation",
+ "renderType": "datapath",
+ "required": false
+ }
+ ],
+ "label": "playbooks_list",
+ "name": "playbooks_list"
+ }
+ },
+ "utilityType": "custom_function",
+ "values": {
+ "playbooks_list": {
+ "category": null,
+ "name": null,
+ "playbook_type": "input",
+ "repo": "local",
+ "tags": "investigate, threat_intel"
+ }
+ }
+ },
+ "errors": {},
+ "id": "2",
+ "type": "utility",
+ "x": 1.4210854715202004e-14,
+ "y": 99.99999999999997
+ },
+ "3": {
+ "data": {
+ "advanced": {
+ "join": []
+ },
+ "conditions": [
+ {
+ "comparisons": [
+ {
+ "conditionIndex": 0,
+ "op": "!=",
+ "param": "list_investigate_playbooks:custom_function_result.data.*.name",
+ "value": ""
+ }
+ ],
+ "conditionIndex": 0,
+ "display": "If",
+ "logic": "and",
+ "type": "if"
+ }
+ ],
+ "functionId": 1,
+ "functionName": "decision_1",
+ "id": "3",
+ "type": "decision"
+ },
+ "errors": {},
+ "id": "3",
+ "type": "decision",
+ "x": 80,
+ "y": 220
+ },
+ "4": {
+ "data": {
+ "advanced": {
+ "customName": "collect all indicators",
+ "customNameId": 0,
+ "join": []
+ },
+ "customFunction": {
+ "draftMode": false,
+ "name": "indicator_collect",
+ "repoName": "community"
+ },
+ "functionId": 2,
+ "functionName": "collect_all_indicators",
+ "id": "4",
+ "selectMore": false,
+ "type": "utility",
+ "utilities": {
+ "indicator_collect": {
+ "description": "Collect all indicators in a container and separate them by data type. Additional output data paths are created for each data type. Artifact scope is ignored. ",
+ "fields": [
+ {
+ "dataTypes": [
+ "phantom container id"
+ ],
+ "description": "The current container",
+ "inputType": "item",
+ "label": "container",
+ "name": "container",
+ "placeholder": "container:id",
+ "renderType": "datapath",
+ "required": false
+ }
+ ],
+ "label": "indicator_collect",
+ "name": "indicator_collect"
+ }
+ },
+ "utilityType": "custom_function",
+ "values": {
+ "indicator_collect": {
+ "container": "container:id"
+ }
+ }
+ },
+ "errors": {},
+ "id": "4",
+ "type": "utility",
+ "x": -1.4210854715202004e-14,
+ "y": 340
+ },
+ "5": {
+ "data": {
+ "advanced": {
+ "customName": "launch investigate playbooks",
+ "customNameId": 0,
+ "description": "Determine if any investigate playbooks are available with input types matching the indicators in the container, and synchronously launch any playbooks that are found. By default, this will look for local playbooks only, but it can be changed to use community playbooks.",
+ "join": [],
+ "note": "Determine if any investigate playbooks are available with input types matching the indicators in the container, and synchronously launch any playbooks that are found. By default, this will look for local playbooks only, but it can be changed to use community playbooks."
+ },
+ "functionId": 1,
+ "functionName": "launch_investigate_playbooks",
+ "id": "5",
+ "inputParameters": [
+ "list_investigate_playbooks:custom_function_result.data.*.full_name",
+ "list_investigate_playbooks:custom_function_result.data.*.input_spec",
+ "collect_all_indicators:custom_function_result.data.all_indicators.*.cef_value",
+ "collect_all_indicators:custom_function_result.data.all_indicators.*.data_types"
+ ],
+ "outputVariables": [
+ "playbooks_launched"
+ ],
+ "type": "code"
+ },
+ "errors": {},
+ "id": "5",
+ "type": "code",
+ "userCode": "\n playbooks_launched = []\n \n # loop through each playbook with the matching tags\n for playbook in list_investigate_playbooks_data:\n playbook_name = playbook[0]\n input_spec = playbook[1]\n phantom.debug(playbook_name)\n inputs_to_provide = []\n # loop through each input parameter, matching only the \"indicators\" input\n for param in input_spec:\n if param['name'] == 'indicators':\n # loop through each accepted data type\n for accepted_data_type in param['contains']:\n # loop through each indicator in the container and add any indicators with matching data types to the \"inputs_to_provide\" list\n for indicator in collect_all_indicators_data_all_indicators:\n for indicator_type in indicator[1]:\n # if the types match and the indicator value is not already in the inputs_to_provide then add it now \n if indicator_type == accepted_data_type and indicator[0] not in inputs_to_provide:\n inputs_to_provide.append(indicator[0])\n # back in the playbook loop, call the playbook if there are any inputs\n if inputs_to_provide != []:\n playbook_run_name = playbook_name.split('/')[1].replace(' ','_').lower()\n playbook_input = {'indicators': inputs_to_provide}\n phantom.debug('launching playbook {} with input {}'.format(playbook_name, playbook_input))\n phantom.playbook(playbook=playbook_name, container=container, name=playbook_run_name, inputs=playbook_input, callback=add_notes)\n playbooks_launched.append(playbook_run_name)\n \n launch_investigate_playbooks__playbooks_launched = playbooks_launched\n\n",
+ "x": -1.4210854715202004e-14,
+ "y": 439.99999999999994
+ },
+ "6": {
+ "data": {
+ "advanced": {
+ "customName": "add notes",
+ "customNameId": 0,
+ "description": "Add notes to the container if any were generated by playbooks from the previous step.",
+ "join": [],
+ "note": "Add notes to the container if any were generated by playbooks from the previous step."
+ },
+ "functionId": 2,
+ "functionName": "add_notes",
+ "id": "6",
+ "inputParameters": [
+ "launch_investigate_playbooks:custom_function:playbooks_launched",
+ ""
+ ],
+ "outputVariables": [],
+ "type": "code"
+ },
+ "errors": {},
+ "id": "6",
+ "type": "code",
+ "userCode": " \n playbooks_launched = launch_investigate_playbooks__playbooks_launched\n \n # return early if any of the launched playbooks are not completed\n if not phantom.completed(playbook_names=launch_investigate_playbooks__playbooks_launched):\n return\n \n playbook_outputs = []\n for playbook_name in playbooks_launched:\n note_title = phantom.collect2(container=container, datapath=[\"{}:playbook_output:note_title\".format(playbook_name)])[0][0]\n note_content = phantom.collect2(container=container, datapath=[\"{}:playbook_output:note_content\".format(playbook_name)])[0][0]\n phantom.add_note(container=container, content=note_content, note_format=\"markdown\", note_type=\"general\", title=note_title)\n \n \n #phantom.add_note(container=container, content=note, note_format=\"markdown\", note_type=\"general\", title='trustar test note')\n\n",
+ "x": 0,
+ "y": 600
+ },
+ "8": {
+ "customCode": "def threat_intel_indicator_review(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):\n phantom.debug(\"threat_intel_indicator_review() called\")\n\n # set user and message variables for phantom.prompt call\n\n user = \"Administrator\"\n message = \"\"\"For each indicator below, please review the gathered information and mark the indicator for further action.\"\"\"\n \n # add the note from each of the launched playbooks\n playbooks_launched = json.loads(phantom.get_run_data(key=\"launch_investigate_playbooks:playbooks_launched\"))\n for playbook in playbooks_launched:\n message += '\\n\\n'\n message += phantom.collect2(container=container, datapath=[\"{}:playbook_output:note_title\".format(playbook)])[0][0] + '\\n'\n message += phantom.collect2(container=container, datapath=[\"{}:playbook_output:note_content\".format(playbook)])[0][0] + '\\n'\n\n # no parameters to add\n parameters = []\n \n # create two questions and responses for each indicator. the first chooses a tag from a preconfigured list, and the second accepts a freeform comma-separated list of tags\n response_types = []\n all_indicators = phantom.collect2(container=container, datapath=[\"collect_all_indicators:custom_function_result.data.all_indicators.*.cef_value\",\"collect_all_indicators:custom_function_result.data.all_indicators.*.data_types\"])\n\n \n for index, indicator in enumerate(all_indicators):\n response_types.append({\n \"prompt\": \"Choose a tag for the indicator [{0}]\".format(indicator[0]),\n \"options\": {\n \"type\": \"list\",\n \"choices\": [\n \"Tag to block\",\n \"Tag as safe\",\n \"Do nothing\"]}})\n response_types.append({\n \"prompt\": \"Add any other comma-separated freeform tags for the indicator [{}], or enter 'n' to not add more tags.\".format(indicator[0]),\n \"options\": {\n \"type\": \"message\"}})\n\n phantom.prompt2(container=container, user=user, message=message, respond_in_mins=30, name=\"threat_intel_indicator_review\", parameters=parameters, response_types=response_types, callback=process_responses)\n\n return",
+ "data": {
+ "advanced": {
+ "customName": "threat intel indicator review",
+ "customNameId": 0,
+ "join": []
+ },
+ "approver": "proyer",
+ "functionId": 1,
+ "functionName": "threat_intel_indicator_review",
+ "id": "8",
+ "message": "a",
+ "parameters": [],
+ "responseTime": 30,
+ "responses": [],
+ "type": "prompt"
+ },
+ "errors": {},
+ "id": "8",
+ "type": "prompt",
+ "x": 80,
+ "y": 740
+ },
+ "9": {
+ "data": {
+ "advanced": {
+ "customName": "process responses",
+ "customNameId": 0,
+ "join": []
+ },
+ "customFunction": {
+ "draftMode": false,
+ "name": "passthrough",
+ "repoName": "community"
+ },
+ "functionId": 3,
+ "functionName": "process_responses",
+ "id": "9",
+ "selectMore": false,
+ "type": "utility",
+ "utilities": {
+ "passthrough": {
+ "description": "Return the inputs as outputs. This is useful for publishing pieces of data for other blocks in the playbook to use.",
+ "fields": [
+ {
+ "dataTypes": [
+ "*"
+ ],
+ "description": "",
+ "inputType": "list",
+ "label": "input_1",
+ "name": "input_1",
+ "placeholder": "",
+ "renderType": "datapath",
+ "required": false
+ },
+ {
+ "dataTypes": [
+ "*"
+ ],
+ "description": "",
+ "inputType": "list",
+ "label": "input_2",
+ "name": "input_2",
+ "placeholder": "",
+ "renderType": "datapath",
+ "required": false
+ },
+ {
+ "dataTypes": [
+ "*"
+ ],
+ "description": "",
+ "inputType": "list",
+ "label": "input_3",
+ "name": "input_3",
+ "placeholder": "",
+ "renderType": "datapath",
+ "required": false
+ },
+ {
+ "dataTypes": [
+ "*"
+ ],
+ "description": "",
+ "inputType": "list",
+ "label": "input_4",
+ "name": "input_4",
+ "placeholder": "",
+ "renderType": "datapath",
+ "required": false
+ },
+ {
+ "dataTypes": [
+ "*"
+ ],
+ "description": "",
+ "inputType": "list",
+ "label": "input_5",
+ "name": "input_5",
+ "placeholder": "",
+ "renderType": "datapath",
+ "required": false
+ },
+ {
+ "dataTypes": [
+ "*"
+ ],
+ "description": "",
+ "inputType": "list",
+ "label": "input_6",
+ "name": "input_6",
+ "placeholder": "",
+ "renderType": "datapath",
+ "required": false
+ },
+ {
+ "dataTypes": [
+ "*"
+ ],
+ "description": "",
+ "inputType": "list",
+ "label": "input_7",
+ "name": "input_7",
+ "placeholder": "",
+ "renderType": "datapath",
+ "required": false
+ },
+ {
+ "dataTypes": [
+ "*"
+ ],
+ "description": "",
+ "inputType": "list",
+ "label": "input_8",
+ "name": "input_8",
+ "placeholder": "",
+ "renderType": "datapath",
+ "required": false
+ },
+ {
+ "dataTypes": [
+ "*"
+ ],
+ "description": "",
+ "inputType": "list",
+ "label": "input_9",
+ "name": "input_9",
+ "placeholder": "",
+ "renderType": "datapath",
+ "required": false
+ },
+ {
+ "dataTypes": [
+ "*"
+ ],
+ "description": "",
+ "inputType": "list",
+ "label": "input_10",
+ "name": "input_10",
+ "placeholder": "",
+ "renderType": "datapath",
+ "required": false
+ }
+ ],
+ "label": "passthrough",
+ "name": "passthrough"
+ }
+ },
+ "utilityType": "custom_function",
+ "values": {
+ "passthrough": {
+ "input_1": "threat_intel_indicator_review:action_result.summary.responses",
+ "input_10": null,
+ "input_2": "collect_all_indicators:custom_function_result.data.all_indicators.*.cef_value",
+ "input_3": null,
+ "input_4": null,
+ "input_5": null,
+ "input_6": null,
+ "input_7": null,
+ "input_8": null,
+ "input_9": null
+ }
+ }
+ },
+ "errors": {},
+ "id": "9",
+ "type": "utility",
+ "userCode": "\n responses = threat_intel_indicator_review_summary_responses[0]\n indicator_values = collect_all_indicators_data_all_indicators___cef_value\n\n # lookup table to turn prompt responses into tags to add. \"Do nothing\" is not included, so no tags will be added\n response_to_tag_map = {\n \"Tag to block\": \"marked_for_block\",\n \"Tag as safe\": \"safe\"\n }\n\n # overwrite the parameters list with a list of one indicator and one tag per parameter dictionary \n parameters = []\n for indicator_index, indicator_value in enumerate(indicator_values):\n preconfigured_response = responses[indicator_index * 2]\n freeform_response = responses[indicator_index * 2 + 1]\n \n # handle the preconfigured responses\n if preconfigured_response in response_to_tag_map:\n phantom.comment(comment=\"Tagging the indicator {} with the preconfigured tag {}\".format(indicator_value, response_to_tag_map[preconfigured_response]))\n parameters.append({\"input_1\": [indicator_value, response_to_tag_map[preconfigured_response]]})\n elif preconfigured_response != 'Do nothing':\n phantom.error('The response {} was chosen for the indicator {}, but that response is not in the set of allowed responses.'.format(preconfigured_response, indicator_value))\n \n # handle the freeform responses\n if freeform_response.lower() not in ['n', 'none', 'na', 'n/a']:\n freeform_tags = freeform_response.replace(' ','').split(',')\n for tag in freeform_tags:\n phantom.comment(comment=\"Tagging the indicator {} with the freeform tag {}\".format(indicator_value, tag))\n parameters.append({\"input_1\": [indicator_value, tag]})\n\n",
+ "x": -1.4210854715202004e-14,
+ "y": 860
+ }
+ },
+ "notes": "Deployment Notes:\n - The prompt is currently sent to the Administrator role, but should be changed to the appropriate user and role\n - The \"list_investigate_playbooks\" block fetches playbooks from the local repository with the tags \n\"investigate\" and \"threat_intel\" by default. The playbook \"trustar_enrich_indicators\" is meant to be used by this playbook, and others can be created to replace it or work alongside it. To add a new input playbook, copy it to the local repository and give it the necessary tags. Define a playbook input with the name \"indicators\" and the data type matching the types of indicators the playbook can process.\n - To add a new tag to the preconfigured list, add it to the \"choices\" array in the \"threat_intel_indicator_review\" prompt block, and add it to the \"response_to_tag_map\" in \"process_indicators\".\n\n"
+ },
+ "input_spec": null,
+ "output_spec": null,
+ "playbook_type": "automation",
+ "python_version": "3",
+ "schema": "5.0.4",
+ "version": "5.1.0.70187"
+ },
+ "create_time": "2021-11-30T16:26:33.845191+00:00",
+ "draft_mode": false,
+ "labels": [
+ "events"
+ ],
+ "tags": []
+}
\ No newline at end of file
diff --git a/playbooks/threat_intel_investigate.png b/playbooks/threat_intel_investigate.png
new file mode 100644
index 0000000000..cb5ec97a49
Binary files /dev/null and b/playbooks/threat_intel_investigate.png differ
diff --git a/playbooks/threat_intel_investigate.py b/playbooks/threat_intel_investigate.py
new file mode 100644
index 0000000000..7c4a68c141
--- /dev/null
+++ b/playbooks/threat_intel_investigate.py
@@ -0,0 +1,361 @@
+"""
+This parent playbook collects data and launches appropriate child playbooks to gather threat intelligence information about indicators. After the child playbooks have run, this playbook posts the notes to the container and prompts the analyst to add tags to each enriched indicator based on the intelligence provided.
+"""
+
+
+import phantom.rules as phantom
+import json
+from datetime import datetime, timedelta
+
+
+def on_start(container):
+ phantom.debug('on_start() called')
+
+ # call 'list_investigate_playbooks' block
+ list_investigate_playbooks(container=container)
+
+ return
+
+def list_investigate_playbooks(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
+ phantom.debug("list_investigate_playbooks() called")
+
+ parameters = []
+
+ parameters.append({
+ "name": None,
+ "repo": "local",
+ "tags": "investigate, threat_intel",
+ "category": None,
+ "playbook_type": "input",
+ })
+
+ ################################################################################
+ ## Custom Code Start
+ ################################################################################
+
+ # Write your custom code here...
+
+ ################################################################################
+ ## Custom Code End
+ ################################################################################
+
+ phantom.custom_function(custom_function="community/playbooks_list", parameters=parameters, name="list_investigate_playbooks", callback=decision_1)
+
+ return
+
+
+def decision_1(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
+ phantom.debug("decision_1() called")
+
+ # check for 'if' condition 1
+ found_match_1 = phantom.decision(
+ container=container,
+ conditions=[
+ ["list_investigate_playbooks:custom_function_result.data.*.name", "!=", ""]
+ ])
+
+ # call connected blocks if condition 1 matched
+ if found_match_1:
+ collect_all_indicators(action=action, success=success, container=container, results=results, handle=handle)
+ return
+
+ return
+
+
+def collect_all_indicators(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
+ phantom.debug("collect_all_indicators() called")
+
+ id_value = container.get("id", None)
+
+ parameters = []
+
+ parameters.append({
+ "container": id_value,
+ })
+
+ ################################################################################
+ ## Custom Code Start
+ ################################################################################
+
+ # Write your custom code here...
+
+ ################################################################################
+ ## Custom Code End
+ ################################################################################
+
+ phantom.custom_function(custom_function="community/indicator_collect", parameters=parameters, name="collect_all_indicators", callback=launch_investigate_playbooks)
+
+ return
+
+
+def launch_investigate_playbooks(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
+ phantom.debug("launch_investigate_playbooks() called")
+
+ ################################################################################
+ # Determine if any investigate playbooks are available with input types matching
+ # the indicators in the container, and synchronously launch any playbooks that
+ # are found. By default, this will look for local playbooks only, but it can be
+ # changed to use community playbooks.
+ ################################################################################
+
+ list_investigate_playbooks_data = phantom.collect2(container=container, datapath=["list_investigate_playbooks:custom_function_result.data.*.full_name","list_investigate_playbooks:custom_function_result.data.*.input_spec"])
+ collect_all_indicators_data_all_indicators = phantom.collect2(container=container, datapath=["collect_all_indicators:custom_function_result.data.all_indicators.*.cef_value","collect_all_indicators:custom_function_result.data.all_indicators.*.data_types"])
+
+ list_investigate_playbooks_data___full_name = [item[0] for item in list_investigate_playbooks_data]
+ list_investigate_playbooks_data___input_spec = [item[1] for item in list_investigate_playbooks_data]
+ collect_all_indicators_data_all_indicators___cef_value = [item[0] for item in collect_all_indicators_data_all_indicators]
+ collect_all_indicators_data_all_indicators___data_types = [item[1] for item in collect_all_indicators_data_all_indicators]
+
+ launch_investigate_playbooks__playbooks_launched = None
+
+ ################################################################################
+ ## Custom Code Start
+ ################################################################################
+
+ playbooks_launched = []
+
+ # loop through each playbook with the matching tags
+ for playbook in list_investigate_playbooks_data:
+ playbook_name = playbook[0]
+ input_spec = playbook[1]
+ phantom.debug(playbook_name)
+ inputs_to_provide = []
+ # loop through each input parameter, matching only the "indicators" input
+ for param in input_spec:
+ if param['name'] == 'indicators':
+ # loop through each accepted data type
+ for accepted_data_type in param['contains']:
+ # loop through each indicator in the container and add any indicators with matching data types to the "inputs_to_provide" list
+ for indicator in collect_all_indicators_data_all_indicators:
+ for indicator_type in indicator[1]:
+ # if the types match and the indicator value is not already in the inputs_to_provide then add it now
+ if indicator_type == accepted_data_type and indicator[0] not in inputs_to_provide:
+ inputs_to_provide.append(indicator[0])
+ # back in the playbook loop, call the playbook if there are any inputs
+ if inputs_to_provide != []:
+ playbook_run_name = playbook_name.split('/')[1].replace(' ','_').lower()
+ playbook_input = {'indicators': inputs_to_provide}
+ phantom.debug('launching playbook {} with input {}'.format(playbook_name, playbook_input))
+ phantom.playbook(playbook=playbook_name, container=container, name=playbook_run_name, inputs=playbook_input, callback=add_notes)
+ playbooks_launched.append(playbook_run_name)
+
+ launch_investigate_playbooks__playbooks_launched = playbooks_launched
+
+ ################################################################################
+ ## Custom Code End
+ ################################################################################
+
+ phantom.save_run_data(key="launch_investigate_playbooks:playbooks_launched", value=json.dumps(launch_investigate_playbooks__playbooks_launched))
+
+ add_notes(container=container)
+
+ return
+
+
+def add_notes(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
+ phantom.debug("add_notes() called")
+
+ ################################################################################
+ # Add notes to the container if any were generated by playbooks from the previous
+ # step.
+ ################################################################################
+
+ launch_investigate_playbooks__playbooks_launched = json.loads(phantom.get_run_data(key="launch_investigate_playbooks:playbooks_launched"))
+
+ input_parameter_0 = ""
+
+ ################################################################################
+ ## Custom Code Start
+ ################################################################################
+
+ playbooks_launched = launch_investigate_playbooks__playbooks_launched
+
+ # return early if any of the launched playbooks are not completed
+ if not phantom.completed(playbook_names=launch_investigate_playbooks__playbooks_launched):
+ return
+
+ playbook_outputs = []
+ for playbook_name in playbooks_launched:
+ note_title = phantom.collect2(container=container, datapath=["{}:playbook_output:note_title".format(playbook_name)])[0][0]
+ note_content = phantom.collect2(container=container, datapath=["{}:playbook_output:note_content".format(playbook_name)])[0][0]
+ phantom.add_note(container=container, content=note_content, note_format="markdown", note_type="general", title=note_title)
+
+
+ #phantom.add_note(container=container, content=note, note_format="markdown", note_type="general", title='trustar test note')
+
+ ################################################################################
+ ## Custom Code End
+ ################################################################################
+
+ threat_intel_indicator_review(container=container)
+
+ return
+
+
+def threat_intel_indicator_review(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
+ phantom.debug("threat_intel_indicator_review() called")
+
+ # set user and message variables for phantom.prompt call
+
+ user = "Administrator"
+ message = """For each indicator below, please review the gathered information and mark the indicator for further action."""
+
+ # add the note from each of the launched playbooks
+ playbooks_launched = json.loads(phantom.get_run_data(key="launch_investigate_playbooks:playbooks_launched"))
+ for playbook in playbooks_launched:
+ message += '\n\n'
+ message += phantom.collect2(container=container, datapath=["{}:playbook_output:note_title".format(playbook)])[0][0] + '\n'
+ message += phantom.collect2(container=container, datapath=["{}:playbook_output:note_content".format(playbook)])[0][0] + '\n'
+
+ # no parameters to add
+ parameters = []
+
+ # create two questions and responses for each indicator. the first chooses a tag from a preconfigured list, and the second accepts a freeform comma-separated list of tags
+ response_types = []
+ all_indicators = phantom.collect2(container=container, datapath=["collect_all_indicators:custom_function_result.data.all_indicators.*.cef_value","collect_all_indicators:custom_function_result.data.all_indicators.*.data_types"])
+
+
+ for index, indicator in enumerate(all_indicators):
+ response_types.append({
+ "prompt": "Choose a tag for the indicator [{0}]".format(indicator[0]),
+ "options": {
+ "type": "list",
+ "choices": [
+ "Tag to block",
+ "Tag as safe",
+ "Do nothing"]}})
+ response_types.append({
+ "prompt": "Add any other comma-separated freeform tags for the indicator [{}], or enter 'n' to not add more tags.".format(indicator[0]),
+ "options": {
+ "type": "message"}})
+
+ phantom.prompt2(container=container, user=user, message=message, respond_in_mins=30, name="threat_intel_indicator_review", parameters=parameters, response_types=response_types, callback=process_responses)
+
+ return
+
+def process_responses(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
+ phantom.debug("process_responses() called")
+
+ threat_intel_indicator_review_result_data = phantom.collect2(container=container, datapath=["threat_intel_indicator_review:action_result.summary.responses","threat_intel_indicator_review:action_result.parameter.context.artifact_id"], action_results=results)
+ collect_all_indicators_data_all_indicators = phantom.collect2(container=container, datapath=["collect_all_indicators:custom_function_result.data.all_indicators.*.cef_value"])
+
+ threat_intel_indicator_review_summary_responses = [item[0] for item in threat_intel_indicator_review_result_data]
+ collect_all_indicators_data_all_indicators___cef_value = [item[0] for item in collect_all_indicators_data_all_indicators]
+
+ parameters = []
+
+ parameters.append({
+ "input_1": threat_intel_indicator_review_summary_responses,
+ "input_2": collect_all_indicators_data_all_indicators___cef_value,
+ "input_3": None,
+ "input_4": None,
+ "input_5": None,
+ "input_6": None,
+ "input_7": None,
+ "input_8": None,
+ "input_9": None,
+ "input_10": None,
+ })
+
+ ################################################################################
+ ## Custom Code Start
+ ################################################################################
+
+ responses = threat_intel_indicator_review_summary_responses[0]
+ indicator_values = collect_all_indicators_data_all_indicators___cef_value
+
+ # lookup table to turn prompt responses into tags to add. "Do nothing" is not included, so no tags will be added
+ response_to_tag_map = {
+ "Tag to block": "marked_for_block",
+ "Tag as safe": "safe"
+ }
+
+ # overwrite the parameters list with a list of one indicator and one tag per parameter dictionary
+ parameters = []
+ for indicator_index, indicator_value in enumerate(indicator_values):
+ preconfigured_response = responses[indicator_index * 2]
+ freeform_response = responses[indicator_index * 2 + 1]
+
+ # handle the preconfigured responses
+ if preconfigured_response in response_to_tag_map:
+ phantom.comment(comment="Tagging the indicator {} with the preconfigured tag {}".format(indicator_value, response_to_tag_map[preconfigured_response]))
+ parameters.append({"input_1": [indicator_value, response_to_tag_map[preconfigured_response]]})
+ elif preconfigured_response != 'Do nothing':
+ phantom.error('The response {} was chosen for the indicator {}, but that response is not in the set of allowed responses.'.format(preconfigured_response, indicator_value))
+
+ # handle the freeform responses
+ if freeform_response.lower() not in ['n', 'none', 'na', 'n/a']:
+ freeform_tags = freeform_response.replace(' ','').split(',')
+ for tag in freeform_tags:
+ phantom.comment(comment="Tagging the indicator {} with the freeform tag {}".format(indicator_value, tag))
+ parameters.append({"input_1": [indicator_value, tag]})
+
+ ################################################################################
+ ## Custom Code End
+ ################################################################################
+
+ phantom.custom_function(custom_function="community/passthrough", parameters=parameters, name="process_responses", callback=tag_indicators)
+
+ return
+
+
+def tag_indicators(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
+ phantom.debug("tag_indicators() called")
+
+ process_responses__result = phantom.collect2(container=container, datapath=["process_responses:custom_function_result.data"])
+
+ parameters = []
+
+ # build parameters list for 'tag_indicators' call
+ for process_responses__result_item in process_responses__result:
+ parameters.append({
+ "tags": process_responses__result_item[0],
+ "indicator": process_responses__result_item[0],
+ "overwrite": None,
+ })
+
+ ################################################################################
+ ## Custom Code Start
+ ################################################################################
+
+ # overwrite the parameters, extracting the indicator and tag for each result from process_responses
+ parameters = []
+ for item in process_responses__result:
+ parameters.append({
+ "indicator": item[0][0]['item'],
+ "tags": item[0][1]['item'],
+ "overwrite": None
+ })
+
+ ################################################################################
+ ## Custom Code End
+ ################################################################################
+
+ phantom.custom_function(custom_function="community/indicator_tag", parameters=parameters, name="tag_indicators")
+
+ return
+
+
+def on_finish(container, summary):
+ phantom.debug("on_finish() called")
+
+ ################################################################################
+ ## Custom Code Start
+ ################################################################################
+
+ # This function is called after all actions are completed.
+ # summary of all the action and/or all details of actions
+ # can be collected here.
+
+ # summary_json = phantom.get_summary()
+ # if 'result' in summary_json:
+ # for action_result in summary_json['result']:
+ # if 'action_run_id' in action_result:
+ # action_results = phantom.get_action_results(action_run_id=action_result['action_run_id'], result_data=False, flatten=False)
+ # phantom.debug(action_results)
+
+ ################################################################################
+ ## Custom Code End
+ ################################################################################
+
+ return
\ No newline at end of file
diff --git a/playbooks/threat_intel_investigate.yml b/playbooks/threat_intel_investigate.yml
new file mode 100644
index 0000000000..65a3095b34
--- /dev/null
+++ b/playbooks/threat_intel_investigate.yml
@@ -0,0 +1,18 @@
+name: Threat Intel Investigate
+id: fc5adc76-fd2b-48b0-5f6f-63bc3493fd46
+version: 1
+date: '2021-11-30'
+author: Philip Royer, Splunk
+type: Investigation
+description: "This parent playbook collects data and launches appropriate child playbooks to gather threat intelligence information about indicators. After the child playbooks have run, this playbook posts the notes to the container and prompts the analyst to add tags to each enriched indicator based on the intelligence provided."
+playbook: threat_intel_investigate
+how_to_implement: "The prompt is currently sent to the Administrator role, but should be changed to the appropriate user and role. The \"list_investigate_playbooks\" block fetches playbooks from the local repository with the tags \"investigate\" and \"threat_intel\" by default. The playbook \"trustar_enrich_indicators\" is meant to be used by this playbook, and others can be created to replace it or work alongside it. To add a new input playbook, copy it to the local repository and give it the necessary tags. Define a playbook input with the name \"indicators\" and the data type matching the types of indicators the playbook can process. To add a new tag to the preconfigured list, add it to the \"choices\" array in the \"threat_intel_indicator_review\" prompt block, and add it to the \"response_to_tag_map\" in \"process_indicators\"."
+references:
+- https://www.splunk.com/en_us/blog/security/TruSTAR-Enrich-Indicators-soar-in-seconds.html
+app_list: []
+tags:
+ platform_tags:
+ - threat_intel
+ playbook_fields: []
+ product:
+ - Splunk SOAR
\ No newline at end of file
diff --git a/playbooks/trustar_enrich_indicators.json b/playbooks/trustar_enrich_indicators.json
new file mode 100644
index 0000000000..06a41bb7e0
--- /dev/null
+++ b/playbooks/trustar_enrich_indicators.json
@@ -0,0 +1,328 @@
+{
+ "blockly": false,
+ "blockly_xml": "",
+ "category": "Use Cases",
+ "coa": {
+ "data": {
+ "description": "Use TruSTAR to gather threat information about indicators in a SOAR event. Tag the indicators with the normalized priority score from TruSTAR and summarize the findings in an analyst note. This playbook is meant to be used as a child playbook executed by a parent playbook such as \"threat_intel_investigate\".",
+ "edges": [
+ {
+ "id": "port_0_to_port_2",
+ "sourceNode": "0",
+ "sourcePort": "0_out",
+ "targetNode": "2",
+ "targetPort": "2_in"
+ },
+ {
+ "id": "port_2_to_port_5",
+ "sourceNode": "2",
+ "sourcePort": "2_out",
+ "targetNode": "5",
+ "targetPort": "5_in"
+ },
+ {
+ "id": "port_3_to_port_1",
+ "sourceNode": "3",
+ "sourcePort": "3_out",
+ "targetNode": "1",
+ "targetPort": "1_in"
+ },
+ {
+ "id": "port_5_to_port_1",
+ "sourceNode": "5",
+ "sourcePort": "5_out",
+ "targetNode": "1",
+ "targetPort": "1_in"
+ },
+ {
+ "id": "port_2_to_port_6",
+ "sourceNode": "2",
+ "sourcePort": "2_out",
+ "targetNode": "6",
+ "targetPort": "6_in"
+ },
+ {
+ "conditions": [
+ {
+ "index": 0
+ }
+ ],
+ "id": "port_6_to_port_3",
+ "sourceNode": "6",
+ "sourcePort": "6_out",
+ "targetNode": "3",
+ "targetPort": "3_in"
+ }
+ ],
+ "hash": "429eb175623d7202cbfce355d56512786b3bc52f",
+ "nodes": {
+ "0": {
+ "data": {
+ "advanced": {
+ "join": []
+ },
+ "functionName": "on_start",
+ "id": "0",
+ "type": "start"
+ },
+ "errors": {},
+ "id": "0",
+ "type": "start",
+ "x": 190,
+ "y": 0
+ },
+ "1": {
+ "data": {
+ "advanced": {
+ "join": []
+ },
+ "functionId": 1,
+ "functionName": "on_finish",
+ "id": "1",
+ "type": "end"
+ },
+ "errors": {},
+ "id": "1",
+ "type": "end",
+ "userCode": "\n # This function is called after all actions are completed.\n # summary of all the action and/or all details of actions\n # can be collected here.\n\n # summary_json = phantom.get_summary()\n # if 'result' in summary_json:\n # for action_result in summary_json['result']:\n # if 'action_run_id' in action_result:\n # action_results = phantom.get_action_results(action_run_id=action_result['action_run_id'], result_data=False, flatten=False)\n # phantom.debug(action_results)\n\n",
+ "x": 190,
+ "y": 684
+ },
+ "2": {
+ "data": {
+ "action": "indicator reputation",
+ "actionType": "investigate",
+ "advanced": {
+ "customName": "indicator reputation",
+ "customNameId": 0,
+ "description": "Query the Indicator API in TruSTAR to find threat intelligence scores and attributes about each of the indicators passed into the playbook.",
+ "join": [],
+ "note": "Query the Indicator API in TruSTAR to find threat intelligence scores and attributes about each of the indicators passed into the playbook."
+ },
+ "connector": "TruSTAR",
+ "connectorConfigs": [
+ "trustar"
+ ],
+ "connectorId": "536ae6e0-72b2-4ae1-9520-810acaddd2d1",
+ "connectorVersion": "v1",
+ "functionId": 1,
+ "functionName": "indicator_reputation",
+ "id": "2",
+ "parameters": {
+ "indicator_value": "playbook_input:indicators"
+ },
+ "requiredParameters": [
+ "indicator_value"
+ ],
+ "type": "action"
+ },
+ "errors": {},
+ "id": "2",
+ "type": "action",
+ "x": 170,
+ "y": 148
+ },
+ "3": {
+ "data": {
+ "advanced": {
+ "customName": "tag indicator with priority score",
+ "customNameId": 0,
+ "join": []
+ },
+ "customFunction": {
+ "draftMode": false,
+ "name": "indicator_tag",
+ "repoName": "community"
+ },
+ "functionId": 1,
+ "functionName": "tag_indicator_with_priority_score",
+ "id": "3",
+ "selectMore": false,
+ "type": "utility",
+ "utilities": {
+ "indicator_tag": {
+ "description": "Tag an existing indicator record. Tags can be overwritten or appended.",
+ "fields": [
+ {
+ "dataTypes": [
+ "*"
+ ],
+ "description": "Specifies the indicator which the tag will be added to. Supports a string indicator value or an indicator id.",
+ "inputType": "item",
+ "label": "indicator",
+ "name": "indicator",
+ "placeholder": "my_indicator",
+ "renderType": "datapath",
+ "required": false
+ },
+ {
+ "dataTypes": [
+ "*"
+ ],
+ "description": "Comma separated list of tags. Tags should only contain characters Aa-Zz, 0-9, '-', and '_'.",
+ "inputType": "item",
+ "label": "tags",
+ "name": "tags",
+ "placeholder": "tag1,tag2,...,tagk",
+ "renderType": "datapath",
+ "required": false
+ },
+ {
+ "dataTypes": [],
+ "description": "Optional input. Either \"true\" or \"false\" with default as \"false\". If set to \"true\", existing tags on the indicator record will be replaced by the provided input. If set to \"false\", the new tags will be appended to the existing indicator tags.",
+ "inputType": "item",
+ "label": "overwrite",
+ "name": "overwrite",
+ "placeholder": "false",
+ "renderType": "datapath",
+ "required": false
+ }
+ ],
+ "label": "indicator_tag",
+ "name": "indicator_tag"
+ }
+ },
+ "utilityType": "custom_function",
+ "values": {
+ "indicator_tag": {
+ "indicator": "filtered-data:indicator_found:condition_1:indicator_reputation:action_result.parameter.indicator_value",
+ "overwrite": null,
+ "tags": "filtered-data:indicator_found:condition_1:indicator_reputation:action_result.data.*.priorityScore"
+ }
+ }
+ },
+ "errors": {},
+ "id": "3",
+ "type": "utility",
+ "x": 0,
+ "y": 513
+ },
+ "5": {
+ "data": {
+ "advanced": {
+ "customName": "format note",
+ "customNameId": 0,
+ "description": "Combine the TruSTAR results into a note to pass up to the parent playbook. Build a markdown table of results with links to TruSTAR queries and select fields from the reputation information for each indicator.",
+ "join": [],
+ "note": "Combine the TruSTAR results into a note to pass up to the parent playbook. Build a markdown table of results with links to TruSTAR queries and select fields from the reputation information for each indicator."
+ },
+ "functionId": 1,
+ "functionName": "format_note",
+ "id": "5",
+ "inputParameters": [
+ "indicator_reputation:action_result.parameter.indicator_value",
+ "indicator_reputation:action_result.data.*.observable.type",
+ "indicator_reputation:action_result.data.*.priorityScore",
+ "indicator_reputation:action_result.data.*.submissionTags",
+ "indicator_reputation:action_result.data.*.attributes",
+ "indicator_reputation:action_result.data.*.safelisted",
+ "indicator_reputation:action_result.data.*.scoreContexts.*.sourceName",
+ "",
+ ""
+ ],
+ "outputVariables": [
+ "note_content"
+ ],
+ "type": "code"
+ },
+ "errors": {},
+ "id": "5",
+ "type": "code",
+ "userCode": "\n import urllib.parse\n \n note = \"\"\"\n| Indicator | Type | Priority Score | Submission Tags | Attributes | Safe Listed? | Sources |\n| :--- | :--- | :--- | :--- | :--- | :--- | :--- |\n \"\"\"\n \n for item in indicator_reputation_result_data:\n if item[1]: \n # for the first column, use the indicator as the text and the trustar query as the href\n trustar_link = 'https://station.trustar.co/browse/search?q={}'.format(urllib.parse.quote(item[0]))\n indicator_markdown = '[{}]({})'.format(item[0], trustar_link)\n indicator_type = item[1]\n priority = item[2]\n submission_tags = json.dumps(item[3]).replace('[', '').replace(']', '')\n attributes = json.dumps(item[4]).replace('{', '').replace('}', '').replace('[', '').replace(']', '')\n safe_listed = item[5]\n sources = item[6]\n note += \"|{}|{}|{}|{}|{}|{}|{}|\\n\".format(indicator_markdown, indicator_type, priority, submission_tags, attributes, safe_listed, sources)\n else:\n note += \"|{}|Not found in TruSTAR||||||\\n\".format(item[0])\n\n format_note__note_content = note\n \n",
+ "x": 340,
+ "y": 506
+ },
+ "6": {
+ "data": {
+ "advanced": {
+ "customName": "indicator_found",
+ "customNameId": 0,
+ "description": "Filter for indicators that were found in TruSTAR.",
+ "join": [],
+ "note": "Filter for indicators that were found in TruSTAR."
+ },
+ "conditions": [
+ {
+ "comparisons": [
+ {
+ "conditionIndex": 0,
+ "op": ">",
+ "param": "indicator_reputation:action_result.summary.indicators_found",
+ "value": "0"
+ }
+ ],
+ "conditionIndex": 0,
+ "customName": "indicator found",
+ "logic": "and"
+ }
+ ],
+ "functionId": 1,
+ "functionName": "indicator_found",
+ "id": "6",
+ "type": "filter"
+ },
+ "errors": {},
+ "id": "6",
+ "type": "filter",
+ "x": 60,
+ "y": 326
+ }
+ },
+ "notes": "This playbook uses the following Apps:\n - TruSTAR (indicator reputation) [asset name = trustar] - query TruSTAR for threat intelligence\n\nDeployment Notes:\n - To use this playbook as a sub-playbook of \"threat_intel_investigate\", copy it to the local git repository and make sure it has the tags \"investigate\" and \"threat_intel\"\n- To use this playbook as a sub-playbook of \"risk_notable_enrich\", copy it to local and make sure it has the tags \"investigate\" and \"risk_notable\"\n - To control the types of indicators processed by this playbook, change the data types of the \"indicators\" input\n"
+ },
+ "input_spec": [
+ {
+ "contains": [
+ "user name",
+ "ip",
+ "domain",
+ "url",
+ "host name",
+ "vault id",
+ "hash",
+ "md5",
+ "sha1",
+ "sha256",
+ "file path"
+ ],
+ "description": "Indicators to enrich with TruSTAR",
+ "name": "indicators"
+ }
+ ],
+ "output_spec": [
+ {
+ "contains": [],
+ "datapaths": [
+ "TruSTAR Indicator Enrichment"
+ ],
+ "deduplicate": false,
+ "description": "The title of the note summarizing reputation data gathered in this playbook",
+ "metadata": {},
+ "name": "note_title"
+ },
+ {
+ "contains": [],
+ "datapaths": [
+ "format_note:custom_function:note_content"
+ ],
+ "deduplicate": false,
+ "description": "The content of the note summarizing reputation data gathered in this playbook",
+ "metadata": {},
+ "name": "note_content"
+ }
+ ],
+ "playbook_type": "data",
+ "python_version": "3",
+ "schema": "5.0.4",
+ "version": "5.1.0.70187"
+ },
+ "create_time": "2021-11-24T21:10:50.604011+00:00",
+ "draft_mode": false,
+ "labels": [
+ "*"
+ ],
+ "tags": [
+ "investigate",
+ "threat_intel"
+ ]
+}
\ No newline at end of file
diff --git a/playbooks/trustar_enrich_indicators.png b/playbooks/trustar_enrich_indicators.png
new file mode 100644
index 0000000000..dac35bbb91
Binary files /dev/null and b/playbooks/trustar_enrich_indicators.png differ
diff --git a/playbooks/trustar_enrich_indicators.py b/playbooks/trustar_enrich_indicators.py
new file mode 100644
index 0000000000..8567500036
--- /dev/null
+++ b/playbooks/trustar_enrich_indicators.py
@@ -0,0 +1,210 @@
+"""
+Use TruSTAR to gather threat information about indicators in a SOAR event. Tag the indicators with the normalized priority score from TruSTAR and summarize the findings in an analyst note. This playbook is meant to be used as a child playbook executed by a parent playbook such as "threat_intel_investigate".
+"""
+
+
+import phantom.rules as phantom
+import json
+from datetime import datetime, timedelta
+
+
+def on_start(container):
+ phantom.debug('on_start() called')
+
+ # call 'indicator_reputation' block
+ indicator_reputation(container=container)
+
+ return
+
+def indicator_reputation(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
+ phantom.debug("indicator_reputation() called")
+
+ # phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED')))
+
+ ################################################################################
+ # Query the Indicator API in TruSTAR to find threat intelligence scores and attributes
+ # about each of the indicators passed into the playbook.
+ ################################################################################
+
+ playbook_input_indicators = phantom.collect2(container=container, datapath=["playbook_input:indicators"])
+
+ parameters = []
+
+ # build parameters list for 'indicator_reputation' call
+ for playbook_input_indicators_item in playbook_input_indicators:
+ if playbook_input_indicators_item[0] is not None:
+ parameters.append({
+ "indicator_value": playbook_input_indicators_item[0],
+ })
+
+ ################################################################################
+ ## Custom Code Start
+ ################################################################################
+
+ # Write your custom code here...
+
+ ################################################################################
+ ## Custom Code End
+ ################################################################################
+
+ phantom.act("indicator reputation", parameters=parameters, name="indicator_reputation", assets=["trustar"], callback=indicator_reputation_callback)
+
+ return
+
+
+def indicator_reputation_callback(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
+ phantom.debug("indicator_reputation_callback() called")
+
+
+ format_note(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=filtered_artifacts, filtered_results=filtered_results)
+ indicator_found(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=filtered_artifacts, filtered_results=filtered_results)
+
+
+ return
+
+
+def tag_indicator_with_priority_score(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
+ phantom.debug("tag_indicator_with_priority_score() called")
+
+ filtered_result_0_data_indicator_found = phantom.collect2(container=container, datapath=["filtered-data:indicator_found:condition_1:indicator_reputation:action_result.data.*.priorityScore","filtered-data:indicator_found:condition_1:indicator_reputation:action_result.parameter.indicator_value"])
+
+ parameters = []
+
+ # build parameters list for 'tag_indicator_with_priority_score' call
+ for filtered_result_0_item_indicator_found in filtered_result_0_data_indicator_found:
+ parameters.append({
+ "tags": filtered_result_0_item_indicator_found[0],
+ "indicator": filtered_result_0_item_indicator_found[1],
+ "overwrite": None,
+ })
+
+ ################################################################################
+ ## Custom Code Start
+ ################################################################################
+
+ # Write your custom code here...
+
+ ################################################################################
+ ## Custom Code End
+ ################################################################################
+
+ phantom.custom_function(custom_function="community/indicator_tag", parameters=parameters, name="tag_indicator_with_priority_score")
+
+ return
+
+
+def format_note(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
+ phantom.debug("format_note() called")
+
+ ################################################################################
+ # Combine the TruSTAR results into a note to pass up to the parent playbook. Build
+ # a markdown table of results with links to TruSTAR queries and select fields
+ # from the reputation information for each indicator.
+ ################################################################################
+
+ indicator_reputation_result_data = phantom.collect2(container=container, datapath=["indicator_reputation:action_result.parameter.indicator_value","indicator_reputation:action_result.data.*.observable.type","indicator_reputation:action_result.data.*.priorityScore","indicator_reputation:action_result.data.*.submissionTags","indicator_reputation:action_result.data.*.attributes","indicator_reputation:action_result.data.*.safelisted","indicator_reputation:action_result.data.*.scoreContexts.*.sourceName"], action_results=results)
+
+ indicator_reputation_parameter_indicator_value = [item[0] for item in indicator_reputation_result_data]
+ indicator_reputation_result_item_1 = [item[1] for item in indicator_reputation_result_data]
+ indicator_reputation_result_item_2 = [item[2] for item in indicator_reputation_result_data]
+ indicator_reputation_result_item_3 = [item[3] for item in indicator_reputation_result_data]
+ indicator_reputation_result_item_4 = [item[4] for item in indicator_reputation_result_data]
+ indicator_reputation_result_item_5 = [item[5] for item in indicator_reputation_result_data]
+ indicator_reputation_result_item_6 = [item[6] for item in indicator_reputation_result_data]
+
+ input_parameter_0 = ""
+ input_parameter_1 = ""
+
+ format_note__note_content = None
+
+ ################################################################################
+ ## Custom Code Start
+ ################################################################################
+
+ import urllib.parse
+
+ note = """
+| Indicator | Type | Priority Score | Submission Tags | Attributes | Safe Listed? | Sources |
+| :--- | :--- | :--- | :--- | :--- | :--- | :--- |
+ """
+
+ for item in indicator_reputation_result_data:
+ if item[1]:
+ # for the first column, use the indicator as the text and the trustar query as the href
+ trustar_link = 'https://station.trustar.co/browse/search?q={}'.format(urllib.parse.quote(item[0]))
+ indicator_markdown = '[{}]({})'.format(item[0], trustar_link)
+ indicator_type = item[1]
+ priority = item[2]
+ submission_tags = json.dumps(item[3]).replace('[', '').replace(']', '')
+ attributes = json.dumps(item[4]).replace('{', '').replace('}', '').replace('[', '').replace(']', '')
+ safe_listed = item[5]
+ sources = item[6]
+ note += "|{}|{}|{}|{}|{}|{}|{}|\n".format(indicator_markdown, indicator_type, priority, submission_tags, attributes, safe_listed, sources)
+ else:
+ note += "|{}|Not found in TruSTAR||||||\n".format(item[0])
+
+ format_note__note_content = note
+
+ ################################################################################
+ ## Custom Code End
+ ################################################################################
+
+ phantom.save_run_data(key="format_note:note_content", value=json.dumps(format_note__note_content))
+
+ return
+
+
+def indicator_found(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
+ phantom.debug("indicator_found() called")
+
+ ################################################################################
+ # Filter for indicators that were found in TruSTAR.
+ ################################################################################
+
+ # collect filtered artifact ids and results for 'if' condition 1
+ matched_artifacts_1, matched_results_1 = phantom.condition(
+ container=container,
+ conditions=[
+ ["indicator_reputation:action_result.summary.indicators_found", ">", 0]
+ ],
+ name="indicator_found:condition_1")
+
+ # call connected blocks if filtered artifacts or results
+ if matched_artifacts_1 or matched_results_1:
+ tag_indicator_with_priority_score(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=matched_artifacts_1, filtered_results=matched_results_1)
+
+ return
+
+
+def on_finish(container, summary):
+ phantom.debug("on_finish() called")
+
+ format_note__note_content = json.loads(phantom.get_run_data(key="format_note:note_content"))
+
+ output = {
+ "note_title": "TruSTAR Indicator Enrichment",
+ "note_content": format_note__note_content,
+ }
+
+ ################################################################################
+ ## Custom Code Start
+ ################################################################################
+
+ # This function is called after all actions are completed.
+ # summary of all the action and/or all details of actions
+ # can be collected here.
+
+ # summary_json = phantom.get_summary()
+ # if 'result' in summary_json:
+ # for action_result in summary_json['result']:
+ # if 'action_run_id' in action_result:
+ # action_results = phantom.get_action_results(action_run_id=action_result['action_run_id'], result_data=False, flatten=False)
+ # phantom.debug(action_results)
+
+ ################################################################################
+ ## Custom Code End
+ ################################################################################
+
+ phantom.save_playbook_output_data(output=output)
+
+ return
\ No newline at end of file
diff --git a/playbooks/trustar_enrich_indicators.yml b/playbooks/trustar_enrich_indicators.yml
new file mode 100644
index 0000000000..62e2d30c7e
--- /dev/null
+++ b/playbooks/trustar_enrich_indicators.yml
@@ -0,0 +1,21 @@
+name: TruSTAR Enrich Indicators
+id: fc5adc76-fd2b-48b0-5f6f-63da6423fd63
+version: 1
+date: '2021-11-24'
+author: Philip Royer, Splunk
+type: Investigation
+description: "Use TruSTAR to gather threat information about indicators in a SOAR event. Tag the indicators with the normalized priority score from TruSTAR and summarize the findings in an analyst note. This playbook is meant to be used as a child playbook executed by a parent playbook such as \"threat_intel_investigate\"."
+playbook: trustar_enrich_indicators
+how_to_implement: "To use this playbook as a sub-playbook of \"threat_intel_investigate\", copy it to the local git repository and make sure it has the tags \"investigate\" and \"threat_intel\". To use this playbook as a sub-playbook of \"risk_notable_enrich\", copy it to local and make sure it has the tags \"investigate\" and \"risk_notable\" To control the types of indicators processed by this playbook, change the data types of the \"indicators\" input\""
+references:
+- https://www.splunk.com/en_us/blog/security/TruSTAR-Enrich-Indicators-soar-in-seconds.html
+app_list:
+- "TruSTAR"
+tags:
+ platform_tags:
+ - threat_intel
+ - risk_notable
+ playbook_fields:
+ - indicators
+ product:
+ - Splunk SOAR
\ No newline at end of file