From 6a69096673909f3dc7060fe5c625a26f61d31374 Mon Sep 17 00:00:00 2001 From: Keijo Korte Date: Fri, 29 Oct 2021 15:52:59 +0300 Subject: [PATCH 1/9] Initial commit --- responders/Netcraft/Dockerfile | 6 +++ responders/Netcraft/Netcraft.py | 66 +++++++++++++++++++++++ responders/Netcraft/NetcraftTakedown.json | 28 ++++++++++ responders/Netcraft/requirements.txt | 3 ++ 4 files changed, 103 insertions(+) create mode 100644 responders/Netcraft/Dockerfile create mode 100755 responders/Netcraft/Netcraft.py create mode 100644 responders/Netcraft/NetcraftTakedown.json create mode 100644 responders/Netcraft/requirements.txt diff --git a/responders/Netcraft/Dockerfile b/responders/Netcraft/Dockerfile new file mode 100644 index 000000000..2630b3e1b --- /dev/null +++ b/responders/Netcraft/Dockerfile @@ -0,0 +1,6 @@ +FROM python:3 + +WORKDIR /worker +COPY . Netcraft +RUN pip install --no-cache-dir -r Netcraft/requirements.txt +ENTRYPOINT Netcraft/Netcraft.py diff --git a/responders/Netcraft/Netcraft.py b/responders/Netcraft/Netcraft.py new file mode 100755 index 000000000..ed2f0afad --- /dev/null +++ b/responders/Netcraft/Netcraft.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +# encoding: utf-8 + +from cortexutils.responder import Responder +import requests +from datetime import datetime + + +class UmbrellaBlacklister(Responder): + def __init__(self): + Responder.__init__(self) + self.integration_url = self.get_param( + 'config.integration_url', None, "Integration URL Missing") + + def run(self): + Responder.run(self) + + data_type = self.get_param('data.dataType') + ioc_types = ["domain", "url", "fqdn"] + if data_type in ioc_types: + + if data_type == "domain" or data_type == "fqdn": + domain = self.get_param( + 'data.data', None, 'No artifacts available') + + dstUrl = "http://" + domain + + elif data_type == "url": + dstUrl = self.get_param( + 'data.data', None, 'No artifacts available') + + domain = dstUrl.split('/')[2] + + date = datetime.now().strftime("%Y-%m-%dT%XZ") + + headers = { + 'user-agent': 'UmbrellaBlacklister-Cortex-Responder', + 'Content-Type': 'application/json' + } + + payload = { + "alertTime": date, + "deviceId": "cortex_thehive", + "deviceVersion": "2.4.81", + "dstDomain": domain, + "dstUrl": dstUrl, + "eventTime": date, + "protocolVersion": "1.0a", + "providerName": "Security Platform" + } + + r = requests.post(self.integration_url, + json=payload, headers=headers) + if r.status_code == 200 | 202: + self.report({'message': 'Blacklisted in Umbrella.'}) + else: + self.error('Failed to add to blacklist.') + else: + self.error('Incorrect dataType. "Domain", "FQDN", or "URL" expected.') + + def operations(self, raw): + return [self.build_operation('AddTagToArtifact', tag='Umbrella:blocked')] + + +if __name__ == '__main__': + UmbrellaBlacklister().run() diff --git a/responders/Netcraft/NetcraftTakedown.json b/responders/Netcraft/NetcraftTakedown.json new file mode 100644 index 000000000..cf5dc3649 --- /dev/null +++ b/responders/Netcraft/NetcraftTakedown.json @@ -0,0 +1,28 @@ +{ + "name": "Netcraft Takedown Phishing URL", + "version": "1.0", + "author": "Keijo Korte - @korteke", + "url": "https://github.com/TheHive-Project/Cortex-Analyzers", + "license": "AGPL-V3", + "description": "Submit URL to Netcrafts Takedown API.", + "dataTypeList": ["url", "domain", "fqdn"], + "command": "Netcraft/Netcraft.py", + "baseConfig": "Netcraft", + "configurationItems": [ + { + "name": "api_key", + "description": "Netcraft Takedown API key", + "type": "string", + "multi": false, + "required": true + }, + { + "name": "takedown_url", + "description": "Netcraft Takedown URL", + "type": "string", + "multi": false, + "required": true, + "defaultValue": "https://takedown.netcraft.com/authorise.php" + } + ] +} diff --git a/responders/Netcraft/requirements.txt b/responders/Netcraft/requirements.txt new file mode 100644 index 000000000..5ae4088dd --- /dev/null +++ b/responders/Netcraft/requirements.txt @@ -0,0 +1,3 @@ +cortexutils +requests +datetime From 995808d7e26b8328d9a7ecf6d19fdff67d4c5192 Mon Sep 17 00:00:00 2001 From: Keijo Korte Date: Fri, 29 Oct 2021 16:01:56 +0300 Subject: [PATCH 2/9] Adding README file --- responders/Netcraft/README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 responders/Netcraft/README.md diff --git a/responders/Netcraft/README.md b/responders/Netcraft/README.md new file mode 100644 index 000000000..8219db51f --- /dev/null +++ b/responders/Netcraft/README.md @@ -0,0 +1,13 @@ +### Netcraft Takedown + +This responder sends observables to [Netcraft Takedown service](https://www.netcraft.com/cybercrime/countermeasures/). + +#### Requirements +One need to request API-key from Netcraft [Contact form](https://www.netcraft.com/contact/). + +#### Configuration +- `api_key` : Netcraft Takedown API-key +- `takedown_url`: Netcraft Takedown URL (default: https://takedown.netcraft.com/authorise.php) + +#### Official documenation +Official API documentation: [Netcraft site](https://takedown.netcraft.com/help_api.php). \ No newline at end of file From 066fb38ba901ab5f6bcd7e2432463440fc0528ca Mon Sep 17 00:00:00 2001 From: Keijo Korte Date: Fri, 29 Oct 2021 16:36:04 +0300 Subject: [PATCH 3/9] Working version --- responders/Netcraft/Netcraft.py | 88 +++++++++++++--------------- responders/Netcraft/requirements.txt | 1 - 2 files changed, 40 insertions(+), 49 deletions(-) diff --git a/responders/Netcraft/Netcraft.py b/responders/Netcraft/Netcraft.py index ed2f0afad..4c91e3ec8 100755 --- a/responders/Netcraft/Netcraft.py +++ b/responders/Netcraft/Netcraft.py @@ -3,64 +3,56 @@ from cortexutils.responder import Responder import requests -from datetime import datetime -class UmbrellaBlacklister(Responder): +class NetcraftReporter(Responder): def __init__(self): Responder.__init__(self) - self.integration_url = self.get_param( - 'config.integration_url', None, "Integration URL Missing") + self.scheme = "https" + self.api_key = self.get_param( + 'config.api_key', None, "API-key Missing") + self.takedown_url = self.get_param( + 'config.takedown_url', None, "Takedown URL Missing") + self.observable_type = self.get_param('data.dataType', None, "Data type is empty") + self.observable_description = self.get_param('data.message', None, "Description is empty") def run(self): Responder.run(self) - - data_type = self.get_param('data.dataType') - ioc_types = ["domain", "url", "fqdn"] - if data_type in ioc_types: - - if data_type == "domain" or data_type == "fqdn": - domain = self.get_param( - 'data.data', None, 'No artifacts available') - - dstUrl = "http://" + domain - - elif data_type == "url": - dstUrl = self.get_param( - 'data.data', None, 'No artifacts available') - - domain = dstUrl.split('/')[2] - - date = datetime.now().strftime("%Y-%m-%dT%XZ") - - headers = { - 'user-agent': 'UmbrellaBlacklister-Cortex-Responder', - 'Content-Type': 'application/json' - } - - payload = { - "alertTime": date, - "deviceId": "cortex_thehive", - "deviceVersion": "2.4.81", - "dstDomain": domain, - "dstUrl": dstUrl, - "eventTime": date, - "protocolVersion": "1.0a", - "providerName": "Security Platform" - } - - r = requests.post(self.integration_url, - json=payload, headers=headers) - if r.status_code == 200 | 202: - self.report({'message': 'Blacklisted in Umbrella.'}) + try: + supported_observables = ["domain", "url", "fqdn"] + if self.observable_type in supported_observables: + if self.observable_type == "domain" or self.observable_type == "fqdn": + domain = self.get_param('data.data', None, 'No artifacts available') + takedown = "{}://{}".format(self.scheme, domain) + elif self.observable_type == "url": + takedown = self.get_param('data.data') + + headers = { + "Authorization": "Bearer {0}".format(self.api_key), + 'user-agent': 'Netcraft-Cortex-Responder' + } + payload = { + "attack": takedown, + "comment": "Automated takedown via Cortex" + } + + response = requests.post(self.takedown_url, data=payload, headers=headers) + if response.status_code == 200: + self.report({'message': 'Takedown sent ot Netcraft. Message: {}'.format(response.text)}) + elif response.status_code == 401: + self.report({'message': 'Failed authentication. Check API-Key Message: {}'.format(response.text)}) + else: + self.error('Failed to submit takedown request. Error code: {}. Error message: {}' + .format(response.status_code, response.text)) else: - self.error('Failed to add to blacklist.') - else: - self.error('Incorrect dataType. "Domain", "FQDN", or "URL" expected.') + self.error('Incorrect dataType. "Domain", "FQDN", or "URL" expected.') + + except requests.exceptions.RequestException as e: + self.error(str(e)) def operations(self, raw): - return [self.build_operation('AddTagToArtifact', tag='Umbrella:blocked')] + return [self.build_operation('AddTagToArtifact', tag='Netcraft:takedown')] if __name__ == '__main__': - UmbrellaBlacklister().run() + NetcraftReporter().run() diff --git a/responders/Netcraft/requirements.txt b/responders/Netcraft/requirements.txt index 5ae4088dd..6aabc3cfa 100644 --- a/responders/Netcraft/requirements.txt +++ b/responders/Netcraft/requirements.txt @@ -1,3 +1,2 @@ cortexutils requests -datetime From 507703412ce83607a2441b9bffe2a8b16fb05108 Mon Sep 17 00:00:00 2001 From: Keijo Korte Date: Fri, 29 Oct 2021 16:39:35 +0300 Subject: [PATCH 4/9] #1052 Fixed logging --- responders/Netcraft/Netcraft.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/responders/Netcraft/Netcraft.py b/responders/Netcraft/Netcraft.py index 4c91e3ec8..3866bdd66 100755 --- a/responders/Netcraft/Netcraft.py +++ b/responders/Netcraft/Netcraft.py @@ -40,7 +40,7 @@ def run(self): if response.status_code == 200: self.report({'message': 'Takedown sent ot Netcraft. Message: {}'.format(response.text)}) elif response.status_code == 401: - self.report({'message': 'Failed authentication. Check API-Key Message: {}'.format(response.text)}) + self.error({'message': 'Failed authentication. Check API-Key. Message: {}'.format(response.text)}) else: self.error('Failed to submit takedown request. Error code: {}. Error message: {}' .format(response.status_code, response.text)) From 58908a1d55e3366dc633eb5d3b590596ff44a3d4 Mon Sep 17 00:00:00 2001 From: Keijo Korte Date: Mon, 1 Nov 2021 10:50:04 +0200 Subject: [PATCH 5/9] Initial commit --- responders/PaloAltoWildfire/Dockerfile | 6 ++ .../PaloAltoWildfire/PaloAltoWildfire.py | 57 +++++++++++++++++++ .../PaloaltoWildfireSubmission.json | 28 +++++++++ responders/PaloAltoWildfire/README.md | 15 +++++ responders/PaloAltoWildfire/requirements.txt | 2 + 5 files changed, 108 insertions(+) create mode 100644 responders/PaloAltoWildfire/Dockerfile create mode 100755 responders/PaloAltoWildfire/PaloAltoWildfire.py create mode 100644 responders/PaloAltoWildfire/PaloaltoWildfireSubmission.json create mode 100644 responders/PaloAltoWildfire/README.md create mode 100644 responders/PaloAltoWildfire/requirements.txt diff --git a/responders/PaloAltoWildfire/Dockerfile b/responders/PaloAltoWildfire/Dockerfile new file mode 100644 index 000000000..ab856e0be --- /dev/null +++ b/responders/PaloAltoWildfire/Dockerfile @@ -0,0 +1,6 @@ +FROM python:3 + +WORKDIR /worker +COPY . PaloAltoWildfire +RUN pip install --no-cache-dir -r PaloAltoWildfire/requirements.txt +ENTRYPOINT PaloAltoWildfire/PaloAltoWildfire.py diff --git a/responders/PaloAltoWildfire/PaloAltoWildfire.py b/responders/PaloAltoWildfire/PaloAltoWildfire.py new file mode 100755 index 000000000..c6b56a51b --- /dev/null +++ b/responders/PaloAltoWildfire/PaloAltoWildfire.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +# encoding: utf-8 + +from cortexutils.responder import Responder +import requests + + +class PaloAltoWildfire(Responder): + def __init__(self): + Responder.__init__(self) + self.scheme = "https" + self.api_key = self.get_param( + 'config.api_key', None, "API-key Missing") + self.wildfire_url = self.get_param( + 'config.wildfire_url', None, "Takedown URL Missing") + self.observable_type = self.get_param('data.dataType', None, "Data type is empty") + self.observable_description = self.get_param('data.message', None, "Description is empty") + + def run(self): + Responder.run(self) + try: + supported_observables = ["domain", "url", "fqdn"] + if self.observable_type in supported_observables: + if self.observable_type == "domain" or self.observable_type == "fqdn": + domain = self.get_param('data.data', None, 'No artifacts available') + observable = "{}://{}".format(self.scheme, domain) + elif self.observable_type == "url": + observable = self.get_param('data.data') + + headers = { + 'user-agent': 'PaloAltoWildfire-Cortex-Responder' + } + payload = { + "apikey": self.api_key, + "link": observable + } + + response = requests.post(self.wildfire_url, data=payload, headers=headers) + if response.status_code == 200: + self.report({'message': 'Takedown request sent to Wildfire. Message: {}'.format(response.text)}) + elif response.status_code == 401: + self.error({'message': 'Failed authentication. Check API-Key. Message: {}'.format(response.text)}) + else: + self.error('Failed to submit request. Error code: {}. Error message: {}' + .format(response.status_code, response.text)) + else: + self.error('Incorrect dataType. "Domain", "FQDN", or "URL" expected.') + + except requests.exceptions.RequestException as e: + self.error(str(e)) + + def operations(self, raw): + return [self.build_operation('AddTagToArtifact', tag='Wildfire:submit')] + + +if __name__ == '__main__': + PaloAltoWildfire().run() diff --git a/responders/PaloAltoWildfire/PaloaltoWildfireSubmission.json b/responders/PaloAltoWildfire/PaloaltoWildfireSubmission.json new file mode 100644 index 000000000..bb618241b --- /dev/null +++ b/responders/PaloAltoWildfire/PaloaltoWildfireSubmission.json @@ -0,0 +1,28 @@ +{ + "name": "PaloAlto Wildfire URL submission", + "version": "1.0", + "author": "Keijo Korte - @korteke", + "url": "https://github.com/TheHive-Project/Cortex-Analyzers", + "license": "AGPL-V3", + "description": "Submit URL to PaloAlto Wildfire service.", + "dataTypeList": ["url", "domain", "fqdn"], + "command": "PaloAltoWildfire/PaloAltoWildfire.py", + "baseConfig": "PaloAltoWildfire", + "configurationItems": [ + { + "name": "api_key", + "description": "PaloAlto Wildfire API key", + "type": "string", + "multi": false, + "required": true + }, + { + "name": "wildfire_url", + "description": "PaloAlto Wildfire Takedown URL", + "type": "string", + "multi": false, + "required": true, + "defaultValue": "https://wildfire.paloaltonetworks.com/publicapi/submit/link" + } + ] +} diff --git a/responders/PaloAltoWildfire/README.md b/responders/PaloAltoWildfire/README.md new file mode 100644 index 000000000..4d78772dd --- /dev/null +++ b/responders/PaloAltoWildfire/README.md @@ -0,0 +1,15 @@ +### PaloAlto Wildfire responder + +This responder sends observable to [PaloAlto Wildfire service](https://docs.paloaltonetworks.com/wildfire/u-v/wildfire-api/submit-files-and-links-through-the-wildfire-api.html). + +#### Requirements +One need valid API-key to PaloAlto's Wildfire service. +* [Cloud Wildfire](https://docs.paloaltonetworks.com/wildfire/u-v/wildfire-api/get-started-with-the-wildfire-api/get-your-api-key/get-your-wildfire-public-cloud-api-key.html#id3809ea9e-090f-459b-a382-9689383d1855) +* [Local Wildfire instance](https://docs.paloaltonetworks.com/wildfire/u-v/wildfire-api/get-started-with-the-wildfire-api/get-your-api-key/get-your-wildfire-appliance-api-key.html#idd900a1f8-95e3-4739-b02a-7a3269d85bea) + +#### Configuration +- `api_key` : Wildfire API-key +- `takedown_url`: Wildfire URL (default: Cloud version) + +#### Official documenation +Official API documentation: [PaloAlto site](https://docs.paloaltonetworks.com/wildfire/u-v/wildfire-api.html). diff --git a/responders/PaloAltoWildfire/requirements.txt b/responders/PaloAltoWildfire/requirements.txt new file mode 100644 index 000000000..6aabc3cfa --- /dev/null +++ b/responders/PaloAltoWildfire/requirements.txt @@ -0,0 +1,2 @@ +cortexutils +requests From 1c77acd8d2d3c3e6def1d5aa5240b35900457c7a Mon Sep 17 00:00:00 2001 From: Keijo Korte Date: Mon, 1 Nov 2021 11:16:01 +0200 Subject: [PATCH 6/9] Fix typo --- responders/PaloAltoWildfire/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/responders/PaloAltoWildfire/README.md b/responders/PaloAltoWildfire/README.md index 4d78772dd..7084c25f5 100644 --- a/responders/PaloAltoWildfire/README.md +++ b/responders/PaloAltoWildfire/README.md @@ -9,7 +9,7 @@ One need valid API-key to PaloAlto's Wildfire service. #### Configuration - `api_key` : Wildfire API-key -- `takedown_url`: Wildfire URL (default: Cloud version) +- `wildfire_url`: Wildfire URL (default: Cloud version) #### Official documenation Official API documentation: [PaloAlto site](https://docs.paloaltonetworks.com/wildfire/u-v/wildfire-api.html). From 5b4a7e180f0c1b81fdeec1c5aa3a0fd40a070cce Mon Sep 17 00:00:00 2001 From: Keijo Korte Date: Mon, 1 Nov 2021 11:18:50 +0200 Subject: [PATCH 7/9] Fix typos part2 --- responders/PaloAltoWildfire/PaloAltoWildfire.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/responders/PaloAltoWildfire/PaloAltoWildfire.py b/responders/PaloAltoWildfire/PaloAltoWildfire.py index c6b56a51b..77dca8504 100755 --- a/responders/PaloAltoWildfire/PaloAltoWildfire.py +++ b/responders/PaloAltoWildfire/PaloAltoWildfire.py @@ -12,7 +12,7 @@ def __init__(self): self.api_key = self.get_param( 'config.api_key', None, "API-key Missing") self.wildfire_url = self.get_param( - 'config.wildfire_url', None, "Takedown URL Missing") + 'config.wildfire_url', None, "Wildfire URL Missing") self.observable_type = self.get_param('data.dataType', None, "Data type is empty") self.observable_description = self.get_param('data.message', None, "Description is empty") @@ -37,7 +37,7 @@ def run(self): response = requests.post(self.wildfire_url, data=payload, headers=headers) if response.status_code == 200: - self.report({'message': 'Takedown request sent to Wildfire. Message: {}'.format(response.text)}) + self.report({'message': 'Observable sent to Wildfire. Message: {}'.format(response.text)}) elif response.status_code == 401: self.error({'message': 'Failed authentication. Check API-Key. Message: {}'.format(response.text)}) else: From 78effe2529f481b91591eeb8451c5364c0ca92b0 Mon Sep 17 00:00:00 2001 From: Keijo Korte Date: Mon, 1 Nov 2021 11:23:38 +0200 Subject: [PATCH 8/9] Squash commits --- responders/PaloAltoWildfire/Dockerfile | 6 ++ .../PaloAltoWildfire/PaloAltoWildfire.py | 57 +++++++++++++++++++ .../PaloaltoWildfireSubmission.json | 28 +++++++++ responders/PaloAltoWildfire/README.md | 15 +++++ responders/PaloAltoWildfire/requirements.txt | 2 + 5 files changed, 108 insertions(+) create mode 100644 responders/PaloAltoWildfire/Dockerfile create mode 100755 responders/PaloAltoWildfire/PaloAltoWildfire.py create mode 100644 responders/PaloAltoWildfire/PaloaltoWildfireSubmission.json create mode 100644 responders/PaloAltoWildfire/README.md create mode 100644 responders/PaloAltoWildfire/requirements.txt diff --git a/responders/PaloAltoWildfire/Dockerfile b/responders/PaloAltoWildfire/Dockerfile new file mode 100644 index 000000000..ab856e0be --- /dev/null +++ b/responders/PaloAltoWildfire/Dockerfile @@ -0,0 +1,6 @@ +FROM python:3 + +WORKDIR /worker +COPY . PaloAltoWildfire +RUN pip install --no-cache-dir -r PaloAltoWildfire/requirements.txt +ENTRYPOINT PaloAltoWildfire/PaloAltoWildfire.py diff --git a/responders/PaloAltoWildfire/PaloAltoWildfire.py b/responders/PaloAltoWildfire/PaloAltoWildfire.py new file mode 100755 index 000000000..77dca8504 --- /dev/null +++ b/responders/PaloAltoWildfire/PaloAltoWildfire.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +# encoding: utf-8 + +from cortexutils.responder import Responder +import requests + + +class PaloAltoWildfire(Responder): + def __init__(self): + Responder.__init__(self) + self.scheme = "https" + self.api_key = self.get_param( + 'config.api_key', None, "API-key Missing") + self.wildfire_url = self.get_param( + 'config.wildfire_url', None, "Wildfire URL Missing") + self.observable_type = self.get_param('data.dataType', None, "Data type is empty") + self.observable_description = self.get_param('data.message', None, "Description is empty") + + def run(self): + Responder.run(self) + try: + supported_observables = ["domain", "url", "fqdn"] + if self.observable_type in supported_observables: + if self.observable_type == "domain" or self.observable_type == "fqdn": + domain = self.get_param('data.data', None, 'No artifacts available') + observable = "{}://{}".format(self.scheme, domain) + elif self.observable_type == "url": + observable = self.get_param('data.data') + + headers = { + 'user-agent': 'PaloAltoWildfire-Cortex-Responder' + } + payload = { + "apikey": self.api_key, + "link": observable + } + + response = requests.post(self.wildfire_url, data=payload, headers=headers) + if response.status_code == 200: + self.report({'message': 'Observable sent to Wildfire. Message: {}'.format(response.text)}) + elif response.status_code == 401: + self.error({'message': 'Failed authentication. Check API-Key. Message: {}'.format(response.text)}) + else: + self.error('Failed to submit request. Error code: {}. Error message: {}' + .format(response.status_code, response.text)) + else: + self.error('Incorrect dataType. "Domain", "FQDN", or "URL" expected.') + + except requests.exceptions.RequestException as e: + self.error(str(e)) + + def operations(self, raw): + return [self.build_operation('AddTagToArtifact', tag='Wildfire:submit')] + + +if __name__ == '__main__': + PaloAltoWildfire().run() diff --git a/responders/PaloAltoWildfire/PaloaltoWildfireSubmission.json b/responders/PaloAltoWildfire/PaloaltoWildfireSubmission.json new file mode 100644 index 000000000..bb618241b --- /dev/null +++ b/responders/PaloAltoWildfire/PaloaltoWildfireSubmission.json @@ -0,0 +1,28 @@ +{ + "name": "PaloAlto Wildfire URL submission", + "version": "1.0", + "author": "Keijo Korte - @korteke", + "url": "https://github.com/TheHive-Project/Cortex-Analyzers", + "license": "AGPL-V3", + "description": "Submit URL to PaloAlto Wildfire service.", + "dataTypeList": ["url", "domain", "fqdn"], + "command": "PaloAltoWildfire/PaloAltoWildfire.py", + "baseConfig": "PaloAltoWildfire", + "configurationItems": [ + { + "name": "api_key", + "description": "PaloAlto Wildfire API key", + "type": "string", + "multi": false, + "required": true + }, + { + "name": "wildfire_url", + "description": "PaloAlto Wildfire Takedown URL", + "type": "string", + "multi": false, + "required": true, + "defaultValue": "https://wildfire.paloaltonetworks.com/publicapi/submit/link" + } + ] +} diff --git a/responders/PaloAltoWildfire/README.md b/responders/PaloAltoWildfire/README.md new file mode 100644 index 000000000..7084c25f5 --- /dev/null +++ b/responders/PaloAltoWildfire/README.md @@ -0,0 +1,15 @@ +### PaloAlto Wildfire responder + +This responder sends observable to [PaloAlto Wildfire service](https://docs.paloaltonetworks.com/wildfire/u-v/wildfire-api/submit-files-and-links-through-the-wildfire-api.html). + +#### Requirements +One need valid API-key to PaloAlto's Wildfire service. +* [Cloud Wildfire](https://docs.paloaltonetworks.com/wildfire/u-v/wildfire-api/get-started-with-the-wildfire-api/get-your-api-key/get-your-wildfire-public-cloud-api-key.html#id3809ea9e-090f-459b-a382-9689383d1855) +* [Local Wildfire instance](https://docs.paloaltonetworks.com/wildfire/u-v/wildfire-api/get-started-with-the-wildfire-api/get-your-api-key/get-your-wildfire-appliance-api-key.html#idd900a1f8-95e3-4739-b02a-7a3269d85bea) + +#### Configuration +- `api_key` : Wildfire API-key +- `wildfire_url`: Wildfire URL (default: Cloud version) + +#### Official documenation +Official API documentation: [PaloAlto site](https://docs.paloaltonetworks.com/wildfire/u-v/wildfire-api.html). diff --git a/responders/PaloAltoWildfire/requirements.txt b/responders/PaloAltoWildfire/requirements.txt new file mode 100644 index 000000000..6aabc3cfa --- /dev/null +++ b/responders/PaloAltoWildfire/requirements.txt @@ -0,0 +1,2 @@ +cortexutils +requests From c1f71b8212bb2ee6d0a4ccf549e483b1f1fead20 Mon Sep 17 00:00:00 2001 From: Keijo Korte Date: Tue, 2 Nov 2021 10:52:54 +0200 Subject: [PATCH 9/9] Fixing logic error --- responders/MSDefenderEndpoints/test.py | 107 ++++++++++++++++++ responders/Netcraft/.gitignore | 1 + .../PaloAltoWildfire/PaloAltoWildfire.py | 9 +- 3 files changed, 112 insertions(+), 5 deletions(-) create mode 100644 responders/MSDefenderEndpoints/test.py create mode 100644 responders/Netcraft/.gitignore diff --git a/responders/MSDefenderEndpoints/test.py b/responders/MSDefenderEndpoints/test.py new file mode 100644 index 000000000..ae06dcfcf --- /dev/null +++ b/responders/MSDefenderEndpoints/test.py @@ -0,0 +1,107 @@ +import requests +import urllib.request +import urllib.parse +import json +import datetime + +msdefenderAppId = "a3a1267e-08f0-4417-9d34-a0ac9636f6ea" +msdefenderTenantId = "f7bab6a1-af76-41a2-a72e-0a9253b9ed40" +msdefenderSecret = "n.hv7Tiax_9Z3tFv6rUtb-D.4K7yE2p-ja" +msdefenderOAuthUr = 'https://login.windows.net/' + +msdefenderSession = requests.Session() +msdefenderSession.headers.update( + { + 'Content-Type' : 'application/json' + } +) + +def getToken(): + msdefenderAppId = "a3a1267e-08f0-4417-9d34-a0ac9636f6ea" + msdefenderTenantId = "f7bab6a1-af76-41a2-a72e-0a9253b9ed40" + msdefenderSecret = "n.hv7Tiax_9Z3tFv6rUtb-D.4K7yE2p-ja" + msdefenderOAuthUri = 'https://login.windows.net/' + msdefenderGrant = "client_credentials" + msdefenderResourceAppIdUri = 'https://api.securitycenter.windows.com' + url = "{}/{}/oauth2/token".format( + msdefenderOAuthUri,msdefenderTenantId + ) + + body = {'grant_type':msdefenderGrant, 'resource':msdefenderResourceAppIdUri, 'client_id':msdefenderAppId, 'client_secret':msdefenderSecret} + + data = urllib.parse.urlencode(body).encode("utf-8") + + req = urllib.request.Request(url, data) + response = urllib.request.urlopen(req) + jsonResponse = json.loads(response.read()) + token = jsonResponse["access_token"] + #print(json.dumps(body)) + #body = str(json.dumps(body)) + + #try: + # req = msdefenderSession.post(url=url, json=body) + #except requests.exceptions.RequestException as e: + # print(e) + + #print(req.json) + #jsonResponse = req.json() + #token = req.json() + #token = jsonResponse["access_token"] + #url="https://httpbin.org/post" + #token_r = requests.post(url, json={'grant_type':" client_credentials", 'resource': msdefenderResourceAppIdUri, 'client_id': msdefenderAppId, 'client_secret': msdefenderSecret}) + #print(token_r.content) + + return token + +def getMachineId(session,id,observable_type): + time = datetime.datetime.now() - datetime.timedelta(minutes=120) + time = time.strftime("%Y-%m-%dT%H:%M:%SZ") + + if observable_type == "ip": + url = "https://api.securitycenter.windows.com/api/machines/findbyip(ip='{}',timestamp={})".format(id,time) + else: + url = "https://api.securitycenter.windows.com/api/machines?$filter=computerDnsName+eq+'{}'".format(id) + + try: + response = session.get(url=url) + if response.status_code == 200: + jsonResponse = response.json() + if len(response.content) > 100: + return jsonResponse["value"][0]["aadDeviceId"] + else: + return "ERROR" + except requests.exceptions.RequestException as e: + print("Exception: {}".format(e)) + +def runFullVirusScan(machineId,session): + url = 'https://api.securitycenter.windows.com/api/machines/{}/runAntiVirusScan'.format(machineId) + + body = { + 'Comment': 'Full scan to machine due to TheHive case {}'.format("1234"), + 'ScanType': 'Full' + } + + try: + response = session.post(url=url, json=body) + if response.status_code == 201: + print("message: Started full VirusScan on machine: {}".format(machine)) + except requests.exceptions.RequestException as e: + print("Error") + +######### +######### +token = getToken() +#print(token) + +msdefenderSession.headers.update( + { + 'Accept' : 'application/json', + 'Content-Type' : 'application/json', + 'Authorization' : 'Bearer {0}'.format(token) + } +) + +print("IP: " + getMachineId(msdefenderSession,"192.168.210.1","ip")) +print("HOST: " + getMachineId(msdefenderSession,"laptop-6gjkth4p","hostname")) + +#runFullVirusScan(machine,msdefenderSession) diff --git a/responders/Netcraft/.gitignore b/responders/Netcraft/.gitignore new file mode 100644 index 000000000..5890b098b --- /dev/null +++ b/responders/Netcraft/.gitignore @@ -0,0 +1 @@ +test.py diff --git a/responders/PaloAltoWildfire/PaloAltoWildfire.py b/responders/PaloAltoWildfire/PaloAltoWildfire.py index 77dca8504..c558e53da 100755 --- a/responders/PaloAltoWildfire/PaloAltoWildfire.py +++ b/responders/PaloAltoWildfire/PaloAltoWildfire.py @@ -28,14 +28,13 @@ def run(self): observable = self.get_param('data.data') headers = { - 'user-agent': 'PaloAltoWildfire-Cortex-Responder' + 'User-Agent': 'PaloAltoWildfire-Cortex-Responder' } payload = { - "apikey": self.api_key, - "link": observable + 'apikey': (None, self.api_key), + 'link': (None, observable), } - - response = requests.post(self.wildfire_url, data=payload, headers=headers) + response = requests.post(self.wildfire_url, files=payload, headers=headers) if response.status_code == 200: self.report({'message': 'Observable sent to Wildfire. Message: {}'.format(response.text)}) elif response.status_code == 401: