diff --git a/monkey_island/cc/resources/pthreport.py b/monkey_island/cc/resources/pthreport.py
index 31233aa1e..7c4046694 100644
--- a/monkey_island/cc/resources/pthreport.py
+++ b/monkey_island/cc/resources/pthreport.py
@@ -3,7 +3,7 @@ import flask_restful
from cc.auth import jwt_required
from cc.services.pth_report import PTHReportService
-__author__ = "itay.mizeretz"
+__author__ = "maor.rayzin"
class PTHReport(flask_restful.Resource):
diff --git a/monkey_island/cc/services/pth_report.py b/monkey_island/cc/services/pth_report.py
index 45a6e3668..7a615db2d 100644
--- a/monkey_island/cc/services/pth_report.py
+++ b/monkey_island/cc/services/pth_report.py
@@ -18,7 +18,8 @@ class PTHReportService(object):
continue
for sid in pth.GetSidsBySecret(secret):
usernames_per_sid_list.append(pth.GetUsernameBySid(sid))
- usernames_lists.append(usernames_per_sid_list)
+
+ usernames_lists.append({'cred_group': usernames_per_sid_list})
return usernames_lists
@@ -99,24 +100,49 @@ class PTHReportService(object):
'ip': m.GetIp(),
'hostname': m.GetHostName(),
'domain': m.GetDomainName(),
- 'services_names': m.GetNonCritialServers(),
+ 'services_names': [],
'user_count': count,
'threatening_users': threatening_users_attackers_dict
}
strong_users_non_crit_list.append(machine)
return strong_users_non_crit_list
+ @staticmethod
+ def get_duplicated_passwords_issues(pth, password_groups):
+ issues = []
+ issues_dict = {}
+ for group in password_groups:
+ for username in group['cred_group']:
+ sid = list(pth.GetSidsByUsername(username.split('\\')[1]))
+ machine_info = pth.GetSidInfo(sid[0])
+ issues.append(
+ {
+ 'type': 'shared_password',
+ 'machine': machine_info.get('hostname').split('.')[0],
+ 'shared_with': [x for x in group['cred_group'] if x != username],
+ 'username': username
+ }
+ )
+
+ for issue in issues:
+ machine = issue['machine']
+ if machine not in issues_dict:
+ issues_dict[machine] = []
+ issues_dict[machine].append(issue)
+
+ return issues_dict
+
@staticmethod
def generate_map_nodes(pth):
nodes_list = []
for node_id in pth.vertices:
machine = Machine(node_id)
node = {
- "id": str(machine.get_monkey_id()),
+ "id": str(node_id),
"label": '{0} : {1}'.format(machine.GetHostName(), machine.GetIp()),
'group': 'critical' if machine.IsCriticalServer() else 'normal',
'users': list(machine.GetCachedUsernames()),
- 'ips': machine.GetIp(),
+ 'ips': [machine.GetIp()],
'services': machine.GetCriticalServicesInstalled(),
'hostname': machine.GetHostName()
}
@@ -127,17 +153,23 @@ class PTHReportService(object):
@staticmethod
def get_report():
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(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)
report = \
{
'report_info':
{
- '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_non_crit_services': PTHReportService.get_strong_users_on_non_crit_services(pth)
+ '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
},
- 'map':
+ 'pthmap':
{
'nodes': PTHReportService.generate_map_nodes(pth),
'edges': pth.edges
@@ -318,4 +350,3 @@ class PTHReportService(object):
# for m in pth.GetAttackersBySecret(secret):
# print """
{hostname}""".format(ip=m.GetIp(), hostname=m.GetHostName())
# print """"""
-
diff --git a/monkey_island/cc/services/pth_report_utils.py b/monkey_island/cc/services/pth_report_utils.py
index 06c40f023..b4839f852 100644
--- a/monkey_island/cc/services/pth_report_utils.py
+++ b/monkey_island/cc/services/pth_report_utils.py
@@ -212,7 +212,8 @@ class Machine(object):
"Username": eval(user.get("Name")),
"Disabled": user.get("Disabled") == "true",
"PasswordRequired": user.get("PasswordRequired") == "true",
- "PasswordExpires": user.get("PasswordExpires") == "true", }
+ "PasswordExpires": user.get("PasswordExpires") == "true",
+ 'hostname': doc.get('data').get('hostname'), }
if not self.IsDomainController():
for dc in self.GetDomainControllers():
@@ -555,6 +556,7 @@ class Machine(object):
return names
+
class PassTheHashReport(object):
# _instance = None
# def __new__(class_, *args, **kwargs):
@@ -570,7 +572,7 @@ class PassTheHashReport(object):
self.edges = self.get_edges_by_sid() # Useful for non-cached domain users
#self.edges |= self.GetEdgesBySamHash() # This will add edges based only on password hash without caring about username
- @cache
+
def GetAllMachines(self):
cur = mongo.db.telemetry.find({"telem_type": "system_info_collection"})
@@ -610,13 +612,12 @@ class PassTheHashReport(object):
attacker_monkey = NodeService.get_monkey_by_guid(attacker)
victim_monkey = NodeService.get_monkey_by_guid(victim)
- attacker_label = NodeService.get_node_label(attacker_monkey)
- victim_label = NodeService.get_node_label(victim_monkey)
+ attacker_label = NodeService.get_monkey_label(attacker_monkey)
+ victim_label = NodeService.get_monkey_label(victim_monkey)
RIGHT_ARROW = u"\u2192"
return "%s %s %s" % (attacker_label, RIGHT_ARROW, victim_label)
- @cache
def get_edges_by_sid(self):
edges_list = []
@@ -637,7 +638,7 @@ class PassTheHashReport(object):
'to': victim,
'users': relevant_users_list,
'_label': PassTheHashReport.__get_edge_label(attacker, victim),
- 'id': uuid.uuid4()
+ 'id': str(uuid.uuid4())
})
return edges_list
@@ -858,9 +859,9 @@ class PassTheHashReport(object):
attackers = set()
- for atck, vic, _ in self.edges:
- if vic == victim:
- attackers.add(atck)
+ for edge in self.edges:
+ if edge.get('to', None) == victim:
+ attackers.add(edge.get('from', None))
return set(map(Machine, attackers))
diff --git a/monkey_island/cc/ui/src/components/pages/ReportPage.js b/monkey_island/cc/ui/src/components/pages/ReportPage.js
index 03939b01d..ed75da059 100644
--- a/monkey_island/cc/ui/src/components/pages/ReportPage.js
+++ b/monkey_island/cc/ui/src/components/pages/ReportPage.js
@@ -47,6 +47,7 @@ class ReportPageComponent extends AuthComponent {
allMonkeysAreDead: false,
runStarted: true
};
+ this.getPth
}
componentDidMount() {
@@ -117,7 +118,7 @@ class ReportPageComponent extends AuthComponent {
.then(res => {
this.setState({
pthreport: res.report_info,
- pthmap: res.map
+ pthmap: res.pthmap
});
});
}
@@ -389,6 +390,7 @@ class ReportPageComponent extends AuthComponent {
{this.generateIssues(this.state.report.recommendations.issues)}
+ {this.generateIssues(this.state.pthreport.pth_issues)}
);
@@ -399,6 +401,7 @@ class ReportPageComponent extends AuthComponent {
(100 * this.state.report.glance.exploited.length) / this.state.report.glance.scanned.length;
return (
+
The Network from the Monkey's Eyes
@@ -452,7 +455,15 @@ class ReportPageComponent extends AuthComponent {
-
+ { /* TODO: use dynamic data */}
+
);
@@ -728,6 +739,18 @@ class ReportPageComponent extends AuthComponent {
);
}
+ generateSharedCredsIssue(issue) {
+ return (
+
+ Some users are sharing passwords, this should be fixed by changing passwords.
+
+ The user {issue.username} is sharing access password with:
+ {this.generateInfoBadges(issue.shared_with)}.
+
+
+ );
+ }
+
generateTunnelIssue(issue) {
return (
@@ -800,6 +823,9 @@ class ReportPageComponent extends AuthComponent {
case 'cross_segment':
data = this.generateCrossSegmentIssue(issue);
break;
+ case 'shared_password':
+ data = this.generateSharedCredsIssue(issue);
+ break;
case 'tunnel':
data = this.generateTunnelIssue(issue);
break;