Bypass AMSI in 2025

admin 2025年4月8日20:22:35评论7 views字数 8363阅读27分52秒阅读模式
Bypass AMSI in 2025

介绍

自从我写下第一篇关于通过手动修改绕过反恶意软件扫描接口 (AMSI) 以及 Powershell 和 .NET 特定绕过之间的区别的博客文章以来,已经过去了四年多的时间:

  • https://s3cur3th1ssh1t.github.io/Powershell-and-the-.NET-AMSI-Interface/

自 2020 年以来,已经发布了许多新的绕过方法,可以作为以前方法的替代方案。这篇博文将阐明 AMSI 背后的原因(大致如此,但希望容易理解)以及如何在四年多后仍然有效地绕过它。有什么变化吗?剧透:只有部分 Bypass AMSI in 20251.什么时候需要绕过 AMSI?

尽管 AMSI 已在许多论文和工具中进行了分析和描述,但我仍然惊讶地发现社区中存在如此多的混乱和误解。例如,GitHub 上发布了许多 shellcode 加载器,它们只执行 shellcode。但 README 还指出它包含一个 AMSI 绕过,这就是它从未被检测到的原因。因此存在很多误解,至少在 GitHub 或社交网络上是如此,这可能会让越来越多的人感到困惑。

我们什么时候真的需要使用 AMSI 绕过?至少对于 shellcode 执行来说,我们不需要。正如我在四年多前在博客中写到的,AMSI 主要用于在运行时分析脚本语言和 .NET 托管代码,例如

  • Powershell

  • VBS

  • Javascript

  • VBA macros

  • C# assemblies

因此,如果您使用的是命令和控制框架的有效负载,并且主要从那里运行 BOF 或 COFF,那么您根本不需要绕过 AMSI。如果您在加载器中实现了绕过,那么您只会增加 IoC 以及被该绕过尝试检测到的可能性。除非您真的需要它们,否则最好不要使用绕过!

另一方面,如果您想运行已知的恶意和未混淆的公共工具(例如来自 GitHub 的上述任何一种语言),或者在您自己的工具中重用这些工具的代码,则需要绕过 AMSI 才能运行这些工具。您要通过 Powershell 中的 Invoke-Expression 执行 GitHub 脚本吗?您是否通过 assembly::load() 加载 .NET 程序集?创建恶意办公宏?通过 mshta.exe 、csc ript.e xe? 或 wscript.exe 将脚本加载到内存中?您可能需要绕过 AMSI。

2. AMSI 的工作原理以及如何绕过它

AMSI 主要是基于签名的检测。与传统的基于签名的检测的主要区别在于,这些签名是在运行时查找的,只要从内存中加载了潜在的恶意内容。正如 IBM X-Force Red 最近指出的那样,当某些内容从磁盘加载到内存时,架构上的 AMSI 在某些时候根本不会触发扫描。

AMSI 签名可能是什么样的?它们可以是像 Invoke-Mimikatz 这样的简单字符串,也可以是像经典 AmsiScanBuffer 补丁使用的字节这样的字节数组:

Powershell[Byte[]] (0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3)

对于 C# 程序集,这可以是特定的十六进制字节,但根据我的经验,AMSI 还使用某种 Yara 规则 - 或者至少是正则表达式。但如果 AMSI 与签名有关,这也意味着我们总是可以通过修改代码来绕过它。如果您更改代码以使签名不适用,则可以绕过 AMSI。就这么“简单”,尽管签名有时很难找出来。

绕过 AMSI 检测的替代方法是以某种方式破坏 amsi.dll 或扫描过程中涉及的其他库中的功能,或者完全阻止 DLL 加载。几乎所有公开记录的绕过方法都是关于这一点的。它们主要在破坏功能的技术上有所不同,例如

  • 修补内存区域(从我的角度来看也包括钩子)

  • 使用向量异常处理程序和硬件断点等来操纵工作流程

  • 创建新进程,并通过各种方式阻止相关 DLL 加载

  • 防止在启动 CLR 和/或初始化 AMSI 之前加载相关 DLL

这些事情通常在运行时完成。IoC 以及对这些绕过的检测依赖于:

  • 绕过代码的签名,或

  • 运行时检测,例如用户空间挂钩、ETWti 或内存扫描。

