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

Cybereason fetch machine details #623

Merged
merged 10 commits into from
Oct 16, 2023
Merged
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
70 changes: 60 additions & 10 deletions Packs/Cybereason/Integrations/Cybereason/Cybereason.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@

USER_HEADERS = ['Username', 'Domain', 'LastMachineLoggedInTo', 'Organization', 'LocalSystem']

SENSOR_HEADERS = ['MachineID', 'MachineName', 'MachineFQDN', 'GroupID', 'GroupName']

CONNECTION_INFO = [
{'field': 'elementDisplayName', 'header': 'Name', 'type': 'simple'},
{'field': 'direction', 'header': 'Direction', 'type': 'simple'},
Expand Down Expand Up @@ -154,6 +156,7 @@ def cybereason_api_call(
error_msg = 'Authentication failed, verify the credentials are correct.'
raise ValueError(
f'Failed to process the API response. {str(error_msg)} {str(error_content)} - {str(e)}')
return None

def error_handler(self, res: requests.Response):
# Handle error responses gracefully
Expand Down Expand Up @@ -409,6 +412,7 @@ def query_connections_command(client: Client, args: dict):
outputs_prefix='Cybereason.Connection',
outputs_key_field='Name',
outputs=context)
return None


def query_connections(client: Client, machine: str, ip: str, filter_input: str) -> dict:
Expand Down Expand Up @@ -551,7 +555,7 @@ def poll_malops(client: Client, start_time):

def get_non_edr_malop_data(client, start_time):
malop_data = poll_malops(client, start_time)
non_edr_malop_data = list()
non_edr_malop_data = []
for malops in malop_data['malops']:
if not malops.get('edr'):
non_edr_malop_data.append(malops)
Expand Down Expand Up @@ -666,7 +670,7 @@ def malop_processes_command(client: Client, args: dict):
raise DemistoException("dateTime could not be parsed. Please enter a valid time parameter.")
date_time_parser = date_time_parser.timestamp()
milliseconds = int(date_time_parser * 1000)
filter_input = [{"facetName": "creationTime", "filterType": "GreaterThan", "values": [milliseconds], "isResult":True}]
filter_input = [{"facetName": "creationTime", "filterType": "GreaterThan", "values": [milliseconds], "isResult": True}]

if isinstance(malop_guids, str):
malop_guids = malop_guids.split(',')
Expand Down Expand Up @@ -909,6 +913,7 @@ def kill_process_command(client: Client, args: dict):
action_status, ['Remediation status'])} \n Reason: {dict_safe_get(
action_status, ['Reason'])} \n Remediation ID: {dict_safe_get(action_status, ['Remediation ID'])}'''
raise DemistoException(failure_response)
return None
else:
raise DemistoException('Machine must be connected to Cybereason in order to perform this action.')

Expand All @@ -933,6 +938,7 @@ def quarantine_file_command(client: Client, args: dict):
action_status, ['Remediation status'])} \n Reason: {dict_safe_get(
action_status, ['Reason'])} \n Remediation ID: {dict_safe_get(action_status, ['Remediation ID'])}'''
raise DemistoException(failure_response)
return None
else:
raise DemistoException('Machine must be connected to Cybereason in order to perform this action.')

Expand All @@ -957,6 +963,7 @@ def unquarantine_file_command(client: Client, args: dict):
action_status, ['Remediation status'])} \n Reason: {dict_safe_get(
action_status, ['Reason'])} \n Remediation ID: {dict_safe_get(action_status, ['Remediation ID'])}'''
raise DemistoException(failure_response)
return None
else:
raise DemistoException('Machine must be connected to Cybereason in order to perform this action.')

Expand All @@ -981,6 +988,7 @@ def block_file_command(client: Client, args: dict):
action_status, ['Remediation status'])} \n Reason: {dict_safe_get(
action_status, ['Reason'])} \n Remediation ID: {dict_safe_get(action_status, ['Remediation ID'])}'''
raise DemistoException(failure_response)
return None
else:
raise DemistoException('Machine must be connected to Cybereason in order to perform this action.')

Expand All @@ -1005,6 +1013,7 @@ def delete_registry_key_command(client: Client, args: dict):
action_status, ['Remediation status'])} \n Reason: {dict_safe_get(
action_status, ['Reason'])} \n Remediation ID: {dict_safe_get(action_status, ['Remediation ID'])}'''
raise DemistoException(failure_response)
return None
else:
raise DemistoException('Machine must be connected to Cybereason in order to perform this action.')

Expand All @@ -1029,6 +1038,7 @@ def kill_prevent_unsuspend_command(client: Client, args: dict):
action_status, ['Remediation status'])} \n" Reason: {dict_safe_get(
action_status, ['Reason'])} \n Remediation ID: {dict_safe_get(action_status, ['Remediation ID'])}'''
raise DemistoException(failure_response)
return None
else:
raise DemistoException('Machine must be connected to Cybereason in order to perform this action.')

