diff --git a/DonPAPI.py b/DonPAPI.py index b669657..cc155e5 100644 --- a/DonPAPI.py +++ b/DonPAPI.py @@ -28,7 +28,7 @@ from myseatbelt import MySeatBelt import concurrent.futures from lib.toolbox import split_targets,bcolors from database import database, reporting - +from datetime import date global assets @@ -173,7 +173,7 @@ def main(): targets = split_targets(options.target_ip) logging.info("Loaded {i} targets".format(i=len(targets))) - if not options.report : + if len(targets) > 0 : try: with concurrent.futures.ThreadPoolExecutor(max_workers=int(options.t)) as executor: executor.map(seatbelt_thread, [(target, options, logging) for target in targets]) @@ -183,20 +183,26 @@ def main(): traceback.print_exc() logging.error(str(e)) #print("ENDING MAIN") - my_report = reporting(sqlite3.connect(options.db_path), logging, options, targets) - my_report.generate_report() - my_report.export_credz() - my_report.export_sam() - my_report.export_cookies() - if options.GetHashes: - my_report.export_MKF_hashes() - my_report.export_dcc2_hashes() - #attendre la fin de toutes les threads ? + if options.report : try: my_report = reporting(sqlite3.connect(options.db_path), logging,options,targets) - my_report.generate_report() + # Splited reports + my_report.generate_report(report_file='%s_Client_view.html' % date.today().strftime("%d-%m-%Y"), + report_content=['credz', 'hash_reuse'], credz_content=['taskscheduler', 'LSA']) + my_report.generate_report(report_file='%s_Most_important_credz.html' % date.today().strftime("%d-%m-%Y"), + report_content=['credz'], + credz_content=['wifi', 'taskscheduler', 'credential-blob', 'browser', 'sysadmin', + 'LSA']) + my_report.generate_report(report_file='%s_cookies.html' % date.today().strftime("%d-%m-%Y"), + report_content=['cookies'], credz_content=['']) + # Main report + my_report.generate_report(report_file='%s_Full_Report.html' % date.today().strftime("%d-%m-%Y")) + logging.info("[+] Exporting loots to raw files : credz, sam, cookies") + my_report.export_credz() + my_report.export_sam() + my_report.export_cookies() if options.GetHashes: my_report.export_MKF_hashes() my_report.export_dcc2_hashes() diff --git a/database.py b/database.py index 16ba741..a22855e 100644 --- a/database.py +++ b/database.py @@ -12,10 +12,12 @@ class reporting: 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): + def add_to_resultpage(self, datas,report_file=""): try: + if report_file=="": + report_file=self.report_file datas = datas.encode('ascii', 'ignore') - f = open(self.report_file, 'ab') + f = open(report_file, 'ab') f.write(datas) f.close() return True @@ -24,7 +26,7 @@ class reporting: self.logging.debug(ex) return False - def generate_report(self,type='', user='', target=''): + def generate_report(self,type='', user='', target='',report_content=['credz','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__)) @@ -39,18 +41,22 @@ class reporting: self.logging.debug(f" Exception {bcolors.WARNING} in running Report {bcolors.ENDC}") self.logging.debug(ex) - self.logging.info("[+] Generating report") + 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 os.path.exists(os.path.join(self.options.output_directory,'%s_result.html' % date.today().strftime("%d-%m-%Y"))): - if os.path.exists(os.path.join(self.options.output_directory,'%s_result_old.html' % date.today().strftime("%d-%m-%Y"))): - os.remove(os.path.join(self.options.output_directory,'%s_result_old.html' % date.today().strftime("%d-%m-%Y"))) - os.rename(os.path.join(self.options.output_directory,'%s_result.html' % date.today().strftime("%d-%m-%Y")), os.path.join(self.options.output_directory,'%s_result_old.html' % date.today().strftime("%d-%m-%Y"))) - os.remove(os.path.join(os.path.join(self.options.output_directory,'%s_result.html' % date.today().strftime("%d-%m-%Y")))) - self.report_file = os.path.join(self.options.output_directory,'%s_result.html' % date.today().strftime("%d-%m-%Y")) + 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: @@ -63,9 +69,6 @@ class reporting: myfile,myfilename=myfiles if os.path.exists(myfile) and not os.path.exists(os.path.join(os.path.join(self.options.output_directory,'res'), myfilename)): shutil.copy2(myfile, os.path.join(os.path.join(self.options.output_directory,'res'), myfilename)) - else: - self.logging.debug(f"{os.path.exists(myfile)} - {os.path.exists(os.path.join(os.path.join(self.options.output_directory,'res'), myfilename))}") - except Exception as ex: self.logging.debug(f" Exception {bcolors.WARNING} in RES copy {bcolors.ENDC}") self.logging.debug(ex) @@ -148,281 +151,300 @@ class reporting: """ self.add_to_resultpage(data) - results = self.get_credz() + 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('VNC') - data = """ - - - - - - \n""" + data = """
UsernamePasswordTargetTypePillaged_from_computeridPillaged_from_userid
+ + + + + + \n""" - # - current_type='' - for index,cred in enumerate(results): - cred_id, file_path, username, password, target, type, pillaged_from_computerid, pillaged_from_userid = cred - 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"""""" + # + 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"""""" - #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"] + #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 + 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) + #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"""""" - else: - data += f"""""" + if index % 2 == 0: + data += f"""""" + else: + data += f"""""" - if 'admin' in username.lower() : #Pour mettre des results en valeur - special_style = '''class="cracked"''' - else: - special_style = "" + 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 + ###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"""""" - for info in [password]: - data += f"""""" - - #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"""""" - - for info in [type, computer_info, pillaged_from_userid]: + for info in [username]: data += f"""""" - data+="""\n""" + for info in [password]: + data += f"""""" - data += """
UsernamePasswordTargetTypePillaged_from_computeridPillaged_from_userid
{current_type} ({current_type_count})
{current_type} ({current_type_count})
{str(info)[:48]} {str(info)[:48]} {str(target)[:48]} {str(info)[:48]}
{str(info)[:48]}

