# -*- coding: utf-8 -*- import ctypes import getpass import json import logging import os import socket import sys import traceback from time import gmtime, strftime from platform import uname from foreign.client_handling.lazagne.config.users import get_username_winapi from foreign.client_handling.lazagne.config.winstructure import string_to_unicode, char_to_int, chr_or_byte, python_version from .constant import constant # --------------------------- Standard output functions --------------------------- STD_OUTPUT_HANDLE = -11 std_out_handle = ctypes.windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE) tmp_user = None class StandardOutput(object): def __init__(self): self.banner = ''' |====================================================================| | | | The LaZagne Project | | | | ! BANG BANG ! | | | |====================================================================| ''' self.FILTER = b''.join([((len(repr(chr_or_byte(x))) == 3 and python_version == 2) or (len(repr(chr_or_byte(x))) == 4 and python_version == 3)) and chr_or_byte(x) or b'.' for x in range(256)]) def set_color(self, color='white', intensity=False): c = {'white': 0x07, 'red': 0x04, 'green': 0x02, 'cyan': 0x03}.get(color, None) if intensity: c |= 0x08 ctypes.windll.kernel32.SetConsoleTextAttribute(std_out_handle, c) # print banner def first_title(self): self.do_print(message=self.banner, color='white', intensity=True) # Python 3.7.3 on Darwin x86_64: i386 python_banner = 'Python {}.{}.{} on'.format(*sys.version_info) + " {0} {4}: {5}\n".format(*uname()) self.print_logging(function=logging.debug, message=python_banner, prefix='[!]', color='white', intensity=True) # info option for the logging def print_title(self, title): t = u'------------------- ' + title + ' passwords -----------------\n' self.do_print(message=t, color='white', intensity=True) # debug option for the logging def title_info(self, title): t = u'------------------- ' + title + ' passwords -----------------\n' self.print_logging(function=logging.info, prefix='', message=t, color='white', intensity=True) def print_user(self, user, force_print=False): pass def print_footer(self, elapsed_time=None): footer = '\n[+] %s passwords have been found!\n' % str(constant.nb_password_found) if not logging.getLogger().isEnabledFor(logging.INFO): footer += 'For more information launch it again with the -v option\n' if elapsed_time: footer += '\nelapsed time = ' + str(elapsed_time) self.do_print(footer) def print_hex(self, src, length=8): N = 0 result = b'' while src: s, src = src[:length], src[length:] hexa = b' '.join([b"%02X" % char_to_int(x) for x in s]) s = s.translate(self.FILTER) result += b"%04X %-*s %s\n" % (N, length * 3, hexa, s) N += length return result def try_unicode(self, obj, encoding='utf-8'): if python_version == 3: try: return obj.decode() except Exception: return obj try: if isinstance(obj, basestring): # noqa: F821 if not isinstance(obj, unicode): # noqa: F821 obj = unicode(obj, encoding) # noqa: F821 except UnicodeDecodeError: return repr(obj) return obj # centralize print function def do_print(self, message='', color=False, intensity=False): # quiet mode => nothing is printed if constant.quiet_mode: return message = self.try_unicode(message) if color: self.set_color(color=color, intensity=intensity) self.print_without_error(message) self.set_color() else: self.print_without_error(message) def print_without_error(self, message): try: print(message.decode()) except Exception: try: print(message) except Exception: print(repr(message)) def print_logging(self, function, prefix='[!]', message='', color=False, intensity=False): if constant.quiet_mode: return try: msg = u'{prefix} {msg}'.format(prefix=prefix, msg=message) except Exception: msg = '{prefix} {msg}'.format(prefix=prefix, msg=str(message)) if color: self.set_color(color, intensity) function(msg) self.set_color() else: function(msg) def print_output(self, software_name, pwd_found): if pwd_found: # if the debug logging level is not apply => print the title if not logging.getLogger().isEnabledFor(logging.INFO): # print the username only if password have been found user = constant.finalResults.get('User', '') global tmp_user if user != tmp_user: tmp_user = user self.print_user(user, force_print=True) # if not title1: self.print_title(software_name) # Particular passwords representation to_write = [] if software_name in ('Hashdump', 'Lsa_secrets', 'Mscache'): pwds = pwd_found[1] for pwd in pwds: self.do_print(pwd) if software_name == 'Lsa_secrets': hex_value = self.print_hex(pwds[pwd], length=16) to_write.append([pwd.decode(), hex_value.decode()]) self.do_print(hex_value) else: to_write.append(pwd) self.do_print() # Other passwords else: # Remove duplicated password pwd_found = [dict(t) for t in set([tuple(d.items()) for d in pwd_found])] # Loop through all passwords found for pwd in pwd_found: # Detect which kinds of password has been found lower_list = [s.lower() for s in pwd] for p in ('password', 'key', 'hash'): pwd_category = [s for s in lower_list if p in s] if pwd_category: pwd_category = pwd_category[0] break write_it = False passwd = None try: # Do not print empty passwords if not pwd[pwd_category.capitalize()]: continue passwd = string_to_unicode(pwd[pwd_category.capitalize()]) except Exception: pass # No password found if not passwd: print_debug("FAILED", u'Password not found.') else: constant.nb_password_found += 1 write_it = True print_debug("OK", u'{pwd_category} found!'.format( pwd_category=pwd_category.title())) # Store all passwords found on a table => for dictionary attack if master password set if passwd not in constant.password_found: constant.password_found.append(passwd) pwd_info = [] for p in pwd: try: pwd_line = '%s: %s' % (p, pwd[p].decode()) # Manage bytes output (py 3) except Exception: pwd_line = '%s: %s' % (p, pwd[p]) pwd_info.append(pwd_line) self.do_print(pwd_line) self.do_print() if write_it: to_write.append(pwd_info) # write credentials into a text file self.checks_write(to_write, software_name) else: print_debug("INFO", "No passwords found.\n") def write_header(self): time = strftime("%Y-%m-%d %H:%M:%S", gmtime()) try: hostname = socket.gethostname().decode(sys.getfilesystemencoding()) except AttributeError: hostname = socket.gethostname() header = u'{banner}\r\n- Date: {date}\r\n- Username: {username}\r\n- Hostname:{hostname}\r\n\r\n'.format( banner=self.banner.replace('\n', '\r\n'), date=str(time), username=get_username_winapi(), hostname=hostname ) with open(os.path.join(constant.folder_name, '{}.txt'.format(constant.file_name_results)), "ab+") as f: f.write(header.encode()) def write_footer(self): footer = '\n[+] %s passwords have been found!\r\n\r\n' % str(constant.nb_password_found) open(os.path.join(constant.folder_name, '%s.txt' % constant.file_name_results), "a+").write(footer) def checks_write(self, values, category): if values: if 'Passwords' not in constant.finalResults: constant.finalResults['Passwords'] = [] constant.finalResults['Passwords'].append((category, values)) def print_debug(error_level, message): # Quiet mode => nothing is printed if constant.quiet_mode: return # print when password is found if error_level == 'OK': constant.st.do_print(message='[+] {message}'.format(message=message), color='green') # print when password is not found elif error_level == 'FAILED': constant.st.do_print(message='[-] {message}'.format(message=message), color='red', intensity=True) elif error_level == 'CRITICAL' or error_level == 'ERROR': constant.st.print_logging(function=logging.error, prefix='[-]', message=message, color='red', intensity=True) elif error_level == 'WARNING': constant.st.print_logging(function=logging.warning, prefix='[!]', message=message, color='cyan') elif error_level == 'DEBUG': constant.st.print_logging(function=logging.debug, message=message, prefix='[!]') else: constant.st.print_logging(function=logging.info, message=message, prefix='[!]') # --------------------------- End of output functions --------------------------- def json_to_string(json_string): string = u'' try: for json in json_string: if json: string += u'################## User: {username} ################## \r\n'.format(username=json['User']) if 'Passwords' not in json: string += u'\r\nNo passwords found for this user.\r\n\r\n' else: for pwd_info in json['Passwords']: category, pwds_tab = pwd_info string += u'\r\n------------------- {category} -----------------\r\n'.format( category=category) if category.lower() in ('lsa_secrets', 'hashdump', 'cachedump'): for pwds in pwds_tab: if category.lower() == 'lsa_secrets': for d in pwds: string += u'%s\r\n' % (constant.st.try_unicode(d)) else: string += u'%s\r\n' % (constant.st.try_unicode(pwds)) else: for pwds in pwds_tab: string += u'\r\nPassword found!\r\n' for pwd in pwds: try: name, value = pwd.split(':', 1) string += u'%s: %s\r\n' % ( name.strip(), constant.st.try_unicode(value.strip())) except Exception: print_debug('DEBUG', traceback.format_exc()) string += u'\r\n' except Exception: print_debug('ERROR', u'Error parsing the json results: {error}'.format(error=traceback.format_exc())) return string def write_in_file(result): """ Write output to file (json and txt files) """ if result: if constant.output in ('json', 'all'): try: # Human readable Json format pretty_json = json.dumps(result, sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False) with open(os.path.join(constant.folder_name, constant.file_name_results + '.json'), 'ab+') as f: f.write(pretty_json.encode()) constant.st.do_print(u'[+] File written: {file}'.format( file=os.path.join(constant.folder_name, constant.file_name_results + '.json')) ) except Exception as e: print_debug('DEBUGG', traceback.format_exc()) if constant.output in ('txt', 'all'): try: with open(os.path.join(constant.folder_name, constant.file_name_results + '.txt'), 'ab+') as f: a = json_to_string(result) f.write(a.encode()) constant.st.write_footer() constant.st.do_print(u'[+] File written: {file}'.format( file=os.path.join(constant.folder_name, constant.file_name_results + '.txt')) ) except Exception as e: print_debug('DEBUG', traceback.format_exc())