首页
社区
课程
招聘
[翻译]“过滤-静音”操作:调查EDR内部通信
发表于: 2023-10-4 12:45 12058

[翻译]“过滤-静音”操作:调查EDR内部通信

2023-10-4 12:45
12058

原文标题: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应用程序(EDR_Process.exe)如何与其EDR驱动程序(EDR_Driver.sys)进行通信?

在进行研究之前,我们必须了解一些EDR的基础知识;EDR代理程序在进程创建期间如何进行钩子/注入自己的DLL?

来自Christopher Vella的EDR观察中采用的回调注入模式是一个很好的总结。
图片描述
我对正在发生的事情添加了一些注释:

我们可以以ired.team网站上的一段代码作为malware.exe的示例。
图片描述
一旦钩子放置好,EDR代理程序(EDR_Process.exe)就可以监视/分析malware.exe。以下是它可能采取的一些行动示例:

注意:这是一种常见的EDR流程,并非唯一的工作方式,例如EDR_Process.exe可能只发送遥测数据,让EDR_C2决定是否恶意以及应采取的行动(阻止与否)。

如果EDR供应商或安全团队操作员(也称为蓝队)在EDR安全控制台中配置了"如果恶意则阻止"的规则,那么malware.exe进程将被EDR_Process.exe(或EDR_Driver.sys)终止。还有其他的应对措施,例如:

这一点非常重要;蓝队越有经验地创建自定义规则,攻击者越难以逃避或在网络中横向移动而不被发现!

现在,在深入研究内部通信之前,我想先退后一步,简化EDR的行为。EDR_Process.exe的内部通信(蓝色箭头)和外部通信(黄色箭头)可以用简单的概述进行可视化:
图片描述
深入研究EDR的内部通信
从Windows内核内存空间,EDR_Driver.sys可以使用多个Windows内核API(回调)来监视并阻止恶意系统活动。例如,API PsSetCreateProcessNotifyRoutine例程可以通过内核回调机制生成以下"监控日志"消息:

从用户模式内存空间,EDR_Process.exe可以向驱动程序发送行动请求并从中接收信息。例如,来自EDR安全控制台的"行动请求"可能是:

在下面的图中,我试图绘制用于监视目的的常见Windows内核回调。
图片描述
在总结这一点之后,一个问题浮现在脑海中,即如何避免EDR_Process.exe与EDR_Driver.sys之间的通信?

最常见的遮蔽EDR传感器的技术包括:

由于我们只关注EDR的内核部分,下面是移除内核回调时会发生的情况的可视化示意图:
在将EDR回调地址清零之前:
图片描述
在将EDR回调地址清零之后:
图片描述
我们不会详细讨论这个话题,这在Zach Stein的博文《在Windows上遮蔽EDR》中有所涵盖。

但是你可能会注意到在下面的图中,每当你将EDR回调地址清零时,这意味着Windows将不再向EDR_Driver.sys发送任何通知(没有"新闻通讯")。最终,将不再向EDR_Process.exe(和安全分析师控制台)发送任何事件日志!
图片描述

在研究这个话题时,我想知道如何在不进行任何回调修改的情况下避免EDR_Process.exe和EDR_Driver.sys之间的通信?我们能否阻止EDR_Process.exe和EDR_Driver.sys之间的"消息"交换?

正如我之前所说,我们希望保持在内核层面上进行讨论。我们可以想象使用这个图形表示的另一种方法:
图片描述
当我尝试使用Windbg进行调查时,Yarden Shafir撰写了一篇关于调查过滤器通信端口的绝妙博文,对此我受益匪浅。在应用程序和驱动程序之间的通信设置过程中,我发现了一些被操纵的Windows数据结构。

名为FLT_SERVER_PORT_OBJECT的数据结构引起了我的注意,因为它似乎包含了一些有趣的字段,看看你是否同意:
图片描述
当我看到这个时,脑海中首先浮现的问题是,如果将MaxConnections设置为零,会发生什么情况?

该数据结构使用名为FltCreateCommunicationPort的Windows驱动程序API进行初始化:

根据Microsoft的文档,提供了以下信息:
图片描述
我们可以得出什么结论呢?如果我们能够将MaxConnections重置为零,那么它只会阻止新的连接发生。让我们来制定以下攻击计划:

步骤1:重置MaxConnections的值
步骤2:强制EDR_Process.exe重新启动(可能需要高权限,例如NT SYSTEM)
步骤3:观察EDR的行为

