标题 简介 类型 公开时间
关联规则 关联知识 关联工具 关联文档 关联抓包
参考1(官网)
参考2
参考3
详情
[SAFE-ID: JIWO-2024-3222]   作者: 大猪 发表于: [2022-11-27]

本文共 [205] 位读者顶过

1 概述

初见是惊鸿一瞥,重逢是始料未及。 [出自:jiwo.org]

各位“蓝颜知己”我们又见面了,本文是《漏洞分析视角下的CVE-2020-0796漏洞》的兄弟篇。CVE-2020-796漏洞成因已经在前文说明,这次我们从漏洞利用视角来看看CVE-2020-796漏洞。

这次我们分析对象是github.com/danigargu上的本地权限提升(LPE,Local Privilege Escalation)利用(Exploit)。

LPE的exploit是以C++源码形式提供的,这也有便于我们分析exploit开发原理。我们可以用Microsoft Visual Studio编译它。然后以非管理员身份运行cmd程序,执行编译后的文件cve-2020-07960local.exe。如图1所示:

图 1

在exploit执行前后的whoami命令显示用户名并未改变。

图 2

如图2所示,而是新弹出一个cmd的界面,在其中执行whoami显示用户名已经被修改为nt authority\system

幸亏本文标题不是《漏洞复现视角下的CVE-2020-0796漏洞》,不然本文就到此为止了。由此可见选一个好标题的重要性,来让我们看看,这一闪而显的黑窗口背后发生了什么。

2 一串神奇的数字

使用之前分析工作中的断点,查看

bp srv2!Srv2DecompressData+0x108 ".printf \"srv2!memmove(Src=0x%I64x, Dst=0x%I64x,Size=%d) \n\", rdx, rcx, r8d;db rdx;.echo"
srv2!memmove(Src=0xffffc68fe8ee2840, Dst=0xffff8d8dec51e0a0,Size=16)

kd> r
rax=ffffc68fe8ee2540 rbx=ffffc68fea74c150 rcx=ffff8d8dec51e0a0
rdx=ffffc68fe8ee2840 rsi=0000000000000010 rdi=ffffc68fe8ee22c0
rip=fffff80fae8a7f68 rsp=fffff2013002ee70 rbp=0000000000000002
 r8=0000000000000010 r9=ffffc68fea9012a0 r10=ffffc68fe7e02160
r11=fffff2013002ee50 r12=0000000000000000 r13=ffffc68fec5ff240
r14=00000000ffffffff r15=0000000000000000
iopl=0     nv up ei ng nz na pe nc
cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b       efl=00000282
srv2!Srv2DecompressData+0x108:
fffff80f`ae8a7f68 e85376ffff   call  srv2!memcpy (fffff80f`ae89f5c0)

目的地址内存:

kd> db 0xffff8d8dec51e0a0 L0n16
ffff8d8d`ec51e0a0 00 00 88 02 06 00 00 00-00 00 80 00 00 00 00 00 ................

源地址内存:

kd> db 0xffffc68fe8ee2840 L0n16
ffffc68f`e8ee2840 bc ff ff f2 1f 00 00 00-bc ff ff f2 1f 00 00 00 ................

这个Exploit要从一个特定的地址读16字节的数据,然后再写入另一个特定的地方去。

根据前文的分析,此处memmove的函数,是要把数据流头部未压缩的数据,移动过来,完成预定的解压缩业务流程。

图 3

如图3所示,从我们抓到的流量数据来看,offset大小与memmove的size,以及数据和数据的确能匹配上。

我们要搞明白这个来自网络流中,现在是memmove的源地址存放的数据,对这个特定目的地址来说有什么特别的意义。

一番眼花缭乱的操作之后,“我不再是我”。我们需要额外了解一些关于Windows内核提权的小技巧。

这是一个在虚拟世界中如何向Windows“证明你自己是你自己”的问题。和现实世界中“大内侍卫凌凌漆的腰牌”有些类似。

3 进程的访问令牌

