适用于驱动程序开发人员的 Windows 安全模型

Windows 安全模型基于安全对象。 操作系统的每个组件必须确保其负责的对象的安全性。 因此,驱动程序必须保护其设备和设备对象的安全性。

本主题总结了 Windows 安全模型如何应用于内核模式驱动程序。

Windows 安全模型

Windows 安全模型主要基于每个对象的权限,具有少量的系统范围权限。 可以保护的对象包括但不限于进程、线程、事件和其他同步对象,以及文件、目录和设备。

对于每种类型的对象,一般读取、写入和执行权限映射到特定于对象的详细权限。 例如,对于文件和目录,可能的权限包括读取或写入文件或目录、读取或写入扩展文件属性的权限、遍历目录的权限以及写入对象的安全描述符的权限。

安全模型涉及以下概念:

  • 安全标识符 (SID)
  • 访问令牌
  • 安全描述符
  • 访问控制列表 (ACL)
  • 权限

安全标识符 (SID)

安全标识符 (SID,也称为 主体) 标识用户、组或登录会话。 每个用户都有一个唯一的 SID,操作系统在登录时会检索该 ID。

SID 由操作系统或域服务器等机构颁发。 某些 SID 是众所周知的,并且具有名称和标识符。 例如,SID S-1-1-0 标识 Everyone (或 World) 。

访问令牌

每个进程都有一个访问令牌。 访问令牌描述该过程的完整安全上下文。 它包含用户的 SID、用户所属组的 SID 和登录会话的 SID,以及授予用户的系统范围权限的列表。

默认情况下,每当进程的线程与安全对象交互时,系统都对进程使用主访问令牌。 但是,线程可以模拟客户端帐户。 当线程模拟时,除了自己的主令牌外,线程还具有模拟令牌。 模拟令牌描述线程正在模拟的用户帐户的安全上下文。 模拟在远程过程调用 (RPC) 处理中尤其常见。

描述线程或进程的受限安全上下文的访问令牌称为受限令牌。 受限 令牌 中的 SID 只能设置为拒绝访问安全对象,不允许访问。 此外,令牌可以描述一组有限的系统范围特权。 用户的 SID 和标识保持不变,但在进程使用受限令牌时,用户的访问权限受到限制。 CreateRestrictedToken 函数创建受限令牌。

安全描述符

每个命名的 Windows 对象都有一个安全描述符;一些未命名的对象也这样做。 安全描述符描述对象的所有者和组 SID 及其 ACL。

对象的安全描述符通常由创建对象的函数创建。 当驱动程序调用 IoCreateDeviceIoCreateDeviceSecure 例程来创建设备对象时,系统会向创建的设备对象应用安全描述符并设置对象的 ACL。 对于大多数设备,ACL 在设备信息 (INF) 文件中指定。

有关详细信息,请参阅内核驱动程序文档中 的安全描述符

访问控制列表

访问控制 Lists (ACL) 启用对对象的访问的精细控制。 ACL 是每个对象的安全描述符的一部分。

每个 ACL (ACE) 包含零个或多个访问控制项。 每个 ACE 又包含一个 SID,用于标识用户、组或计算机,以及拒绝或允许该 SID 的权限列表。

设备对象的 ACL

可以通过以下三种方式之一设置设备对象的 ACL:

  • 在其设备类型的默认安全描述符中设置。
  • RtlCreateSecurityDescriptor 函数以编程方式创建,由 RtlSetDaclSecurityDescriptor 函数设置。
  • 在安全描述符定义语言 (SDDL) 设备的 INF 文件或对 IoCreateDeviceSecure 例程的调用中指定。

所有驱动程序都应使用 INF 文件中的 SDDL 为其设备对象指定 ACL。

SDDL 是一种可扩展的描述语言,使组件能够创建字符串格式的 ACL。 用户模式代码和内核模式代码都使用 SDDL。 下图显示了设备对象的 SDDL 字符串的格式。

显示设备对象的 SDDL 字符串格式的关系图。

Access 值指定允许的访问类型。 SID 值指定一个安全标识符,用于确定 Access 值应用于谁 (例如用户或组) 。

例如,以下 SDDL 字符串允许系统 (SY) 访问所有内容,并允许其他所有人 (WD 仅) 读取访问权限:

“D:P(A;;GA;;;SY)(A;;GR;;;WD)”

