From c373bfbcfb3f3af2a669c36c67998727188b12d8 Mon Sep 17 00:00:00 2001 From: "maor.rayzin" Date: Thu, 23 Aug 2018 15:17:08 +0300 Subject: [PATCH] * integrated parts of the pth report to the main report module. * Changed the ui a bit, removed some tables and add information to the current tables. --- monkey_island/cc/services/pth_report.py | 59 +++++++++++++++---- monkey_island/cc/services/pth_report_utils.py | 40 +++++++------ monkey_island/cc/services/report.py | 45 +++++++++----- .../components/pages/PassTheHashMapPage.js | 3 - .../cc/ui/src/components/pages/ReportPage.js | 49 +++------------ .../report-components/StrongUsers.js | 3 +- 6 files changed, 106 insertions(+), 93 deletions(-) diff --git a/monkey_island/cc/services/pth_report.py b/monkey_island/cc/services/pth_report.py index 3640e29e2..03167f81a 100644 --- a/monkey_island/cc/services/pth_report.py +++ b/monkey_island/cc/services/pth_report.py @@ -24,7 +24,8 @@ class PTHReportService(object): if count <= 1: continue for sid in pth.GetSidsBySecret(secret): - usernames_per_sid_list.append(pth.GetUsernameBySid(sid)) + if sid: + usernames_per_sid_list.append(pth.GetUsernameBySid(sid)) usernames_lists.append({'cred_group': usernames_per_sid_list}) @@ -56,7 +57,7 @@ class PTHReportService(object): return shared_admin_machines @staticmethod - def get_strong_users_on_crit_services(pth): + def get_strong_users_on_crit_services_by_machine(pth): threatening = dict(map(lambda x: (x, len(pth.GetThreateningUsersByVictim(x))), pth.GetCritialServers())) strong_users_crit_list = [] @@ -77,13 +78,34 @@ class PTHReportService(object): 'ip': m.GetIp(), 'hostname': m.GetHostName(), 'domain': m.GetDomainName(), - 'services_names': m.GetCriticalServicesInstalled(), - 'user_count': count, + 'services': m.GetCriticalServicesInstalled(), 'threatening_users': threatening_users_attackers_dict } strong_users_crit_list.append(machine) return strong_users_crit_list + + @staticmethod + def get_strong_users_on_crit_services_by_user(pth): + critical_servers = pth.GetCritialServers() + strong_users_dict = {} + + for server in critical_servers: + users = pth.GetThreateningUsersByVictim(server) + for sid in users: + username = pth.GetUsernameBySid(sid) + if username not in strong_users_dict: + strong_users_dict[username] = { + 'services_names': [], + 'machines': [] + } + strong_users_dict[username]['username'] = username + strong_users_dict[username]['domain'] = server.GetDomainName() + strong_users_dict[username]['services_names'] += server.GetCriticalServicesInstalled() + strong_users_dict[username]['machines'].append(server.GetHostName()) + + return strong_users_dict.values() + @staticmethod def get_strong_users_on_non_crit_services(pth): threatening = dict(map(lambda x: (x, len(pth.GetThreateningUsersByVictim(x))), pth.GetNonCritialServers())) @@ -117,8 +139,11 @@ class PTHReportService(object): @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( @@ -128,6 +153,7 @@ class PTHReportService(object): 'shared_with': group['cred_group'] } ) + previeous_group += group['cred_group'] return issues @@ -154,19 +180,19 @@ class PTHReportService(object): { 'type': 'strong_users_on_crit', 'machine': machine.get('hostname'), - 'services': machine.get('services_names'), + 'services': machine.get('services'), 'ip': machine.get('ip'), - 'threatening_users': machine.get('threatening_users') + 'threatening_users': machine.get('threatening_users').keys() } ) return issues @staticmethod - def generate_map_nodes(pth): - nodes_list = [] - for node_id in pth.vertices: - machine = Machine(node_id) + def get_machine_details(node_id): + machine = Machine(node_id) + node = {} + if machine.latest_system_info: node = { "id": str(node_id), "label": '{0} : {1}'.format(machine.GetHostName(), machine.GetIp()), @@ -176,6 +202,13 @@ class PTHReportService(object): 'services': machine.GetCriticalServicesInstalled(), 'hostname': machine.GetHostName() } + return node + + @staticmethod + def generate_map_nodes(pth): + nodes_list = [] + for node_id in pth.vertices: + node = PTHReportService.get_machine_details(node_id) nodes_list.append(node) return nodes_list @@ -200,13 +233,13 @@ class PTHReportService(object): 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(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(strong_users_on_crit_services) - #formated_issues = PTHReportService.get_issues_list(issues) + issues += PTHReportService.strong_users_on_crit_issues( + PTHReportService.get_strong_users_on_crit_services_by_machine(pth)) report = \ { diff --git a/monkey_island/cc/services/pth_report_utils.py b/monkey_island/cc/services/pth_report_utils.py index b4839f852..9e9baa6b3 100644 --- a/monkey_island/cc/services/pth_report_utils.py +++ b/monkey_island/cc/services/pth_report_utils.py @@ -99,6 +99,8 @@ class Machine(object): if self.latest_system_info.count() > 0: self.latest_system_info = self.latest_system_info[0] + else: + self.latest_system_info = None self.monkey_info = NodeService.get_monkey_by_guid(self.monkey_guid) @@ -163,7 +165,7 @@ class Machine(object): def IsDomainController(self): return self.GetDomainRole() in (DsRole_RolePrimaryDomainController, DsRole_RoleBackupDomainController) - @cache + #@cache def GetSidByUsername(self, username, domain=None): doc = self.latest_system_info @@ -227,7 +229,7 @@ class Machine(object): @cache def GetCriticalServicesInstalled(self): def IsNameOfCriticalService(name): - services = ("W3svc", "MSExchangeServiceHost", "MSSQLServer", "dns") + services = ("W3svc", "MSExchangeServiceHost", "MSSQLServer", "dns", 'MSSQL$SQLEXPRESS') services = map(str.lower, services) if not name: @@ -290,7 +292,6 @@ class Machine(object): usernames = self.GetUsernamesBySecret(secret) return set(map(self.GetSidByUsername, usernames)) - @cache def GetGroupSidByGroupName(self, group_name): doc = self.latest_system_info @@ -305,7 +306,6 @@ class Machine(object): return None - @cache def GetUsersByGroupSid(self, sid): doc = self.latest_system_info @@ -360,7 +360,6 @@ class Machine(object): return GUIDs - @cache def GetLocalAdmins(self): admins = self.GetUsersByGroupSid(self.GetGroupSidByGroupName("Administrators")) @@ -369,7 +368,6 @@ class Machine(object): return admins - @cache def GetLocalAdminSids(self): return set(self.GetLocalAdmins().keys()) @@ -510,7 +508,7 @@ class Machine(object): DCs = self.GetDomainControllersMonkeyGuidByDomainName(domain_name) return map(Machine, DCs) - @cache + def GetDomainAdminsOfMachine(self): DCs = self.GetDomainControllers() @@ -521,15 +519,15 @@ class Machine(object): return domain_admins - @cache + #@cache def GetAdmins(self): - return self.GetLocalAdminSids() | self.GetDomainAdminsOfMachine() + return self.GetLocalAdminSids() # | self.GetDomainAdminsOfMachine() @cache def GetAdminNames(self): return set(map(lambda x: self.GetUsernameBySid(x), self.GetAdmins())) - @cache + #@cache def GetCachedSids(self): doc = self.latest_system_info @@ -622,16 +620,18 @@ class PassTheHashReport(object): edges_list = [] for attacker in self.vertices: - cached = self.GetCachedSids(Machine(attacker)) + cached = list(self.GetCachedSids(Machine(attacker))) for victim in self.vertices: if attacker == victim: continue - admins = Machine(victim).GetAdmins() + admins = list(Machine(victim).GetAdmins()) - if len(cached & admins) > 0: - relevant_users_list = self.ReprSidList(cached & admins, attacker, victim) + cached_admins = [i for i in cached if i in admins] + + if cached_admins: + relevant_users_list = self.ReprSidList(cached_admins, attacker, victim) edges_list.append( { 'from': attacker, @@ -929,7 +929,7 @@ class PassTheHashReport(object): def GetNonCritialServers(self): return set(self.machines) - self.GetCritialServers() - @cache + #@cache def GetCachedSids(self, m): sids = set() tmp = m.GetCachedSids() @@ -958,15 +958,17 @@ class PassTheHashReport(object): return threatening_users - @cache def GetSharedAdmins(self, m): - shared_admins = set() + shared_admins = [] for other in self.machines: if m == other: continue + for sid in m.GetLocalAdminSids(): + if sid in other.GetLocalAdminSids(): + shared_admins.append(sid) - shared_admins |= (m.GetLocalAdminSids() & other.GetLocalAdminSids()) + #shared_admins |= (m.GetLocalAdminSids() & other.GetLocalAdminSids()) - shared_admins -= m.GetDomainAdminsOfMachine() + #shared_admins -= m.GetDomainAdminsOfMachine() return shared_admins \ No newline at end of file diff --git a/monkey_island/cc/services/report.py b/monkey_island/cc/services/report.py index 6a89afa58..122f8ff5d 100644 --- a/monkey_island/cc/services/report.py +++ b/monkey_island/cc/services/report.py @@ -44,9 +44,9 @@ class ReportService: AZURE = 6 STOLEN_SSH_KEYS = 7 STRUTS2 = 8 + MSSQL_TO_BE = 9 PTH_CRIT_SERVICES_ACCESS = 10 - class WARNINGS_DICT(Enum): CROSS_SEGMENT = 0 TUNNEL = 1 @@ -103,25 +103,31 @@ class ReportService: @staticmethod def get_scanned(): + + formatted_nodes = [] + nodes = \ [NodeService.get_displayed_node_by_id(node['_id'], True) for node in mongo.db.node.find({}, {'_id': 1})] \ + [NodeService.get_displayed_node_by_id(monkey['_id'], True) for monkey in mongo.db.monkey.find({}, {'_id': 1})] - nodes = [ - { - 'label': node['label'], - 'ip_addresses': node['ip_addresses'], - 'accessible_from_nodes': - (x['hostname'] for x in - (NodeService.get_displayed_node_by_id(edge['from'], True) - for edge in EdgeService.get_displayed_edges_by_to(node['id'], True))), - 'services': node['services'] - } - for node in nodes] + + for node in nodes: + pth_services = PTHReportService.get_machine_details(NodeService.get_monkey_by_id(node['id']) + .get('guid', None)).get('services', None) + formatted_nodes.append( + { + 'label': node['label'], + 'ip_addresses': node['ip_addresses'], + 'accessible_from_nodes': + (x['hostname'] for x in + (NodeService.get_displayed_node_by_id(edge['from'], True) + for edge in EdgeService.get_displayed_edges_by_to(node['id'], True))), + 'services': node['services'] + pth_services if pth_services else [] + }) logger.info('Scanned nodes generated for reporting') - return nodes + return formatted_nodes @staticmethod def get_exploited(): @@ -160,13 +166,14 @@ class ReportService: origin = NodeService.get_monkey_by_guid(telem['monkey_guid'])['hostname'] for user in monkey_creds: for pass_type in monkey_creds[user]: - creds.append( + 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 @@ -374,7 +381,7 @@ class ReportService: PTHReportService.get_report().get('report_info').get('pth_issues', []) issues_dict = {} for issue in issues: - machine = issue['machine'] + machine = issue.get('machine', '').upper() if machine not in issues_dict: issues_dict[machine] = [] issues_dict[machine].append(issue) @@ -483,6 +490,7 @@ class ReportService: issues = ReportService.get_issues() config_users = ReportService.get_config_users() config_passwords = ReportService.get_config_passwords() + pth_report = PTHReportService.get_report() report = \ @@ -511,6 +519,11 @@ class ReportService: 'recommendations': { 'issues': issues + }, + 'pth': + { + 'map': pth_report.get('pthmap'), + 'info': pth_report.get('report_info') } } diff --git a/monkey_island/cc/ui/src/components/pages/PassTheHashMapPage.js b/monkey_island/cc/ui/src/components/pages/PassTheHashMapPage.js index 8c7ded49b..20faafca7 100644 --- a/monkey_island/cc/ui/src/components/pages/PassTheHashMapPage.js +++ b/monkey_island/cc/ui/src/components/pages/PassTheHashMapPage.js @@ -50,9 +50,6 @@ class PassTheHashMapPageComponent extends AuthComponent { - - - ); } diff --git a/monkey_island/cc/ui/src/components/pages/ReportPage.js b/monkey_island/cc/ui/src/components/pages/ReportPage.js index 400381c8a..37c224956 100644 --- a/monkey_island/cc/ui/src/components/pages/ReportPage.js +++ b/monkey_island/cc/ui/src/components/pages/ReportPage.js @@ -29,6 +29,7 @@ class ReportPageComponent extends AuthComponent { AZURE: 6, STOLEN_SSH_KEYS: 7, STRUTS2: 8, + MSSQL_TO_BE: 9, PTH_CRIT_SERVICES_ACCESS: 10 }; @@ -55,9 +56,8 @@ class ReportPageComponent extends AuthComponent { componentDidMount() { this.updateMonkeysRunning().then(res => this.getReportFromServer(res)); - this.getPTHReportFromServer(); this.updateMapFromServer(); - this.interval = setInterval(this.updateMapFromServer, 1000); + /*this.interval = setInterval(this.updateMapFromServer, 1000);*/ } componentWillUnmount() { @@ -132,7 +132,9 @@ class ReportPageComponent extends AuthComponent { .then(res => res.json()) .then(res => { this.setState({ - report: res + report: res, + pthreport: res.pth.info, + pthmap: res.pth.map }); }); } @@ -450,59 +452,24 @@ class ReportPageComponent extends AuthComponent {
-
- -
{this.generateReportPthMap()}
- -
-
- +
- { /* TODO: use dynamic data */} - +
); } generateReportPthMap() { - // TODO: remove this and use updateMapFromSerever to get actual map data. - const my_map = { - nodes: [ - {id: '1', label: 'MYPC-1', group: 'normal', users: ['MYPC-2\\user1', 'Dom\\user2'], ips: ['192.168.0.1'], services: ["DC", "SQL"], 'hostname': 'aaa1'}, - {id: 2, label: 'MYPC-2', group: 'critical', users: ['MYPC-2\\user1', 'Dom\\user2'], ips: ['192.168.0.1'], services: ["DC", "SQL"], 'hostname': 'aaa2'}, - {id: 3, label: 'MYPC-3', group: 'normal', users: ['MYPC-3\\user1', 'Dom\\user3'], ips: ['192.168.0.2'], services: ["DC", "SQL"], 'hostname': 'aaa3'}, - {id: 4, label: 'MYPC-4', group: 'critical', users: ['MYPC-4\\user1', 'Dom\\user4'], ips: ['192.168.0.3', '192.168.0.4'], services: ["DC", "SQL"], 'hostname': 'aaa4'}, - {id: 5, label: 'MYPC-5', group: 'normal', users: ['MYPC-5\\user1', 'Dom\\user5'], ips: ['192.168.0.1'], services: [], 'hostname': 'aaa5'}, - {id: 6, label: 'MYPC-6', group: 'critical', users: ['MYPC-6\\user1', 'Dom\\user6'], ips: ['192.168.0.1'], services: ["DC"], 'hostname': 'aaa6'}, - {id: 7, label: 'MYPC-7', group: 'critical', users: ['MYPC-7\\user1', 'Dom\\user7'], ips: ['192.168.0.1'], services: ["DC", "SQL"], 'hostname': 'aaa7'} - ], - edges: [ - {id: 10, from: '1', to: 2, users: ['MYPC-3\\user1', 'Dom\\user3'], _label: 'bla0'}, - {id: 11, from: '1', to: 3, users: ['MYPC-3\\user1', 'Dom\\user3'], _label: 'bla1'}, - {id: 12, from: '1', to: 4, users: ['MYPC-3\\user1', 'Dom\\user3'], _label: 'bla2'}, - {id: 13, from: 5, to: 6, users: ['MYPC-3\\user1', 'Dom\\user3'], _label: 'bla3'}, - {id: 14, from: 6, to: 7, users: ['MYPC-3\\user1', 'Dom\\user3'], _label: 'bla4'}, - {id: 15, from: 6, to: 5, users: ['MYPC-3\\user1', 'Dom\\user3'], _label: 'bla5'}, - ] - - }; return (

Credential Map

-
+

diff --git a/monkey_island/cc/ui/src/components/report-components/StrongUsers.js b/monkey_island/cc/ui/src/components/report-components/StrongUsers.js index bfb933ec1..36068f26e 100644 --- a/monkey_island/cc/ui/src/components/report-components/StrongUsers.js +++ b/monkey_island/cc/ui/src/components/report-components/StrongUsers.js @@ -2,6 +2,7 @@ import React from 'react'; import ReactTable from 'react-table' let renderArray = function(val) { + console.log(val); return
{val.map(x =>
{x}
)}
; }; @@ -12,7 +13,7 @@ const columns = [ { Header: 'Username', accessor: 'username'}, { Header: 'Domain', accessor: 'domain'}, { Header: 'Machines', id: 'machines', accessor: x => renderArray(x.machines)}, - { Header: 'Services', id: 'services', accessor: x => renderArray(x.services)} + { Header: 'Services', id: 'services', accessor: x => renderArray(x.services_names)} ] } ];