-
Notifications
You must be signed in to change notification settings - Fork 385
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'feature/msdefender' of https://github.com/korteke/Corte…
…x-Analyzers into korteke-feature/msdefender
- Loading branch information
Showing
10 changed files
with
559 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
.vscode/* | ||
test.py |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,221 @@ | ||
#!/usr/bin/python3 | ||
from cortexutils.responder import Responder | ||
import requests | ||
import urllib | ||
import urllib.error | ||
import json | ||
import datetime | ||
|
||
class MSDefenderEndpoints(Responder): | ||
def __init__(self): | ||
Responder.__init__(self) | ||
self.msdefenderTenantId = self.get_param('config.tenantId', None, 'TenantId missing!') | ||
self.msdefenderAppId = self.get_param('config.appId', None, 'AppId missing!') | ||
self.msdefenderSecret = self.get_param('config.appSecret', None, 'AppSecret missing!') | ||
self.msdefenderResourceAppIdUri = self.get_param('config.resourceAppIdUri', None, 'resourceAppIdUri missing!') | ||
self.msdefenderOAuthUri = self.get_param('config.oAuthUri', None, 'oAuthUri missing!') | ||
|
||
self.observable = self.get_param('data.data', None, "Data is empty") | ||
self.observableType = self.get_param('data.dataType', None, "Data type is empty") | ||
self.caseId = self.get_param("data.case.caseId", None, "caseId is missing") | ||
self.caseTitle = self.get_param('data.case.title', None, 'Case title is missing').encode("utf-8") | ||
self.service = self.get_param("config.service", None, "Service Missing") | ||
|
||
self.msdefenderSession = requests.Session() | ||
self.msdefenderSession.headers.update( | ||
{ | ||
'Accept' : 'application/json', | ||
'Content-Type' : 'application/json' | ||
} | ||
) | ||
|
||
def run(self): | ||
Responder.run(self) | ||
url = "{}{}/oauth2/token".format( | ||
self.msdefenderOAuthUri,self.msdefenderTenantId | ||
) | ||
|
||
body = { | ||
'resource' : self.msdefenderResourceAppIdUri, | ||
'client_id' : self.msdefenderAppId, | ||
'client_secret' : self.msdefenderSecret, | ||
'grant_type' : 'client_credentials' | ||
} | ||
|
||
data = urllib.parse.urlencode(body).encode("utf-8") | ||
req = urllib.request.Request(url, data) | ||
|
||
try: | ||
response = urllib.request.urlopen(req) | ||
except urllib.error.HTTPError as e: | ||
#print("message: HTTP ErrorCode {}. Reason: {}".format(e.code,e.reason)) | ||
self.error({'message': "HTTP ErrorCode {}. Reason: {}".format(e.code,e.reason)}) | ||
except urllib.error.URLError as e: | ||
#print("message: URL Error: {}".format(e.reason)) | ||
self.error({'message': "URL Error: {}".format(e.reason)}) | ||
|
||
jsonResponse = json.loads(response.read()) | ||
token = jsonResponse["access_token"] | ||
|
||
self.msdefenderSession.headers.update( | ||
{ | ||
'Authorization' : 'Bearer {0}'.format(token) | ||
} | ||
) | ||
|
||
def getMachineId(id): | ||
time = datetime.datetime.now() - datetime.timedelta(minutes=60) | ||
time = time.strftime("%Y-%m-%dT%H:%M:%SZ") | ||
|
||
if self.observableType == "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 = self.msdefenderSession.get(url=url) | ||
if response.status_code == 200: | ||
jsonResponse = response.json() | ||
if len(response.content) > 100: | ||
return jsonResponse["value"][0]["aadDeviceId"] | ||
else: | ||
self.error({'message': "Can't get hostname from Microsoft API"}) | ||
except requests.exceptions.RequestException as e: | ||
self.error({'message': e}) | ||
|
||
def isolateMachine(machineId): | ||
''' | ||
example | ||
POST https://api.securitycenter.windows.com/api/machines/{id}/isolate | ||
''' | ||
url = 'https://api.securitycenter.windows.com/api/machines/{}/isolate'.format(machineId) | ||
|
||
body = { | ||
'Comment': 'Isolate machine due to TheHive case {}'.format(self.caseId), | ||
'IsolationType': 'Full' | ||
} | ||
|
||
try: | ||
response = self.msdefenderSession.post(url=url, json=body) | ||
if response.status_code == 201: | ||
self.report({'message': "Isolated machine: " + self.observable }) | ||
elif response.status_code == 400 and "ActiveRequestAlreadyExists" in response.content.decode("utf-8"): | ||
self.report({'message': "Error isolating machine: ActiveRequestAlreadyExists"}) | ||
else: | ||
self.error({'message': "Can't isolate machine"}) | ||
except requests.exceptions.RequestException as e: | ||
self.error({'message': e}) | ||
|
||
self.report({'message': "Isolated machine: " + self.observable }) | ||
|
||
def runFullVirusScan(machineId): | ||
''' | ||
example | ||
POST https://api.securitycenter.windows.com/api/machines/{id}/runAntiVirusScan | ||
''' | ||
url = 'https://api.securitycenter.windows.com/api/machines/{}/runAntiVirusScan'.format(machineId) | ||
|
||
body = { | ||
'Comment': 'Full scan to machine due to TheHive case {}'.format(self.caseId), | ||
'ScanType': 'Full' | ||
} | ||
|
||
try: | ||
response = self.msdefenderSession.post(url=url, json=body) | ||
if response.status_code == 201: | ||
self.report({'message': "Started full VirusScan on machine: " + self.observable }) | ||
elif response.status_code == 400 and "ActiveRequestAlreadyExists" in response.content.decode("utf-8"): | ||
self.report({'message': "Error full VirusScan on machine: ActiveRequestAlreadyExists"}) | ||
else: | ||
self.error({'message': "Error full VirusScan on machine"}) | ||
except requests.exceptions.RequestException as e: | ||
self.error({'message': e}) | ||
|
||
|
||
def unisolateMachine(machineId): | ||
''' | ||
example | ||
POST https://api.securitycenter.windows.com/api/machines/{id}/unisolate | ||
''' | ||
url = 'https://api.securitycenter.windows.com/api/machines/{}/unisolate'.format(machineId) | ||
body = { | ||
'Comment': 'Unisolate machine due to TheHive case {}'.format(self.caseId) | ||
} | ||
|
||
try: | ||
response = self.msdefenderSession.post(url=url, json=body) | ||
if response.status_code == 201: | ||
self.report({'message': "Unisolated machine: " + self.observable }) | ||
elif response.status_code == 400 and "ActiveRequestAlreadyExists" in response.content.decode("utf-8"): | ||
self.report({'message': "Error unisolating machine: ActiveRequestAlreadyExists"}) | ||
else: | ||
self.error({'message': "Can't unisolate machine"}) | ||
except requests.exceptions.RequestException as e: | ||
self.error({'message': e}) | ||
|
||
def pushCustomIocAlert(ipAddress): | ||
action="Alert" | ||
url = 'https://api.securitycenter.windows.com/api/indicators' | ||
body = { | ||
'indicatorValue': ipAddress, | ||
'indicatorType': 'IpAddress', | ||
'action': action, | ||
'title': self.caseTitle, | ||
'severity': 'High', | ||
'description': self.caseTitle, | ||
'recommendedActions': 'N/A' | ||
} | ||
|
||
try: | ||
response = self.msdefenderSession.post(url=url, json=body) | ||
if response.status_code == 200: | ||
self.report({'message': "Added IOC to Defender with Alert mode: " + self.observable }) | ||
except requests.exceptions.RequestException as e: | ||
self.error({'message': e}) | ||
|
||
def pushCustomIocBlock(ipAddress): | ||
action="AlertAndBlock" | ||
url = 'https://api.securitycenter.windows.com/api/indicators' | ||
body = { | ||
'indicatorValue' : ipAddress, | ||
'indicatorType' : 'IpAddress', | ||
'action' : action, | ||
'title' : "TheHive IOC: {}".format(self.caseTitle), | ||
'severity' : 'High', | ||
'description' : "TheHive case: {} - caseId {}".format(self.caseTitle,self.caseId), | ||
'recommendedActions' : 'N/A' | ||
} | ||
|
||
try: | ||
response = self.msdefenderSession.post(url=url, json=body) | ||
if response.status_code == 200: | ||
self.report({'message': "Added IOC to Defender with Alert and Block mode: " + self.observable }) | ||
except requests.exceptions.RequestException as e: | ||
self.error({'message': e}) | ||
|
||
print("blop") | ||
if self.service == "isolateMachine": | ||
isolateMachine(getMachineId(self.observable)) | ||
elif self.service == "unisolateMachine": | ||
unisolateMachine(getMachineId(self.observable)) | ||
elif self.service == "runFullVirusScan": | ||
runFullVirusScan(getMachineId(self.observable)) | ||
elif self.service == "pushIOCBlock": | ||
pushCustomIocBlock(self.observable) | ||
elif self.service == "pushIOCAlert": | ||
pushCustomIocAlert(self.observable) | ||
else: | ||
self.error({'message': "Unidentified service"}) | ||
|
||
def operations(self, raw): | ||
self.build_operation('AddTagToCase', tag='MSDefenderResponder:run') | ||
if self.service == "isolateMachine": | ||
return [self.build_operation("AddTagToArtifact", tag="MsDefender:isolated")] | ||
elif self.service == "runFullVirusScan": | ||
return [self.build_operation("AddTagToArtifact", tag="MsDefender:fullVirusScan")] | ||
elif self.service == "unisolateMachine": | ||
return [self.build_operation("AddTagToArtifact", tag="MsDefender:unIsolated")] | ||
|
||
if __name__ == '__main__': | ||
|
||
MSDefenderEndpoints().run() |
56 changes: 56 additions & 0 deletions
56
responders/MSDefenderEndpoints/MSDefenderEndpoints_Isolate.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
{ | ||
"name": "MSDefender-IsolateMachine", | ||
"version": "1.0", | ||
"author": "Keijo Korte", | ||
"url": "https://github.com/TheHive-Project/Cortex-Analyzers", | ||
"license": "AGPL-V3", | ||
"description": "Isolate machine with Microsoft Defender for Endpoints", | ||
"dataTypeList": ["thehive:case_artifact"], | ||
"command": "MSDefenderEndpoints/MSDefenderEndpoints.py", | ||
"baseConfig": "MSDefenderforEndpoints", | ||
"config": { | ||
"service": "isolateMachine" | ||
}, | ||
"configurationItems": [ | ||
{ | ||
"name": "tenantId", | ||
"description": "Azure tenant ID", | ||
"type": "string", | ||
"multi": false, | ||
"required": true, | ||
"defaultValue": "abcdef12-ab12-abc12-ab12-abcdef123456" | ||
}, | ||
{ | ||
"name": "appId", | ||
"description": "Azure app ID", | ||
"type": "string", | ||
"multi": false, | ||
"required": true, | ||
"defaultValue": "abcdef12-ab12-abc12-ab12-abcdef123456" | ||
}, | ||
{ | ||
"name": "appSecret", | ||
"description": "Azure app secret", | ||
"type": "string", | ||
"multi": false, | ||
"required": true, | ||
"defaultValue": "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890=" | ||
}, | ||
{ | ||
"name": "resourceAppIdUri", | ||
"description": "Security Center URI, usually doens't need to change", | ||
"type": "string", | ||
"multi": false, | ||
"required": true, | ||
"defaultValue": "https://api.securitycenter.windows.com" | ||
}, | ||
{ | ||
"name": "oAuthUri", | ||
"description": "Azure oAuth2 authentication endpoint", | ||
"type": "string", | ||
"multi": false, | ||
"required": true, | ||
"defaultValue": "https://login.windows.net/" | ||
} | ||
] | ||
} |
56 changes: 56 additions & 0 deletions
56
responders/MSDefenderEndpoints/MSDefenderEndpoints_PushIOCAlert.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
{ | ||
"name": "MSDefender-PushIOC-Alert", | ||
"version": "1.0", | ||
"author": "Keijo Korte", | ||
"url": "https://github.com/TheHive-Project/Cortex-Analyzers", | ||
"license": "AGPL-V3", | ||
"description": "Push IOC to Defender client. Alert mode", | ||
"dataTypeList": ["thehive:case_artifact"], | ||
"command": "MSDefenderEndpoints/MSDefenderEndpoints.py", | ||
"baseConfig": "MSDefenderforEndpoints", | ||
"config": { | ||
"service": "pushIOCAlert" | ||
}, | ||
"configurationItems": [ | ||
{ | ||
"name": "tenantId", | ||
"description": "Azure tenant ID", | ||
"type": "string", | ||
"multi": false, | ||
"required": true, | ||
"defaultValue": "abcdef12-ab12-abc12-ab12-abcdef123456" | ||
}, | ||
{ | ||
"name": "appId", | ||
"description": "Azure app ID", | ||
"type": "string", | ||
"multi": false, | ||
"required": true, | ||
"defaultValue": "abcdef12-ab12-abc12-ab12-abcdef123456" | ||
}, | ||
{ | ||
"name": "appSecret", | ||
"description": "Azure app secret", | ||
"type": "string", | ||
"multi": false, | ||
"required": true, | ||
"defaultValue": "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890=" | ||
}, | ||
{ | ||
"name": "resourceAppIdUri", | ||
"description": "Security Center URI, usually doens't need to change", | ||
"type": "string", | ||
"multi": false, | ||
"required": true, | ||
"defaultValue": "https://api.securitycenter.windows.com" | ||
}, | ||
{ | ||
"name": "oAuthUri", | ||
"description": "Azure oAuth2 authentication endpoint", | ||
"type": "string", | ||
"multi": false, | ||
"required": true, | ||
"defaultValue": "https://login.windows.net/" | ||
} | ||
] | ||
} |
56 changes: 56 additions & 0 deletions
56
responders/MSDefenderEndpoints/MSDefenderEndpoints_PushIOCBlock.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
{ | ||
"name": "MSDefender-PushIOC-Block", | ||
"version": "1.0", | ||
"author": "Keijo Korte", | ||
"url": "https://github.com/TheHive-Project/Cortex-Analyzers", | ||
"license": "AGPL-V3", | ||
"description": "Push IOC to Defender client. Blocking mode", | ||
"dataTypeList": ["thehive:case_artifact"], | ||
"command": "MSDefenderEndpoints/MSDefenderEndpoints.py", | ||
"baseConfig": "MSDefenderforEndpoints", | ||
"config": { | ||
"service": "pushIOCBlock" | ||
}, | ||
"configurationItems": [ | ||
{ | ||
"name": "tenantId", | ||
"description": "Azure tenant ID", | ||
"type": "string", | ||
"multi": false, | ||
"required": true, | ||
"defaultValue": "abcdef12-ab12-abc12-ab12-abcdef123456" | ||
}, | ||
{ | ||
"name": "appId", | ||
"description": "Azure app ID", | ||
"type": "string", | ||
"multi": false, | ||
"required": true, | ||
"defaultValue": "abcdef12-ab12-abc12-ab12-abcdef123456" | ||
}, | ||
{ | ||
"name": "appSecret", | ||
"description": "Azure app secret", | ||
"type": "string", | ||
"multi": false, | ||
"required": true, | ||
"defaultValue": "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890=" | ||
}, | ||
{ | ||
"name": "resourceAppIdUri", | ||
"description": "Security Center URI, usually doens't need to change", | ||
"type": "string", | ||
"multi": false, | ||
"required": true, | ||
"defaultValue": "https://api.securitycenter.windows.com" | ||
}, | ||
{ | ||
"name": "oAuthUri", | ||
"description": "Azure oAuth2 authentication endpoint", | ||
"type": "string", | ||
"multi": false, | ||
"required": true, | ||
"defaultValue": "https://login.windows.net/" | ||
} | ||
] | ||
} |
Oops, something went wrong.