再聊Windows的访问控制(Access Control)(一)

本文详细阐述了Windows系统的访问控制机制,涉及访问令牌、安全描述符的作用,以及模拟机制、受限令牌和安全ID的属性。通过实例和API函数讲解了如何创建、管理和应用这些安全组件。
摘要由CSDN通过智能技术生成

        有朋友告诉我前面有关Windows访问控制(Access Control)的博文太长,描述也偏生硬,希望我再把Windows的访问控制机制描述得简单明了一些。非常感谢这位朋友的提醒,我现在再用几篇较短的篇幅把我所理解的Windows访问控制机制重新梳理一下,希望能够说得足够明白。

        访问控制说白了就是一个进程(或线程)试图访问一个可保护对象(Securable Object)或执行系统操作时系统对其所进行的一个安全检查。在Windows系统中,可保护对象就是可被赋予安全描述符的对象,即可通过安全描述符对其实施访问控制的对象。所有的可命名的Windows对象都是可保护的,某些不需命名的对象如进程和线程对象也是可保护的,也可以拥有安全描述符。可保护对象几乎涵盖我们所能遇到的所有Windows对象,比如管道(Pipes)、注册表键(Registry Key)、文件(Files)以及我们正在讨论的令牌,都是可保护的。我在文末列出一个表格,里面列出了常见的可保护对象以及适用的操作其安全信息的函数。

        整个访问控制机制涉及到两个基本的组成成分,一个是访问令牌(Access Token),一个是安全描述符(Security Descriptor)。访问令牌是包含登录账户信息的数据结构,安全描述符标识可保护对象的所有者以及访问控制信息,包括一个自由访问控制列表(DACL,Discretionary Access Control List),定义允许和拒绝访问该对象的用户和组;一个系统访问控制列表(SACL,System access control list),规定系统如何审计对该对象的访问动作。访问控制列表是一个由若干访问控制条目(ACE, Access Control Entries)组成的列表,ACE规定对某个受信任实体(Trustee)所允许、拒绝或审计的访问权限。

        在图1中,线程拿着访问令牌去访问可保护对象(比如一个文件),系统会根据可保护对象所拥有的安全描述符对此次访问进行安全检查,如果通过了,就允许该线程访问,通不过就拒绝其访问。访问令牌就像一个身份证,安全描述符就像一份准入客户的名单。

        有了上面的基本逻辑,我们首先要了解访问令牌和安全描述符是怎么来的。

访问令牌是怎么来的

        当客户登录到系统时,系统会对其身份进行验证,比如通过验证用户的密码、指纹等来判断是否允许该客户登录,这就是身份认证过程(Authentication,这也是Windows安全的一个重要组成部分,而且知识面更广,暂不在讨论之列)。如果登录成功,系统就会为该用户创建一个访问令牌,其中描述了该用户的安全环境,主要包含以下信息:

  • 用户账户的安全ID(SID, Security Identifier)
  • 用户加入的所有的组的SID
  • 当前登录会话的SID(Logon SID)
  • 当前用户及其所属组的特权(Privileges)列表
  • 一个默认的访问控制列表(DACL)。如果用户在创建新的可保护对象时没有明确指定所应用的安全描述符,系统会为新对象创建一个默认的安全描述符,其中将用户的SID作为对象的所有者SID,并且从用户的令牌中取出这个DACL放入新对象的安全描述符中。
  • 该令牌的来源
  • 该令牌是主令牌(Primary token)还是模拟令牌(Impersonation token)
  • 受限的SID列表(可选)
  • 当前的模拟级别(Impersonation Level)
  • 其他信息

        每一个在该用户环境运行的进程都会有一个该令牌的副本(图1中的①),被称作主令牌(Primary Token)。当进程试图访问受保护对象时,系统就用该主令牌结合被访问对象的安全描述符进行安全验证。

安全描述符是怎么来的

        当一个可保护对象被创建的时候,系统会自动赋予它一个安全描述符。这个安全描述符可能是由创建者在调用创建函数的时候明确指定的。如果创建者没有明确指定,系统就会提供给这个可保护对象一个默认的安全描述符,其中将当前用户的SID作为对象的所有者SID,并取当前用户访问令牌中的默认DACL作为安全描述符的DACL。

