* 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.
This commit is contained in:
maor.rayzin 2018-08-23 15:17:08 +03:00
parent 4e1c4c3290
commit c373bfbcfb
6 changed files with 106 additions and 93 deletions

View File

@ -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 = \
{

View File

@ -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

View File

@ -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')
}
}

View File

@ -50,9 +50,6 @@ class PassTheHashMapPageComponent extends AuthComponent {
<ReactiveGraph graph={this.state.graph} options={optionsPth} events={this.events}/>
</div>
</Col>
<Col xs={12}>
<PthPreviewPaneComponent item={this.state.selected} type={this.state.selectedType}/>
</Col>
</div>
);
}

View File

@ -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 {
<div style={{marginBottom: '20px'}}>
<ScannedServers data={this.state.report.glance.scanned}/>
</div>
<div>
<StolenPasswords data={this.state.report.glance.stolen_creds, this.state.report.glance.ssh_keys}/>
</div>
{this.generateReportPthMap()}
<div style={{marginBottom: '20px'}}>
<StolenPasswords data={this.state.report.glance.stolen_creds}/>
</div>
<div style={{marginBottom: '20px'}}>
<SharedAdmins data = {this.state.pthreport.local_admin_shared} />
<StolenPasswords data={this.state.report.glance.stolen_creds.concat(this.state.report.glance.ssh_keys)}/>
</div>
<div>
{ /* TODO: use dynamic data */}
<StrongUsers data = {[
{
username: 'SharedLocalAdmin',
domain: 'MyDomain',
machines: ['hello : 1.2.3.4'],
services: ['DC', 'DNS']
}
]} />
<StrongUsers data = {this.state.pthreport.strong_users_on_crit_services} />
</div>
</div>
);
}
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 (
<div id="pth">
<h3>
Credential Map
</h3>
<div style={{position: 'relative', height: '100vh'}}>
<div>
<PassTheHashMapPageComponent graph={this.state.pthmap} />
</div>
<br />

View File

@ -2,6 +2,7 @@ import React from 'react';
import ReactTable from 'react-table'
let renderArray = function(val) {
console.log(val);
return <div>{val.map(x => <div>{x}</div>)}</div>;
};
@ -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)}
]
}
];