头文件 wdmsec.h 还包括一组适用于设备对象的预定义 SDDL 字符串。 例如,头文件定义SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RWX_RES_RWX,如下所示:

"D:P(A;;GA;;;SY)(A;;GRGWGX;;;BA)(A;;GRGWGX;;;WD)(A;;GRGWGX;;;RC)"

此字符串的第一段允许内核和操作系统 (SY) 完全控制设备。 第二个段允许内置管理员组 (BA) 中的任何人访问整个设备,但不能更改 ACL。 第三个段允许 (WD) 的每个人都读取或写入设备,第四个段向不受信任的代码 (RC) 授予相同的权限。 驱动程序可以按原样使用预定义字符串,也可以作为特定于设备对象的字符串的模型使用。

堆栈中的所有设备对象都应具有相同的 ACL。 更改堆栈中一个设备对象上的 ACL 会更改整个设备堆栈上的 ACL。

但是,向堆栈添加新设备对象不会更改任何 ACL,新设备对象的 ACL (如果新设备对象的 ACL) 或堆栈中任何现有设备对象的 ACL。 当驱动程序创建新的设备对象并将其附加到堆栈顶部时,驱动程序应通过从下一个较低的驱动程序复制 DeviceObject.Characteristics 字段,将堆栈的 ACL 复制到新的设备对象。

IoCreateDeviceSecure 例程支持使用 WD 和 SY 等预定义 SDDL 字符串的子集。 用户模式 API 和 INF 文件支持完整的 SDDL 语法。

使用 ACL 进行安全检查

当进程请求访问对象时,安全检查会将对象的 ACL 与调用方访问令牌中的 SID 进行比较。

系统会按严格的自上而下顺序比较 ACE,并在第一次相关匹配时停止。 因此,创建 ACL 时,应始终将拒绝 ACE 置于相应的授予 ACE 之上。 以下示例演示比较的进行方式。

示例 1:将 ACL 与访问令牌进行比较

示例 1 演示了系统如何将 ACL 与调用方进程的访问令牌进行比较。 假设调用方想要打开具有下表所示的 ACL 的文件。

示例文件 ACL

权限 SID Access
Allow 计帐 写入、删除
Allow Sales 追加
拒绝 Legal 追加、写入、删除
Allow 所有人 读取

此 ACL 有四个 ACE,它们专门适用于会计组、销售组、法律组和每个人组。

接下来,假设请求进程的访问令牌包含一个用户和三个组的 SID,顺序如下:

用户 Jim (S-1-5-21...)

组会计 (S-1-5-22...)

Group Legal (S-1-5-23...)

将所有人 (S-1-1-0)

将文件 ACL 与访问令牌进行比较时,系统首先在文件的 ACL 中查找用户 Jim 的 ACE。 没有显示,因此接下来它会查找会计组的 ACE。 如上表所示,会计组的 ACE 显示为文件 ACL 中的第一个条目,因此 Jim 的进程有权写入或删除该文件,比较将停止。 如果法律组的 ACE 位于 ACL 中会计组的 ACE 之前,则进程将被拒绝对文件的写入、追加和删除访问权限。

示例 2:将 ACL 与受限令牌进行比较

系统将 ACL 与受限令牌进行比较,其方式与在不受限制的令牌中进行比较的方式相同。 但是,受限令牌中的拒绝 SID 只能匹配 ACL 中的拒绝 ACE。

示例 2 演示了系统如何将文件的 ACL 与受限令牌进行比较。 假设该文件的 ACL 与上表中所示的 ACL 相同。 但是,在此示例中,进程具有包含以下 SID 的受限令牌:

用户 Jim (S-1-5-21...) 拒绝

组会计 (S-1-5-22...) 拒绝

Group Legal (S-1-5-23...) Deny

将所有人 (S-1-1-0)

文件的 ACL 未列出 Jim 的 SID,因此系统会继续转到会计组 SID。 尽管文件的 ACL 具有会计组的 ACE,但此 ACE 允许访问;因此,它与拒绝访问的进程受限令牌中的 SID 不匹配。 因此,系统会转到“法律”组 SID。 文件的 ACL 包含拒绝访问的法律组的 ACE,因此进程无法写入、追加或删除该文件。

权限

权限是用户在本地计算机上执行与系统相关的操作的权利,例如加载驱动程序、更改时间或关闭系统。

