WinDBG如何滥用令牌来提升内核权限
导语:本文的目的是在更高的层次上,让你学会不用编写任何内核代码,而是使用WinDBG滥用令牌来提升内核权限。
本文的目的是在更高的层次上,让你学会不用编写任何内核代码,而是使用WinDBG滥用令牌来提升内核权限。其中,会涉及两个技巧:
1. 令牌窃取或替换:将低权限令牌替换为高权限令牌;
2. 令牌权限调整:向现有令牌添加并启用更多权限。
关键结构
在继续之前,我们需要了解一些内核内存结构。
_EPROCESS
_EPROCESS是一种内核内存结构,用于描述系统进程,换句话说,每个在系统上运行的进程在内核中的某个位置都有其对应的_EPROCESS对象,因为我们知道它们包含诸如进程映像名称,访问令牌等信息。
以下是该结构的部分片段:
dt _eprocess
_TOKEN
_TOKEN是一个内核内存结构,它描述了进程的安全上下文,并包含诸如进程令牌权限、登录ID、会话ID、令牌类型等信息。
以下是_TOKEN结构的代码段:
dt _token
现在,让我们看看如何使用上述有关进程和令牌的信息,以内核漏洞利用的方式将一个中等完整性进程提升为一个系统完整性进程。
替换令牌并进行权限升级
内核利用升级权限的一种方法是用高权限令牌替换低权限令牌,以下是解释开发进程的一些关键点:
1.1 系统上运行的每个进程都有其对应的_EPROCESS内核结构;
1.2 _EPROCESS结构包含指向_TOKEN内存结构的指针,该指针描述了进程的安全上下文;
1.3 内核漏洞查找一个低权限进程的_TOKEN结构的地址,它希望从这个进程升级;
1.4 内核利用查找作为NT\SYSTEM运行的权限进程的_TOKEN结构的地址;
1.5 内核漏洞利用高权限令牌替换低权限进程的令牌。
在本文的测试中,除了使用WinDBG手动完成之外,我将用系统进程的高权限令牌(总是PID 4)替换低权限powershell进程的身份验证令牌。注意:我运行的是Windows 10 x64 1903。
下面尝试用一个结构图来直观地表示上面描述的进程:
令牌窃取或交换进程
带有蓝色标题的框表示一个MEDIUM完整的进程,以WS02\spotless方式运行:
1. WS02是我的运行设备名称;
2. spotless是低权限的本地用户。
带有红色标题的方框表示系统完整性进程,有效地作为NT\SYSTEM运行
1. WS02 $是我运行的计算机帐户;
2. OFFENSE是计算机所属的域;
3. 红色虚线表示,一旦操作了_TOKEN内核内存结构,低权限进程powershell将从进程系统中获得高权限令牌。
现在,让我们尝试看看如何使用WinDBG替换进程令牌。
替换进程
首先,列出WinDBG系统中所有正在运行的进程,如下所示:
!process 0 0
下面是一些在系统上运行的进程的片段,其中突出显示的是指向给定进程的_EPROCESS结构的地址:
Medium完整性流程
接下来,让我们启动powershell(将低权限令牌替换为高权限令牌的进程),作为Medium完整性/非提升进程(在本文示例中,以本地非管理员用户ws02\spotless运行)并获取其进程ID:
在WinDBG中获得PID为2648 (0xa58)的powershell进程的过程:
!process a58 0
下面确认我们就要查看powershell.exe进程,请注意_EPROCESS位置ffffdc8fbe1f1080:
查找Powershell令牌
一旦我们在内核中拥有powershell的_EPROCESS位置,就可以如下检查其内容:
kd> dt _eprocess ffffdc8fbe1f1080
由于我们对交换令牌感兴趣,因此我们需要的_EPROCESS内存结构的关键项是位于偏移量0x358的令牌:
请注意,offset0x358表明它是指向_EX_FAST_REF内存结构的指针,稍后我们将讨论这个问题。
让我们读取_EPROCESS.Token所指向的指针的内存内容,令牌指向的是ffffc507'dab7799f:
kd> dq ffffdc8fbe1f1080+0x358 l1 ffffdc8f`be1f13d8 ffffc507`dab7799f
如果我们尝试使用!token ffffc507dab7799f命令检查内存位置ffffc507'dab7799f,我们被告知这个地址不指向token对象,我们可能会发现有点奇怪:
然而,这就是_EX_FAST_REF发挥作用的地方,前面已经指出_EPROCESS.Token实际上指向_EX_FAST_REF结构而不是_TOKEN结构。
让我们用_EX_FAST_REF结构覆盖_EPROCESS.Token中存储的地址,该地址是ffffdc8f`be1f13d8(_EPROCESS位置加上令牌项偏移量(ffffdc8fbe1f1080+0x358)),然后查看其中的内容:
kd> dt _EX_FAST_REF ffffdc8fbe1f1080+0x358 ntdll!_EX_FAST_REF +0x000 Object : 0xffffc507`dab7799f Void +0x000 RefCnt : 0y1111 +0x000 Value : 0xffffc507`dab7799f
请注意,所有三个项的偏移量都相同,并且Object和Value指向相同的地址,但是有趣的是RefCnt具有4位(等于0xF,这似乎是Object和Value项的最后一位)指向-0xffffc507`dab7799f)。
如果我们根据符号检查无数据的_EX_FAST_REF,则其定义如下:
ntdll!_EX_FAST_REF +0x000 Object : Ptr64 Void +0x000 RefCnt : Pos 0, 4 Bits +0x000 Value : Uint8B
它指示并确认项Object和Value(在本文的示例中为0xffffc507`dab7799f)所指向的值的后4位(Object和Value的最后十六进制数字)用于表示此令牌的引用计数,表示它不是令牌地址的一部分,这意味着我们应该能够将其归零并为我们的Powershell进程获取实际的_TOKEN结构地址。
实际上,如果Object和Value为0xffffc507`dab7799f,那么我们应该能够将最后一个f与0进行交换,得到0xffffc507'dab77990,它应该是我们的_TOKEN地址。
实际上,如果我们使用更详细的输出来检查我们的powershell进程,如下所示:
!process ffffdc8fbe1f1080 1 // or !process 0xa58 1
可以看到,实际上这个令牌指向的是0xffffc507'dab77990,注意最后一位是0,而不是f,这证实了我们总是可以将_EX_FAST_REF指向的最后一位归零,以获得有效的_TOKEN结构地址:
我们可以使用按位与运算来屏蔽最后一位数字,如下所示:
kd> ? (ffffc507dab7799f & 0xFFFFFFF0); !token (ffffc507dab7799f & 0xFFFFFFF0)
0xf被归零
现在,如果我们再次使用!token命令并将_EPROCESS.Token-> Value的最后一位设置为0,我们将不再看到错误消息,提示该地址上没有令牌,并且我们开始看到一些实际的令牌详细信息,例如用户组 它属于等信息:
确认SID
我们可以再次检查我们实际上看到的是正确的令牌——SID,SID出现在whoami /all的输出并和!token (ffffc507dab7799f & 0xFFFFFFF0)匹配:
查找系统令牌
现在,让我们找到高权限_TOKEN的地址,权限较低的Powershell进程将采用的令牌。
下面显示了一些关于系统进程的信息,我们感兴趣的是它的_TOKEN位置,即ffffc507d8818040,如下所示:
kd> !process 4 1 Searching for Process with Cid == 4 PROCESS ffffdc8fbdad3040 SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000 DirBase: 001aa002 ObjectTable: ffffc507d88032c0 HandleCount: 3042. Image: System VadRoot ffffdc8fbdad1170 Vads 8 Clone 0 Private 21. Modified 76433. Locked 0. DeviceMap ffffc507d8818eb0 Token ffffc507d8818040
交换令牌
现在,我们只需简单地将系统进程的令牌地址写入到我们的PowerShell进程的_EPROCESS.Token中,就可以成功地将Powershell进程令牌(位于ffffdc8fbe1f1080 + 0x358)与系统进程(ffffc507d8818040)持有的令牌交换所需的所有信息:
eq ffffdc8fbe1f1080+0x358 ffffc507d8818040
点此动图,就可以显示上面的操作以及在操作令牌之前,PowerShell是如何以ws02 \ spotless和nt Authority \ system身份运行的。
修改令牌权限
_TOKEN结构的另一个有趣的项(并且滥用为权限升级)是偏移量为0x040的权限,定义为_SEP_TOKEN_PRIVILEGES结构:
dt _token 0xffffc507dab77990
启用现有权限
我们可以覆盖低权限的Powershell令牌地址+偏移量0x40来检查_sep_token_privileges结构:
dt _sep_token_privileges 0xffffc507dab77990+0x40
实际上,_sep_token_privileges显示令牌具有哪些权限以及启用或禁用了哪些权限,我们还可以使用whoami / priv从用户域中查看该信息。
请注意_sep_token_privileges的“当前”和“启用”值是如何不匹配的,这就是导致在whoami / priv 状态栏中看到的“启用或禁用”权限的原因:
我们可以操作内核内存,并使目前和启用的值匹配,如下所示:
eq 0xffffc507dab77990+0x40+8 0x00000006`02880000
在操作内存并匹配当前值和启用值之后,我们现在可以看到如何启用whoami /priv输出状态列中的所有权限:
添加更多的权限
让我们看看是否可以尝试向现有令牌添加更多权限,而不仅仅是启用已经存在的权限。
我们如何知道“当前”字段中的有效值是什么,它将赋予我们更多或更高的权限有什么?通过检查SYSTEM进程(PID 4)令牌的现值,我们可以得到很好的提示:
!process 4 1 sep_token_privileges 0x40+ffffde8fe9a06040
让我们看看是否可以将这个值分配给powershell进程的令牌,并将其分配给Present和Enabled字段。如果成功,我们应该启用所有系统权限,让我们的低权限powershell进程在用户ws02\spotless上下文中运行:
kd> eq 0x40+ffffde8ff8cde5f0+8 0x0000001f`f2ffffbc kd> eq 0x40+ffffde8ff8cde5f0 0x0000001f`f2ffffbc
让我们检查是否将新值分配给了_sep_token_privileges结构:
kd> dt _sep_token_privileges 0x40+ffffde8ff8cde5f0 nt!_SEP_TOKEN_PRIVILEGES +0x000 Present : 0x0000001f`f2ffffbc +0x008 Enabled : 0x0000001f`f2ffffbc +0x010 EnabledByDefault : 0x40800000
现在运行whoami /priv,显示我们拥有所有系统权限,并且所有权限均已启用:
发表评论