* Added information about what info gathered to the report's issues section

This commit is contained in:
maor.rayzin 2018-08-01 13:07:18 +03:00
parent af3b5665ce
commit cdfd6284ee
4 changed files with 80 additions and 22 deletions

View File

@ -3,7 +3,7 @@ import flask_restful
from cc.auth import jwt_required from cc.auth import jwt_required
from cc.services.pth_report import PTHReportService from cc.services.pth_report import PTHReportService
__author__ = "itay.mizeretz" __author__ = "maor.rayzin"
class PTHReport(flask_restful.Resource): class PTHReport(flask_restful.Resource):

View File

@ -18,7 +18,8 @@ class PTHReportService(object):
continue continue
for sid in pth.GetSidsBySecret(secret): for sid in pth.GetSidsBySecret(secret):
usernames_per_sid_list.append(pth.GetUsernameBySid(sid)) 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 return usernames_lists
@ -99,24 +100,49 @@ class PTHReportService(object):
'ip': m.GetIp(), 'ip': m.GetIp(),
'hostname': m.GetHostName(), 'hostname': m.GetHostName(),
'domain': m.GetDomainName(), 'domain': m.GetDomainName(),
'services_names': m.GetNonCritialServers(), 'services_names': [],
'user_count': count, 'user_count': count,
'threatening_users': threatening_users_attackers_dict 'threatening_users': threatening_users_attackers_dict
} }
strong_users_non_crit_list.append(machine) strong_users_non_crit_list.append(machine)
return strong_users_non_crit_list 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 @staticmethod
def generate_map_nodes(pth): def generate_map_nodes(pth):
nodes_list = [] nodes_list = []
for node_id in pth.vertices: for node_id in pth.vertices:
machine = Machine(node_id) machine = Machine(node_id)
node = { node = {
"id": str(machine.get_monkey_id()), "id": str(node_id),
"label": '{0} : {1}'.format(machine.GetHostName(), machine.GetIp()), "label": '{0} : {1}'.format(machine.GetHostName(), machine.GetIp()),
'group': 'critical' if machine.IsCriticalServer() else 'normal', 'group': 'critical' if machine.IsCriticalServer() else 'normal',
'users': list(machine.GetCachedUsernames()), 'users': list(machine.GetCachedUsernames()),
'ips': machine.GetIp(), 'ips': [machine.GetIp()],
'services': machine.GetCriticalServicesInstalled(), 'services': machine.GetCriticalServicesInstalled(),
'hostname': machine.GetHostName() 'hostname': machine.GetHostName()
} }
@ -127,17 +153,23 @@ class PTHReportService(object):
@staticmethod @staticmethod
def get_report(): def get_report():
pth = PassTheHashReport() 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 = \
{ {
'report_info': 'report_info':
{ {
'same_password': PTHReportService.get_duplicated_password_nodes(pth), 'same_password': same_password,
'local_admin_shared': PTHReportService.get_shared_local_admins_nodes(pth), 'local_admin_shared': local_admin_shared,
'strong_users_on_crit_services': PTHReportService.get_strong_users_on_crit_services(pth), 'strong_users_on_crit_services': strong_users_on_crit_services,
'strong_users_on_non_crit_services': PTHReportService.get_strong_users_on_non_crit_services(pth) 'strong_users_on_non_crit_services': strong_users_on_non_crit_services,
'pth_issues': issues
}, },
'map': 'pthmap':
{ {
'nodes': PTHReportService.generate_map_nodes(pth), 'nodes': PTHReportService.generate_map_nodes(pth),
'edges': pth.edges 'edges': pth.edges
@ -318,4 +350,3 @@ class PTHReportService(object):
# for m in pth.GetAttackersBySecret(secret): # for m in pth.GetAttackersBySecret(secret):
# print """<li><a href="#{ip}">{hostname}</a></li>""".format(ip=m.GetIp(), hostname=m.GetHostName()) # print """<li><a href="#{ip}">{hostname}</a></li>""".format(ip=m.GetIp(), hostname=m.GetHostName())
# print """</ul>""" # print """</ul>"""

View File

@ -212,7 +212,8 @@ class Machine(object):
"Username": eval(user.get("Name")), "Username": eval(user.get("Name")),
"Disabled": user.get("Disabled") == "true", "Disabled": user.get("Disabled") == "true",
"PasswordRequired": user.get("PasswordRequired") == "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(): if not self.IsDomainController():
for dc in self.GetDomainControllers(): for dc in self.GetDomainControllers():
@ -555,6 +556,7 @@ class Machine(object):
return names return names
class PassTheHashReport(object): class PassTheHashReport(object):
# _instance = None # _instance = None
# def __new__(class_, *args, **kwargs): # 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.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 #self.edges |= self.GetEdgesBySamHash() # This will add edges based only on password hash without caring about username
@cache
def GetAllMachines(self): def GetAllMachines(self):
cur = mongo.db.telemetry.find({"telem_type": "system_info_collection"}) 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) attacker_monkey = NodeService.get_monkey_by_guid(attacker)
victim_monkey = NodeService.get_monkey_by_guid(victim) victim_monkey = NodeService.get_monkey_by_guid(victim)
attacker_label = NodeService.get_node_label(attacker_monkey) attacker_label = NodeService.get_monkey_label(attacker_monkey)
victim_label = NodeService.get_node_label(victim_monkey) victim_label = NodeService.get_monkey_label(victim_monkey)
RIGHT_ARROW = u"\u2192" RIGHT_ARROW = u"\u2192"
return "%s %s %s" % (attacker_label, RIGHT_ARROW, victim_label) return "%s %s %s" % (attacker_label, RIGHT_ARROW, victim_label)
@cache
def get_edges_by_sid(self): def get_edges_by_sid(self):
edges_list = [] edges_list = []
@ -637,7 +638,7 @@ class PassTheHashReport(object):
'to': victim, 'to': victim,
'users': relevant_users_list, 'users': relevant_users_list,
'_label': PassTheHashReport.__get_edge_label(attacker, victim), '_label': PassTheHashReport.__get_edge_label(attacker, victim),
'id': uuid.uuid4() 'id': str(uuid.uuid4())
}) })
return edges_list return edges_list
@ -858,9 +859,9 @@ class PassTheHashReport(object):
attackers = set() attackers = set()
for atck, vic, _ in self.edges: for edge in self.edges:
if vic == victim: if edge.get('to', None) == victim:
attackers.add(atck) attackers.add(edge.get('from', None))
return set(map(Machine, attackers)) return set(map(Machine, attackers))