微软文档中关于访问令牌(Access Token,原文详见引用2)的描述如下:

访问令牌是一个描述进程或线程的安全上下文的对象。令牌中的信息包括与进程或线程关联的用户帐号的标识和特权。当用户登录时,系统通过将用户密码与安全数据库中存储的信息进行比较来验证用户密码。如果密码通过了验证,则系统将生成一个访问令牌。 以该用户名义执行的每个进程都有此访问令牌的副本。

当线程与安全对象进行交互或尝试执行需要特权的系统任务时,系统使用访问令牌来标识用户。 访问令牌包含以下信息:

· 用户帐号的安全标识符(SID)

· 用户所属的组的SID

· 标识当前登录会话的登录SID

· 用户或用户组拥有的特权列表

· 所有者SID

· 主要组(primary group)的SID

· 用户创建安全对象而没有指定安全描述符时,系统使用的默认DACL

· 访问令牌的来源

· 令牌是令牌(primary token)还是模拟令牌(impersonation token)

· 限制SIDrestricting SIDs)的可选列表

· 当前的模拟级别

· 其他统计信息

每个进程都有一个主要令牌,用于描述与该进程关联的用户帐号的安全上下文。 默认情况下,当进程的线程与安全对象进行交互时,系统将使用主令牌。 此外,线程可以模拟当事人帐号(client account)。 模拟允许线程使用当事人的安全上下文与安全对象进行交互。 模拟当事人的线程同时具有主令牌和模拟令牌。

4 ATT&CK中的访问令牌操纵攻击技术

MITRE ATT&CK®是一个基于真实世界的观察对手的战术和技术的可全球访问的知识库。ATT&CK知识库被用作在私营部门、政府以及网络安全产品和服务社区中开发特定威胁模型和方法的基础。

在权限提升(Privilege Escalation)一节中的访问令牌操纵(Access Token Manipulation)详细介绍了在Windows系统中通过如何操纵访问令牌来提升权限的技巧。

Windows使用访问令牌来确定正在运行的进程的所有权。用户可以操纵访问令牌以使正在运行的进程看起来像它属于启动该进程的用户以外的其他人。发生这种情况时,该进程还将采用与新令牌关联的安全上下文。例如,Microsoft提倡使用访问令牌作为最佳安全实践。管理员应以标准用户身份登录,通过内置的访问令牌操作命令runas,以管理员特权运行其工具。

攻击者可以使用访问令牌在不同的用户或系统安全上下文下进行操作,并逃避检测。对手可以使用内置的Windows API函数来复制现有进程中的访问令牌;这被称为令牌窃取。对手必须已经在特权用户上下文(即管理员)中才能窃取令牌。但是,攻击者通常使用令牌窃取来将其安全上下文从管理员级别提升到SYSTEM级别。如果帐户在远程系统上具有适当的权限,则对手可以使用令牌作为该令牌的帐户向远程系统进行身份验证。

5 手动修改访问令牌

我们了解到Windows识别进程的身份靠的就是这个令牌。在Windows中应用态的内存可被进程任意读写,为了更好地安全性,Windows把令牌(token)的放进了内核态。

先看其中一个手动修改方法:

WinDBG中的!process扩展命令会显示指定进程或者全部进程的EPROCESS块信息。

kd> !process 0 0 System
PROCESS ffff980dd646a380
  SessionId: none Cid: 0004  Peb: 00000000 ParentCid: 0000
  DirBase: 001ad002 ObjectTable: ffffd5872c604b80 HandleCount: 2203.
  Image: System

其中的PROCESS ffff980dd646a380即表示System进程的EPROCESS的内存地址是0x ffff980dd646a380。

可使用dt _EPROCESS指令查看0x ffff980dd646a380地址处的EPROCESS块信息:

