From c208d0ebe80d32dff154637bf613faa95e95d0c3 Mon Sep 17 00:00:00 2001 From: "maor.rayzin" Date: Thu, 18 Oct 2018 17:10:14 +0300 Subject: [PATCH] re-arranged the code and cleaned up a bit --- .../monkey_island/cc/resources/telemetry.py | 154 ++---------------- .../monkey_island/cc/services/group_info.py | 4 - monkey/monkey_island/cc/services/user_info.py | 2 + .../cc/services/wmi_info_handler.py | 148 +++++++++++++++++ 4 files changed, 162 insertions(+), 146 deletions(-) delete mode 100644 monkey/monkey_island/cc/services/group_info.py create mode 100644 monkey/monkey_island/cc/services/wmi_info_handler.py diff --git a/monkey/monkey_island/cc/resources/telemetry.py b/monkey/monkey_island/cc/resources/telemetry.py index 644fd6984..efd5e2414 100644 --- a/monkey/monkey_island/cc/resources/telemetry.py +++ b/monkey/monkey_island/cc/resources/telemetry.py @@ -1,5 +1,4 @@ import json -import traceback import logging import copy from datetime import datetime @@ -10,11 +9,12 @@ from flask import request from cc.auth import jwt_required from cc.database import mongo -from cc.services import user_info, group_info +from cc.services import user_info from cc.services.config import ConfigService from cc.services.edge import EdgeService from cc.services.node import NodeService from cc.encryptor import encryptor +from cc.services.wmi_info_handler import WMIHandler __author__ = 'Barak' @@ -188,147 +188,17 @@ class Telemetry(flask_restful.Resource): if 'mimikatz' in telemetry_json['data']: users_secrets = user_info.extract_secrets_from_mimikatz(telemetry_json['data'].get('mimikatz', '')) if 'wmi' in telemetry_json['data']: - info_for_mongo = {} - users_info = telemetry_json['data']['wmi']['Win32_UserAccount'] - groups_info = telemetry_json['data']['wmi']['Win32_Group'] - group_user_dict = telemetry_json['data']['wmi']['Win32_GroupUser'] - Telemetry.add_groups_to_collection(groups_info, info_for_mongo, monkey_id) - Telemetry.add_users_to_collection(users_info, info_for_mongo, users_secrets, monkey_id) - Telemetry.create_group_user_connection(info_for_mongo, group_user_dict) - for entity in info_for_mongo.values(): - if entity['machine_id']: - # Handling for local entities. - mongo.db.groupsandusers.update({'SID': entity['SID'], - 'machine_id': entity['machine_id']}, entity, upsert=True) - else: - # Handlings for domain entities. - if not mongo.db.groupsandusers.find_one({'SID': entity['SID']}): - mongo.db.groupsandusers.insert_one(entity) - else: - # if entity is domain entity, add the monkey id of current machine to secrets_location. - # (found on this machine) - if entity.get('NTLM_secret'): - mongo.db.groupsandusers.update_one({'SID': entity['SID'], 'type': 1}, - {'$addToSet': {'secret_location': monkey_id}}) - - Telemetry.add_admin(info_for_mongo[group_info.ADMINISTRATORS_GROUP_KNOWN_SID], monkey_id) - Telemetry.update_admins_retrospective(info_for_mongo) - Telemetry.update_critical_services(telemetry_json['data']['wmi']['Win32_Service'], - telemetry_json['data']['wmi']['Win32_Product'], - monkey_id) - - @staticmethod - def update_critical_services(wmi_services, wmi_products, machine_id): - critical_names = ("W3svc", "MSExchangeServiceHost", "MSSQLServer", "dns", 'MSSQL$SQLEXPRESS', 'SQL') - - services_names_list = [str(i['Name'])[2:-1] for i in wmi_services] - products_names_list = [str(i['Name'])[2:-2] for i in wmi_products] - - for name in critical_names: - if name in services_names_list or name in products_names_list: - logger.info('found a critical service') - mongo.db.monkey.update({'_id': machine_id}, {'$addToSet': {'critical_services': name}}) - - @staticmethod - def update_admins_retrospective(info_for_mongo): - for profile in info_for_mongo: - groups_from_mongo = mongo.db.groupsandusers.find({'SID': {'$in': info_for_mongo[profile]['member_of']}}, - {'admin_on_machines': 1}) - for group in groups_from_mongo: - if group['admin_on_machines']: - mongo.db.groupsandusers.update_one({'SID': info_for_mongo[profile]['SID']}, - {'$addToSet': {'admin_on_machines': { - '$each': group['admin_on_machines']}}}) - - @staticmethod - def add_admin(group, machine_id): - for sid in group['entities_list']: - mongo.db.groupsandusers.update_one({'SID': sid}, - {'$addToSet': {'admin_on_machines': machine_id}}) - entity_details = mongo.db.groupsandusers.find_one({'SID': sid}, - {'type': 1, 'entities_list': 1}) - if entity_details.get('type') == 2: - Telemetry.add_admin(entity_details, machine_id) - - @staticmethod - def add_groups_to_collection(groups_info, info_for_mongo, monkey_id): - for group in groups_info: - if not group.get('LocalAccount'): - base_entity = Telemetry.build_entity_document(group) - else: - base_entity = Telemetry.build_entity_document(group, monkey_id) - base_entity['entities_list'] = [] - base_entity['type'] = 2 - info_for_mongo[base_entity.get('SID')] = base_entity - - @staticmethod - def add_users_to_collection(users_info, info_for_mongo, users_secrets, monkey_id): - for user in users_info: - if not user.get('LocalAccount'): - base_entity = Telemetry.build_entity_document(user) - else: - base_entity = Telemetry.build_entity_document(user, monkey_id) - base_entity['NTLM_secret'] = users_secrets.get(base_entity['name'], {}).get('ntlm') - base_entity['SAM_secret'] = users_secrets.get(base_entity['name'], {}).get('sam') - base_entity['secret_location'] = [] - - base_entity['type'] = 1 - info_for_mongo[base_entity.get('SID')] = base_entity - - @staticmethod - def build_entity_document(entity_info, monkey_id=None): - general_properties_dict = { - 'SID': str(entity_info['SID'])[4:-1], - 'name': str(entity_info['Name'])[2:-1], - 'machine_id': monkey_id, - 'member_of': [], - 'admin_on_machines': [] - } - - if monkey_id: - general_properties_dict['domain_name'] = None - else: - general_properties_dict['domain_name'] = str(entity_info['Domain'])[2:-1] - - return general_properties_dict - - @staticmethod - def create_group_user_connection(info_for_mongo, group_user_list): - for group_user_couple in group_user_list: - group_part = group_user_couple['GroupComponent'] - child_part = group_user_couple['PartComponent'] - group_sid = str(group_part['SID'])[4:-1] - groups_entities_list = info_for_mongo[group_sid]['entities_list'] - child_sid = '' - - if type(child_part) in (unicode, str): - child_part = str(child_part) - if "cimv2:Win32_UserAccount" in child_part: - # domain user - domain_name = child_part.split('cimv2:Win32_UserAccount.Domain="')[1].split('",Name="')[0] - name = child_part.split('cimv2:Win32_UserAccount.Domain="')[1].split('",Name="')[1][:-2] - - if "cimv2:Win32_Group" in child_part: - # domain group - domain_name = child_part.split('cimv2:Win32_Group.Domain="')[1].split('",Name="')[0] - name = child_part.split('cimv2:Win32_Group.Domain="')[1].split('",Name="')[1][:-2] - - for entity in info_for_mongo: - if info_for_mongo[entity]['name'] == name and info_for_mongo[entity]['domain'] == domain_name: - child_sid = info_for_mongo[entity]['SID'] - else: - child_sid = str(child_part['SID'])[4:-1] - - if child_sid and child_sid not in groups_entities_list: - groups_entities_list.append(child_sid) - - if child_sid: #and info_for_mongo.get(child_sid, {}).get('type') == 1: - if child_sid in info_for_mongo: - info_for_mongo[child_sid]['member_of'].append(group_sid) - - -################################################################ + wmi_handler = WMIHandler(monkey_id, telemetry_json['data']['wmi'], users_secrets) + wmi_handler.add_groups_to_collection() + wmi_handler.add_users_to_collection() + wmi_handler.create_group_user_connection() + wmi_handler.insert_info_to_mongo() + wmi_handler.add_admin(wmi_handler.info_for_mongo[wmi_handler.ADMINISTRATORS_GROUP_KNOWN_SID], monkey_id) + wmi_handler.update_admins_retrospective() + wmi_handler.update_critical_services(telemetry_json['data']['wmi']['Win32_Service'], + telemetry_json['data']['wmi']['Win32_Product'], + monkey_id) @staticmethod def add_ip_to_ssh_keys(ip, ssh_info): diff --git a/monkey/monkey_island/cc/services/group_info.py b/monkey/monkey_island/cc/services/group_info.py deleted file mode 100644 index 5b4008ef6..000000000 --- a/monkey/monkey_island/cc/services/group_info.py +++ /dev/null @@ -1,4 +0,0 @@ - - -ADMINISTRATORS_GROUP_KNOWN_SID = '1-5-32-544' - diff --git a/monkey/monkey_island/cc/services/user_info.py b/monkey/monkey_island/cc/services/user_info.py index 9bbdb38cb..c69a011a6 100644 --- a/monkey/monkey_island/cc/services/user_info.py +++ b/monkey/monkey_island/cc/services/user_info.py @@ -41,3 +41,5 @@ def extract_secrets_from_mimikatz(mim_string): extract_ntlm_secrets(mim_string, users_dict) return users_dict + + diff --git a/monkey/monkey_island/cc/services/wmi_info_handler.py b/monkey/monkey_island/cc/services/wmi_info_handler.py new file mode 100644 index 000000000..133f4742a --- /dev/null +++ b/monkey/monkey_island/cc/services/wmi_info_handler.py @@ -0,0 +1,148 @@ +from cc.database import mongo + + +class WMIHandler: + + ADMINISTRATORS_GROUP_KNOWN_SID = '1-5-32-544' + + def __init__(self, monkey_id, wmi_info, user_secrets): + + self.monkey_id = monkey_id + self.info_for_mongo = {} + self.users_secrets = user_secrets + self.users_info = wmi_info['Win32_UserAccount'] + self.groups_info = wmi_info['Win32_Group'] + self.groups_and_users = wmi_info['Win32_GroupUser'] + + def process_and_handle(self): + + self.add_groups_to_collection() + self.add_users_to_collection() + self.create_group_user_connection() + self.add_admin(self.info_for_mongo[self.ADMINISTRATORS_GROUP_KNOWN_SID], self.monkey_id) + self.update_admins_retrospective() + self.insert_info_to_mongo() + + def update_critical_services(self, wmi_services, wmi_products, machine_id): + critical_names = ("W3svc", "MSExchangeServiceHost", "MSSQLServer", "dns", 'MSSQL$SQLEXPRESS', 'SQL') + + services_names_list = [str(i['Name'])[2:-1] for i in wmi_services] + products_names_list = [str(i['Name'])[2:-2] for i in wmi_products] + + for name in critical_names: + if name in services_names_list or name in products_names_list: + mongo.db.monkey.update({'_id': machine_id}, {'$addToSet': {'critical_services': name}}) + + def build_entity_document(self, entity_info, monkey_id=None): + general_properties_dict = { + 'SID': str(entity_info['SID'])[4:-1], + 'name': str(entity_info['Name'])[2:-1], + 'machine_id': monkey_id, + 'member_of': [], + 'admin_on_machines': [] + } + + if monkey_id: + general_properties_dict['domain_name'] = None + else: + general_properties_dict['domain_name'] = str(entity_info['Domain'])[2:-1] + + return general_properties_dict + + def add_users_to_collection(self): + for user in self.users_info: + if not user.get('LocalAccount'): + base_entity = self.build_entity_document(user) + else: + base_entity = self.build_entity_document(user, self.monkey_id) + base_entity['NTLM_secret'] = self.users_secrets.get(base_entity['name'], {}).get('ntlm') + base_entity['SAM_secret'] = self.users_secrets.get(base_entity['name'], {}).get('sam') + base_entity['secret_location'] = [] + + base_entity['type'] = 1 + self.info_for_mongo[base_entity.get('SID')] = base_entity + + def add_groups_to_collection(self): + for group in self.groups_info: + if not group.get('LocalAccount'): + base_entity = self.build_entity_document(group) + else: + base_entity = self.build_entity_document(group, self.monkey_id) + base_entity['entities_list'] = [] + base_entity['type'] = 2 + self.info_for_mongo[base_entity.get('SID')] = base_entity + + def create_group_user_connection(self): + for group_user_couple in self.groups_and_users: + group_part = group_user_couple['GroupComponent'] + child_part = group_user_couple['PartComponent'] + group_sid = str(group_part['SID'])[4:-1] + groups_entities_list = self.info_for_mongo[group_sid]['entities_list'] + child_sid = '' + + if type(child_part) in (unicode, str): + child_part = str(child_part) + name = None + domain_name = None + if "cimv2:Win32_UserAccount" in child_part: + # domain user + domain_name = child_part.split('cimv2:Win32_UserAccount.Domain="')[1].split('",Name="')[0] + name = child_part.split('cimv2:Win32_UserAccount.Domain="')[1].split('",Name="')[1][:-2] + + if "cimv2:Win32_Group" in child_part: + # domain group + domain_name = child_part.split('cimv2:Win32_Group.Domain="')[1].split('",Name="')[0] + name = child_part.split('cimv2:Win32_Group.Domain="')[1].split('",Name="')[1][:-2] + + for entity in self.info_for_mongo: + if self.info_for_mongo[entity]['name'] == name and \ + self.info_for_mongo[entity]['domain'] == domain_name: + child_sid = self.info_for_mongo[entity]['SID'] + else: + child_sid = str(child_part['SID'])[4:-1] + + if child_sid and child_sid not in groups_entities_list: + groups_entities_list.append(child_sid) + + if child_sid: + if child_sid in self.info_for_mongo: + self.info_for_mongo[child_sid]['member_of'].append(group_sid) + + def insert_info_to_mongo(self): + for entity in self.info_for_mongo.values(): + if entity['machine_id']: + # Handling for local entities. + mongo.db.groupsandusers.update({'SID': entity['SID'], + 'machine_id': entity['machine_id']}, entity, upsert=True) + else: + # Handlings for domain entities. + if not mongo.db.groupsandusers.find_one({'SID': entity['SID']}): + mongo.db.groupsandusers.insert_one(entity) + else: + # if entity is domain entity, add the monkey id of current machine to secrets_location. + # (found on this machine) + if entity.get('NTLM_secret'): + mongo.db.groupsandusers.update_one({'SID': entity['SID'], 'type': 1}, + {'$addToSet': {'secret_location': self.monkey_id}}) + + def update_admins_retrospective(self): + for profile in self.info_for_mongo: + groups_from_mongo = mongo.db.groupsandusers.find({ + 'SID': {'$in': self.info_for_mongo[profile]['member_of']}}, + {'admin_on_machines': 1}) + + for group in groups_from_mongo: + if group['admin_on_machines']: + mongo.db.groupsandusers.update_one({'SID': self.info_for_mongo[profile]['SID']}, + {'$addToSet': {'admin_on_machines': { + '$each': group['admin_on_machines']}}}) + + def add_admin(self, group, machine_id): + for sid in group['entities_list']: + mongo.db.groupsandusers.update_one({'SID': sid}, + {'$addToSet': {'admin_on_machines': machine_id}}) + entity_details = mongo.db.groupsandusers.find_one({'SID': sid}, + {'type': 1, 'entities_list': 1}) + if entity_details.get('type') == 2: + self.add_admin(entity_details, machine_id) +