3. 使用公共混淆器的缺点

当我写第一篇关于 AMSI 规避的博客时,使用公共混淆器来规避检测仍然是可能的。现在情况仍然如此吗?为了好玩,我们做一个简单的实验。为此,我确实让 ChatGPT 生成了一个随机的 Powershell 脚本,该脚本在 Virustotal 上没有被检测到, 原因很明显,它根本不是恶意的。
在使用 Invoke-Obfuscation 进行混淆并再次将其上传到 VirusTotal 后,它获得了 1 次检测, 而不是之前的 0 次检测。因此乍一看,似乎只有一家供应商拥有某种通用规则来检测 Invoke-Obfuscation 的混淆。然而,由于 Invoke-Obfuscation 与 TOKEN OBFUSCATION 的使用匹配,因此触发了 Sigma 规则:

Bypass AMSI in 2025

混淆技术如何对抗已知的恶意 GitHub 工具?我们来看两个例子:

普通 GitHub 版本

1.PowersharpPack 中的 Invoke-Rubeus — 撰写本文时有 29 次检测。

2.WinPwn—— 截至撰写本文时,已检测到 19 项

Invoke-Obfuscation 混淆版本

  • PowersharpPack 中的 Invoke-Rubeus - 10 次检测

  • WinPwn - 首次上传时检测到 2 个

我们可以看到,近五年后,仍然可以使用相同的公共混淆器来逃避基于签名的检测。哇,说实话我没想到这一点,我以为现在会有更多 Invoke-Obfuscation 的特定检测。 注意: 在完成这篇博文后,我摆弄了一些 EDR 和上面的脚本 - 事实证明,有些脚本有专门用于 Invoke-Obfuscation 的 AMSI 签名!即使是非恶意的第一个脚本也被标记为恶意。所以这意味着,VirusTotal 不会向您显示基于 AMSI 的检测,而只会显示基于文件本身签名的检测。这一事实使得 Invoke-Obfuscation 对这些供应商完全无用。

WinPwn 在几个地方出现故障,因此内置的 AMSI 旁路等一些功能不再起作用:

Bypass AMSI in 2025

图 2:部分破坏的脚本执行

但菜单仍然显示,并且原生的 Powershell 功能可以正常使用。

由于混淆,混淆的 Invoke-Rubeus 版本首先在这里出现问题,因为类型 [dreIKOpFhund.pROGRam] 被放置在函数之前并作为变量,此时这些命名空间和类名无法正确解析。为了解决这个问题,我在混淆的 [assembly::load] 行之后手动将此部分放入函数本身中:

Bypass AMSI in 2025

图 3:Invoke-Rubeus 的混淆修复

这显示了第一个缺点,即混淆器可能会破坏我们的代码,并且需要手动调整才能正常工作。我们是否要绕过 Defender 的 AMSI?

Bypass AMSI in 2025

图4:脚本加载/执行

乍一看,我们似乎确实这样做了,但只针对 Powershell 脚本本身,而不是针对运行时调用的 Rubeus 程序集。为什么会这样?这已经在我的第二篇私人博客中描述过了。因此,在这种情况下,我们必须先混淆程序集,然后嵌入它,再混淆 Powershell 脚本。我们需要对每个程序集/脚本都采用这种方法。使用现有的 AMSI 绕过方法会不会更容易?

4. 使用哪种旁路?

