有关Windows Token的一些坑
Ⅰ:前言
当我们需要从运行在Session 0的服务进程创建一个具有管理员权限,且运行在登录用户Session的进程时,一般的流程代码应该如:
|
|
这流程看起来很普通,但是暗藏几个细节坑,下面将详细解释
Ⅱ:驱动保护与WTSQueryUserToken
-
前置条件
在Windows 7系统上,当需要对进程及线程进行保护时,一般我们使用
ObRegisterCallbacks
进行权限过滤来实现,并且一般而言,我们需要放过对部分系统关键进程的过滤,如csrss.exe
。 -
问题描述
WTSQueryUserToken
失败,错误代码ERROR_ACCESS_DENIED
-
原因分析
-
根据经验判断,开关进程保护,
WTSQueryUserToken
会表现出不同的行为(成功/失败) -
猜测在Windows 7上
WTSQueryUserToken
的实现方式为RPC调用,最后经由RPC服务进程DuplicateTokenEx
来实现最后的Token覆写,且RPC服务进程没有在驱动白名单内,导致无权限执行DuplicateTokenEx
-
掏出IDA一通马操作,得到如下的执行流程图
如图所示(可以在新标签页中查看大图)
-
证实猜测
再次证明写代码得靠猜
-
-
解决办法
将系统进程
lsm.exe
加入驱动保护过滤白名单即可
Ⅲ:系统UAC与GetTokenInformation
-
前置条件
在session 0进程下,我们默认拥有
SeTcbPrivilege
权限,此时获取一个普通权限的用户Token的LinkedToken
时,则能自动提权并得到具有管理员权限的Token。 -
问题描述
在Windows 10系统上,若禁用系统UAC后,调用
GetTokenInformation
且第二个参数为TokenLinkedToken
时,会失败且错误码为ERROR_NO_SUCH_LOGON_SESSION
-
原因分析
Windows系统的Token具有三种提权类型,分别是
-
TokenElevationTypeDefault
:此用户使用了Single Token换而言之,没有使用Split Token,比如说UAC禁用的条件下。
此时当前Token已经提权
-
TokenElevationTypeFull
:此用户使用的了Split Token,且当前Token已经提权此时的
LinkedToken
为受限权限Token -
TokenElevationTypeLimited
:此用户使用的了Split Token,且当前Token为受限权限Token此时的
LinkedToken
为提权Token
那么当UAC禁用的条件下,由于没有使用Split Token,自然无法获取到相应的
LinkedToken
-
-
解决办法
判断当前Token的提权类型,当且仅当其权限类型为
TokenElevationTypeLimited
才获取其LinkedToken
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
// ...前置代码 // 获取token类型 TOKEN_ELEVATION_TYPE tokenElvType; DWORD typeSize = sizeof(tokenElvType); if (!GetTokenInformation(userToken, TokenElevationType, &tokenElvType, sizeof(TOKEN_ELEVATION_TYPE), &typeSize)) { CloseHandle(userToken); return {}; } if (tokenElvType == TokenElevationTypeLimited) { // 获取linked token TOKEN_LINKED_TOKEN adminTokenInfo = { 0 }; DWORD infoLen = sizeof(adminTokenInfo); if (!GetTokenInformation(userToken, TokenLinkedToken, &adminTokenInfo, sizeof(TOKEN_LINKED_TOKEN), &infoLen)) { CloseHandle(userToken); return {}; } } // ...后接代码
Ⅳ:后话
-
如何验证系统UAC是否处于启用状态
执行命令
1
REG QUERY HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\ /v EnableLUA
得到
EnableLUA
如果为1
表示启用,0
则反之