长亭百川云 - 文章详情

主机安全技术剖析 - Windows 本地提权(基础篇)

crispr

82

2022-03-25

作者:crispr

在长亭牧云(CloudWalker)团队,有那么一群研究员,他们孜孜不倦、持续探索,专治攻防场景下各类“疑难杂症”。

2022虎虎生风,长亭科技特开辟「主机安全技术专栏」,分享最实用、最深入的安全研究。

前言

Windows 的提权是在攻防对抗中绕不开的话题,本文针对 Windows 操作系统的本地提权手段原理以及方式进行梳理介绍。提权是后渗透中的一个重要环节,在权限较低的情况下,站在攻击者的视角进行内部网络安全测试、系统安全测试、应用安全测试等方面会受到限制。

考虑到在 Windows 操作系统中提权手段的丰富性和多样性,本文分为两部分,分别是基础概念篇和应用篇。基础概念篇介绍Windows提权相关的前置知识,包括 SID/安全描述符/令牌等;应用篇则从权限等级角度切入,介绍包括从常规用户到管理员用户,从服务账户到 SYSTEM,以及从常规用户到 SYSTEM 三类情形的Win提权姿势,应用篇的阅读没有前后顺序,可根据读者自身所需进行全文阅读或某方向内容的阅读。

图片
目前长亭牧云(CloudWalker)主机安全管理平台已支持通用性的的 windows 提权检测,如果你也对主机安全感兴趣,欢迎投递简历到邮箱:jingyuan.chen@chaitin.com

Windows 提权相关基础概念

安全标识符 SID

安全标识符(Security Identifiers,SID),是标识用户、组和计算机帐户的唯一的号码。每个账户都有一个由权威机构(例如 Windows 域控制器)颁发的唯一 SID,并存储在安全数据库中。每次用户登录时,系统都会从数据库中检索该用户的 SID,并将其放在该用户的访问令牌中。在与 Windows 安全性相关的所有后续交互中,系统使用访问令牌中的 SID 识别用户。当 SID 用作用户或组的唯一标识符时,就不能再使用它来标识另一个用户或组。

MSDN 提供了常见的 SID 列表

  • S-1-5-18 (LocalSystem)
  • S-1-5-19 (LocalService)
  • S-1-5-20 (NetworkService)
  • S-1-5-32-544 (Administrators)
  • S-1-5-32-545 (Users)
  • S-1-5-32-550 (PrintOperators)

SID 简化的结构如下

首先第一位 S 是默认位,第二位 R 代表 SID 的版本号,第三位 A 代表主标识值,后面的若干位 SA 则代表子标识值,其中 SA1 到 SAn-1代表域相关的标识值, SAn 则是 RID(relative identifier),代表相对标识值

针对 S-1-5-21-1004336348-1177238915-682003330-512进行解析,可以知道

  • SID 的版本号是 1
  • 主标识值是 5,代表 NT 权限
  • 域相关的子标识值是 21-1004336348-1177238915-682003330
  • RID 是 512,代表 DomainAdmins

这里我们可以使用 windbg 来查看进程的 Token,Token 中包含了用户 SID 以及所属组SID的信息

访问权限

访问权限是对对象执行特定操作需要的权限。例如, FILE_READ_DATA 这个访问权限代表了读取文件的权限。

访问权限分为两种类型:

  • 特定访问权限:执行单个操作的权限,如调试进程需要 SeDebugPrivilege 的权限,这个权限和需要操作的对象无关,只和操作行为本身有关系。具体可以参考官方文档。
  • 一般访问权限:一种泛型访问权限,和对象的ACL有关,如上文中的FILE_READ_DATA 权限。

在 Windows 系统中,当我们访问某个 Object (对象)的时候,则由 ACL (访问控制表)判断当前账户/账户组所拥有的权限是否允许我们执行相应的操作, ACL 涉及到了多个概念,包括 ACE/ DACL/ SACL 等,这些概念的涵义如下:

  • ACL: Access Control List,用来表示用户(组)权限的列表,包括 DACL和 SACL
  • ACE: Access Control Entry,ACL 中的访问控制实体,一个 ACL 可能包含0到多个 ACE
  • DACL:当一个进程尝试访问一个安全对象的时候,系统检查对象的 DACL 中的ACE
    注:DACL就是ACE链表,一种数据结构,每一个节点存放着访问权限等信息(这种链表查找起来比数组快)来决定是否赋予访问权限。如果对象没有DACL,系统赋予完全的访问权限
  • SACL: System Access Control List,用来记录对安全对象访问的日志

当我们对某个文件进行访问时,系统将做以下判断:

  • 如果没有 DACL,系统将允许访问
  • 如果存在 DACL,但没有 ACE,系统将拒绝所有访问
  • 如果存在 DACL,也存在 ACE,那么会按照每个 ACE 指定允许或拒绝