特权不同于访问权限,因为它们适用于与系统相关的任务和资源,而不是对象,并且由系统管理员而不是操作系统分配给用户或组。

每个进程的访问令牌都包含授予进程的特权列表。 在使用之前,必须专门启用权限。 有关特权的详细信息,请参阅内核驱动程序文档中的 特权

Windows 安全模型方案:创建文件

每当进程创建文件或对象的句柄时,系统都使用 Windows 安全模型中所述的安全构造。

下图显示了在用户模式进程尝试创建文件时触发的安全相关操作。

说明用户模式进程尝试创建文件时与安全相关的操作的流程图。

上图显示了用户模式应用程序调用 CreateFile 函数时系统如何响应。 以下注释引用了图中带圆圈的数字:

  1. 用户模式应用程序调用 CreateFile 函数,传递有效的 Microsoft Win32 文件名。
  2. 用户模式 Kernel32.dll 将请求传递给 Ntdll.dll,这将 Win32 名称转换为 Microsoft Windows NT文件名。
  3. Ntdll.dll 使用 Windows 文件名调用 NtCreateFile 函数。 在 Ntoskrnl.exe 内,I/O 管理器处理 NtCreateFile
  4. I/O 管理器将请求重新打包到对象管理器调用中。
  5. 对象管理器解析符号链接,并确保用户对要在其中创建文件的路径具有遍历权限。 有关详细信息,请参阅 对象管理器中的安全检查
  6. 对象管理器调用拥有与请求关联的基础对象类型的系统组件。 对于文件创建请求,此组件是拥有设备对象的 I/O 管理器。
  7. I/O 管理器根据用户进程的访问令牌检查设备对象的安全描述符,以确保用户对设备具有所需的访问权限。 有关详细信息,请参阅 I/O 管理器中的安全检查
  8. 如果用户进程具有所需的访问权限,则 I/O 管理器会创建一个句柄,并向设备或文件系统的驱动程序发送IRP_MJ_CREATE请求。
  9. 驱动程序会根据需要执行其他安全检查。 例如,如果请求在设备的命名空间中指定对象,则驱动程序必须确保调用方具有所需的访问权限。 有关详细信息,请参阅 驱动程序中的安全检查

对象管理器中的安全检查

检查访问权限的责任属于可执行此类检查的最高级别组件。 如果对象管理器可以验证调用方的访问权限,则会进行验证。 否则,对象管理器会将请求传递给负责基础对象类型的组件。 反过来,该组件会验证访问权限(如果可以);如果不能,它会将请求传递给仍然较低的组件,例如驱动程序。

对象管理器检查 ACL 中是否存在简单的对象类型,例如事件和互斥锁。 对于具有命名空间的对象,类型所有者执行安全检查。 例如,I/O 管理器被视为设备对象和文件对象的类型所有者。 如果对象管理器在分析名称时找到设备对象或文件对象的名称,则会将名称移交给 I/O 管理器,如上面所示的文件创建方案所示。 然后,I/O 管理器会检查访问权限(如果可以)。 如果名称指定了设备命名空间中的对象,则 中的 I/O 管理器会将名称转交给设备 (或文件系统) 驱动程序,并且该驱动程序负责验证请求的访问权限。

I/O 管理器中的安全检查

当 I/O 管理器创建句柄时,它会根据进程访问令牌检查对象的权限,然后将授予用户的权限与句柄一起存储。 当以后的 I/O 请求到达时,I/O 管理器会检查与句柄关联的权限,以确保进程有权执行请求的 I/O 操作。 例如,如果进程稍后请求写入操作,则 I/O 管理器会检查与句柄关联的权限,以确保调用方对对象具有写入访问权限。

如果句柄重复,则可以从副本中删除权限,但不能向副本添加权限。

当 I/O 管理器创建对象时,它会将通用 Win32 访问模式转换为特定于对象的权限。 例如,以下权限适用于文件和目录:

Win32 访问模式 特定于对象的权限
GENERIC_READ ReadData
GENERIC_WRITE WriteData
GENERIC_EXECUTE ReadAttributes
GENERIC_ALL 全部

若要创建文件,进程必须对目标路径中的父目录具有遍历权限。 例如,若要创建 \Device\CDROM0\Directory\File.txt,进程必须有权遍历 \Device、\Device\CDROM0 和 \Device\CDROM0\Directory。 I/O 管理器仅检查这些目录的遍历权限。

