From 707c88434cb937fb8e6b20168cd3e3582ff41252 Mon Sep 17 00:00:00 2001 From: "maor.rayzin" Date: Wed, 31 Oct 2018 14:20:56 +0200 Subject: [PATCH] RE-CR: changed names, added doc, created consts. --- .../monkey_island/cc/resources/telemetry.py | 6 ++-- .../cc/services/groups_and_users_consts.py | 5 +++ .../{user_info.py => mimikatz_utils.py} | 0 .../monkey_island/cc/services/pth_report.py | 35 ++++++++++++++++--- monkey/monkey_island/cc/services/report.py | 2 +- .../{wmi_info_handler.py => wmi_handler.py} | 9 ++--- 6 files changed, 45 insertions(+), 12 deletions(-) create mode 100644 monkey/monkey_island/cc/services/groups_and_users_consts.py rename monkey/monkey_island/cc/services/{user_info.py => mimikatz_utils.py} (100%) rename monkey/monkey_island/cc/services/{wmi_info_handler.py => wmi_handler.py} (96%) diff --git a/monkey/monkey_island/cc/resources/telemetry.py b/monkey/monkey_island/cc/resources/telemetry.py index 1680f7664..0db3b0eb4 100644 --- a/monkey/monkey_island/cc/resources/telemetry.py +++ b/monkey/monkey_island/cc/resources/telemetry.py @@ -9,12 +9,12 @@ from flask import request from cc.auth import jwt_required from cc.database import mongo -from cc.services import user_info +from cc.services import mimikatz_utils 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 +from cc.services.wmi_handler import WMIHandler __author__ = 'Barak' @@ -186,7 +186,7 @@ class Telemetry(flask_restful.Resource): Telemetry.add_system_info_creds_to_config(creds) Telemetry.replace_user_dot_with_comma(creds) if 'mimikatz' in telemetry_json['data']: - users_secrets = user_info.MimikatzSecrets.\ + 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) diff --git a/monkey/monkey_island/cc/services/groups_and_users_consts.py b/monkey/monkey_island/cc/services/groups_and_users_consts.py new file mode 100644 index 000000000..03fefbe02 --- /dev/null +++ b/monkey/monkey_island/cc/services/groups_and_users_consts.py @@ -0,0 +1,5 @@ +"""This file will include consts values regarding the groupsandusers collection""" + +USERTYPE = 1 + +GROUPTYPE = 2 \ No newline at end of file diff --git a/monkey/monkey_island/cc/services/user_info.py b/monkey/monkey_island/cc/services/mimikatz_utils.py similarity index 100% rename from monkey/monkey_island/cc/services/user_info.py rename to monkey/monkey_island/cc/services/mimikatz_utils.py diff --git a/monkey/monkey_island/cc/services/pth_report.py b/monkey/monkey_island/cc/services/pth_report.py index edb882561..f72a430ba 100644 --- a/monkey/monkey_island/cc/services/pth_report.py +++ b/monkey/monkey_island/cc/services/pth_report.py @@ -3,6 +3,7 @@ from itertools import product from cc.database import mongo from bson import ObjectId +from cc.services.groups_and_users_consts import USERTYPE from cc.services.node import NodeService __author__ = 'maor.rayzin' @@ -11,6 +12,19 @@ class PTHReportService(object): @staticmethod def __dup_passwords_mongoquery(): + """ + This function build and query the mongoDB for users found that are using the same passwords, this is done + by comparing the NTLM hash found for each user by mimikatz. + :return: + A list of mongo documents (dicts in python) that look like this: + { + '_id': The NTLM hash, + 'count': How many users share it. + 'Docs': the name, domain name, _Id, and machine_id of the users + } + """ + + pipeline = [ {"$match": { 'NTLM_secret': { @@ -30,18 +44,31 @@ class PTHReportService(object): @staticmethod def __get_admin_on_machines_format(admin_on_machines, domain_name): - + """ + This function finds for each admin user, what machines its admin of and compile them to a list. + :param admin_on_machines: A list of "monkey" documents "_id"s + :param domain_name: The admin's domain name + :return: + A list of formatted machines names *domain*\*hostname*, to use in shared admins issues. + """ machines = mongo.db.monkey.find({'_id': {'$in': admin_on_machines}}, {'hostname': 1}) return [domain_name + '\\' + i['hostname'] for i in list(machines)] @staticmethod def __strong_users_on_crit_query(): + """ + This function build and query the mongoDB for users that mimikatz was able to find cached NTLM hash and + are administrators on machines with services predefined as important services thus making these machines + critical. + :return: + A list of said users + """ pipeline = [ { '$unwind': '$admin_on_machines' }, { - '$match': {'type': 1, 'domain_name': {'$ne': None}} + '$match': {'type': USERTYPE, 'domain_name': {'$ne': None}} }, { '$lookup': @@ -100,7 +127,7 @@ class PTHReportService(object): # object has at least two objects in it, by making sure any value exists in the array index 1. # Excluding the name Administrator - its spamming the lists and not a surprise the domain Administrator account # is shared. - admins = mongo.db.groupsandusers.find({'type': 1, 'name': {'$ne': 'Administrator'}, + admins = mongo.db.groupsandusers.find({'type': USERTYPE, 'name': {'$ne': 'Administrator'}, 'admin_on_machines.1': {'$exists': True}}, {'admin_on_machines': 1, 'name': 1, 'domain_name': 1}) return [ @@ -200,7 +227,7 @@ class PTHReportService(object): { 'admin_on_machines': {'$ne': []}, 'secret_location': {'$ne': []}, - 'type': 1 + 'type': USERTYPE }, { 'admin_on_machines': 1, 'secret_location': 1 diff --git a/monkey/monkey_island/cc/services/report.py b/monkey/monkey_island/cc/services/report.py index 216882fa7..fe8928a56 100644 --- a/monkey/monkey_island/cc/services/report.py +++ b/monkey/monkey_island/cc/services/report.py @@ -561,7 +561,7 @@ class ReportService: issues_dict = {} for issue in issues: if issue.get('is_local', True): - machine = issue.get('machine', '').upper() + machine = issue.get('machine').upper() if machine not in issues_dict: issues_dict[machine] = [] issues_dict[machine].append(issue) diff --git a/monkey/monkey_island/cc/services/wmi_info_handler.py b/monkey/monkey_island/cc/services/wmi_handler.py similarity index 96% rename from monkey/monkey_island/cc/services/wmi_info_handler.py rename to monkey/monkey_island/cc/services/wmi_handler.py index d119772f5..a0ffcba8b 100644 --- a/monkey/monkey_island/cc/services/wmi_info_handler.py +++ b/monkey/monkey_island/cc/services/wmi_handler.py @@ -1,4 +1,5 @@ from cc.database import mongo +from cc.services.groups_and_users_consts import USERTYPE __author__ = 'maor.rayzin' @@ -15,8 +16,8 @@ class WMIHandler(object): self.users_info = wmi_info['Win32_UserAccount'] self.groups_info = wmi_info['Win32_Group'] self.groups_and_users = wmi_info['Win32_GroupUser'] - self.products = wmi_info['Win32_Service'] - self.services = wmi_info['Win32_Product'] + self.services = wmi_info['Win32_Service'] + self.products = wmi_info['Win32_Product'] def process_and_handle_wmi_info(self): @@ -128,7 +129,7 @@ class WMIHandler(object): # 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}, + mongo.db.groupsandusers.update_one({'SID': entity['SID'], 'type': USERTYPE}, {'$addToSet': {'secret_location': self.monkey_id}}) def update_admins_retrospective(self): @@ -148,7 +149,7 @@ class WMIHandler(object): 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}) + {'type': USERTYPE, 'entities_list': 1}) if entity_details.get('type') == 2: self.add_admin(entity_details, machine_id)