这一步的首要条件是我们必须具备一个内核模式的读/写原语,以便我们可以将该值设置为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。

此阶段可能会很困难,因为EDR_Process.exe会尽其所能保护自己。通常,该程序作为一个服务启动,并且在其终止后会重新启动,但我们不关心这一点,因为由于第1步,EDR_Driver.sys不允许任何连接 ;-)

个人而言,我使用自己的工具(未签名的恶意驱动程序)来执行此操作,该工具允许我们终止受保护的进程,但也可以使用Process Hacker(如果未列入拒绝列表),或者更好的是任何可利用的“进程终止驱动程序”。我强烈推荐阅读Alice Climent-Pommeret(@AliceCliment)的博文《用LOL找到并利用进程终止驱动程序,只需3000美元》涵盖了这个主题!

让我们创建一个恶意软件(代码基础可在ired.team上找到),命名为iwanttobeflag.exe,它将触发Windows Defender的警报:
图片描述
然后,我们可以通过将恶意载荷从共享目录复制到本地磁盘来测试对恶意软件的默认反应。这将引发一个警报,并且如预期一样被Windows Defender阻止:太棒了!

现在,我们有了一个通常会引发警报的内容,我们可以用来测试我们的技术是否屏蔽了EDR。通过这样做,我们可以测试我们的“禁音技术”是否有效。

让我们将所有这些整合到一个工具中,并测试我们的第1步和第2步是否可以干扰第3步引起的警报。

我非常喜欢Thomas DIOT(Qazeer)和Maxime MEIGNAN(@th3m4ks)制作的EDRSandblast工具,它真的很棒。我提了一个拉取请求/问题,但我不知道这个项目是否得到维护。这促使我开始了自己的项目,名为EDRSnowblast,以实现这种“minifilter驱动程序静音技术”。有关该项目的更多详细信息,请访问https://v1k1ngfr.github.io/edrsnowblast/

让我们在实际计算机上按步骤进行操作,看看会发生什么!

图片描述
2. 获取有关WdFilter过滤器的详细信息:例如MaxConnections和NumberOfConnections。

图片描述
3. 将 WdFilter 静音:将 MaxConnections 设置为零。

图片描述
4.(可选)使用之前看到的 --filter-enum 选项验证 MaxConnections 值。
5.识别 Windows Defender 用户模式进程的 PID 并终止它。

6.复制我们在步骤3中创建的恶意载荷并执行。

图片描述
7.为我们的成功而欢欣鼓舞。
如果您愿意,您可以观看以下的实时演示视频。
https://youtu.be/PakPq-83IEE
这种技术已经成功地对Windows Defender和其他两个EDR供应商进行了测试。

我们必须首先问一下“filter-mute”预备条件是什么?

现在我们可以想知道Windows用户是否能够保护自己?是的,一些缓解措施是存在的。以下是一些建议:

另一个问题是:EDR供应商能够保护他们的驱动程序免受这种攻击吗?是的,他们可以!

最快的解决方案可能是将已知易受攻击的驱动程序列入拒绝列表,以防止它们被加载。但是,这种方法与防病毒软件签名具有相同的局限性;未知的易受攻击的驱动程序将不会被阻止。
开发人员可以实施更好的保护措施:

有关KDP的更多详细信息,请参考Andrea Allievi的文章:Introducing Kernel Data Protection

在这个过程中,我想给您提供最相关的资源,并感谢那些帮助这项研究更加顺利的人。

感谢您与社区分享知识!

好吧,您很好奇,这种黑客精神很棒!想知道如何获取MaxConnections值吗?下面的方法展示了如何使用Windbg获取名为bindflt.sys的驱动程序的MaxConnections值。作为提醒/帮助,请查看下面的徒步旅行地图。
图片描述
第0步 - 确定入口点

起点是名为FLTMGR!FltGlobals的结构。您可以直接获取地址:

但是,如果您需要使用内存泄漏来检索此地址,以下路径非常有效:

这样就可以计算出FLTMGR!FltGlobals的起始地址。
图片描述
第1步 - 计算存储在FLTMGR!FltGlobals中的FrameList字段地址:0xfffff8061ea8b6c0

第2步 - 使用指针间接引用并获取FLTMGR!_FLTP_FRAME中第一个帧(Links字段)的地址:0xffffca0c38c61058

第3步 - 计算第一个帧的起始地址:0xffffca0c38c61050

