您现在的位置是:首页 >技术交流 >如何使用osquery在Windows上实时监控文件?网站首页技术交流

如何使用osquery在Windows上实时监控文件?

网安周 2023-06-26 04:00:02
简介如何使用osquery在Windows上实时监控文件?

导语:Osquery是一个SQL驱动操作系统检测和分析工具,它由Facebook创建,支持像SQL语句一样查询系统的各项指标,可以用于OSX和Linux操作系统。

Osquery是一个SQL驱动操作系统检测和分析工具,它由Facebook创建,支持像SQL语句一样查询系统的各项指标,可以用于OSX和Linux操作系统。Osquery是一个多平台软件,可以安装在Linux,Windows,MacOS和FreeBSD上。它允许我们使用基于SQL的查询来处理操作系统的配置文件、性能、安全检查等。

加密安全团队Trail of Bits开发了一个ntfs_journal_events工具,准确地说,这是一个适用于Windows的新的基于事件的osquery表,支持实时的文件更改监控。你现在就可以使用这个表来有效地监控Windows端点上特定文件、目录和整个模式的更改。
在这里插入图片描述

为安全和管理目的而进行的文件监控

文件事件监控和审计是端点安全和管理的重要基础:

  1. 许多恶意活动都是通过众所周知且易于识别的文件系统活动模式可靠地预先检测或预测的:比如重写系统库、将有效载荷放置到固定位置以及(试图)删除防御程序,所有这些都表明存在潜在的危害。

  2. 非恶意的完整性违规也可以通过文件监控检测到,比如员工越狱他们的公司设备或以其他方式绕过公司制定的安全政策;

  3. 跨大型机群的软件部署、更新和自动化配置:比如是否每个主机都安装了软件X并将其更新到版本Y?

  4. 非安全问题故障的自动排除和修复:比如共享文件的权限不正确、网络配置错误、磁盘被过度利用。

Windows上文件监控的简要介绍

Windows上的文件监控方法通常分为以下三种:

  1. Win32/WinAPI接口:FindFirstChangeNotification, ReadDirectoryChangesW;

  2. 文件系统过滤器驱动程序和Minifilter,所谓的Minifilter其实就是符合过滤器标准的过滤组件,它其实是一组回调函数,这组回调函数向过滤管理器注册之后,在合适的时机(比如,要求的文件操作发生时)过滤管理器就会以合适的方式来调用某个回调函数。如果我们编写这个回调函数中的内容,就可以对文件系统加以过滤了。这比花很多精力去绑定各种设备要简单得多,因为复杂的任务都在过滤管理器里面做了。

  3. 日志监控。

我们将在下面介绍每种方法的技术细节,以及它们的优缺点(包括一般性的和与osquery有关的)。

Win32 API

Windows API提供了一组(大部分)与文件系统无关的函数,用于对注册目录上的事件进行轮Windows API提供了很多与文件系统无关的函数,用于轮询注册目录中的事件:

  1. FindFirstChangeNotification可用于在特定目录的条目以及所有子目录的条目上放置一组通知过滤器。

  2. FindFirstChangeNotification返回的句柄可以与标准的Windows对象等待例程一起使用,例如WaitForSingleObject和WaitForMultipleObjects。

  3. 等待和处理后,后续事件可以使用FindNextChangeNotification进行排队。

这些例程有几个漏洞:

  1. FindFirstChangeNotification不监控指定的目录本身,只监控其条目。因此,监控目录及其条目的方法是两次调用该函数:一次为目录本身,另一次为其父目录或驱动器根目录。反过来,如果父目录中惟一感兴趣的条目是目录本身,则需要进行额外的筛选。

  2. 这些例程为检索文件系统事件提供过滤和同步,但不公开事件本身或其关联的元数据。实际的事件必须通过ReadDirectoryChangesW来检索,它接受被监控目录的一个打开句柄和许多与轮询函数相同的参数,因为它可以完全独立于它们使用。为了在异步上下文中安全地使用ReadDirectoryChangesW,用户还必须熟悉OVERLAPPED,OVERLAPPED即OVERLAPPED是一个包含了用于异步输入输出的信息的结构体。

  3. ReadDirectoryChangesW很难与Windows上的回收站和其他伪目录概念一起使用,这篇文章建议使用GetFinalPathNameByHandle解析最终移动的名称。这个GitHub问题表明,函数的行为在Windows版本之间也是不一致的。

  4. 最后但并非最不重要的是,ReadDirectoryChangesW在内部为每个目录句柄使用一个固定大小的缓冲区,如果它不能跟上事件的数量,就会在处理所有更改记录之前刷新它们。换句话说,它的内部缓冲区不作为一个环,并且在存在大量高I/O载荷的情况下,它不会逐渐或优雅地降级。

