def __init__(self): Analyzer.__init__(self) self.region = self.get_param("config.region").lower() self.client_id = self.get_param("config.client_id", None, "No Threat Response client ID given.") self.client_password = self.get_param( "config.client_password", None, "No Threat Response client Password given.") self.extract_amp_targets = self.get_param("config.extract_amp_targets", False) # Validate that the supplied region is valid if self.region and self.region not in ("us", "eu", "apjc"): self.error( "{} is not a valid Threat Response region. Must be 'us', 'eu', or 'apjc'" .format(self.region)) # Set region to '' if 'us' was supplied if self.region == "us": self.region = "" # Create Threat Response client self.client = ThreatResponse( client_id=self.client_id, client_password=self.client_password, region=self.region, )
def get_module(module_name=MODULE_NAME): tr = ThreatResponse(CTR_CLIENT_ID, CTR_CLIENT_PASSWORD) modules = tr.int.module_instance.get() return next( (module for module in modules if module['name'] == module_name), None)
def check_for_sighting(returned_observables_json): ''' this function checks if there is a sighting for a specific observable ''' ctr_client = ThreatResponse( client_id=config_file["ctr"]["client_id"], client_password=config_file["ctr"]["client_password"], region='us', # can be change to eu or apjc ) data = returned_observables_json response = ctr_client.enrich.observe.observables(data) #check if request was succesful if response: returned_data = response total_amp_sighting_count = 0 total_umbrella_sighting_count = 0 total_email_sighting_count = 0 # run through all modules to check for sightings (currently checking the amp, umbrella and SMA modules) for module in returned_data['data']: if module['module'] == "AMP for Endpoints": # json key not always there, error checking... if 'sightings' in module['data']: # store amount of sightings total_amp_sighting_count = module['data']['sightings']['count'] if module['module'] == "Umbrella": # json key not always there, error checking... if 'sightings' in module['data']: # store amount of sightings total_umbrella_sighting_count = module['data']['sightings']['count'] if module['module'] == "SMA Email": # json key not always there, error checking... if 'sightings' in module['data']: # store amount of sightings total_email_sighting_count = module['data']['sightings']['count'] # create dict to store information regarding the sightings total_sighting_count = total_amp_sighting_count + total_umbrella_sighting_count + total_email_sighting_count return_sightings = { 'total_sighting_count': total_sighting_count, 'total_amp_sighting_count': total_amp_sighting_count, 'total_umbrella_sighting_count': total_umbrella_sighting_count, 'total_email_sighting_count': total_email_sighting_count } return(return_sightings) else: print(f"Sighting check request failed...\n") return(response)
def add(client_id, client_password, settings_file): tr = ThreatResponse(client_id, client_password) settings = load_settings(settings_file) modules = tr.int.module_instance.get() module = _module(modules, settings) if module: template = 'Relay module "{name}" already exists!' message = template.format(**settings) raise ModuleAlreadyExistsError(message) tr.int.module_instance.post(settings) template = 'Relay module "{name}" has been successfully added!' return template.format(**settings)
def remove(client_id, client_password, settings_file): tr = ThreatResponse(client_id, client_password) settings = load_settings(settings_file) modules = tr.int.module_instance.get() module = _module(modules, settings) if not module: template = 'Relay module "{name}" does not exist!' message = template.format(**settings) raise ModuleDoesNotExistError(message) tr.int.module_instance.delete(module['id']) template = 'Relay module "{name}" has been successfully removed!' return template.format(**settings)
def connect(self): message = None try: tr = ThreatResponse( client_id=self._client_id, client_password=self._client_password, region=self._region ) self.check_scopes(tr) return tr except RegionError as error: self.debug_print(repr(error)) message = ( 'Please make sure that your Region is valid.' ) except ScopeError as error: self.debug_print(repr(error)) message = error.args[0] except HTTPError as error: self.debug_print(repr(error)) if self.is_authentication_error(error.response): message = ( 'Please make sure that your API credentials ' '(Client ID and Client Password) are valid.' ) else: message = 'Unexpected HTTPError occurred.' except ConnectionError as error: self.debug_print(repr(error)) message = 'Failed to connect to Cisco Threat Response.' except Exception as error: self.debug_print(repr(error)) message = 'Unexpected error occurred.' raise Exception(message)
def main(): client_id = '' client_password = '' client = ThreatResponse(client_id=client_id, client_password=client_password, region='us') cleaup_modules(client) module_configs = get_module_type_configs() module_output = {} for module in module_configs: config = read_module_type_config(module) response = post_module_type(client, config) module_id = response['id'] title = response['title'] module_output.setdefault(module_id, title) save_module_id(module_id)
def test_python_module_negative_token(token, error): """Perform testing of availability perform request to the Threat response using invalid token ID: CCTRI-1579-4ca2a94f-db81-44c9-bf5b-53146cfd127a Steps: 1. Inspect observable using empty token 2. Inspect observable using invalid token Expectedresults: Inspect for provided observable doesn't returns expected values, because token is invalid Importance: Critical """ with pytest.raises(error): assert ThreatResponse(token=token).inspect.inspect( {'content': '1.1.1.1'}) != [{ 'type': 'ip', 'value': '1.1.1.1' }]
def return_observables(tweet_text): ''' this function will parse raw text and return the observables and types ''' ctr_client = ThreatResponse( client_id=config_file["ctr"]["client_id"], client_password=config_file["ctr"]["client_password"], region='us', # can be change to eu or apjc ) data = {"content":tweet_text} response = ctr_client.inspect.inspect(data) #check if request was succesful if response: return(response) elif response == []: print(f"No observables found for tweet:\n\n{tweet_text}\n") return False else: print(f"Error occured in inspecting tweet:\n\n{tweet_text}\n") return False
def edit(client_id, client_password, settings_file): tr = ThreatResponse(client_id, client_password) settings = load_settings(settings_file) modules = tr.int.module_instance.get() module = _module(modules, settings) if not module: template = 'Relay module "{name}" does not exist!' message = template.format(**settings) raise ModuleDoesNotExistError(message) diff = _diff(module, settings) if not diff: template = 'Relay module "{name}" has not been changed!' message = template.format(**settings) raise ModuleHasNotBeenChangedError(message) tr.int.module_instance.patch(module['id'], diff) template = 'Relay module "{name}" has been successfully edited!' return template.format(**settings)
def return_non_clean_observables(returned_observables_json): ''' this function returns only non clean observables (to remove noise) ''' ctr_client = ThreatResponse( client_id=config_file["ctr"]["client_id"], client_password=config_file["ctr"]["client_password"], region='us', # can be change to eu or apjc ) # create empty list to store clean observables clean_observables = [] # retrieve dispositions for observables response = ctr_client.enrich.deliberate.observables(returned_observables_json) disposition_observables = response # parse through json and search for observables with clean disposition (1) for module in disposition_observables['data']: module_name = module['module'] if 'verdicts' in module['data'] and module['data']['verdicts']['count'] > 0: docs = module['data']['verdicts']['docs'] for doc in docs: observable = doc['observable'] # if the disposition is clean / 1 then add to separate list to remove from other list if doc['disposition'] == 1: clean_observables.append(observable) #print(f"Clean observable, omitting: {observable}\n") non_clean_observables = [i for i in returned_observables_json if not i in clean_observables or clean_observables.remove(i)] non_clean_observables_json = non_clean_observables return non_clean_observables_json
def module_tool_client_token(module_token): return ThreatResponse(token=module_token)
def module_tool_client(): return ThreatResponse(client_id=settings.server.ctr_client_id, client_password=settings.server.ctr_client_password)
def main(): '''Main script logic ''' # Calculate now timestamp and store as file system friendly string now = datetime.now() start_time = datetime.strftime(now, '%Y-%m-%dT%H.%M.%S.%f') # Validate a SHA256 or file was provided user_input, input_type = validate_input() # AMP for Endpoints API Credentials amp_client_id = 'a1b2c3d4e5f6g7h8i9j0' amp_client_password = '******' amp_hostname = 'api.amp.cisco.com' # Instantiate AMP for Endpoints Session amp_session = requests.Session() amp_session.auth = (amp_client_id, amp_client_password) # Threat Response API Credentials tr_client_id = 'client-asdf12-34as-df12-34as-df1234asdf12' tr_client_password = '******' # Instantiate Threat Response Client client = ThreatResponse( client_id=tr_client_id, client_password=tr_client_password, ) # Container to store SHA256s that have malicious disposition in AMP cloud malicious_hashes = set() if input_type == 'File': # Validate and store user provided hashes in a set validated_user_provided_hashes, invalid_user_provided_hashes = validate_file_contents(user_input) print() # New line so "Getting SCD Lists" is printed in its own section else: # Store the user provided SHA256 validated_user_provided_hashes = {user_input} # Get Simple Custom Detaction File Lists print('Getting SCD Lists') scd_lists = get_scd_file_lists(amp_session, amp_hostname).json() data = scd_lists.get('data', []) # Present SCD Lists to user and ask which one to use for index, scd in enumerate(data, start=1): print(f'{index} - {scd["name"]}') index = ask_for_scd_index(len(data)) # Name SCD Name and GUID scd_name = data[index]['name'] scd_guid = data[index]['guid'] # Query AMP for Endpoints to get list items for selected SCD List print(f'\nGetting SHA256s for: {scd_name}') scd_list_items = get_file_list_items(amp_session, amp_hostname, scd_guid) # Put the SHA256s from the SCD List into a set using set comprehension existing_list_items = {list_item.get("sha256") for list_item in scd_list_items} # Inform how many SCD List items were found print(f'SHA256s on {scd_name}: {len(existing_list_items)}') # Compare user provided hashes against the slected SCD List new_user_provided_hashes = compare_list_items( scd_name, existing_list_items, validated_user_provided_hashes ) if not new_user_provided_hashes: sys.exit(f'\nAll of the provided SHA256s are already on {scd_name}\nBye!') # Build Threat Response Enrich Payloads using list comprehension enrich_payloads = [{"value": sha256, "type": "sha256"} for sha256 in new_user_provided_hashes] # Split payloads into list of lists with 20 items maximum item_count = len(enrich_payloads) if item_count > 20: print(f'\nSplitting into {math.ceil(item_count/20)} chunks of 20 or less and checking verdicts') else: print('\nChecking verdicts to remove any known malicious SHA256s') chunked_enrich_payloads = split_list(enrich_payloads) # Iterate over list and get Verdicts for list of SCD List items for payload_index, payload in enumerate(chunked_enrich_payloads, start=1): # Query Threat Response for verdcits print(f' Checking verdicts for chunk {payload_index} of {len(chunked_enrich_payloads)}') verdicts = get_verdicts(client, payload) parse_verdicts(verdicts, malicious_hashes) # Inform how many malicious dispositions were returned print(f'Number of provided SHA256s with a malicious disposition: {len(malicious_hashes)}') non_malicious_user_provided_hashes = new_user_provided_hashes.difference(malicious_hashes) if not non_malicious_user_provided_hashes: sys.exit('\nProvided SHA256s are already malicious in the AMP File Reputation Database\nBye!') if not confirm_continue( f'\nDo you want to add {len(non_malicious_user_provided_hashes)}' f' SHA256(s) to {scd_name}? (y/n): ' ): sys.exit("Bye!") # Iterate over remaining SHA256(s) and add to selected SCD List for sha256 in non_malicious_user_provided_hashes: print(f'Adding {sha256}', end=' ') response = add_list_item(amp_session, amp_hostname, scd_guid, sha256) if response.ok: print('- DONE!') else: print('- SOMETHING WENT WRONG!')
def main(): '''The main script logic ''' # Warn user and verify that you want to continue print('-=== WARNING THIS SCRIPT WILL DELETE THINGS ===-') if not confirm_continue(): sys.exit("Bye!") # Threat Response Credentials tr_client_id = 'client-asdf12-34as-df12-34as-df1234asdf12' tr_client_password = '******' # Instantiate Threat Response client client = ThreatResponse(client_id=tr_client_id, client_password=tr_client_password) # Define CTIM entities to delete entities = [ 'actor', 'attack-pattern', 'campaign', 'casebook', 'coa', 'incident', 'indicator', 'judgement', 'malware', 'relationship', 'sighting', 'tool', 'weakness' ] # Store response from second confirmation to continue delete_objects = False for entity in entities: print(entity) # Build Private Intel search objects obj = build_object(client, entity, 'search') # Search Private Intel for entity # A time range can be submitted as the query to delete entities within that range # response = search(obj, 'timestamp:["2020-01-01T01:00:00.000Z" TO "2020-03-31T00:00:00.000Z"]') response = search(obj) # Extract IDs from response ids = extract_ids(response) # Print number of IDs found print(' Found:', len(ids)) # Build Private Intel delete objects obj = build_object(client, entity, 'delete') # Warn user and verify that you want to continue if not delete_objects and len(ids) > 0: print( '\n-=== FINAL WARNING CONTINUING WILL DELETE EVERYTHING FOUND ===-' ) print( '-=== WILL NOT ASK AGAIN FOR OTHER ENTITIES ===-' ) if not confirm_continue(): sys.exit("Bye!") else: delete_objects = True # Delete the entities delete(obj, ids) # UNCOMMENT TO DELETE STUFF
def new_casebook(returned_observables_json,returned_sightings,user_name,tweet_text): ''' this function post list of observables to new case in casebook ''' ctr_client = ThreatResponse( client_id=config_file["ctr"]["client_id"], client_password=config_file["ctr"]["client_password"], region='us', # can be change to eu or apjc ) # create title and description for SOC researcher to have more context, if there are sightings, add high priority if returned_sightings['total_sighting_count'] == 0: casebook_title = " #opendir Tweet: " + user_name if returned_sightings['total_sighting_count'] != 0: casebook_title = "*HIGH PRIORITY* #opendir Tweet: " + user_name casebook_description = f"Twitter generated casebook from #opendir by: {user_name}, Tweet: {tweet_text}" casebook_datetime = datetime.now().isoformat() + "Z" # create right json format to create casebook casebook_json = { "title": casebook_title, "description": casebook_description, "observables": returned_observables_json, "type": "casebook", "timestamp": casebook_datetime } # create CTR search url search_base_url = "https://visibility.amp.cisco.com/investigate?q=" for observable in returned_observables_json: search_string_to_append = observable["type"] + "%3A" + observable["value"] + "%0A" search_base_url = search_base_url + search_string_to_append # post request to create casebook response = ctr_client.private_intel.casebook.post(casebook_json) if response: print(f"[201] Success, case added to Casebook added from #opendir Tweet by: {user_name}\n") # if Webex Teams tokens set, then send message to Webex room if config_file['webex']['access_token'] == '' or config_file['webex']['room_id'] == '': # user feed back print("Webex Teams not set.\n\n") else: # instantiate the Webex handler with the access token teams = webexteamssdk.WebexTeamsAPI(config_file['webex']['access_token']) # post a message to the specified Webex room try: if returned_sightings['total_sighting_count'] == 0: webex_text = f"🚨🚨🚨 - **New case added to SecureX Casebook added from 🐦 *#OPENDIR*!** - 🚨🚨🚨\n\nTweet by {user_name}:\n\n---\n>{tweet_text}\n\n---\n\n**Investigate directly with SecureX threat response:** {search_base_url}" message = teams.messages.create(config_file['webex']['room_id'], markdown=webex_text) if returned_sightings['total_sighting_count'] != 0: webex_text = f"🚨🚨🚨 - **New case added to SecureX Casebook added from 🐦 *#OPENDIR*!** - 🚨🚨🚨\n\nTweet by {user_name}:\n\n---\n>{tweet_text}\n\n---\n\n**HIGH PRIORITY**, Target Sightings have been identified! AMP targets: {str(returned_sightings['total_amp_sighting_count'])}, Umbrella targets: {str(returned_sightings['total_umbrella_sighting_count'])}, Email targets: {str(returned_sightings['total_email_sighting_count'])}.\n\n**Investigate directly with SecureX threat response:** {search_base_url}" message = teams.messages.create(config_file['webex']['room_id'], markdown=webex_text) # error handling, if for example the Webex API key expired except Exception: print("Webex authentication failed... Please make sure Webex Teams API key has not expired. Please review developer.webex.com for more info.\n") else: print(f"Something went wrong while posting the casebook to CTR...\n") return response
def main(): '''Main script logic ''' # Calculate now timestamp and store as file system friendly string now = datetime.now() start_time = datetime.strftime(now, '%Y-%m-%dT%H.%M.%S.%f') # AMP for Endpoints API Credentials amp_client_id = 'a1b2c3d4e5f6g7h8i9j0' amp_client_password = '******' amp_hostname = 'api.amp.cisco.com' # Instantiate AMP for Endpoints Session amp_session = requests.Session() amp_session.auth = (amp_client_id, amp_client_password) # Threat Response API Credentials tr_client_id = 'client-asdf12-34as-df12-34as-df1234asdf12' tr_client_password = '******' # Instantiate Threat Response Client client = ThreatResponse( client_id=tr_client_id, client_password=tr_client_password, ) # Container to store SHA256s that have malicious disposition in AMP cloud malicious_hashes = [] # Get Simple Custom Detaction File Lists scd_lists = get_scd_file_lists(amp_session, amp_hostname).json() data = scd_lists.get('data', []) # Present SCD Lists to user and ask which one to process for index, scd in enumerate(data, start=1): print(f'{index} - {scd["name"]}') index = ask_for_scd_index(len(data)) # Name SCD Name and GUID scd_name = data[index]['name'] scd_guid = data[index]['guid'] # Get List items for selected SCD List print(f'\nGetting SHA256s for: {scd_name}') scd_list_items = get_file_list_items(amp_session, amp_hostname, scd_guid) # Build Threat Response Enrich Payloads using list comprehension enrich_payloads = [ {"value": list_item.get("sha256"), "type": "sha256"} for list_item in scd_list_items ] # Inform how many SCD List items were found item_count = len(enrich_payloads) print(f'SHA256s on {scd_name}: {item_count} \n') if not enrich_payloads: sys.exit() # Split payloads into list of lists with 20 items maximum if item_count > 20: print(f'Splitting into {math.ceil(item_count/20)} chunks of 20 or less and checking verdicts') else: print('Checking verdicts') chunked_enrich_payloads = split_list(enrich_payloads) # Iterate over list and get Verdicts for list of SCD List items for payload_index, payload in enumerate(chunked_enrich_payloads, start=1): # Query Threat Response for verdcits print(f' Checking verdicts for chunk {payload_index} of {len(chunked_enrich_payloads)}') verdicts = get_verdicts(client, payload) parse_verdicts(verdicts, malicious_hashes) # Inform how many malicious dispositions were returned print(f'Number of SHA256s on {scd_name} with a malicious disposition: {len(malicious_hashes)}') # Verify there are SHA256s with malicious dispositions and confirm the user wants to delete them if malicious_hashes: file_name = f'{replace_space(scd_name)}_{scd_guid}_{start_time}.txt' print(f'\nSaving SHA256s to file:\n {file_name}') save_list_items(file_name, malicious_hashes) if not confirm_continue( f'\nDo you want to remove these SHA256s from {scd_name}? (y/n): ' ): sys.exit("Bye!") else: sys.exit() # Delete SHA256s with malicious disposition from selected SCD List for sha256 in malicious_hashes: print(f'Deleting {sha256}', end=' ') response = delete_list_item(amp_session, amp_hostname, scd_guid, sha256) if response.ok: print('- DONE!') else: print('- SOMETHING WENT WRONG!')