mirror of
https://github.com/splunk/security_content
synced 2026-06-08 17:32:49 +00:00
Branch was auto-updated.
This commit is contained in:
@@ -0,0 +1,380 @@
|
||||
{
|
||||
"blockly": false,
|
||||
"blockly_xml": "<xml></xml>",
|
||||
"category": "Message Identifier Activity Analysis",
|
||||
"coa": {
|
||||
"data": {
|
||||
"description": "Accepts an internet message id, and asks Gmail for a list of mailboxes to search, and then searches each one to look for records that have a matching internet message id. It then produces a normalized output and summary table.\n\nThis may not work in the intended fashion if your organization has more than 500 mailboxes.\n\nRef: D3-IAA: https://d3fend.mitre.org/technique/d3f:IdentifierActivityAnalysis/",
|
||||
"edges": [
|
||||
{
|
||||
"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_6_to_port_7",
|
||||
"sourceNode": "6",
|
||||
"sourcePort": "6_out",
|
||||
"targetNode": "7",
|
||||
"targetPort": "7_in"
|
||||
},
|
||||
{
|
||||
"id": "port_7_to_port_1",
|
||||
"sourceNode": "7",
|
||||
"sourcePort": "7_out",
|
||||
"targetNode": "1",
|
||||
"targetPort": "1_in"
|
||||
},
|
||||
{
|
||||
"id": "port_5_to_port_9",
|
||||
"sourceNode": "5",
|
||||
"sourcePort": "5_out",
|
||||
"targetNode": "9",
|
||||
"targetPort": "9_in"
|
||||
},
|
||||
{
|
||||
"id": "port_0_to_port_3",
|
||||
"sourceNode": "0",
|
||||
"sourcePort": "0_out",
|
||||
"targetNode": "3",
|
||||
"targetPort": "3_in"
|
||||
},
|
||||
{
|
||||
"conditions": [
|
||||
{
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
"id": "port_9_to_port_6",
|
||||
"sourceNode": "9",
|
||||
"sourcePort": "9_out",
|
||||
"targetNode": "6",
|
||||
"targetPort": "6_in"
|
||||
}
|
||||
],
|
||||
"hash": "c253dd1339538806313a6f387f6df7a39d537dff",
|
||||
"nodes": {
|
||||
"0": {
|
||||
"data": {
|
||||
"advanced": {
|
||||
"join": []
|
||||
},
|
||||
"functionName": "on_start",
|
||||
"id": "0",
|
||||
"type": "start"
|
||||
},
|
||||
"errors": {},
|
||||
"id": "0",
|
||||
"type": "start",
|
||||
"warnings": {},
|
||||
"x": 19.999999999999986,
|
||||
"y": -1.2789769243681803e-13
|
||||
},
|
||||
"1": {
|
||||
"data": {
|
||||
"advanced": {
|
||||
"join": []
|
||||
},
|
||||
"functionName": "on_finish",
|
||||
"id": "1",
|
||||
"type": "end"
|
||||
},
|
||||
"errors": {},
|
||||
"id": "1",
|
||||
"type": "end",
|
||||
"warnings": {},
|
||||
"x": 19.999999999999986,
|
||||
"y": 1220
|
||||
},
|
||||
"3": {
|
||||
"data": {
|
||||
"advanced": {
|
||||
"customName": "artifact filter",
|
||||
"customNameId": 0,
|
||||
"delimiter": ",",
|
||||
"delimiter_enabled": true,
|
||||
"join": [],
|
||||
"note": "Ensure we have an input"
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"comparisons": [
|
||||
{
|
||||
"conditionIndex": 0,
|
||||
"op": "!=",
|
||||
"param": "playbook_input:message_id",
|
||||
"value": "None"
|
||||
}
|
||||
],
|
||||
"conditionIndex": 0,
|
||||
"customName": "Input Valid",
|
||||
"logic": "and"
|
||||
}
|
||||
],
|
||||
"functionId": 1,
|
||||
"functionName": "artifact_filter",
|
||||
"id": "3",
|
||||
"type": "filter"
|
||||
},
|
||||
"errors": {},
|
||||
"id": "3",
|
||||
"type": "filter",
|
||||
"warnings": {},
|
||||
"x": 60,
|
||||
"y": 148
|
||||
},
|
||||
"4": {
|
||||
"data": {
|
||||
"action": "list users",
|
||||
"actionType": "investigate",
|
||||
"advanced": {
|
||||
"customName": "get mailboxes",
|
||||
"customNameId": 0,
|
||||
"description": "Required step in order to search \"All\" of a Gsuite organization. This receives a list of mailboxes that are passed to the next action.",
|
||||
"join": [],
|
||||
"note": "Gets a list of mailboxes from GMail to search"
|
||||
},
|
||||
"connector": "G Suite for GMail",
|
||||
"connectorConfigs": [
|
||||
"g_suite_for_gmail"
|
||||
],
|
||||
"connectorId": "9c73f233-2c4a-406a-855e-41d8d2497d0e",
|
||||
"connectorVersion": "v1",
|
||||
"functionId": 1,
|
||||
"functionName": "get_mailboxes",
|
||||
"id": "4",
|
||||
"parameters": {
|
||||
"max_items": "500",
|
||||
"page_token": ""
|
||||
},
|
||||
"requiredParameters": [],
|
||||
"type": "action"
|
||||
},
|
||||
"errors": {},
|
||||
"id": "4",
|
||||
"type": "action",
|
||||
"warnings": {},
|
||||
"x": 0,
|
||||
"y": 328
|
||||
},
|
||||
"5": {
|
||||
"data": {
|
||||
"action": "run query",
|
||||
"actionType": "investigate",
|
||||
"advanced": {
|
||||
"customName": "search mailboxes",
|
||||
"customNameId": 0,
|
||||
"join": [],
|
||||
"note": "Searches mailboxes returned by the previous action for messages with an internet message ID matching the input"
|
||||
},
|
||||
"connector": "G Suite for GMail",
|
||||
"connectorConfigs": [
|
||||
"g_suite_for_gmail"
|
||||
],
|
||||
"connectorId": "9c73f233-2c4a-406a-855e-41d8d2497d0e",
|
||||
"connectorVersion": "v1",
|
||||
"functionId": 1,
|
||||
"functionName": "search_mailboxes",
|
||||
"id": "5",
|
||||
"parameters": {
|
||||
"email": "get_mailboxes:action_result.data.*.emails.*.address",
|
||||
"internet_message_id": "filtered-data:artifact_filter:condition_1:playbook_input:message_id",
|
||||
"label": "Inbox",
|
||||
"max_results": 100,
|
||||
"query": ""
|
||||
},
|
||||
"requiredParameters": [
|
||||
{
|
||||
"data_type": "string",
|
||||
"field": "email"
|
||||
},
|
||||
{
|
||||
"data_type": "string",
|
||||
"default": "Inbox",
|
||||
"field": "label"
|
||||
},
|
||||
{
|
||||
"data_type": "numeric",
|
||||
"default": 100,
|
||||
"field": "max_results"
|
||||
}
|
||||
],
|
||||
"type": "action"
|
||||
},
|
||||
"errors": {},
|
||||
"id": "5",
|
||||
"type": "action",
|
||||
"warnings": {},
|
||||
"x": 0,
|
||||
"y": 506
|
||||
},
|
||||
"6": {
|
||||
"data": {
|
||||
"advanced": {
|
||||
"customName": "format message report",
|
||||
"customNameId": 0,
|
||||
"join": [],
|
||||
"note": "Format the markdown table summary of this playbook execution"
|
||||
},
|
||||
"functionId": 1,
|
||||
"functionName": "format_message_report",
|
||||
"id": "6",
|
||||
"parameters": [
|
||||
"filtered-data:results_filter:condition_1:search_mailboxes:action_result.parameter.internet_message_id",
|
||||
"filtered-data:results_filter:condition_1:search_mailboxes:action_result.data.*.delivered_to",
|
||||
"filtered-data:results_filter:condition_1:search_mailboxes:action_result.data.*.to",
|
||||
"filtered-data:results_filter:condition_1:search_mailboxes:action_result.data.*.subject",
|
||||
"filtered-data:results_filter:condition_1:search_mailboxes:action_result.data.*.from"
|
||||
],
|
||||
"template": "SOAR searched for occurrences of `{0}` within your environment using GSuite for GMail. The table below shows a summary of the information gathered.\n\n| Recipient | Addressed To | Subject | Sender |\n| --- | --- | --- | --- |\n%%\n| {1} | {2} | {3} | {4} |\n%%\n",
|
||||
"type": "format"
|
||||
},
|
||||
"errors": {},
|
||||
"id": "6",
|
||||
"type": "format",
|
||||
"userCode": " \n \n",
|
||||
"warnings": {},
|
||||
"x": 0,
|
||||
"y": 864
|
||||
},
|
||||
"7": {
|
||||
"data": {
|
||||
"advanced": {
|
||||
"customName": "build message output",
|
||||
"customNameId": 0,
|
||||
"description": "Logic regarding observable construction goes here",
|
||||
"join": [],
|
||||
"note": "Format the observable output object produced by this playbook"
|
||||
},
|
||||
"functionId": 1,
|
||||
"functionName": "build_message_output",
|
||||
"id": "7",
|
||||
"inputParameters": [
|
||||
"filtered-data:results_filter:condition_1:search_mailboxes:action_result.parameter.internet_message_id",
|
||||
"filtered-data:results_filter:condition_1:search_mailboxes:action_result.data.*.delivered_to",
|
||||
"filtered-data:results_filter:condition_1:search_mailboxes:action_result.data.*.to",
|
||||
"filtered-data:results_filter:condition_1:search_mailboxes:action_result.data.*.subject",
|
||||
"filtered-data:results_filter:condition_1:search_mailboxes:action_result.data.*.from"
|
||||
],
|
||||
"outputVariables": [
|
||||
"observable_array"
|
||||
],
|
||||
"type": "code"
|
||||
},
|
||||
"errors": {},
|
||||
"id": "7",
|
||||
"type": "code",
|
||||
"userCode": "\n # Variable renaming for convenince\n messageID = filtered_result_0_parameter_internet_message_id\n recipients = filtered_result_0_data___delivered_to\n addressees = filtered_result_0_data___to\n subjects = filtered_result_0_data___subject\n senders = filtered_result_0_data___from\n \n build_message_output__observable_array = []\n recordList = []\n \n for message_id in messageID:\n \n # construct iterables for records\n for recipient, addressee, subject, sender in zip(recipients, addressees, subjects, senders):\n record = {\n \"recipient\": recipient,\n \"addressee\": addressee,\n \"subject\": subject,\n \"sender\": sender\n }\n recordList.append(record)\n \n # Create observable body\n observable = {\n \"value\": message_id,\n \"type\": \"internet message ID\",\n \"count\": len(recordList),\n \"source\": \"GSuite for GMail\",\n \"message_identifier_activity\": recordList\n }\n \n build_message_output__observable_array.append(observable)\n \n\n",
|
||||
"warnings": {},
|
||||
"x": 0,
|
||||
"y": 1040
|
||||
},
|
||||
"9": {
|
||||
"data": {
|
||||
"advanced": {
|
||||
"customName": "results filter",
|
||||
"customNameId": 0,
|
||||
"delimiter": ",",
|
||||
"delimiter_enabled": true,
|
||||
"description": "Filter results from mailbox search",
|
||||
"join": [],
|
||||
"note": "Remove mailboxes that couldn't be searched or where no results were found"
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"comparisons": [
|
||||
{
|
||||
"conditionIndex": 0,
|
||||
"op": "==",
|
||||
"param": "search_mailboxes:action_result.status",
|
||||
"value": "success"
|
||||
},
|
||||
{
|
||||
"conditionIndex": 0,
|
||||
"op": ">",
|
||||
"param": "search_mailboxes:action_result.summary.total_messages_returned",
|
||||
"value": "0"
|
||||
}
|
||||
],
|
||||
"conditionIndex": 0,
|
||||
"customName": "Results found",
|
||||
"logic": "and"
|
||||
}
|
||||
],
|
||||
"functionId": 2,
|
||||
"functionName": "results_filter",
|
||||
"id": "9",
|
||||
"type": "filter"
|
||||
},
|
||||
"errors": {},
|
||||
"id": "9",
|
||||
"type": "filter",
|
||||
"warnings": {},
|
||||
"x": 60,
|
||||
"y": 684
|
||||
}
|
||||
},
|
||||
"notes": "Inputs: internet message id\nInteractions: GSuite For GMail\nActions: get users, run query\nOutputs: observable, markdown report"
|
||||
},
|
||||
"input_spec": [
|
||||
{
|
||||
"contains": [
|
||||
"internet message id"
|
||||
],
|
||||
"description": "An Internet Message ID to look for within a GMail environment",
|
||||
"name": "message_id"
|
||||
}
|
||||
],
|
||||
"output_spec": [
|
||||
{
|
||||
"contains": [],
|
||||
"datapaths": [
|
||||
"build_message_output:custom_function:observable_array"
|
||||
],
|
||||
"deduplicate": false,
|
||||
"description": "An array of Observable objects ",
|
||||
"metadata": {},
|
||||
"name": "observable"
|
||||
},
|
||||
{
|
||||
"contains": [],
|
||||
"datapaths": [
|
||||
"format_message_report:formatted_data"
|
||||
],
|
||||
"deduplicate": false,
|
||||
"description": "An array of markdown reports",
|
||||
"metadata": {},
|
||||
"name": "markdown_report"
|
||||
}
|
||||
],
|
||||
"playbook_type": "data",
|
||||
"python_version": "3",
|
||||
"schema": "5.0.10",
|
||||
"version": "6.0.1.123902"
|
||||
},
|
||||
"create_time": "2023-06-16T15:48:09.633490+00:00",
|
||||
"draft_mode": false,
|
||||
"labels": [
|
||||
"*"
|
||||
],
|
||||
"tags": [
|
||||
"message_identifier_activity",
|
||||
"internet_message_id",
|
||||
"gsuite_for_gmail",
|
||||
"D3-IAA"
|
||||
]
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 57 KiB |
@@ -0,0 +1,258 @@
|
||||
"""
|
||||
Accepts an internet message id, and asks Gmail for a list of mailboxes to search, and then searches each one to look for records that have a matching internet message id. It then produces a normalized output and summary table.\n\nThis may not work in the intended fashion if your organization has more than 500 mailboxes.\n\nRef: D3-IAA: https://d3fend.mitre.org/technique/d3f:IdentifierActivityAnalysis/
|
||||
"""
|
||||
|
||||
|
||||
import phantom.rules as phantom
|
||||
import json
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
|
||||
@phantom.playbook_block()
|
||||
def on_start(container):
|
||||
phantom.debug('on_start() called')
|
||||
|
||||
# call 'artifact_filter' block
|
||||
artifact_filter(container=container)
|
||||
|
||||
return
|
||||
|
||||
@phantom.playbook_block()
|
||||
def artifact_filter(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
|
||||
phantom.debug("artifact_filter() called")
|
||||
|
||||
# collect filtered artifact ids and results for 'if' condition 1
|
||||
matched_artifacts_1, matched_results_1 = phantom.condition(
|
||||
container=container,
|
||||
conditions=[
|
||||
["playbook_input:message_id", "!=", None]
|
||||
],
|
||||
name="artifact_filter:condition_1",
|
||||
delimiter=",")
|
||||
|
||||
# call connected blocks if filtered artifacts or results
|
||||
if matched_artifacts_1 or matched_results_1:
|
||||
get_mailboxes(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=matched_artifacts_1, filtered_results=matched_results_1)
|
||||
|
||||
return
|
||||
|
||||
|
||||
@phantom.playbook_block()
|
||||
def get_mailboxes(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
|
||||
phantom.debug("get_mailboxes() called")
|
||||
|
||||
# phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED')))
|
||||
|
||||
################################################################################
|
||||
# Required step in order to search "All" of a Gsuite organization. This receives
|
||||
# a list of mailboxes that are passed to the next action.
|
||||
################################################################################
|
||||
|
||||
parameters = []
|
||||
|
||||
parameters.append({
|
||||
"max_items": 500,
|
||||
"page_token": "",
|
||||
})
|
||||
|
||||
################################################################################
|
||||
## Custom Code Start
|
||||
################################################################################
|
||||
|
||||
# Write your custom code here...
|
||||
|
||||
################################################################################
|
||||
## Custom Code End
|
||||
################################################################################
|
||||
|
||||
phantom.act("list users", parameters=parameters, name="get_mailboxes", assets=["g_suite_for_gmail"], callback=search_mailboxes)
|
||||
|
||||
return
|
||||
|
||||
|
||||
@phantom.playbook_block()
|
||||
def search_mailboxes(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
|
||||
phantom.debug("search_mailboxes() called")
|
||||
|
||||
# phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED')))
|
||||
|
||||
get_mailboxes_result_data = phantom.collect2(container=container, datapath=["get_mailboxes:action_result.data.*.emails.*.address","get_mailboxes:action_result.parameter.context.artifact_id"], action_results=results)
|
||||
filtered_input_0_message_id = phantom.collect2(container=container, datapath=["filtered-data:artifact_filter:condition_1:playbook_input:message_id"])
|
||||
|
||||
parameters = []
|
||||
|
||||
# build parameters list for 'search_mailboxes' call
|
||||
for get_mailboxes_result_item in get_mailboxes_result_data:
|
||||
for filtered_input_0_message_id_item in filtered_input_0_message_id:
|
||||
if get_mailboxes_result_item[0] is not None:
|
||||
parameters.append({
|
||||
"email": get_mailboxes_result_item[0],
|
||||
"label": "Inbox",
|
||||
"query": "",
|
||||
"max_results": 100,
|
||||
"internet_message_id": filtered_input_0_message_id_item[0],
|
||||
"context": {'artifact_id': get_mailboxes_result_item[1]},
|
||||
})
|
||||
|
||||
################################################################################
|
||||
## Custom Code Start
|
||||
################################################################################
|
||||
|
||||
# Write your custom code here...
|
||||
|
||||
################################################################################
|
||||
## Custom Code End
|
||||
################################################################################
|
||||
|
||||
phantom.act("run query", parameters=parameters, name="search_mailboxes", assets=["g_suite_for_gmail"], callback=results_filter)
|
||||
|
||||
return
|
||||
|
||||
|
||||
@phantom.playbook_block()
|
||||
def format_message_report(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
|
||||
phantom.debug("format_message_report() called")
|
||||
|
||||
template = """SOAR searched for occurrences of `{0}` within your environment using GSuite for GMail. The table below shows a summary of the information gathered.\n\n| Recipient | Addressed To | Subject | Sender |\n| --- | --- | --- | --- |\n%%\n| {1} | {2} | {3} | {4} |\n%%\n"""
|
||||
|
||||
# parameter list for template variable replacement
|
||||
parameters = [
|
||||
"filtered-data:results_filter:condition_1:search_mailboxes:action_result.parameter.internet_message_id",
|
||||
"filtered-data:results_filter:condition_1:search_mailboxes:action_result.data.*.delivered_to",
|
||||
"filtered-data:results_filter:condition_1:search_mailboxes:action_result.data.*.to",
|
||||
"filtered-data:results_filter:condition_1:search_mailboxes:action_result.data.*.subject",
|
||||
"filtered-data:results_filter:condition_1:search_mailboxes:action_result.data.*.from"
|
||||
]
|
||||
|
||||
################################################################################
|
||||
## Custom Code Start
|
||||
################################################################################
|
||||
|
||||
|
||||
################################################################################
|
||||
## Custom Code End
|
||||
################################################################################
|
||||
|
||||
phantom.format(container=container, template=template, parameters=parameters, name="format_message_report")
|
||||
|
||||
build_message_output(container=container)
|
||||
|
||||
return
|
||||
|
||||
|
||||
@phantom.playbook_block()
|
||||
def build_message_output(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
|
||||
phantom.debug("build_message_output() called")
|
||||
|
||||
################################################################################
|
||||
# Logic regarding observable construction goes here
|
||||
################################################################################
|
||||
|
||||
filtered_result_0_data_results_filter = phantom.collect2(container=container, datapath=["filtered-data:results_filter:condition_1:search_mailboxes:action_result.parameter.internet_message_id","filtered-data:results_filter:condition_1:search_mailboxes:action_result.data.*.delivered_to","filtered-data:results_filter:condition_1:search_mailboxes:action_result.data.*.to","filtered-data:results_filter:condition_1:search_mailboxes:action_result.data.*.subject","filtered-data:results_filter:condition_1:search_mailboxes:action_result.data.*.from"])
|
||||
|
||||
filtered_result_0_parameter_internet_message_id = [item[0] for item in filtered_result_0_data_results_filter]
|
||||
filtered_result_0_data___delivered_to = [item[1] for item in filtered_result_0_data_results_filter]
|
||||
filtered_result_0_data___to = [item[2] for item in filtered_result_0_data_results_filter]
|
||||
filtered_result_0_data___subject = [item[3] for item in filtered_result_0_data_results_filter]
|
||||
filtered_result_0_data___from = [item[4] for item in filtered_result_0_data_results_filter]
|
||||
|
||||
build_message_output__observable_array = None
|
||||
|
||||
################################################################################
|
||||
## Custom Code Start
|
||||
################################################################################
|
||||
|
||||
# Variable renaming for convenince
|
||||
messageID = filtered_result_0_parameter_internet_message_id
|
||||
recipients = filtered_result_0_data___delivered_to
|
||||
addressees = filtered_result_0_data___to
|
||||
subjects = filtered_result_0_data___subject
|
||||
senders = filtered_result_0_data___from
|
||||
|
||||
build_message_output__observable_array = []
|
||||
recordList = []
|
||||
|
||||
for message_id in messageID:
|
||||
|
||||
# construct iterables for records
|
||||
for recipient, addressee, subject, sender in zip(recipients, addressees, subjects, senders):
|
||||
record = {
|
||||
"recipient": recipient,
|
||||
"addressee": addressee,
|
||||
"subject": subject,
|
||||
"sender": sender
|
||||
}
|
||||
recordList.append(record)
|
||||
|
||||
# Create observable body
|
||||
observable = {
|
||||
"value": message_id,
|
||||
"type": "internet message ID",
|
||||
"count": len(recordList),
|
||||
"source": "GSuite for GMail",
|
||||
"message_identifier_activity": recordList
|
||||
}
|
||||
|
||||
build_message_output__observable_array.append(observable)
|
||||
|
||||
|
||||
################################################################################
|
||||
## Custom Code End
|
||||
################################################################################
|
||||
|
||||
phantom.save_run_data(key="build_message_output:observable_array", value=json.dumps(build_message_output__observable_array))
|
||||
|
||||
return
|
||||
|
||||
|
||||
@phantom.playbook_block()
|
||||
def results_filter(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
|
||||
phantom.debug("results_filter() called")
|
||||
|
||||
################################################################################
|
||||
# Filter results from mailbox search
|
||||
################################################################################
|
||||
|
||||
# collect filtered artifact ids and results for 'if' condition 1
|
||||
matched_artifacts_1, matched_results_1 = phantom.condition(
|
||||
container=container,
|
||||
logical_operator="and",
|
||||
conditions=[
|
||||
["search_mailboxes:action_result.status", "==", "success"],
|
||||
["search_mailboxes:action_result.summary.total_messages_returned", ">", 0]
|
||||
],
|
||||
name="results_filter:condition_1",
|
||||
delimiter=",")
|
||||
|
||||
# call connected blocks if filtered artifacts or results
|
||||
if matched_artifacts_1 or matched_results_1:
|
||||
format_message_report(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=matched_artifacts_1, filtered_results=matched_results_1)
|
||||
|
||||
return
|
||||
|
||||
|
||||
@phantom.playbook_block()
|
||||
def on_finish(container, summary):
|
||||
phantom.debug("on_finish() called")
|
||||
|
||||
format_message_report = phantom.get_format_data(name="format_message_report")
|
||||
build_message_output__observable_array = json.loads(_ if (_ := phantom.get_run_data(key="build_message_output:observable_array")) != "" else "null") # pylint: disable=used-before-assignment
|
||||
|
||||
output = {
|
||||
"observable": build_message_output__observable_array,
|
||||
"markdown_report": format_message_report,
|
||||
}
|
||||
|
||||
################################################################################
|
||||
## Custom Code Start
|
||||
################################################################################
|
||||
|
||||
# Write your custom code here...
|
||||
|
||||
################################################################################
|
||||
## Custom Code End
|
||||
################################################################################
|
||||
|
||||
phantom.save_playbook_output_data(output=output)
|
||||
|
||||
return
|
||||
@@ -0,0 +1,22 @@
|
||||
name: G Suite for GMail Message Identifier Activity Analysis
|
||||
id: 5299d6dd-e9c4-4afa-b051-928ace0ff816d
|
||||
version: 1
|
||||
date: '2023-05-12'
|
||||
author: Lou Stella, Splunk
|
||||
type: Investigation
|
||||
description: "Accepts an internet message id, and asks Gmail for a list of mailboxes to search, and then searches each one to look for records that have a matching internet message id. It then produces a normalized output and summary table."
|
||||
playbook: G_Suite_for_GMail_Message_Identifier_Acitivity_Analysis
|
||||
how_to_implement: This input playbook requires the G Suite for GMail connector to be configured. It is designed to work in environments that posess a maximum of 500 mailboxes at this time, due to a limitation in the G Suite for GMail connector.
|
||||
references: []
|
||||
app_list:
|
||||
- G Suite for GMail
|
||||
tags:
|
||||
platform_tags:
|
||||
- message_identifier_activity
|
||||
- internet_message_id
|
||||
- gsuite_for_gmail
|
||||
playbook_type: Input
|
||||
vpe_type: Modern
|
||||
playbook_fields: []
|
||||
product:
|
||||
- Splunk SOAR
|
||||
@@ -0,0 +1,361 @@
|
||||
{
|
||||
"blockly": false,
|
||||
"blockly_xml": "<xml></xml>",
|
||||
"category": "Message Identifier Activity Analysis",
|
||||
"coa": {
|
||||
"data": {
|
||||
"description": "Accepts an internet message id, and asks Splunk \n to look for records that have a matching internet message id. It then produces a normalized output and summary table.\n\nRef: D3-IAA https://d3fend.mitre.org/technique/d3f:IdentifierActivityAnalysis/",
|
||||
"edges": [
|
||||
{
|
||||
"conditions": [
|
||||
{
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
"id": "port_3_to_port_5",
|
||||
"sourceNode": "3",
|
||||
"sourcePort": "3_out",
|
||||
"targetNode": "5",
|
||||
"targetPort": "5_in"
|
||||
},
|
||||
{
|
||||
"id": "port_5_to_port_6",
|
||||
"sourceNode": "5",
|
||||
"sourcePort": "5_out",
|
||||
"targetNode": "6",
|
||||
"targetPort": "6_in"
|
||||
},
|
||||
{
|
||||
"id": "port_7_to_port_8",
|
||||
"sourceNode": "7",
|
||||
"sourcePort": "7_out",
|
||||
"targetNode": "8",
|
||||
"targetPort": "8_in"
|
||||
},
|
||||
{
|
||||
"id": "port_8_to_port_1",
|
||||
"sourceNode": "8",
|
||||
"sourcePort": "8_out",
|
||||
"targetNode": "1",
|
||||
"targetPort": "1_in"
|
||||
},
|
||||
{
|
||||
"id": "port_6_to_port_9",
|
||||
"sourceNode": "6",
|
||||
"sourcePort": "6_out",
|
||||
"targetNode": "9",
|
||||
"targetPort": "9_in"
|
||||
},
|
||||
{
|
||||
"conditions": [
|
||||
{
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
"id": "port_9_to_port_7",
|
||||
"sourceNode": "9",
|
||||
"sourcePort": "9_out",
|
||||
"targetNode": "7",
|
||||
"targetPort": "7_in"
|
||||
},
|
||||
{
|
||||
"id": "port_0_to_port_3",
|
||||
"sourceNode": "0",
|
||||
"sourcePort": "0_out",
|
||||
"targetNode": "3",
|
||||
"targetPort": "3_in"
|
||||
}
|
||||
],
|
||||
"hash": "6bbfd0d0e879b5ebab46c26404cb193313b5ea56",
|
||||
"nodes": {
|
||||
"0": {
|
||||
"data": {
|
||||
"advanced": {
|
||||
"join": []
|
||||
},
|
||||
"functionName": "on_start",
|
||||
"id": "0",
|
||||
"type": "start"
|
||||
},
|
||||
"errors": {},
|
||||
"id": "0",
|
||||
"type": "start",
|
||||
"warnings": {},
|
||||
"x": 19.999999999999986,
|
||||
"y": -1.9184653865522705e-13
|
||||
},
|
||||
"1": {
|
||||
"data": {
|
||||
"advanced": {
|
||||
"join": []
|
||||
},
|
||||
"functionName": "on_finish",
|
||||
"id": "1",
|
||||
"type": "end"
|
||||
},
|
||||
"errors": {},
|
||||
"id": "1",
|
||||
"type": "end",
|
||||
"warnings": {},
|
||||
"x": 19.999999999999986,
|
||||
"y": 1220
|
||||
},
|
||||
"3": {
|
||||
"data": {
|
||||
"advanced": {
|
||||
"customName": "input filter",
|
||||
"customNameId": 0,
|
||||
"delimiter": ",",
|
||||
"delimiter_enabled": true,
|
||||
"join": [],
|
||||
"note": "Ensure there is an input"
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"comparisons": [
|
||||
{
|
||||
"conditionIndex": 0,
|
||||
"op": "!=",
|
||||
"param": "playbook_input:message_id",
|
||||
"value": "None"
|
||||
}
|
||||
],
|
||||
"conditionIndex": 0,
|
||||
"customName": "Input Valid",
|
||||
"logic": "and"
|
||||
}
|
||||
],
|
||||
"functionId": 1,
|
||||
"functionName": "input_filter",
|
||||
"id": "3",
|
||||
"type": "filter"
|
||||
},
|
||||
"errors": {},
|
||||
"id": "3",
|
||||
"type": "filter",
|
||||
"warnings": {},
|
||||
"x": 60,
|
||||
"y": 148
|
||||
},
|
||||
"5": {
|
||||
"data": {
|
||||
"advanced": {
|
||||
"customName": "format message query",
|
||||
"customNameId": 0,
|
||||
"join": [],
|
||||
"note": "construct Splunk query with substitution of input"
|
||||
},
|
||||
"functionId": 1,
|
||||
"functionName": "format_message_query",
|
||||
"id": "5",
|
||||
"parameters": [
|
||||
"filtered-data:input_filter:condition_1:playbook_input:message_id"
|
||||
],
|
||||
"template": "summariesonly=false fillnull_value=\"Unknown\" count from datamodel=Email.All_Email where All_Email.message_id=\"{0}\" by All_Email.orig_recipient, All_Email.recipient, All_Email.src_user, All_Email.subject | `drop_dm_object_name(\"All_Email\")` | rename orig_recipient as Addressee, recipient as Recipient, src_user as Sender, subject as Subject | fields Addressee, Recipient, Sender, Subject | fillnull value=\"Unknown\"",
|
||||
"type": "format"
|
||||
},
|
||||
"errors": {},
|
||||
"id": "5",
|
||||
"type": "format",
|
||||
"warnings": {},
|
||||
"x": 0,
|
||||
"y": 328
|
||||
},
|
||||
"6": {
|
||||
"data": {
|
||||
"action": "run query",
|
||||
"actionType": "investigate",
|
||||
"advanced": {
|
||||
"customName": "run message query",
|
||||
"customNameId": 0,
|
||||
"join": [],
|
||||
"note": "Run Splunk query created in prior block"
|
||||
},
|
||||
"connector": "Splunk",
|
||||
"connectorConfigs": [
|
||||
"splunk"
|
||||
],
|
||||
"connectorId": "91883aa8-9c81-470b-97a1-5d8f7995f560",
|
||||
"connectorVersion": "v1",
|
||||
"functionId": 1,
|
||||
"functionName": "run_message_query",
|
||||
"id": "6",
|
||||
"parameters": {
|
||||
"command": "tstats",
|
||||
"display": "Addressee,Recipient,Sender,Subject",
|
||||
"query": "format_message_query:formatted_data",
|
||||
"search_mode": "smart"
|
||||
},
|
||||
"requiredParameters": [
|
||||
{
|
||||
"data_type": "string",
|
||||
"field": "query"
|
||||
},
|
||||
{
|
||||
"data_type": "string",
|
||||
"default": "search",
|
||||
"field": "command"
|
||||
},
|
||||
{
|
||||
"data_type": "string",
|
||||
"default": "smart",
|
||||
"field": "search_mode"
|
||||
}
|
||||
],
|
||||
"type": "action"
|
||||
},
|
||||
"errors": {},
|
||||
"id": "6",
|
||||
"type": "action",
|
||||
"warnings": {},
|
||||
"x": 0,
|
||||
"y": 506
|
||||
},
|
||||
"7": {
|
||||
"data": {
|
||||
"advanced": {
|
||||
"customName": "format message report",
|
||||
"customNameId": 0,
|
||||
"join": [],
|
||||
"note": "Format the markdown table summary of this playbook execution"
|
||||
},
|
||||
"functionId": 2,
|
||||
"functionName": "format_message_report",
|
||||
"id": "7",
|
||||
"parameters": [
|
||||
"filtered-data:input_filter:condition_1:playbook_input:message_id",
|
||||
"filtered-data:results_filter:condition_1:run_message_query:action_result.data.*.Recipient",
|
||||
"filtered-data:results_filter:condition_1:run_message_query:action_result.data.*.Addressee",
|
||||
"filtered-data:results_filter:condition_1:run_message_query:action_result.data.*.Subject",
|
||||
"filtered-data:results_filter:condition_1:run_message_query:action_result.data.*.Sender"
|
||||
],
|
||||
"template": "SOAR searched for occurrences of `{0}` within your environment using Splunk's Email datamodel. The table below shows a summary of the information gathered.\n\n\n| Recipient | Addressed To | Subject | Sender |\n| --- | --- | --- | --- |\n%%\n| {1} | {2} | {3} | {4} |\n%%\n\n",
|
||||
"type": "format"
|
||||
},
|
||||
"errors": {},
|
||||
"id": "7",
|
||||
"type": "format",
|
||||
"warnings": {},
|
||||
"x": 0,
|
||||
"y": 864
|
||||
},
|
||||
"8": {
|
||||
"data": {
|
||||
"advanced": {
|
||||
"customName": "build message output",
|
||||
"customNameId": 0,
|
||||
"description": "Logic regarding observable construction goes here",
|
||||
"join": [],
|
||||
"note": "Format the observable output object produced by this playbook"
|
||||
},
|
||||
"functionId": 1,
|
||||
"functionName": "build_message_output",
|
||||
"id": "8",
|
||||
"inputParameters": [
|
||||
"filtered-data:input_filter:condition_1:playbook_input:message_id",
|
||||
"filtered-data:results_filter:condition_1:run_message_query:action_result.data.*.Recipient",
|
||||
"filtered-data:results_filter:condition_1:run_message_query:action_result.data.*.Addressee",
|
||||
"filtered-data:results_filter:condition_1:run_message_query:action_result.data.*.Subject",
|
||||
"filtered-data:results_filter:condition_1:run_message_query:action_result.data.*.Sender"
|
||||
],
|
||||
"outputVariables": [
|
||||
"observable_array"
|
||||
],
|
||||
"type": "code"
|
||||
},
|
||||
"errors": {},
|
||||
"id": "8",
|
||||
"type": "code",
|
||||
"userCode": "\n # Variable renames for convenience\n message_id = filtered_input_0_message_id_values\n recipients = filtered_result_0_data___recipient\n addressees = filtered_result_0_data___addressee\n subjects = filtered_result_0_data___subject\n senders = filtered_result_0_data___sender\n \n build_message_output__observable_array = []\n recordList = []\n \n # unwind records\n for message_identifier in message_id:\n for recipient, addressee, subject, sender in zip(recipients, addressees, subjects, senders):\n record = {\n \"recipient\": recipient,\n \"addressee\": addressee,\n \"subject\": subject,\n \"sender\": sender\n }\n recordList.append(record)\n \n observable = {\n \"value\": message_identifier,\n \"type\": \"internet message ID\",\n \"count\": len(recordList),\n \"source\": \"Splunk\",\n \"message_identifier_activity\": recordList\n }\n\n build_message_output__observable_array.append(observable)\n \n",
|
||||
"warnings": {},
|
||||
"x": 0,
|
||||
"y": 1042
|
||||
},
|
||||
"9": {
|
||||
"data": {
|
||||
"advanced": {
|
||||
"customName": "Results filter",
|
||||
"customNameId": 0,
|
||||
"delimiter": ",",
|
||||
"delimiter_enabled": true,
|
||||
"join": [],
|
||||
"note": "Ensure there are results"
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"comparisons": [
|
||||
{
|
||||
"conditionIndex": 0,
|
||||
"op": ">",
|
||||
"param": "run_message_query:action_result.summary.total_events",
|
||||
"value": "0"
|
||||
}
|
||||
],
|
||||
"conditionIndex": 0,
|
||||
"customName": "Results exist",
|
||||
"logic": "and"
|
||||
}
|
||||
],
|
||||
"functionId": 2,
|
||||
"functionName": "results_filter",
|
||||
"id": "9",
|
||||
"type": "filter"
|
||||
},
|
||||
"errors": {},
|
||||
"id": "9",
|
||||
"type": "filter",
|
||||
"warnings": {},
|
||||
"x": 60,
|
||||
"y": 684
|
||||
}
|
||||
},
|
||||
"notes": "Inputs: internet message id\nInteractions: Splunk\nActions: run query\nOutputs: observable, markdown report"
|
||||
},
|
||||
"input_spec": [
|
||||
{
|
||||
"contains": [
|
||||
"internet message id"
|
||||
],
|
||||
"description": "An Internet Message ID to look for within message logs in Splunk",
|
||||
"name": "message_id"
|
||||
}
|
||||
],
|
||||
"output_spec": [
|
||||
{
|
||||
"contains": [],
|
||||
"datapaths": [
|
||||
"build_message_output:custom_function:observable_array"
|
||||
],
|
||||
"deduplicate": false,
|
||||
"description": "An array of Observable objects ",
|
||||
"metadata": {},
|
||||
"name": "observable"
|
||||
},
|
||||
{
|
||||
"contains": [],
|
||||
"datapaths": [
|
||||
"format_message_report:formatted_data"
|
||||
],
|
||||
"deduplicate": false,
|
||||
"description": "An array of markdown reports",
|
||||
"metadata": {},
|
||||
"name": "markdown_report"
|
||||
}
|
||||
],
|
||||
"playbook_type": "data",
|
||||
"python_version": "3",
|
||||
"schema": "5.0.10",
|
||||
"version": "6.0.1.123902"
|
||||
},
|
||||
"create_time": "2023-06-20T13:25:49.471837+00:00",
|
||||
"draft_mode": false,
|
||||
"labels": [
|
||||
"*"
|
||||
],
|
||||
"tags": [
|
||||
"message_identifier_activity",
|
||||
"internet_message_id",
|
||||
"splunk",
|
||||
"D3-IAA"
|
||||
]
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 55 KiB |
@@ -0,0 +1,240 @@
|
||||
"""
|
||||
Accepts an internet message id, and asks Splunk \n to look for records that have a matching internet message id. It then produces a normalized output and summary table.\n\nRef: D3-IAA https://d3fend.mitre.org/technique/d3f:IdentifierActivityAnalysis/
|
||||
"""
|
||||
|
||||
|
||||
import phantom.rules as phantom
|
||||
import json
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
|
||||
@phantom.playbook_block()
|
||||
def on_start(container):
|
||||
phantom.debug('on_start() called')
|
||||
|
||||
# call 'input_filter' block
|
||||
input_filter(container=container)
|
||||
|
||||
return
|
||||
|
||||
@phantom.playbook_block()
|
||||
def input_filter(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
|
||||
phantom.debug("input_filter() called")
|
||||
|
||||
# collect filtered artifact ids and results for 'if' condition 1
|
||||
matched_artifacts_1, matched_results_1 = phantom.condition(
|
||||
container=container,
|
||||
conditions=[
|
||||
["playbook_input:message_id", "!=", None]
|
||||
],
|
||||
name="input_filter:condition_1",
|
||||
delimiter=",")
|
||||
|
||||
# call connected blocks if filtered artifacts or results
|
||||
if matched_artifacts_1 or matched_results_1:
|
||||
format_message_query(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=matched_artifacts_1, filtered_results=matched_results_1)
|
||||
|
||||
return
|
||||
|
||||
|
||||
@phantom.playbook_block()
|
||||
def format_message_query(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
|
||||
phantom.debug("format_message_query() called")
|
||||
|
||||
template = """summariesonly=false fillnull_value=\"Unknown\" count from datamodel=Email.All_Email where All_Email.message_id=\"{0}\" by All_Email.orig_recipient, All_Email.recipient, All_Email.src_user, All_Email.subject | `drop_dm_object_name(\"All_Email\")` | rename orig_recipient as Addressee, recipient as Recipient, src_user as Sender, subject as Subject | fields Addressee, Recipient, Sender, Subject | fillnull value=\"Unknown\""""
|
||||
|
||||
# parameter list for template variable replacement
|
||||
parameters = [
|
||||
"filtered-data:input_filter:condition_1:playbook_input:message_id"
|
||||
]
|
||||
|
||||
################################################################################
|
||||
## Custom Code Start
|
||||
################################################################################
|
||||
|
||||
# Write your custom code here...
|
||||
|
||||
################################################################################
|
||||
## Custom Code End
|
||||
################################################################################
|
||||
|
||||
phantom.format(container=container, template=template, parameters=parameters, name="format_message_query")
|
||||
|
||||
run_message_query(container=container)
|
||||
|
||||
return
|
||||
|
||||
|
||||
@phantom.playbook_block()
|
||||
def run_message_query(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
|
||||
phantom.debug("run_message_query() called")
|
||||
|
||||
# phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED')))
|
||||
|
||||
format_message_query = phantom.get_format_data(name="format_message_query")
|
||||
|
||||
parameters = []
|
||||
|
||||
if format_message_query is not None:
|
||||
parameters.append({
|
||||
"query": format_message_query,
|
||||
"command": "tstats",
|
||||
"display": "Addressee,Recipient,Sender,Subject",
|
||||
"search_mode": "smart",
|
||||
})
|
||||
|
||||
################################################################################
|
||||
## Custom Code Start
|
||||
################################################################################
|
||||
|
||||
# Write your custom code here...
|
||||
|
||||
################################################################################
|
||||
## Custom Code End
|
||||
################################################################################
|
||||
|
||||
phantom.act("run query", parameters=parameters, name="run_message_query", assets=["splunk"], callback=results_filter)
|
||||
|
||||
return
|
||||
|
||||
|
||||
@phantom.playbook_block()
|
||||
def format_message_report(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
|
||||
phantom.debug("format_message_report() called")
|
||||
|
||||
template = """SOAR searched for occurrences of `{0}` within your environment using Splunk's Email datamodel. The table below shows a summary of the information gathered.\n\n\n| Recipient | Addressed To | Subject | Sender |\n| --- | --- | --- | --- |\n%%\n| {1} | {2} | {3} | {4} |\n%%\n\n"""
|
||||
|
||||
# parameter list for template variable replacement
|
||||
parameters = [
|
||||
"filtered-data:input_filter:condition_1:playbook_input:message_id",
|
||||
"filtered-data:results_filter:condition_1:run_message_query:action_result.data.*.Recipient",
|
||||
"filtered-data:results_filter:condition_1:run_message_query:action_result.data.*.Addressee",
|
||||
"filtered-data:results_filter:condition_1:run_message_query:action_result.data.*.Subject",
|
||||
"filtered-data:results_filter:condition_1:run_message_query:action_result.data.*.Sender"
|
||||
]
|
||||
|
||||
################################################################################
|
||||
## Custom Code Start
|
||||
################################################################################
|
||||
|
||||
# Write your custom code here...
|
||||
|
||||
################################################################################
|
||||
## Custom Code End
|
||||
################################################################################
|
||||
|
||||
phantom.format(container=container, template=template, parameters=parameters, name="format_message_report")
|
||||
|
||||
build_message_output(container=container)
|
||||
|
||||
return
|
||||
|
||||
|
||||
@phantom.playbook_block()
|
||||
def build_message_output(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
|
||||
phantom.debug("build_message_output() called")
|
||||
|
||||
################################################################################
|
||||
# Logic regarding observable construction goes here
|
||||
################################################################################
|
||||
|
||||
filtered_input_0_message_id = phantom.collect2(container=container, datapath=["filtered-data:input_filter:condition_1:playbook_input:message_id"])
|
||||
filtered_result_0_data_results_filter = phantom.collect2(container=container, datapath=["filtered-data:results_filter:condition_1:run_message_query:action_result.data.*.Recipient","filtered-data:results_filter:condition_1:run_message_query:action_result.data.*.Addressee","filtered-data:results_filter:condition_1:run_message_query:action_result.data.*.Subject","filtered-data:results_filter:condition_1:run_message_query:action_result.data.*.Sender"])
|
||||
|
||||
filtered_input_0_message_id_values = [item[0] for item in filtered_input_0_message_id]
|
||||
filtered_result_0_data___recipient = [item[0] for item in filtered_result_0_data_results_filter]
|
||||
filtered_result_0_data___addressee = [item[1] for item in filtered_result_0_data_results_filter]
|
||||
filtered_result_0_data___subject = [item[2] for item in filtered_result_0_data_results_filter]
|
||||
filtered_result_0_data___sender = [item[3] for item in filtered_result_0_data_results_filter]
|
||||
|
||||
build_message_output__observable_array = None
|
||||
|
||||
################################################################################
|
||||
## Custom Code Start
|
||||
################################################################################
|
||||
|
||||
# Variable renames for convenience
|
||||
message_id = filtered_input_0_message_id_values
|
||||
recipients = filtered_result_0_data___recipient
|
||||
addressees = filtered_result_0_data___addressee
|
||||
subjects = filtered_result_0_data___subject
|
||||
senders = filtered_result_0_data___sender
|
||||
|
||||
build_message_output__observable_array = []
|
||||
recordList = []
|
||||
|
||||
# unwind records
|
||||
for message_identifier in message_id:
|
||||
for recipient, addressee, subject, sender in zip(recipients, addressees, subjects, senders):
|
||||
record = {
|
||||
"recipient": recipient,
|
||||
"addressee": addressee,
|
||||
"subject": subject,
|
||||
"sender": sender
|
||||
}
|
||||
recordList.append(record)
|
||||
|
||||
observable = {
|
||||
"value": message_identifier,
|
||||
"type": "internet message ID",
|
||||
"count": len(recordList),
|
||||
"source": "Splunk",
|
||||
"message_identifier_activity": recordList
|
||||
}
|
||||
|
||||
build_message_output__observable_array.append(observable)
|
||||
|
||||
################################################################################
|
||||
## Custom Code End
|
||||
################################################################################
|
||||
|
||||
phantom.save_run_data(key="build_message_output:observable_array", value=json.dumps(build_message_output__observable_array))
|
||||
|
||||
return
|
||||
|
||||
|
||||
@phantom.playbook_block()
|
||||
def results_filter(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
|
||||
phantom.debug("results_filter() called")
|
||||
|
||||
# collect filtered artifact ids and results for 'if' condition 1
|
||||
matched_artifacts_1, matched_results_1 = phantom.condition(
|
||||
container=container,
|
||||
conditions=[
|
||||
["run_message_query:action_result.summary.total_events", ">", 0]
|
||||
],
|
||||
name="results_filter:condition_1",
|
||||
delimiter=",")
|
||||
|
||||
# call connected blocks if filtered artifacts or results
|
||||
if matched_artifacts_1 or matched_results_1:
|
||||
format_message_report(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=matched_artifacts_1, filtered_results=matched_results_1)
|
||||
|
||||
return
|
||||
|
||||
|
||||
@phantom.playbook_block()
|
||||
def on_finish(container, summary):
|
||||
phantom.debug("on_finish() called")
|
||||
|
||||
format_message_report = phantom.get_format_data(name="format_message_report")
|
||||
build_message_output__observable_array = json.loads(_ if (_ := phantom.get_run_data(key="build_message_output:observable_array")) != "" else "null") # pylint: disable=used-before-assignment
|
||||
|
||||
output = {
|
||||
"observable": build_message_output__observable_array,
|
||||
"markdown_report": format_message_report,
|
||||
}
|
||||
|
||||
################################################################################
|
||||
## Custom Code Start
|
||||
################################################################################
|
||||
|
||||
# Write your custom code here...
|
||||
|
||||
################################################################################
|
||||
## Custom Code End
|
||||
################################################################################
|
||||
|
||||
phantom.save_playbook_output_data(output=output)
|
||||
|
||||
return
|
||||
@@ -0,0 +1,23 @@
|
||||
name: Splunk Message Identifier Activity Analysis
|
||||
id: 5299b9dc-e8c4-46ba-d942-98dae0fa816d
|
||||
version: 1
|
||||
date: '2023-05-12'
|
||||
author: Lou Stella, Splunk
|
||||
type: Investigation
|
||||
description: "Accepts an internet message id, and asks Splunk
|
||||
to look for records that have a matching internet message id. It then produces a normalized output and summary table."
|
||||
playbook: Splunk_Message_Identifier_Activity_Analysis
|
||||
how_to_implement: This input playbook requires the Splunk connector to be configured. You will also need data populating the Email.All_Email datamodel in the out-of-the-box configuration of this playbook.
|
||||
references: []
|
||||
app_list:
|
||||
- Splunk
|
||||
tags:
|
||||
platform_tags:
|
||||
- message_identifier_activity
|
||||
- internet_message_id
|
||||
- splunk
|
||||
playbook_type: Input
|
||||
vpe_type: Modern
|
||||
playbook_fields: []
|
||||
product:
|
||||
- Splunk SOAR
|
||||
@@ -0,0 +1,276 @@
|
||||
{
|
||||
"blockly": false,
|
||||
"blockly_xml": "<xml></xml>",
|
||||
"category": "Isolation",
|
||||
"coa": {
|
||||
"data": {
|
||||
"description": "Accepts a URL or list of URLs as input. Blocks the given URLs in ZScaler.\n\nhttps://d3fend.mitre.org/technique/d3f:OutboundTrafficFiltering/",
|
||||
"edges": [
|
||||
{
|
||||
"id": "port_0_to_port_4",
|
||||
"sourceNode": "0",
|
||||
"sourcePort": "0_out",
|
||||
"targetNode": "4",
|
||||
"targetPort": "4_in"
|
||||
},
|
||||
{
|
||||
"conditions": [
|
||||
{
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
"id": "port_4_to_port_6",
|
||||
"sourceNode": "4",
|
||||
"sourcePort": "4_out",
|
||||
"targetNode": "6",
|
||||
"targetPort": "6_in"
|
||||
},
|
||||
{
|
||||
"id": "port_7_to_port_1",
|
||||
"sourceNode": "7",
|
||||
"sourcePort": "7_out",
|
||||
"targetNode": "1",
|
||||
"targetPort": "1_in"
|
||||
},
|
||||
{
|
||||
"id": "port_6_to_port_8",
|
||||
"sourceNode": "6",
|
||||
"sourcePort": "6_out",
|
||||
"targetNode": "8",
|
||||
"targetPort": "8_in"
|
||||
},
|
||||
{
|
||||
"conditions": [
|
||||
{
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
"id": "port_8_to_port_7",
|
||||
"sourceNode": "8",
|
||||
"sourcePort": "8_out",
|
||||
"targetNode": "7",
|
||||
"targetPort": "7_in"
|
||||
}
|
||||
],
|
||||
"hash": "ddce2d54e4936120ec7acc772b88f91fab41ec55",
|
||||
"nodes": {
|
||||
"0": {
|
||||
"data": {
|
||||
"advanced": {
|
||||
"join": []
|
||||
},
|
||||
"functionName": "on_start",
|
||||
"id": "0",
|
||||
"type": "start"
|
||||
},
|
||||
"errors": {},
|
||||
"id": "0",
|
||||
"type": "start",
|
||||
"warnings": {},
|
||||
"x": 19.999999999999986,
|
||||
"y": -6.394884621840902e-14
|
||||
},
|
||||
"1": {
|
||||
"data": {
|
||||
"advanced": {
|
||||
"join": []
|
||||
},
|
||||
"functionName": "on_finish",
|
||||
"id": "1",
|
||||
"type": "end"
|
||||
},
|
||||
"errors": {},
|
||||
"id": "1",
|
||||
"type": "end",
|
||||
"warnings": {},
|
||||
"x": 19.999999999999986,
|
||||
"y": 864
|
||||
},
|
||||
"4": {
|
||||
"data": {
|
||||
"advanced": {
|
||||
"customName": "url input filter",
|
||||
"customNameId": 0,
|
||||
"delimiter": ",",
|
||||
"delimiter_enabled": true,
|
||||
"description": "Determine branches based on provided inputs.",
|
||||
"join": [],
|
||||
"note": "Determine branches based on provided inputs."
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"comparisons": [
|
||||
{
|
||||
"conditionIndex": 0,
|
||||
"op": "!=",
|
||||
"param": "playbook_input:input_url",
|
||||
"value": "None"
|
||||
}
|
||||
],
|
||||
"conditionIndex": 0,
|
||||
"customName": "input_url",
|
||||
"logic": "and"
|
||||
}
|
||||
],
|
||||
"functionId": 1,
|
||||
"functionName": "url_input_filter",
|
||||
"id": "4",
|
||||
"type": "filter"
|
||||
},
|
||||
"errors": {},
|
||||
"id": "4",
|
||||
"type": "filter",
|
||||
"warnings": {},
|
||||
"x": 60,
|
||||
"y": 148
|
||||
},
|
||||
"6": {
|
||||
"data": {
|
||||
"action": "block url",
|
||||
"actionType": "contain",
|
||||
"advanced": {
|
||||
"customName": "block url",
|
||||
"customNameId": 0,
|
||||
"description": "Block urls in ZScaler based on given urls. ",
|
||||
"join": [],
|
||||
"note": "Block urls in ZScaler based on given urls. "
|
||||
},
|
||||
"connector": "Zscaler",
|
||||
"connectorConfigs": [
|
||||
"zscaler"
|
||||
],
|
||||
"connectorId": "ee475421-d2e4-4d8e-addb-a2ce1465d6a3",
|
||||
"connectorVersion": "v1",
|
||||
"functionId": 1,
|
||||
"functionName": "block_url",
|
||||
"id": "6",
|
||||
"parameters": {
|
||||
"url": "filtered-data:url_input_filter:condition_1:playbook_input:input_url"
|
||||
},
|
||||
"requiredParameters": [
|
||||
{
|
||||
"data_type": "string",
|
||||
"field": "url"
|
||||
}
|
||||
],
|
||||
"type": "action"
|
||||
},
|
||||
"errors": {},
|
||||
"id": "6",
|
||||
"type": "action",
|
||||
"warnings": {},
|
||||
"x": 0,
|
||||
"y": 328
|
||||
},
|
||||
"7": {
|
||||
"data": {
|
||||
"advanced": {
|
||||
"customName": "build observable",
|
||||
"customNameId": 0,
|
||||
"description": "This block uses custom code to generate an observable dictionary to output into the observables data path.",
|
||||
"join": [],
|
||||
"note": "This block uses custom code to generate an observable dictionary to output into the observables data path."
|
||||
},
|
||||
"functionId": 1,
|
||||
"functionName": "build_observable",
|
||||
"id": "7",
|
||||
"inputParameters": [
|
||||
"block_url:action_result.summary",
|
||||
"block_url:action_result.status",
|
||||
"block_url:action_result.message",
|
||||
"filtered-data:url_input_filter:condition_1:playbook_input:input_url"
|
||||
],
|
||||
"outputVariables": [
|
||||
"observable_array"
|
||||
],
|
||||
"type": "code"
|
||||
},
|
||||
"errors": {},
|
||||
"id": "7",
|
||||
"type": "code",
|
||||
"userCode": "\n build_observable__observable_array = list()\n for summary, status, message, url in zip(block_url_result_item_0, block_url_result_item_1, block_url_result_message, filtered_input_0_input_url_values):\n observable = {\n \"type\": \"url\",\n \"value\": url,\n \"source\": \"ZScaler\"\n }\n if len(summary[\"ignored\"]) > 0:\n observable[\"status\"] = \"ignored\"\n else:\n observable[\"status\"] = \"updated\"\n \n build_observable__observable_array.append(observable)\n \n\n",
|
||||
"warnings": {},
|
||||
"x": 0,
|
||||
"y": 700
|
||||
},
|
||||
"8": {
|
||||
"data": {
|
||||
"advanced": {
|
||||
"customName": "block url success filter",
|
||||
"customNameId": 0,
|
||||
"delimiter": ",",
|
||||
"delimiter_enabled": true,
|
||||
"description": "Determine if the block url action was successful or not.",
|
||||
"join": [],
|
||||
"note": "Determine if the block url action was successful or not."
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"comparisons": [
|
||||
{
|
||||
"conditionIndex": 0,
|
||||
"op": "==",
|
||||
"param": "block_url:action_result.status",
|
||||
"value": "success"
|
||||
}
|
||||
],
|
||||
"conditionIndex": 0,
|
||||
"customName": "success",
|
||||
"logic": "and"
|
||||
}
|
||||
],
|
||||
"functionId": 2,
|
||||
"functionName": "block_url_success_filter",
|
||||
"id": "8",
|
||||
"type": "filter"
|
||||
},
|
||||
"errors": {},
|
||||
"id": "8",
|
||||
"type": "filter",
|
||||
"warnings": {},
|
||||
"x": 60,
|
||||
"y": 506
|
||||
}
|
||||
},
|
||||
"notes": "Inputs: url\nInteractions: ZScaler\nActions: block url\nOutputs: observables"
|
||||
},
|
||||
"input_spec": [
|
||||
{
|
||||
"contains": [
|
||||
"url"
|
||||
],
|
||||
"description": "Accepts url and block them",
|
||||
"name": "input_url"
|
||||
}
|
||||
],
|
||||
"output_spec": [
|
||||
{
|
||||
"contains": [
|
||||
"url"
|
||||
],
|
||||
"datapaths": [
|
||||
"build_observable:custom_function:observable_array"
|
||||
],
|
||||
"deduplicate": false,
|
||||
"description": "An array of observable dictionaries with value, type and information about the blocked urls.",
|
||||
"metadata": {},
|
||||
"name": "observable"
|
||||
}
|
||||
],
|
||||
"playbook_type": "data",
|
||||
"python_version": "3",
|
||||
"schema": "5.0.10",
|
||||
"version": "6.0.1.123902"
|
||||
},
|
||||
"create_time": "2023-06-21T09:27:47.392005+00:00",
|
||||
"draft_mode": false,
|
||||
"labels": [
|
||||
"*"
|
||||
],
|
||||
"tags": [
|
||||
"url",
|
||||
"D3-OTF",
|
||||
"ZScaler",
|
||||
"denylist"
|
||||
]
|
||||
}
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
@@ -0,0 +1,173 @@
|
||||
"""
|
||||
Accepts a URL or list of URLs as input. Blocks the given URLs in ZScaler.\n\nhttps://d3fend.mitre.org/technique/d3f:OutboundTrafficFiltering/
|
||||
"""
|
||||
|
||||
|
||||
import phantom.rules as phantom
|
||||
import json
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
|
||||
@phantom.playbook_block()
|
||||
def on_start(container):
|
||||
phantom.debug('on_start() called')
|
||||
|
||||
# call 'url_input_filter' block
|
||||
url_input_filter(container=container)
|
||||
|
||||
return
|
||||
|
||||
@phantom.playbook_block()
|
||||
def url_input_filter(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
|
||||
phantom.debug("url_input_filter() called")
|
||||
|
||||
################################################################################
|
||||
# Determine branches based on provided inputs.
|
||||
################################################################################
|
||||
|
||||
# collect filtered artifact ids and results for 'if' condition 1
|
||||
matched_artifacts_1, matched_results_1 = phantom.condition(
|
||||
container=container,
|
||||
conditions=[
|
||||
["playbook_input:input_url", "!=", None]
|
||||
],
|
||||
name="url_input_filter:condition_1",
|
||||
delimiter=",")
|
||||
|
||||
# call connected blocks if filtered artifacts or results
|
||||
if matched_artifacts_1 or matched_results_1:
|
||||
block_url(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=matched_artifacts_1, filtered_results=matched_results_1)
|
||||
|
||||
return
|
||||
|
||||
|
||||
@phantom.playbook_block()
|
||||
def block_url(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
|
||||
phantom.debug("block_url() called")
|
||||
|
||||
# phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED')))
|
||||
|
||||
################################################################################
|
||||
# Block urls in ZScaler based on given urls.
|
||||
################################################################################
|
||||
|
||||
filtered_input_0_input_url = phantom.collect2(container=container, datapath=["filtered-data:url_input_filter:condition_1:playbook_input:input_url"])
|
||||
|
||||
parameters = []
|
||||
|
||||
# build parameters list for 'block_url' call
|
||||
for filtered_input_0_input_url_item in filtered_input_0_input_url:
|
||||
if filtered_input_0_input_url_item[0] is not None:
|
||||
parameters.append({
|
||||
"url": filtered_input_0_input_url_item[0],
|
||||
})
|
||||
|
||||
################################################################################
|
||||
## Custom Code Start
|
||||
################################################################################
|
||||
|
||||
# Write your custom code here...
|
||||
|
||||
################################################################################
|
||||
## Custom Code End
|
||||
################################################################################
|
||||
|
||||
phantom.act("block url", parameters=parameters, name="block_url", assets=["zscaler"], callback=block_url_success_filter)
|
||||
|
||||
return
|
||||
|
||||
|
||||
@phantom.playbook_block()
|
||||
def build_observable(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
|
||||
phantom.debug("build_observable() called")
|
||||
|
||||
################################################################################
|
||||
# This block uses custom code to generate an observable dictionary to output into
|
||||
# the observables data path.
|
||||
################################################################################
|
||||
|
||||
block_url_result_data = phantom.collect2(container=container, datapath=["block_url:action_result.summary","block_url:action_result.status","block_url:action_result.message"], action_results=results)
|
||||
filtered_input_0_input_url = phantom.collect2(container=container, datapath=["filtered-data:url_input_filter:condition_1:playbook_input:input_url"])
|
||||
|
||||
block_url_result_item_0 = [item[0] for item in block_url_result_data]
|
||||
block_url_result_item_1 = [item[1] for item in block_url_result_data]
|
||||
block_url_result_message = [item[2] for item in block_url_result_data]
|
||||
filtered_input_0_input_url_values = [item[0] for item in filtered_input_0_input_url]
|
||||
|
||||
build_observable__observable_array = None
|
||||
|
||||
################################################################################
|
||||
## Custom Code Start
|
||||
################################################################################
|
||||
|
||||
build_observable__observable_array = list()
|
||||
for summary, status, message, url in zip(block_url_result_item_0, block_url_result_item_1, block_url_result_message, filtered_input_0_input_url_values):
|
||||
observable = {
|
||||
"type": "url",
|
||||
"value": url,
|
||||
"source": "ZScaler"
|
||||
}
|
||||
if len(summary["ignored"]) > 0:
|
||||
observable["status"] = "ignored"
|
||||
else:
|
||||
observable["status"] = "updated"
|
||||
|
||||
build_observable__observable_array.append(observable)
|
||||
|
||||
|
||||
################################################################################
|
||||
## Custom Code End
|
||||
################################################################################
|
||||
|
||||
phantom.save_run_data(key="build_observable:observable_array", value=json.dumps(build_observable__observable_array))
|
||||
|
||||
return
|
||||
|
||||
|
||||
@phantom.playbook_block()
|
||||
def block_url_success_filter(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
|
||||
phantom.debug("block_url_success_filter() called")
|
||||
|
||||
################################################################################
|
||||
# Determine if the block url action was successful or not.
|
||||
################################################################################
|
||||
|
||||
# collect filtered artifact ids and results for 'if' condition 1
|
||||
matched_artifacts_1, matched_results_1 = phantom.condition(
|
||||
container=container,
|
||||
conditions=[
|
||||
["block_url:action_result.status", "==", "success"]
|
||||
],
|
||||
name="block_url_success_filter:condition_1",
|
||||
delimiter=",")
|
||||
|
||||
# call connected blocks if filtered artifacts or results
|
||||
if matched_artifacts_1 or matched_results_1:
|
||||
build_observable(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=matched_artifacts_1, filtered_results=matched_results_1)
|
||||
|
||||
return
|
||||
|
||||
|
||||
@phantom.playbook_block()
|
||||
def on_finish(container, summary):
|
||||
phantom.debug("on_finish() called")
|
||||
|
||||
build_observable__observable_array = json.loads(_ if (_ := phantom.get_run_data(key="build_observable:observable_array")) != "" else "null") # pylint: disable=used-before-assignment
|
||||
|
||||
output = {
|
||||
"observable": build_observable__observable_array,
|
||||
}
|
||||
|
||||
################################################################################
|
||||
## Custom Code Start
|
||||
################################################################################
|
||||
|
||||
# Write your custom code here...
|
||||
|
||||
################################################################################
|
||||
## Custom Code End
|
||||
################################################################################
|
||||
|
||||
phantom.save_playbook_output_data(output=output)
|
||||
|
||||
return
|
||||
@@ -0,0 +1,24 @@
|
||||
name: ZScaler Outbound Traffic Filtering
|
||||
id: 3e0df448-0546-4b2b-9143-365161cf40f9
|
||||
version: 1
|
||||
date: '2023-03-31'
|
||||
author: Patrick Bareiss, Splunk
|
||||
type: Response
|
||||
description: Accepts a URL or list of URLs and block them in ZScaler. Generates a list of observables with the blocked URLs.
|
||||
playbook: ZScaler_Outbound_Traffic_Filtering
|
||||
how_to_implement: This input playbook requires the ZScaler connector to be configured. It is designed to work in conjunction with the Dynamic URL Outbound Traffic Filtering Analysis playbook or other playbooks in the same style.
|
||||
references:
|
||||
- https://d3fend.mitre.org/technique/d3f:OutboundTrafficFiltering/
|
||||
app_list:
|
||||
- ZScaler
|
||||
tags:
|
||||
platform_tags:
|
||||
- denylist
|
||||
- url
|
||||
- D3-OTF
|
||||
- ZScaler
|
||||
playbook_type: Input
|
||||
vpe_type: Modern
|
||||
playbook_fields: []
|
||||
product:
|
||||
- Splunk SOAR
|
||||
Reference in New Issue
Block a user