def add_misp_tag(self, tag_name, tag_color) -> Optional[dict]: """ Create a new tag in MISP :param tag_name: Name of the new tag :param tag_color: Color of the new tag :return: Server response if succesful, else None """ LOGGER.debug(f'Creating a {tag_name} tag in MISP') if not self.verify_tls: urllib3.disable_warnings() response = requests.post(f'{self.protocol}://{self.host}/tags/add', json={ 'name': tag_name, 'colour': tag_color }, headers={ 'Authorization': self.token, 'Accept': 'application/json' }, timeout=10, verify=self.verify_tls) try: response.raise_for_status() return response.json() except requests.HTTPError: LOGGER.critical( f'Creating MISP Tag responded with status code:{response.status_code}' ) return None
def add_misp_tag_to_event(self, event_id, tag_id): """ Add MISP tag to MISP event :param event_id: :param tag_id: :return: """ LOGGER.debug('Adding DDoSCH tag to the event') if not self.verify_tls: urllib3.disable_warnings() response = requests.post( f'{self.protocol}://{self.host}/events/addTag/{event_id}/{tag_id}', headers={ 'Authorization': self.token, 'Accept': 'application/json' }, timeout=10, verify=self.verify_tls) LOGGER.debug(f'status: {response.status_code}') try: response.raise_for_status() return response.json() except requests.HTTPError: LOGGER.critical( f'Creating MISP Tag responded with status code:{response.status_code}' ) return None
def search_misp_events(self, misp_filter: dict = None) -> Optional[dict]: """ Search for MISP events :param misp_filter: fields by which to filter retrieved MISP events :return: MISP events if found, else None """ LOGGER.debug(f'Searching MISP events with filter: {misp_filter}') if not self.verify_tls: urllib3.disable_warnings() response = requests.post(f'{self.protocol}://{self.host}/events/index', json=misp_filter or dict(), headers={ 'Authorization': self.token, 'Accept': 'application/json' }, timeout=10, verify=self.verify_tls) try: response.raise_for_status() return response.json() except requests.HTTPError: LOGGER.critical( f'Retrieving MISP events responded with status code:{response.status_code}' ) return None
def pauseRunner(self): if self.dut.task_runner.b_started: self.dut.pauseRunner() self.actionRun.setEnabled(True) self.actionPause.setDisabled(True) #self.self.dut.task_runner.b_started = False #self.dut.task_runner.scenario_level_pause = True LOGGER.critical("Pause task runner")
def __init__(self, host: str, token: str, protocol: str, verify_tls: bool): self.host = host self.token = token self.protocol = protocol self.verify_tls = verify_tls self.misp: ExpandedPyMISP try: self.misp = ExpandedPyMISP(f'{self.protocol}://{self.host}', self.token, ssl=self.verify_tls, tool='dissector') except PyMISPError: LOGGER.critical( f'Could not connect to MISP instance at "{self.protocol}://{self.host}".' ) self.misp = None
def upload_to_misp(self, misp_instance: MispInstance) -> int: """ Upload fingerprint to a MISP instance :param misp_instance: MISP instance to which to upload the fingerprint :return: HTTP response code """ LOGGER.info(f'Uploading fingerprint to MISP: {misp_instance.host}') fingerprint_json = self.as_dict(anonymous=not self.show_target) misp_filter = { 'minimal': True, 'tag': 'DDoSCH', 'eventinfo': self.checksum, } LOGGER.debug( f'Checking if fingerprint {self.checksum} is already present in the MISP' ) try: misp_events = misp_instance.search_misp_events(misp_filter) except requests.exceptions.SSLError: LOGGER.critical( f'SSL Certificate verification of the server {misp_instance.host} failed. ' f'To ignore the certificate pass the --noverify flag.') LOGGER.info('Fingerprint NOT uploaded.') return 500 if misp_events: LOGGER.critical( 'The fingerprint already exists in this MISP instance.') LOGGER.info('Fingerprint NOT uploaded.') return 500 misp_instance.add_misp_fingerprint(fingerprint_json) return 201
def infer_target(attack: Attack) -> IPNetwork: """ Infer the target IP address(es) of this attack. If the target is a subnet, this method will homogenize the IP adresses in the attack to the first address in the subnet. :param attack: Attack object of which to determine the target IP address or network :return: Target IP address as an IPNetwork """ LOGGER.debug("Inferring attack target.") targets: list[IPAddress] = get_outliers(attack.data, column='destination_address', fraction_for_outlier=0.5, use_zscore=False) if len(targets) > 0: return IPNetwork(targets[0]) LOGGER.info('No clear target IP address could be inferred.') # Ask the user if the most common destination address (most packets received) is the target packets_per_ip = attack.data.groupby( 'destination_address').nr_packets.sum().sort_values(ascending=False) most_traffic_address, nr_packets = list(packets_per_ip.items())[0] use_most_common = input( f'The most common destination address is {most_traffic_address} ' f'({round(nr_packets / packets_per_ip.sum() * 100, 1)}% of captured packets), ' f'is this the target? y/n: ') if use_most_common.lower().strip() in ['y', 'yes']: return IPNetwork(most_traffic_address) LOGGER.info( 'You can pass a target IP address with the --target flag. ' 'Alternatively, Dissector can look for a target subnet (IPv4/24 or IPv6/64) in case of a carpet ' 'bombing attack.') keep_going = input('Continue looking for a target subnet? y/n: ') if keep_going.lower().strip() not in ['y', 'yes']: LOGGER.info('Aborting.') sys.exit() # Check for the most targeted IP addresses the fraction of destination IPs that is in their /24 or /64 subnet best_network, fraction_ips_in_network = None, 0 for target in packets_per_ip.keys()[:25]: network = IPNetwork(f'{target}/{"24" if target.version == 4 else "64"}' ) # /24 (IPv4) or /64 (IPv6) subnet frac = packets_per_ip[[ x for x in packets_per_ip.keys() if x in network ]].sum() / packets_per_ip.sum() if frac > fraction_ips_in_network: best_network, fraction_ips_in_network = network, frac if fraction_ips_in_network > 0.7: LOGGER.debug( f'Found an IP subnet that comprises a large fraction of the flows\' destination IPs ' f'({round(fraction_ips_in_network, 2)}).') else: LOGGER.critical( 'Could not infer a clear target IP address from the data. You can explicitly identify the ' 'target with the --target flag.') use_target = input( f'The most prominent destination IP network is {best_network}, with ' f'{round(fraction_ips_in_network * 100, 1)}% of packets. ' f'Is this the target of this attack? y/n: ') if use_target.lower() not in ['y', 'yes']: sys.exit(-1) return best_network
def upload_to_ddosdb(self, host: str, token: str, protocol: str = 'https', noverify: bool = False) -> int: """ Upload fingerprint to a DDoS-DB instance :param host: hostname of the DDoS-DB instance, without schema (like db.example.com) :param token: DDoS-DB Authorization Token :param protocol: Protocol to use (http or https) :param noverify: (bool) ignore invalid TLS certificate :return: HTTP response code """ LOGGER.info(f'Uploading fingerprint to DDoS-DB: {host}...') fp_json = json.dumps(self.as_dict(anonymous=not self.show_target)) headers = {'Authorization': f'Token {token}'} try: try: if noverify: urllib3.disable_warnings() r = requests.post(f'{protocol}://{host}/api/fingerprint/', json=fp_json, headers=headers, verify=not noverify) except requests.exceptions.SSLError: LOGGER.critical( f'SSL Certificate verification of the server {host} failed. To ignore the certificate ' f'pass the --noverify flag.') LOGGER.info('Fingerprint NOT uploaded to DDoS-DB') return 500 except requests.exceptions.RequestException as e: LOGGER.critical( 'Cannot connect to the DDoS-DB server to upload fingerprint') LOGGER.debug(e) return 500 if r.status_code == 403: LOGGER.critical( 'Invalid DDoS-DB credentials or no permission to upload fingerprints.' ) elif r.status_code == 413: LOGGER.critical( 'Fingerprint is too large to upload to this DDoS-DB instance.') elif r.status_code == 201: LOGGER.info( f'Upload success! URL: https://{host}/details?key={self.checksum}' ) else: LOGGER.critical('DDoS-DB Internal Server Error.') LOGGER.critical('Error Code: {}'.format(r.status_code)) return r.status_code