I was recently doing some analysis on PoshC2 beacons, and, why they're getting killed out of the box by defender. My starting point is the standard payload which passes a base64 encoded string to a Non-interactive, hidden powershell prompt:
This base64 encoded string, decodes to the following - another IEX cradle pulling more code. It's worth noting at this point that both of these get killed by defender when inputted into a PS console, let's keep unraveling.
If we browse to the URL which is hosting the payload, we can observe the following:
So, another powershell child process is being spawned D: Passing an encoded string in the command line once again. From a tradecraft perspective this isn't great and is providing plenty noise for the Blue team to get a sniff. Let's decode this second encoded string:
It decodes to the string above, a gzipped, base64 encoded string. At this stage when we input this into a powershell window, we no longer get killed - we just receive a message from PS stating that our AV has blocked it, this is just AMSI using signatures and blocking the input :D
At this stage we can see the same settings which we configured server side using posh-config, such as URI, user agent etc.
This is still picked up by AMSI, but we're not getting killed at this stage.
Payload Tweaks
Execution Flow
The first tweak I have decided to make is to remove the double powershell.exe invocations with encoded commands, there's almost 0 value in this when we can just pull the payload and execute it within the current thread.
Instead, we just have powershell being executed once:
Having worked in security operations, I almost feel like powershell being executed with encoded parameters is something the SOC are very aware of. If instead, we can obfuscate the IEX cradle in a way which looks like valid behaviour, I feel there is more chance of getting away with it. The command above is just an IEX cradle with some nonsense wrapped around it to look like a Microsoft update, or something xD
AMSI Patch at Stage 1
Since our payload is being caught by AMSI, we can patch out the AmsiScanBuffer function to always return "clean", if we do this right at the start, our PoshC2 beacon will be loaded with no noise, and, all of the other modules will inherit that patch in the current threads memory space.
We can then chain that payload with the raw PoshC2 stager by throwing an IEX cradle at the bottom of the script to execute it in the current thread - AMSI is already patched out at this point.
Stage 1 - being pulled by our "MS-Update-Security" cradle seen before;
Which then loads stage 2, in this case, just our raw decoded PoshC2 payload seen before. I decided to use the raw payload due to opsec considerations around ScriptBlockLogging. Again, I believe blue teams will be more likely to log on gzip decompressions than to fingerprint some of the specifics around a PoshC2 stager - maybe not tho?
Hopefully the slight tweaks in this payload reduce the risk of Blue catching the beacon, by reducing the child processes involved and removing simple indicators from the blue team. The slight tweaks also allow PoshC2 beacons to run on endpoints with latest MS defender security updates by bypassing AMSI off-the-bat.