最近趋势科技研究团队的Guy Lederfein和Jason McFadyen详细介绍了微软Windows操作系统近期修复的一个代码执行漏洞。该漏洞最初是由Yuki Chen发现并报告给微软的。Windows网络文件系统存在堆栈缓冲区溢出漏洞。远程攻击者可以通过向服务器发送特制的 RPC 数据包来利用此漏洞,从而在 SYSTEM 上下文中执行代码。以下是关于CVE-2022-26937的部分内容,有一些小的修改。
Windows 网络文件系统中存在堆栈缓冲区溢出漏洞。该漏洞是由于对 Network Lock Manager (NLM) RPC 程序对 Portmap 请求的精心设计的 RPC 响应处理不当造成的。
远程攻击者可以通过向目标服务器发送恶意 RPC 调用来利用此漏洞。成功利用可能会导致在 SYSTEM 环境下执行任意代码。不成功的利用会导致目标系统崩溃。
漏洞
Microsoft Windows 附带了几个旨在与非 Windows 文件共享进行通信和交互的网络功能。其中一个模块称为网络文件系统(NFS)。
NFS是一种网络文件系统协议,最初由Sun Microsystems于1984年开发。版本 2 记录在 RFC 1094 中。版本 3 记录在 RFC 1813 中。版本 4 由 IETF 开发并记录在 RFC 3010(2000 年 12 月发布)和在 RFC 3530(2003 年 4 月发布)和 RFC 7530(2015 年 3 月发布)中进行了修订。NFS 允许用户以与访问本地文件系统相同的方式访问远程文件共享。可以在共享上设置不同的访问级别和权限,例如读写和只读。此外,还可以使用 IP/UID/GID/Kerberos 安全性。 NFS 使用开放网络计算 (ONC) 远程过程调用 (RPC) 来交换控制消息。 ONC RPC 最初由 Sun Microsystems 开发,也可以称为 Sun RPC。
Network Lock Manager (NLM) 协议是 NFS 版本 2 和 3 的扩展,它提供了 System V 模式的咨询文件和网络上的记录锁定。由于 NFS 版本 2 和 3 是无状态协议,因此开发了 NLM 协议来管理存储在 NFS 共享上的文件的锁定状态。 NLM 协议支持同步和异步过程来实现锁定和文件共享功能。与 NFS 类似,NLM 也使用 ONC RPC 来交换控制消息。
ONC RPC 使用 XDR 数据包,可以通过 UDP 或 TCP 传输。通过 UDP,XDR 数据包包含在 UDP 有效负载中。在 TCP 上,XDR 数据包前面有一个 Fragment 标头(如下表所示)。 Fragment 头的最高位表示该数据包是否是最后一个分片,其余 31 位是后面的 Fragment 的长度。 Fragment 本身包含 XDR 数据包。
ONC RPC调用的结构如下:
RPC 消息的 Program 字段指定将消息发送到哪个 RPC 服务。 Windows NFS 服务器通过RPC 实现NLM 协议,程序类型设置为100021。它支持多个RPC 过程,可以在RPC 消息的Procedure 字段中指定。 以下是NLM协议版本3支持的同步和异步过程列表:
Microsoft Windows运行RPCBIND RPC程序,它实现了RFC 1833中记录的端口映射器协议。RPCBIND程序将RPC程序号转换为通用地址,然后程序可以使用通用地址进行UDP或TCP通信。简而言之,它的工作方式是,当程序希望使用 RPC 时,它会将其端口注册到主机的端口映射器。希望发出RPC调用的客户端连接到端口映射器,使用各种RPC调用,如GETPORT、GETADDR等,以获得RPC服务可用的端口,并连接到所需的RPC服务。IANA已经定义和维护了标准RPC程序号,它们包括portmapper(100000)、nfs(100003)、mount daemon(100005)和其他数百个不太常用的程序。只有portmapper服务和NFS服务的标准端口分别为111和2049。
Windows通过RPC实现RPCBIND协议,程序类型设置为100000。它支持多个RPC过程,可以在RPC消息中的Procedure字段中指定这些过程。当程序版本设置为3或4时,RPCBIND程序支持的一个过程是GETADDR,它是过程3。对此 RPC 调用的回复包含与被调用者关联的通用地址。返回的通用地址的格式为 XDR_String,格式如下:
Windows 网络文件系统中存在堆栈缓冲区溢出漏洞。更具体地说,该漏洞是由于对GETADDR RPC 回复中返回的通用地址字段的错误处理造成的。当 Windows NFS 以异步方式响应 NLM 调用时,将调用 NlmGetClientAddressAndConnection() 函数。此流程是作为对异步 NLM 调用(例如 NLM_TEST_MSG、NLM_LOCK_MSG、NLM_UNLOCK_MSG)的响应,或者当服务器在客户端先前(同步或异步)调用以创建锁返回状态“LCK_BLOCKED”。如果 IPv6 协议用于通过 ONC RPC 协议进行通信,则服务器将向客户端发出 GETADDR RPC 调用以检索其 IP 地址。当 NFS 服务器处理客户端在 GETADDR 回复中返回的通用地址字段时,使用 memmove() 将该字段复制到大小为 96 字节的缓冲区中。此外,稍后会在等于通用地址字段的字符串大小的索引处引用此缓冲区并将其设置为 0。但是,NlmGetClientAddressAndConnection() 函数不会验证返回的通用地址字符串的大小。因此,如果客户端提供的字符串为 96 字节或更长,缓冲区将被写入超出其边界,从而导致堆栈缓冲区溢出情况。
远程攻击者可以通过发送NLM请求触发NFS服务器的异步响应来利用此漏洞。当服务器发送一个GETADDR RPC请求时,攻击者可以用一个精心制作的GETADDR回复来响应。成功的利用可能导致在SYSTEM上下文中任意执行代码。不成功的利用会导致目标系统崩溃。
源代码
以下代码片段取自 nfssvr.sys 版本 10.0.17763.1999。趋势科技添加的注释被突出显示。
In function NlmGetClientAddressAndConnection():
检测攻击
ONC RPC 使用 XDR 数据包,可以通过 UDP 或 TCP 传输。通过 UDP,XDR 数据包包含在 UDP 有效负载中。在 TCP 上,XDR 数据包前面有一个 Fragment 标头(如下表所示)。 Fragment 标头的最高位表示该数据包是否是最后一个Fragment ,其余 31 位是后面的 Fragment 的长度。 Fragment 本身包含 XDR 数据包。
检测设备必须检查所有传出的 ONC RPC 调用,其结构如下:
检测设备需要检查Message Type字段是否为0 (Call), Program Type字段是否为100000 (portmap), Program Version字段是否大于2,Procedure字段是否为3 (GETADDR)。如果找到,设备必须检查特定于program的数据对象。该对象使用rpcb结构,格式如下:
如果 Program Number 字段设置为 100021 (NLM),则检测设备必须检查所有传入的 ONC RPC 回复,其结构如下:
检测设备需要检查XID字段是否等于检测到的GETADDR RPC调用的XID字段,且Message Type字段为1 (Reply)。如果找到,设备必须检查特定于program的数据对象。该对象使用XDR_String结构,其格式如下:
如果String Length字段大于95,则认为该流量是可疑的;可能正在进行利用此漏洞的攻击。
总结
微软于 2022 年 5 月修复了此漏洞,并分配了 CVE-2022-26937。在他们的文章中,他们还列出了禁用NFSV2和NFSV3作为缓解攻击的一种方法。然而,这可能会导致功能的丧失。应用安全更新是完全解决此漏洞的最佳方法。