Windows文件系统驱动开发挑战:NTFS安全描述符继承漏洞解析

本文详细分析了Windows NTFS文件系统中一个长期存在的安全描述符继承漏洞,通过代码示例和命令行验证展示了$INDEX_ALLOCATION技巧如何导致目录继承错误的安全权限。

Windows文件系统驱动开发挑战:NTFS安全描述符继承漏洞解析

一条来自@jonasLyk的推文让我想起了几个月前在NTFS中发现的一个漏洞,经核实该漏洞在Windows 10 2004版本中仍然存在。虽然这个漏洞不能直接用于绕过安全机制,但很可能被利用在攻击链中。NTFS很好地展示了在Windows上编写文件系统驱动程序的复杂性,因此随着时间的推移出现各种奇怪的边界情况并不令人意外。

这个问题的核心在于创建新目录时的默认安全描述符(SD)分配机制。如果你了解Windows安全描述符,就会知道可以通过CONTAINER_INHERIT_ACE和/或OBJECT_INHERIT_ACE标志来指定继承规则。这些标志决定了当新条目是目录或文件时,是否应该从父目录继承ACE。

让我们看看NTFS用于为新文件分配安全性的代码,看看你能否发现这个bug:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
NTSTATUS NtfsAssignSecurity(PIO_STACK_LOCATION StackLocation, 
 PFILE_OBJECT FileObject, 
 PFILE_OBJECT ParentFileObject) {

 BOOLEAN IsDirectoryObject = 
 StackLocation->Parameters.Create.Options & FILE_DIRECTORY_FILE;
 
 PACCESS_STATE AccessState = 
 StackLocation->Parameters.Create.SecurityContext->AccessState;
 
 PGENERIC_MAPPING GenericMapping = 
 IoGetFileObjectGenericMapping();
 
 PSECURITY_DESCRIPTOR NewDescriptor;

 NTSTATUS status = SeAssignSecurityEx(
  ParentFileObject->SecurityDescriptor,
  AccessState->SecurityDescriptor,
  &NewDescriptor,
  NULL,
  IsDirectoryObject,
  &AccessState->SubjectSecurityContext);
    ...
}

这段代码使用SeAssignSecurityEx基于父SD和调用者提供的显式SD来创建新的安全描述符。要使继承生效,不能指定显式SD,因此我们可以忽略这一点。SeAssignSecurityEx是否应用目录或文件的继承规则取决于IsDirectoryObject参数的值。如果向NtCreateFile传递了FILE_DIRECTORY_FILE选项标志,则该参数设置为TRUE。看起来没问题:如果不指定FILE_DIRECTORY_FILE标志,就无法创建目录;如果不指定任何标志,默认会创建文件。

但等等,这完全不对。如果你指定了形如ABC::$INDEX_ALLOCATION的名称,那么无论指定什么标志,NTFS都会创建一个目录。因此,这个bug就是:如果你使用$INDEX_ALLOCATION技巧创建目录,新的安全描述符将按照文件而不是目录的方式继承。我们可以在命令提示符下验证这种行为:

1
2
3
C:\> mkdir ABC
C:\> icacls ABC /grant "INTERACTIVE":(CI)(IO)(F)
C:\> icacls ABC /grant "NETWORK":(OI)(IO)(F)

首先我们创建一个目录ABC并授予两个ACE:一个用于INTERACTIVE组将在目录上继承,另一个用于NETWORK将在文件上继承。

1
2
C:\> echo "Hello" > ABC\XYZ::$INDEX_ALLOCATION
Incorrect function.

然后我们使用$INDEX_ALLOCATION技巧创建子目录XYZ。我们可以确定它成功了,因为CMD在尝试将"Hello"写入目录对象时打印了"Incorrect function"。

1
2
3
4
C:\> icacls ABC\XYZ
ABC\XYZ NT AUTHORITY\NETWORK:(I)(F)
        NT AUTHORITY\SYSTEM:(I)(F)
        BUILTIN\Administrators:(I)(F)

转储XYZ子目录的安全描述符,我们看到ACE是基于文件而不是目录继承的,因为我们看到了NETWORK的ACE而不是INTERACTIVE的ACE。最后我们列出ABC来确认它确实是一个目录:

1
2
3
4
5
6
7
8
9
C:\> dir ABC
 Volume in drive C has no label.
 Volume Serial Number is 9A7B-865C

 Directory of C:\ABC

2020-05-20  19:09    <DIR>          .
2020-05-20  19:09    <DIR>          ..
2020-05-20  19:05    <DIR>          XYZ

这个漏洞有用吗?老实说可能没什么用。我能想象的唯一场景是:如果你可以向系统服务指定一个路径,该服务在某个位置创建文件,其中继承的文件访问权限会授予访问权,而继承的目录访问权限不会。这将允许你创建一个可以控制的目录,但说实话这似乎有点牵强。如果有人能想到这个漏洞的好用途,请告诉我或微软:-)

尽管如此,有趣的是这是另一个案例,说明在确定对象是目录还是文件时,$INDEX_ALLOCATION没有被正确验证。另一个很好的例子是CVE-2018-1036,你可以在只有FILE_ADD_FILE权限的情况下创建新目录。为什么在设计上决定在使用流类型时自动创建目录,这一点尚不清楚。我想我们可能永远不会知道。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计