-
-
[原创] #30天写作挑战#在内核中利用Token实现提权
-
2020-9-13 01:01 12677
-
使用WinDBG在内核中利用Token实现提权
关键的数据结构
在开始之前,我们需要知道两个内核重要的数据结构
_EPROCESS
_EPROCESS
是一个用来描述系统进程的内核结构体(或者说每一个运行在系统上的进程都有一个储存在内核中的某个地方的被称为描述符的_EPROCESS
结构体和它相对),它包含了诸如进程映像名,运行在哪一个桌面会话中,打开了多少个内核对象的句柄,token的访问权限......之类的信息。
在WinDBG中使用
dt _eprocess
可以查看该结构体的内容。
nt!_EPROCESS +0x000 Pcb : _KPROCESS +0x2e0 ProcessLock : _EX_PUSH_LOCK +0x2e8 UniqueProcessId : Ptr64 Void +0x2f0 ActiveProcessLinks : _LIST_ENTRY +0x300 RundownProtect : _EX_RUNDOWN_REF +0x308 Flags2 : Uint4B +0x308 JobNotReallyActive : Pos 0, 1 Bit +0x308 AccountingFolded : Pos 1, 1 Bit +0x308 NewProcessReported : Pos 2, 1 Bit +0x308 ExitProcessReported : Pos 3, 1 Bit +0x308 ReportCommitChanges : Pos 4, 1 Bit +0x308 LastReportMemory : Pos 5, 1 Bit +0x308 ForceWakeCharge : Pos 6, 1 Bit +0x308 CrossSessionCreate : Pos 7, 1 Bit +0x308 NeedsHandleRundown : Pos 8, 1 Bit +0x308 RefTraceEnabled : Pos 9, 1 Bit +0x308 PicoCreated : Pos 10, 1 Bit +0x308 EmptyJobEvaluated : Pos 11, 1 Bit +0x308 DefaultPagePriority : Pos 12, 3 Bits +0x308 PrimaryTokenFrozen : Pos 15, 1 Bit +0x308 ProcessVerifierTarget : Pos 16, 1 Bit +0x308 RestrictSetThreadContext : Pos 17, 1 Bit +0x308 AffinityPermanent : Pos 18, 1 Bit +0x308 AffinityUpdateEnable : Pos 19, 1 Bit +0x308 PropagateNode : Pos 20, 1 Bit +0x308 ExplicitAffinity : Pos 21, 1 Bit +0x308 ProcessExecutionState : Pos 22, 2 Bits +0x308 EnableReadVmLogging : Pos 24, 1 Bit +0x308 EnableWriteVmLogging : Pos 25, 1 Bit +0x308 FatalAccessTerminationRequested : Pos 26, 1 Bit +0x308 DisableSystemAllowedCpuSet : Pos 27, 1 Bit +0x308 ProcessStateChangeRequest : Pos 28, 2 Bits +0x308 ProcessStateChangeInProgress : Pos 30, 1 Bit +0x308 InPrivate : Pos 31, 1 Bit ...
_TOKEN
_TOKEN
是一个描述进程安全上下文的内核数据结构,包含了诸如进程token的等级,登陆id,会话id,token的类型......之类的信息。
dt _token
下面介绍下如何使用上述两个结构体来把一个中等权限进程提升到system权限。
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 +0x078 SessionId : Uint4B +0x07c UserAndGroupCount : Uint4B +0x080 RestrictedSidCount : Uint4B +0x084 VariableLength : Uint4B +0x088 DynamicCharged : Uint4B +0x08c DynamicAvailable : Uint4B +0x090 DefaultOwnerIndex : Uint4B +0x098 UserAndGroups : Ptr64 _SID_AND_ATTRIBUTES +0x0a0 RestrictedSids : Ptr64 _SID_AND_ATTRIBUTES +0x0a8 PrimaryGroup : Ptr64 Void +0x0b0 DynamicPart : Ptr64 Uint4B +0x0b8 DefaultDacl : Ptr64 _ACL +0x0c0 TokenType : _TOKEN_TYPE +0x0c4 ImpersonationLevel : _SECURITY_IMPERSONATION_LEVEL +0x0c8 TokenFlags : Uint4B +0x0cc TokenInUse : UChar +0x0d0 IntegrityLevelIndex : Uint4B +0x0d4 MandatoryPolicy : Uint4B +0x0d8 LogonSession : Ptr64 _SEP_LOGON_SESSION_REFERENCES ...
两种方法
1.替换Tokens实现权限提升
一种内核提权的方式是使用高权限等级的token替换低权限等级的token,下面列出一些关键操作。
- 每一个运行在系统上的进程都有一个
_EPROCESS
内核结构体描述符。 _EPROCESS
结构体包含一个指向一个描述进程安全上下文的_TOKEN
结构体的内存指针。- 在内核中找到描述目标进程的
_TOKEN
结构体地址。 - 在内核中找到描述运行在
NT\SYSTEM
权限下的进程的_TOKEN
结构体地址。 - 在内核中把
_TOKEN
进行替换。
下面手工使用WinDBG使用一个高权限等级的system进程(PID=4)来替换一个低权限等级的cmd。
下面尝试用一个图直观地表示上面描述的过程:
列出进程
!process 0 0
下面是个使用上述命令获取当前运行在此计算机的进程截图,高亮部分是指向所属进程的_EPROCESS
结构体。
下面运行一个powershell(使用非管理员权限)并且查看它的进程id(pid):
使用下面命令获取pid为2780(0xadc)的powershell进程的信息:
!process adc 0
从下图可看到我们的powershell.exe进程信息,_EPROCESS
在ffff8c0ab3e77080地址处:
找到powershellcode进程的_EPROCESS
结构体在内核中的位置,我们可以使用下述命令:
dt _eprocess ffff8c0ab3e77080
我们可以看到token在偏移0x360处。
读出指针_EPROCESS.Token
指向的位置,0xffff9f891b2296b5:
0: kd> dx -id 0,0,ffff8c0aaf868380 -r1 (*((ntkrnlmp!_EX_FAST_REF *)0xffff8c0ab3e773e0)) (*((ntkrnlmp!_EX_FAST_REF *)0xffff8c0ab3e773e0)) [Type: _EX_FAST_REF] [+0x000] Object : 0xffff9f891b2296b5 [Type: void *] [+0x000 ( 3: 0)] RefCnt : 0x5 [Type: unsigned __int64] [+0x000] Value : 0xffff9f891b2296b5 [Type: unsigned __int64]
但是当我们使用时:
!token ffff9f891b2296b5
显示
0: kd> !token ffff9f891b2296b5 The address 0xffff9f891b2296b5 does not point to a token object.
再让我们仔细看一下_EX_FAST_RED
这个结构体:
0: kd> dx -id 0,0,ffff8c0aaf868380 -r1 (*((ntkrnlmp!_EX_FAST_REF *)0xffff8c0ab3e773e0)) (*((ntkrnlmp!_EX_FAST_REF *)0xffff8c0ab3e773e0)) [Type: _EX_FAST_REF] [+0x000] Object : 0xffff9f891b2296b5 [Type: void *] [+0x000 ( 3: 0)] RefCnt : 0x5 [Type: unsigned __int64] [+0x000] Value : 0xffff9f891b2296b5 [Type: unsigned __int64]
注意这三个成员都有相同的偏移,但是RefCnt只有四位,看起来像object和value成员的最后的一个数。
可以检查一下没有数据的_EX_FAST_REF
结构体,定义如下:
ntdll!_EX_FAST_REF +0x000 Object : Ptr64 Void +0x000 RefCnt : Pos 0, 4 Bits +0x000 Value : Uint8B
上述结构体表明,16进制的最后四位数被用来作为这个token的引用计数的,也就是说它不是token地址的一部分,因此我们可以把它置为0后,就可以得到token结构体的地址。
实际上我们可以通过
0: kd> !process ffff8c0ab3e77080 1
或
0: kd> !process adc 1
直接得到token结构体的地址
查看token的内容:
找到system进程token
0: kd> !process 4 1 Searching for Process with Cid == 4 PROCESS ffff8c0aaf868380 SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000 DirBase: 001ad002 ObjectTable: ffff9f8912427c00 HandleCount: 2219. Image: System VadRoot ffff8c0ab27f1370 Vads 8 Clone 0 Private 22. Modified 411031. Locked 0. DeviceMap ffff9f8912413600 Token ffff9f8912408760
替换token
eq ffff8c0ab3e77080+0x360 ffff9f8912408760
可以看到提权成功。
2.更改token权限
在_TOKEN结构体中偏移0x40处有一个Privileges的成员,它是_SEP_TOKEN_PRIVILEGES结构体:
dt _token ffff9f891bbdc610
检查该结构体:
此时权限状态为:
我们把Present的值复制到Enable值中:
eq ffff9f891bbdc610+0x40+0x8 0x602880000
权限状态如下所示:
添加权限
查看系统进程的权限,过程如下:
更改其权限:
0: kd> eq ffff9f891bbdc610+0x40 0x1ff2ffffbc 0: kd> eq ffff9f891bbdc610+0x40+0x8 0x1ff2ffffbc
查看权限:
[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。