访问令牌的一点扩展知识

        1.模拟机制

        在Windows系统中存在一种模拟(Impersonation)机制(其实我更愿意把它译作“扮演”),一个线程可以用另外一个用户的身份运行,即运行在另外一个它所扮演的用户的安全环境,我们熟知的RunAs命令就是这种机制的应用。扮演其他用户的线程有两个访问令牌,除了主令牌外,还多了一个模拟令牌(Impersonation Token),线程访问可保护对象或执行系统操作时出示的是模拟令牌,这时候图1就变成下面的样子了。

        顺便提一下,模拟机制并不仅是使用RunAs命令那样简单,它主要用于服务端/客户端形式的访问控制,服务端掌握着资源,服务端通过创建扮演客户端的线程来代替客户端访问资源,以此利用系统的访问控制机制来决断客户端是否有权访问资源。

        2.SID的属性

         在令牌中包含的SID是带有属性信息的。令牌中每个用户和组的SID都有一组属性用来控制这些SID在安全检查过程中的用法。这组属性包含以下三个:

SE_GROUP_ENABLED带有该属性的SID可参与安全检查过程。在系统i进行安全检查时,会在被访问对象的访问控制列表中选取可用于该SID的ACE用于安全检查操作。
SE_GROUP_USE_FOR_DENY_ONLY

又称作deny-only属性。具有该属性的SID在安全检查中只能做拒绝处理,系统只会挑选被访问对象访问控制列表中对该SID采取拒绝操作的ACE参与安全检查,即使存在允许某权限的ACE也会被忽略。

比如某对象的所有者允许某用户组group1的成员对其进行读取,但拒绝写入。当该组成员的令牌中组SID被设置deny-only属性后,本来允许的读操作也被禁止了。

一旦该属性被设置,已有的SE_GROUP_ENABLED属性就会被取消,而且也不能再设置了。

SE_GROUP_MANDATORY如果某组的SID具有该属性,就不能取消其SE_GROUP_ENABLED属性。

        3.受限令牌(Restricted Token)

        CreateRestrictedToken()函数可以在已有令牌的基础上创建一个功能受限的令牌,用该函数生成的令牌就是受限令牌。受限令牌可以在主令牌的基础上创建,也可以在模拟令牌的基础上创建。受限令牌可能在以下几方面被限制:

  • 可能被减少特权(privileges)
  • 向令牌中的组SID加入deny-only属性,使令牌中的这些SID不能用于访问可保护对象
  • 向令牌加入受限SID列表(见前面令牌结构)

        令牌中的受限SID列表在安全检查(图1的③)过程中起着重要作用,当一个受限的线程试图访问一个可保护对象时,系统会执行两种检查,一种是用令牌中启用的SID(即有SE_GROUP_ENABLED属性的SID),另一种用受限SID列表中的SID。只有在两种检查均未阻止访问的情况下才能获得访问可保护对象的许可。

受限令牌机制的一个重要应用场景

        当一个标准用户账户试图创建以另一个用户身份运行的子进程时受限令牌机制是非常有用的。要创建以另一个用户身份运行的子进程要调用CreateProcessAsUser()函数,但要使用该函数需要其调用者拥有SE_ASSIGNPRIMARYTOKEN_NAME特权,而该特权通常是系统代码或以LocalSystem账户运行的服务才有的。这时候受限令牌机制对于普通权限的程序来说就派上用场了,因为如果用CreateRestrictedToken()从主令牌派生一个受限令牌,将它用于CreateProcessAsUser()函数,此时就不再需要SE_ASSIGNPRIMARYTOKEN_NAME特权了,也就是说普通权限程序可以利用受限令牌机制创建模拟其他用户身份的进程。

        需要留意的一点是使用受限令牌运行的程序会不会通过向非受限进程发消息来获取高一些的特权,这是一个可能的安全漏洞。如果将受限应用程序运行在不同的桌面环境可避免这一点,只是需要在不同桌面间来回切换。

用于令牌操作的API函数

        以下是用于令牌操作的函数。强烈建议用系统提供的函数操作令牌或安全描述符等系统结构,不要编程直接操作,因为一旦系统更新导致数据结构细节发生改变,这些操作代码就会出错了。