View File

@ -47,6 +47,7 @@ class ReportPageComponent extends AuthComponent {
allMonkeysAreDead: false, allMonkeysAreDead: false,
runStarted: true runStarted: true
}; };
this.getPth
} }
componentDidMount() { componentDidMount() {
@ -117,7 +118,7 @@ class ReportPageComponent extends AuthComponent {
.then(res => { .then(res => {
this.setState({ this.setState({
pthreport: res.report_info, pthreport: res.report_info,
pthmap: res.map pthmap: res.pthmap
}); });
}); });
} }
@ -389,6 +390,7 @@ class ReportPageComponent extends AuthComponent {
</h3> </h3>
<div> <div>
{this.generateIssues(this.state.report.recommendations.issues)} {this.generateIssues(this.state.report.recommendations.issues)}
{this.generateIssues(this.state.pthreport.pth_issues)}
</div> </div>
</div> </div>
); );
@ -399,6 +401,7 @@ class ReportPageComponent extends AuthComponent {
(100 * this.state.report.glance.exploited.length) / this.state.report.glance.scanned.length; (100 * this.state.report.glance.exploited.length) / this.state.report.glance.scanned.length;
return ( return (
<div id="glance"> <div id="glance">
<h3> <h3>
The Network from the Monkey's Eyes The Network from the Monkey's Eyes
</h3> </h3>
@ -452,7 +455,15 @@ class ReportPageComponent extends AuthComponent {
<SharedAdmins data = {this.state.pthreport.local_admin_shared} /> <SharedAdmins data = {this.state.pthreport.local_admin_shared} />
</div> </div>
<div> <div>
<StrongUsers data = {this.state.pthreport.strong_users_on_crit_services} /> { /* TODO: use dynamic data */}
<StrongUsers data = {[
{
username: 'SharedLocalAdmin',
domain: 'MyDomain',
machines: ['hello : 1.2.3.4'],
services: ['DC', 'DNS']
}
]} />
</div> </div>
</div> </div>
); );
@ -728,6 +739,18 @@ class ReportPageComponent extends AuthComponent {
); );
} }
generateSharedCredsIssue(issue) {
return (
<li>
Some users are sharing passwords, this should be fixed by changing passwords.
<CollapsibleWellComponent>
The user <span className="label label-primary">{issue.username}</span> is sharing access password with:
{this.generateInfoBadges(issue.shared_with)}.
</CollapsibleWellComponent>
</li>
);
}
generateTunnelIssue(issue) { generateTunnelIssue(issue) {
return ( return (
<li> <li>
@ -800,6 +823,9 @@ class ReportPageComponent extends AuthComponent {
case 'cross_segment': case 'cross_segment':
data = this.generateCrossSegmentIssue(issue); data = this.generateCrossSegmentIssue(issue);
break; break;
case 'shared_password':
data = this.generateSharedCredsIssue(issue);
break;
case 'tunnel': case 'tunnel':
data = this.generateTunnelIssue(issue); data = this.generateTunnelIssue(issue);
break; break;