Whether it's on HackTheBox, at work, or just for fun, sometimes you need to infect a Windows system with some C2 software or malware. In my case, I most often use CobaltStrike (link) or the equally effective Sliver (link). Since I've done this many times, I immediately thought, "I'll disable AMSI, use a simple loader in PowerShell, and let's get started." But recently, I was quite surprised...

What is AMSI?

AMSI (Antimalware Scan Interface) is an interface introduced by Microsoft that allows antivirus and antimalware software to scan scripts and other content before execution. AMSI enables the detection and prevention of harmful scripts and other non-traditional IT threats.

It is a rather annoying (for testers and hackers), yet very useful (for defenders) interface that passes executed commands through the antivirus engine. AMSI has been implemented in PowerShell since Windows 10, version 1903. It works by loading the amsi.dll library into the memory space of PowerShell.

This means that AMSI can scan everything I put in the terminal before execution, which significantly complicates my task.

How Could AMSI Be Bypassed?

There have been numerous projects and individual scripts that allow for an "AMSI Bypass"! Without diving into technical details, it was enough to Google "site:github.com amsi bypass" to get a wall of interesting projects:

However, more technical individuals preferred to create their own bypasses rather than using ready-made tools. This reduced the chances of being detected by the antivirus engine based on signatures that Microsoft regularly updates.

One must remember that any file that touches the disk is (theoretically) scanned by the antivirus. Therefore, it's essential to have a tool (script, code, binary file, or set of commands) that isn't marked as dangerous but allows disabling AMSI so that subsequent things are not flagged as dangerous.

Example image

Difference Between Working AMSI (Detection of "Malicious Content") and Patched AMSI (Information That the Command Does Not Exist)

My Own Toy

In my case, I had a script called Bypass-AMSI.ps1 that I wrote some time ago. I just needed to load it into memory, run the command with the same name as the script, and voila! I could start taking over the world.

Example image

Working AMSI Patching Script

To my surprise, a few days ago, when I tried to run the bypass in the terminal, I received a strange error, "BadImageFormatException," which basically means that there was most likely an issue with a mismatch between 32-bit and 64-bit systems.

Example image

Error in PowerShell indicating that the bypass failed

However, I didn’t recall updating this environment recently. Following the principle "if it ain't broke, don't fix it," I always used the same version of the system, PowerShell, and script. It should have worked! But it didn't...

Fortunately, I had a snapshot of the system from 2020. I restored the image, uploaded the script, and it ran without any issues! During testing, I accidentally closed the window. So, I opened a new one, tried to run the patch, and bam! Within minutes, on a machine with internet access, my bypass stopped working. I specifically restored the snapshot to reproduce the problem for a screenshot.

Example image

Two PowerShell sessions, run a few minutes apart. | Right – immediately after system startup, Left – a moment later.

Cause? And Solution!

I couldn’t figure out why the script immediately stopped working. Only when I started searching for another bypass, I came across a post from Emarci Nasi – a cybersecurity researcher. He is also the founder of BallisKit, a company that provides tools and services for professional pentesters and red teamers.

A tweet (link) clearly pointed out that Microsoft Defender had started combating most AMSI bypass methods available in open-source tools. And as it turns out, quite effectively!

So, I decided to revisit the theory and read up on relatively basic AMSI bypass methods. Theoretically, these include:

  • Patching AMSI DLL: Modifying the code in the AMSI library (amsi.dll) to avoid detection by antivirus software.
  • ScriptBlock Smuggling: Using a technique that allows delivering a script block, enabling scripts to execute without detection by AMSI.
  • Encoding/Encrypting: Encoding or encrypting critical strings to avoid detection by AMSI.
  • Hooking AMSI Functions: Using DLLs to intercept AMSI functions like AmsiScanBuffer.
  • Downgrading PowerShell: Running an older version of PowerShell (e.g., PowerShell 2.0), which doesn’t have AMSI protections.

However, most of the tools I knew for the above methods were effectively blocked, or the methods were not available in the target environment. After reading several articles and getting frustrated with Copilot, I remembered a technique called Unmanaged PowerShell.

This method allows running PowerShell scripts without using powershell.exe. This way, you can execute scripts without relying on the official tool. So, if I found a solution to run PowerShell without using PowerShell (and therefore without AMSI), I would be golden!

I remembered a project called PowerShdll (link), which allowed running commands using rundll32. Unfortunately, the executable file was detected by Defender. But after some more searching, I came across another old tool called NotPowerShell (link), which, as the name suggests, is not PowerShell but allows executing commands in this language!

I decided to test the assertion of this application that it isn’t what it seems. I downloaded the project, compiled it on the fly, ran the file from a regular terminal, and… I had to install .NET 3.5 on my test machine.

Only after a while could I run the command "amsicontext". However, I didn’t receive any message. No error, no information about a block.

Example image

amsicontext run via NotPowerShell

So, when playing around, it's either "go big or go home," since I needed to run something more than a nonexistent command to test the bypass. I set up CobaltStrike on another virtual machine, generated a payload in the "PowerShell IEX" format, and first checked if AMSI was still blocking me. It was still blocking me hard, as shown in the screenshot. So, I ran nps.exe, used the same command, and to my surprise, a C2 connection was established. It worked!

Example image

Triumphant execution of Invoke-Expression via nps.exe (NotPowerShell)

The project, in general, is just a slightly modified fork of Ben0xA/nps (link), and both projects were last updated 6-8 years ago. Nevertheless, the solution works. So, I can get back to my world domination scenario. The question is, for how long?

... for few months.

Example image Example image

Previous Post Next Post