作者:crispr
在长亭牧云(CloudWalker)团队,有那么一群研究员,他们孜孜不倦、持续探索,专治攻防场景下各类“疑难杂症”。
2022虎虎生风,长亭科技特开辟「主机安全技术专栏」,分享最实用、最深入的安全研究。
Windows 的提权是在攻防对抗中绕不开的话题,本文针对 Windows 操作系统的本地提权手段原理以及方式进行梳理介绍。提权是后渗透中的一个重要环节,在权限较低的情况下,站在攻击者的视角进行内部网络安全测试、系统安全测试、应用安全测试等方面会受到限制。
考虑到在 Windows 操作系统中提权手段的丰富性和多样性,本文分为两部分,分别是基础概念篇和应用篇。基础概念篇介绍Windows提权相关的前置知识,包括 SID/安全描述符/令牌等;应用篇则从权限等级角度切入,介绍包括从常规用户到管理员用户,从服务账户到 SYSTEM,以及从常规用户到 SYSTEM 三类情形的Win提权姿势,应用篇的阅读没有前后顺序,可根据读者自身所需进行全文阅读或某方向内容的阅读。
图片
目前长亭牧云(CloudWalker)主机安全管理平台已支持通用性的的 windows 提权检测,如果你也对主机安全感兴趣,欢迎投递简历到邮箱:jingyuan.chen@chaitin.com
安全标识符(Security Identifiers,SID),是标识用户、组和计算机帐户的唯一的号码。每个账户都有一个由权威机构(例如 Windows 域控制器)颁发的唯一 SID,并存储在安全数据库中。每次用户登录时,系统都会从数据库中检索该用户的 SID,并将其放在该用户的访问令牌中。在与 Windows 安全性相关的所有后续交互中,系统使用访问令牌中的 SID 识别用户。当 SID 用作用户或组的唯一标识符时,就不能再使用它来标识另一个用户或组。
MSDN 提供了常见的 SID 列表
SID 简化的结构如下
首先第一位 S 是默认位,第二位 R 代表 SID 的版本号,第三位 A 代表主标识值,后面的若干位 SA 则代表子标识值,其中 SA1 到 SAn-1代表域相关的标识值, SAn 则是 RID(relative identifier),代表相对标识值
针对 S-1-5-21-1004336348-1177238915-682003330-512进行解析,可以知道
这里我们可以使用 windbg 来查看进程的 Token,Token 中包含了用户 SID 以及所属组SID的信息
访问权限是对对象执行特定操作需要的权限。例如, FILE_READ_DATA 这个访问权限代表了读取文件的权限。
访问权限分为两种类型:
在 Windows 系统中,当我们访问某个 Object (对象)的时候,则由 ACL (访问控制表)判断当前账户/账户组所拥有的权限是否允许我们执行相应的操作, ACL 涉及到了多个概念,包括 ACE/ DACL/ SACL 等,这些概念的涵义如下:
当我们对某个文件进行访问时,系统将做以下判断:
下面我们同样通过 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
访问令牌组成
令牌分为如下两类:
主令牌是由 windows 内核创建并分配给进程的默认访问令牌,每一个进程有一个主令牌,它描述了与当前进程相关的用户账户的安全上下文。
此外,线程可以模拟客户端帐户。模拟允许线程使用客户端的安全上下文与安全对象进行交互。模拟客户端的线程同时具有主令牌和模拟令牌,笔者理解为线程可以以客户端的身份和服务端交互并且是有模拟服务端的身份的权限
令牌的组成主要有如下部分:
令牌的创建过程如下:
令牌模拟级别
官方说明如下:
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 函数创建一个主令牌。
因此,一个模拟令牌的过程大概是:
所以当我们拥有 SeImpersonatePrivilege 权限时便可以通过对进程爆破的方式找到满足如下条件的进程:
这其实就是 Token Kidnapping 技术,相关介绍
https://msrc-blog.microsoft.com/2009/04/14/token-kidnapping/
不过如果不是 Administrator 身份通过这种方式爆破来提权的成功性很低,这里只是提供了一种思路。
基础概念篇主要介绍了 windows 提权当中涉及到的一些基础概念,目的是为了后面的应用篇抛砖引玉,敬请持续关注。