kd> dt _EPROCESS ffff980dd646a380
nt!_EPROCESS
  +0x000 Pcb       : _KPROCESS
  +0x2e0 ProcessLock   : _EX_PUSH_LOCK
  +0x2e8 UniqueProcessId : 0x00000000`00000004 Void
  +0x2f0 ActiveProcessLinks : _LIST_ENTRY [ 0xffff980d`d64d2370 - 0xfffff804`6ece1bc0 ]
  +0x300 RundownProtect  : _EX_RUNDOWN_REF
..
  +0x360 Token      : _EX_FAST_REF
..

EPROCESS是较大的结构体,我们省略了部分,重点关注0x360偏移处的这个Token结构。该结构是一个_EX_FAST_REF类型。

kd> dt _EX_FAST_REF
nt!_EX_FAST_REF
  +0x000 Object      : Ptr64 Void
  +0x000 RefCnt      : Pos 0, 4 Bits
  +0x000 Value      : Uint8B

观察_EX_FAST_REF字段定义可知,0~4bit是引用计数,其余bit位才是value。

kd> dq ffff980dd646a380+0x360 L1
ffff980d`d646a6e0 ffffd587`2c60604f

一般会通过位与法获得最终的值

kd> ? ffffd587`2c60604f & ffffffff`fffffff0
Evaluate expression: -46698434895808 = ffffd587`2c606040

可使用!token扩展指令查看令牌的具体结构

kd> !token ffffd587`2c606040
_TOKEN 0xffffd5872c606040
TS Session ID: 0
User: S-1-5-18
User Groups: 
 00 S-1-5-32-544  Attributes - Default Enabled Owner 
 01 S-1-1-0  Attributes - Mandatory Default Enabled 
 02 S-1-5-11  Attributes - Mandatory Default Enabled 
 03 S-1-16-16384  Attributes - GroupIntegrity GroupIntegrityEnabled 

Primary Group: S-1-5-18

Privs: 
 02 0x000000002 SeCreateTokenPrivilege      Attributes - 
 03 0x000000003 SeAssignPrimaryTokenPrivilege   Attributes - 
 04 0x000000004 SeLockMemoryPrivilege       Attributes - Enabled Default 
 05 0x000000005 SeIncreaseQuotaPrivilege     Attributes - 
 07 0x000000007 SeTcbPrivilege          Attributes - Enabled Default 
 08 0x000000008 SeSecurityPrivilege        Attributes - 
 09 0x000000009 SeTakeOwnershipPrivilege     Attributes - 
 10 0x00000000a SeLoadDriverPrivilege       Attributes - 
 11 0x00000000b SeSystemProfilePrivilege     Attributes - Enabled Default 
 12 0x00000000c SeSystemtimePrivilege       Attributes - 
 13 0x00000000d SeProfileSingleProcessPrivilege  Attributes - Enabled Default 
 14 0x00000000e SeIncreaseBasePriorityPrivilege  Attributes - Enabled Default 
 15 0x00000000f SeCreatePagefilePrivilege     Attributes - Enabled Default 
 16 0x000000010 SeCreatePermanentPrivilege    Attributes - Enabled Default 
 17 0x000000011 SeBackupPrivilege         Attributes - 
 18 0x000000012 SeRestorePrivilege        Attributes - 
 19 0x000000013 SeShutdownPrivilege        Attributes - 
 20 0x000000014 SeDebugPrivilege         Attributes - Enabled Default 
 21 0x000000015 SeAuditPrivilege         Attributes - Enabled Default 
 22 0x000000016 SeSystemEnvironmentPrivilege   Attributes - 
 23 0x000000017 SeChangeNotifyPrivilege      Attributes - Enabled Default 
 25 0x000000019 SeUndockPrivilege         Attributes - 
 28 0x00000001c SeManageVolumePrivilege      Attributes - 
 29 0x00000001d SeImpersonatePrivilege      Attributes - Enabled Default 
 30 0x00000001e SeCreateGlobalPrivilege      Attributes - Enabled Default 
 31 0x00000001f SeTrustedCredManAccessPrivilege  Attributes - 
 32 0x000000020 SeRelabelPrivilege        Attributes - 
 33 0x000000021 SeIncreaseWorkingSetPrivilege   Attributes - Enabled Default 
 34 0x000000022 SeTimeZonePrivilege        Attributes - Enabled Default 
 35 0x000000023 SeCreateSymbolicLinkPrivilege   Attributes - Enabled Default 
 36 0x000000024 SeDelegateSessionUserImpersonatePrivilege Attributes - Enabled Default 

Authentication ID:     (0,3e7)
Impersonation Level:    Anonymous
TokenType:         Primary
Source: *SYSTEM*      TokenFlags: 0x2000 ( Token in use )
Token ID: 3eb       ParentToken ID: 0
Modified ID:        (0, 3ec)
RestrictedSidCount: 0   RestrictedSids: 0x0000000000000000
OriginatingLogonSession: 0
PackageSid: (null)
CapabilityCount: 0   Capabilities: 0x0000000000000000
LowboxNumberEntry: 0x0000000000000000
Security Attributes:
Invalid AUTHZBASEP_SECURITY_ATTRIBUTES_INFORMATION with no claims
Process Token TrustLevelSid: S-1-19-1024-8192

也可使用dt _TOKEN查看字段定义。

kd> dt _TOKEN
nt!_TOKEN
  +0x000 TokenSource   : _TOKEN_SOURCE
  +0x010 TokenId     : _LUID
  +0x018 AuthenticationId : _LUID
  +0x020 ParentTokenId  : _LUID
  +0x028 ExpirationTime  : _LARGE_INTEGER
  +0x030 TokenLock    : Ptr64 _ERESOURCE
  +0x038 ModifiedId    : _LUID
  +0x040 Privileges    : _SEP_TOKEN_PRIVILEGES
  +0x058 AuditPolicy   : _SEP_AUDIT_POLICY
  ..

在尝试把替换cmd.exe进程的TOKEN指针之前,我们先执行一下whoami看看。

图 4

如图4中,whoami显示,当前账户是admin。

手动查看cmd.exe进程的TOKEN指针和前文所述基本一致,不再赘述。

kd> !process 0 0 cmd.exe
PROCESS ffff980ddcba8080
  SessionId: 1 Cid: 0ae8  Peb: 1265dcd000 ParentCid: 0d38
  DirBase: 530d6002 ObjectTable: ffffd58733943400 HandleCount: 75.
  Image: cmd.exe

然后把cmd.exe进程的TOKEN指针替换为system的TOKEN指针。

kd> eq ffff980ddcba8080+0x360 ffffd587`2c606040
图 5

