Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes and added features to Defender for endpoints responder #1225

Open
wants to merge 9 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions responders/MSDefenderEndpoints/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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
RUN test ! -e MSDefenderEndpoints/requirements.txt || pip install --no-cache-dir -r MSDefenderEndpoints/requirements.txt
ENTRYPOINT MSDefenderEndpoints/MSDefenderEndpoints.py
150 changes: 133 additions & 17 deletions responders/MSDefenderEndpoints/MSDefenderEndpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -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!')
Expand All @@ -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
)

Expand Down Expand Up @@ -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"})
Expand Down Expand Up @@ -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'
}

Expand All @@ -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),
Expand All @@ -193,28 +299,38 @@ 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":
pushCustomIocAlert(self.observable)
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")]
elif self.service == "runFullVirusScan":
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__':

Expand Down
Original file line number Diff line number Diff line change
@@ -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"
}

Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"type": "string",
"multi": false,
"required": true,
"defaultValue": "https://login.windows.net/"
"defaultValue": "https://login.microsoftonline.com"
}
],
"registration_required": true,
Expand Down
Original file line number Diff line number Diff line change
@@ -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",
Expand Down Expand Up @@ -50,7 +50,7 @@
"type": "string",
"multi": false,
"required": true,
"defaultValue": "https://login.windows.net/"
"defaultValue": "https://login.microsoftonline.com"
}
],
"registration_required": true,
Expand Down
Original file line number Diff line number Diff line change
@@ -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",
Expand Down Expand Up @@ -50,7 +50,7 @@
"type": "string",
"multi": false,
"required": true,
"defaultValue": "https://login.windows.net/"
"defaultValue": "https://login.microsoftonline.com"
}
],
"registration_required": true,
Expand Down
Original file line number Diff line number Diff line change
@@ -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"
}

Loading