forked from p15670423/monkey
WIP pth feature
This commit is contained in:
parent
81694862b6
commit
9a05d0e87d
|
@ -13,7 +13,4 @@ PyInstaller
|
|||
six
|
||||
ecdsa
|
||||
netifaces
|
||||
nos
|
||||
ipaddress
|
||||
wmi
|
||||
|
||||
ipaddress
|
|
@ -130,6 +130,7 @@ class WindowsInfoCollector(InfoCollector):
|
|||
super(WindowsInfoCollector, self).__init__()
|
||||
self._config = infection_monkey.config.WormConfiguration
|
||||
self.info['reg'] = {}
|
||||
self.info['wmi'] = {}
|
||||
|
||||
def get_info(self):
|
||||
"""
|
||||
|
@ -164,7 +165,7 @@ class WindowsInfoCollector(InfoCollector):
|
|||
|
||||
def get_wmi_info(self):
|
||||
for wmi_class_name in WMI_CLASSES:
|
||||
self.info[wmi_class_name] = self.get_wmi_class(wmi_class_name)
|
||||
self.info['wmi'][wmi_class_name] = self.get_wmi_class(wmi_class_name)
|
||||
|
||||
def get_wmi_class(self, class_name, moniker="//./root/cimv2", properties=None):
|
||||
_wmi = wmi.WMI(moniker=moniker)
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
"info_file_handler": {
|
||||
"class": "logging.handlers.RotatingFileHandler",
|
||||
"level": "INFO",
|
||||
"level": "DEBUG",
|
||||
"formatter": "simple",
|
||||
"filename": "info.log",
|
||||
"maxBytes": 10485760,
|
||||
|
@ -27,7 +27,7 @@
|
|||
},
|
||||
|
||||
"root": {
|
||||
"level": "INFO",
|
||||
"level": "DEBUG",
|
||||
"handlers": ["console", "info_file_handler"]
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ 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.config import ConfigService
|
||||
from cc.services.edge import EdgeService
|
||||
from cc.services.node import NodeService
|
||||
|
@ -170,6 +171,8 @@ class Telemetry(flask_restful.Resource):
|
|||
|
||||
@staticmethod
|
||||
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)
|
||||
|
@ -182,6 +185,142 @@ class Telemetry(flask_restful.Resource):
|
|||
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 = 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']:
|
||||
mongo.db.groupsandusers.update({'SID': entity['SID'],
|
||||
'machine_id': entity['machine_id']}, entity, upsert=True)
|
||||
else:
|
||||
if not mongo.db.groupsandusers.find_one({'SID': entity['SID']}):
|
||||
mongo.db.groupsandusers.insert_one(entity)
|
||||
|
||||
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)
|
||||
|
||||
|
||||
################################################################
|
||||
|
||||
|
||||
@staticmethod
|
||||
def add_ip_to_ssh_keys(ip, ssh_info):
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
|
||||
ADMINISTRATORS_GROUP_KNOWN_SID = '1-5-32-544'
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
from cc.services.pth_report_utils import PassTheHashReport, Machine
|
||||
from cc.database import mongo
|
||||
from bson import ObjectId
|
||||
|
||||
|
||||
class PTHReportService(object):
|
||||
|
@ -10,29 +12,95 @@ class PTHReportService(object):
|
|||
def __init__(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def get_duplicated_password_nodes(pth):
|
||||
"""
|
||||
|
||||
"""
|
||||
|
||||
usernames_lists = []
|
||||
usernames_per_sid_list = []
|
||||
dups = dict(map(lambda x: (x, len(pth.GetSidsBySecret(x))), pth.GetAllSecrets()))
|
||||
|
||||
for secret, count in sorted(dups.iteritems(), key=lambda (k, v): (v, k), reverse=True):
|
||||
if count <= 1:
|
||||
continue
|
||||
for sid in pth.GetSidsBySecret(secret):
|
||||
if sid:
|
||||
usernames_per_sid_list.append(pth.GetUsernameBySid(sid))
|
||||
|
||||
usernames_lists.append({'cred_group': usernames_per_sid_list})
|
||||
|
||||
return usernames_lists
|
||||
|
||||
@staticmethod
|
||||
def get_shared_local_admins_nodes(pth):
|
||||
def get_duplicated_passwords_nodes():
|
||||
users_cred_groups = []
|
||||
|
||||
pipeline = [
|
||||
{"$match": {
|
||||
'NTLM_secret': {
|
||||
"$exists": "true", "$ne": None}
|
||||
}},
|
||||
{
|
||||
"$group": {
|
||||
"_id": {
|
||||
"NTLM_secret": "$NTLM_secret"},
|
||||
"count": {"$sum": 1},
|
||||
"Docs": {"$push": {'_id': "$_id", 'name': '$name', 'domain_name': '$domain_name',
|
||||
'machine_id': '$machine_id'}}
|
||||
}},
|
||||
{'$match': {'count': {'$gt': 1}}}
|
||||
]
|
||||
docs = mongo.db.groupsandusers.aggregate(pipeline)
|
||||
for doc in docs:
|
||||
users_list = []
|
||||
for user in doc['Docs']:
|
||||
hostname = None
|
||||
if user['machine_id']:
|
||||
machine = mongo.db.monkey.find_one({'_id': ObjectId(user['machine_id'])}, {'hostname': 1})
|
||||
if machine.get('hostname'):
|
||||
hostname = machine['hostname']
|
||||
users_list.append({'username': user['name'], 'domain_name': user['domain_name'],
|
||||
'hostname': hostname})
|
||||
users_cred_groups.append({'cred_groups': users_list})
|
||||
|
||||
return users_cred_groups
|
||||
|
||||
@staticmethod
|
||||
def get_duplicated_passwords_issues():
|
||||
# TODO: Fix bug if both local and non local account share the same password
|
||||
user_groups = PTHReportService.get_duplicated_passwords_nodes()
|
||||
issues = []
|
||||
users_gathered = []
|
||||
for group in user_groups:
|
||||
for user_info in group['cred_groups']:
|
||||
users_gathered.append(user_info['username'])
|
||||
issues.append(
|
||||
{
|
||||
'type': 'shared_passwords_domain' if user_info['domain_name'] else 'shared_passwords',
|
||||
'machine': user_info['hostname'] if user_info['hostname'] else user_info['domain_name'],
|
||||
'shared_with': [i['username'] for i in group['cred_groups']],
|
||||
'is_local': False if user_info['domain_name'] else True
|
||||
}
|
||||
)
|
||||
break
|
||||
return issues
|
||||
|
||||
@staticmethod
|
||||
def get_shared_admins_nodes():
|
||||
admins = mongo.db.groupsandusers.find({'type': 1, 'admin_on_machines.1': {'$exists': True}},
|
||||
{'admin_on_machines': 1, 'name': 1, 'domain_name': 1})
|
||||
admins_info_list = []
|
||||
for admin in admins:
|
||||
machines = mongo.db.monkey.find({'_id': {'$in': admin['admin_on_machines']}}, {'hostname': 1})
|
||||
|
||||
# appends the host names of the machines this user is admin on.
|
||||
admins_info_list.append({'name': admin['name'],'domain_name': admin['domain_name'],
|
||||
'admin_on_machines': [i['hostname'] for i in list(machines)]})
|
||||
|
||||
return admins_info_list
|
||||
|
||||
@staticmethod
|
||||
def get_shared_admins_issues():
|
||||
admins_info = PTHReportService.get_shared_admins_nodes()
|
||||
issues = []
|
||||
for admin in admins_info:
|
||||
issues.append(
|
||||
{
|
||||
'is_local': False,
|
||||
'type': 'shared_admins_domain',
|
||||
'machine': admin['domain_name'],
|
||||
'username': admin['name'],
|
||||
'shared_machines': admin['admin_on_machines'],
|
||||
}
|
||||
)
|
||||
|
||||
return issues
|
||||
|
||||
|
||||
@staticmethod
|
||||
def old_get_shared_local_admins_nodes(pth):
|
||||
dups = dict(map(lambda x: (x, len(pth.GetSharedAdmins(x))), pth.machines))
|
||||
shared_admin_machines = []
|
||||
for m, count in sorted(dups.iteritems(), key=lambda (k, v): (v, k), reverse=True):
|
||||
|
@ -135,41 +203,8 @@ class PTHReportService(object):
|
|||
strong_users_non_crit_list.append(machine)
|
||||
return strong_users_non_crit_list
|
||||
|
||||
@staticmethod
|
||||
def get_duplicated_passwords_issues(pth, password_groups):
|
||||
issues = []
|
||||
previeous_group = []
|
||||
for group in password_groups:
|
||||
username = group['cred_group'][0]
|
||||
if username in previeous_group:
|
||||
continue
|
||||
sid = list(pth.GetSidsByUsername(username.split('\\')[1]))
|
||||
machine_info = pth.GetSidInfo(sid[0])
|
||||
issues.append(
|
||||
{
|
||||
'type': 'shared_passwords',
|
||||
'machine': machine_info.get('hostname').split('.')[0],
|
||||
'shared_with': group['cred_group']
|
||||
}
|
||||
)
|
||||
previeous_group += group['cred_group']
|
||||
|
||||
return issues
|
||||
|
||||
@staticmethod
|
||||
def get_shared_local_admins_issues(shared_admins_machines):
|
||||
issues = []
|
||||
for machine in shared_admins_machines:
|
||||
issues.append(
|
||||
{
|
||||
'type': 'shared_admins',
|
||||
'machine': machine.get('hostname'),
|
||||
'shared_accounts': machine.get('admins_accounts'),
|
||||
'ip': machine.get('ip'),
|
||||
}
|
||||
)
|
||||
|
||||
return issues
|
||||
|
||||
@staticmethod
|
||||
def strong_users_on_crit_issues(strong_users):
|
||||
|
@ -227,25 +262,23 @@ class PTHReportService(object):
|
|||
@staticmethod
|
||||
def get_report():
|
||||
|
||||
|
||||
|
||||
issues = []
|
||||
pth = PassTheHashReport()
|
||||
|
||||
same_password = PTHReportService.get_duplicated_password_nodes(pth)
|
||||
local_admin_shared = PTHReportService.get_shared_local_admins_nodes(pth)
|
||||
strong_users_on_crit_services = PTHReportService.get_strong_users_on_crit_services_by_user(pth)
|
||||
strong_users_on_non_crit_services = PTHReportService.get_strong_users_on_non_crit_services(pth)
|
||||
|
||||
issues += PTHReportService.get_duplicated_passwords_issues(pth, same_password)
|
||||
issues += PTHReportService.get_shared_local_admins_issues(local_admin_shared)
|
||||
issues += PTHReportService.strong_users_on_crit_issues(
|
||||
PTHReportService.get_strong_users_on_crit_services_by_machine(pth))
|
||||
issues += PTHReportService.get_duplicated_passwords_issues()
|
||||
# issues += PTHReportService.get_shared_local_admins_issues(local_admin_shared)
|
||||
# issues += PTHReportService.strong_users_on_crit_issues(
|
||||
# PTHReportService.get_strong_users_on_crit_services_by_machine(pth))
|
||||
|
||||
report = \
|
||||
{
|
||||
'report_info':
|
||||
{
|
||||
'same_password': same_password,
|
||||
'local_admin_shared': local_admin_shared,
|
||||
'strong_users_on_crit_services': strong_users_on_crit_services,
|
||||
'strong_users_on_non_crit_services': strong_users_on_non_crit_services,
|
||||
'pth_issues': issues
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import itertools
|
||||
import functools
|
||||
import pprint
|
||||
|
||||
import ipaddress
|
||||
import logging
|
||||
|
@ -184,6 +185,10 @@ class ReportService:
|
|||
logger.info('Stolen creds generated for reporting')
|
||||
return creds
|
||||
|
||||
@staticmethod
|
||||
def get_pth_shared_passwords():
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def get_ssh_keys():
|
||||
"""
|
||||
|
@ -531,23 +536,42 @@ class ReportService:
|
|||
|
||||
return cross_segment_issues
|
||||
|
||||
@staticmethod
|
||||
def get_domain_issues():
|
||||
|
||||
ISSUE_GENERATORS = [
|
||||
PTHReportService.get_duplicated_passwords_issues,
|
||||
PTHReportService.get_shared_admins_issues,
|
||||
]
|
||||
issues = functools.reduce(lambda acc, issue_gen: acc + issue_gen(), ISSUE_GENERATORS, [])
|
||||
domain_issues_dict = {}
|
||||
for issue in issues:
|
||||
if not issue.get('is_local', True):
|
||||
machine = issue.get('machine', '').upper()
|
||||
if machine not in domain_issues_dict:
|
||||
domain_issues_dict[machine] = []
|
||||
domain_issues_dict[machine].append(issue)
|
||||
logger.info('Domain issues generated for reporting')
|
||||
return domain_issues_dict
|
||||
|
||||
@staticmethod
|
||||
def get_issues():
|
||||
ISSUE_GENERATORS = [
|
||||
ReportService.get_exploits,
|
||||
ReportService.get_tunnels,
|
||||
ReportService.get_island_cross_segment_issues,
|
||||
ReportService.get_azure_issues
|
||||
ReportService.get_azure_issues,
|
||||
PTHReportService.get_duplicated_passwords_issues
|
||||
]
|
||||
#TODO: PTHReportService.get_report().get('report_info').get('pth_issues', [])
|
||||
issues = functools.reduce(lambda acc, issue_gen: acc + issue_gen(), ISSUE_GENERATORS, [])
|
||||
|
||||
issues_dict = {}
|
||||
for issue in issues:
|
||||
machine = issue.get('machine', '').upper()
|
||||
if machine not in issues_dict:
|
||||
issues_dict[machine] = []
|
||||
issues_dict[machine].append(issue)
|
||||
if issue.get('is_local', True):
|
||||
machine = issue.get('machine', '').upper()
|
||||
if machine not in issues_dict:
|
||||
issues_dict[machine] = []
|
||||
issues_dict[machine].append(issue)
|
||||
logger.info('Issues generated for reporting')
|
||||
return issues_dict
|
||||
|
||||
|
@ -657,13 +681,13 @@ class ReportService:
|
|||
|
||||
@staticmethod
|
||||
def get_report():
|
||||
domain_issues = ReportService.get_domain_issues()
|
||||
issues = ReportService.get_issues()
|
||||
config_users = ReportService.get_config_users()
|
||||
config_passwords = ReportService.get_config_passwords()
|
||||
cross_segment_issues = ReportService.get_cross_segment_issues()
|
||||
pth_report = PTHReportService.get_report()
|
||||
|
||||
|
||||
report = \
|
||||
{
|
||||
'overview':
|
||||
|
@ -690,7 +714,8 @@ class ReportService:
|
|||
},
|
||||
'recommendations':
|
||||
{
|
||||
'issues': issues
|
||||
'issues': issues,
|
||||
'domain_issues': domain_issues
|
||||
},
|
||||
'pth':
|
||||
{
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
|
||||
|
||||
def extract_sam_secrets(mim_string, users_dict):
|
||||
users_secrets = mim_string.split("\n42.")[1].split("\nSAMKey :")[1].split("\n\n")[1:]
|
||||
|
||||
if mim_string.count("\n42.") != 2:
|
||||
return {}
|
||||
|
||||
for sam_user_txt in users_secrets:
|
||||
sam_user = dict([map(unicode.strip, line.split(":")) for line in
|
||||
filter(lambda l: l.count(":") == 1, sam_user_txt.splitlines())])
|
||||
username = sam_user.get("User")
|
||||
users_dict[username] = {}
|
||||
|
||||
ntlm = sam_user.get("NTLM")
|
||||
if "[hashed secret]" not in ntlm:
|
||||
continue
|
||||
|
||||
users_dict[username]['SAM'] = ntlm.replace("[hashed secret]", "").strip()
|
||||
|
||||
|
||||
def extract_ntlm_secrets(mim_string, users_dict):
|
||||
|
||||
if mim_string.count("\n42.") != 2:
|
||||
return {}
|
||||
|
||||
ntds_users = mim_string.split("\n42.")[2].split("\nRID :")[1:]
|
||||
|
||||
for ntds_user_txt in ntds_users:
|
||||
user = ntds_user_txt.split("User :")[1].splitlines()[0].replace("User :", "").strip()
|
||||
ntlm = ntds_user_txt.split("* Primary\n NTLM :")[1].splitlines()[0].replace("NTLM :", "").strip()
|
||||
ntlm = ntlm.replace("[hashed secret]", "").strip()
|
||||
users_dict[user] = {}
|
||||
if ntlm:
|
||||
users_dict[user]['ntlm'] = ntlm
|
||||
|
||||
|
||||
def extract_secrets_from_mimikatz(mim_string):
|
||||
users_dict = {}
|
||||
extract_sam_secrets(mim_string, users_dict)
|
||||
extract_ntlm_secrets(mim_string, users_dict)
|
||||
|
||||
return users_dict
|
File diff suppressed because it is too large
Load Diff
|
@ -31,14 +31,14 @@
|
|||
"babel-preset-react": "^6.24.1",
|
||||
"babel-preset-stage-0": "^6.5.0",
|
||||
"bower-webpack-plugin": "^0.1.9",
|
||||
"chai": "^4.1.2",
|
||||
"copyfiles": "^2.0.0",
|
||||
"chai": "^4.2.0",
|
||||
"copyfiles": "^2.1.0",
|
||||
"css-loader": "^1.0.0",
|
||||
"eslint": "^5.3.0",
|
||||
"eslint-loader": "^2.1.0",
|
||||
"eslint": "^5.6.1",
|
||||
"eslint-loader": "^2.1.1",
|
||||
"eslint-plugin-react": "^7.11.1",
|
||||
"file-loader": "^1.1.11",
|
||||
"glob": "^7.0.0",
|
||||
"glob": "^7.1.3",
|
||||
"html-loader": "^0.5.5",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"karma": "^3.0.0",
|
||||
|
@ -48,44 +48,44 @@
|
|||
"karma-mocha-reporter": "^2.2.5",
|
||||
"karma-phantomjs-launcher": "^1.0.0",
|
||||
"karma-sourcemap-loader": "^0.3.5",
|
||||
"karma-webpack": "^3.0.0",
|
||||
"karma-webpack": "^3.0.5",
|
||||
"minimist": "^1.2.0",
|
||||
"mocha": "^5.2.0",
|
||||
"null-loader": "^0.1.1",
|
||||
"open": "0.0.5",
|
||||
"phantomjs-prebuilt": "^2.1.16",
|
||||
"react-addons-test-utils": "^15.6.2",
|
||||
"react-hot-loader": "^4.3.4",
|
||||
"react-hot-loader": "^4.3.11",
|
||||
"rimraf": "^2.6.2",
|
||||
"style-loader": "^0.22.1",
|
||||
"url-loader": "^1.1.0",
|
||||
"webpack": "^4.16.5",
|
||||
"webpack-cli": "^3.1.0",
|
||||
"webpack-dev-server": "^3.1.5"
|
||||
"url-loader": "^1.1.2",
|
||||
"webpack": "^4.20.2",
|
||||
"webpack-cli": "^3.1.2",
|
||||
"webpack-dev-server": "^3.1.9"
|
||||
},
|
||||
"dependencies": {
|
||||
"bootstrap": "3.3.7",
|
||||
"core-js": "^2.5.7",
|
||||
"downloadjs": "^1.4.7",
|
||||
"fetch": "^1.1.0",
|
||||
"js-file-download": "^0.4.1",
|
||||
"js-file-download": "^0.4.4",
|
||||
"json-loader": "^0.5.7",
|
||||
"jwt-decode": "^2.2.0",
|
||||
"moment": "^2.22.2",
|
||||
"normalize.css": "^8.0.0",
|
||||
"npm": "^6.3.0",
|
||||
"npm": "^6.4.1",
|
||||
"prop-types": "^15.6.2",
|
||||
"rc-progress": "^2.2.5",
|
||||
"react": "^16.4.2",
|
||||
"react-bootstrap": "^0.32.1",
|
||||
"rc-progress": "^2.2.6",
|
||||
"react": "^16.5.2",
|
||||
"react-bootstrap": "^0.32.4",
|
||||
"react-copy-to-clipboard": "^5.0.1",
|
||||
"react-data-components": "^1.2.0",
|
||||
"react-dimensions": "^1.3.0",
|
||||
"react-dom": "^16.4.2",
|
||||
"react-dom": "^16.5.2",
|
||||
"react-fa": "^5.0.0",
|
||||
"react-graph-vis": "^1.0.2",
|
||||
"react-json-tree": "^0.11.0",
|
||||
"react-jsonschema-form": "^1.0.4",
|
||||
"react-jsonschema-form": "^1.0.5",
|
||||
"react-redux": "^5.0.7",
|
||||
"react-router-dom": "^4.3.1",
|
||||
"react-table": "^6.8.6",
|
||||
|
|
|
@ -39,7 +39,8 @@ class ReportPageComponent extends AuthComponent {
|
|||
CROSS_SEGMENT: 0,
|
||||
TUNNEL: 1,
|
||||
SHARED_LOCAL_ADMIN: 2,
|
||||
SHARED_PASSWORDS: 3
|
||||
SHARED_PASSWORDS: 3,
|
||||
SHARED_PASSWORDS_DOMAIN: 4
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
|
@ -375,6 +376,8 @@ class ReportPageComponent extends AuthComponent {
|
|||
<li>Weak segmentation - Machines were able to communicate over unused ports.</li> : null}
|
||||
{this.state.report.overview.warnings[this.Warning.SHARED_LOCAL_ADMIN] ?
|
||||
<li>The monkey has found that some users have administrative rights on several machines.</li> : null}
|
||||
{this.state.report.overview.warnings[this.Warning.SHARED_PASSWORDS_DOMAIN] ?
|
||||
<li>The monkey has found that some users are sharing passwords on domain accounts.</li> : null}
|
||||
{this.state.report.overview.warnings[this.Warning.SHARED_PASSWORDS] ?
|
||||
<li>The monkey has found that some users are sharing passwords.</li> : null}
|
||||
</ul>
|
||||
|
@ -413,6 +416,12 @@ class ReportPageComponent extends AuthComponent {
|
|||
<div>
|
||||
{this.generateIssues(this.state.report.recommendations.issues)}
|
||||
</div>
|
||||
<h3>
|
||||
Domain related recommendations
|
||||
</h3>
|
||||
<div>
|
||||
{this.generateIssues(this.state.report.recommendations.domain_issues)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -465,9 +474,6 @@ class ReportPageComponent extends AuthComponent {
|
|||
<div style={{marginBottom: '20px'}}>
|
||||
<StolenPasswords data={this.state.report.glance.stolen_creds.concat(this.state.report.glance.ssh_keys)}/>
|
||||
</div>
|
||||
<div>
|
||||
<StolenPasswords data={this.state.report.glance.stolen_creds.concat(this.state.report.glance.ssh_keys)}/>
|
||||
</div>
|
||||
<div>
|
||||
<StrongUsers data = {this.state.pthreport.strong_users_on_crit_services} />
|
||||
</div>
|
||||
|
@ -745,6 +751,18 @@ class ReportPageComponent extends AuthComponent {
|
|||
);
|
||||
}
|
||||
|
||||
generateSharedCredsDomainIssue(issue) {
|
||||
return (
|
||||
<li>
|
||||
Some domain users are sharing passwords, this should be fixed by changing passwords.
|
||||
<CollapsibleWellComponent>
|
||||
These users are sharing access password:
|
||||
{this.generateInfoBadges(issue.shared_with)}.
|
||||
</CollapsibleWellComponent>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
generateSharedCredsIssue(issue) {
|
||||
return (
|
||||
<li>
|
||||
|
@ -760,10 +778,10 @@ class ReportPageComponent extends AuthComponent {
|
|||
generateSharedLocalAdminsIssue(issue) {
|
||||
return (
|
||||
<li>
|
||||
This machine shares a local admin account with another machine
|
||||
Credentials for the user <span className="label label-primary">{issue.username}</span> could be found and the user is an administrator account on more than one machines in the domain.
|
||||
<CollapsibleWellComponent>
|
||||
Here is a list showing users that are acting as admins on this machine and others:
|
||||
{this.generateInfoBadges(issue.shared_accounts)}
|
||||
Here is a list of machines which has this account defined as an administrator:
|
||||
{this.generateInfoBadges(issue.shared_machines)}
|
||||
</CollapsibleWellComponent>
|
||||
</li>
|
||||
);
|
||||
|
@ -892,7 +910,10 @@ class ReportPageComponent extends AuthComponent {
|
|||
case 'shared_passwords':
|
||||
data = this.generateSharedCredsIssue(issue);
|
||||
break;
|
||||
case 'shared_admins':
|
||||
case 'shared_passwords_domain':
|
||||
data = this.generateSharedCredsDomainIssue(issue);
|
||||
break;
|
||||
case 'shared_admins_domain':
|
||||
data = this.generateSharedLocalAdminsIssue(issue);
|
||||
break;
|
||||
case 'strong_users_on_crit':
|
||||
|
|
Loading…
Reference in New Issue