如图5中,再执行一下whoami显示,当前账户已经由admin变化为nt authority\system。

6 TOKEN+0x40偏移

我们现在来看看memmove()函数的目的地址是否就是cve-2020-0796-local.exe进程的TOKEN指针。

srv2!memmove(Src=0xffff8205e4678590, Dst=0xffff998e13d780a0,Size=16)

kd> r
rax=ffff8205e4678290 rbx=ffff8205e9ad4150 rcx=ffff998e13d780a0
rdx=ffff8205e4678590 rsi=0000000000000010 rdi=ffff8205e4678010
rip=fffff80218327f68 rsp=ffffe8861b123e70 rbp=0000000000000002
 r8=0000000000000010 r9=0000000000000000 r10=ffff8205df002290
r11=ffffe8861b123e50 r12=0000000000000000 r13=ffff8205e8434680
r14=00000000ffffffff r15=0000000000000000
iopl=0     nv up ei ng nz na po nc
cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b       efl=00000286
srv2!Srv2DecompressData+0x108:
fffff802`18327f68 e85376ffff   call  srv2!memcpy (fffff802`1831f5c0)

kd> db rdx L0n16
ffff8205`e4678590 bc ff ff f2 1f 00 00 00-bc ff ff f2 1f 00 00 00 ................

kd> db rcx L0n16
ffff998e`13d780a0 00 00 88 02 06 00 00 00-00 00 80 00 00 00 00 00 ................

