Files
2023-07-06 10:21:12 +02:00

2131 lines
98 KiB
Python

import logging
import binascii, os, json, datetime, shutil, base64
from datetime import date, datetime, timedelta
from donpapi.lib.toolbox import bcolors
class reporting:
def __init__(self, conn, logger, options, targets):
self.conn = conn
self.logging = logger
self.options = options
self.targets = targets
self.report_file = os.path.join(self.options.output_directory,
'%s_result.html' % date.today().strftime("%d-%m-%Y"))
def add_to_resultpage(self, datas, report_file=""):
try:
if report_file == "":
report_file = self.report_file
datas = datas.encode('ascii', 'ignore')
f = open(report_file, 'ab')
f.write(datas)
f.close()
return True
except Exception as ex:
self.logging.debug(f" Exception {bcolors.WARNING} in add to resultat {bcolors.ENDC}")
self.logging.debug(ex)
return False
def generate_report(self, type='', user='', target='',
report_content=['credz', 'certificates', 'cookies', 'files', 'connected_user', 'hash_reuse',
'audited_scope', 'masterkeys'],
credz_content=['wifi', 'taskscheduler', 'credential-blob', 'browser', 'sysadmin', 'SAM', 'LSA',
'DCC2'], report_file=""):
try:
my_path = os.path.dirname(os.path.realpath(__file__))
with open(os.path.join(os.path.join(my_path, "config"), "donpapi_config.json")) as config:
config_parser = json.load(config)
# Gérer les chemins Win vs Linux pour le .replace('\\', '/')
with open(os.path.join(my_path, config_parser['css']).replace('\\', '/'),'r') as f:
mycss = f.read()
with open(os.path.join(my_path, config_parser['logo_login']).replace('\\', '/'),'rb') as f:
logo_login = base64.b64encode(f.read()).decode('utf-8')
# self.logging.debug(f"[+] {logo_login_path}")
except Exception as ex:
self.logging.debug(f" Exception {bcolors.WARNING} in running Report {bcolors.ENDC}")
self.logging.debug(ex)
self.logging.info(f"[+] Generating report : {report_file}")
if self.conn == None:
self.logging.debug(f"[+] db ERROR - {self.options.output_directory}")
return -1
try:
if report_file == "":
self.report_file = os.path.join(self.options.output_directory,
'%s_result.html' % date.today().strftime("%d-%m-%Y"))
else:
self.report_file = os.path.join(self.options.output_directory, report_file)
if os.path.exists(self.report_file):
if os.path.exists(self.report_file + "_old"):
os.remove(self.report_file + "_old")
os.rename(self.report_file, self.report_file + "_old")
os.remove(self.report_file)
if not os.path.exists(os.path.join(self.options.output_directory, 'res')):
os.mkdir(os.path.join(self.options.output_directory, 'res'))
except Exception as ex:
self.logging.debug(f" Exception {bcolors.WARNING} in Creating Report File {bcolors.ENDC}")
self.logging.debug(ex)
return False
data = """ <!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<title>DonPapi - Results</title>
<style>
%s
</style>
</head>
<body onload="toggleAll()">
\n""" % (mycss)
self.add_to_resultpage(data)
# Tableau en top de page pour les liens ?
data = """<table class="statistics"><TR><Th><a class="firstletter">M</a><a>enu</A></Th></TR>\n"""
data = """<div class="navbar">\n"""
for menu in ['wifi', 'taskscheduler', 'credential-blob', 'certificates', 'browser-internet_explorer', 'cookies',
'SAM', 'LSA', 'DCC2',
'Files', 'Connected-users', 'Local_account_reuse', 'Scope_Audited']:
# data += f"""<TR><TD class="menu_top"><BR><a href="#{menu}"> {menu} </A><BR></TD></TR>\n"""
data += f"""<a href="#{menu}"> {menu.upper()}</A>\n"""
data += """</DIV><BR>\n"""
self.add_to_resultpage(data)
# Logo @ Titre
data = """<DIV class="main">\n"""
data += """<table class="main"><TR><TD>\n"""
data += """<table><TR><TD class="menu_top"><a class="firstletter">D</a><a>onPapi Audit</a></TD></TR>\n"""
data += """<TR><TD class="menu_top"><BR> %s <BR></TD></TR></TABLE><BR>\n""" % date.today().strftime("%d/%m/%Y")
data += """<table><TR><TD><img class="logo_left" src="data:image/png;base64,%s"></TD>""" % logo_login
'''if os.path.isfile(os.path.join(logdir, 'logo.png')):
data += """<TD><img class="logo" src='%s'></TD>""" % (os.path.join(logdir, 'logo.png'))'''
data += """<BR></div></TD></TR></TABLE><BR>\n"""
self.add_to_resultpage(data)
# JS Stuff
data = """
<script>
function toggle_by_class(cls, on) {
var lst = document.getElementsByClassName(cls);
for(var i = 0; i < lst.length; ++i) {
lst[i].style.display = on ? '' : 'none';
}
}
function toggle_it(thisname) {
tr=document.getElementsByTagName('tr')
for (i=0;i<tr.length;i++){
if (tr[i].getAttribute(thisname)){
if ( tr[i].style.display=='none' ){
tr[i].style.display = '';
}
else {
tr[i].style.display = 'none';
}
}
}
}
function toggleAll() {
toggle_it("cookies");
toggle_it("wifi");
toggle_it("taskscheduler");
toggle_it("credential-blob");
toggle_it("browser-internet_explorer");
toggle_it("browser-firefox");
toggle_it("browser-chrome");
toggle_it("SAM");
toggle_it("LSA");
toggle_it("DCC2");
toggle_it("VNC");
toggle_it("MRemoteNG");
}
function CopyToClipboard(data_to_copy) {
const dummy = document.createElement('textarea');
dummy.style.position = 'absolute';
dummy.style.left = '-9999px';
dummy.style.top = '-9999px';
document.body.appendChild(dummy);
dummy.value = data_to_copy;
dummy.select();
document.execCommand('copy');
document.body.removeChild(dummy);
// Copy the text inside the text field
//navigator.clipboard.writeText(data_to_copy);
//alert("Copied the text: " + data_to_copy);
}
</script>
"""
self.add_to_resultpage(data)
if 'credz' in report_content:
results = self.get_credz()
# popolute credz report filtering :
if 'browser' in credz_content:
credz_content.append('browser-internet_explorer')
credz_content.append('browser-firefox')
credz_content.append('browser-chrome')
if 'sysadmin' in credz_content:
credz_content.append('VNC')
credz_content.append('MRemoteNG')
credz_content.append('Putty')
credz_content.append('Winscp')
# credz_content.append('VNC')
data = """<table class="statistics"><TR>
<Th><a class="firstletter">U</a><a>sername</A></Th>
<Th><a class="firstletter">P</a><a>assword</A></Th>
<Th><a class="firstletter">T</a><a>arget</A></Th>
<Th><a class="firstletter">T</a><a>ype</A></Th>
<Th><a class="firstletter">P</a><a>illaged_from_computerid</A></Th>
<Th><a class="firstletter">P</a><a>illaged_from_userid</A></Th></TR>\n"""
# <a href="#" id="toggle" onClick="toggle_it('tr1');toggle_it('tr2')">
current_type = ''
for index, cred in enumerate(results):
cred_id, file_path, username, password, target, type, pillaged_from_computerid, pillaged_from_userid = cred
# filtering data to be included in the report
if type not in credz_content:
continue
if type != current_type:
current_type = type
current_type_count = self.get_credz_count(current_type,
'AND username NOT IN ("NL$KM_history") AND target NOT IN ("WindowsLive:target=virtualapp/didlogical","Adobe App Info","Adobe App Prefetched Info","Adobe User Info","Adobe User OS Info","MicrosoftOffice16_Data:ADAL","LegacyGeneric:target=msteams_adalsso/adal_contex")')[
0][0]
data += f"""<TR id={current_type}><TD colspan="6" class="toggle_menu" onClick="toggle_it('{current_type}')"><A>{current_type} ({current_type_count})</A></TD></TR>"""
# Skip infos of
# WindowsLive:target=virtualapp/didlogical
untreated_targets = ["WindowsLive:target=virtualapp/didlogical", "Adobe App Info",
"Adobe App Prefetched Info", "Adobe User Info", "Adobe User OS Info",
"MicrosoftOffice16_Data:ADAL", "LegacyGeneric:target=msteams_adalsso/adal_contex"]
untreated_users = ["NL$KM_history"]
blacklist_bypass = False
for untreated in untreated_targets:
if untreated in target:
blacklist_bypass = True
for untreated in untreated_users:
if untreated in username:
blacklist_bypass = True
if blacklist_bypass:
continue
# Get computer infos
res = self.get_computer_infos(pillaged_from_computerid)
for index_, res2 in enumerate(res):
ip, hostname = res2
computer_info = f"{ip} | {hostname}"
# pillaged_from_userid
if pillaged_from_userid != None:
res = self.get_user_infos(pillaged_from_userid)
for index_, pillaged_username in enumerate(res):
pillaged_from_userid = pillaged_username[0]
else:
pillaged_from_userid = str(pillaged_from_userid)
if index % 2 == 0:
data += f"""<TR class=tableau_resultat_row0 {current_type}=1>"""
else:
data += f"""<TR class=tableau_resultat_row1 {current_type}=1>"""
if 'admin' in username.lower(): # Pour mettre des results en valeur
special_style = '''class="cracked"'''
else:
special_style = ""
###Print block
# Recup des username dans le target #/# a update dans myseatbelt.py pour faire une fonction dump_CREDENTIAL_XXXXX clean
if "LegacyGeneric:target=MicrosoftOffice1" in target:
username = f'''{target.split(':')[-1]}'''
# Les pass LSA sont souvent en Hexa
# if "LSA" in type:
try:
hex_passw = ''
hex_passw = binascii.unhexlify(password).replace(b'>', b'')
except Exception as ex:
# print(ex)
pass
for info in [username]:
data += f"""<TD {special_style} ><A title="{info}"> {str(info)[:48]} </A></TD>"""
for info in [password]:
data += f"""<TD {special_style} ><A title="{hex_passw}"> {str(info)[:48]} </A></TD>"""
# check if info contains a URL
if 'http:' in target or 'https:' in target:
info2 = target[target.index('http'):]
special_ref = f'''href="{info2}" target="_blank" title="{target}"'''
elif 'ftp:' in target:
info2 = target[target.index('ftp'):]
special_ref = f'''href="{info2}" target="_blank" title="{target}"'''
elif "Domain:target=" in target:
info2 = f'''rdp://full%20address=s:{target[target.index('Domain:target=') + len('Domain:target='):]}:3389&username=s:{username}&audiomode=i:2&disable%20themes=i:1'''
special_ref = f'''href="{info2}" title="{target}"'''
elif "LegacyGeneric:target=MicrosoftOffice1" in target:
target = f'''{target[target.index('LegacyGeneric:target=') + len('LegacyGeneric:target='):]}'''
special_ref = f'''href="https://login.microsoftonline.com/" target="_blank" title="OfficeLogin"'''
else:
special_ref = f'''title="{target}"'''
data += f"""<TD {special_style} ><A {special_ref}> {str(target)[:48]} </A></TD>"""
for info in [type, computer_info, pillaged_from_userid]:
data += f"""<TD {special_style} ><A title="{info}"> {str(info)[:48]} </A></TD>"""
data += """</TR>\n"""
data += """</TABLE><BR>"""
self.add_to_resultpage(data)
###
if 'certificates' in report_content:
results = self.get_certificates()
data = """
<table class="statistics"><TR>
<Th style="text-align: center"><a class="firstletter">I</a><a>ssuer</A></Th>
<Th style="text-align: center"><a class="firstletter">S</a><a>ubject</A></Th>
<Th style="text-align: center"><a class="firstletter">P</a><a>illaged from</A></Th>
<Th style="text-align: center"><a class="firstletter">P</a><a>illaged with</A></Th>
<Th style="text-align: center"><a class="firstletter">C</a><a>lient auth</A></Th></TR>\n
"""
current_type = 'certificates'
data += f"""<TR id=certificates><TD colspan="6" class="toggle_menu" onClick="toggle_it('certificates')"><A>Certificates ({len(results)})</A></TD></TR>"""
for index, cred in enumerate(results):
_, pfx_filepath, guid, issuer, subject, client_auth, pillaged_from_computerid, pillaged_from_userid = cred
res = self.get_computer_infos(pillaged_from_computerid)
for _, res2 in enumerate(res):
ip, hostname = res2
computer_info = f"{ip} | {hostname}"
if pillaged_from_userid != None:
res = self.get_user_infos(pillaged_from_userid)
for _, pillaged_username in enumerate(res):
pillaged_from_userid = pillaged_username[0]
else:
pillaged_from_userid = str(pillaged_from_userid)
if index % 2 == 0:
data += f"""<TR class=tableau_resultat_row0 {current_type}=1>"""
else:
data += f"""<TR class=tableau_resultat_row1 {current_type}=1>"""
special_style = ""
###Print block
for info in [issuer, subject, computer_info, pillaged_from_userid]:
data += f"""<TD {special_style} ><A title="{info}"> {str(info)[:48]} </A></TD>"""
for info in [client_auth]:
if client_auth:
cmd = f"certipy auth -pfx {os.path.join(os.getcwd(), pfx_filepath)}"
data += f"""<TD {special_style} ><button onclick="CopyToClipboard('{cmd}')">Yes</button></TD>"""
else:
data += f"""<TD {special_style} ><A title="No">No</A></TD>"""
data += """</TR>\n"""
data += """</TABLE><BR>"""
self.add_to_resultpage(data)
##### List cookies
if 'cookies' in report_content:
results = self.get_cookies()
data = """<table class="statistics"><TR>
<Th><a class="firstletter">N</a><a>ame</A></Th>
<Th><a class="firstletter">V</a><a>alue</A></Th>
<Th><a class="firstletter">U</a><a>ntil</A></Th>
<Th><a class="firstletter">T</a><a>arget</A></Th>
<Th><a class="firstletter">T</a><a>ype</A></Th>
<Th><a class="firstletter">P</a><a>illaged_from_computerid</A></Th>
<Th><a class="firstletter">P</a><a>illaged_from_userid</A></Th>
<Th><a class="firstletter">B</a><a>ypass MFA</A></Th></TR>\n"""
# <a href="#" id="toggle" onClick="toggle_it('tr1');toggle_it('tr2')">
current_type = 'cookies'
data += f"""<TR id=cookies><TD colspan="8" class="toggle_menu" onClick="toggle_it('cookies')"><A>Cookies ({len(results)})</A></TD></TR>"""
previous_target = ''
previous_userid = ''
previous_computerid = ''
temp_cookie = ''
temp = []
groupindex = 0
for index_, cred_ in enumerate(results):
name_, value_, expires_utc, target, type, pillaged_from_computerid, pillaged_from_userid = cred_
if target == previous_target and pillaged_from_userid == previous_userid and pillaged_from_computerid == previous_computerid:
temp.append((index_, cred_))
if value_ != '':
temp_cookie = f"{temp_cookie}\\ndocument.cookie=\\'{name_}={value_}\\'"
rendering = False
else:
rendering = True
previous_target = target
previous_computerid = pillaged_from_computerid
previous_userid = pillaged_from_userid
if rendering == True or index_ == (len(results) - 1):
groupindex += 1
for index, cred in temp:
name, value, expires_utc, target, type, pillaged_from_computerid, pillaged_from_userid = cred
# Skip infos of
self.logging.debug(
f" analysing cookie {bcolors.WARNING} {name} {value} {type} {target} UTC:{expires_utc} {bcolors.ENDC}")
try:
if value == '':
continue
if (type == "browser-chrome" and (expires_utc != 0) and (
datetime(1601, 1, 1) + timedelta(microseconds=expires_utc)) < datetime.today()) or (
type != "browser-chrome" and datetime.fromtimestamp(
expires_utc) < datetime.today()):
self.logging.debug(
f" Skipping old cookie {bcolors.WARNING} {name} {value} {type} {target} {expires_utc} {bcolors.ENDC}")
continue
#####
except Exception as ex:
self.logging.debug(
f" Exception {bcolors.WARNING} Exception in Cookie {name} {value} {type} {target} {expires_utc} {bcolors.ENDC}")
self.logging.debug(ex)
continue
# Add browser version
# self.logging.debug(f'get browser type browser_type={type}, pillaged_from_computerid={pillaged_from_computerid},pillaged_from_userid={pillaged_from_userid}')
res = self.get_browser_version(browser_type=type,
pillaged_from_computerid=pillaged_from_computerid,
pillaged_from_userid=pillaged_from_userid)
if len(res) > 0:
type += f" - {res[0]}"
# self.logging.debug(f'Type:{type}')
# Get computer infos
res = self.get_computer_infos(pillaged_from_computerid)
for index_, res2 in enumerate(res):
ip, hostname = res2
computer_info = f"{ip} | {hostname}"
# pillaged_from_userid
if pillaged_from_userid != None:
res = self.get_user_infos(pillaged_from_userid)
for index_, pillaged_username in enumerate(res):
pillaged_from_userid = pillaged_username[0]
else:
pillaged_from_userid = str(pillaged_from_userid)
if groupindex % 2 == 0:
data += f"""<TR class=tableau_resultat_row0 {current_type}=1>"""
else:
data += f"""<TR class=tableau_resultat_row1 {current_type}=1>"""
###Print block
if name.lower() in ['estsauthpersistant', 'estsauth', 'sid', 'aws-userinfo', 'aws-credz',
'osid', 'hsid', 'ssid', 'apisid', 'sapisid', 'lsid', 'sub_session_onelogin',
'sub_session_onelogin.com', 'user_session']:
special_style = '''class="cracked"'''
else:
special_style = ""
### On supprime les cookies expirés
for info in [name, value]:
data += f"""<TD {special_style} ><A title="{info}"> {str(info)[:48]}</TD>"""
for info in [
expires_utc]: # Formule a change si on intègre des cookies venant d'autre chose que chrome
try:
if type == "browser-chrome":
data += f"""<TD {special_style} ><A title="{info}"> {(datetime(1601, 1, 1) + timedelta(microseconds=info)).strftime('%b %d %Y %H:%M:%S')} </A></TD>"""
else:
data += f"""<TD {special_style} ><A title="{info}"> {(datetime.fromtimestamp(info)).strftime('%b %d %Y %H:%M:%S')} </A></TD>"""
except:
data += f"""<TD {special_style} ><A title="{info}"> {info} </A></TD>"""
# check for known providers
if '.microsoftonline.com' in target:
special_ref = f'''href="https://myaccount.microsoft.com/?ref=MeControl" target="_blank" title="{target}"'''
elif '.okta.com' in target: # should be yourdomain.okta.com
special_ref = f'''href="https://{target}" target="_blank" title="{target}"'''
elif ".google.com" in target:
special_ref = f'''href="https://console.cloud.google.com/" title="{target}"'''
elif ".amazon.com" in target:
special_ref = f'''href="https://console.aws.amazon.com/" title="{target}"'''
elif ".onelogin.com" in target:
special_ref = f'''href="https://app.onelogin.com/login" title="{target}"'''
elif ".github.com" in target:
special_ref = f'''href="https://github.com/login" title="{target}"'''
else:
special_ref = f'''title="{target}"'''
data += f"""<TD {special_style} ><A {special_ref}> {str(target)[:48]} </A></TD>"""
for info in [type, computer_info, pillaged_from_userid]:
data += f"""<TD {special_style} ><A title="{info}"> {str(info)[:48]} </A></TD>"""
data += f"""<TD {special_style} ><button onclick="CopyToClipboard('{temp_cookie}')">Copy</button></TD>"""
data += """</TR>\n"""
temp = []
temp.append((index_, cred_))
temp_cookie = f"document.cookie=\\'{name_}={value_}\\'"
data += """</TABLE><BR>"""
self.add_to_resultpage(data)
##### List gathered files
if 'files' in report_content:
results = self.get_file()
data = """<table class="statistics" id="Files"><TR><Th><a class="firstletter">F</a><a>ilename</A></Th>
<Th><a class="firstletter">T</a><a>ype</A></Th>
<Th><a class="firstletter">U</a><a>ser</A></Th>
<Th><a class="firstletter">I</a><a>p</A></Th></TR>\n"""
for index, myfile in enumerate(results):
try:
file_path, filename, extension, pillaged_from_computerid, pillaged_from_userid = myfile
res = self.get_computer_infos(pillaged_from_computerid)
for index, res2 in enumerate(res):
ip, hostname = res2
computer_info = f"{ip} | {hostname}"
res = self.get_user_infos(pillaged_from_userid)
for index, res2 in enumerate(res):
username = res2[0]
special_ref = f'''href="file://{file_path}" target="_blank" title="{filename}"'''
data += f"""<TR><TD><A {special_ref}> {filename} </A></TD><TD> {extension} </TD><TD> {username} </TD><TD> {computer_info} </TD></TR>\n"""
except Exception as ex:
self.logging.debug(f" Exception {bcolors.WARNING} in getting File for {file_path} {bcolors.ENDC}")
self.logging.debug(ex)
return False
data += """</TABLE><BR>"""
self.add_to_resultpage(data)
##### Identify user / IP relations
# Confirm audited scope :
if 'connected_user' in report_content:
results = self.get_connected_user()
data = """<table class="statistics" id="Connected-users"><TR><Th><a class="firstletter">U</a><a>sername</A></Th>
<Th><a class="firstletter">I</a><a>P</A></Th></TR>\n"""
for index, cred in enumerate(results):
try:
ip, username = cred
data += """<TR><TD> %s </TD><TD> %s </TD></TR>\n""" % (username, ip)
except Exception as ex:
self.logging.debug(
f" Exception {bcolors.WARNING} in Identify user / IP relations for {cred} {bcolors.ENDC}")
self.logging.debug(ex)
return False
data += """</TABLE><BR>"""
self.add_to_resultpage(data)
##### Identify Local hash reuse
if 'hash_reuse' in report_content:
results = self.get_credz_sam()
data = """<table class="statistics" id="Local_account_reuse"><TR><Th><a class="firstletter">L</a><a>ocal account reuse : </Th></TR>\n"""
for index, cred in enumerate(results):
username, password, type, pillaged_from_computerid = cred
res = self.get_computer_infos(pillaged_from_computerid)
for index, res2 in enumerate(res):
ip, hostname = res2
computer_info = f"{ip} | {hostname}"
data += """<TR><TD> %s </TD><TD> %s </TD><TD> %s </TD><TD> %s </TD></TR>\n""" % (
username, password, type, computer_info)
data += """</TABLE><BR>"""
self.add_to_resultpage(data)
# Confirm audited scope :
if 'audited_scope' in report_content:
results = self.get_computers()
data = """<table class="statistics" id="Scope_Audited"><TR><Th><a class="firstletter">S</a><a>cope Audited : </Th></TR>\n"""
data += """<TR><Th><a class="firstletter">I</a><a>p</A></Th>
<Th><a class="firstletter">H</a><a>ostname</A></Th>
<Th><a class="firstletter">D</a><a>omain</A></Th>
<Th><a class="firstletter">O</a><a>S</A></Th>
<Th><a class="firstletter">S</a><a>mb signing enabled</A></Th>
<Th><a class="firstletter">S</a><a>mb v1</A></Th>
<Th><a class="firstletter">I</a><a>s Admin</A></Th>
<Th><a class="firstletter">C</a><a>onnectivity</A></Th>
</TR>\n"""
for index, cred in enumerate(results):
ip, hostname, domain, my_os, smb_signing_enabled, smbv1_enabled, is_admin, connectivity = cred
data += "<TR>"
for info in [ip, hostname, domain, my_os]:
data += f"""<TD> {info} </TD>"""
if smb_signing_enabled:
data += "<TD> Ok </TD>"
else:
data += """<TD><a class="firstletter"> NOT required </A></TD>"""
if smbv1_enabled:
data += "<TD> Yes </TD>"
else:
data += "<TD> No </TD>"
if is_admin:
data += """<TD> Admin </A></TD>"""
else:
data += """<TD><a class="firstletter"> No </A></TD>"""
for info in [connectivity]:
data += f"""<TD> {info} </TD>"""
data += "</TR>\n"
data += """</TABLE><BR>\n"""
self.add_to_resultpage(data)
# Etat des masterkeyz
if self.options.debug and 'masterkeys' in report_content:
results = self.get_masterkeys()
data = """<table class="statistics" id="Scope_Audited"><TR><Th><a class="firstletter">M</a><a>asterkeys : </Th></TR>\n"""
data += """<TR><Th><a class="firstletter">G</a><a>uid</A></Th>
<Th><a class="firstletter">S</a><a>tatus</A></Th>
<Th><a class="firstletter">D</a><a>ecrypted_with</A></Th>
<Th><a class="firstletter">D</a><a>ecrypted_value</A></Th>
<Th><a class="firstletter">C</a><a>omputer</A></Th>
<Th><a class="firstletter">U</a><a>ser</A></Th>
</TR>\n"""
for index, cred in enumerate(results):
id, file_path, guid, status, pillaged_from_computerid, pillaged_from_userid, decrypted_with, decrypted_value = cred
data += "<TR>"
for info in [guid, status, decrypted_with]:
data += f"""<TD> {info} </TD>"""
for info in [decrypted_value]:
data += f"""<TD><A title="{info}"> {str(info)[:12]}</A></TD>"""
res = self.get_computer_infos(pillaged_from_computerid)
for index, res2 in enumerate(res):
ip, hostname = res2
computer_info = f"{ip} | {hostname}"
data += f"""<TD> {computer_info} </TD>"""
res = self.get_user_infos(pillaged_from_userid)
for index, res2 in enumerate(res):
username = res2[0]
data += f"""<TD> {username} </TD>"""
data += "</TR>\n"
data += """</TABLE><BR>\n"""
self.add_to_resultpage(data)
# finalise result page
data = "</body></html>"
self.add_to_resultpage(data)
def get_dpapi_hashes(self):
user_hashes = []
with self.conn:
cur = self.conn.cursor()
cur.execute(f"SELECT sid,hash FROM dpapi_hash")
results = cur.fetchall()
for line in results:
sid = line[0]
hash = line[1]
with self.conn:
cur = self.conn.cursor()
cur.execute(f"SELECT user_id FROM user_sid WHERE LOWER(sid)=LOWER('{sid}')")
res1 = cur.fetchall()
if len(res1) > 0:
result = res1[0]
user_id = result[0]
with self.conn:
cur = self.conn.cursor()
cur.execute(f"SELECT username FROM users WHERE id={user_id}")
res2 = cur.fetchall()
if len(res2) > 0:
result = res2[0]
username = result[0]
user_hashes.append((username, hash))
return user_hashes
def export_MKF_hashes(self):
user_hashes = self.get_dpapi_hashes()
self.logging.debug(f"Exporting {len(user_hashes)} MKF Dpapi hash to {self.options.output_directory}")
for algo_type in [1, 2]:
for context in [1, 2, 3]:
filename = os.path.join(self.options.output_directory, 'MKFv%i_type_%i' % (algo_type, context))
if os.path.exists(filename):
os.remove(filename)
for entry in user_hashes:
try:
username = entry[0]
hash = entry[1]
# on retire les hash "MACHINE$"
if username != "MACHINE$":
# Pour le moment on copie juste les hash. voir pour faire evoluer CrackHash et prendrre username:hash
algo_type = int(hash.split('*')[0][-1])
context = int(hash.split('*')[1])
filename = os.path.join(self.options.output_directory, 'MKFv%i_type_%i' % (algo_type, context))
filename2 = os.path.join(self.options.output_directory,
'MKFv%i_type_%i_WITH_USERNAME' % (algo_type, context))
f = open(filename, 'ab')
f.write(f"{hash}\n".encode('utf-8'))
f.close()
f = open(filename2, 'ab')
f.write(f"{username}:{hash}\n".encode('utf-8'))
f.close()
except Exception as ex:
self.logging.error(f"Exception in export dpapi hash to {filename}")
self.logging.debug(ex)
def get_dcc2_hashes(self):
with self.conn:
cur = self.conn.cursor()
cur.execute(
"SELECT DISTINCT username,password FROM credz WHERE LOWER(type)=LOWER('DCC2') ORDER BY username ASC ")
results = cur.fetchall()
return results
def export_dcc2_hashes(self):
user_hashes = self.get_dcc2_hashes()
filename = os.path.join(self.options.output_directory, 'hash_DCC2')
self.logging.debug(f"Exporting {len(user_hashes)} DCC2 hash to {self.options.output_directory}")
if os.path.exists(filename):
os.remove(filename)
for entry in user_hashes:
try:
username = entry[0]
hash = entry[1]
f = open(filename, 'ab')
f.write(f"{username}:{hash}\n".encode('utf-8'))
f.close()
except Exception as ex:
self.logging.error(f"Exception in export DCC2 hash to {filename}")
self.logging.debug(ex)
self.logging.debug(f"Export Done!")
def export_credz(self, distinct=True):
user_credz = self.get_credz(distinct=True)
filename = os.path.join(self.options.output_directory, 'raw_credz')
self.logging.info(f"Exporting {len(user_credz)} credz to {self.options.output_directory}")
if os.path.exists(filename):
os.remove(filename)
for index, cred in enumerate(user_credz):
username, password = cred
try:
f = open(filename, 'ab')
f.write(f"{username}:{password}\n".encode('utf-8'))
f.close()
except Exception as ex:
self.logging.error(f"Exception in export raw credz to {filename}")
self.logging.debug(ex)
self.logging.debug(f"Export Done!")
def export_sam(self):
user_credz = self.get_credz(distinct_sam=True)
filename = os.path.join(self.options.output_directory, 'raw_sam')
self.logging.info(f"Exporting {len(user_credz)} NTLM credz to {self.options.output_directory}")
if os.path.exists(filename):
os.remove(filename)
for index, cred in enumerate(user_credz):
username, password = cred
try:
f = open(filename, 'ab')
f.write(f"{username}:{password}\n".encode('utf-8'))
f.close()
except Exception as ex:
self.logging.error(f"Exception in export raw sam to {filename}")
self.logging.debug(ex)
self.logging.debug(f"Export Done!")
def export_cookies(self):
user_credz = self.get_cookies()
filename = os.path.join(self.options.output_directory, 'raw_cookies')
self.logging.info(f"Exporting {len(user_credz)} cookies to {self.options.output_directory}")
if os.path.exists(filename):
os.remove(filename)
for index, cred in enumerate(user_credz):
name, value, expires_utc, target, type, pillaged_from_computerid, pillaged_from_userid = cred
try:
f = open(filename, 'ab')
f.write(f"{target}:{name}:{value}\n".encode('utf-8'))
f.close()
except Exception as ex:
self.logging.error(f"Exception in export raw credz to {filename}")
self.logging.debug(ex)
self.logging.debug(f"Export Done!")
def export_LSA(self):
user_credz = self.get_credz(credz_type='LSA')
filename = os.path.join(self.options.output_directory, 'raw_lsa.csv')
self.logging.info(f"Exporting {len(user_credz)} LSA secrets to {self.options.output_directory}")
if os.path.exists(filename):
os.remove(filename)
for index, cred in enumerate(user_credz):
id, file_path, username, password, target, type, pillaged_from_computerid, pillaged_from_userid = cred
# Get computer infos
res = self.get_computer_infos(pillaged_from_computerid)
if '#' in username:
service = username.split('#')[0]
username = username[len(service) + 1:]
else:
service = ''
try:
for index_, res2 in enumerate(res):
ip, hostname = res2
f = open(filename, 'ab')
f.write(f"{hostname},{ip},{service},{username},{password}\n".encode('utf-8'))
f.close()
except Exception as ex:
self.logging.error(f"Exception in export raw LSA Secrets to {filename}")
self.logging.debug(ex)
self.logging.debug(f"Export Done!")
def get_credz_count(self, current_type, extra_conditions=''):
with self.conn:
cur = self.conn.cursor()
cur.execute(f"SELECT count(id) FROM credz WHERE LOWER(type)=LOWER('{current_type}') {extra_conditions}")
results = cur.fetchall()
return results
def get_certificates(self, distinct=False):
"""
Return certificates from db
"""
if distinct:
with self.conn:
cur = self.conn.cursor()
cur.execute(
"SELECT DISTINCT subject,issuer,guid FROM certificates ORDER BY subject DESC, (case when client_auth then 1 else 2 end) asc")
else:
with self.conn:
cur = self.conn.cursor()
cur.execute(
"SELECT * FROM certificates ORDER BY subject DESC, (case when client_auth then 1 else 2 end) asc")
results = cur.fetchall()
return results
def get_credz(self, filterTerm=None, credz_type=None, distinct=False, distinct_sam=False):
"""
Return credentials from the database.
"""
if credz_type:
with self.conn:
cur = self.conn.cursor()
cur.execute(f"SELECT * FROM credz WHERE LOWER(type)=LOWER('{credz_type}')")
# if we're filtering by username
elif filterTerm and filterTerm != '':
with self.conn:
cur = self.conn.cursor()
cur.execute("SELECT * FROM users WHERE LOWER(username) LIKE LOWER(?)", ['%{}%'.format(filterTerm)])
elif distinct:
with self.conn:
cur = self.conn.cursor()
cur.execute(
"SELECT DISTINCT username,password FROM credz WHERE LOWER(type) NOT IN ('sam','lsa','dcc2') AND password NOT IN ('')")
elif distinct_sam:
with self.conn:
cur = self.conn.cursor()
cur.execute(
"SELECT DISTINCT username,password FROM credz WHERE LOWER(type) IN ('sam') AND password NOT IN ('')")
# otherwise return all credentials
else:
with self.conn:
cur = self.conn.cursor()
cur.execute("SELECT * FROM credz ORDER BY type DESC, target ASC ")
results = cur.fetchall()
return results
def get_credz_sam(self):
all = []
with self.conn:
cur = self.conn.cursor()
cur.execute(
"SELECT count(DISTINCT pillaged_from_computerid),password FROM credz WHERE LOWER(type)=LOWER('SAM') AND LOWER(password) != LOWER('31d6cfe0d16ae931b73c59d7e0c089c0') GROUP BY password ORDER BY username ASC")
results = cur.fetchall()
for index, res in enumerate(results):
nb, passw = res
if nb > 1:
with self.conn:
cur = self.conn.cursor()
cur.execute(
"SELECT DISTINCT username, password, type, pillaged_from_computerid FROM credz WHERE LOWER(type)=LOWER('SAM') AND LOWER(password)=LOWER('%s') ORDER BY password ASC, username ASC " % (
passw))
all += cur.fetchall()
return all
def get_computers(self):
with self.conn:
cur = self.conn.cursor()
cur.execute(
"SELECT ip,hostname,domain,os,smb_signing_enabled,smbv1_enabled,is_admin,connectivity from computers ORDER BY ip")
results = cur.fetchall()
return results
def get_browser_version(self, browser_type, pillaged_from_computerid, pillaged_from_userid):
with self.conn:
cur = self.conn.cursor()
cur.execute(
f"SELECT version from browser_version WHERE browser_type LIKE '{browser_type}' AND pillaged_from_computerid='{pillaged_from_computerid}' AND pillaged_from_userid='{pillaged_from_userid}' LIMIT 1")
results = cur.fetchall()
return results
def get_masterkeys(self):
with self.conn:
cur = self.conn.cursor()
cur.execute(
"SELECT id,file_path,guid,status,pillaged_from_computerid,pillaged_from_userid,decrypted_with,decrypted_value from masterkey ORDER BY pillaged_from_computerid ASC, pillaged_from_userid ASC")
results = cur.fetchall()
return results
def get_computer_infos(self, computer_id):
with self.conn:
cur = self.conn.cursor()
cur.execute(f"SELECT ip,hostname FROM computers WHERE id={computer_id} LIMIT 1")
results = cur.fetchall()
return results
def get_user_infos(self, user_id):
with self.conn:
cur = self.conn.cursor()
cur.execute(f"SELECT username FROM users WHERE id={user_id} LIMIT 1")
results = cur.fetchall()
return results
def get_user_id(self, username):
with self.conn:
cur = self.conn.cursor()
cur.execute(f"SELECT id FROM users WHERE username={username} LIMIT 1")
results = cur.fetchall()
return results
def get_connected_user(self):
with self.conn:
cur = self.conn.cursor()
cur.execute(f"SELECT ip,username FROM connected_user ORDER BY username ASC, ip ASC")
results = cur.fetchall()
return results
def get_os_from_ip(self, ip):
with self.conn:
cur = self.conn.cursor()
cur.execute(f"SELECT os FROM computers WHERE ip={ip} LIMIT 1")
results = cur.fetchall()
return results
def get_file(self):
with self.conn:
cur = self.conn.cursor()
cur.execute(
f"SELECT file_path,filename,extension,pillaged_from_computerid,pillaged_from_userid FROM files ORDER BY pillaged_from_computerid ASC, extension ASC ")
results = cur.fetchall()
return results
def get_cookies(self):
with self.conn:
cur = self.conn.cursor()
cur.execute(
f"SELECT name,value,expires_utc,target,type,pillaged_from_computerid,pillaged_from_userid FROM cookies ORDER BY pillaged_from_computerid ASC, pillaged_from_userid ASC, target ASC, expires_utc DESC ")
results = cur.fetchall()
return results
class database:
def __init__(self, conn, logger):
self.conn = conn
self.logging = logger
def get_credz(self, filterTerm=None, credz_type=None):
"""
Return credentials from the database.
"""
if credz_type:
with self.conn:
cur = self.conn.cursor()
cur.execute(f"SELECT * FROM credz WHERE type='{credz_type}'")
# if we're filtering by username
elif filterTerm and filterTerm != '':
with self.conn:
cur = self.conn.cursor()
cur.execute("SELECT * FROM users WHERE LOWER(username) LIKE LOWER(?)", ['%{}%'.format(filterTerm)])
# otherwise return all credentials
else:
with self.conn:
cur = self.conn.cursor()
cur.execute("SELECT * FROM credz")
results = cur.fetchall()
return results
@staticmethod
def db_schema(db_conn):
db_conn.execute('''CREATE TABLE "computers" (
"id" integer PRIMARY KEY,
"ip" text,
"hostname" text,
"domain" text,
"os" text,
"dc" boolean,
"smb_signing_enabled" boolean,
"smbv1_enabled" boolean,
'default_user_id' integer,
"is_admin" boolean,
"connectivity" text
)''')
db_conn.execute('''CREATE TABLE "compliance" (
"id" integer PRIMARY KEY,
"laps_enabled" boolean DEFAULT 0,
"smb_signing_enabled" boolean DEFAULT 0,
"smbv1_enabled" boolean DEFAULT 0,
"llmnr_disabled" boolean DEFAULT 0,
"pillaged_from_computerid" integer,
FOREIGN KEY(pillaged_from_computerid) REFERENCES computers(id)
)''')
# type = hash, plaintext
db_conn.execute('''CREATE TABLE "users" (
"id" integer PRIMARY KEY,
"domain" text,
"username" text,
"password" text,
"credtype" text,
"pillaged_from_computerid" integer,
FOREIGN KEY(pillaged_from_computerid) REFERENCES computers(id)
)''')
db_conn.execute('''CREATE TABLE "groups" (
"id" integer PRIMARY KEY,
"domain" text,
"name" text
)''')
db_conn.execute('''CREATE TABLE "credz" (
"id" integer PRIMARY KEY,
"file_path" text,
"username" text,
"password" text,
"target" text,
"type" text,
"pillaged_from_computerid" integer,
"pillaged_from_userid" integer,
FOREIGN KEY(pillaged_from_computerid) REFERENCES computers(id),
FOREIGN KEY(pillaged_from_userid) REFERENCES users(id)
)''')
db_conn.execute('''CREATE TABLE "certificates" (
"id" integer PRIMARY KEY,
"pfx_file_path" text,
"guid" text,
"issuer" text,
"subject" text,
"client_auth" bool,
"pillaged_from_computerid" integer,
"pillaged_from_userid" integer,
FOREIGN KEY(pillaged_from_computerid) REFERENCES computers(id),
FOREIGN KEY(pillaged_from_userid) REFERENCES users(id)
)''')
db_conn.execute('''CREATE TABLE "cookies" (
"id" integer PRIMARY KEY,
"file_path" text,
"name" text,
"value" text,
"expires_utc" int,
"target" text,
"type" text,
"pillaged_from_computerid" integer,
"pillaged_from_userid" integer,
FOREIGN KEY(pillaged_from_computerid) REFERENCES computers(id),
FOREIGN KEY(pillaged_from_userid) REFERENCES users(id)
)''')
db_conn.execute('''CREATE TABLE "dpapi_hash" (
"id" integer PRIMARY KEY,
"file_path" text,
"sid" text,
"guid" text,
"hash" text,
"context" text,
"pillaged_from_computerid" integer,
FOREIGN KEY(pillaged_from_computerid) REFERENCES computers(id)
)''')
db_conn.execute('''CREATE TABLE "user_sid" (
"id" integer PRIMARY KEY,
"sid" text,
"user_id" integer,
FOREIGN KEY(user_id) REFERENCES users(id)
)''')
db_conn.execute('''CREATE TABLE "connected_user" (
"id" integer PRIMARY KEY,
"username" text,
"ip" text
)''')
db_conn.execute('''CREATE TABLE "files" (
"id" integer PRIMARY KEY,
"file_path" text,
"filename" text,
"extension" text,
"pillaged_from_computerid" integer,
"pillaged_from_userid" integer,
FOREIGN KEY(pillaged_from_computerid) REFERENCES computers(id),
FOREIGN KEY(pillaged_from_userid) REFERENCES users(id)
)''')
db_conn.execute('''CREATE TABLE "masterkey" (
"id" integer PRIMARY KEY,
"file_path" text,
"guid" text,
"status" integer DEFAULT 0,
"pillaged_from_computerid" integer,
"pillaged_from_userid" integer,
"decrypted_with" text,
"decrypted_value" text,
FOREIGN KEY(pillaged_from_computerid) REFERENCES computers(id),
FOREIGN KEY(pillaged_from_userid) REFERENCES users(id)
)''')
db_conn.execute('''CREATE TABLE "browser_version" (
"id" integer PRIMARY KEY,
"browser_type" text,
"version" text,
"pillaged_from_computerid" integer,
"pillaged_from_userid" integer,
FOREIGN KEY(pillaged_from_computerid) REFERENCES computers(id),
FOREIGN KEY(pillaged_from_userid) REFERENCES users(id)
)''')
def add_browser_version(self, browser_type, version, pillaged_from_computerid=None, pillaged_from_computer_ip=None,
pillaged_from_userid=None, pillaged_from_username=None):
"""
Check if this host has already been added to the database, if not add it in.
"""
self.logging.debug(
f"Adding Browser version {browser_type} : {version} - from user {pillaged_from_username}")
try:
if pillaged_from_userid == None and pillaged_from_username != None:
with self.conn:
cur = self.conn.cursor()
cur.execute(f"SELECT id FROM users WHERE username='{pillaged_from_username}'")
results = cur.fetchall()
if len(results) > 0:
result = results[0]
pillaged_from_userid = result[0]
# print(f"{pillaged_from_userid} is {pillaged_from_username}")
if pillaged_from_computer_ip != None:
with self.conn:
cur = self.conn.cursor()
cur.execute(f"SELECT * FROM computers WHERE LOWER(ip)=LOWER('{pillaged_from_computer_ip}')")
results = cur.fetchall()
if len(results) > 0:
result = results[0]
pillaged_from_computerid = result[0]
if pillaged_from_computerid != None:
with self.conn:
cur = self.conn.cursor()
cur.execute(
f'SELECT * FROM browser_version WHERE browser_type LIKE "{browser_type}" AND pillaged_from_computerid={pillaged_from_computerid} AND pillaged_from_userid={pillaged_from_userid}')
results = cur.fetchall()
if not len(results):
# self.logging.debug(f"inserting file {filename} - {file_path} -{extension}")
cur.execute(
f"INSERT INTO browser_version (browser_type,version,pillaged_from_computerid,pillaged_from_userid) VALUES ('{browser_type}', '{version}', '{pillaged_from_computerid}', {pillaged_from_userid})")
except Exception as ex:
self.logging.error(f"Exception in Add browser_version")
self.logging.debug(ex)
def add_computer(self, ip, hostname='', domain='', os='', default_user_id=0, dc=0, smb_signing_enabled=False,
smbv1_enabled=False, is_admin=False, connectivity='Ok'):
"""
Check if this host has already been added to the database, if not add it in.
"""
self.logging.debug(f"[{ip}] {bcolors.OKBLUE}Adding Computer {hostname}{bcolors.ENDC}")
try:
# domain = domain.split('.')[0].upper()
with self.conn:
cur = self.conn.cursor()
cur.execute(f'SELECT * FROM computers WHERE ip LIKE "{ip}"')
results = cur.fetchall()
if not len(results):
with self.conn:
cur = self.conn.cursor()
cur.execute(
f"INSERT INTO computers (ip, hostname, domain, os, dc, default_user_id,smb_signing_enabled,is_admin,smbv1_enabled,connectivity) VALUES ('{ip}', '{hostname}', '{domain}', '{os}', {dc},{default_user_id},{smb_signing_enabled},{is_admin},{smbv1_enabled},'{connectivity}')")
return cur.lastrowid
except Exception as ex:
self.logging.error(f"Exception in Add Computeur")
self.logging.debug(ex)
def update_computer(self, ip, hostname=None, domain=None, os=None, default_user_id=None, dc=None,
smb_signing_enabled=None, smbv1_enabled=None, is_admin=None, connectivity=None):
"""
Check if this host has already been added to the database, if not add it in.
"""
self.logging.debug(f"Updating Computer {ip}")
try:
# domain = domain.split('.')[0].upper()
with self.conn:
cur = self.conn.cursor()
cur.execute(f'SELECT * FROM computers WHERE ip LIKE "{ip}"')
results = cur.fetchall()
if len(results):
for host in results:
id_ = host[0]
hostname_ = host[2]
domain_ = host[3]
os_ = host[4]
dc_ = host[5]
is_admin_ = host[8]
connectivity_ = host[9]
for val in [(hostname, 'hostname'), (domain, 'domain'), (os, 'os'), (hostname, 'hostname'),
(default_user_id, 'default_user_id'), (dc, 'dc'),
(smb_signing_enabled, 'smb_signing_enabled'), (smbv1_enabled, 'smbv1_enabled'),
(is_admin, 'is_admin'), (connectivity, 'connectivity')]:
value = val[0]
var = val[1]
if value != None:
with self.conn:
cur = self.conn.cursor()
cur.execute(f"UPDATE computers SET {var}='{value}' WHERE id={id_}")
except Exception as ex:
self.logging.error(f"Exception in Add Computeur")
self.logging.debug(ex)
def add_file(self, file_path, filename, extension, pillaged_from_computerid=None, pillaged_from_computer_ip=None,
pillaged_from_userid=None, pillaged_from_username=None):
"""
Check if this host has already been added to the database, if not add it in.
"""
self.logging.debug(
f"Adding file {filename} - path : {file_path} - {extension} - from user {pillaged_from_username}")
try:
# domain = domain.split('.')[0].upper()
if pillaged_from_userid == None and pillaged_from_username != None:
with self.conn:
cur = self.conn.cursor()
cur.execute(f"SELECT id FROM users WHERE username='{pillaged_from_username}'")
results = cur.fetchall()
if len(results) > 0:
result = results[0]
pillaged_from_userid = result[0]
# print(f"{pillaged_from_userid} is {pillaged_from_username}")
if pillaged_from_computer_ip != None:
with self.conn:
cur = self.conn.cursor()
cur.execute(f"SELECT * FROM computers WHERE LOWER(ip)=LOWER('{pillaged_from_computer_ip}')")
results = cur.fetchall()
if len(results) > 0:
result = results[0]
pillaged_from_computerid = result[0]
if pillaged_from_computerid != None:
with self.conn:
cur = self.conn.cursor()
cur.execute(
f'SELECT * FROM files WHERE filename LIKE "{filename}" AND pillaged_from_computerid={pillaged_from_computerid}')
results = cur.fetchall()
if not len(results):
# self.logging.debug(f"inserting file {filename} - {file_path} -{extension}")
cur.execute(
f"INSERT INTO files (file_path,filename,extension,pillaged_from_computerid,pillaged_from_userid) VALUES ('{file_path}', '{filename}', '{extension}', '{pillaged_from_computerid}', {pillaged_from_userid})")
except Exception as ex:
self.logging.error(f"Exception in Add Files")
self.logging.debug(ex)
def add_masterkey(self, file_path, guid, status, decrypted_with='', decrypted_value='', pillaged_from_computerid=None,
pillaged_from_computer_ip=None, pillaged_from_userid=None, pillaged_from_username=None):
"""
Check if this host has already been added to the database, if not add it in.
"""
self.logging.debug(
f"[{pillaged_from_computer_ip}] Adding Masterkey {guid} - path : {file_path} - from user {pillaged_from_username} - {status} ")
try:
# domain = domain.split('.')[0].upper()
if pillaged_from_userid == None and pillaged_from_username != None:
with self.conn:
cur = self.conn.cursor()
cur.execute(f"SELECT id FROM users WHERE username='{pillaged_from_username}'")
results = cur.fetchall()
if len(results) > 0:
result = results[0]
pillaged_from_userid = result[0]
self.logging.debug(
f"[{pillaged_from_computer_ip}] {pillaged_from_userid} is {pillaged_from_username}")
if pillaged_from_computer_ip != None:
with self.conn:
cur = self.conn.cursor()
cur.execute(f"SELECT * FROM computers WHERE LOWER(ip)=LOWER('{pillaged_from_computer_ip}')")
results = cur.fetchall()
if len(results) > 0:
result = results[0]
pillaged_from_computerid = result[0]
self.logging.debug(
f"[{pillaged_from_computer_ip}] {pillaged_from_computer_ip} is {pillaged_from_computerid}")
if pillaged_from_computerid != None:
with self.conn:
cur = self.conn.cursor()
cur.execute(
f'SELECT * FROM masterkey WHERE guid LIKE "{guid}" AND pillaged_from_computerid={pillaged_from_computerid}')
results = cur.fetchall()
if not len(results):
with self.conn:
cur = self.conn.cursor()
self.logging.debug(
f"[{pillaged_from_computer_ip}] inserting Masterkey {guid} - {file_path} -{status} {pillaged_from_computerid}', {pillaged_from_userid},{decrypted_with},{decrypted_value}")
cur.execute(
f"INSERT INTO masterkey (file_path,guid,status,pillaged_from_computerid,pillaged_from_userid,decrypted_with,decrypted_value) VALUES ('{file_path}', '{guid}', '{status}', '{pillaged_from_computerid}', {pillaged_from_userid},'{decrypted_with}','{decrypted_value}')")
else:
for masterkey in results:
if (status != masterkey[3]) or (decrypted_with != masterkey[6]) or (
decrypted_value != masterkey[7]):
with self.conn:
cur = self.conn.cursor()
cur.execute(
f"UPDATE masterkey SET status='{status}', decrypted_with='{decrypted_with}', decrypted_value='{decrypted_value}' WHERE id={masterkey[0]}")
except Exception as ex:
self.logging.error(f"Exception in Add Masterkey")
self.logging.debug(ex)
def update_masterkey(self, file_path, guid, status, decrypted_with=None, decrypted_value=None,
pillaged_from_computerid=None, pillaged_from_computer_ip=None, pillaged_from_userid=None,
pillaged_from_username=None):
"""
Check if this host has already been added to the database, if not add it in.
"""
self.logging.debug(f"Updating Masterkey {guid} {status} {decrypted_value} {decrypted_with}")
try:
if pillaged_from_computer_ip != None:
with self.conn:
cur = self.conn.cursor()
cur.execute(f"SELECT * FROM computers WHERE LOWER(ip)=LOWER('{pillaged_from_computer_ip}')")
results = cur.fetchall()
if len(results) > 0:
result = results[0]
pillaged_from_computerid = result[0]
self.logging.debug(
f"[{pillaged_from_computer_ip}] {pillaged_from_computer_ip} is {pillaged_from_computerid}")
if pillaged_from_computerid != None:
with self.conn:
cur = self.conn.cursor()
cur.execute(
f'SELECT * FROM masterkey WHERE guid LIKE "{guid}" AND pillaged_from_computerid={pillaged_from_computerid}')
results = cur.fetchall()
if len(results):
self.logging.debug("Found initial Masterkey")
for masterkey in results:
with self.conn:
cur = self.conn.cursor()
cur.execute(
f"UPDATE masterkey SET status='{status}', decrypted_with='{decrypted_with}', decrypted_value='{decrypted_value}' WHERE id={masterkey[0]}")
except Exception as ex:
self.logging.debug(f"Exception in update Masterkey")
self.logging.debug(ex)
def add_connected_user(self, ip, username):
"""
Check connected users to the machine
"""
self.logging.info(f"Adding connected user {username} from {ip}")
ip = ip.replace('\\', '')
try:
# domain = domain.split('.')[0].upper()
with self.conn:
cur = self.conn.cursor()
cur.execute(f'SELECT * FROM connected_user WHERE username LIKE "{username}" AND ip LIKE "{ip}"')
results = cur.fetchall()
if not len(results):
with self.conn:
cur = self.conn.cursor()
cur.execute(f"INSERT INTO connected_user (username, ip) VALUES ('{username}','{ip}')")
return cur.lastrowid
except Exception as ex:
self.logging.error(f"Exception in Add Connected users")
self.logging.debug(ex)
def add_user(self, domain='', username='', password='', credtype='', pillaged_from_computerid=None,
pillaged_from_computer_ip=None):
try:
# domain = domain.split('.')[0].upper()
user_rowid = None
if pillaged_from_computer_ip != None:
with self.conn:
cur = self.conn.cursor()
cur.execute(f"SELECT * FROM computers WHERE LOWER(ip)=LOWER('{pillaged_from_computer_ip}')")
results = cur.fetchall()
if len(results) > 0:
result = results[0]
pillaged_from_computerid = result[0]
if pillaged_from_computerid != None:
query = f"SELECT * FROM users WHERE LOWER(domain)=LOWER('{domain}') AND LOWER(username)=LOWER('{username}') AND pillaged_from_computerid={pillaged_from_computerid}"
self.logging.debug(query)
with self.conn:
cur = self.conn.cursor()
cur.execute(query)
results = cur.fetchall()
if not len(results):
query = f"INSERT INTO users (domain, username, password, credtype, pillaged_from_computerid) VALUES ('{domain}','{username}','{password}','{credtype}',{pillaged_from_computerid})"
self.logging.debug(query)
with self.conn:
cur = self.conn.cursor()
cur.execute(query)
user_rowid = cur.lastrowid
self.logging.debug('add_user(domain={}, username={}) => {}'.format(domain, username, user_rowid))
else:
self.logging.debug('add_user(domain={}, username={}) ALREADY EXIST'.format(domain, username))
else:
self.logging.error(f"user {username} associated computer not found ")
except Exception as ex:
self.logging.error(f"Exception in add_user ")
self.logging.debug(ex)
return user_rowid
def add_sid(self, username=None, user_id=None, sid=None):
try:
if user_id == None and username != None:
with self.conn:
cur = self.conn.cursor()
cur.execute(f"SELECT id FROM users WHERE LOWER(username)=LOWER('{username}')")
results = cur.fetchall()
if len(results) > 0:
result = results[0]
user_id = result[0]
if user_id != None and sid != None:
# Deja en base ?
query = f"SELECT * FROM user_sid WHERE LOWER(sid)=LOWER('{sid}') AND user_id={user_id}"
self.logging.debug(query)
with self.conn:
cur = self.conn.cursor()
cur.execute(query)
results = cur.fetchall()
if not len(results):
query = f"INSERT INTO user_sid (user_id, sid) VALUES ('{user_id}','{sid}')"
self.logging.debug(query)
with self.conn:
cur = self.conn.cursor()
cur.execute(query)
user_rowid = cur.lastrowid
self.logging.debug(f'added SID {sid} for user id {user_id}')
except Exception as ex:
self.logging.error(f"Exception in add_sid ")
self.logging.debug(ex)
self.logging.debug(f"Added {username} sid {sid} to database")
return 1
def add_dpapi_hash(self, file_path=None, sid=None, guid=None, hash=None, context=None, pillaged_from_computerid=None,
pillaged_from_computer_ip=None):
try:
# domain = domain.split('.')[0].upper()
user_rowid = None
if pillaged_from_computer_ip != None:
with self.conn:
cur = self.conn.cursor()
cur.execute(f"SELECT * FROM computers WHERE LOWER(ip)=LOWER('{pillaged_from_computer_ip}')")
results = cur.fetchall()
if len(results) > 0:
result = results[0]
pillaged_from_computerid = result[0]
if pillaged_from_computerid != None and sid != None and guid != None and hash != None and context != None:
query = f"SELECT * FROM dpapi_hash WHERE LOWER(sid)=LOWER('{sid}') AND LOWER(guid)=LOWER('{guid}') AND LOWER(hash)=LOWER('{hash}') AND pillaged_from_computerid={pillaged_from_computerid} AND LOWER(context)=LOWER('{context}')"
self.logging.debug(query)
with self.conn:
cur = self.conn.cursor()
cur.execute(query)
results = cur.fetchall()
if not len(results):
query = f"INSERT INTO dpapi_hash (file_path, sid, guid, hash, context, pillaged_from_computerid) VALUES ('{file_path}','{sid}','{guid}','{hash}','{context}',{pillaged_from_computerid})"
self.logging.debug(query)
with self.conn:
cur = self.conn.cursor()
cur.execute(query)
user_rowid = cur.lastrowid
self.logging.debug(f'added DPAPI hash {hash}')
else:
self.logging.debug(f'DPAPI hash {hash} ALREADY EXIST')
else:
self.logging.error(
f"missing infos to register DPAPI hash {hash} - {file_path},{sid},{guid},{hash},{context},{pillaged_from_computerid}")
except Exception as ex:
self.logging.error(f"Exception in add_hash ")
self.logging.debug(ex)
return user_rowid
def clear_input(self, data):
if isinstance(data, int):
return data
if data is None:
data = ''
result = data.replace('\x00', '')
return result
def add_certificate(self, guid, pfx_file_path, issuer, subject, client_auth, pillaged_from_computerid=None,
pillaged_from_userid=None, pillaged_from_computer_ip=None, pillaged_from_username=None):
"""
Check if this cert has already been added to db, if not then add it
"""
user_rowid = None
try:
guid = self.clear_input(guid)
pfx_file_path = self.clear_input(pfx_file_path)
issuer = self.clear_input(issuer)
subject = self.clear_input(subject)
self.logging.debug(f"{guid} - {binascii.hexlify(guid.encode('utf-8'))}")
self.logging.debug(f"{issuer} - {binascii.hexlify(issuer.encode('utf-8'))}")
self.logging.debug(f"{subject} - {binascii.hexlify(subject.encode('utf-8'))}")
self.logging.debug(f"{client_auth}")
self.logging.debug(f"{pfx_file_path} - {binascii.hexlify(pfx_file_path.encode('utf-8'))}")
self.logging.debug(
f"pillaged_from_computer_ip {pillaged_from_computer_ip} - {binascii.hexlify(pillaged_from_computer_ip.encode('utf-8'))}")
self.logging.debug(f"pillaged_from_username {pillaged_from_username}")
if pillaged_from_computer_ip != None:
with self.conn:
cur = self.conn.cursor()
cur.execute(f"SELECT * FROM computers WHERE LOWER(ip)=LOWER('{pillaged_from_computer_ip}')")
results = cur.fetchall()
if len(results) > 0:
result = results[0]
pillaged_from_computerid = result[0]
self.logging.debug(f"[+] Resolved {pillaged_from_computer_ip} to id : {pillaged_from_computerid}")
except Exception as ex:
self.logging.error(f"Exception in add_certificate 1")
self.logging.debug(ex)
try:
if pillaged_from_username != None:
with self.conn:
cur = self.conn.cursor()
cur.execute(
f"SELECT * FROM users WHERE LOWER(username)=LOWER('{pillaged_from_username}') AND pillaged_from_computerid={pillaged_from_computerid}")
results = cur.fetchall()
if len(results) > 0:
result = results[0]
pillaged_from_userid = result[0]
self.logging.debug(
f"[+] Resolved {pillaged_from_username} on machine {pillaged_from_computerid} to id : {pillaged_from_userid}")
except Exception as ex:
self.logging.error(f"Exception in add_certificate 2")
self.logging.debug(ex)
pass
if pillaged_from_computerid == None or pillaged_from_userid == None:
self.logging.debug(
f"[-] Missing computerId or UserId to register Certificate {pillaged_from_username} {pfx_file_path}")
# return None
try:
if pillaged_from_userid == None:
query = "SELECT * FROM certificates WHERE LOWER(guid)=LOWER(:guid) AND LOWER(subject)=LOWER(:subject) AND LOWER(issuer)=LOWER(:issuer) AND pillaged_from_computerid=:pillaged_from_computerid"
parameters = {
"guid": guid,
"subject": subject,
"issuer": issuer,
"pillaged_from_computerid": int(pillaged_from_computerid),
}
else:
query = "SELECT * FROM certificates WHERE LOWER(guid)=LOWER(:guid) AND LOWER(subject)=LOWER(:subject) AND LOWER(issuer)=LOWER(:issuer) AND pillaged_from_computerid=:pillaged_from_computerid AND pillaged_from_userid=:pillaged_from_userid"
parameters = {
"guid": guid,
"subject": subject,
"issuer": issuer,
"pillaged_from_computerid": int(pillaged_from_computerid),
"pillaged_from_computerid": int(pillaged_from_computerid),
"pillaged_from_userid": int(pillaged_from_userid)
}
self.logging.debug(query)
with self.conn:
cur = self.conn.cursor()
cur.execute(query, parameters)
results = cur.fetchall()
except Exception as ex:
self.logging.error(f"Exception in add_certificate 3")
self.logging.debug(ex)
try:
if not result or not len(results):
if pillaged_from_userid == None:
query = "INSERT INTO certificates (pfx_file_path, guid, issuer, subject, client_auth, pillaged_from_computerid) VALUES (:pfx_file_path, :guid, :issuer, :subject, :client_auth, :pillaged_from_computerid)"
parameters = {
"pfx_file_path": pfx_file_path,
"guid": guid,
"issuer": issuer,
"subject": subject,
"client_auth": client_auth,
"pillaged_from_computerid": int(pillaged_from_computerid),
}
else:
query = "INSERT INTO certificates (pfx_file_path, guid, issuer, subject, client_auth, pillaged_from_computerid, pillaged_from_userid) VALUES (:pfx_file_path, :guid, :issuer, :subject, :client_auth, :pillaged_from_computerid, :pillaged_from_userid)"
parameters = {
"pfx_file_path": pfx_file_path,
"guid": guid,
"issuer": issuer,
"subject": subject,
"client_auth": client_auth,
"pillaged_from_computerid": int(pillaged_from_computerid),
"pillaged_from_userid": int(pillaged_from_userid),
}
self.logging.debug(query)
with self.conn:
cur = self.conn.cursor()
cur.execute(query, parameters)
user_rowid = cur.lastrowid
self.logging.debug(
f'added_certificate(guid={guid}, issuer={issuer}, subject={subject}, client_auth={client_auth}) => {user_rowid}')
else:
self.logging.debug(
f'added_certificate(guid={guid}, issuer={issuer}, subject={subject}, client_auth={client_auth}) => ALREADY IN DB')
except Exception as ex:
self.logging.error(f"Exception in add_certificates 4")
self.logging.debug(ex)
def add_credz(self, credz_type, credz_username, credz_password, credz_target, credz_path, pillaged_from_computerid=None,
pillaged_from_userid=None, pillaged_from_computer_ip=None, pillaged_from_username=None):
"""
Check if this credential has already been added to the database, if not add it in.
"""
user_rowid = None
try:
credz_username = self.clear_input(credz_username)
credz_password = self.clear_input(credz_password)
credz_target = self.clear_input(credz_target)
credz_path = self.clear_input(credz_path)
self.logging.debug(f"{credz_username} - {binascii.hexlify(credz_username.encode('utf-8'))}")
self.logging.debug(f"{credz_password} - {binascii.hexlify(credz_password.encode('utf-8'))}")
self.logging.debug(f"{credz_target} - {binascii.hexlify(credz_target.encode('utf-8'))}")
self.logging.debug(f"{credz_path} - {binascii.hexlify(credz_path.encode('utf-8'))}")
self.logging.debug(
f"pillaged_from_computer_ip {pillaged_from_computer_ip} - {binascii.hexlify(pillaged_from_computer_ip.encode('utf-8'))}")
self.logging.debug(f"pillaged_from_username {pillaged_from_username}")
if pillaged_from_computer_ip != None:
with self.conn:
cur = self.conn.cursor()
cur.execute(f"SELECT * FROM computers WHERE LOWER(ip)=LOWER('{pillaged_from_computer_ip}')")
results = cur.fetchall()
if len(results) > 0:
result = results[0]
pillaged_from_computerid = result[0]
self.logging.debug(f"[+] Resolved {pillaged_from_computer_ip} to id : {pillaged_from_computerid}")
except Exception as ex:
self.logging.error(f"Exception in add_credz 1")
self.logging.debug(ex)
try:
if pillaged_from_username != None:
with self.conn:
cur = self.conn.cursor()
cur.execute(
f"SELECT * FROM users WHERE LOWER(username)=LOWER('{pillaged_from_username}') AND pillaged_from_computerid={pillaged_from_computerid}")
results = cur.fetchall()
if len(results) > 0:
result = results[0]
pillaged_from_userid = result[0]
self.logging.debug(
f"[+] Resolved {pillaged_from_username} on machine {pillaged_from_computerid} to id : {pillaged_from_userid}")
except Exception as ex:
self.logging.error(f"Exception in add_credz 2")
self.logging.debug(ex)
pass
if pillaged_from_computerid == None or pillaged_from_userid == None:
self.logging.debug(
f"[-] Missing computerId or UserId to register Credz {credz_username} {credz_password} - {credz_target}")
# return None
try:
if pillaged_from_userid == None:
query = "SELECT * FROM credz WHERE LOWER(username)=LOWER(:credz_username) AND LOWER(password)=LOWER(:credz_password) AND LOWER(type)=LOWER(:credz_type) AND LOWER(target)=LOWER(:credz_target) AND pillaged_from_computerid=:pillaged_from_computerid"
parameters = {
"credz_username": credz_username,
"credz_password": credz_password,
"credz_type": credz_type, "credz_target": credz_target,
"pillaged_from_computerid": int(pillaged_from_computerid),
}
else:
query = "SELECT * FROM credz WHERE LOWER(username)=LOWER(:credz_username) AND LOWER(password)=LOWER(:credz_password) AND LOWER(type)=LOWER(:credz_type) AND LOWER(target)=LOWER(:credz_target) AND pillaged_from_computerid=:pillaged_from_computerid AND pillaged_from_userid=:pillaged_from_userid"
parameters = {
"credz_username": credz_username,
"credz_password": credz_password,
"credz_type": credz_type, "credz_target": credz_target,
"pillaged_from_computerid": int(pillaged_from_computerid),
"pillaged_from_userid": int(pillaged_from_userid)
}
self.logging.debug(query)
with self.conn:
cur = self.conn.cursor()
cur.execute(query, parameters)
results = cur.fetchall()
except Exception as ex:
self.logging.error(f"Exception in add_credz 3")
self.logging.debug(ex)
try:
if not len(results):
if pillaged_from_userid == None:
query = "INSERT INTO credz (username, password, target, type, pillaged_from_computerid, file_path) VALUES (:credz_username, :credz_password, :credz_target, :credz_type, :pillaged_from_computerid, :credz_path)"
parameters = {
"credz_username": credz_username,
"credz_password": credz_password,
"credz_target": credz_target,
"credz_type": credz_type,
"pillaged_from_computerid": int(pillaged_from_computerid),
"credz_path": credz_path,
}
else:
query = "INSERT INTO credz (username, password, target, type, pillaged_from_computerid,pillaged_from_userid, file_path) VALUES (:credz_username, :credz_password, :credz_target, :credz_type, :pillaged_from_computerid, :pillaged_from_userid, :credz_path)"
parameters = {
"credz_username": credz_username,
"credz_password": credz_password,
"credz_type": credz_type,
"credz_target": credz_target,
"pillaged_from_computerid": int(pillaged_from_computerid),
"pillaged_from_userid": int(pillaged_from_userid),
"credz_path": credz_path,
}
self.logging.debug(query)
with self.conn:
cur = self.conn.cursor()
cur.execute(query, parameters)
user_rowid = cur.lastrowid
self.logging.debug(
f'added_credential(credtype={credz_type}, target={credz_target}, username={credz_username}, password={credz_password}) => {user_rowid}')
else:
self.logging.debug(
f'added_credential(credtype={credz_type}, target={credz_target}, username={credz_username}, password={credz_password}) => ALREADY IN DB')
except Exception as ex:
self.logging.error(f"Exception in add_credz 4")
self.logging.debug(ex)
return None
def add_cookies(self, credz_type, credz_name, credz_value, credz_expires_utc, credz_target, credz_path,
pillaged_from_computerid=None, pillaged_from_userid=None, pillaged_from_computer_ip=None,
pillaged_from_username=None):
"""
Check if this credential has already been added to the database, if not add it in.
"""
user_rowid = None
try:
credz_name = self.clear_input(credz_name)
self.logging.debug(f"{credz_name} - {binascii.hexlify(credz_name.encode('utf-8'))}")
credz_value = self.clear_input(credz_value)
self.logging.debug(f"{credz_value} - {binascii.hexlify(credz_value.encode('utf-8'))}")
credz_expires_utc = self.clear_input(credz_expires_utc)
self.logging.debug(f"{credz_expires_utc}")
credz_target = self.clear_input(credz_target)
self.logging.debug(f"{credz_target} - {binascii.hexlify(credz_target.encode('utf-8'))}")
credz_path = self.clear_input(credz_path)
self.logging.debug(f"{credz_path} - {binascii.hexlify(credz_path.encode('utf-8'))}")
self.logging.debug(
f"pillaged_from_computer_ip {pillaged_from_computer_ip} - {binascii.hexlify(pillaged_from_computer_ip.encode('utf-8'))}")
self.logging.debug(f"pillaged_from_username {pillaged_from_username}")
if pillaged_from_computer_ip != None:
with self.conn:
cur = self.conn.cursor()
cur.execute(f"SELECT * FROM computers WHERE LOWER(ip)=LOWER('{pillaged_from_computer_ip}')")
results = cur.fetchall()
if len(results) > 0:
result = results[0]
pillaged_from_computerid = result[0]
self.logging.debug(f"[+] Resolved {pillaged_from_computer_ip} to id : {pillaged_from_computerid}")
except Exception as ex:
self.logging.error(f"Exception in add_cookie 1")
self.logging.debug(ex)
try:
if pillaged_from_username != None:
with self.conn:
cur = self.conn.cursor()
cur.execute(
f"SELECT * FROM users WHERE LOWER(username)=LOWER('{pillaged_from_username}') AND pillaged_from_computerid={pillaged_from_computerid}")
results = cur.fetchall()
if len(results) > 0:
result = results[0]
pillaged_from_userid = result[0]
self.logging.debug(
f"[+] Resolved {pillaged_from_username} on machine {pillaged_from_computerid} to id : {pillaged_from_userid}")
except Exception as ex:
self.logging.error(f"Exception in add_cookies 2")
self.logging.debug(ex)
pass
if pillaged_from_computerid == None or pillaged_from_userid == None:
self.logging.debug(
f"[-] Missing computerId or UserId to register Cookie {credz_name} {credz_value} - {credz_target}")
# return None
try:
if pillaged_from_userid == None:
query = "SELECT * FROM cookies WHERE LOWER(name)=LOWER(:credz_name) AND LOWER(value)=LOWER(:credz_value) AND expires_utc=:credz_expires_utc AND LOWER(type)=LOWER(:credz_type) AND LOWER(target)=LOWER(:credz_target) AND pillaged_from_computerid=:pillaged_from_computerid"
parameters = {
"credz_name": credz_name,
"credz_value": credz_value,
"credz_expires_utc": credz_expires_utc,
"credz_type": credz_type, "credz_target": credz_target,
"pillaged_from_computerid": int(pillaged_from_computerid),
}
else:
query = "SELECT * FROM cookies WHERE LOWER(name)=LOWER(:credz_name) AND LOWER(value)=LOWER(:credz_value) AND expires_utc=:credz_expires_utc AND LOWER(type)=LOWER(:credz_type) AND LOWER(target)=LOWER(:credz_target) AND pillaged_from_computerid=:pillaged_from_computerid AND pillaged_from_userid=:pillaged_from_userid"
parameters = {
"credz_name": credz_name,
"credz_value": credz_value,
"credz_expires_utc": credz_expires_utc,
"credz_type": credz_type, "credz_target": credz_target,
"pillaged_from_computerid": int(pillaged_from_computerid),
"pillaged_from_userid": int(pillaged_from_userid)
}
self.logging.debug(query)
with self.conn:
cur = self.conn.cursor()
cur.execute(query, parameters)
results = cur.fetchall()
except Exception as ex:
self.logging.error(f"Exception in add_cookie 3")
self.logging.debug(ex)
try:
if not len(results):
if pillaged_from_userid == None:
query = "INSERT INTO cookies (name, value, expires_utc, target, type, pillaged_from_computerid, file_path) VALUES (:credz_name, :credz_value, :credz_expires_utc, :credz_target, :credz_type, :pillaged_from_computerid, :credz_path)"
parameters = {
"credz_name": credz_name,
"credz_value": credz_value,
"credz_expires_utc": credz_expires_utc,
"credz_target": credz_target,
"credz_type": credz_type,
"pillaged_from_computerid": int(pillaged_from_computerid),
"credz_path": credz_path,
}
else:
query = "INSERT INTO cookies (name, value, expires_utc, target, type, pillaged_from_computerid,pillaged_from_userid, file_path) VALUES (:credz_name, :credz_value, :credz_expires_utc, :credz_target, :credz_type, :pillaged_from_computerid, :pillaged_from_userid, :credz_path)"
parameters = {
"credz_name": credz_name,
"credz_value": credz_value,
"credz_expires_utc": credz_expires_utc,
"credz_type": credz_type,
"credz_target": credz_target,
"pillaged_from_computerid": int(pillaged_from_computerid),
"pillaged_from_userid": int(pillaged_from_userid),
"credz_path": credz_path,
}
self.logging.debug(query)
with self.conn:
cur = self.conn.cursor()
cur.execute(query, parameters)
user_rowid = cur.lastrowid
self.logging.debug(
f'added_cookies(credtype={credz_type}, target={credz_target}, name={credz_name}, value={credz_value}) => {user_rowid}')
else:
self.logging.debug(
f'added_credential(credtype={credz_type}, target={credz_target}, name={credz_name}, value={credz_value}) => ALREADY IN DB')
except Exception as ex:
self.logging.error(f"Exception in add_cookie 4")
self.logging.debug(ex)
return None
def get_credz_old(self, filterTerm=None, credz_type=None):
"""
Return credentials from the database.
"""
cur = self.conn.cursor()
if credz_type:
cur.execute(f"SELECT * FROM credz WHERE credtype='{credz_type}'")
# if we're filtering by username
elif filterTerm and filterTerm != '':
cur.execute("SELECT * FROM users WHERE LOWER(username) LIKE LOWER(?)", ['%{}%'.format(filterTerm)])
# otherwise return all credentials
else:
cur.execute("SELECT * FROM credz")
results = cur.fetchall()
cur.close()
return results
def add_group(self, domain, name):
domain = domain.split('.')[0].upper()
cur = self.conn.cursor()
cur.execute("SELECT * FROM groups WHERE LOWER(domain)=LOWER(?) AND LOWER(name)=LOWER(?)", [domain, name])
results = cur.fetchall()
if not len(results):
cur.execute("INSERT INTO groups (domain, name) VALUES (?,?)", [domain, name])
cur.close()
self.logging.debug('add_group(domain={}, name={}) => {}'.format(domain, name, cur.lastrowid))
return cur.lastrowid
def add_admin_user(self, credtype, domain, username, password, host, userid=None):
domain = domain.split('.')[0].upper()
cur = self.conn.cursor()
if userid:
cur.execute("SELECT * FROM users WHERE id=?", [userid])
users = cur.fetchall()
else:
cur.execute(
"SELECT * FROM users WHERE credtype=? AND LOWER(domain)=LOWER(?) AND LOWER(username)=LOWER(?) AND password=?",
[credtype, domain, username, password])
users = cur.fetchall()
cur.execute('SELECT * FROM computers WHERE ip LIKE ?', [host])
hosts = cur.fetchall()
if len(users) and len(hosts):
for user, host in zip(users, hosts):
userid = user[0]
hostid = host[0]
# Check to see if we already added this link
cur.execute("SELECT * FROM admin_relations WHERE userid=? AND computerid=?", [userid, hostid])
links = cur.fetchall()
if not len(links):
cur.execute("INSERT INTO admin_relations (userid, computerid) VALUES (?,?)", [userid, hostid])
cur.close()
def get_admin_relations(self, userID=None, hostID=None):
cur = self.conn.cursor()
if userID:
cur.execute("SELECT * FROM admin_relations WHERE userid=?", [userID])
elif hostID:
cur.execute("SELECT * FROM admin_relations WHERE computerid=?", [hostID])
results = cur.fetchall()
cur.close()
return results
def get_group_relations(self, userID=None, groupID=None):
cur = self.conn.cursor()
if userID and groupID:
cur.execute("SELECT * FROM group_relations WHERE userid=? and groupid=?", [userID, groupID])
elif userID:
cur.execute("SELECT * FROM group_relations WHERE userid=?", [userID])
elif groupID:
cur.execute("SELECT * FROM group_relations WHERE groupid=?", [groupID])
results = cur.fetchall()
cur.close()
return results
def remove_admin_relation(self, userIDs=None, hostIDs=None):
cur = self.conn.cursor()
if userIDs:
for userID in userIDs:
cur.execute("DELETE FROM admin_relations WHERE userid=?", [userID])
elif hostIDs:
for hostID in hostIDs:
cur.execute("DELETE FROM admin_relations WHERE hostid=?", [hostID])
cur.close()
def remove_group_relations(self, userID=None, groupID=None):
cur = self.conn.cursor()
if userID:
cur.execute("DELETE FROM group_relations WHERE userid=?", [userID])
elif groupID:
cur.execute("DELETE FROM group_relations WHERE groupid=?", [groupID])
results = cur.fetchall()
cur.close()
return results
def is_credential_valid(self, credentialID):
"""
Check if this credential ID is valid.
"""
cur = self.conn.cursor()
cur.execute('SELECT * FROM users WHERE id=? AND password IS NOT NULL LIMIT 1', [credentialID])
results = cur.fetchall()
cur.close()
return len(results) > 0
def is_credential_local(self, credentialID):
cur = self.conn.cursor()
cur.execute('SELECT domain FROM users WHERE id=?', [credentialID])
user_domain = cur.fetchall()
if user_domain:
cur.execute('SELECT * FROM computers WHERE LOWER(hostname)=LOWER(?)', [user_domain])
results = cur.fetchall()
cur.close()
return len(results) > 0
def get_credentials(self, filterTerm=None, credtype=None):
"""
Return credentials from the database.
"""
cur = self.conn.cursor()
# if we're returning a single credential by ID
if self.is_credential_valid(filterTerm):
cur.execute("SELECT * FROM users WHERE id=?", [filterTerm])
elif credtype:
cur.execute("SELECT * FROM users WHERE credtype=?", [credtype])
# if we're filtering by username
elif filterTerm and filterTerm != '':
cur.execute("SELECT * FROM users WHERE LOWER(username) LIKE LOWER(?)", ['%{}%'.format(filterTerm)])
# otherwise return all credentials
else:
cur.execute("SELECT * FROM users")
results = cur.fetchall()
cur.close()
return results
def is_user_valid(self, userID):
"""
Check if this User ID is valid.
"""
cur = self.conn.cursor()
cur.execute('SELECT * FROM users WHERE id=? LIMIT 1', [userID])
results = cur.fetchall()
cur.close()
return len(results) > 0
def get_users(self, filterTerm=None):
cur = self.conn.cursor()
if self.is_user_valid(filterTerm):
cur.execute("SELECT * FROM users WHERE id=? LIMIT 1", [filterTerm])
# if we're filtering by username
elif filterTerm and filterTerm != '':
cur.execute("SELECT * FROM users WHERE LOWER(username) LIKE LOWER(?)", ['%{}%'.format(filterTerm)])
else:
cur.execute("SELECT * FROM users")
results = cur.fetchall()
cur.close()
return results
def is_computer_valid(self, hostID):
"""
Check if this host ID is valid.
"""
cur = self.conn.cursor()
cur.execute('SELECT * FROM computers WHERE id=? LIMIT 1', [hostID])
results = cur.fetchall()
cur.close()
return len(results) > 0
def get_computers(self, filterTerm=None, domain=None):
"""
Return hosts from the database.
"""
cur = self.conn.cursor()
# if we're returning a single host by ID
if self.is_computer_valid(filterTerm):
cur.execute("SELECT * FROM computers WHERE id=? LIMIT 1", [filterTerm])
# if we're filtering by domain controllers
elif filterTerm == 'dc':
if domain:
cur.execute("SELECT * FROM computers WHERE dc=1 AND LOWER(domain)=LOWER(?)", [domain])
else:
cur.execute("SELECT * FROM computers WHERE dc=1")
# if we're filtering by ip/hostname
elif filterTerm and filterTerm != "":
cur.execute("SELECT * FROM computers WHERE ip LIKE ? OR LOWER(hostname) LIKE LOWER(?)",
['%{}%'.format(filterTerm), '%{}%'.format(filterTerm)])
# otherwise return all computers
else:
cur.execute("SELECT * FROM computers")
results = cur.fetchall()
cur.close()
return results
def get_domain_controllers(self, domain=None):
return self.get_computers(filterTerm='dc', domain=domain)
def is_group_valid(self, groupID):
"""
Check if this group ID is valid.
"""
cur = self.conn.cursor()
cur.execute('SELECT * FROM groups WHERE id=? LIMIT 1', [groupID])
results = cur.fetchall()
cur.close()
self.logging.debug('is_group_valid(groupID={}) => {}'.format(groupID, True if len(results) else False))
return len(results) > 0
def get_groups(self, filterTerm=None, groupName=None, groupDomain=None):
"""
Return groups from the database
"""
if groupDomain:
groupDomain = groupDomain.split('.')[0].upper()
cur = self.conn.cursor()
if self.is_group_valid(filterTerm):
cur.execute("SELECT * FROM groups WHERE id=? LIMIT 1", [filterTerm])
elif groupName and groupDomain:
cur.execute("SELECT * FROM groups WHERE LOWER(name)=LOWER(?) AND LOWER(domain)=LOWER(?)",
[groupName, groupDomain])
elif filterTerm and filterTerm != "":
cur.execute("SELECT * FROM groups WHERE LOWER(name) LIKE LOWER(?)", ['%{}%'.format(filterTerm)])
else:
cur.execute("SELECT * FROM groups")
results = cur.fetchall()
cur.close()
self.logging.debug(
'get_groups(filterTerm={}, groupName={}, groupDomain={}) => {}'.format(filterTerm, groupName, groupDomain,
results))
return results