还有一种更老的解决方案:SHChangeNotifyRegister可用于将窗口注册为通过Windows消息从shell程序(即Explorer)接收文件通知的收件人。这种方法也有很多缺点:它要求接收方应用程序维护一个窗口,即使它只是一个只显示消息的窗口,使用文件系统路径的一些奇怪的“项目列表”视图,并且受到Windows消息传递的吞吐量的限制。

总而言之,这些API的性能和准确性问题使它们不适合osquery。

过滤器驱动程序和Minifilter

与Windows环境中的许多其他工程挑战一样,文件监视具有内核模式API形式的核选择。 Windows足以为此目的提供两个常规类别:老旧文件系统过滤器API和更新的minifilter框架。我们将在这篇文章中讨论后者,因为这是微软推荐的。

Minifilter是内核模式驱动程序,它直接插入由Windows文件系统执行的I/O操作。因为它们是在公共文件系统接口层进行操作的,所以它们在大多数情况下不了解其底层存储,理论上,它们可以插入NT内核已知的任何文件系统操作,而与文件系统类型或底层实现无关。Minifilter也是可组合的,这意味着可以注册多个过滤器并与文件系统进行交互,而不会产生冲突。

Minifilter是通过过滤器管理器实现的,它根据配置的唯一“高度”(较低的海拔高度对应于较早的负载,因此较早的访问权限)和“加载顺序组”中的存在(对应于唯一的高度范围)建立一个过滤器加载顺序。加载顺序组本身是按升序加载的,其成员则是按随机顺序加载的,这意味着,与你所在组中的另一个Minifilter相比,具有更低的高度并不保证具有更高的优先级。微软提供了一些加载顺序组和高度范围的文档,这里有一份已知“高度”的列表,你甚至可以自定义一个!

虽然功能强大、灵活,并且通常是在Windows上自检文件系统的正确选择,但是Minifilter不适合osquery的文件监控用途:

  1. 对于树内(即非扩展)表,osquery具有禁止系统修改的策略。安装Minifilter需要我们通过加载驱动程序来修改系统,并且需要osquery随驱动程序一起提供或在安装时获取一个驱动程序。

  2. 因为Minifilter是完整的内核模式驱动程序,所以它们具有不良的安全性和稳定性风险。

  3. osquery的设计向用户保证:它是一个单一可执行的、用户模式的代理,在运行时自动监控它的性能开销,内核模式的驱动程序会违反这种设计。

日志监控

我们可以使用第三个选项:NTFS日志。

像大多数(相对)现代的文件系统一样,NTFS被记录在日志中:对基础存储的更改之前是对(通常是循环的)区域的更新,该区域记录了与更改相关的元数据。丹·卢(Dan Luu)的“文件充满危险”,其中包含一些以“撤消日志”形式进行日志记录的好例子。

日记功能具有许多优点:

  1. 增强了抵御破坏力的能力:单个I/O操作(例如,取消链接文件)的用户空间,内核到硬件的完整操作链在内部不是原子的,这意味着崩溃可能使文件系统处于不确定状态或被破坏的状态,拥有最后一次预提交操作的日志记录使文件系统更有可能被回滚到一个已知的良好状态。

  2. 因为日志提供了文件系统操作的可逆记录,所以可以更积极地与底层存储硬件进行交互:触发提交的批处理大小可以增加,从而提高性能。

  3. 相对于文件系统,由于日志是及时且较小的,因此可用于避免对元数据进行昂贵的文件系统查询(例如stat)。这在Windows上尤其重要,在Windows中,元数据请求通常涉及获取完整句柄。