在我发表第一篇博客文章时,我的 Amsi Bypass Powershell 存储库包含 15 种不同的绕过方法 。四年多后,它包含 23 个不同的代码片段,所以又增加了 8 个。这些只是已发布的技术,包括 Powershell 代码。我故意没有添加任何其他已发布的绕过方法,例如在 CLR 启动之前使用本机语言,例如我自己的 Ruy-Lopez 。
因此,技术的数量已经增加了很多。但是,到 2025 年,什么会真正有效,什么又不会有效呢?你如何评估呢?通常,所有公共绕过方法本身都由 AMSI 本身签名和标记,至少在使用上述脚本语言(例如 Powershell)之一实现它们时是这样。因此,对于所有这些方法,都需要手动修改或混淆代码,以便绕过方法本身不再被标记。这是一项艰苦的工作。并且需要反复试验。但这种方法的问题在于,不同的供应商具有不同的签名,即使你修改后的绕过方法对一个供应商有效,也可能对另一个供应商无效。我自己做了几年,但最终意识到工作量太大了。
选择的语言
另一方面,坚持使用本机语言的优点是您的代码不会被 AMSI 本身扫描。相反,您必须处理磁盘上的二进制文件/dll 的“老式”基于签名的检测。您将需要使用字符串混淆/加密、反模拟、反沙盒技术以及类似于脚本语言和所选旁路的用户空间钩子旁路。最重要的是,默认情况下 CLR 不会加载到本机进程中,AMSI 也不会被初始化,这通常提供了更多可供选择的旁路选项。使用本机编程语言已成为我如今绕过 AMSI 的首选方式,但这需要更多有关需要注意什么以及一般的 Windows API 编程的背景知识。什么仍然有效
我们如何“评估”有效性?如前所述, 所有公共绕过方法都可以进行修改,以绕过基于签名的磁盘检测以及 AMSI 本身。但对于某些技术,来自不同供应商的运行时检测变得越来越重要。这些检测不能像基于签名的检测那样轻易被绕过。修补
如果您选择修补,您将面临用户空间钩子,这些钩子会阻止您修改 msi. 的内存权限或将数据写入其内存。您需要通过使用解除钩子、间接系统调用或类似方法绕过这些钩子。
即使您已完成这些操作,您仍可能会面临补丁的 ETWti/内存扫描检测。一个很好的例子是最近 Microsoft Defender 对经典 AmsiScanBuffer 补丁的检测。每当 AmsiScanBuffer 函数(或其他几个函数)被修改为仅 return ,就会发出警报并终止您的进程。AV/EDR 可以通过 ETWti 事件简单地看到,eG AmsiScanBuffer 对 amsi.dll 的保护已被修改,并且数据已写入此位置。您无法从用户空间绕过这些事件,因为它们是在内核空间发出的。AV/EDR 随后可以扫描函数位置以实际验证是否执行了某些恶意操作(就绕过而言)。最终,这意味着您不应该坚持使用这个特定的补丁,因为无论使用什么用户空间逃避技术,您都可能会被检测到。

注意:这种用于入口点修补的检测已经被其他几家 EDR 供应商使用了好几年 ,但由于其广泛的使用,在 Defender 推出它时得到了*更多*的关注。

使用硬件断点

由于上述修补部分的发现,社区中的人们提出了使用硬件断点的想法。它们具有很大的优势,即用户空间钩子不需要被绕过,目标 DLL 的完整性仍然有效,并且内存扫描器无法检测到操纵。

根据我的经验,很少有供应商在运行时检测到使用硬件断点绕过 AMSI 的情况。然而,从理论上讲,可以通过检查调试寄存器值轻松检测到硬件断点 - 例如,如果其中一个设置为 AmsiScanBuffer 地址,则可以发出警报。理论与实践,从未遇到过这样的检测,可能是因为误报率?然而,一些供应商最近通过 SetThreadContext 提出了基于 ETWti 的检测,如下所述。

总体而言,根据我的经验,使用硬件断点仍然被认为是 OpSec 安全,可以抵御大多数 AV/EDR 供应商,因此是推荐的方法。但是,随着新的检测出现,这种情况随时可能改变,Cat & Mouse Bypass AMSI in 2025

防止 DLL 加载

已经发布了一些技术,可以防止 AMSI 相关的 DLL 加载,这样初始化和扫描就根本不会发生。如上所述,这主要可以用于本机语言或新生成的进程,因为在这些情况下,加载和初始化都尚未完成。例如

  • 使用 DEBUG_PROCESS 标志创建一个新进程,并使用 SharpBlock 修补 LOAD_DLL_DEBUG_EVENT 上的入口点

    https://github.com/CCob/SharpBlock

  • 以 NtCreateSection 为例, 在 DLL 加载过程中钩住函数返回失败

    https://waawaa.github.io/es/amsi_bypass-hooking-NtCreateSection/

  • DLL 加载过程中的挂钩函数会因使用 Ruy-Lopez 的新生成的进程而返回失败

    https://github.com/S3cur3Th1sSh1t/Ruy-Lopez

