diff --git a/infection_monkey/system_info/windows_info_collector.py b/infection_monkey/system_info/windows_info_collector.py
index e9e7f71fd..1444f5805 100644
--- a/infection_monkey/system_info/windows_info_collector.py
+++ b/infection_monkey/system_info/windows_info_collector.py
@@ -15,18 +15,9 @@ LOG = logging.getLogger(__name__)
__author__ = 'uri'
-WMI_CLASSES = set(["Win32_OperatingSystem",
- "Win32_ComputerSystem",
- "Win32_LoggedOnUser",
- "Win32_UserAccount",
- "Win32_UserProfile",
- "Win32_Group",
- "Win32_GroupUser",
- "Win32_Product",
- "Win32_Service",
- "Win32_OptionalFeature",
- #"Win32_Process",
- ])
+WMI_CLASSES = {"Win32_OperatingSystem", "Win32_ComputerSystem", "Win32_LoggedOnUser", "Win32_UserAccount",
+ "Win32_UserProfile", "Win32_Group", "Win32_GroupUser", "Win32_Product", "Win32_Service",
+ "Win32_OptionalFeature"}
# These wmi queries are able to return data about all the users & machines in the domain.
# For these queries to work, the monkey shohuld be run on a domain machine and
diff --git a/monkey_island/cc/app.py b/monkey_island/cc/app.py
index 33d1bb53b..a965ab070 100644
--- a/monkey_island/cc/app.py
+++ b/monkey_island/cc/app.py
@@ -19,7 +19,7 @@ from cc.resources.monkey_configuration import MonkeyConfiguration
from cc.resources.monkey_download import MonkeyDownload
from cc.resources.netmap import NetMap
from cc.resources.pthmap import PthMap
-from cc.resources.pthreporthtml import PthReportHtml
+from cc.resources.pthreport import Report
from cc.resources.node import Node
from cc.resources.report import Report
from cc.resources.root import Root
@@ -107,6 +107,6 @@ def init_app(mongo_url):
api.add_resource(TelemetryFeed, '/api/telemetry-feed', '/api/telemetry-feed/')
api.add_resource(Log, '/api/log', '/api/log/')
api.add_resource(PthMap, '/api/pthmap', '/api/pthmap/')
- api.add_resource(PthReportHtml, '/api/pthreport', '/api/pthreport/')
+ api.add_resource(Report, '/api/pthreport', '/api/pthreport/')
return app
diff --git a/monkey_island/cc/resources/pthmap.py b/monkey_island/cc/resources/pthmap.py
index 5230ef30e..d19afd56f 100644
--- a/monkey_island/cc/resources/pthmap.py
+++ b/monkey_island/cc/resources/pthmap.py
@@ -1,13 +1,10 @@
-import hashlib
-import binascii
import copy
import flask_restful
-from pthreport import PassTheHashReport, Machine
+
from cc.auth import jwt_required
-from cc.services.edge import EdgeService
-from cc.services.node import NodeService
-from cc.database import mongo
+from cc.services.pth_report_utils import PassTheHashReport, Machine
+
class PthMap(flask_restful.Resource):
@jwt_required()
diff --git a/monkey_island/cc/resources/pthreport.py b/monkey_island/cc/resources/pthreport.py
index b89a8b078..2484f7b07 100644
--- a/monkey_island/cc/resources/pthreport.py
+++ b/monkey_island/cc/resources/pthreport.py
@@ -1,1250 +1,13 @@
-import hashlib
-import binascii
-import copy
+import flask_restful
-if __name__ == "__main__":
- from pymongo import MongoClient
+from cc.auth import jwt_required
+from cc.services.pth_report import PTHReportService
- class mongo(object):
- db = MongoClient().monkeyisland
-else:
- from cc.database import mongo
-
-DsRole_RoleStandaloneWorkstation = 0
-DsRole_RoleMemberWorkstation = 1
-DsRole_RoleStandaloneServer = 2
-DsRole_RoleMemberServer = 3
-DsRole_RoleBackupDomainController = 4
-DsRole_RolePrimaryDomainController = 5
+__author__ = "itay.mizeretz"
-SidTypeUser = 1
-SidTypeGroup = 2
-SidTypeDomain = 3
-SidTypeAlias = 4
-SidTypeWellKnownGroup = 5
-SidTypeDeletedAccount = 6
-SidTypeInvalid = 7
-SidTypeUnknown = 8
-SidTypeComputer = 9
-def is_group_sid_type(type):
- return type in (SidTypeGroup, SidTypeAlias, SidTypeWellKnownGroup)
+class Report(flask_restful.Resource):
-def myntlm(x):
- hash = hashlib.new('md4', x.encode('utf-16le')).digest()
- return str(binascii.hexlify(hash))
-
-def cache(foo):
- def hash(o):
- if type(o) in (int, float, str, unicode):
- return repr(o)
-
- elif type(o) in (type(None),):
- return "___None___"
-
- elif type(o) in (list, tuple, set):
- hashed = tuple([hash(x) for x in o])
-
- if "NotHashable" in hashed:
- return "NotHashable"
-
- return hashed
-
- elif type(o) == dict:
- hashed_keys = tuple([hash(k) for k, v in o.iteritems()])
- hashed_vals = tuple([hash(v) for k, v in o.iteritems()])
-
- if "NotHashable" in hashed_keys or "NotHashable" in hashed_vals:
- return "NotHashable"
-
- return tuple(zip(hashed_keys, hashed_vals))
-
- elif type(o) == Machine:
- return o.monkey_guid
-
-# elif type(o) == PthMap:
-# return "PthMapSingleton"
-
- elif type(o) == PassTheHashReport:
- return "PassTheHashReportSingleton"
-
- else:
- assert False, "%s of type %s is not hashable" % (repr(o), type(o))
- return "NotHashable"
-
- def wrapper(*args, **kwargs):
- hashed = (hash(args), hash(kwargs))
-
- if "NotHashable" in hashed:
- return foo(*args, **kwargs)
-
- if not hasattr(foo, "_mycache_"):
- foo._mycache_ = dict()
-
- if hashed not in foo._mycache_.keys():
- foo._mycache_[hashed] = foo(*args, **kwargs)
-
- return copy.deepcopy(foo._mycache_[hashed])
-
- return wrapper
-
-class Machine(object):
- def __init__(self, monkey_guid):
- self.monkey_guid = str(monkey_guid)
-
- self.latest_system_info = mongo.db.telemetry.find({"telem_type":"system_info_collection", "monkey_guid": self.monkey_guid}).sort([("timestamp", -1)]).limit(1)
-
- if self.latest_system_info.count() > 0:
- self.latest_system_info = self.latest_system_info[0]
-
- def __eq__(self, other):
- if isinstance(other, self.__class__):
- return self.monkey_guid == other.monkey_guid
- else:
- return False
-
- @cache
- def GetMimikatzOutput(self):
- doc = self.latest_system_info
-
- if not doc:
- return None
-
- return doc["data"]["mimikatz"]
-
- @cache
- def GetHostName(self):
- doc = self.latest_system_info
-
- for comp in doc["data"]["Win32_ComputerSystem"]:
- return eval(comp["Name"])
-
- return None
-
- @cache
- def GetIp(self):
- doc = self.latest_system_info
-
- for addr in doc["data"]["network_info"]["networks"]:
- return str(addr["addr"])
-
- return None
-
- @cache
- def GetDomainName(self):
- doc = self.latest_system_info
-
- for comp in doc["data"]["Win32_ComputerSystem"]:
- return eval(comp["Domain"])
-
- return None
-
- @cache
- def GetDomainRole(self):
- doc = self.latest_system_info
-
- for comp in doc["data"]["Win32_ComputerSystem"]:
- return comp["DomainRole"]
-
- return None
-
- @cache
- def IsDomainController(self):
- return self.GetDomainRole() in (DsRole_RolePrimaryDomainController, DsRole_RoleBackupDomainController)
-
- @cache
- def GetSidByUsername(self, username, domain=None):
- doc = self.latest_system_info
-
- for user in doc["data"]["Win32_UserAccount"]:
- if eval(user["Name"]) != username:
- continue
-
- if user["SIDType"] != SidTypeUser:
- continue
-
- if domain and user["Domain"] != domain:
- continue
-
- return eval(user["SID"])
-
- if not self.IsDomainController():
- for dc in self.GetDomainControllers():
- sid = dc.GetSidByUsername(username)
-
- if sid != None:
- return sid
-
- return None
-
- @cache
- def GetUsernameBySid(self, sid):
- info = self.GetSidInfo(sid)
-
- if not info:
- return None
-
- return info["Domain"] + "\\" + info["Username"]
-
- @cache
- def GetSidInfo(self, sid):
- doc = self.latest_system_info
-
- for user in doc["data"]["Win32_UserAccount"]:
- if eval(user["SID"]) != sid:
- continue
-
- if user["SIDType"] != SidTypeUser:
- continue
-
- return { "Domain": eval(user["Domain"]),
- "Username": eval(user["Name"]),
- "Disabled": user["Disabled"] == "true",
- "PasswordRequired": user["PasswordRequired"] == "true",
- "PasswordExpires": user["PasswordExpires"] == "true", }
-
- if not self.IsDomainController():
- for dc in self.GetDomainControllers():
- domain = dc.GetSidInfo(sid)
-
- if domain != None:
- return domain
-
- return None
-
- @cache
- def GetCriticalServicesInstalled(self):
- def IsNameOfCriticalService(name):
- services = ("W3svc", "MSExchangeServiceHost", "MSSQLServer", "dns")
- services = map(str.lower, services)
-
- if not name:
- return False
-
- name = name.lower()
-
- return name in services
- #for ser in services:
- # if ser in name:
- # return True
-
- return False
-
- doc = self.latest_system_info
- found = []
-
- if self.IsDomainController():
- found.append("Domain Controller")
-
- for product in doc["data"]["Win32_Product"]:
- service_name = eval(product["Name"])
-
- if not IsNameOfCriticalService(service_name):
- continue
-
- found.append(service_name)
-
- for service in doc["data"]["Win32_Service"]:
- service_name = eval(service["Name"])
-
- if not IsNameOfCriticalService(service_name):
- continue
-
- if eval(service["State"]) != "Running":
- continue
-
- found.append(service_name)
-
- return found
-
- @cache
- def IsCriticalServer(self):
- return len(self.GetCriticalServicesInstalled()) > 0
-
- @cache
- def GetUsernamesBySecret(self, secret):
- sam = self.GetLocalSecrets()
-
- names = set()
-
- for username, user_secret in sam.iteritems():
- if secret == user_secret:
- names.add(username)
-
- return names
-
- @cache
- def GetSidsBySecret(self, secret):
- usernames = self.GetUsernamesBySecret(secret)
- return set(map(self.GetSidByUsername, usernames))
-
- @cache
- def GetGroupSidByGroupName(self, group_name):
- doc = self.latest_system_info
-
- for group in doc["data"]["Win32_Group"]:
- if eval(group["Name"]) != group_name:
- continue
-
- if not is_group_sid_type(group["SIDType"]):
- continue
-
- return eval(group["SID"])
-
- return None
-
- @cache
- def GetUsersByGroupSid(self, sid):
- doc = self.latest_system_info
-
- users = dict()
-
- for group_user in doc["data"]["Win32_GroupUser"]:
- if eval(group_user["GroupComponent"]["SID"]) != sid:
- continue
-
- if not is_group_sid_type(group_user["GroupComponent"]["SIDType"]):
- continue
-
- if "PartComponent" not in group_user.keys():
- continue
-
- if type(group_user["PartComponent"]) in (str, unicode):
- # PartComponent is an id to Win32_UserAccount table
-
- wmi_id = group_user["PartComponent"]
-
- if "cimv2:Win32_UserAccount" not in wmi_id:
- continue
-
- # u'\\\\WIN-BFA01FFQFLS\\root\\cimv2:Win32_UserAccount.Domain="MYDOMAIN",Name="WIN-BFA01FFQFLS$"'
- username = wmi_id.split('cimv2:Win32_UserAccount.Domain="')[1].split('",Name="')[0]
- domain = wmi_id.split('cimv2:Win32_UserAccount.Domain="')[1].split('",Name="')[1][:-1]
-
- sid = self.GetSidByUsername(username, domain)
- users[sid] = username
-
- else:
- if group_user["PartComponent"]["SIDType"] != SidTypeUser:
- continue
-
- users[eval(group_user["PartComponent"]["SID"])] = eval(group_user["PartComponent"]["Name"])
-
- return users
-
- @cache
- def GetDomainControllersMonkeyGuidByDomainName(self, domain_name):
- cur = mongo.db.telemetry.find({"telem_type":"system_info_collection", "data.Win32_ComputerSystem.Domain":"u'%s'" % (domain_name,)})
-
- GUIDs = set()
-
- for doc in cur:
- if not Machine(doc["monkey_guid"]).IsDomainController():
- continue
-
- GUIDs.add(doc["monkey_guid"])
-
- return GUIDs
-
- @cache
- def GetLocalAdmins(self):
- admins = self.GetUsersByGroupSid(self.GetGroupSidByGroupName("Administrators"))
-
- #debug = self.GetUsersByGroupSid(self.GetGroupSidByGroupName("Users"))
- #admins.update(debug)
-
- return admins
-
- @cache
- def GetLocalAdminSids(self):
- return set(self.GetLocalAdmins().keys())
-
- @cache
- def GetLocalSids(self):
- doc = self.latest_system_info
-
- SIDs = set()
-
- for user in doc["data"]["Win32_UserAccount"]:
- if user["SIDType"] != SidTypeUser:
- continue
-
- SIDs.add(eval(user["SID"]))
-
- return SIDs
-
- @cache
- def GetLocalAdminNames(self):
- return set(self.GetLocalAdmins().values())
-
- @cache
- def GetSam(self):
- if not self.GetMimikatzOutput():
- return {}
-
- mimikatz = self.GetMimikatzOutput()
-
- if mimikatz.count("\n42.") != 2:
- return {}
-
- try:
- sam_users = mimikatz.split("\n42.")[1].split("\nSAMKey :")[1].split("\n\n")[1:]
-
- sam = {}
-
- for sam_user_txt in sam_users:
- sam_user = dict([map(unicode.strip, line.split(":")) for line in filter(lambda l: l.count(":") == 1, sam_user_txt.splitlines())])
-
- ntlm = sam_user["NTLM"]
- if "[hashed secret]" not in ntlm:
- continue
-
- sam[sam_user["User"]] = ntlm.replace("[hashed secret]", "").strip()
-
- return sam
-
- except:
- return {}
-
- @cache
- def GetNtds(self):
- if not self.GetMimikatzOutput():
- return {}
-
- mimikatz = self.GetMimikatzOutput()
-
- if mimikatz.count("\n42.") != 2:
- return {}
-
- ntds_users = mimikatz.split("\n42.")[2].split("\nRID :")[1:]
- ntds = {}
-
- for ntds_user_txt in ntds_users:
- user = ntds_user_txt.split("User :")[1].splitlines()[0].replace("User :", "").strip()
- ntlm = ntds_user_txt.split("* Primary\n NTLM :")[1].splitlines()[0].replace("NTLM :", "").strip()
- ntlm = ntlm.replace("[hashed secret]", "").strip()
-
- if ntlm:
- ntds[user] = ntlm
-
- return ntds
-
- @cache
- def GetLocalSecrets(self):
- sam = self.GetSam()
- ntds = self.GetNtds()
-
- secrets = sam.copy()
- secrets.update(ntds)
-
- return secrets
-
- @cache
- def GetLocalAdminSecrets(self):
- return set(self.GetLocalAdminCreds().values())
-
- @cache
- def GetLocalAdminCreds(self):
- admin_names = self.GetLocalAdminNames()
- sam = self.GetLocalSecrets()
-
- admin_creds = dict()
-
- for username, secret in sam.iteritems():
- if username not in admin_names:
- continue
-
- admin_creds[username] = secret
-
- return admin_creds
-
- @cache
- def GetCachedSecrets(self):
- return set(self.GetCachedCreds().values())
-
- @cache
- def GetCachedCreds(self):
- doc = self.latest_system_info
-
- creds = dict()
-
- if not self.GetMimikatzOutput():
- return {}
-
- mimikatz = self.GetMimikatzOutput()
-
- for user in mimikatz.split("\n42.")[0].split("Authentication Id")[1:]:
- username = None
- secret = None
-
- for line in user.splitlines():
- if "User Name" in line:
- username = line.split(":")[1].strip()
-
- if ("NTLM" in line or "Password" in line) and "[hashed secret]" in line:
- secret = line.split(":")[1].replace("[hashed secret]", "").strip()
-
- if username and secret:
- creds[username] = secret
-
- return creds
-
- @cache
- def GetDomainControllers(self):
- domain_name = self.GetDomainName()
- DCs = self.GetDomainControllersMonkeyGuidByDomainName(domain_name)
- return map(Machine, DCs)
-
- @cache
- def GetDomainAdminsOfMachine(self):
- DCs = self.GetDomainControllers()
-
- domain_admins = set()
-
- for dc in DCs:
- domain_admins |= set(dc.GetUsersByGroupSid(self.GetGroupSidByGroupName("Domain Admins")).keys())
-
- return domain_admins
-
- @cache
- def GetAdmins(self):
- return self.GetLocalAdminSids() | self.GetDomainAdminsOfMachine()
-
- @cache
- def GetAdminNames(self):
- return set(map(lambda x: self.GetUsernameBySid(x), self.GetAdmins()))
-
- @cache
- def GetCachedSids(self):
- doc = self.latest_system_info
-
- SIDs = set()
-
- for username in doc["data"]["credentials"]:
- sid = self.GetSidByUsername(username)
-
- if not sid:
- sid = "__USERNAME__" + username
-
- SIDs.add(sid)
-
- return SIDs
-
- @cache
- def GetCachedUsernames(self):
- doc = self.latest_system_info
-
- names = set()
-
- for username in doc["data"]["credentials"]:
- names.add(username)
-
- return names
-
-class PassTheHashReport(object):
- #_instance = None
- #def __new__(class_, *args, **kwargs):
- # if not class_._instance:
- # class_._instance = object.__new__(class_, *args, **kwargs)
- #
- # return class_._instance
-
- def __init__(self):
- self.vertices = self.GetAllMachines()
-
- self.machines = map(Machine, self.vertices)
-
- self.edges = set()
- self.edges |= self.GetEdgesBySid() # 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"})
-
- GUIDs = set()
-
- for doc in cur:
- GUIDs.add(doc["monkey_guid"])
-
- return GUIDs
-
- @cache
- def ReprSidList(self, sid_list, attacker, victim):
- label = set()
-
- for sid in sid_list:
- username = Machine(victim).GetUsernameBySid(sid)
-
- #if not username:
- # username = Machine(attacker).GetUsernameBySid(sid)
-
- if username:
- label.add(username)
-
- return ",\n".join(label)
-
- @cache
- def ReprSecretList(self, secret_list, victim):
- label = set()
-
- for secret in secret_list:
- label |= Machine(victim).GetUsernamesBySecret(secret)
-
- return ",\n".join(label)
-
- @cache
- def GetEdgesBySid(self):
- edges = set()
-
- for attacker in self.vertices:
- cached = self.GetCachedSids(Machine(attacker))
-
- for victim in self.vertices:
- if attacker == victim:
- continue
-
- admins = Machine(victim).GetAdmins()
-
- if len(cached & admins) > 0:
- label = self.ReprSidList(cached & admins, attacker, victim)
- edges.add((attacker, victim, label))
-
- return edges
-
- @cache
- def GetEdgesBySamHash(self):
- edges = set()
-
- for attacker in self.vertices:
- cached_creds = set(Machine(attacker).GetCachedCreds().items())
-
- for victim in self.vertices:
- if attacker == victim:
- continue
-
- admin_creds = set(Machine(victim).GetLocalAdminCreds().items())
-
- if len(cached_creds & admin_creds) > 0:
- label = self.ReprSecretList(set(dict(cached_creds & admin_creds).values()), victim)
- edges.add((attacker, victim, label))
-
- return edges
-
- @cache
- def GetEdgesByUsername(self):
- edges = set()
-
- for attacker in self.vertices:
- cached = Machine(attacker).GetCachedUsernames()
-
- for victim in self.vertices:
- if attacker == victim:
- continue
-
- admins = Machine(victim).GetAdminNames()
-
- if len(cached & admins) > 0:
- edges.add((attacker, victim))
-
- return edges
-
- @cache
- def Print(self):
- print map(lambda x: Machine(x).GetIp(), self.vertices)
- print map(lambda x: (Machine(x[0]).GetIp(), Machine(x[1]).GetIp()), self.edges)
-
- @cache
- def GetPossibleAttackCountBySid(self, sid):
- return len(self.GetPossibleAttacksBySid(sid))
-
- @cache
- def GetPossibleAttacksByAttacker(self, attacker):
- attacks = set()
-
- cached_creds = set(Machine(attacker).GetCachedCreds().items())
-
- for victim in self.vertices:
- if attacker == victim:
- continue
-
- admin_creds = set(Machine(victim).GetLocalAdminCreds().items())
-
- if len(cached_creds & admin_creds) > 0:
- curr_attacks = dict(cached_creds & admin_creds)
- attacks.add((attacker, victim, curr_attacks))
-
- return attacks
-
- @cache
- def GetPossibleAttacksBySid(self, sid):
- attacks = set()
-
- for attacker in self.vertices:
- tmp = self.GetPossibleAttacksByAttacker(attacker)
-
- for _, victim, curr_attacks in tmp:
- for username, secret in curr_attacks.iteritems():
- if Machine(victim).GetSidByUsername(username) == sid:
- attacks.add((attacker, victim))
-
- return attacks
-
- @cache
- def GetSecretBySid(self, sid):
- for m in self.machines:
- for user, user_secret in m.GetLocalSecrets().iteritems():
- if m.GetSidByUsername(user) == sid:
- return user_secret
-
- return None
-
- @cache
- def GetVictimCountBySid(self, sid):
- return len(self.GetVictimsBySid(sid))
-
- @cache
- def GetVictimCountByMachine(self, attacker):
- return len(self.GetVictimsByAttacker(attacker))
-
- @cache
- def GetAttackCountBySecret(self, secret):
- return len(self.GetAttackersBySecret(secret))
-
- @cache
- def GetAllUsernames(self):
- names = set()
-
- for sid in self.GetAllSids():
- names.add(self.GetUsernameBySid(sid))
-
- return names
-
- @cache
- def GetAllSids(self):
- SIDs = set()
-
- for m in self.machines:
- SIDs |= m.GetLocalSids()
-
- return SIDs
-
- @cache
- def GetAllSecrets(self):
- secrets = set()
-
- for m in self.machines:
- for secret in m.GetLocalAdminSecrets():
- secrets.add(secret)
-
- return secrets
-
- @cache
- def GetUsernameBySid(self, sid):
- for m in self.machines:
- username = m.GetUsernameBySid(sid)
-
- if username:
- return username
-
- return None
-
- @cache
- def GetSidInfo(self, sid):
- for m in self.machines:
- info = m.GetSidInfo(sid)
-
- if info:
- return info
-
- return None
-
- @cache
- def GetSidsBySecret(self, secret):
- SIDs = set()
-
- for m in self.machines:
- SIDs |= m.GetSidsBySecret(secret)
-
- return SIDs
-
- @cache
- def GetAllDomainControllers(self):
- DCs = set()
-
- for m in self.machines:
- if m.IsDomainController():
- DCs.add(m)
-
- return DCs
-
- @cache
- def GetSidsByUsername(self, username):
- SIDs = set()
-
- for m in self.machines:
- sid = m.GetSidByUsername(username)
- if sid:
- SIDs.add(sid)
-
- return SIDs
-
- @cache
- def GetVictimsBySid(self, sid):
- machines = set()
-
- for m in self.machines:
- if sid in m.GetAdmins():
- machines.add(m)
-
- return machines
-
- @cache
- def GetVictimsBySecret(self, secret):
- machines = set()
-
- SIDs = self.GetSidsBySecret(secret)
-
- for m in self.machines:
- if len(SIDs & m.GetAdmins()) > 0:
- machines.add(m)
-
- return machines
-
- @cache
- def GetAttackersBySecret(self, secret):
- machines = set()
-
- for m in self.machines:
- if secret in m.GetCachedSecrets():
- machines.add(m)
-
- return machines
-
- @cache
- def GetAttackersByVictim(self, victim):
- if type(victim) != unicode:
- victim = victim.monkey_guid
-
- attackers = set()
-
- for atck, vic, _ in self.edges:
- if vic == victim:
- attackers.add(atck)
-
- return set(map(Machine, attackers))
-
- @cache
- def GetAttackersBySid(self, sid):
- machines = set()
-
- for m in self.machines:
- if sid in self.GetCachedSids(m):
- machines.add(m)
-
- return machines
-
- @cache
- def GetVictimsByAttacker(self, attacker):
- if type(attacker) != unicode:
- attacker = attacker.monkey_guid
-
- victims = set()
-
- for atck, vic, _ in self.edges:
- if atck == attacker:
- victims.add(vic)
-
- return set(map(Machine, victims))
-
- @cache
- def GetInPathCountByVictim(self, victim, already_processed=None):
- if type(victim) != unicode:
- victim = victim.monkey_guid
-
- if not already_processed:
- already_processed = set([victim])
-
- count = 0
-
- for atck, vic, _ in self.edges:
- if atck == vic:
- continue
-
- if vic != victim:
- continue
-
- if atck in already_processed:
- continue
-
- count += 1
-
- already_processed.add(atck)
- count += self.GetInPathCountByVictim(atck, already_processed)
-
- return count
-
- @cache
- def GetCritialServers(self):
- machines = set()
-
- for m in self.machines:
- if m.IsCriticalServer():
- machines.add(m)
-
- return machines
-
- @cache
- def GetNonCritialServers(self):
- return set(self.machines) - self.GetCritialServers()
-
- @cache
- def GetCachedSids(self, m):
- sids = set()
- tmp = m.GetCachedSids()
-
- for sid in tmp:
- if sid.startswith("__USERNAME__"):
-
- s = self.GetSidsByUsername(sid[len("__USERNAME__"):])
- if len(s) == 1:
- sids.add(s.pop())
- else:
- sids.add(sid)
-
- else:
- sids.add(sid)
-
- return sids
-
- @cache
- def GetThreateningUsersByVictim(self, victim):
- threatening_users = set()
-
- for attacker in self.GetAttackersByVictim(victim):
- threatening_users |= (self.GetCachedSids(attacker) & victim.GetAdmins())
-
- return threatening_users
-
- @cache
- def GetSharedAdmins(self, m):
- shared_admins = set()
-
- for other in self.machines:
- if m == other:
- continue
-
- shared_admins |= (m.GetLocalAdminSids() & other.GetLocalAdminSids())
-
- shared_admins -= m.GetDomainAdminsOfMachine()
- return shared_admins
-
-def main():
- pth = PassTheHashReport()
-
- print """"""
- print "
Pass The Hash Report
"
-
- print "Duplicated Passwords
"
- print "How many users share each secret?
"
-
- dups = dict(map(lambda x: (x, len(pth.GetSidsBySecret(x))), pth.GetAllSecrets()))
-
- print """"""
- print """User Count | Users That Share This Password |
"""
- for secret, count in sorted(dups.iteritems(), key=lambda (k,v): (v,k), reverse=True):
- if count <= 1:
- continue
- print """{count} | """.format(secret=secret, count=count)
- print """"""
- for sid in pth.GetSidsBySecret(secret):
- print """- {username}
""".format(sid=sid, username=pth.GetUsernameBySid(sid))
- print """ |
"""
- print """
"""
-
-
- print "Local Admin Uniqueness
"
- print "We argue that each machine should have it's own distinct set of local admins
"
-
- dups = dict(map(lambda x: (x, len(pth.GetSharedAdmins(x))), pth.machines))
-
- print """"""
- print """Ip | Hostname | Domain | Critical Services Installed | Shared User Count | Shared Users |
"""
- for m, count in sorted(dups.iteritems(), key=lambda (k,v): (v,k), reverse=True):
- if count <= 0:
- continue
-
- print """{ip} | {hostname} | {domain} | """.format(ip=m.GetIp(), hostname=m.GetHostName(), domain=m.GetDomainName(), count=count)
-
- print """"""
- for service_name in m.GetCriticalServicesInstalled():
- print """- {service_name}
""".format(service_name=service_name)
- print """ | """
-
- print """{count} | """.format(count=count)
-
- print """"""
-
- for sid in pth.GetSharedAdmins(m):
- print """- {username}
""".format(sid=sid, username=pth.GetUsernameBySid(sid))
-
- print """ |
"""
- print """
"""
-
- print "Strong Users That Threat Critical Servers
"
- print "Administrators of critical servers that we could find thier secret cached somewhere
"
-
- threatening = dict(map(lambda x: (x, len(pth.GetThreateningUsersByVictim(x))), pth.GetCritialServers()))
-
- print """"""
- print """Critical Server | Hostname | Domain | Critical Services Installed | Threatening User Count | Threatening Users |
"""
- for m, count in sorted(threatening.iteritems(), key=lambda (k,v): (v,k), reverse=True):
- if count <= 0:
- continue
- print """{ip} | {hostname} | {domain} | """.format(ip=m.GetIp(), hostname=m.GetHostName(), domain=m.GetDomainName(), count=count)
-
- print """"""
- for service_name in m.GetCriticalServicesInstalled():
- print """- {service_name}
""".format(service_name=service_name)
- print """ | """
-
- print """{count} | """.format(count=count)
-
- print """"""
-
- for sid in pth.GetThreateningUsersByVictim(m):
- print """- {username} attackers:
""".format(sid=sid, username=pth.GetUsernameBySid(sid))
-
- for mm in pth.GetAttackersBySid(sid):
- if m == mm:
- continue
- print """- {ip}
""".format(ip=mm.GetIp())
- print """ """
-
- print """ |
"""
- print """
"""
-
-
- print "Strong Users That Threat NonCritical Servers
"
- print "Administrators of non-critical servers that we could find thier secret cached somewhere
"
-
- threatening = dict(map(lambda x: (x, len(pth.GetThreateningUsersByVictim(x))), pth.GetNonCritialServers()))
-
- print """"""
- print """Critical Server | Hostname | Domain | Threatening User Count | Threatening Users |
"""
- for m, count in sorted(threatening.iteritems(), key=lambda (k,v): (v,k), reverse=True):
- if count <= 0:
- continue
- print """{ip} | {hostname} | {domain} | """.format(ip=m.GetIp(), hostname=m.GetHostName(), domain=m.GetDomainName(), count=count)
-
-
- print """{count} | """.format(count=count)
-
- print """"""
-
- for sid in pth.GetThreateningUsersByVictim(m):
- print """- {username} attackers:
""".format(sid=sid, username=pth.GetUsernameBySid(sid))
-
- for mm in pth.GetAttackersBySid(sid):
- if m == mm:
- continue
- print """- {ip}
""".format(ip=mm.GetIp())
- print """ """
-
- print """ |
"""
- print """
"""
-
- print """"""
-
- print "
Cached Passwords
"
- print "
On how many machines each secret is cached (possible attacker count)?
"
- cache_counts = dict(map(lambda x: (x, pth.GetAttackCountBySecret(x)), pth.GetAllSecrets()))
-
- print """
"""
- print """Secret | Machine Count |
"""
- for secret, count in sorted(cache_counts.iteritems(), key=lambda (k,v): (v,k), reverse=True):
- if count <= 0:
- continue
- print """{secret} | {count} | """.format(secret=secret, count=count)
- print """
"""
-
- print "
User's Creds
"
- print "
To how many machines each user is able to connect with admin rights
"
- attackable_counts = dict(map(lambda x: (x, pth.GetVictimCountBySid(x)), pth.GetAllSids()))
-
- print """
"""
- print """SID | Username | Machine Count |
"""
- for sid, count in sorted(attackable_counts.iteritems(), key=lambda (k,v): (v,k), reverse=True):
- if count <= 1:
- continue
- print """{sid} | {username} | {count} | """.format(sid=sid, username=pth.GetUsernameBySid(sid), count=count)
- print """
"""
-
- print "
Actual Possible Attacks By SID
"
- print "
How many attacks possible using each SID (aka len(attacker->victim pairs))
"
- possible_attacks_by_sid = dict(map(lambda x: (x, pth.GetPossibleAttackCountBySid(x)), pth.GetAllSids()))
-
- print """
"""
- print """SID | Username | Machine Count |
"""
- for sid, count in sorted(possible_attacks_by_sid.iteritems(), key=lambda (k,v): (v,k), reverse=True):
- if count <= 1:
- continue
- print """{sid} | {username} | {count} | """.format(sid=sid, username=pth.GetUsernameBySid(sid), count=count)
- print """
"""
-
- print "
Machine's Creds
"
- print "
To how many machines each machine is able to directly connect with admin rights?
"
- attackable_counts = dict(map(lambda m: (m, pth.GetVictimCountByMachine(m)), pth.machines))
-
- print """
"""
- print """Attacker Ip | Attacker Hostname | Domain Name | Victim Machine Count |
"""
- for m, count in sorted(attackable_counts.iteritems(), key=lambda (k,v): (v,k), reverse=True):
- if count <= 1:
- continue
- print """{ip} | {hostname} | {domain} | {count} | """.format(ip=m.GetIp(), hostname=m.GetHostName(), domain=m.GetDomainName(), count=count)
- print """
"""
-
- print "
Domain Controllers
"
- print "
List of domain controllers (we count them as critical points, so they are listed here)
"
- DCs = dict(map(lambda m: (m, pth.GetInPathCountByVictim(m)), pth.GetAllDomainControllers()))
-
- print """
"""
- print """DC Ip | DC Hostname | Domain Name | In-Path Count |
"""
- for m, path_count in sorted(DCs.iteritems(), key=lambda (k,v): (v,k), reverse=True):
- print """{ip} | {hostname} | {domain} | {path_count} |
""".format(ip=m.GetIp(), hostname=m.GetHostName(), domain=m.GetDomainName(), path_count=path_count)
- print """
"""
-
- print "
Most Vulnerable Machines
"
- print "
List all machines in the network sorted by the potincial to attack them
"
- all_machines = dict(map(lambda m: (m, pth.GetInPathCountByVictim(m)), pth.machines))
-
- print """
"""
- print """Ip | Hostname | Domain Name | In-Path Count |
"""
- for m, path_count in sorted(all_machines.iteritems(), key=lambda (k,v): (v,k), reverse=True):
- if count <= 0:
- continue
- print """{ip} | {hostname} | {domain} | {path_count} |
""".format(ip=m.GetIp(), hostname=m.GetHostName(), domain=m.GetDomainName(), path_count=path_count)
- print """
"""
-
- print "
Critical Servers
"
- print "
List of all machines identified as critical servers
"
- critical_servers = pth.GetCritialServers()
-
- print """
"""
- print """Ip | Hostname | Domain Name |
"""
- for m in critical_servers:
- print """{ip} | {hostname} | {domain} |
""".format(ip=m.GetIp(), hostname=m.GetHostName(), domain=m.GetDomainName())
- print """
"""
-
- print "
"
-
- for m in pth.machines:
- print """
Machine '{ip}'
-
Hostname '{hostname}'
""".format(ip=m.GetIp(), hostname=m.GetHostName())
-
- print """
Cached SIDs
"""
- print """
SIDs cached on this machine
"""
- print """
"""
- for sid in pth.GetCachedSids(m):
- print """- {username} ({sid})
""".format(username=pth.GetUsernameBySid(sid), sid=sid)
- print """
"""
-
- print """
Possible Attackers
"""
- print """
Machines that can attack this machine
"""
- print """
"""
- for attacker in pth.GetAttackersByVictim(m):
- print """- {ip} ({hostname})
""".format(ip=attacker.GetIp(), hostname=attacker.GetHostName())
- print """
"""
-
-
- print """
Admins
"""
- print """
Users that have admin rights on this machine
"""
- print """
"""
- for sid in m.GetAdmins():
- print """- {username} ({sid})
""".format(username=m.GetUsernameBySid(sid), sid=sid)
- print """
"""
-
- print """
Installed Critical Services
"""
- print """
List of crtical services found installed on machine
"""
- print """
"""
- for service_name in m.GetCriticalServicesInstalled():
- print """- {service_name}
""".format(service_name=service_name)
- print """
"""
-
-
-
- print "
"
-
- for username in pth.GetAllUsernames():
- print """
User '{username}'
""".format(username=username)
-
- print """
Matching SIDs
"""
- print """
"""
- for sid in pth.GetSidsByUsername(username):
- print """- {username} ({sid})
""".format(username=pth.GetUsernameBySid(sid), sid=sid)
- print """
"""
-
- print "
"
-
- for sid in pth.GetAllSids():
- print """
SID '{sid}'
-
-
Domain: {domain}
-
- """.format(username=pth.GetUsernameBySid(sid), sid=sid, secret=pth.GetSecretBySid(sid), domain=pth.GetSidInfo(sid)["Domain"])
-
- print """
Machines the sid is local admin on
"""
- print """
"""
- for m in pth.GetVictimsBySid(sid):
- print """- {ip} ({hostname})
""".format(ip=m.GetIp(), hostname=m.GetHostName())
- print """
"""
-
- print """
Machines the sid is in thier cache
"""
- print """
"""
- for m in pth.GetAttackersBySid(sid):
- print """- {ip} ({hostname})
""".format(ip=m.GetIp(), hostname=m.GetHostName())
- print """
"""
-
- for secret in pth.GetAllSecrets():
- print """
Secret '{secret}'
""".format(secret=secret)
-
- print """
SIDs that use that secret
"""
- print """
"""
- for sid in pth.GetSidsBySecret(secret):
- print """- {username} ({sid})
""".format(username=pth.GetUsernameBySid(sid), sid=sid)
- print """
"""
-
- print """
Attackable Machines with that secret
"""
- print """
"""
- for m in pth.GetVictimsBySecret(secret):
- print """- {hostname}
""".format(ip=m.GetIp(), hostname=m.GetHostName())
- print """
"""
-
- print """
Machines that have this secret cached and can use it to attack other machines
"""
- print """
"""
- for m in pth.GetAttackersBySecret(secret):
- print """- {hostname}
""".format(ip=m.GetIp(), hostname=m.GetHostName())
- print """
"""
-
- print """
"""
-if __name__ == "__main__":
- main()
-
-from cStringIO import StringIO
-import sys
-
-class Capturing(list):
- def __enter__(self):
- self._stdout = sys.stdout
- sys.stdout = self._stringio = StringIO()
- return self
- def __exit__(self, *args):
- self.extend(self._stringio.getvalue().splitlines())
- del self._stringio # free up some memory
- sys.stdout = self._stdout
-
-def get_report_html():
- with Capturing() as output:
- main()
-
- return "\n".join(output)
\ No newline at end of file
+ @jwt_required()
+ def get(self):
+ return PTHReportService.get_report()
diff --git a/monkey_island/cc/resources/pthreporthtml.py b/monkey_island/cc/resources/pthreporthtml.py
deleted file mode 100644
index 8aa10870f..000000000
--- a/monkey_island/cc/resources/pthreporthtml.py
+++ /dev/null
@@ -1,21 +0,0 @@
-import hashlib
-import binascii
-import copy
-import flask_restful
-from pthreport import PassTheHashReport, Machine, get_report_html
-
-from cc.auth import jwt_required
-from cc.services.edge import EdgeService
-from cc.services.node import NodeService
-from cc.database import mongo
-
-class PthReportHtml(flask_restful.Resource):
- @jwt_required()
- def get(self, **kw):
- pth = PassTheHashReport()
- html = get_report_html()
-
- return \
- {
- "html": html
- }
diff --git a/monkey_island/cc/services/pth_report.py b/monkey_island/cc/services/pth_report.py
new file mode 100644
index 000000000..3f01839f3
--- /dev/null
+++ b/monkey_island/cc/services/pth_report.py
@@ -0,0 +1,302 @@
+from cc.services.pth_report_utils import PassTheHashReport
+
+
+class PTHReportService(object):
+
+ def __init__(self):
+ pass
+
+ @staticmethod
+ def get_duplicated_password_nodes(pth):
+
+ usernames_lists = []
+ usernames_per_sid_list = []
+ dups = dict(map(lambda x: (x, len(pth.GetSidsBySecret(x))), pth.GetAllSecrets()))
+
+ for secret, count in sorted(dups.iteritems(), key=lambda (k, v): (v, k), reverse=True):
+ if count <= 1:
+ continue
+ for sid in pth.GetSidsBySecret(secret):
+ usernames_per_sid_list.append(pth.GetUsernameBySid(sid))
+ usernames_lists.append(usernames_per_sid_list)
+
+ return usernames_lists
+
+ @staticmethod
+ def get_shared_local_admins_nodes(pth):
+ dups = dict(map(lambda x: (x, len(pth.GetSharedAdmins(x))), pth.machines))
+ shared_admin_machines = []
+ for m, count in sorted(dups.iteritems(), key=lambda (k, v): (v, k), reverse=True):
+ if count <= 0:
+ continue
+ shared_admin_account_list = []
+
+ for sid in pth.GetSharedAdmins(m):
+ shared_admin_account_list.append(pth.GetUsernameBySid(sid))
+
+ machine = {
+ 'ip': m.GetIp(),
+ 'hostname': m.GetHostName(),
+ 'domain': m.GetDomainName(),
+ 'services_names': m.GetCriticalServicesInstalled(),
+ 'user_count': count,
+ 'admins_accounts': shared_admin_account_list
+ }
+
+ shared_admin_machines.append(machine)
+
+ return shared_admin_machines
+
+ @staticmethod
+ def get_strong_users_on_crit_services(pth):
+ threatening = dict(map(lambda x: (x, len(pth.GetThreateningUsersByVictim(x))), pth.GetCritialServers()))
+ strong_users_crit_list = []
+
+ for m, count in sorted(threatening.iteritems(), key=lambda (k, v): (v, k), reverse=True):
+ if count <= 0:
+ continue
+
+ threatening_users_attackers_dict = {}
+ for sid in pth.GetThreateningUsersByVictim(m):
+ username = pth.GetUsernameBySid(sid)
+ threatening_users_attackers_dict[username] = []
+ for mm in pth.GetAttackersBySid(sid):
+ if m == mm:
+ continue
+ threatening_users_attackers_dict[username] = mm.GetIp()
+
+ machine = {
+ 'ip': m.GetIp(),
+ 'hostname': m.GetHostName(),
+ 'domain': m.GetDomainName(),
+ 'services_names': m.GetCriticalServicesInstalled(),
+ 'user_count': count,
+ 'threatening_users': threatening_users_attackers_dict
+ }
+ strong_users_crit_list.append(machine)
+ return strong_users_crit_list
+
+ @staticmethod
+ def get_strong_users_on_non_crit_services(pth):
+ threatening = dict(map(lambda x: (x, len(pth.GetThreateningUsersByVictim(x))), pth.GetNonCritialServers()))
+
+ strong_users_non_crit_list = []
+
+ for m, count in sorted(threatening.iteritems(), key=lambda (k, v): (v, k), reverse=True):
+ if count <= 0:
+ continue
+
+ threatening_users_attackers_dict = {}
+ for sid in pth.GetThreateningUsersByVictim(m):
+ username = pth.GetUsernameBySid(sid)
+ threatening_users_attackers_dict[username] = []
+ for mm in pth.GetAttackersBySid(sid):
+ if m == mm:
+ continue
+ threatening_users_attackers_dict[username] = mm.GetIp()
+
+ machine = {
+ 'ip': m.GetIp(),
+ 'hostname': m.GetHostName(),
+ 'domain': m.GetDomainName(),
+ 'services_names': m.GetNonCritialServers(),
+ '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_report():
+ pth = PassTheHashReport()
+
+ 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)
+ },
+ 'map':
+ {
+
+ }
+ }
+ return report
+
+ # print """"""
+ #
+ # print "
Cached Passwords
"
+ # print "
On how many machines each secret is cached (possible attacker count)?
"
+ # cache_counts = dict(map(lambda x: (x, pth.GetAttackCountBySecret(x)), pth.GetAllSecrets()))
+ #
+ # print """
"""
+ # print """Secret | Machine Count |
"""
+ # for secret, count in sorted(cache_counts.iteritems(), key=lambda (k,v): (v,k), reverse=True):
+ # if count <= 0:
+ # continue
+ # print """{secret} | {count} | """.format(secret=secret, count=count)
+ # print """
"""
+ #
+ # print "
User's Creds
"
+ # print "
To how many machines each user is able to connect with admin rights
"
+ # attackable_counts = dict(map(lambda x: (x, pth.GetVictimCountBySid(x)), pth.GetAllSids()))
+ #
+ # print """
"""
+ # print """SID | Username | Machine Count |
"""
+ # for sid, count in sorted(attackable_counts.iteritems(), key=lambda (k,v): (v,k), reverse=True):
+ # if count <= 1:
+ # continue
+ # print """{sid} | {username} | {count} | """.format(sid=sid, username=pth.GetUsernameBySid(sid), count=count)
+ # print """
"""
+ #
+ # print "
Actual Possible Attacks By SID
"
+ # print "
How many attacks possible using each SID (aka len(attacker->victim pairs))
"
+ # possible_attacks_by_sid = dict(map(lambda x: (x, pth.GetPossibleAttackCountBySid(x)), pth.GetAllSids()))
+ #
+ # print """
"""
+ # print """SID | Username | Machine Count |
"""
+ # for sid, count in sorted(possible_attacks_by_sid.iteritems(), key=lambda (k,v): (v,k), reverse=True):
+ # if count <= 1:
+ # continue
+ # print """{sid} | {username} | {count} | """.format(sid=sid, username=pth.GetUsernameBySid(sid), count=count)
+ # print """
"""
+ #
+ # print "
Machine's Creds
"
+ # print "
To how many machines each machine is able to directly connect with admin rights?
"
+ # attackable_counts = dict(map(lambda m: (m, pth.GetVictimCountByMachine(m)), pth.machines))
+ #
+ # print """
"""
+ # print """Attacker Ip | Attacker Hostname | Domain Name | Victim Machine Count |
"""
+ # for m, count in sorted(attackable_counts.iteritems(), key=lambda (k,v): (v,k), reverse=True):
+ # if count <= 1:
+ # continue
+ # print """{ip} | {hostname} | {domain} | {count} | """.format(ip=m.GetIp(), hostname=m.GetHostName(), domain=m.GetDomainName(), count=count)
+ # print """
"""
+ #
+ # print "
Domain Controllers
"
+ # print "
List of domain controllers (we count them as critical points, so they are listed here)
"
+ # DCs = dict(map(lambda m: (m, pth.GetInPathCountByVictim(m)), pth.GetAllDomainControllers()))
+ #
+ # print """
"""
+ # print """DC Ip | DC Hostname | Domain Name | In-Path Count |
"""
+ # for m, path_count in sorted(DCs.iteritems(), key=lambda (k,v): (v,k), reverse=True):
+ # print """{ip} | {hostname} | {domain} | {path_count} |
""".format(ip=m.GetIp(), hostname=m.GetHostName(), domain=m.GetDomainName(), path_count=path_count)
+ # print """
"""
+ #
+ # print "
Most Vulnerable Machines
"
+ # print "
List all machines in the network sorted by the potincial to attack them
"
+ # all_machines = dict(map(lambda m: (m, pth.GetInPathCountByVictim(m)), pth.machines))
+ #
+ # print """
"""
+ # print """Ip | Hostname | Domain Name | In-Path Count |
"""
+ # for m, path_count in sorted(all_machines.iteritems(), key=lambda (k,v): (v,k), reverse=True):
+ # if count <= 0:
+ # continue
+ # print """{ip} | {hostname} | {domain} | {path_count} |
""".format(ip=m.GetIp(), hostname=m.GetHostName(), domain=m.GetDomainName(), path_count=path_count)
+ # print """
"""
+ #
+ # print "
Critical Servers
"
+ # print "
List of all machines identified as critical servers
"
+ # critical_servers = pth.GetCritialServers()
+ #
+ # print """
"""
+ # print """Ip | Hostname | Domain Name |
"""
+ # for m in critical_servers:
+ # print """{ip} | {hostname} | {domain} |
""".format(ip=m.GetIp(), hostname=m.GetHostName(), domain=m.GetDomainName())
+ # print """
"""
+ #
+ # print "
"
+ #
+ # for m in pth.machines:
+ # print """
Machine '{ip}'
+ #
Hostname '{hostname}'
""".format(ip=m.GetIp(), hostname=m.GetHostName())
+ #
+ # print """
Cached SIDs
"""
+ # print """
SIDs cached on this machine
"""
+ # print """
"""
+ # for sid in pth.GetCachedSids(m):
+ # print """- {username} ({sid})
""".format(username=pth.GetUsernameBySid(sid), sid=sid)
+ # print """
"""
+ #
+ # print """
Possible Attackers
"""
+ # print """
Machines that can attack this machine
"""
+ # print """
"""
+ # for attacker in pth.GetAttackersByVictim(m):
+ # print """- {ip} ({hostname})
""".format(ip=attacker.GetIp(), hostname=attacker.GetHostName())
+ # print """
"""
+ #
+ #
+ # print """
Admins
"""
+ # print """
Users that have admin rights on this machine
"""
+ # print """
"""
+ # for sid in m.GetAdmins():
+ # print """- {username} ({sid})
""".format(username=m.GetUsernameBySid(sid), sid=sid)
+ # print """
"""
+ #
+ # print """
Installed Critical Services
"""
+ # print """
List of crtical services found installed on machine
"""
+ # print """
"""
+ # for service_name in m.GetCriticalServicesInstalled():
+ # print """- {service_name}
""".format(service_name=service_name)
+ # print """
"""
+ #
+ #
+ # print "
"
+ #
+ # for username in pth.GetAllUsernames():
+ # print """
User '{username}'
""".format(username=username)
+ #
+ # print """
Matching SIDs
"""
+ # print """
"""
+ # for sid in pth.GetSidsByUsername(username):
+ # print """- {username} ({sid})
""".format(username=pth.GetUsernameBySid(sid),
+ # sid=sid)
+ # print """
"""
+ #
+ # print "
"
+ #
+ # for sid in pth.GetAllSids():
+ # print """
SID '{sid}'
+ #
+ #
Domain: {domain}
+ #
+ # """.format(username=pth.GetUsernameBySid(sid), sid=sid, secret=pth.GetSecretBySid(sid),
+ # domain=pth.GetSidInfo(sid)["Domain"])
+ #
+ # print """
Machines the sid is local admin on
"""
+ # print """
"""
+ # for m in pth.GetVictimsBySid(sid):
+ # print """- {ip} ({hostname})
""".format(ip=m.GetIp(), hostname=m.GetHostName())
+ # print """
"""
+ #
+ # print """
Machines the sid is in thier cache
"""
+ # print """
"""
+ # for m in pth.GetAttackersBySid(sid):
+ # print """- {ip} ({hostname})
""".format(ip=m.GetIp(), hostname=m.GetHostName())
+ # print """
"""
+ #
+ # for secret in pth.GetAllSecrets():
+ # print """
Secret '{secret}'
""".format(secret=secret)
+ #
+ # print """
SIDs that use that secret
"""
+ # print """
"""
+ # for sid in pth.GetSidsBySecret(secret):
+ # print """- {username} ({sid})
""".format(username=pth.GetUsernameBySid(sid),
+ # sid=sid)
+ # print """
"""
+ #
+ # print """
Attackable Machines with that secret
"""
+ # print """
"""
+ # for m in pth.GetVictimsBySecret(secret):
+ # print """- {hostname}
""".format(ip=m.GetIp(), hostname=m.GetHostName())
+ # print """
"""
+ #
+ # print """
Machines that have this secret cached and can use it to attack other machines
"""
+ # print """
"""
+ # 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
new file mode 100644
index 000000000..463030f5a
--- /dev/null
+++ b/monkey_island/cc/services/pth_report_utils.py
@@ -0,0 +1,944 @@
+import hashlib
+import binascii
+import copy
+
+from cc.database import mongo
+
+DsRole_RoleStandaloneWorkstation = 0
+DsRole_RoleMemberWorkstation = 1
+DsRole_RoleStandaloneServer = 2
+DsRole_RoleMemberServer = 3
+DsRole_RoleBackupDomainController = 4
+DsRole_RolePrimaryDomainController = 5
+
+SidTypeUser = 1
+SidTypeGroup = 2
+SidTypeDomain = 3
+SidTypeAlias = 4
+SidTypeWellKnownGroup = 5
+SidTypeDeletedAccount = 6
+SidTypeInvalid = 7
+SidTypeUnknown = 8
+SidTypeComputer = 9
+
+
+
+def is_group_sid_type(type):
+ return type in (SidTypeGroup, SidTypeAlias, SidTypeWellKnownGroup)
+
+
+def myntlm(x):
+ hash = hashlib.new('md4', x.encode('utf-16le')).digest()
+ return str(binascii.hexlify(hash))
+
+
+def cache(foo):
+ def hash(o):
+ if type(o) in (int, float, str, unicode):
+ return repr(o)
+
+ elif type(o) in (type(None),):
+ return "___None___"
+
+ elif type(o) in (list, tuple, set):
+ hashed = tuple([hash(x) for x in o])
+
+ if "NotHashable" in hashed:
+ return "NotHashable"
+
+ return hashed
+
+ elif type(o) == dict:
+ hashed_keys = tuple([hash(k) for k, v in o.iteritems()])
+ hashed_vals = tuple([hash(v) for k, v in o.iteritems()])
+
+ if "NotHashable" in hashed_keys or "NotHashable" in hashed_vals:
+ return "NotHashable"
+
+ return tuple(zip(hashed_keys, hashed_vals))
+
+ elif type(o) == Machine:
+ return o.monkey_guid
+
+ # elif type(o) == PthMap:
+ # return "PthMapSingleton"
+
+ elif type(o) == PassTheHashReport:
+ return "PassTheHashReportSingleton"
+
+ else:
+ assert False, "%s of type %s is not hashable" % (repr(o), type(o))
+ return "NotHashable"
+
+ def wrapper(*args, **kwargs):
+ hashed = (hash(args), hash(kwargs))
+
+ if "NotHashable" in hashed:
+ return foo(*args, **kwargs)
+
+ if not hasattr(foo, "_mycache_"):
+ foo._mycache_ = dict()
+
+ if hashed not in foo._mycache_.keys():
+ foo._mycache_[hashed] = foo(*args, **kwargs)
+
+ return copy.deepcopy(foo._mycache_[hashed])
+
+ return wrapper
+
+
+class Machine(object):
+ def __init__(self, monkey_guid):
+ self.monkey_guid = str(monkey_guid)
+
+ self.latest_system_info = mongo.db.telemetry.find(
+ {"telem_type": "system_info_collection", "monkey_guid": self.monkey_guid}).sort([("timestamp", -1)]).limit(
+ 1)
+
+ if self.latest_system_info.count() > 0:
+ self.latest_system_info = self.latest_system_info[0]
+
+ def __eq__(self, other):
+ if isinstance(other, self.__class__):
+ return self.monkey_guid == other.monkey_guid
+ else:
+ return False
+
+ @cache
+ def GetMimikatzOutput(self):
+ doc = self.latest_system_info
+
+ if not doc:
+ return None
+
+ return doc["data"]["mimikatz"]
+
+ @cache
+ def GetHostName(self):
+ doc = self.latest_system_info
+
+ for comp in doc["data"]["Win32_ComputerSystem"]:
+ return eval(comp["Name"])
+
+ return None
+
+ @cache
+ def GetIp(self):
+ doc = self.latest_system_info
+
+ for addr in doc["data"]["network_info"]["networks"]:
+ return str(addr["addr"])
+
+ return None
+
+ @cache
+ def GetDomainName(self):
+ doc = self.latest_system_info
+
+ for comp in doc["data"]["Win32_ComputerSystem"]:
+ return eval(comp["Domain"])
+
+ return None
+
+ @cache
+ def GetDomainRole(self):
+ doc = self.latest_system_info
+
+ for comp in doc["data"]["Win32_ComputerSystem"]:
+ return comp["DomainRole"]
+
+ return None
+
+ @cache
+ def IsDomainController(self):
+ return self.GetDomainRole() in (DsRole_RolePrimaryDomainController, DsRole_RoleBackupDomainController)
+
+ @cache
+ def GetSidByUsername(self, username, domain=None):
+ doc = self.latest_system_info
+
+ for user in doc["data"]["Win32_UserAccount"]:
+ if eval(user["Name"]) != username:
+ continue
+
+ if user["SIDType"] != SidTypeUser:
+ continue
+
+ if domain and user["Domain"] != domain:
+ continue
+
+ return eval(user["SID"])
+
+ if not self.IsDomainController():
+ for dc in self.GetDomainControllers():
+ sid = dc.GetSidByUsername(username)
+
+ if sid != None:
+ return sid
+
+ return None
+
+ @cache
+ def GetUsernameBySid(self, sid):
+ info = self.GetSidInfo(sid)
+
+ if not info:
+ return None
+
+ return info["Domain"] + "\\" + info["Username"]
+
+ @cache
+ def GetSidInfo(self, sid):
+ doc = self.latest_system_info
+
+ for user in doc["data"]["Win32_UserAccount"]:
+ if eval(user["SID"]) != sid:
+ continue
+
+ if user["SIDType"] != SidTypeUser:
+ continue
+
+ return {"Domain": eval(user["Domain"]),
+ "Username": eval(user["Name"]),
+ "Disabled": user["Disabled"] == "true",
+ "PasswordRequired": user["PasswordRequired"] == "true",
+ "PasswordExpires": user["PasswordExpires"] == "true", }
+
+ if not self.IsDomainController():
+ for dc in self.GetDomainControllers():
+ domain = dc.GetSidInfo(sid)
+
+ if domain != None:
+ return domain
+
+ return None
+
+ @cache
+ def GetCriticalServicesInstalled(self):
+ def IsNameOfCriticalService(name):
+ services = ("W3svc", "MSExchangeServiceHost", "MSSQLServer", "dns")
+ services = map(str.lower, services)
+
+ if not name:
+ return False
+
+ name = name.lower()
+
+ return name in services
+ # for ser in services:
+ # if ser in name:
+ # return True
+
+ return False
+
+ doc = self.latest_system_info
+ found = []
+
+ if self.IsDomainController():
+ found.append("Domain Controller")
+
+ for product in doc["data"]["Win32_Product"]:
+ service_name = eval(product["Name"])
+
+ if not IsNameOfCriticalService(service_name):
+ continue
+
+ found.append(service_name)
+
+ for service in doc["data"]["Win32_Service"]:
+ service_name = eval(service["Name"])
+
+ if not IsNameOfCriticalService(service_name):
+ continue
+
+ if eval(service["State"]) != "Running":
+ continue
+
+ found.append(service_name)
+
+ return found
+
+ @cache
+ def IsCriticalServer(self):
+ return len(self.GetCriticalServicesInstalled()) > 0
+
+ @cache
+ def GetUsernamesBySecret(self, secret):
+ sam = self.GetLocalSecrets()
+
+ names = set()
+
+ for username, user_secret in sam.iteritems():
+ if secret == user_secret:
+ names.add(username)
+
+ return names
+
+ @cache
+ def GetSidsBySecret(self, secret):
+ usernames = self.GetUsernamesBySecret(secret)
+ return set(map(self.GetSidByUsername, usernames))
+
+ @cache
+ def GetGroupSidByGroupName(self, group_name):
+ doc = self.latest_system_info
+
+ for group in doc["data"]["Win32_Group"]:
+ if eval(group["Name"]) != group_name:
+ continue
+
+ if not is_group_sid_type(group["SIDType"]):
+ continue
+
+ return eval(group["SID"])
+
+ return None
+
+ @cache
+ def GetUsersByGroupSid(self, sid):
+ doc = self.latest_system_info
+
+ users = dict()
+
+ for group_user in doc["data"]["Win32_GroupUser"]:
+ if eval(group_user["GroupComponent"]["SID"]) != sid:
+ continue
+
+ if not is_group_sid_type(group_user["GroupComponent"]["SIDType"]):
+ continue
+
+ if "PartComponent" not in group_user.keys():
+ continue
+
+ if type(group_user["PartComponent"]) in (str, unicode):
+ # PartComponent is an id to Win32_UserAccount table
+
+ wmi_id = group_user["PartComponent"]
+
+ if "cimv2:Win32_UserAccount" not in wmi_id:
+ continue
+
+ # u'\\\\WIN-BFA01FFQFLS\\root\\cimv2:Win32_UserAccount.Domain="MYDOMAIN",Name="WIN-BFA01FFQFLS$"'
+ username = wmi_id.split('cimv2:Win32_UserAccount.Domain="')[1].split('",Name="')[0]
+ domain = wmi_id.split('cimv2:Win32_UserAccount.Domain="')[1].split('",Name="')[1][:-1]
+
+ sid = self.GetSidByUsername(username, domain)
+ users[sid] = username
+
+ else:
+ if group_user["PartComponent"]["SIDType"] != SidTypeUser:
+ continue
+
+ users[eval(group_user["PartComponent"]["SID"])] = eval(group_user["PartComponent"]["Name"])
+
+ return users
+
+ @cache
+ def GetDomainControllersMonkeyGuidByDomainName(self, domain_name):
+ cur = mongo.db.telemetry.find(
+ {"telem_type": "system_info_collection", "data.Win32_ComputerSystem.Domain": "u'%s'" % (domain_name,)})
+
+ GUIDs = set()
+
+ for doc in cur:
+ if not Machine(doc["monkey_guid"]).IsDomainController():
+ continue
+
+ GUIDs.add(doc["monkey_guid"])
+
+ return GUIDs
+
+ @cache
+ def GetLocalAdmins(self):
+ admins = self.GetUsersByGroupSid(self.GetGroupSidByGroupName("Administrators"))
+
+ # debug = self.GetUsersByGroupSid(self.GetGroupSidByGroupName("Users"))
+ # admins.update(debug)
+
+ return admins
+
+ @cache
+ def GetLocalAdminSids(self):
+ return set(self.GetLocalAdmins().keys())
+
+ @cache
+ def GetLocalSids(self):
+ doc = self.latest_system_info
+
+ SIDs = set()
+
+ for user in doc["data"]["Win32_UserAccount"]:
+ if user["SIDType"] != SidTypeUser:
+ continue
+
+ SIDs.add(eval(user["SID"]))
+
+ return SIDs
+
+ @cache
+ def GetLocalAdminNames(self):
+ return set(self.GetLocalAdmins().values())
+
+ @cache
+ def GetSam(self):
+ if not self.GetMimikatzOutput():
+ return {}
+
+ mimikatz = self.GetMimikatzOutput()
+
+ if mimikatz.count("\n42.") != 2:
+ return {}
+
+ try:
+ sam_users = mimikatz.split("\n42.")[1].split("\nSAMKey :")[1].split("\n\n")[1:]
+
+ sam = {}
+
+ for sam_user_txt in sam_users:
+ sam_user = dict([map(unicode.strip, line.split(":")) for line in
+ filter(lambda l: l.count(":") == 1, sam_user_txt.splitlines())])
+
+ ntlm = sam_user["NTLM"]
+ if "[hashed secret]" not in ntlm:
+ continue
+
+ sam[sam_user["User"]] = ntlm.replace("[hashed secret]", "").strip()
+
+ return sam
+
+ except:
+ return {}
+
+ @cache
+ def GetNtds(self):
+ if not self.GetMimikatzOutput():
+ return {}
+
+ mimikatz = self.GetMimikatzOutput()
+
+ if mimikatz.count("\n42.") != 2:
+ return {}
+
+ ntds_users = mimikatz.split("\n42.")[2].split("\nRID :")[1:]
+ ntds = {}
+
+ for ntds_user_txt in ntds_users:
+ user = ntds_user_txt.split("User :")[1].splitlines()[0].replace("User :", "").strip()
+ ntlm = ntds_user_txt.split("* Primary\n NTLM :")[1].splitlines()[0].replace("NTLM :", "").strip()
+ ntlm = ntlm.replace("[hashed secret]", "").strip()
+
+ if ntlm:
+ ntds[user] = ntlm
+
+ return ntds
+
+ @cache
+ def GetLocalSecrets(self):
+ sam = self.GetSam()
+ ntds = self.GetNtds()
+
+ secrets = sam.copy()
+ secrets.update(ntds)
+
+ return secrets
+
+ @cache
+ def GetLocalAdminSecrets(self):
+ return set(self.GetLocalAdminCreds().values())
+
+ @cache
+ def GetLocalAdminCreds(self):
+ admin_names = self.GetLocalAdminNames()
+ sam = self.GetLocalSecrets()
+
+ admin_creds = dict()
+
+ for username, secret in sam.iteritems():
+ if username not in admin_names:
+ continue
+
+ admin_creds[username] = secret
+
+ return admin_creds
+
+ @cache
+ def GetCachedSecrets(self):
+ return set(self.GetCachedCreds().values())
+
+ @cache
+ def GetCachedCreds(self):
+ doc = self.latest_system_info
+
+ creds = dict()
+
+ if not self.GetMimikatzOutput():
+ return {}
+
+ mimikatz = self.GetMimikatzOutput()
+
+ for user in mimikatz.split("\n42.")[0].split("Authentication Id")[1:]:
+ username = None
+ secret = None
+
+ for line in user.splitlines():
+ if "User Name" in line:
+ username = line.split(":")[1].strip()
+
+ if ("NTLM" in line or "Password" in line) and "[hashed secret]" in line:
+ secret = line.split(":")[1].replace("[hashed secret]", "").strip()
+
+ if username and secret:
+ creds[username] = secret
+
+ return creds
+
+ @cache
+ def GetDomainControllers(self):
+ domain_name = self.GetDomainName()
+ DCs = self.GetDomainControllersMonkeyGuidByDomainName(domain_name)
+ return map(Machine, DCs)
+
+ @cache
+ def GetDomainAdminsOfMachine(self):
+ DCs = self.GetDomainControllers()
+
+ domain_admins = set()
+
+ for dc in DCs:
+ domain_admins |= set(dc.GetUsersByGroupSid(self.GetGroupSidByGroupName("Domain Admins")).keys())
+
+ return domain_admins
+
+ @cache
+ def GetAdmins(self):
+ return self.GetLocalAdminSids() | self.GetDomainAdminsOfMachine()
+
+ @cache
+ def GetAdminNames(self):
+ return set(map(lambda x: self.GetUsernameBySid(x), self.GetAdmins()))
+
+ @cache
+ def GetCachedSids(self):
+ doc = self.latest_system_info
+
+ SIDs = set()
+
+ for username in doc["data"]["credentials"]:
+ sid = self.GetSidByUsername(username)
+
+ if not sid:
+ sid = "__USERNAME__" + username
+
+ SIDs.add(sid)
+
+ return SIDs
+
+ @cache
+ def GetCachedUsernames(self):
+ doc = self.latest_system_info
+
+ names = set()
+
+ for username in doc["data"]["credentials"]:
+ names.add(username)
+
+ return names
+
+class PassTheHashReport(object):
+ # _instance = None
+ # def __new__(class_, *args, **kwargs):
+ # if not class_._instance:
+ # class_._instance = object.__new__(class_, *args, **kwargs)
+ #
+ # return class_._instance
+
+ def __init__(self):
+ self.vertices = self.GetAllMachines()
+
+ self.machines = map(Machine, self.vertices)
+
+ self.edges = set()
+ self.edges |= self.GetEdgesBySid() # 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"})
+
+ GUIDs = set()
+
+ for doc in cur:
+ GUIDs.add(doc["monkey_guid"])
+
+ return GUIDs
+
+ @cache
+ def ReprSidList(self, sid_list, attacker, victim):
+ label = set()
+
+ for sid in sid_list:
+ username = Machine(victim).GetUsernameBySid(sid)
+
+ # if not username:
+ # username = Machine(attacker).GetUsernameBySid(sid)
+
+ if username:
+ label.add(username)
+
+ return ",\n".join(label)
+
+ @cache
+ def ReprSecretList(self, secret_list, victim):
+ label = set()
+
+ for secret in secret_list:
+ label |= Machine(victim).GetUsernamesBySecret(secret)
+
+ return ",\n".join(label)
+
+ @cache
+ def GetEdgesBySid(self):
+ edges = set()
+
+ for attacker in self.vertices:
+ cached = self.GetCachedSids(Machine(attacker))
+
+ for victim in self.vertices:
+ if attacker == victim:
+ continue
+
+ admins = Machine(victim).GetAdmins()
+
+ if len(cached & admins) > 0:
+ label = self.ReprSidList(cached & admins, attacker, victim)
+ edges.add((attacker, victim, label))
+
+ return edges
+
+ @cache
+ def GetEdgesBySamHash(self):
+ edges = set()
+
+ for attacker in self.vertices:
+ cached_creds = set(Machine(attacker).GetCachedCreds().items())
+
+ for victim in self.vertices:
+ if attacker == victim:
+ continue
+
+ admin_creds = set(Machine(victim).GetLocalAdminCreds().items())
+
+ if len(cached_creds & admin_creds) > 0:
+ label = self.ReprSecretList(set(dict(cached_creds & admin_creds).values()), victim)
+ edges.add((attacker, victim, label))
+
+ return edges
+
+ @cache
+ def GetEdgesByUsername(self):
+ edges = set()
+
+ for attacker in self.vertices:
+ cached = Machine(attacker).GetCachedUsernames()
+
+ for victim in self.vertices:
+ if attacker == victim:
+ continue
+
+ admins = Machine(victim).GetAdminNames()
+
+ if len(cached & admins) > 0:
+ edges.add((attacker, victim))
+
+ return edges
+
+ @cache
+ def Print(self):
+ print map(lambda x: Machine(x).GetIp(), self.vertices)
+ print map(lambda x: (Machine(x[0]).GetIp(), Machine(x[1]).GetIp()), self.edges)
+
+ @cache
+ def GetPossibleAttackCountBySid(self, sid):
+ return len(self.GetPossibleAttacksBySid(sid))
+
+ @cache
+ def GetPossibleAttacksByAttacker(self, attacker):
+ attacks = set()
+
+ cached_creds = set(Machine(attacker).GetCachedCreds().items())
+
+ for victim in self.vertices:
+ if attacker == victim:
+ continue
+
+ admin_creds = set(Machine(victim).GetLocalAdminCreds().items())
+
+ if len(cached_creds & admin_creds) > 0:
+ curr_attacks = dict(cached_creds & admin_creds)
+ attacks.add((attacker, victim, curr_attacks))
+
+ return attacks
+
+ @cache
+ def GetPossibleAttacksBySid(self, sid):
+ attacks = set()
+
+ for attacker in self.vertices:
+ tmp = self.GetPossibleAttacksByAttacker(attacker)
+
+ for _, victim, curr_attacks in tmp:
+ for username, secret in curr_attacks.iteritems():
+ if Machine(victim).GetSidByUsername(username) == sid:
+ attacks.add((attacker, victim))
+
+ return attacks
+
+ @cache
+ def GetSecretBySid(self, sid):
+ for m in self.machines:
+ for user, user_secret in m.GetLocalSecrets().iteritems():
+ if m.GetSidByUsername(user) == sid:
+ return user_secret
+
+ return None
+
+ @cache
+ def GetVictimCountBySid(self, sid):
+ return len(self.GetVictimsBySid(sid))
+
+ @cache
+ def GetVictimCountByMachine(self, attacker):
+ return len(self.GetVictimsByAttacker(attacker))
+
+ @cache
+ def GetAttackCountBySecret(self, secret):
+ return len(self.GetAttackersBySecret(secret))
+
+ @cache
+ def GetAllUsernames(self):
+ names = set()
+
+ for sid in self.GetAllSids():
+ names.add(self.GetUsernameBySid(sid))
+
+ return names
+
+ @cache
+ def GetAllSids(self):
+ SIDs = set()
+
+ for m in self.machines:
+ SIDs |= m.GetLocalSids()
+
+ return SIDs
+
+ @cache
+ def GetAllSecrets(self):
+ secrets = set()
+
+ for m in self.machines:
+ for secret in m.GetLocalAdminSecrets():
+ secrets.add(secret)
+
+ return secrets
+
+ @cache
+ def GetUsernameBySid(self, sid):
+ for m in self.machines:
+ username = m.GetUsernameBySid(sid)
+
+ if username:
+ return username
+
+ return None
+
+ @cache
+ def GetSidInfo(self, sid):
+ for m in self.machines:
+ info = m.GetSidInfo(sid)
+
+ if info:
+ return info
+
+ return None
+
+ @cache
+ def GetSidsBySecret(self, secret):
+ SIDs = set()
+
+ for m in self.machines:
+ SIDs |= m.GetSidsBySecret(secret)
+
+ return SIDs
+
+ @cache
+ def GetAllDomainControllers(self):
+ DCs = set()
+
+ for m in self.machines:
+ if m.IsDomainController():
+ DCs.add(m)
+
+ return DCs
+
+ @cache
+ def GetSidsByUsername(self, username):
+ SIDs = set()
+
+ for m in self.machines:
+ sid = m.GetSidByUsername(username)
+ if sid:
+ SIDs.add(sid)
+
+ return SIDs
+
+ @cache
+ def GetVictimsBySid(self, sid):
+ machines = set()
+
+ for m in self.machines:
+ if sid in m.GetAdmins():
+ machines.add(m)
+
+ return machines
+
+ @cache
+ def GetVictimsBySecret(self, secret):
+ machines = set()
+
+ SIDs = self.GetSidsBySecret(secret)
+
+ for m in self.machines:
+ if len(SIDs & m.GetAdmins()) > 0:
+ machines.add(m)
+
+ return machines
+
+ @cache
+ def GetAttackersBySecret(self, secret):
+ machines = set()
+
+ for m in self.machines:
+ if secret in m.GetCachedSecrets():
+ machines.add(m)
+
+ return machines
+
+ @cache
+ def GetAttackersByVictim(self, victim):
+ if type(victim) != unicode:
+ victim = victim.monkey_guid
+
+ attackers = set()
+
+ for atck, vic, _ in self.edges:
+ if vic == victim:
+ attackers.add(atck)
+
+ return set(map(Machine, attackers))
+
+ @cache
+ def GetAttackersBySid(self, sid):
+ machines = set()
+
+ for m in self.machines:
+ if sid in self.GetCachedSids(m):
+ machines.add(m)
+
+ return machines
+
+ @cache
+ def GetVictimsByAttacker(self, attacker):
+ if type(attacker) != unicode:
+ attacker = attacker.monkey_guid
+
+ victims = set()
+
+ for atck, vic, _ in self.edges:
+ if atck == attacker:
+ victims.add(vic)
+
+ return set(map(Machine, victims))
+
+ @cache
+ def GetInPathCountByVictim(self, victim, already_processed=None):
+ if type(victim) != unicode:
+ victim = victim.monkey_guid
+
+ if not already_processed:
+ already_processed = set([victim])
+
+ count = 0
+
+ for atck, vic, _ in self.edges:
+ if atck == vic:
+ continue
+
+ if vic != victim:
+ continue
+
+ if atck in already_processed:
+ continue
+
+ count += 1
+
+ already_processed.add(atck)
+ count += self.GetInPathCountByVictim(atck, already_processed)
+
+ return count
+
+ @cache
+ def GetCritialServers(self):
+ machines = set()
+
+ for m in self.machines:
+ if m.IsCriticalServer():
+ machines.add(m)
+
+ return machines
+
+ @cache
+ def GetNonCritialServers(self):
+ return set(self.machines) - self.GetCritialServers()
+
+ @cache
+ def GetCachedSids(self, m):
+ sids = set()
+ tmp = m.GetCachedSids()
+
+ for sid in tmp:
+ if sid.startswith("__USERNAME__"):
+
+ s = self.GetSidsByUsername(sid[len("__USERNAME__"):])
+ if len(s) == 1:
+ sids.add(s.pop())
+ else:
+ sids.add(sid)
+
+ else:
+ sids.add(sid)
+
+ return sids
+
+ @cache
+ def GetThreateningUsersByVictim(self, victim):
+ threatening_users = set()
+
+ for attacker in self.GetAttackersByVictim(victim):
+ # For each attacker, get the cached users and check which of them is an admin on the victim
+ threatening_users |= (self.GetCachedSids(attacker) & victim.GetAdmins())
+
+ return threatening_users
+
+ @cache
+ def GetSharedAdmins(self, m):
+ shared_admins = set()
+
+ for other in self.machines:
+ if m == other:
+ continue
+
+ shared_admins |= (m.GetLocalAdminSids() & other.GetLocalAdminSids())
+
+ shared_admins -= m.GetDomainAdminsOfMachine()
+ return shared_admins
\ No newline at end of file