kd> !process 0 0 cve-2020-0796-local.exe
PROCESS ffff8205e9ad5080
  SessionId: 1 Cid: 16e0  Peb: 44e943b000 ParentCid: 024c
  DirBase: 1abc2002 ObjectTable: ffff998e13777580 HandleCount: 58.
  Image: cve-2020-0796-local.exe

kd> dq ffff8205e9ad5080+0x360 L1
ffff8205`e9ad53e0 ffff998e`13d7806e

我们发现memmove()函数的目的地0xffff998e13d780a0和cve-2020-0796-local.exe进程的TOKEN指针存放地址ffff8205e9ad53e0相去甚远。但却指向ffff998e`13d78060+0x40处。

kd> dt _TOKEN
nt!_TOKEN
  +0x000 TokenSource   : _TOKEN_SOURCE
  +0x010 TokenId     : _LUID
  +0x018 AuthenticationId : _LUID
  +0x020 ParentTokenId  : _LUID
  +0x028 ExpirationTime  : _LARGE_INTEGER
  +0x030 TokenLock    : Ptr64 _ERESOURCE
  +0x038 ModifiedId    : _LUID
  +0x040 Privileges    : _SEP_TOKEN_PRIVILEGES

从_TOKEN结构体的定义中,我们了解在0x40偏移处是一个_SEP_TOKEN_PRIVILEGES的结构。

kd> db ffff998e`13d78060+0x40 L0x16
ffff998e`13d780a0 00 00 88 02 06 00 00 00-00 00 80 00 00 00 00 00 ................
ffff998e`13d780b0 00 00 80 40 00 00                 ...@..

kd> dt _SEP_TOKEN_PRIVILEGES ffff998e`13d78060+0x40
nt!_SEP_TOKEN_PRIVILEGES
  +0x000 Present     : 0x00000006`02880000
  +0x008 Enabled     : 0x800000
  +0x010 EnabledByDefault : 0x40800000

kd> p
srv2!Srv2DecompressData+0x10d:
fffff802`18327f6d 8b442460    mov   eax,dword ptr [rsp+60h]

单步步过之后,再查看一下:

kd> db ffff998e`13d78060+0x40 L0x16
ffff998e`13d780a0 bc ff ff f2 1f 00 00 00-bc ff ff f2 1f 00 00 00 ................
ffff998e`13d780b0 00 00 80 40 00 00                ...@..

kd> dt _SEP_TOKEN_PRIVILEGES ffff998e`13d78060+0x40
nt!_SEP_TOKEN_PRIVILEGES
  +0x000 Present     : 0x0000001f`f2ffffbc
  +0x008 Enabled     : 0x0000001f`f2ffffbc
  +0x010 EnabledByDefault : 0x40800000

我们观察到cve-2020-0796-local.exe进程的TOKEN中的Privileges 确实被修改过了。

kd> dt _SEP_TOKEN_PRIVILEGES ffff8d8de`1206040+0x40
nt!_SEP_TOKEN_PRIVILEGES
  [+0x000] Present     : 0x1ff2ffffbc [Type: unsigned __int64]
  [+0x008] Enabled     : 0x1e60b1e890 [Type: unsigned __int64]
  [+0x010] EnabledByDefault : 0x1e60b1e890 [Type: unsigned __int64]

这神奇的16字节数据正是之前我们曾观察到的System进程的TOKEN中的Privileges的Present值。Present字段表示启用的特权,Enabled字段表示拥有的特权。这神奇的16字节数据覆盖写进程的Privileges结构之后,系统即拥有并启用了的system进程所有的特权。

我们了解到_SEP_TOKEN_PRIVILEGES是由3个bitmap构成的结构体,通过每bit是否为1来判断进程特权的。看上去应该不会超过64种特权。

7 “有种贪心叫我全要”

如果我们把这16字节全部用0xFF填充呢?

图 6

我们使用前文所述的WinDBG手动设置了一下,如图6所示,使用whoami /PRIV命令时会提示“错误: 指定的特权不存在”。我们在微软文档中也仅发现了44中特权。