""" - self.add_to_resultpage(data) - ### + #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"""
{str(target)[:48]} """ + + for info in [type, computer_info, pillaged_from_userid]: + data += f""" {str(info)[:48]} """ + data+="""\n""" + + data += """
""" + self.add_to_resultpage(data) + ### ##### List cookies - results = self.get_cookies() + if 'cookies' in report_content: + results = self.get_cookies() - data = """ - - - - - - - \n""" + data = """
NameValueUntilTargetTypePillaged_from_computeridPillaged_from_userid
+ + + + + + + \n""" - # - current_type = 'cookies' - data += f"""""" - for index, cred in enumerate(results): - name,value,expires_utc,target,type,pillaged_from_computerid,pillaged_from_userid = cred - # Skip infos of - # 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) + # + current_type = 'cookies' + data += f"""""" + for index, cred in enumerate(results): + name,value,expires_utc,target,type,pillaged_from_computerid,pillaged_from_userid = cred + # Skip infos of + # 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"""""" - else: - data += f"""""" + if index % 2 == 0: + data += f"""""" + else: + data += f"""""" - special_style = "" + special_style = "" - ###Print block - for info in [name,value]: - data += f"""""" - 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"""""" - else: - data += f"""""" - except: - data += f"""""" + ###Print block + for info in [name,value]: + data += f"""""" + 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"""""" + else: + data += f"""""" + except: + data += f"""""" - # 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"""""" + # 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"""""" - for info in [type, computer_info, pillaged_from_userid]: - data += f"""""" - data += """\n""" + for info in [type, computer_info, pillaged_from_userid]: + data += f"""""" + data += """\n""" + + data += """
NameValueUntilTargetTypePillaged_from_computeridPillaged_from_userid
Cookies ({len(results)})
Cookies ({len(results)})
{str(info)[:48]} {(datetime(1601, 1, 1) + timedelta(microseconds=info)).strftime('%b %d %Y %H:%M:%S')} {(datetime.fromtimestamp(info)).strftime('%b %d %Y %H:%M:%S')} {info} {str(info)[:48]} {(datetime(1601, 1, 1) + timedelta(microseconds=info)).strftime('%b %d %Y %H:%M:%S')} {(datetime.fromtimestamp(info)).strftime('%b %d %Y %H:%M:%S')} {info} {str(target)[:48]} {str(target)[:48]} {str(info)[:48]}
{str(info)[:48]}

