diff --git a/responders/MSDefenderEndpoints/Dockerfile b/responders/MSDefenderEndpoints/Dockerfile index 6f153365f..6d3fed6a2 100644 --- a/responders/MSDefenderEndpoints/Dockerfile +++ b/responders/MSDefenderEndpoints/Dockerfile @@ -17,5 +17,5 @@ FROM python:3 WORKDIR /worker COPY . MSDefenderEndpoints -RUN test ! -e MSDefenderEndpoints/requirements.txt || pip install --no-cache-dir -rMSDefenderEndpoints/requirements.txt -ENTRYPOINT MSDefenderEndpoints/MSDefenderEndpoints.py \ No newline at end of file +RUN test ! -e MSDefenderEndpoints/requirements.txt || pip install --no-cache-dir -r MSDefenderEndpoints/requirements.txt +ENTRYPOINT MSDefenderEndpoints/MSDefenderEndpoints.py diff --git a/responders/MSDefenderEndpoints/MSDefenderEndpoints.py b/responders/MSDefenderEndpoints/MSDefenderEndpoints.py index 8775fd5f9..c7baaff02 100755 --- a/responders/MSDefenderEndpoints/MSDefenderEndpoints.py +++ b/responders/MSDefenderEndpoints/MSDefenderEndpoints.py @@ -7,7 +7,7 @@ import datetime class MSDefenderEndpoints(Responder): - def __init__(self): + 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!') @@ -29,9 +29,9 @@ def __init__(self): } ) - def run(self): + def run(self): Responder.run(self) - url = "{}{}/oauth2/token".format( + url = "{}/{}/oauth2/token".format( self.msdefenderOAuthUri,self.msdefenderTenantId ) @@ -77,6 +77,8 @@ def getMachineId(id): if response.status_code == 200: jsonResponse = response.json() if len(response.content) > 100: + if jsonResponse["value"][0]["aadDeviceId"] is None: + return jsonResponse["value"][0]["id"] return jsonResponse["value"][0]["aadDeviceId"] else: self.error({'message': "Can't get hostname from Microsoft API"}) @@ -153,16 +155,102 @@ def unisolateMachine(machineId): except requests.exceptions.RequestException as e: self.error({'message': e}) - def pushCustomIocAlert(ipAddress): - action="Alert" + + def restrictAppExecution(machineId): + ''' + example + POST https://api.securitycenter.windows.com/api/machines/{id}/restrictCodeExecution + ''' + url = 'https://api.securitycenter.windows.com/api/machines/{}/restrictCodeExecution'.format(machineId) + body = { + 'Comment': 'Restrict code execution due to TheHive case {}'.format(self.caseId) + } + + try: + response = self.msdefenderSession.post(url=url, json=body) + if response.status_code == 201: + self.report({'message': "Restricted app execution on machine: " + self.observable }) + elif response.status_code == 400 and "ActiveRequestAlreadyExists" in response.content.decode("utf-8"): + self.report({'message': "Error restricting app execution on machine: ActiveRequestAlreadyExists"}) + else: + self.error({'message': "Can't restrict app execution"}) + except requests.exceptions.RequestException as e: + self.error({'message': e}) + + + def unrestrictAppExecution(machineId): + ''' + example + POST https://api.securitycenter.windows.com/api/machines/{id}/unrestrictCodeExecution + ''' + url = 'https://api.securitycenter.windows.com/api/machines/{}/unrestrictCodeExecution'.format(machineId) + body = { + 'Comment': '"Remove code execution restriction since machine was cleaned and validated due to TheHive case {}'.format(self.caseId) + } + + try: + response = self.msdefenderSession.post(url=url, json=body) + if response.status_code == 201: + self.report({'message': "Removed app execution restriction on machine: " + self.observable }) + elif response.status_code == 400 and "ActiveRequestAlreadyExists" in response.content.decode("utf-8"): + self.report({'message': "Error removing app execution restriction on machine: ActiveRequestAlreadyExists"}) + else: + self.error({'message': "Can't unrestrict app execution"}) + except requests.exceptions.RequestException as e: + self.error({'message': e}) + + + def startAutoInvestigation(machineId): + ''' + example + POST https://api.securitycenter.windows.com/api/machines/{id}/startInvestigation + ''' + url = 'https://api.securitycenter.windows.com/api/machines/{}/startInvestigation'.format(machineId) + + body = { + 'Comment': 'Start investigation due to TheHive case {}'.format(self.caseId) + } + + try: + response = self.msdefenderSession.post(url=url, json=body) + if response.status_code == 201: + self.report({'message': "Started Auto Investigation on : " + self.observable }) + elif response.status_code == 400 and "ActiveRequestAlreadyExists" in response.content.decode("utf-8"): + self.report({'message': "Error lauching auto investigation on machine: ActiveRequestAlreadyExists"}) + else: + self.error({'message': "Error auto investigation on machine"}) + except requests.exceptions.RequestException as e: + self.error({'message': e}) + + + def pushCustomIocAlert(observable): + + if self.observableType == 'ip': + indicatorType = 'IpAddress' + elif self.observableType == 'url': + indicatorType = 'Url' + elif self.observableType == 'domain': + indicatorType = 'DomainName' + elif self.observableType == 'hash': + if len(observable) == 32: + indicatorType = 'FileMd5' + elif len(observable) == 40: + indicatorType = 'FileSha1' + elif len(observable) == 64: + indicatorType = 'FileSha256' + else: + self.report({'message':"Observable is not a valid hash"}) + else: + self.error({'message':"Observable type must be ip, url, domain or hash"}) + url = 'https://api.securitycenter.windows.com/api/indicators' body = { - 'indicatorValue': ipAddress, - 'indicatorType': 'IpAddress', - 'action': action, - 'title': self.caseTitle, + 'indicatorValue': observable, + 'indicatorType': indicatorType, + 'action': 'Alert', + 'title': "TheHive IOC: {}".format(self.caseTitle), 'severity': 'High', - 'description': self.caseTitle, + 'description': "TheHive case: {} - caseId {}".format(self.caseTitle,self.caseId), 'recommendedActions': 'N/A' } @@ -173,13 +261,31 @@ def pushCustomIocAlert(ipAddress): except requests.exceptions.RequestException as e: self.error({'message': e}) - def pushCustomIocBlock(ipAddress): - action="AlertAndBlock" + def pushCustomIocBlock(observable): + + if self.observableType == 'ip': + indicatorType = 'IpAddress' + elif self.observableType == 'url': + indicatorType = 'Url' + elif self.observableType == 'domain': + indicatorType = 'DomainName' + elif self.observableType == 'hash': + if len(observable) == 32: + indicatorType = 'FileMd5' + elif len(observable) == 40: + indicatorType = 'FileSha1' + elif len(observable) == 64: + indicatorType = 'FileSha256' + else: + self.report({'message':"Observable is not a valid hash"}) + else: + self.error({'message':"Observable type must be ip, url, domain or hash"}) + url = 'https://api.securitycenter.windows.com/api/indicators' body = { - 'indicatorValue' : ipAddress, - 'indicatorType' : 'IpAddress', - 'action' : action, + 'indicatorValue' : observable, + 'indicatorType' : indicatorType, + 'action' : 'AlertAndBlock', 'title' : "TheHive IOC: {}".format(self.caseTitle), 'severity' : 'High', 'description' : "TheHive case: {} - caseId {}".format(self.caseTitle,self.caseId), @@ -193,13 +299,19 @@ def pushCustomIocBlock(ipAddress): 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 == "restrictAppExecution": + restrictAppExecution(getMachineId(self.observable)) + elif self.service == "unrestrictAppExecution": + unrestrictAppExecution(getMachineId(self.observable)) + elif self.service == "startAutoInvestigation": + startAutoInvestigation(getMachineId(self.observable)) elif self.service == "pushIOCBlock": pushCustomIocBlock(self.observable) elif self.service == "pushIOCAlert": @@ -207,7 +319,7 @@ def pushCustomIocBlock(ipAddress): else: self.error({'message': "Unidentified service"}) - def operations(self, raw): + def operations(self, raw): self.build_operation('AddTagToCase', tag='MSDefenderResponder:run') if self.service == "isolateMachine": return [self.build_operation("AddTagToArtifact", tag="MsDefender:isolated")] @@ -215,6 +327,10 @@ def operations(self, raw): return [self.build_operation("AddTagToArtifact", tag="MsDefender:fullVirusScan")] elif self.service == "unisolateMachine": return [self.build_operation("AddTagToArtifact", tag="MsDefender:unIsolated")] + elif self.service == "restrictAppExecution": + return [self.build_operation("AddTagToArtifact", tag="MsDefender:restrictedAppExec")] + elif self.service == "unrestrictAppExecution": + return [self.build_operation("AddTagToArtifact", tag="MsDefender:unrestrictedAppExec")] if __name__ == '__main__': diff --git a/responders/MSDefenderEndpoints/MSDefenderEndpoints_AutoInvestigation.json b/responders/MSDefenderEndpoints/MSDefenderEndpoints_AutoInvestigation.json new file mode 100644 index 000000000..ac4ece72c --- /dev/null +++ b/responders/MSDefenderEndpoints/MSDefenderEndpoints_AutoInvestigation.json @@ -0,0 +1,61 @@ +{ + "name": "MSDefender-AutoInvestigation", + "version": "1.0", + "author": "Keijo Korte, Louis-Maximilien Dupouy", + "url": "https://github.com/TheHive-Project/Cortex-Analyzers", + "license": "AGPL-V3", + "description": "Start an automated investigation on a device", + "dataTypeList": ["thehive:case_artifact"], + "command": "MSDefenderEndpoints/MSDefenderEndpoints.py", + "baseConfig": "MSDefenderforEndpoints", + "config": { + "service": "startAutoInvestigation" + }, + "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.microsoftonline.com" + } + ], + "registration_required": true, + "subscription_required": true, + "free_subscription": false, + "service_homepage": "https://securitycenter.windows.com" + } + \ No newline at end of file diff --git a/responders/MSDefenderEndpoints/MSDefenderEndpoints_Isolate.json b/responders/MSDefenderEndpoints/MSDefenderEndpoints_Isolate.json index 9dcc99547..e78dac91e 100644 --- a/responders/MSDefenderEndpoints/MSDefenderEndpoints_Isolate.json +++ b/responders/MSDefenderEndpoints/MSDefenderEndpoints_Isolate.json @@ -50,7 +50,7 @@ "type": "string", "multi": false, "required": true, - "defaultValue": "https://login.windows.net/" + "defaultValue": "https://login.microsoftonline.com" } ], "registration_required": true, diff --git a/responders/MSDefenderEndpoints/MSDefenderEndpoints_PushIOCAlert.json b/responders/MSDefenderEndpoints/MSDefenderEndpoints_PushIOCAlert.json index fe9c10a2f..255fa328c 100644 --- a/responders/MSDefenderEndpoints/MSDefenderEndpoints_PushIOCAlert.json +++ b/responders/MSDefenderEndpoints/MSDefenderEndpoints_PushIOCAlert.json @@ -1,7 +1,7 @@ { "name": "MSDefender-PushIOC-Alert", - "version": "1.0", - "author": "Keijo Korte", + "version": "2.0", + "author": "Keijo Korte, Louis-Maximilien Dupouy", "url": "https://github.com/TheHive-Project/Cortex-Analyzers", "license": "AGPL-V3", "description": "Push IOC to Defender client. Alert mode", @@ -50,7 +50,7 @@ "type": "string", "multi": false, "required": true, - "defaultValue": "https://login.windows.net/" + "defaultValue": "https://login.microsoftonline.com" } ], "registration_required": true, diff --git a/responders/MSDefenderEndpoints/MSDefenderEndpoints_PushIOCBlock.json b/responders/MSDefenderEndpoints/MSDefenderEndpoints_PushIOCBlock.json index d87914e25..eb211d7cd 100644 --- a/responders/MSDefenderEndpoints/MSDefenderEndpoints_PushIOCBlock.json +++ b/responders/MSDefenderEndpoints/MSDefenderEndpoints_PushIOCBlock.json @@ -1,7 +1,7 @@ { "name": "MSDefender-PushIOC-Block", - "version": "1.0", - "author": "Keijo Korte", + "version": "2.0", + "author": "Keijo Korte, Louis-Maximilien Dupouy", "url": "https://github.com/TheHive-Project/Cortex-Analyzers", "license": "AGPL-V3", "description": "Push IOC to Defender client. Blocking mode", @@ -50,7 +50,7 @@ "type": "string", "multi": false, "required": true, - "defaultValue": "https://login.windows.net/" + "defaultValue": "https://login.microsoftonline.com" } ], "registration_required": true, diff --git a/responders/MSDefenderEndpoints/MSDefenderEndpoints_RestrictAppExecution.json b/responders/MSDefenderEndpoints/MSDefenderEndpoints_RestrictAppExecution.json new file mode 100644 index 000000000..525a80990 --- /dev/null +++ b/responders/MSDefenderEndpoints/MSDefenderEndpoints_RestrictAppExecution.json @@ -0,0 +1,61 @@ +{ + "name": "MSDefender-RestrictAppExecution", + "version": "1.0", + "author": "Keijo Korte, Louis-Maximilien Dupouy", + "url": "https://github.com/TheHive-Project/Cortex-Analyzers", + "license": "AGPL-V3", + "description": "Restrict execution of all applications on the device except a predefined set", + "dataTypeList": ["thehive:case_artifact"], + "command": "MSDefenderEndpoints/MSDefenderEndpoints.py", + "baseConfig": "MSDefenderforEndpoints", + "config": { + "service": "restrictAppExecution" + }, + "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.microsoftonline.com" + } + ], + "registration_required": true, + "subscription_required": true, + "free_subscription": false, + "service_homepage": "https://securitycenter.windows.com" + } + \ No newline at end of file diff --git a/responders/MSDefenderEndpoints/MSDefenderEndpoints_UnRestrictAppExecution.json b/responders/MSDefenderEndpoints/MSDefenderEndpoints_UnRestrictAppExecution.json new file mode 100644 index 000000000..7b0c20d6a --- /dev/null +++ b/responders/MSDefenderEndpoints/MSDefenderEndpoints_UnRestrictAppExecution.json @@ -0,0 +1,60 @@ +{ + "name": "MSDefender-UnRestrictAppExecution", + "version": "1.0", + "author": "Keijo Korte, Louis-Maximilien Dupouy", + "url": "https://github.com/TheHive-Project/Cortex-Analyzers", + "license": "AGPL-V3", + "description": "Enable execution of any application on the device", + "dataTypeList": ["thehive:case_artifact"], + "command": "MSDefenderEndpoints/MSDefenderEndpoints.py", + "baseConfig": "MSDefenderforEndpoints", + "config": { + "service": "unrestrictAppExecution" + }, + "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.microsoftonline.com" + } + ], + "registration_required": true, + "subscription_required": true, + "free_subscription": false, + "service_homepage": "https://securitycenter.windows.com" +} diff --git a/responders/MSDefenderEndpoints/MSDefenderEndpoints_Unisolate.json b/responders/MSDefenderEndpoints/MSDefenderEndpoints_Unisolate.json index 32ee5b4cd..eda10343b 100644 --- a/responders/MSDefenderEndpoints/MSDefenderEndpoints_Unisolate.json +++ b/responders/MSDefenderEndpoints/MSDefenderEndpoints_Unisolate.json @@ -50,7 +50,7 @@ "type": "string", "multi": false, "required": true, - "defaultValue": "https://login.windows.net/" + "defaultValue": "https://login.microsoftonline.com" } ], "registration_required": true, diff --git a/responders/MSDefenderEndpoints/MSDefenderEndpoints_VirusScan.json b/responders/MSDefenderEndpoints/MSDefenderEndpoints_VirusScan.json index cccbaf2f5..69a9f9645 100644 --- a/responders/MSDefenderEndpoints/MSDefenderEndpoints_VirusScan.json +++ b/responders/MSDefenderEndpoints/MSDefenderEndpoints_VirusScan.json @@ -50,7 +50,7 @@ "type": "string", "multi": false, "required": true, - "defaultValue": "https://login.windows.net/" + "defaultValue": "https://login.microsoftonline.com" } ], "registration_required": true, diff --git a/responders/MSDefenderEndpoints/README.md b/responders/MSDefenderEndpoints/README.md index cb338f07a..66394dba9 100644 --- a/responders/MSDefenderEndpoints/README.md +++ b/responders/MSDefenderEndpoints/README.md @@ -4,7 +4,10 @@ * Isolate machine * Unisolate machine +* Restrict App Execution on a machine +* Remove app restriction on a machine * Run full antivirus scan +* Run an automated scan * Push IoC to Microsoft defender * Alert * BlockAndAlert @@ -37,7 +40,7 @@ In the registration form: ##### API permission On your new application page, click API Permissions > Add permission > APIs my organization uses > type **WindowsDefenderATP** and click on WindowsDefenderATP -Choose Application permissions, select **Alert.Read.All** AND **TI.ReadWrite.All** AND **Machine.ReadAll** AND **Machine.Isolate** AND **Machine.Scan** > Click on Add permissions. +Choose Application permissions, select **Alert.Read.All** AND **TI.ReadWrite.All** AND **Machine.ReadAll** AND **Machine.Isolate** AND **Machine.Scan** AND **Machine.RestrictExecution** > Click on Add permissions. After clicking the Add Permissions button, on the next screen we need to grant consent for the permission to take effect. Press the "Grant admin consent for {your tenant name}" button.