NTFS的日志记录机制实际上分为两个单独的组件: L o g F i l e 是一个预写日志,用于处理日志记录以进行回滚,而更改日志( LogFile是一个预写日志,用于处理日志记录以进行回滚,而更改日志( LogFile是一个预写日志,用于处理日志记录以进行回滚,而更改日志(Extend$UsnJrnl)按种类记录卷上最近的更改,不过不包含不包含回滚所需的偏移量和大小信息。

Windows将后者用于“文件历史记录”功能,这也是我们将要使用的功能。

访问变更日志

先说明一下,下面的样本已经被简化了,它们不包含错误处理和边界检查,这两项对于安全和正确使用都是必需的。在复制之前,请阅读MSDN或osquery中的完整源代码!

对于我们来说幸运的是,打开NTFS更改日志的句柄并从中读取内容是一件相对轻松的事情,只需几个步骤。

  1. 我们将通过一个普通的旧的CreateFile调用获得我们想要监控的卷的句柄:
    在这里插入图片描述
  2. 我们在句柄上发出DeviceIoControl[FSCTL_QUERY_USN_JOURNAL]来获取最新的更新序列号(USN)。USN唯一标识一起提交的批记录,我们将使用第一个按时间顺序“锚定”查询:在这里插入图片描述
  3. 我们使用FSCTL_READ_USN_JOURNAL发出另一个DeviceIoControl,以从日志中提取记录的原始缓冲区,我们使用READ_USN_JOURNAL_DATA_V1来告诉日志只给我们从最后一步得到的USN开始的记录:
    在这里插入图片描述
    请注意最后两个字段(2U和3U),稍后再讨论。

解释变更记录缓冲区

DeviceIoControl[FSCTL_READ_USN_JOURNAL]为我们提供了一个长度可变的USN_RECORDs原始缓冲区,前面加上一个USN前缀,我们可以使用它来发出未来的请求:在这里插入图片描述
然后,在process_usn_record中:
在这里插入图片描述
回想一下READ_USN_JOURNAL_DATA_V1中的最后两个字段,它们对应于返回给我们的USN_RECORD版本的范围。我们明确排除了v4记录,因为它们仅作为范围跟踪的一部分发出,不包含我们需要的任何附加信息,你可以在其MSDN页面上阅读有关它们的更多信息。

MSDN明确指出了这些强制转换的必要性:USN_RECORD是USN_RECORD_V2的别名,并且USN_RECORD_V3不保证具有除USN_RECORD_COMMON_HEADER之外的任何公共布局。

但是,一旦出现问题,以下两个字段均可用:

  1. Reason:标志的位掩码,指示当前记录中已累积的更改。有关原因常量的列表,请参见MSDN的USN_RECORD_V2或USN_RECORD_V3。

  2. FileReferenceNumber:引用底层文件系统对象的惟一(通常为128位)序号,这与可以通过调用getfileidinfo作为信息类的GetFileInformationByHandleEx来获得的FRN相同。FRN大致相当于UNIX系统中的“inode”概念,并且具有类似的语义(每个文件系统是惟一的,而不是系统范围的)。什么是inode?要理解inode,要从文件储存说起。文件储存在硬盘上,硬盘的最小存储单位叫扇区(Sector)。每个扇区储存512字节(相当于0.5KB)。操作系统读取硬盘的时候,不会一个个扇区地读取,这样效率太低,而是一次性连续读取多个扇区,即一次性读取一个块(block)。这种由多个扇区组成的块是文件存取的最小单位,块的大小,最常见的是4KB,即连续八个sector组成一个block,文件数据都储存在块中,那么很明显,我们还必须找到一个地方储存文件的元信息,比如文件的创建者、文件的创年日期、文件的大小等等。这种储存元信息的区域叫做inode,中文译名为”索引节点”。

  3. ParentFileReferenceNumber:另一个FRN,这个FRN表示该记录所针对的文件或目录的父目录或卷。

  4. FileNameLength, FileNameOffset, FileName:字节长度、偏移量和指向该文件或目录文件名的指针。请注意,FileName是基本名称(即非限定名称),要检索完全限定名称,我们需要通过打开父FRN的句柄(OpenFileById),调用GetFinalPathNameByHandle并将二者结合来解析父FRN的名称。

通过更改日志记录文件事件,可以观察到,我们的方法已经绕过了文件监控中的许多常见性能和开销问题:我们完全异步运行,而不会阻塞文件系统,仅这一点就比在每个I/O操作上增加开销的minifilter模型有了很大的改进。

在osquery更改日志记录

上面我们介绍了通过单个卷检索和解释变更日志记录,但osquery的用例更加复杂,因为我们希望监控用户注册的每个卷,并对检索到的记录执行过滤,从而将输出限制在一组配置的模式中。

每个NTFS卷都有自己的更改日志,因此需要独立地打开和监控每个卷,而osquery的发布子框架非常适合这项任务,具体过程如下:

  1. 定义一个事件发布程序(NTFSEventPublisher);

  2. 在配置阶段(NTFSEventPublisher::configure()),我们从Linux的file_events表中读取用户配置:在这里插入图片描述

  3. 配置为我们提供了用于监控变更日志的卷的基本列表,我们为每个类创建一个USNJournalReader,并通过Dispatcher::addService()将它们作为服务添加;

  4. 每个阅读器执行自己的更改日志监控和事件收集,向发布服务器报告事件列表;

  5. 我们执行一些规范化,包括将“旧”和“新”事件重命名为单个NTFSEventRecords。我们还维护了一个父frn到目录名的缓存,以避免由于目录重命名而丢失的更改,并最小化我们发出的开放处理请求的数量;

  6. fire()通过订阅表ntfs_journal_events使用这些规范化事件。

完成这些步骤后,就得到了上面的屏幕截图所示的基于事件的表。
在这里插入图片描述
总结

ntfs_journal_events表使osquery成为Windows上文件监控的首选项,并进一步减少了Windows和Linux/macOS之间osquery特性的差距(Linux/macOS拥有file_events表已经很长时间了)。

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。