def get_ssh_keys(): """ Return private ssh keys found as credentials :return: List of credentials """ creds = [] for telem in mongo.db.telemetry.find( {"telem_category": "system_info", "data.ssh_info": {"$exists": True}}, {"data.ssh_info": 1, "monkey_guid": 1}, ): origin = NodeService.get_monkey_by_guid(telem["monkey_guid"])["hostname"] if telem["data"]["ssh_info"]: # Pick out all ssh keys not yet included in creds ssh_keys = [ { "username": key_pair["name"], "type": "Clear SSH private key", "origin": origin, } for key_pair in telem["data"]["ssh_info"] if key_pair["private_key"] and { "username": key_pair["name"], "type": "Clear SSH private key", "origin": origin, } not in creds ] creds.extend(ssh_keys) return creds
def get_ssh_keys(): """ Return private ssh keys found as credentials :return: List of credentials """ creds = [] for telem in mongo.db.telemetry.find( { 'telem_category': 'system_info', 'data.ssh_info': { '$exists': True } }, { 'data.ssh_info': 1, 'monkey_guid': 1 }): origin = NodeService.get_monkey_by_guid( telem['monkey_guid'])['hostname'] if telem['data']['ssh_info']: # Pick out all ssh keys not yet included in creds ssh_keys = [{ 'username': key_pair['name'], 'type': 'Clear SSH private key', 'origin': origin } for key_pair in telem['data']['ssh_info'] if key_pair['private_key'] and { 'username': key_pair['name'], 'type': 'Clear SSH private key', 'origin': origin } not in creds] creds.extend(ssh_keys) return creds
def get_azure_creds(): """ Recover all credentials marked as being from an Azure machine :return: List of credentials. """ creds = [] for telem in mongo.db.telemetry.find( { 'telem_category': 'system_info', 'data.Azure': { '$exists': True } }, { 'data.Azure': 1, 'monkey_guid': 1 }): azure_users = telem['data']['Azure']['usernames'] if len(azure_users) == 0: continue origin = NodeService.get_monkey_by_guid( telem['monkey_guid'])['hostname'] azure_leaked_users = [{ 'username': user.replace(',', '.'), 'type': 'Clear Password', 'origin': origin } for user in azure_users] creds.extend(azure_leaked_users) logger.info('Azure machines creds generated for reporting') return creds
def get_stolen_creds(): PASS_TYPE_DICT = { 'password': '******', 'lm_hash': 'LM hash', 'ntlm_hash': 'NTLM hash' } creds = [] for telem in mongo.db.telemetry.find( { 'telem_type': 'system_info_collection', 'data.credentials': { '$exists': True } }, { 'data.credentials': 1, 'monkey_guid': 1 }): monkey_creds = telem['data']['credentials'] if len(monkey_creds) == 0: continue origin = NodeService.get_monkey_by_guid( telem['monkey_guid'])['hostname'] for user in monkey_creds: for pass_type in monkey_creds[user]: cred_row = \ { 'username': user.replace(',', '.'), 'type': PASS_TYPE_DICT[pass_type], 'origin': origin } if cred_row not in creds: creds.append(cred_row) logger.info('Stolen creds generated for reporting') return creds
def process_tunnel_telemetry(telemetry_json): monkey_id = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid'])["_id"] if telemetry_json['data']['proxy'] is not None: tunnel_host_ip = telemetry_json['data']['proxy'].split(":")[-2].replace("//", "") NodeService.set_monkey_tunnel(monkey_id, tunnel_host_ip) else: NodeService.unset_all_monkey_tunnels(monkey_id)
def process_wmi_info(telemetry_json): users_secrets = {} if "wmi" in telemetry_json["data"]: monkey_id = NodeService.get_monkey_by_guid(telemetry_json["monkey_guid"]).get("_id") wmi_handler = WMIHandler(monkey_id, telemetry_json["data"]["wmi"], users_secrets) wmi_handler.process_and_handle_wmi_info()
def process_system_info_telemetry(telemetry_json): users_secrets = {} monkey_id = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid']).get('_id') if 'ssh_info' in telemetry_json['data']: ssh_info = telemetry_json['data']['ssh_info'] Telemetry.encrypt_system_info_ssh_keys(ssh_info) if telemetry_json['data']['network_info']['networks']: # We use user_name@machine_ip as the name of the ssh key stolen, thats why we need ip from telemetry Telemetry.add_ip_to_ssh_keys(telemetry_json['data']['network_info']['networks'][0], ssh_info) Telemetry.add_system_info_ssh_keys_to_config(ssh_info) if 'credentials' in telemetry_json['data']: creds = telemetry_json['data']['credentials'] Telemetry.encrypt_system_info_creds(creds) Telemetry.add_system_info_creds_to_config(creds) Telemetry.replace_user_dot_with_comma(creds) if 'mimikatz' in telemetry_json['data']: users_secrets = mimikatz_utils.MimikatzSecrets.\ extract_secrets_from_mimikatz(telemetry_json['data'].get('mimikatz', '')) if 'wmi' in telemetry_json['data']: wmi_handler = WMIHandler(monkey_id, telemetry_json['data']['wmi'], users_secrets) wmi_handler.process_and_handle_wmi_info() if 'aws' in telemetry_json['data']: if 'instance_id' in telemetry_json['data']['aws']: mongo.db.monkey.update_one({'_id': monkey_id}, {'$set': {'aws_instance_id': telemetry_json['data']['aws']['instance_id']}})
def get_azure_creds(): """ Recover all credentials marked as being from an Azure machine :return: List of credentials. """ creds = [] for telem in mongo.db.telemetry.find( { "telem_category": "system_info", "data.Azure": { "$exists": True } }, { "data.Azure": 1, "monkey_guid": 1 }, ): azure_users = telem["data"]["Azure"]["usernames"] if len(azure_users) == 0: continue origin = NodeService.get_monkey_by_guid( telem["monkey_guid"])["hostname"] azure_leaked_users = [{ "username": user.replace(",", "."), "type": "Clear Password", "origin": origin } for user in azure_users] creds.extend(azure_leaked_users) logger.info("Azure machines creds generated for reporting") return creds
def patch(self, guid): monkey_json = json.loads(request.data) update = {"$set": {'modifytime': datetime.now()}} monkey = NodeService.get_monkey_by_guid(guid) if 'keepalive' in monkey_json: update['$set']['keepalive'] = dateutil.parser.parse( monkey_json['keepalive']) else: update['$set']['keepalive'] = datetime.now() if 'config' in monkey_json: update['$set']['config'] = monkey_json['config'] if 'config_error' in monkey_json: update['$set']['config_error'] = monkey_json['config_error'] if 'tunnel' in monkey_json: tunnel_host_ip = monkey_json['tunnel'].split(":")[-2].replace( "//", "") NodeService.set_monkey_tunnel(monkey["_id"], tunnel_host_ip) ttl = create_monkey_ttl_document( DEFAULT_MONKEY_TTL_EXPIRY_DURATION_IN_SECONDS) update['$set']['ttl_ref'] = ttl.id return mongo.db.monkey.update({"_id": monkey["_id"]}, update, upsert=False)
def process_tunnel_telemetry(telemetry_json): check_tunneling_violation(telemetry_json) monkey_id = NodeService.get_monkey_by_guid(telemetry_json["monkey_guid"])["_id"] if telemetry_json["data"]["proxy"] is not None: tunnel_host_ip = get_tunnel_host_ip_from_proxy_field(telemetry_json) NodeService.set_monkey_tunnel(monkey_id, tunnel_host_ip) else: NodeService.unset_all_monkey_tunnels(monkey_id)
def process_mimikatz_and_wmi_info(telemetry_json): users_secrets = {} if 'mimikatz' in telemetry_json['data']: users_secrets = mimikatz_utils.MimikatzSecrets. \ extract_secrets_from_mimikatz(telemetry_json['data'].get('mimikatz', '')) if 'wmi' in telemetry_json['data']: monkey_id = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid']).get('_id') wmi_handler = WMIHandler(monkey_id, telemetry_json['data']['wmi'], users_secrets) wmi_handler.process_and_handle_wmi_info()
def process_wmi_info(telemetry_json): users_secrets = {} if 'wmi' in telemetry_json['data']: monkey_id = NodeService.get_monkey_by_guid( telemetry_json['monkey_guid']).get('_id') wmi_handler = WMIHandler(monkey_id, telemetry_json['data']['wmi'], users_secrets) wmi_handler.process_and_handle_wmi_info()
def get_edge_by_scan_or_exploit_telemetry(telemetry_json): dst_ip = telemetry_json['data']['machine']['ip_addr'] dst_domain_name = telemetry_json['data']['machine']['domain_name'] src_monkey = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid']) dst_node = NodeService.get_monkey_by_ip(dst_ip) if dst_node is None: dst_node = NodeService.get_or_create_node(dst_ip, dst_domain_name) return EdgeService.get_or_create_edge(src_monkey["_id"], dst_node["_id"])
def get_displayed_telemetry(telem): monkey = NodeService.get_monkey_by_guid(telem['monkey_guid']) default_hostname = "GUID-" + telem['monkey_guid'] return \ { 'id': telem['_id'], 'timestamp': telem['timestamp'].strftime('%d/%m/%Y %H:%M:%S'), 'hostname': monkey.get('hostname', default_hostname) if monkey else default_hostname, 'brief': TELEM_PROCESS_DICT[telem['telem_type']](telem) }
def post(self): telemetry_json = json.loads(request.data) monkey_id = NodeService.get_monkey_by_guid( telemetry_json['monkey_guid'])['_id'] # This shouldn't contain any unicode characters. this'll take 2 time less space. log_data = str(telemetry_json['log']) log_id = LogService.add_log(monkey_id, log_data) return mongo.db.log.find_one_or_404({"_id": log_id})
def get_displayed_telemetry(telem): monkey = NodeService.get_monkey_by_guid(telem["monkey_guid"]) default_hostname = "GUID-" + telem["monkey_guid"] return { "id": telem["_id"], "timestamp": telem["timestamp"].strftime("%d/%m/%Y %H:%M:%S"), "hostname": monkey.get("hostname", default_hostname) if monkey else default_hostname, "brief": TelemetryFeed.get_telem_brief(telem), }
def _get_credentials_from_system_info_telems(): formatted_creds = [] for telem in mongo.db.telemetry.find( {"telem_category": "system_info", "data.credentials": {"$exists": True}}, {"data.credentials": 1, "monkey_guid": 1}, ): creds = telem["data"]["credentials"] origin = NodeService.get_monkey_by_guid(telem["monkey_guid"])["hostname"] formatted_creds.extend(ReportService._format_creds_for_reporting(telem, creds, origin)) return formatted_creds
def process_aws_data(telemetry_json): if 'aws' in telemetry_json['data']: if 'instance_id' in telemetry_json['data']['aws']: monkey_id = NodeService.get_monkey_by_guid( telemetry_json['monkey_guid']).get('_id') mongo.db.monkey.update_one({'_id': monkey_id}, { '$set': { 'aws_instance_id': telemetry_json['data']['aws']['instance_id'] } })
def get_edge_by_scan_or_exploit_telemetry(telemetry_json): dst_ip = telemetry_json["data"]["machine"]["ip_addr"] dst_domain_name = telemetry_json["data"]["machine"]["domain_name"] src_monkey = NodeService.get_monkey_by_guid(telemetry_json["monkey_guid"]) dst_node = NodeService.get_monkey_by_ip(dst_ip) if dst_node is None: dst_node = NodeService.get_or_create_node(dst_ip, dst_domain_name) src_label = NodeService.get_label_for_endpoint(src_monkey["_id"]) dst_label = NodeService.get_label_for_endpoint(dst_node["_id"]) return EdgeService.get_or_create_edge(src_monkey["_id"], dst_node["_id"], src_label, dst_label)
def process_state_telemetry(telemetry_json): monkey = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid']) NodeService.add_communication_info( monkey, telemetry_json['command_control_channel']) if telemetry_json['data']['done']: NodeService.set_monkey_dead(monkey, True) else: NodeService.set_monkey_dead(monkey, False) if telemetry_json['data']['done']: current_monkey = Monkey.get_single_monkey_by_guid( telemetry_json['monkey_guid']) test_passed_findings_for_unreached_segments(current_monkey)
def post(self): telemetry_json = json.loads(request.data) telemetry_json['data'] = json.loads(telemetry_json['data']) telemetry_json['timestamp'] = datetime.now() telemetry_json['command_control_channel'] = {'src': request.remote_addr, 'dst': request.host} # Monkey communicated, so it's alive. Update the TTL. Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid']).renew_ttl() monkey = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid']) NodeService.update_monkey_modify_time(monkey["_id"]) process_telemetry(telemetry_json) telem_id = mongo.db.telemetry.insert(telemetry_json) return mongo.db.telemetry.find_one_or_404({"_id": telem_id})
def process_state_telemetry(telemetry_json): monkey = NodeService.get_monkey_by_guid(telemetry_json["monkey_guid"]) NodeService.add_communication_info( monkey, telemetry_json["command_control_channel"]) if telemetry_json["data"]["done"]: NodeService.set_monkey_dead(monkey, True) else: NodeService.set_monkey_dead(monkey, False) if telemetry_json["data"]["done"]: current_monkey = Monkey.get_single_monkey_by_guid( telemetry_json["monkey_guid"]) check_passed_findings_for_unreached_segments(current_monkey) if telemetry_json["data"]["version"]: logger.info(f"monkey {telemetry_json['monkey_guid']} has version " f"{telemetry_json['data']['version']}")
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 post(self): telemetry_json = json.loads(request.data) telemetry_json['timestamp'] = datetime.now() monkey = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid']) try: NodeService.update_monkey_modify_time(monkey["_id"]) telem_type = telemetry_json.get('telem_type') if telem_type in TELEM_PROCESS_DICT: TELEM_PROCESS_DICT[telem_type](telemetry_json) else: logger.info('Got unknown type of telemetry: %s' % telem_type) except Exception as ex: logger.error("Exception caught while processing telemetry", exc_info=True) telem_id = mongo.db.telemetry.insert(telemetry_json) return mongo.db.telemetry.find_one_or_404({"_id": telem_id})
def _get_credentials_from_system_info_telems(): formatted_creds = [] for telem in mongo.db.telemetry.find( { 'telem_category': 'system_info', 'data.credentials': { '$exists': True } }, { 'data.credentials': 1, 'monkey_guid': 1 }): creds = telem['data']['credentials'] origin = NodeService.get_monkey_by_guid( telem['monkey_guid'])['hostname'] formatted_creds.extend( ReportService._format_creds_for_reporting( telem, creds, origin)) return formatted_creds
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(text_type(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 post(self): telemetry_json = json.loads(request.data) telemetry_json["data"] = json.loads(telemetry_json["data"]) telemetry_json["timestamp"] = datetime.now() telemetry_json["command_control_channel"] = { "src": request.remote_addr, "dst": request.host, } # Monkey communicated, so it's alive. Update the TTL. Monkey.get_single_monkey_by_guid( telemetry_json["monkey_guid"]).renew_ttl() monkey = NodeService.get_monkey_by_guid(telemetry_json["monkey_guid"]) NodeService.update_monkey_modify_time(monkey["_id"]) process_telemetry(telemetry_json) save_telemetry(telemetry_json) return {}, 201
def patch(self, guid): monkey_json = json.loads(request.data) update = {"$set": {'modifytime': datetime.now()}} monkey = NodeService.get_monkey_by_guid(guid) if 'keepalive' in monkey_json: update['$set']['keepalive'] = dateutil.parser.parse( monkey_json['keepalive']) else: update['$set']['keepalive'] = datetime.now() if 'config' in monkey_json: update['$set']['config'] = monkey_json['config'] if 'config_error' in monkey_json: update['$set']['config_error'] = monkey_json['config_error'] if 'tunnel' in monkey_json: tunnel_host_ip = monkey_json['tunnel'].split(":")[-2].replace( "//", "") NodeService.set_monkey_tunnel(monkey["_id"], tunnel_host_ip) return mongo.db.monkey.update({"_id": monkey["_id"]}, update, upsert=False)
def process_state_telemetry(telemetry_json): monkey = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid']) if telemetry_json['data']['done']: NodeService.set_monkey_dead(monkey, True) else: NodeService.set_monkey_dead(monkey, False)