Expand All @@ -1053,6 +1063,7 @@ def unsuspend_process_command(client: Client, args: dict):
action_status, ['Remediation status'])} \n Reason: {dict_safe_get(
action_status, ['Reason'])} \n Remediation ID: {dict_safe_get(action_status, ['Remediation ID'])}'''
raise DemistoException(failure_response)
return None
else:
raise DemistoException('Machine must be connected to Cybereason in order to perform this action.')

Expand Down Expand Up @@ -1177,7 +1188,7 @@ def query_file_command(client: Client, args: dict) -> Any:

is_signed = None
if 'isSigned' in simple_values:
is_signed = True if dict_safe_get(simple_values, ['isSigned', 'values', 0]) == 'true' else False
is_signed = dict_safe_get(simple_values, ['isSigned', 'values', 0]) == 'true'

cybereason_outputs.append({
'Name': file_name,
Expand Down Expand Up @@ -1206,6 +1217,7 @@ def query_file_command(client: Client, args: dict) -> Any:
outputs=cybereason_outputs)
else:
raise DemistoException('No results found.')
return None


def query_file(client: Client, filters: list) -> dict:
Expand Down Expand Up @@ -1296,6 +1308,7 @@ def query_domain_command(client: Client, args: dict) -> Any:
outputs=cybereason_outputs)
else:
raise DemistoException('No results found.')
return None


def query_domain(client: Client, filters: list) -> dict:
Expand Down Expand Up @@ -1337,7 +1350,7 @@ def query_user_command(client: Client, args: dict):
element_values = dict_safe_get(user, ['elementValues'], default_return_value={}, return_type=dict)

domain = dict_safe_get(simple_values, ['domain', 'values', 0])
local_system = True if dict_safe_get(simple_values, ['isLocalSystem', 'values', 0]) == 'true' else False
local_system = dict_safe_get(simple_values, ['isLocalSystem', 'values', 0]) == 'true'
machine = dict_safe_get(element_values, ['ownerMachine', 'elementValues', 0, 'name'])
organization = dict_safe_get(element_values, ['ownerOrganization', 'elementValues', 0, 'name'])

Expand All @@ -1357,6 +1370,7 @@ def query_user_command(client: Client, args: dict):
outputs=cybereason_outputs)
else:
raise DemistoException('No results found.')
return None


def query_user(client: Client, filters: list) -> dict:
Expand Down Expand Up @@ -1620,16 +1634,16 @@ def fetch_malop_processes(client: Client, malop_id: str) -> list:
{
"requestedType": "MalopProcess",
"filters": [],
"guidList":[malop_id],
"connectionFeature":{
"guidList": [malop_id],
"connectionFeature": {
"elementInstanceType": "MalopProcess",
"featureName": "suspects"
}
},
{
"requestedType": "Process",
"filters": [],
"isResult":True
"isResult": True
}
],
"totalResultLimit": 1000,
Expand Down Expand Up @@ -1693,10 +1707,10 @@ def fetch_imagefile_guids(client: Client, processes: list) -> dict:
"integrity", "isHidden", "logonSession", "remoteSession", "isWhiteListClassification", "matchedWhiteListRuleIds"]
}
response = client.cybereason_api_call('POST', '/rest/visualsearch/query/simple', json_body=json_body)
img_file_guids = dict()
img_file_guids = {}
result = response['data']['resultIdToElementDataMap']
try:
for process, details in list(result.items()):
for _process, details in list(result.items()):
image_files = ('' if details['elementValues']['imageFile']['elementValues'] is None else details[
'elementValues']['imageFile']['elementValues'])
for image_file in image_files:
Expand All @@ -1710,13 +1724,14 @@ def start_fetchfile_command(client: Client, args: dict):
malop_id = str(args.get('malopGUID'))
user_name = str(args.get('userName'))
response = get_file_guids(client, malop_id)
for filename, file_guid in list(response.items()):
for _filename, file_guid in list(response.items()):
api_response = start_fetchfile(client, file_guid, user_name)
try:
if api_response['status'] == "SUCCESS":
return CommandResults(readable_output="Successfully started fetching file for the given malop")
except Exception:
raise Exception("Failed to start fetch file process")
return None


def start_fetchfile(client: Client, element_id: str, user_name: str) -> dict:
Expand Down Expand Up @@ -1819,6 +1834,7 @@ def malware_query_command(client: Client, args: dict):
if limit_range > 0:
filter_response = malware_query_filter(client, needs_attention, malware_type, malware_status, time_stamp, limit_range)
return CommandResults(raw_response=filter_response)
return None
else:
raise DemistoException("Limit cannot be zero or a negative number.")

Expand Down Expand Up @@ -1910,6 +1926,37 @@ def get_sensor_id_command(client: Client, args: dict):
return CommandResults(readable_output=f"Available Sensor IDs are {output}")


def fetch_machine_details_command(client: Client, args: dict):
machine_name = str(args.get('machineName'))
json_body = {}
if machine_name:
json_body = {
"limit": 1000,
"offset": 0
}
response = client.cybereason_api_call('POST', '/rest/sensors/query', json_body=json_body)
if dict_safe_get(response, ['sensors']) == []:
raise DemistoException("Could not find any Sensor ID for the machine" + machine_name)
else:
outputs = []
for single_sensor in response['sensors']:
if single_sensor['machineName'] == machine_name:
outputs.append({
"MachineID": single_sensor["sensorId"],
"MachineName": single_sensor["machineName"],
"MachineFQDN": single_sensor["fqdn"],
"GroupID": single_sensor["groupId"],
"GroupName": single_sensor["groupName"]
})
empty_output_message = 'No Machine Details found for the given Machine Name'
return CommandResults(
readable_output=tableToMarkdown(
'Machine Details', outputs, headers=SENSOR_HEADERS) if outputs else empty_output_message,
outputs_prefix='Cybereason.Sensor',
outputs_key_field='MachineID',
outputs=outputs)


def main():
auth = ''
params = demisto.params()
Expand Down Expand Up @@ -2041,6 +2088,9 @@ def main():
elif demisto.command() == 'cybereason-get-sensor-id':
return_results(get_sensor_id_command(client, args))

elif demisto.command() == 'cybereason-fetch-machine-details':
return_results(fetch_machine_details_command(client, args))

else:
raise NotImplementedError(f'Command {demisto.command()} is not implemented.')

Expand Down
Loading
Loading