虽然对于 EDR 来说,挂钩通常很容易被发现,但我不知道有任何供应商会标记新设置的挂钩,这可能也是由于误报率的原因。即使他们确实对新挂钩发出警报,也可以使用硬件断点来实现相同的效果。所以我不知道有任何基于运行时的检测来检测这些技术,而且它们至今仍然有效。

针对特定替代方案

根据 AMSI 绕过目标(例如 Powershell 或 C# 程序集),可以使用其他几种替代方案。在许多情况下,它仍在修补 - 但在不同的偏移量/位置。关于 Powerhell,下图反映了我在撰写这篇博文时的个人经历 - 不要责怪我 :

Bypass AMSI in 2025

第一个绕过方法现在是一个特殊情况。它发布时,既适用于 Powershell 脚本,也适用于加载的 .NET 程序集。但在发布后,微软对 Powershell 内部进行了调整,使其不再影响脚本,而只影响 .NET 程序集。因此,这可以与橙色标记的绕过方法之一结合使用,或者如果您的脚本未被标记并且加载了程序集,那么没问题。

对于所有绿色的,你只需要混淆/修改源代码以逃避签名就可以了。由于基于补丁的检测,红色的现在更有可能被标记。橙色的只对本机 Powershell 脚本有帮助,但是一旦调用 assembly::load ,AMSI 就根本无法绕过。在某些情况下,你可能还需要删除 Add-Type 并坚持使用本机 Powershell 替代方案。很少有供应商也使用 clr.dll 挂钩,在这些情况下,你也可能由于基于行为的检测而失败,需要取消挂钩 clr.dll 。

提供程序补丁在我的仓库中有两个代码片段,带有 Add-Type 代码片段仅适用于 Powershell 脚本,而使用反射的代码片段适用于脚本和 .NET 程序集。

如您所见,绿色/橙色仍然包含一些基于补丁的旁路。但这些不太为人所知/使用,因此根据我的经验,内存扫描无法检查/发现它们。

在少数情况下,EDR 供应商甚至不再依赖 amsi.dll 进行扫描。任何针对此特定 DLL 的绕过都不会导致绕过。在这些情况下,您需要通过注册表或内存遍历枚举其 AMSI 提供程序 DLL,然后修补该 DLL 或自定义 AMSI DLL。更多信息可以在 2022 年的这个黑帽演讲中找到。

上面链接的 IBM 的 C# 程序集特定 AMSI 绕过现在也不应该在运行时被标记。整个“欺骗” CLR 从磁盘加载程序集的概念并不新鲜,并且已在 2021 年与另一个 .NET 特定绕过一起发布。自该版本发布以来,Windows Defender 的行为没有改变,通过 PoC SharpTransactedLoad 加载程序集仍然有效。但是,一些 EDR 供应商的行为并不相同,并且还将 AMSI 扫描应用于从磁盘加载的程序集,因此至少 2021 年的 PoC 不再完全 OpSec 安全。

5.AmsiScanbuffer 补丁真的失效了吗?

在阅读了一篇试图了解最近的 Defender 补丁检测的博客文章后,我开始好奇地想亲自深入研究这些检测。还有什么可能导致这样的检测?最知名的公共补丁在 AmsiScanBuffer 的入口点某处使用 0xC3 (RET) 退出该函数并返回 INVALID ARG ,让调用者认为没有发现任何恶意内容。让我们在这里玩一玩,只是为了好玩。

它只是入口点吗?

正如我之前所说,我相信 Defender 在采取行动之前会使用内存扫描来检查是否存在恶意操作。那么内存扫描只是检查函数的开头吗?那个入口点是什么样的?

Bypass AMSI in 2025

图 6:AmsiScanBuffer 入口点

首先,输入参数被推送到堆栈上。如果我们在 push r15 之后进行修补,并在返回带有 INVALID ARG 函数之前弹出已经推送的寄存器,如下所示:

Bypass AMSI in 2025

图 7:偏移量 0x14 处的替代补丁字节

代码如下:
> powershell$Win32 = @"using System;using System.Runtime.InteropServices;public class Win32 { [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);}"@Add-Type $Win32 $LoadLibrary = [Win32]::LoadLibrary("am" + "si.dll")$Address = [Win32]::GetProcAddress($LoadLibrary, "Amsi" + "Scan" + "Buffer")$p = 0[Win32]::VirtualProtect($Address, [uint32]5, 0x40, [ref]$p)# POP R14# POP R15# POP RDI$Patch = [Byte[]] (0x41, 0x5F, 0x41, 0x5E, 0x5F, 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3)$Address = [Int64]$Address + 0x14$new = [System.Runtime.InteropServices.Marshal]$new::Copy($Patch, 0, $Address, 11)

结果是 -> 我们不再被标记并且绕过了 AMSI:

Bypass AMSI in 2025

图 8:成功绕过 AMSI

这验证了此检测与入口点相关,并且仅在此处进行某种验证。不过,对于标记提前返回的情况,我们是否有其他方法可以替代使用 0xC3 ?让我们再次检查输入参数:

Bypass AMSI in 2025

第三个输入参数是要扫描的缓冲区的长度。如果我们将其设置为 0 会怎么样?这实际上应该会导致扫描 0 个字节的大小,对吗?所以我们的脚本或程序集根本不会被看到。AmsiScanBuffer 函数将输入参数从 r8 寄存器移动并将其放置在 edi 寄存器中,如下所示:

Bypass AMSI in 2025

图 10:存储在 edi 中的第三个参数的值

我们可以用 sub edi edi 替换 mov edi, r8d 来清除它的值,如下所示:

Bypass AMSI in 2025

图 11:补丁替代方案二

再次,结果是一个有效的绕过,而我们的进程没有被终止:

Bypass AMSI in 2025

图 12:无内存扫描触发器的工作旁路演示

有趣的事实:您可能还记得 Defender 曾经将字符串 amsiscanbuffer 、 amsi.dll 和补丁字节标记为恶意,对吧?现在情况已不再如此,因为这种新引入的检测现在是查找和阻止已修补的 AmsiScanBuffer 函数的主要检测。因此,这些“旧”签名现在已被内存扫描取代。

理论上,在发布本博客后,很容易添加所示的两个旁路(以及补丁的内存签名),因此不要指望它们能持续太久。但好消息是,还有几十种其他补丁替代方案。您只需要在调整补丁偏移量和字节方面发挥创造力即可。

6. 结论

多年前关于绕过 AMSI 的许多内容在几年后仍然适用。它仍然与签名有关,并通过手动修改或混淆来绕过签名。出于某种原因,多年前的混淆工具仍然未被通用签名覆盖。但一些 EDR 供应商确实为其构建了基于 AMSI 的签名,这实际上使得未经修改的工具变得毫无用处。然而,一般来说,修改或混淆仍然足以完全逃避 AMSI 检测,但由于有许多不同的供应商,因此签名数据库也不同,很难确定它们都被绕过了。

或者,在运行时操纵 AMSI 进程中涉及的 DLL 会导致通用绕过,从而可以加载已知的恶意脚本或程序集。已发布的绕过方法主要使用内存补丁或矢量异常处理程序(例如硬件断点)来在运行时操纵扫描或初始化过程。其他一些方法依赖于操纵 DLL 加载过程 - 无论是在 AMSI 尚未初始化时还是对于新生成的进程。

2025 年什么才是有效的?从我的角度来看,有效性可以通过基于行为的检测来衡量,因为所有绕过方法都可以轻松修改以避免基于签名的检测。根据我的经验,在 amsi.dll 函数的入口点使用补丁不再被认为是安全的,因为几年来,一些供应商一直通过内核事件触发的内存扫描来检测这些补丁。在撰写本文时,使用硬件断点可以被认为是更安全的 OpSec,但供应商也开始为此使用基于行为的检测,猫捉老鼠的游戏仍在继续。在加载之前操纵 DLL 加载过程或 AMSI 初始化尚未被行为检测到,但只能在初始化之前或新生成的进程中使用。

尽管由于内存扫描检测,在入口点处修补不再被认为是安全的,但对于 amsi.dll 来说,在自定义偏移处修补仍然是合适的。修补 clr.dll 或 AMSI 进程中涉及的其他 DLL 的替代方案通常也不会触发基于内存扫描的检测。那么修补就此终结了吗?我想说它还远没有终结。

原文始发于微信公众号(Ots安全):Bypass AMSI in 2025

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年4月8日20:22:35
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Bypass AMSI in 2025https://cn-sec.com/archives/3930317.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息