forked from p15670423/monkey
RE-CR: changed names, added doc, created consts.
This commit is contained in:
parent
3a2d9a9cc2
commit
707c88434c
|
@ -9,12 +9,12 @@ from flask import request
|
||||||
|
|
||||||
from cc.auth import jwt_required
|
from cc.auth import jwt_required
|
||||||
from cc.database import mongo
|
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.config import ConfigService
|
||||||
from cc.services.edge import EdgeService
|
from cc.services.edge import EdgeService
|
||||||
from cc.services.node import NodeService
|
from cc.services.node import NodeService
|
||||||
from cc.encryptor import encryptor
|
from cc.encryptor import encryptor
|
||||||
from cc.services.wmi_info_handler import WMIHandler
|
from cc.services.wmi_handler import WMIHandler
|
||||||
|
|
||||||
__author__ = 'Barak'
|
__author__ = 'Barak'
|
||||||
|
|
||||||
|
@ -186,7 +186,7 @@ class Telemetry(flask_restful.Resource):
|
||||||
Telemetry.add_system_info_creds_to_config(creds)
|
Telemetry.add_system_info_creds_to_config(creds)
|
||||||
Telemetry.replace_user_dot_with_comma(creds)
|
Telemetry.replace_user_dot_with_comma(creds)
|
||||||
if 'mimikatz' in telemetry_json['data']:
|
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', ''))
|
extract_secrets_from_mimikatz(telemetry_json['data'].get('mimikatz', ''))
|
||||||
if 'wmi' in telemetry_json['data']:
|
if 'wmi' in telemetry_json['data']:
|
||||||
wmi_handler = WMIHandler(monkey_id, telemetry_json['data']['wmi'], users_secrets)
|
wmi_handler = WMIHandler(monkey_id, telemetry_json['data']['wmi'], users_secrets)
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
"""This file will include consts values regarding the groupsandusers collection"""
|
||||||
|
|
||||||
|
USERTYPE = 1
|
||||||
|
|
||||||
|
GROUPTYPE = 2
|
|
@ -3,6 +3,7 @@ from itertools import product
|
||||||
from cc.database import mongo
|
from cc.database import mongo
|
||||||
from bson import ObjectId
|
from bson import ObjectId
|
||||||
|
|
||||||
|
from cc.services.groups_and_users_consts import USERTYPE
|
||||||
from cc.services.node import NodeService
|
from cc.services.node import NodeService
|
||||||
|
|
||||||
__author__ = 'maor.rayzin'
|
__author__ = 'maor.rayzin'
|
||||||
|
@ -11,6 +12,19 @@ class PTHReportService(object):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __dup_passwords_mongoquery():
|
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 = [
|
pipeline = [
|
||||||
{"$match": {
|
{"$match": {
|
||||||
'NTLM_secret': {
|
'NTLM_secret': {
|
||||||
|
@ -30,18 +44,31 @@ class PTHReportService(object):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __get_admin_on_machines_format(admin_on_machines, domain_name):
|
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})
|
machines = mongo.db.monkey.find({'_id': {'$in': admin_on_machines}}, {'hostname': 1})
|
||||||
return [domain_name + '\\' + i['hostname'] for i in list(machines)]
|
return [domain_name + '\\' + i['hostname'] for i in list(machines)]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __strong_users_on_crit_query():
|
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 = [
|
pipeline = [
|
||||||
{
|
{
|
||||||
'$unwind': '$admin_on_machines'
|
'$unwind': '$admin_on_machines'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'$match': {'type': 1, 'domain_name': {'$ne': None}}
|
'$match': {'type': USERTYPE, 'domain_name': {'$ne': None}}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'$lookup':
|
'$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.
|
# 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
|
# Excluding the name Administrator - its spamming the lists and not a surprise the domain Administrator account
|
||||||
# is shared.
|
# 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': {'$exists': True}},
|
||||||
{'admin_on_machines': 1, 'name': 1, 'domain_name': 1})
|
{'admin_on_machines': 1, 'name': 1, 'domain_name': 1})
|
||||||
return [
|
return [
|
||||||
|
@ -200,7 +227,7 @@ class PTHReportService(object):
|
||||||
{
|
{
|
||||||
'admin_on_machines': {'$ne': []},
|
'admin_on_machines': {'$ne': []},
|
||||||
'secret_location': {'$ne': []},
|
'secret_location': {'$ne': []},
|
||||||
'type': 1
|
'type': USERTYPE
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'admin_on_machines': 1, 'secret_location': 1
|
'admin_on_machines': 1, 'secret_location': 1
|
||||||
|
|
|
@ -561,7 +561,7 @@ class ReportService:
|
||||||
issues_dict = {}
|
issues_dict = {}
|
||||||
for issue in issues:
|
for issue in issues:
|
||||||
if issue.get('is_local', True):
|
if issue.get('is_local', True):
|
||||||
machine = issue.get('machine', '').upper()
|
machine = issue.get('machine').upper()
|
||||||
if machine not in issues_dict:
|
if machine not in issues_dict:
|
||||||
issues_dict[machine] = []
|
issues_dict[machine] = []
|
||||||
issues_dict[machine].append(issue)
|
issues_dict[machine].append(issue)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from cc.database import mongo
|
from cc.database import mongo
|
||||||
|
from cc.services.groups_and_users_consts import USERTYPE
|
||||||
|
|
||||||
__author__ = 'maor.rayzin'
|
__author__ = 'maor.rayzin'
|
||||||
|
|
||||||
|
@ -15,8 +16,8 @@ class WMIHandler(object):
|
||||||
self.users_info = wmi_info['Win32_UserAccount']
|
self.users_info = wmi_info['Win32_UserAccount']
|
||||||
self.groups_info = wmi_info['Win32_Group']
|
self.groups_info = wmi_info['Win32_Group']
|
||||||
self.groups_and_users = wmi_info['Win32_GroupUser']
|
self.groups_and_users = wmi_info['Win32_GroupUser']
|
||||||
self.products = wmi_info['Win32_Service']
|
self.services = wmi_info['Win32_Service']
|
||||||
self.services = wmi_info['Win32_Product']
|
self.products = wmi_info['Win32_Product']
|
||||||
|
|
||||||
def process_and_handle_wmi_info(self):
|
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.
|
# if entity is domain entity, add the monkey id of current machine to secrets_location.
|
||||||
# (found on this machine)
|
# (found on this machine)
|
||||||
if entity.get('NTLM_secret'):
|
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}})
|
{'$addToSet': {'secret_location': self.monkey_id}})
|
||||||
|
|
||||||
def update_admins_retrospective(self):
|
def update_admins_retrospective(self):
|
||||||
|
@ -148,7 +149,7 @@ class WMIHandler(object):
|
||||||
mongo.db.groupsandusers.update_one({'SID': sid},
|
mongo.db.groupsandusers.update_one({'SID': sid},
|
||||||
{'$addToSet': {'admin_on_machines': machine_id}})
|
{'$addToSet': {'admin_on_machines': machine_id}})
|
||||||
entity_details = mongo.db.groupsandusers.find_one({'SID': sid},
|
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:
|
if entity_details.get('type') == 2:
|
||||||
self.add_admin(entity_details, machine_id)
|
self.add_admin(entity_details, machine_id)
|
||||||
|
|
Loading…
Reference in New Issue