我们修改了Exploit的源码,编译后执行验证,并在之前的断点停下来确认符合预期。

srv2!memmove(Src=0xffff8205e32a2590, Dst=0xffff998e11a4c6b0,Size=16)

srv2!Srv2DecompressData+0x108:
fffff802`18327f68 e85376ffff   call  srv2!memcpy (fffff802`1831f5c0)

kd> r
rax=ffff8205e32a2290 rbx=ffff8205e5679150 rcx=ffff998e11a4c6b0
rdx=ffff8205e32a2590 rsi=0000000000000010 rdi=ffff8205e32a2010
rip=fffff80218327f68 rsp=ffffe8861a6f9e70 rbp=0000000000000002
 r8=0000000000000010 r9=ffff8205e4e008e0 r10=ffff8205df002160
r11=ffffe8861a6f9e50 r12=0000000000000000 r13=ffff8205e8434680
r14=00000000ffffffff r15=0000000000000000
iopl=0     nv up ei ng nz na po nc
cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b       efl=00000286
srv2!Srv2DecompressData+0x108:
fffff802`18327f68 e85376ffff   call  srv2!memcpy (fffff802`1831f5c0)

kd> db rdx L0n16
ffff8205`e32a2590 ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff ................

kd> db rcx L0n16
ffff998e`11a4c6b0 00 00 88 02 06 00 00 00-00 00 80 00 00 00 00 00 ................
图 7

在图7中,结果显示这并不影响Exploit的效果,

8 特权影响名字?

替换TOKEN不仅可以获取特权,也直接变更了账户。这个可以理解。那么问题来了,虽然修改Privileges结构,可以获得特权,但又是如何影响账户名的呢?

现在我们需要来看看exploit的源码。

图 8

如图8中源码显示,在修改本进程的_SEP_TOKEN_PRIVILEGES结构之后,就已经获得system进程的特权,后续调用了inject()函数。

图 9

在图9中,inject()函数中通过CreateToolhelp32Snapshot()创建进程快照,使用比较进程名的方法找到"winlogon.exe"进程。然后使用VirtualAllocEx申请了一块带有可执行属性的内存(PAGE_EXECUTE_READWRITE),WriteProcessMemory把shellcode写入目标进程,CreateRemoteThread()最终完成任务。这是一个经典的远程线程注入。

图 10

图10中shellcode应该开启cmd的功能。

图 11

如图11所示,从进程树上我们能清楚的看出cmd.exe是winlogon.exe的子进程,和powershell.exe并未任何关系。

winlogon.exe是以nt authority\system用户的名义启动的,而子进程继承了父进程winlogon.exe的TOKEN,也就获得了system的特权,在使用whoami命令时显示用户也就是nt authority\system。

图 12

如图12所示,选取winlogon.exe作为注入进程只是一个特例,任何以NT AUTHORITY\SYSTEM或者其他特权账户名义启动的进程都有相同的效果。

9 应用层获取TOKEN的地址编程技巧

前文我们介绍了如何借助WinDBG手动修改位于内核态内存的进程的访问令牌,现在我们需要了解一下exploit如何使用编程技巧从应用层获取了TOKEN的地址的。

图 13

从源码上来看,其使用get_process_token()获取到了token在内核中的地址,然后利用CVE-2020-0796漏洞向ktoken+0x40处,即访问令牌中的_SEP_TOKEN_PRIVILEGES字段,写入了数据。

图 14
图 15

在Windows上有一些众所周知的信息泄漏技巧,如本例中使用的NtQuerySystemInformation函数。这个函数有一些神奇的功能,它会返回许多内核地址。我们主要感兴趣的是此函数能够提供目前分配的每个对象的列表,使用SystemExtendedHandleInformation参数调用NtQuerySystemInformation,我们可以得到SYSTEM_HANDLE_INFORMATION_EX结构。借助此列表,我们可以使用PID和句柄获取所需对象的内核地址。

至此漏洞利用视角下的工作基本完成。

评论

暂无
发表评论
 返回顶部 
热度(205)
 关注微信