diff --git a/playbooks/AD_LDAP_Account_Locking.json b/playbooks/AD_LDAP_Account_Locking.json new file mode 100644 index 0000000000..a607c65591 --- /dev/null +++ b/playbooks/AD_LDAP_Account_Locking.json @@ -0,0 +1,283 @@ +{ + "blockly": false, + "blockly_xml": "", + "category": "Account Locking", + "coa": { + "data": { + "description": "Accepts user name that needs to be disabled in Microsoft LDAP Active Directory. Generates an observable output based on the status of account locking or disabling.", + "edges": [ + { + "id": "port_0_to_port_2", + "sourceNode": "0", + "sourcePort": "0_out", + "targetNode": "2", + "targetPort": "2_in" + }, + { + "conditions": [ + { + "index": 0 + } + ], + "id": "port_2_to_port_3", + "sourceNode": "2", + "sourcePort": "2_out", + "targetNode": "3", + "targetPort": "3_in" + }, + { + "id": "port_4_to_port_1", + "sourceNode": "4", + "sourcePort": "4_out", + "targetNode": "1", + "targetPort": "1_in" + }, + { + "id": "port_3_to_port_5", + "sourceNode": "3", + "sourcePort": "3_out", + "targetNode": "5", + "targetPort": "5_in" + }, + { + "conditions": [ + { + "index": 0 + } + ], + "id": "port_5_to_port_4", + "sourceNode": "5", + "sourcePort": "5_out", + "targetNode": "4", + "targetPort": "4_in" + } + ], + "hash": "9e264b4abd2e3c0730a58dd66c6ef88d9b2453af", + "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 + }, + "2": { + "data": { + "advanced": { + "customName": "username filter", + "customNameId": 0, + "description": "Filter user name inputs to route inputs to appropriate actions.", + "join": [], + "note": "Filter user name inputs to route inputs to appropriate actions." + }, + "conditions": [ + { + "comparisons": [ + { + "conditionIndex": 0, + "op": "!=", + "param": "playbook_input:user_name", + "value": "" + } + ], + "conditionIndex": 0, + "customName": "filter_username_check", + "logic": "and" + } + ], + "functionId": 1, + "functionName": "username_filter", + "id": "2", + "type": "filter" + }, + "errors": {}, + "id": "2", + "type": "filter", + "warnings": { + "config": [ + "Reconfigure invalid datapath." + ] + }, + "x": 60, + "y": 140 + }, + "3": { + "data": { + "action": "disable account", + "actionType": "generic", + "advanced": { + "customName": "disable user account", + "customNameId": 0, + "delayTime": 0, + "description": "Disable user account from filtered playbook inputs.", + "join": [], + "note": "Disable user account from filtered playbook inputs." + }, + "connector": "AD LDAP", + "connectorConfigs": [ + "microsoft ad ldap" + ], + "connectorId": "a5730e5d-a396-4695-92c2-35ff391aaf45", + "connectorVersion": "v1", + "functionId": 1, + "functionName": "disable_user_account", + "id": "3", + "parameters": { + "use_samaccountname": true, + "user": "playbook_input:user_name" + }, + "requiredParameters": [ + { + "data_type": "string", + "default": false, + "field": "user" + } + ], + "type": "action" + }, + "errors": {}, + "id": "3", + "type": "action", + "warnings": { + "config": [ + "Reconfigure invalid datapath." + ] + }, + "x": 0, + "y": 328 + }, + "4": { + "data": { + "advanced": { + "customName": "username observables", + "customNameId": 0, + "description": "Format a normalized output for each user.", + "join": [], + "note": "Format a normalized output for each user." + }, + "functionId": 1, + "functionName": "username_observables", + "id": "4", + "inputParameters": [ + "disable_user_account:action_result.parameter.user", + "disable_user_account:action_result.parameter.use_samaccountname", + "disable_user_account:action_result.data.*.user_dn", + "disable_user_account:action_result.status", + "disable_user_account:action_result.message", + "disable_user_account:action_result.data.*.starting_status" + ], + "outputVariables": [ + "observable_array" + ], + "type": "code" + }, + "errors": {}, + "id": "4", + "type": "code", + "userCode": "\n # Write your custom code here...\n username_observables__observable_array = []\n \n for user, sam_account, user_dn, status, msg, prev_status in zip(disable_user_account_parameter_user, disable_user_account_parameter_use_samaccountname, disable_user_account_result_item_2, disable_user_account_result_item_3, disable_user_account_result_message, disable_user_account_result_item_5):\n user_acc_status = {\n \"type\": \"Microsoft AD LDAP Account\",\n \"value\": user,\n \"previous_status\": prev_status,\n \"sam_account\": sam_account,\n \"user_dn\": user_dn,\n \"message\": msg,\n \"status\": status\n }\n \n username_observables__observable_array.append(user_acc_status)\n #phantom.debug(username_observables__observable_array)\n", + "warnings": {}, + "x": 0, + "y": 686 + }, + "5": { + "data": { + "advanced": { + "customName": "filter disable account", + "customNameId": 0, + "description": "filter check if the user is disabled successfully.", + "join": [], + "note": "filter check if the user is disabled successfully." + }, + "conditions": [ + { + "comparisons": [ + { + "conditionIndex": 0, + "op": "==", + "param": "disable_user_account:action_result.status", + "value": "success" + } + ], + "conditionIndex": 0, + "customName": "disabled_success", + "logic": "and" + } + ], + "functionId": 2, + "functionName": "filter_disable_account", + "id": "5", + "type": "filter" + }, + "errors": {}, + "id": "5", + "type": "filter", + "warnings": {}, + "x": 60, + "y": 500 + } + }, + "notes": "Inputs: users\nInteractions: Microsoft AD LDAP\nActions: Account Locking/Disabling\nOutputs: observables" + }, + "input_spec": [ + { + "contains": [ + "user name" + ], + "description": "A user name provided to be disable - AD LDAP", + "name": "user_name" + } + ], + "output_spec": [ + { + "contains": [], + "datapaths": [ + "username_observables:custom_function:observable_array" + ], + "deduplicate": false, + "description": "An array of observable dictionaries ", + "metadata": {}, + "name": "observable" + } + ], + "playbook_type": "data", + "python_version": "3", + "schema": "5.0.9", + "version": "6.0.0.114895" + }, + "create_time": "2023-05-15T10:29:31.917563+00:00", + "draft_mode": false, + "labels": [ + "*" + ], + "tags": [ + "user", + "microsoft_ad_ldap", + "disable_account", + "D3-AL" + ] +} \ No newline at end of file diff --git a/playbooks/AD_LDAP_Account_Locking.png b/playbooks/AD_LDAP_Account_Locking.png new file mode 100644 index 0000000000..683568a458 Binary files /dev/null and b/playbooks/AD_LDAP_Account_Locking.png differ diff --git a/playbooks/AD_LDAP_Account_Locking.py b/playbooks/AD_LDAP_Account_Locking.py new file mode 100644 index 0000000000..83697b1fbd --- /dev/null +++ b/playbooks/AD_LDAP_Account_Locking.py @@ -0,0 +1,173 @@ +""" +Accepts user name that needs to be disabled in Microsoft LDAP Active Directory. Generates an observable output based on the status of account locking or disabling. +""" + + +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 'username_filter' block + username_filter(container=container) + + return + +@phantom.playbook_block() +def username_filter(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("username_filter() called") + + ################################################################################ + # Filter user name inputs to route inputs to appropriate actions. + ################################################################################ + + # collect filtered artifact ids and results for 'if' condition 1 + matched_artifacts_1, matched_results_1 = phantom.condition( + container=container, + conditions=[ + ["playbook_input:user_name", "!=", ""] + ], + name="username_filter:condition_1") + + # call connected blocks if filtered artifacts or results + if matched_artifacts_1 or matched_results_1: + disable_user_account(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 disable_user_account(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("disable_user_account() called") + + # phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED'))) + + ################################################################################ + # Disable user account from filtered playbook inputs. + ################################################################################ + + playbook_input_user_name = phantom.collect2(container=container, datapath=["playbook_input:user_name"]) + + parameters = [] + + # build parameters list for 'disable_user_account' call + for playbook_input_user_name_item in playbook_input_user_name: + if playbook_input_user_name_item[0] is not None: + parameters.append({ + "user": playbook_input_user_name_item[0], + "use_samaccountname": True, + }) + + ################################################################################ + ## Custom Code Start + ################################################################################ + + # Write your custom code here... + + ################################################################################ + ## Custom Code End + ################################################################################ + + phantom.act("disable account", parameters=parameters, name="disable_user_account", assets=["microsoft ad ldap"], callback=filter_disable_account) + + return + + +@phantom.playbook_block() +def username_observables(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("username_observables() called") + + ################################################################################ + # Format a normalized output for each user. + ################################################################################ + + disable_user_account_result_data = phantom.collect2(container=container, datapath=["disable_user_account:action_result.parameter.user","disable_user_account:action_result.parameter.use_samaccountname","disable_user_account:action_result.data.*.user_dn","disable_user_account:action_result.status","disable_user_account:action_result.message","disable_user_account:action_result.data.*.starting_status"], action_results=results) + + disable_user_account_parameter_user = [item[0] for item in disable_user_account_result_data] + disable_user_account_parameter_use_samaccountname = [item[1] for item in disable_user_account_result_data] + disable_user_account_result_item_2 = [item[2] for item in disable_user_account_result_data] + disable_user_account_result_item_3 = [item[3] for item in disable_user_account_result_data] + disable_user_account_result_message = [item[4] for item in disable_user_account_result_data] + disable_user_account_result_item_5 = [item[5] for item in disable_user_account_result_data] + + username_observables__observable_array = None + + ################################################################################ + ## Custom Code Start + ################################################################################ + + # Write your custom code here... + username_observables__observable_array = [] + + for user, sam_account, user_dn, status, msg, prev_status in zip(disable_user_account_parameter_user, disable_user_account_parameter_use_samaccountname, disable_user_account_result_item_2, disable_user_account_result_item_3, disable_user_account_result_message, disable_user_account_result_item_5): + user_acc_status = { + "type": "Microsoft AD LDAP Account", + "value": user, + "previous_status": prev_status, + "sam_account": sam_account, + "user_dn": user_dn, + "message": msg, + "status": status + } + + username_observables__observable_array.append(user_acc_status) + #phantom.debug(username_observables__observable_array) + ################################################################################ + ## Custom Code End + ################################################################################ + + phantom.save_run_data(key="username_observables:observable_array", value=json.dumps(username_observables__observable_array)) + + return + + +@phantom.playbook_block() +def filter_disable_account(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("filter_disable_account() called") + + ################################################################################ + # filter check if the user is disabled successfully. + ################################################################################ + + # collect filtered artifact ids and results for 'if' condition 1 + matched_artifacts_1, matched_results_1 = phantom.condition( + container=container, + conditions=[ + ["disable_user_account:action_result.status", "==", "success"] + ], + name="filter_disable_account:condition_1") + + # call connected blocks if filtered artifacts or results + if matched_artifacts_1 or matched_results_1: + username_observables(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") + + username_observables__observable_array = json.loads(_ if (_ := phantom.get_run_data(key="username_observables:observable_array")) != "" else "null") # pylint: disable=used-before-assignment + + output = { + "observable": username_observables__observable_array, + } + + ################################################################################ + ## Custom Code Start + ################################################################################ + + # Write your custom code here... + + ################################################################################ + ## Custom Code End + ################################################################################ + + phantom.save_playbook_output_data(output=output) + + return \ No newline at end of file diff --git a/playbooks/AD_LDAP_Account_Locking.yml b/playbooks/AD_LDAP_Account_Locking.yml new file mode 100644 index 0000000000..de25477e50 --- /dev/null +++ b/playbooks/AD_LDAP_Account_Locking.yml @@ -0,0 +1,24 @@ +name: AD LDAP Account Locking +id: e6f96caf-610c-4ced-aa2c-ba9b19b89e1f +version: 1 +date: '2023-05-08' +author: Teoderick Contreras, Splunk +type: Investigation +description: "Accepts user, to be disabled using Microsoft AD LDAP connector. This playbook produces a normalized observables output for each user and device." +playbook: AD_LDAP_Account_Locking +how_to_implement: This input playbook requires the Microsoft AD LDAP connector to be configured. + It is designed to work in conjunction with the Dynamic Attribute Lookup playbook or other playbooks in the same style. +references: [] +app_list: + - AD LDAP API +tags: + platform_tags: + - user + - microsoft_ad_ldap + - D3-AL + - disable_account + playbook_type: Input + vpe_type: Modern + playbook_fields: [] + product: + - Splunk SOAR