第4步 - 计算过滤器列表的地址:0xffffca0c38c61100

第5步 - 使用指针间接引用并获取第一个过滤器的地址(PrimaryLink字段):0xffffca0c386e8020

第6步 - 计算第一个过滤器的地址(FLTMGR!_FLT_FILTER的基址):0xffffca0c386e8010

好的,很好。我们可以使用Windbg过滤器内核调试器(fltkd)命令来验证和可视化我们所在的位置,该命令名为frames:
图片描述
第7步 - 计算服务器端口列表的地址:0xffffca0c386e8250

第8步 - 使用指针间接引用并获取第一个服务器端口对象的地址(FLTMGR!_FLT_SERVER_PORT_OBJECT的FilterLink地址):0xffffca0c3eaf73f0

第9步 - 计算MaxConnections字段的地址:0xffffca0c3eaf7430

第10步 - 使用指针间接引用并获取MaxConnections的值:1000

或者

最后!您成功了!
图片描述
最后的步骤 - 将MaxConnections的值设置为零并终止EDR_Process.exe进程

然后

由于对我来说,在内核内存中可视化自己所处的位置、数据结构的字段、数据结构之间的链接、应该使用的偏移量等等,这真是一场噩梦。因此,我制作了下面的地图(还包括Windbg命令)。
图片描述
也许(就像我一样),您想知道那些用于计算的值是什么:是的,那些是偏移量。

偏移量可能会在Windows更新后发生变化,个人我使用了EDRSandblast的ExtractOffsets.py脚本的自定义版本(在EDRSnowblast中可用)。下面显示了输出示例。
图片描述
因为我们知道方法和偏移量,我们可以自动化这个过程!PoC或者GTFO,可以在EDRSnowblast中找到执行此操作的代码。

祝您愉快的黑客之旅!

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
);
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
);
copy z:\iwanttobeflag.exe c:\
copy z:\iwanttobeflag.exe c:\
EDRSnowblast.exe filter-enum --kernelmode
EDRSnowblast.exe filter-enum --kernelmode
EDRSnowblast.exe filter-enum --kernelmode --filter-index 9
EDRSnowblast.exe filter-enum --kernelmode --filter-index 9
EDRSnowblast.exe filter-mute --kernelmode --filter-index 9
EDRSnowblast.exe filter-mute --kernelmode --filter-index 9
tasklist | findstr MsMpEng.exe
MsMpEng.exe                   2956 Services                   0    206,788 K
c:\pimpmypid_clt.exe /kill 2956
tasklist | findstr MsMpEng.exe
MsMpEng.exe                   2956 Services                   0    206,788 K
c:\pimpmypid_clt.exe /kill 2956
copy z:\iwanttobeflag.exe c:\
c:\iwanttobeflag.exe
copy z:\iwanttobeflag.exe c:\
c:\iwanttobeflag.exe
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");
   }
}
// 其他常见的错误应该检查的有:
// ERROR_BAD_PATHNAME (HR=0x800700A1)
// E_FILE_NOT_FOUND (HR=0x80070002)
// E_ACCESSDENIED (HR=0x80070005)
// ERROR_INVALID_NAME (HR=0x8007007B)
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");
   }
}
// 其他常见的错误应该检查的有:
// ERROR_BAD_PATHNAME (HR=0x800700A1)
// E_FILE_NOT_FOUND (HR=0x80070002)
// E_ACCESSDENIED (HR=0x80070005)
// ERROR_INVALID_NAME (HR=0x8007007B)
kd> ? FLTMGR!FltGlobals
kd> ? FLTMGR!FltGlobals
lmdvm fltmgr
lmdvm fltmgr
u FLTMGR!FltEnumerateFilters L15
u FLTMGR!FltEnumerateFilters L15
? fffff806`1ea8b658 - 0x58
? fffff806`1ea8b658 - 0x58
kd> ? fffff806`1ea8b600 + 0x58 + 0x68
kd> ? fffff806`1ea8b600 + 0x58 + 0x68
kd> ? poi(fffff806`1ea8b6c0)
kd> ? poi(fffff806`1ea8b6c0)
kd> ? ffffca0c`38c61058 - 0x008

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 3
支持
分享
最新回复 (2)
雪    币: 3059
活跃值: (30876)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2023-10-5 20:07
1
雪    币: 97
活跃值: (141)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
3
感谢分享
2023-12-28 06:35
0
游客
登录 | 注册 方可回帖
返回
//