""" + self.add_to_resultpage(data) - data += """
""" - self.add_to_resultpage(data) ##### List gathered files - results = self.get_file() + if 'files' in report_content: + results = self.get_file() - data = """ - - - \n""" - for index, myfile in enumerate(results): - try: - file_path, filename, extension, pillaged_from_computerid,pillaged_from_userid = myfile + data = """
FilenameTypeUserIp
+ + + \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"""\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 += """
FilenameTypeUserIp
{filename} {extension} {username} {computer_info}

""" + self.add_to_resultpage(data) + + ##### Identify user / IP relations + # Confirm audited scope : + if 'connected_user' in report_content: + results = self.get_connected_user() + + data = """ + \n""" + + for index, cred in enumerate(results): + try: + ip, username = cred + data += """\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 += """
UsernameIP
%s %s

""" + self.add_to_resultpage(data) + + + ##### Identify Local hash reuse + if 'hash_reuse' in report_content: + results = self.get_credz_sam() + data = """\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}" - 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"""\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 += """
Local account reuse :
{filename} {extension} {username} {computer_info}

""" - self.add_to_resultpage(data) - - ##### Identify user / IP relations - # Confirm audited scope : - results = self.get_connected_user() - - data = """ - \n""" - - for index, cred in enumerate(results): - try: - ip, username = cred - data += """\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 += """
UsernameIP
%s %s

""" - self.add_to_resultpage(data) - - - ##### Identify Local hash reuse - results = self.get_credz_sam() - data = """\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 += """\n""" % (username, password, type, computer_info) - data += """
Local account reuse :
%s %s %s %s

""" - self.add_to_resultpage(data) + data += """ %s %s %s %s \n""" % (username, password, type, computer_info) + data += """
""" + self.add_to_resultpage(data) # Confirm audited scope : - results = self.get_computers() - data = """\n""" - data += """ - - - - - - - - \n""" + if 'audited_scope' in report_content: + results = self.get_computers() + data = """
Scope Audited :
IpHostnameDomainOSSmb signing enabledSmb v1Is AdminConnectivity
\n""" + data += """ + + + + + + + + \n""" - for index, cred in enumerate(results): - ip, hostname, domain, my_os, smb_signing_enabled,smbv1_enabled,is_admin,connectivity = cred - data+="" - for info in [ip, hostname, domain, my_os]: - data += f"""""" - if smb_signing_enabled: - data+="" - else: - data += """""" - if smbv1_enabled: - data+="" - else: - data += "" - if is_admin: - data+="""""" - else: - data += """""" - for info in [connectivity]: - data += f"""""" - data+="\n" - data += """
Scope Audited :
IpHostnameDomainOSSmb signing enabledSmb v1Is AdminConnectivity
{info} Ok NOT required Yes No Admin No {info}

\n""" - self.add_to_resultpage(data) + for index, cred in enumerate(results): + ip, hostname, domain, my_os, smb_signing_enabled,smbv1_enabled,is_admin,connectivity = cred + data+="" + for info in [ip, hostname, domain, my_os]: + data += f""" {info} """ + if smb_signing_enabled: + data+=" Ok " + else: + data += """
NOT required """ + if smbv1_enabled: + data+=" Yes " + else: + data += " No " + if is_admin: + data+=""" Admin """ + else: + data += """ No """ + for info in [connectivity]: + data += f""" {info} """ + data+="\n" + data += """
\n""" + self.add_to_resultpage(data) #Etat des masterkeyz - if self.options.debug : + if self.options.debug and 'masterkeys' in report_content : results=self.get_masterkeys() data = """\n""" data += """
Masterkeys :
Guid