下面我们同样通过 windbg 来获得内核对象的安全描述符,安全描述符指针放置在内核对象头部的字段中。

在 windbg 中启动内核调试,使用!sd 可以查看安全描述符信息,这里借用一张图

可以看到该 DACL 存在3个 ACE,ACE 的类型为 ACCESS_ALLOWED_ACE_TYPE, Mask 是权限掩码,用来指定对应的权限。

因此图中第一条 ACE 代表的意思是允许 SID 为 S-1-5-32-544的对象能够对该安全对象做 0x001fffff 对应的操作( Mask的权限位根据不同对象类型有不同含义),具体的权限位可以参考下图

本地账户

本地账户存储在本地服务器上。与提权相关的的本地账户包括管理员账户,默认本地系统账户,以及服务账户。

管理员账户(Administrators)

每台计算机都有一个管理员账户,管理员账户是在 Windows 安装过程中创建的第一个账户。

管理员帐户可以完全控制本地计算机上的文件、目录、服务和其他资源。管理员帐户可以创建其他本地用户、分配用户权限和分配权限。

默认管理员帐户无法删除或锁定,但可以重命名或禁用。

在 Windows 10和 Windows Server 2016中,Windows 安装程序禁用内置管理员账户并创建另一个本地账户,该账户是管理员组的成员。

默认本地系统账户

SYSTEM 账户由操作系统和在 Windows下运行的服务使用。Windows 操作系统中有许多服务和进程需要能够在内部登录,例如在 Windows 安装期间。

SYSTEM 账户就是为此目的而设计的。它是一个内部帐户,不会显示在用户管理器中,并且无法添加到任何组中。

默认情况下,SYSTEM 账户被授予对 NTFS 卷上所有文件的完全控制权限。因此 SYSTEM 账户包含管理员账户所具有的功能和权限。

网络服务账户

NETWORK SERVICE 是服务控制管理器( SCM )使用的预定义本地账户, 以这个账户运行的服务,允许把访问凭据提交给远程的计算机。

本地服务账户

LOCAL SERVICE 账户是服务控制管理器使用的预定义本地账户,它在本地计算机上具有最低权限,并在网络上提供匿名凭据。

类似于Sqlserver、IIS等都属于本地服务账户,本地服务账户具有 SeImpersonatePrivilege 这个特殊访问权限,至于为何强调该权限,下一篇会展开叙述。

访问令牌

WindowsToken 又叫 AccessToken (访问令牌),它是一个描述进程或者线程安全上下文的一个对象。不同的用户登录计算机后,都会生成一个 AccessToken,这个 Token 在用户创建进程或者线程时会被使用并且不断地拷贝,这也就解释了 A 用户创建一个进程而该进程也不会有 B 用户的权限。

关于令牌的解释可以在 MSDN 中找到:
https://docs.microsoft.com/zh-cn/windows/win32/secauthz/access-tokens?redirectedfrom=MSDN

访问令牌组成

令牌分为如下两类:

  • Primary Token(主令牌)
  • Impersonation令牌(模拟令牌)
    注:当用户注销后,系统将会使主令牌切换为模拟令牌,而模拟令牌不会被清除,只有在重启机器后才会清除

主令牌是由 windows 内核创建并分配给进程的默认访问令牌,每一个进程有一个主令牌,它描述了与当前进程相关的用户账户的安全上下文。

此外,线程可以模拟客户端帐户。模拟允许线程使用客户端的安全上下文与安全对象进行交互。模拟客户端的线程同时具有主令牌和模拟令牌,笔者理解为线程可以以客户端的身份和服务端交互并且是有模拟服务端的身份的权限

令牌的组成主要有如下部分:

  • TokenID (标识 token 的唯一ID)
  • Privileges (当前 token 的权限,具体可查看官方文档)
  • UserAndGroups (当前用户及所属的组,实际是一个 SID 数组)
  • RestrictedSids (如果该值不为空,则表示令牌属于受限令牌,同样是一个 SID 数组)
  • TokenType (当前令牌的类型)
  • ImpersonationLevel (模拟令牌的等级)

令牌的创建过程如下:

  1. 使用凭据(用户密码)进行认证
  2. 登录 Session 创建
  3. Windows 返回用户 sid 和用户组 sid
  4. LSA(Local Security Authority)创建一个 Token
  5. 依据该 token 创建进程、线程(如果 CreaetProcess 时,自己指定了 Token, LSA会用该 Token, 否则就继承父进程 Token 进行运行)

令牌模拟级别

官方说明如下:
https://docs.microsoft.com/zh-cn/windows/win32/secauthz/impersonation-levels