I/O 管理器在分析文件名时检查遍历权限。 如果文件名是符号链接,则 I/O 管理器将其解析为完整路径,然后从根目录开始检查遍历权限。 例如,假设符号链接 \DosDevices\D 映射到Windows NT设备名称 \Device\CDROM0。 进程必须具有对 \Device 目录的遍历权限。

有关详细信息,请参阅 对象句柄对象安全性

驱动程序中的安全检查

操作系统内核实际上将每个驱动程序视为具有其自己的命名空间的文件系统。 因此,当调用方尝试在设备命名空间中创建对象时,I/O 管理器会检查进程是否对路径中的目录具有遍历权限。

使用 WDM 驱动程序时,I/O 管理器不会对命名空间执行安全检查,除非已创建指定FILE_DEVICE_SECURE_OPEN的设备对象。 如果未设置FILE_DEVICE_SECURE_OPEN,驱动程序负责确保其命名空间的安全性。 有关详细信息,请参阅控制设备命名空间访问和保护设备对象

对于 WDF 驱动程序,始终设置FILE_DEVICE_SECURE_OPEN标志,以便在允许应用程序访问设备命名空间中的任何名称之前,检查设备的安全描述符。 有关详细信息,请参阅 控制 KMDF 驱动程序中的设备访问

Windows 安全边界

可以认为驱动程序相互通信,并与不同特权级别的用户模式调用方通信,这可以视为跨越信任边界。 信任边界是从较低特权进程交叉到较高特权进程的任何代码执行路径。

特权级别的差距越大,对于想要对目标驱动程序或进程执行权限提升攻击等攻击的攻击者来说,边界就越有趣。

创建威胁模型的过程的一部分是检查安全边界并查找意外的路径。 有关详细信息,请参阅 驱动程序的威胁建模

任何跨信任边界的数据都是不受信任的,必须进行验证。

此图显示了三个内核驱动程序和两个应用,一个在应用容器中,一个应用以管理员权限运行。 红线表示示例信任边界。

图中描绘了具有三个内核驱动程序的驱动程序攻击图面、应用容器中的应用以及具有管理权限的应用。

由于应用容器可以提供其他约束,并且未在管理员级别运行,因此路径 (1) 是升级攻击的风险更高的路径,因为信任边界介于应用容器 ( 非常低的特权进程) 和内核驱动程序之间。

路径 (2) 是风险较低的路径,因为应用以管理员权限运行,并且直接调用内核驱动程序。 管理员在系统上已经是一个相当高的特权,因此从管理员到内核的攻击面对攻击者不是一个有趣的目标,但仍然是一个值得注意的信任边界。

路径 (3) 是代码执行路径的一个示例,该路径跨越多个信任边界,如果未创建威胁模型,可能会错过这些信任边界。 在此示例中,驱动程序 1 和驱动程序 3 之间存在信任边界,因为驱动程序 1 从用户模式应用获取输入并将其直接传递给驱动程序 3。

从用户模式进入驱动程序的所有输入不受信任,应进行验证。 来自其他驱动程序的输入也可能不受信任,具体取决于以前的驱动程序是否只是一个简单的直通 (例如,驱动程序 1 从应用 1 接收数据,驱动程序 1 未对数据执行任何验证,只是将其转发到驱动程序 3) 。 通过创建完整的威胁模型,确保识别所有攻击面和信任边界,并验证跨这些攻击面的所有数据。

Windows 安全中心模型建议

  • 在对 IoCreateDeviceSecure 例程的调用中设置强默认 ACL。
  • 在每个设备的 INF 文件中指定 ACL。 如有必要,这些 ACL 可以松动严格的默认 ACL。
  • 设置FILE_DEVICE_SECURE_OPEN特征以将设备对象安全设置应用于设备命名空间。
  • 请勿定义允许FILE_ANY_ACCESS的 IOCTL,除非此类访问不能被恶意利用。
  • 使用 IoValidateDeviceIoControlAccess 例程加强对允许FILE_ANY_ACCESS的现有 IOCTLS 的安全性。
  • 创建威胁模型以检查安全边界并查找意外路径。 有关详细信息,请参阅 驱动程序的威胁建模
  • 有关其他 驱动程序安全 建议,请参阅驱动程序安全清单。

另请参阅

保护设备对象

驱动程序安全清单