From c5329df2af35b5048e381eeb8bf02b1e025ef8c1 Mon Sep 17 00:00:00 2001 From: JD Babac Date: Sat, 6 Sep 2025 02:49:20 +0800 Subject: [PATCH] IDEV-2225: Add Domain Hotlist playbook. --- .../DomainTools_Feeds_Domain_Hotlist.tgz | Bin 0 -> 2777 bytes .../README.md | 12 ++ .../src/DomainTools Feeds Domain Hotlist.json | 153 ++++++++++++++++++ .../src/DomainTools Feeds Domain Hotlist.py | 127 +++++++++++++++ 4 files changed, 292 insertions(+) create mode 100644 Splunk SOAR/DomainTools_Feeds_Domain_Hotlist/DomainTools_Feeds_Domain_Hotlist.tgz create mode 100644 Splunk SOAR/DomainTools_Feeds_Domain_Hotlist/README.md create mode 100644 Splunk SOAR/DomainTools_Feeds_Domain_Hotlist/src/DomainTools Feeds Domain Hotlist.json create mode 100644 Splunk SOAR/DomainTools_Feeds_Domain_Hotlist/src/DomainTools Feeds Domain Hotlist.py diff --git a/Splunk SOAR/DomainTools_Feeds_Domain_Hotlist/DomainTools_Feeds_Domain_Hotlist.tgz b/Splunk SOAR/DomainTools_Feeds_Domain_Hotlist/DomainTools_Feeds_Domain_Hotlist.tgz new file mode 100644 index 0000000000000000000000000000000000000000..d7606aced37cbb95c7e289d4cf83a2d8bc930164 GIT binary patch literal 2777 zcmV;~3MTa*iwFQ%FuQ01|Lt2@Z{s);&U1bRp+R7K)>b6h@rChX8c7=6z)YjDeJmES zA;=PKYoco!e7?^~p(gR*QZ=_EjU#0N(rtBOVPt71{C$fxq@@1Kpk57@A9c=RGg ziOa6Piqh?6&$M1uG*vr7cSo;K))6s6=>F*MS+pT?iS6QvIyf6BqoLAM<$kvZYce`M zs>@GbE_~Opy|2FSMCd)nR)pdj^uZ^N9T7RWe-``i_nEx|%GCT{`lz01%8`1eob~%% zMHy&d|AU@3I6}&6+JCSzJmR}M=?=BO(IQFG@iF^3w%x!F3E~5l!?lAWWJF0t(*9TE zd&i5=cadcfOnnF9Ey;Tu(O(NY!~n6sCjtYWUs!e!z4df9BGbS-2Wj)Sd6>zL8~!roECVzAX$F>Lh(U|Clii1oz?O9`@W zCz%4V;1H8_=pE9(T&iIb+xI4)d=DlgTAL=0BL0?j7{7-&T010Zg{~|I^OuF~5FCO) z7`MFunNhJpWHtfIzN;_RUcP(B_X9e21nYF^cvxPElnr`NHw}5=hps{RbX+w&!^MqR8#NnWzMY?6US3>YG?2YWW(Z*?!l?27-N!$m zq1EEu1V#vaFyIJH(ABJ1DH}n6J*)Y^>S>YY7&^w%7)eq)tBs%wP(!yZ+G2YIFGGVW z7Ig@Yi0v)&)Wi>R0yfEzpSf_M=$tJbI`=J%zEGUMOmBSI{vFx`qkDfHa?AiKq2m=` zN|xo<8|kHIz2G<#a02=gL?e!9L=i2ub10f*k8MP8Iw>~-U^1czgUwxW0z&sr6Ra_k z0NT_-=7xDa@z*3+6P*hQ?dV#kVhYaXkasK>^5*@^u5lGTOz9GEt#sIK4DbkvXwwHy zlW1iwy-oKfBQVG+UABVIaXh75woJ+CatgK4g#WFg9axyFE7W7rrz>TKz$YR4i~xrR z2R8c~`5Q{KH=tk?yv~enVkgah@eTHfPVE(DI$dsgiNWM)EdYs0w;h~NE%J67&wODu z9+G}#&%bT9_e7y)ghLr^F3TbNt|$q+9s$@T~)rFljVe zGNX_hK+OOX-MwTo`#3)>_NDq*vrHFZhYvFJFX30J(8 z3MojF*`JuInNd;mV=m*6`Nft?EfzSgX4#Aty9fatRANh@5MF*k7kB}yfyfG@^UL?C z1_t_kPgXvtVlx+Gg!clYi5H23dk`8}aAL7|g#|AcUKGO$q7}*zW1lZ*tG&&et#F

wwp3FQgo3JKQyI4a*gUV$P{x@+JI|EV73enw zgnWx)cw|VRo&h2X@IrtSuxFHP&;n-~-&olW-rUlxY&YB@a*R24CV9u$f$kj&iVii7 zNZRI!!1sa`+)t#Hy=WlK!= zg{@rx{TYuCj#7NA8!7WLY4G@S#p#i8gX8Fzd*bFb26XL+6?NVApgtNaj)jGm%uBRm?-O{iTI zZO8)^qb?_sPY$w^Ky}O^}+Tj!ZtPAsy+z3m1 zcw%rU`$))rz{JI}jvt|w#7^^UN_SE0@4wLZ4x(El^lWdrx1T#`R7dhCUxxi1#MD`L zR9PDgM}z)I>vj7>b*OZ4N9|UzL%{gkL`!_eJ`5MN3S6*fy$o2?Ltvpjza~_Ay{6IX9`kseP;)hhXeBAvpwBe_kvZ;`B z-&@$r@}NkZ?&2-=PV!lmem^plF4WLP3THow7yu_ec|LP+wf5$ccAupiqBcXTRhv9-fW9~3q<$~(n)OR*{w;(wD=GiPr zaHXmo-|1IPIxR{C4gij2(+RqKw0eKQuvv$|qaC0RjCF)74~0=D!_b~&UReUi;gu;( z5Gs!ZACojx?zed&(US%h;{UE~u-ec0!ClQNY*5^XzQ`V>9onOK=R2risl#&~Fi*WI z--xH))ces*Pua7_sXe9AuGW2>&?laOG<0DXApFi6(F+hnJ=r1LWkQ(CJmealC)|3p zciwTkbJ#!c-Co$$kZ3biHpvQCNKe{4<3WBmLYsQr$|g)L;AITBd&q!U*UxCa2UC1v zk}St3en;`2Jy||emgX~+TD*^ZqR;f1GkvCfxP@Gw>64RvQnlmXT)xk=!o~DdYNM%i zJ3XB9!zWWr`blOd7L(?G#IHhHM_iRp*NAJRFrJ3TkSFdxz!iAa`okm z1)K=Xg|>15BB}&_v8kLr2SI^1SHDB?0$4@O3ytL>L~IKJ#HMog)DQ&Dw6@5!DT3({ zX+t7wY2MJbhGpq9+f;Gp&~#7g5SaZoTbevHW!XceG%Vp}T9!amO1_msB$Bjf4EaJ? zL{+_YSsqb-11Mw?WdkUr5>0ArqtKnVp#Nf-t9vBSEh_FxJ4lxFMuFoBL5vNN1q(v)&Mu7aEOxvwoU0b**BZ6@DDcT z3cK_WB%R5MtPb*RIi9}UgXWPE&%h?RC}djc)NimOrlOs7lu<`H`>GDdy}?)=$-RDW z&>Q_jQN~L0NnNr+V?lH`4oCBMm&`GVk-x;^Nd5tg2jCJh;*V&v<4v6nP?x&Yr7m@; fOI_+xm%7xYE_JC(UFuSopRN2K!|w;E08jt`FV$-( literal 0 HcmV?d00001 diff --git a/Splunk SOAR/DomainTools_Feeds_Domain_Hotlist/README.md b/Splunk SOAR/DomainTools_Feeds_Domain_Hotlist/README.md new file mode 100644 index 0000000..e45ff3e --- /dev/null +++ b/Splunk SOAR/DomainTools_Feeds_Domain_Hotlist/README.md @@ -0,0 +1,12 @@ +## DomainTools Feeds Domain Hotlist Playbook + +This playbook retrieves newly observed domains from DomainTools Real Time Unified Feeds from a given `sessionID`, `before`, and/or `after` params. It will save the Domain Hotlist feeds list in a csv file with naming convention - `domaintools_feed_domain_hotlist__.csv`. The flow will continue to retrieve Domain Hotlist feeds based on the scheduled run + +#### DomainTools API Asset + +**The DomainTools playbooks in this repo will require an asset called `domaintoolscreds`** for a DomainTools API username and key. This asset is used to retrieve Domain Hotlist feeds data via the DomainTools API during playbook execution. Current action selected is `domain_hotlist_feed` and it requires `sessionID`, `before`, and/or `after` input parameters. Optional input parameters are `domain` and `top`. +
+ +#### Installation + +Download the tar file in this directory and import the playbook using that file. The asset accessed in the playbook is for DomainTools API credentials. Point the playbook to the `domaintoolscreds` asset, save the playbook, make sure it's active, and give it a shot. diff --git a/Splunk SOAR/DomainTools_Feeds_Domain_Hotlist/src/DomainTools Feeds Domain Hotlist.json b/Splunk SOAR/DomainTools_Feeds_Domain_Hotlist/src/DomainTools Feeds Domain Hotlist.json new file mode 100644 index 0000000..45b4c21 --- /dev/null +++ b/Splunk SOAR/DomainTools_Feeds_Domain_Hotlist/src/DomainTools Feeds Domain Hotlist.json @@ -0,0 +1,153 @@ +{ + "blockly": false, + "blockly_xml": "", + "category": "Uncategorized", + "coa": { + "data": { + "description": "", + "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" + }, + { + "id": "port_3_to_port_1", + "sourceNode": "3", + "sourcePort": "3_out", + "targetNode": "1", + "targetPort": "1_in" + } + ], + "hash": "286c15eed27262925745bfb80a40129e99bc925f", + "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": 443.9999999999998 + }, + "2": { + "data": { + "action": "domain hotlist feed", + "actionType": "investigate", + "advanced": { + "join": [] + }, + "connector": "DomainTools Iris Investigate", + "connectorConfigs": [ + "domaintoolscreds" + ], + "connectorId": "f18b7ef9-0cbd-4dbb-b8ed-ce62e17f0603", + "connectorVersion": "v1", + "functionId": 1, + "functionName": "domain_hotlist_feed_1", + "id": "2", + "loop": { + "enabled": false, + "exitAfterUnit": "m", + "exitAfterValue": 10, + "exitConditionEnabled": false, + "exitLoopAfter": 2, + "pauseUnit": "m", + "pauseValue": 2 + }, + "parameters": { + "before": "", + "session_id": "integrations-testing", + "top": "" + }, + "requiredParameters": [], + "type": "action" + }, + "errors": {}, + "id": "2", + "type": "action", + "userCode": "\n # Write your custom code here...\n\n", + "warnings": {}, + "x": 0, + "y": 140 + }, + "3": { + "data": { + "advanced": { + "customName": "output domain risk list", + "customNameId": 0, + "join": [] + }, + "functionId": 1, + "functionName": "output_domain_risk_list", + "id": "3", + "inputParameters": [ + "domain_hotlist_feed_1:action_result.data" + ], + "outputVariables": [], + "type": "code" + }, + "errors": {}, + "id": "3", + "type": "code", + "userCode": "\n # Write your custom code here...\n from datetime import datetime\n import csv\n \n event_name = container.get(\"name\")\n file_name = f\"domaintools_feed_domain_hotlist_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv\"\n phantom.debug(f\"dt debug - exporting Domain Hotlist to {file_name} file.\")\n \n with open(f\"/opt/phantom/vault/tmp/{file_name}\", \"w\", newline='') as report_csv_file:\n headers = [\"domain\", \"timestamp\", \"phishing_risk\", \"malware_risk\", \"spam_risk\", \"proximity_risk\", \"overall_risk\", \"expires\"]\n writer = csv.DictWriter(report_csv_file, fieldnames=headers)\n \n writer.writeheader()\n for data in domain_hotlist_feed_1_result_item_0[0] or []:\n writer.writerow(data)\n \n # Define the CEF data in a Python dictionary\n cef_data = {\n \"destinationDnsDomain\": data.get(\"domain\"),\n \"timestamp\": data.get(\"timestamp\"),\n \"phishing_risk\": data.get(\"phishing_risk\"),\n \"malware_risk\": data.get(\"malware_risk\"),\n \"spam_risk\": data.get(\"spam_risk\"),\n \"proximity_risk\": data.get(\"proximity_risk\"),\n \"overall_risk\": data.get(\"overall_risk\"),\n \"expires\": data.get(\"expires\"),\n }\n # Use the dictionary to create a new artifact\n phantom.add_artifact(\n container=container,\n label=\"domaintools-realtime-feeds\",\n severity=\"high\",\n name=\"Domain Hotlist Feed\",\n cef_data=cef_data\n )\n\n success, message, vault_id = phantom.vault_add(container=container, file_location=f\"/opt/phantom/vault/tmp/{file_name}\", file_name=file_name)\n \n if not success:\n phantom.error(f\"dt error - Error creating {file_name} file. Message: {message}\")\n return\n\n\n", + "warnings": {}, + "x": 0, + "y": 300 + } + }, + "notes": "", + "origin": { + "playbook_id": 364, + "playbook_name": "DomainTools Feeds Domain Risk", + "playbook_repo_id": 2, + "playbook_repo_name": "local" + } + }, + "input_spec": null, + "output_spec": null, + "playbook_trigger": "artifact_created", + "playbook_type": "automation", + "python_version": "3", + "schema": "5.0.17", + "version": "6.4.0.90" + }, + "create_time": "2025-09-05T18:47:19.464749+00:00", + "draft_mode": false, + "labels": [ + "events" + ], + "tags": [] +} \ No newline at end of file diff --git a/Splunk SOAR/DomainTools_Feeds_Domain_Hotlist/src/DomainTools Feeds Domain Hotlist.py b/Splunk SOAR/DomainTools_Feeds_Domain_Hotlist/src/DomainTools Feeds Domain Hotlist.py new file mode 100644 index 0000000..1f45afe --- /dev/null +++ b/Splunk SOAR/DomainTools_Feeds_Domain_Hotlist/src/DomainTools Feeds Domain Hotlist.py @@ -0,0 +1,127 @@ +""" + +""" + + +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 'domain_hotlist_feed_1' block + domain_hotlist_feed_1(container=container) + + return + +@phantom.playbook_block() +def domain_hotlist_feed_1(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, loop_state_json=None, **kwargs): + phantom.debug("domain_hotlist_feed_1() called") + + # phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED'))) + + parameters = [] + + parameters.append({ + "before": "", + "session_id": "integrations-testing", + "top": "", + }) + + ################################################################################ + ## Custom Code Start + ################################################################################ + + # Write your custom code here... + + ################################################################################ + ## Custom Code End + ################################################################################ + + phantom.act("domain hotlist feed", parameters=parameters, name="domain_hotlist_feed_1", assets=["domaintoolscreds"], callback=output_domain_risk_list) + + return + + +@phantom.playbook_block() +def output_domain_risk_list(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, loop_state_json=None, **kwargs): + phantom.debug("output_domain_risk_list() called") + + domain_hotlist_feed_1_result_data = phantom.collect2(container=container, datapath=["domain_hotlist_feed_1:action_result.data"], action_results=results) + + domain_hotlist_feed_1_result_item_0 = [item[0] for item in domain_hotlist_feed_1_result_data] + + ################################################################################ + ## Custom Code Start + ################################################################################ + + # Write your custom code here... + from datetime import datetime + import csv + + event_name = container.get("name") + file_name = f"domaintools_feed_domain_hotlist_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv" + phantom.debug(f"dt debug - exporting Domain Hotlist to {file_name} file.") + + with open(f"/opt/phantom/vault/tmp/{file_name}", "w", newline='') as report_csv_file: + headers = ["domain", "timestamp", "phishing_risk", "malware_risk", "spam_risk", "proximity_risk", "overall_risk", "expires"] + writer = csv.DictWriter(report_csv_file, fieldnames=headers) + + writer.writeheader() + for data in domain_hotlist_feed_1_result_item_0[0] or []: + writer.writerow(data) + + # Define the CEF data in a Python dictionary + cef_data = { + "destinationDnsDomain": data.get("domain"), + "timestamp": data.get("timestamp"), + "phishing_risk": data.get("phishing_risk"), + "malware_risk": data.get("malware_risk"), + "spam_risk": data.get("spam_risk"), + "proximity_risk": data.get("proximity_risk"), + "overall_risk": data.get("overall_risk"), + "expires": data.get("expires"), + } + # Use the dictionary to create a new artifact + phantom.add_artifact( + container=container, + label="domaintools-realtime-feeds", + severity="high", + name="Domain Hotlist Feed", + cef_data=cef_data + ) + + success, message, vault_id = phantom.vault_add(container=container, file_location=f"/opt/phantom/vault/tmp/{file_name}", file_name=file_name) + + if not success: + phantom.error(f"dt error - Error creating {file_name} file. Message: {message}") + return + + + ################################################################################ + ## Custom Code End + ################################################################################ + + phantom.save_block_result(key="output_domain_risk_list__inputs:0:domain_hotlist_feed_1:action_result.data", value=json.dumps(domain_hotlist_feed_1_result_item_0)) + + return + + +@phantom.playbook_block() +def on_finish(container, summary): + phantom.debug("on_finish() called") + + ################################################################################ + ## Custom Code Start + ################################################################################ + + # Write your custom code here... + + ################################################################################ + ## Custom Code End + ################################################################################ + + return \ No newline at end of file