模拟等级通过如下所示的 SECURITY_IMPERSONATION_LEVEL 枚举表示

1: typedef enum _SECURITY_IMPERSONATION_LEVEL {
2: SecurityAnonymous,
3: SecurityIdentification,
4: SecurityImpersonation,
5: SecurityDelegation
6: } SECURITY_IMPERSONATION_LEVEL, *PSECURITY_IMPERSONATION_LEVEL;
模拟级别 说明
SecurityAnonymous 无法获取有关客户端的标识信息且无法模拟客户端;
SecurityIdentification 可以获取有关客户端的信息(如安全标识符和特权)但是无法模拟客户端
SecurityImpersonation 可以在本地模拟客户端但无法在远程系统上模拟客户端
SecurityDelegation 可以在本地和远程系统上模拟客户端
文档中给出了三个通过用户身份创建进程的函数:
函数 需要特权 输入


CreateProcessWithLogon null 域/用户名/密码
CreateProcessWithToken SeImpersonatePrivilege Primary令牌
CreateProcessAsUser SeAssignPrimaryTokenPrivilege和SeIncreaseQuotaPrivilege Primary令牌
从这三个Win API中我们可以很容易的发现,当拥有SeAssignPrimaryToken或者SeImpersonate权限时,我们可以通过模拟Primary令牌的方式来创建新进程从而提升权限(前提是令牌具有Impersonation和Delegation级别),因此如果想通过模拟令牌的方式来进行提权,一个非常重要的前提就是用户具有SeImpersonate权限

如何获取令牌

Win API 中提供了 OpenProcessToken/openThreadToken 等函数用来打开某个进程或者线程的访问令牌,其函数原型如下:

BOOL 
OpenProcessToken
(


  HANDLE  
ProcessHandle
, 
//访问令牌已打开的进程的句柄


  DWORD   
DesiredAccess
,


  PHANDLE 
TokenHandle
//指向句柄的指针,该句柄在函数返回时标识新打开的访问令牌。


);



BOOL 
OpenThreadToken
(


  HANDLE  
ThreadHandle
, 
//打开访问令牌的线程的句柄。


  DWORD   
DesiredAccess
,


  BOOL    
OpenAsSelf
,


  PHANDLE 
TokenHandle
//指向接收新打开的访问令牌句柄的变量的指针


);

通过 OpenThreadToken/OpenProcessToken 函数来获取访问令牌。

在这里,模拟令牌和主令牌之间是可以通过 DuplicateTokenEx 函数互相转换的

DuplicateTokenEx :我们在最后调用 CreateProcessWithToken/CreateThreadWithToken 时所传递的令牌必须要是主令牌,而我们一般获得的令牌都是模拟令牌,因此中间过程需要由 DuplicateTokenEx 函数来进行转化。

微软官方文档中给出了 DuplicateTokenEx 创建主令牌的典型场景,服务器应用程序创建一个线程,该线程调用其中一个模拟函数(例如 ImpersonateNamedPipeClient)来模拟客户端。模拟线程然后调用 OpenThreadToken 函数来获取自己的令牌,该令牌是模拟令牌。该线程在对 DuplicateTokenEx 的调用中指定此模拟令牌,并指定 TokenPrimary 标志。然后 DuplicateTokenEx 函数创建一个主令牌。

因此,一个模拟令牌的过程大概是:

  1. OpenProcess (获取目标进程上下文),返回的句柄将用于步骤2的参数
  2. OpenProcessToken (获得进程访问令牌的句柄),通过该函数获得对应进程的令牌句柄
  3. DuplicateTokenEx (创建一个主令牌),设置访问令牌模拟级别并复制一个令牌句柄
  4. CreateProcessWithTokenW(创建进程),模拟主令牌的权限创建对应进程

所以当我们拥有 SeImpersonatePrivilege 权限时便可以通过对进程爆破的方式找到满足如下条件的进程:

  • 进程运行用户是 SYSTEM
  • 令牌级别至少是 Impersonation 级别
  • 攻击者运行的权限至少拥有 SeImpersonatePrivilege

这其实就是 Token Kidnapping 技术,相关介绍
https://msrc-blog.microsoft.com/2009/04/14/token-kidnapping/

不过如果不是 Administrator 身份通过这种方式爆破来提权的成功性很低,这里只是提供了一种思路。

结 语

基础概念篇主要介绍了 windows 提权当中涉及到的一些基础概念,目的是为了后面的应用篇抛砖引玉,敬请持续关注。

相关推荐
关注或联系我们
添加百川云公众号,移动管理云安全产品
咨询热线:
4000-327-707
百川公众号
百川公众号
百川云客服
百川云客服

Copyright ©2024 北京长亭科技有限公司
icon
京ICP备 2024055124号-2