monkey/monkey_island/mymap.py

331 lines
11 KiB
Python

import hashlib
import binascii
from pymongo import MongoClient
db = MongoClient().monkeyisland
DsRole_RoleStandaloneWorkstation = 0
DsRole_RoleMemberWorkstation = 1
DsRole_RoleStandaloneServer = 2
DsRole_RoleMemberServer = 3
DsRole_RoleBackupDomainController = 4
DsRole_RolePrimaryDomainController = 5
def myntlm(x):
hash = hashlib.new('md4', x.encode('utf-16le')).digest()
return str(binascii.hexlify(hash))
class Machine(object):
def __init__(self, monkey_guid):
self.monkey_guid = str(monkey_guid)
def GetMimikatzOutput(self):
cur = db.telemetry.find({"telem_type":"system_info_collection", "monkey_guid": self.monkey_guid})
output = set()
for doc in cur:
output.add(doc["data"]["mimikatz"])
if len(output) == 1:
return output.pop()
return None
def GetHostName(self):
cur = db.telemetry.find({"telem_type":"system_info_collection", "monkey_guid": self.monkey_guid})
names = set()
for doc in cur:
for comp in doc["data"]["Win32_ComputerSystem"]:
names.add(eval(comp["Name"]))
if len(names) == 1:
return names.pop()
return None
def GetIp(self):
cur = db.telemetry.find({"telem_type":"system_info_collection", "monkey_guid": self.monkey_guid})
names = set()
for doc in cur:
for addr in doc["data"]["network_info"]["networks"]:
return str(addr["addr"])
return None
def GetDomainName(self):
cur = db.telemetry.find({"telem_type":"system_info_collection", "monkey_guid": self.monkey_guid})
names = set()
for doc in cur:
for comp in doc["data"]["Win32_ComputerSystem"]:
names.add(eval(comp["Domain"]))
if len(names) == 1:
return names.pop()
return None
def GetDomainRole(self):
cur = db.telemetry.find({"telem_type":"system_info_collection", "monkey_guid": self.monkey_guid})
roles = set()
for doc in cur:
for comp in doc["data"]["Win32_ComputerSystem"]:
roles.add(comp["DomainRole"])
if len(roles) == 1:
return roles.pop()
return None
def GetSidByUsername(self, username):
cur = db.telemetry.find({"telem_type":"system_info_collection", "monkey_guid": self.monkey_guid, "data.Win32_UserAccount.Name":"u'%s'" % (username,)})
SIDs = set()
for doc in cur:
for user in doc["data"]["Win32_UserAccount"]:
if eval(user["Name"]) != username:
continue
SIDs.add(eval(user["SID"]))
if len(SIDs) == 1:
return SIDs.pop()
return None
def GetUsernameBySid(self, sid):
cur = db.telemetry.find({"telem_type":"system_info_collection", "monkey_guid": self.monkey_guid, "data.Win32_UserAccount.SID":"u'%s'" % (sid,)})
names = set()
for doc in cur:
for user in doc["data"]["Win32_UserAccount"]:
if eval(user["SID"]) != sid:
continue
names.add(eval(user["Name"]))
if len(names) == 1:
return names.pop()
return None
def GetGroupSidByGroupName(self, group_name):
cur = db.telemetry.find({"telem_type":"system_info_collection", "monkey_guid": self.monkey_guid, "data.Win32_Group.Name":"u'%s'" % (group_name,)})
SIDs = set()
for doc in cur:
for group in doc["data"]["Win32_Group"]:
if eval(group["Name"]) != group_name:
continue
SIDs.add(eval(group["SID"]))
if len(SIDs) == 1:
return SIDs.pop()
return None
def GetUsersByGroupSid(self, sid):
cur = db.telemetry.find({"telem_type":"system_info_collection", "monkey_guid": self.monkey_guid, "data.Win32_GroupUser.GroupComponent.SID":"u'%s'" % (sid,)})
users = dict()
for doc in cur:
for group_user in doc["data"]["Win32_GroupUser"]:
if eval(group_user["GroupComponent"]["SID"]) != sid:
continue
if "PartComponent" not in group_user.keys():
continue
users[eval(group_user["PartComponent"]["SID"])] = eval(group_user["PartComponent"]["Name"])
return users
def GetDomainControllersMonkeyGuidByDomainName(self, domain_name):
cur = db.telemetry.find({"telem_type":"system_info_collection", "data.Win32_ComputerSystem.Domain":"u'%s'" % (domain_name,)})
GUIDs = set()
for doc in cur:
for comp in doc["data"]["Win32_ComputerSystem"]:
if ((comp["DomainRole"] != DsRole_RolePrimaryDomainController) and
(comp["DomainRole"] != DsRole_RoleBackupDomainController)):
continue
GUIDs.add(doc["monkey_guid"])
return GUIDs
def GetLocalAdmins(self):
return set(self.GetUsersByGroupSid(self.GetGroupSidByGroupName("Administrators")).keys())
def GetLocalAdminNames(self):
return set(self.GetUsersByGroupSid(self.GetGroupSidByGroupName("Administrators")).values())
def GetLocalAdminSecrets(self):
admin_names = self.GetLocalAdminNames()
sam_users = str(self.GetMimikatzOutput()).split("\nSAMKey :")[1].split("\n\n")[1:]
admin_secrets = set()
for sam_user_txt in sam_users:
sam_user = dict([map(str.strip, line.split(":")) for line in filter(lambda l: l.count(":") == 1, sam_user_txt.splitlines())])
if sam_user["User"] not in admin_names:
continue
admin_secrets.add(sam_user["NTLM"].replace("[hashed secret]", "").strip())
return admin_secrets
def GetCachedSecrets(self):
cur = db.telemetry.find({"telem_type":"system_info_collection", "monkey_guid": self.monkey_guid})
secrets = set()
for doc in cur:
for username in doc["data"]["credentials"]:
user = doc["data"]["credentials"][username]
if "password" in user.keys():
ntlm = myntlm(str(user["password"]))
elif "ntlm_hash" in user.keys():
ntlm = str(user["ntlm_hash"])
else:
continue
secret = hashlib.md5(ntlm.decode("hex")).hexdigest()
secrets.add(secret)
return secrets
def GetDomainAdminsOfMachine(self):
domain_name = self.GetDomainName()
DCs = self.GetDomainControllersMonkeyGuidByDomainName(domain_name)
domain_admins = set()
for dc_monkey_guid in DCs:
domain_admins |= Machine(dc_monkey_guid).GetLocalAdmins()
return domain_admins
def GetAdmins(self):
return self.GetLocalAdmins() | self.GetDomainAdminsOfMachine()
def GetAdminNames(self):
return set(map(lambda x: self.GetUsernameBySid(x), self.GetAdmins()))
def GetCachedSids(self):
cur = db.telemetry.find({"telem_type":"system_info_collection", "monkey_guid": self.monkey_guid})
SIDs = set()
for doc in cur:
for username in doc["data"]["credentials"]:
SIDs.add(self.GetSidByUsername(username))
return SIDs
def GetCachedUsernames(self):
cur = db.telemetry.find({"telem_type":"system_info_collection", "monkey_guid": self.monkey_guid})
SIDs = set()
for doc in cur:
for username in doc["data"]["credentials"]:
SIDs.add(username)
return SIDs
class PassTheHashMap(object):
def __init__(self):
self.vertices = self.GetAllMachines()
self.edges = set()
self.GenerateEdgesBySid() # Useful for non-cached domain users
self.GenerateEdgesBySamHash() # This will add edges based only on password hash without caring about username
def GetAllMachines(self):
cur = db.telemetry.find({"telem_type":"system_info_collection"})
GUIDs = set()
for doc in cur:
GUIDs.add(doc["monkey_guid"])
return GUIDs
def GenerateEdgesBySid(self):
for attacker in self.vertices:
cached = Machine(attacker).GetCachedSids()
for victim in self.vertices:
if attacker == victim:
continue
admins = Machine(victim).GetAdmins()
if len(cached & admins) > 0:
self.edges.add((attacker, victim))
def GenerateEdgesBySamHash(self):
for attacker in self.vertices:
cached = Machine(attacker).GetCachedSecrets()
for victim in self.vertices:
if attacker == victim:
continue
admins = Machine(victim).GetLocalAdminSecrets()
if len(cached & admins) > 0:
self.edges.add((attacker, victim))
def GenerateEdgesByUsername(self):
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:
self.edges.add((attacker, victim))
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)
PassTheHashMap().Print()
#monkey_guid_island = 345051728334
#monkey_guid_c = 345051740363
#monkey_guid_d = 345051735830
#
#island = Machine(monkey_guid_island)
#c = Machine(monkey_guid_c)
#d = Machine(monkey_guid_d)
#
#assert str(island.GetIp()).endswith(".5")
#assert str(c.GetIp()).endswith(".203")
#assert str(d.GetIp()).endswith(".204")
#print "sam", island.GetLocalAdminSecrets()
#print "lsa", island.GetCachedSecrets()
#print "cached", c.GetCachedSids()
#print "admins", d.GetAdmins()