def _get_inaccessible_subnets_ips(self): """ For each of the machine's IPs, checks if it's in one of the subnets specified in the 'inaccessible_subnets' config value. If so, all other subnets in the config value shouldn't be accessible. All these subnets are returned. :return: A list of subnets that shouldn't be accessible from the machine the monkey is running on. """ subnets_to_scan = [] if len(WormConfiguration.inaccessible_subnets) > 1: for subnet_str in WormConfiguration.inaccessible_subnets: if NetworkScanner._is_any_ip_in_subnet( [unicode(x) for x in self._ip_addresses], subnet_str): # If machine has IPs from 2 different subnets in the same group, there's no point checking the other # subnet. for other_subnet_str in WormConfiguration.inaccessible_subnets: if other_subnet_str == subnet_str: continue if not NetworkScanner._is_any_ip_in_subnet( [unicode(x) for x in self._ip_addresses], other_subnet_str): subnets_to_scan.append( NetworkRange.get_range_obj(other_subnet_str)) break return subnets_to_scan
def get_cross_segment_issues_per_subnet_pair(scans, source_subnet, target_subnet): """ Gets list of cross segment issues from source_subnet to target_subnet. :param scans: List of all scan telemetry entries. Must have monkey_guid, ip_addr and services. This should be a PyMongo cursor object. :param source_subnet: The subnet which shouldn't be able to access target_subnet. :param target_subnet: The subnet which shouldn't be accessible from source_subnet. :return: """ if source_subnet == target_subnet: return [] source_subnet_range = NetworkRange.get_range_obj(source_subnet) target_subnet_range = NetworkRange.get_range_obj(target_subnet) cross_segment_issues = [] scans.rewind() # If we iterated over scans already we need to rewind. for scan in scans: target_ip = scan["data"]["machine"]["ip_addr"] if target_subnet_range.is_in_range(str(target_ip)): monkey = NodeService.get_monkey_by_guid(scan["monkey_guid"]) cross_segment_ip = get_ip_in_src_and_not_in_dst( monkey["ip_addresses"], source_subnet_range, target_subnet_range) if cross_segment_ip is not None: cross_segment_issues.append({ "source": cross_segment_ip, "hostname": monkey["hostname"], "target": target_ip, "services": scan["data"]["machine"]["services"], "icmp": scan["data"]["machine"]["icmp"], "is_self": False, }) return cross_segment_issues + ReportService.get_cross_segment_issues_of_single_machine( source_subnet_range, target_subnet_range)
def get_cross_segment_issues_per_subnet_pair(scans, source_subnet, target_subnet): """ Gets list of cross segment issues from source_subnet to target_subnet. :param scans: List of all scan telemetry entries. Must have monkey_guid, ip_addr and services. This should be a PyMongo cursor object. :param source_subnet: The subnet which shouldn't be able to access target_subnet. :param target_subnet: The subnet which shouldn't be accessible from source_subnet. :return: """ if source_subnet == target_subnet: return [] source_subnet_range = NetworkRange.get_range_obj(source_subnet) target_subnet_range = NetworkRange.get_range_obj(target_subnet) cross_segment_issues = [] scans.rewind() # If we iterated over scans already we need to rewind. for scan in scans: target_ip = scan['data']['machine']['ip_addr'] if target_subnet_range.is_in_range(unicode(target_ip)): monkey = NodeService.get_monkey_by_guid(scan['monkey_guid']) cross_segment_ip = ReportService.get_ip_in_src_and_not_in_dst( monkey['ip_addresses'], source_subnet_range, target_subnet_range) if cross_segment_ip is not None: cross_segment_issues.append({ 'source': cross_segment_ip, 'hostname': monkey['hostname'], 'target': target_ip, 'services': scan['data']['machine']['services'], 'is_self': False }) return cross_segment_issues + ReportService.get_cross_segment_issues_of_single_machine( source_subnet_range, target_subnet_range)
def is_segmentation_violation(current_monkey: Monkey, target_ip: str, source_subnet: str, target_subnet: str) -> bool: """ Checks is a specific communication is a segmentation violation. :param current_monkey: The source monkey which originated the communication. :param target_ip: The target with which the current monkey communicated with. :param source_subnet: The segment the monkey belongs to. :param target_subnet: Another segment which the monkey isn't supposed to communicate with. :return: True if this is a violation of segmentation between source_subnet and target_subnet; Otherwise, False. """ if source_subnet == target_subnet: return False source_subnet_range = NetworkRange.get_range_obj(source_subnet) target_subnet_range = NetworkRange.get_range_obj(target_subnet) if target_subnet_range.is_in_range(str(target_ip)): cross_segment_ip = get_ip_in_src_and_not_in_dst( current_monkey.ip_addresses, source_subnet_range, target_subnet_range) return cross_segment_ip is not None
def get_segmentation_violation_event(current_monkey, source_subnet, target_ip, target_subnet): return Event.create_event( title="Segmentation event", message=SEGMENTATION_VIOLATION_EVENT_TEXT.format( hostname=current_monkey.hostname, source_ip=get_ip_if_in_subnet( current_monkey.ip_addresses, NetworkRange.get_range_obj(source_subnet)), source_seg=source_subnet, target_ip=target_ip, target_seg=target_subnet), event_type=zero_trust_consts.EVENT_TYPE_MONKEY_NETWORK)
def initialize(self): """ Set up scanning. based on configuration: scans local network and/or scans fixed list of IPs/subnets. """ # get local ip addresses self._ip_addresses = local_ips() if not self._ip_addresses: raise Exception("Cannot find local IP address for the machine") LOG.info("Found local IP addresses of the machine: %r", self._ip_addresses) # for fixed range, only scan once. self._ranges = [NetworkRange.get_range_obj(address_str=x) for x in WormConfiguration.subnet_scan_list] if WormConfiguration.local_network_scan: self._ranges += get_interfaces_ranges() self._ranges += self._get_inaccessible_subnets_ips() LOG.info("Base local networks to scan are: %r", self._ranges)
def create_or_add_findings_for_all_pairs(all_subnets, current_monkey): # Filter the subnets that this monkey is part of. this_monkey_subnets = [] for subnet in all_subnets: if get_ip_if_in_subnet(current_monkey.ip_addresses, NetworkRange.get_range_obj(subnet)) is not None: this_monkey_subnets.append(subnet) # Get all the other subnets. other_subnets = list(set(all_subnets) - set(this_monkey_subnets)) # Calculate the cartesian product - (this monkey subnets X other subnets). These pairs are the pairs that the monkey # should have tested. all_subnets_pairs_for_this_monkey = itertools.product( this_monkey_subnets, other_subnets) for subnet_pair in all_subnets_pairs_for_this_monkey: SegmentationFinding.create_or_add_to_existing_finding( subnets=list(subnet_pair), status=zero_trust_consts.STATUS_PASSED, segmentation_event=get_segmentation_done_event( current_monkey, subnet_pair))
def _is_any_ip_in_subnet(ip_addresses, subnet_str): for ip_address in ip_addresses: if NetworkRange.get_range_obj(subnet_str).is_in_range(ip_address): return True return False