forked from p15670423/monkey
* 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:
parent
4e1c4c3290
commit
c373bfbcfb
|
@ -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 = \
|
||||
{
|
||||
|
|
|
@ -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
|
|
@ -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')
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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 />
|
||||
|
|
|
@ -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)}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
|
Loading…
Reference in New Issue