函数 简介
AdjustTokenGroups

更改令牌中的组信息。该函数可用于设置或清除令牌中特定组SID的SE_GROUP_ENABLED属性,但如果SID带有SE_GROUP_USE_FOR_DENY_ONLY属性就不能再设置SE_GROUP_ENABLED属性了。

不可用于屏蔽具有SE_GROUP_MANDATORY属性的组SID,也不能用于屏蔽用户SID。

AdjustTokenPrivileges启用或关闭令牌中的特权。它不会创造新的特权,也不能作废现有的特权。
CheckTokenMembership检查指定的SID在令牌中是否有效
CreateRestrictedToken

创建现有令牌的受限版本。受限的令牌可能被禁用了部分SID、被删除了部分特权或被加入了受限SID列表。

如果要为某SID设置SE_GROUP_USE_FOR_DENY_ONLY属性,可在调用CreateRestrictedToken()时将该SID放入deny-only SID列表。

该函数可将SE_GROUP_USE_FOR_DENY_ONLY属性应用到任何SID,既可以是用户SID,也可以是组SID,哪怕是带有SE_GROUP_MANDATORY属性。但它不能删除SID的该SE_GROUP_USE_FOR_DENY_ONLY属性。

DuplicateToken从现有令牌创建一个新的模拟令牌
DuplicateTokenEx从现有令牌创建一个新的主令牌或模拟令牌
GetTokenInformation获取令牌信息,包括SID的属性信息。该函数返回一个SID_AND_ATTRIBUTTES数组,其中包含各组SID及其属性。
IsTokenRestricted检查令牌中是否包含受限SID列表
OpenProcessToken获取进程的主令牌句柄
OpenThreadToken获取线程的模拟令牌句柄
SetThreadToken赋予或删除线程的模拟令牌
SetTokenInformation设置令牌信息,包括令牌的所有者,主组,默认DACL等。
小结

        安全检查过程(图1中的③)是整个访问控制机制的核心内容,要在介绍完所有组成部分后再总结。但现在已经有一部分被揭示了,就是令牌中SID的属性在安全检查中的作用。

        访问令牌本身也是可保护对象,它也有自己的安全描述符,在介绍完安全描述符后再返回来讨论访问令牌的安全保护话题。

        在下篇将介绍安全描述符的细节。


附:
常见的可保护对象及适用的安全信息操作函数
对象类型安全描述符操作函数
NTFS文件系统中的文件和目录GetNamedSecurityInfo, SetNamedSecurityInfo, GetSecurityInfo, SetSecurityInfo
命名管道(Named pipes),匿名管道(Anonymous pipes)GetSecurityInfo, SetSecurityInfo
进程(Processes)、线程(Threads)GetSecurityInfo, SetSecurityInfo
文件映射对象(File-mapping objects)GetNamedSecurityInfo, SetNamedSecurityInfo,GetSecurityInfo, SetSecurityInfo
 
访问令牌(Access Tokens)SetKernelObjectSecurity, GetKernelObjectSecurity
窗口管理对象 (窗口站、桌面)GetSecurityInfo, SetSecurityInfo
注册表键(Registry Keys)GetNamedSecurityInfo, SetNamedSecurityInfo,GetSecurityInfo, SetSecurityInfo
Windows 服务(Windows Services)GetNamedSecurityInfo, SetNamedSecurityInfo,GetSecurityInfo, SetSecurityInfo
本地或远程打印机GetNamedSecurityInfo, SetNamedSecurityInfo,GetSecurityInfo, SetSecurityInfo

网络共享

(Network shares)

GetNamedSecurityInfo, SetNamedSecurityInfo,GetSecurityInfo, SetSecurityInfo
进程间同步对象(事件、互斥对象、信号灯、可等待定时器)(Events,Mutexes,Semophores,Waitable Timers)GetNamedSecurityInfo, SetNamedSecurityInfo,GetSecurityInfo, SetSecurityInfo
任务对象(Task Objects)GetNamedSecurityInfo, SetNamedSecurityInfo,GetSecurityInfo, SetSecurityInfo
目录服务对象(Directory Service Objects)由活动目录处理

  • 17
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值