本节内容
解决上节关于提升Windows权限的遗留问题,在上节内容卸载DLL中,利用了EjectDll.exe从notepad.exe的加载模块中进行指定模块的卸载。这个过程中涉及了跨内存操作,都知道Windows系统需要保证进程之间互不干扰,相互独立。在Windows下编程有些涉及到硬件或者跨内存的API会发现失效,这就需要在执行某些跨内存的操作前提升Windows的权限。
这部分的具体实例是EjectDll -> SetPrivilege(SE_DEBUG_NAME, TRUE),函数定义如下:
1 | BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege) |
SetPrivilege -> OpenProcessToken
1 | BOOL OpenProcessToken( |
Access token
学习这个API之前有必要了解access token:
访问令牌是一个被保护的对象,包含了与用户帐户相关的辨识和特权信息。当用户登陆到一台windows计算机,登陆进程会验证用户的登陆凭据。成功后,登陆进程返回一个对应用户的SID和一个用户的安全组SID列表。计算机LSA使用这些信息创建一个访问令牌(主访问令牌)。该访问令牌包括了由登录进程返回的SIDs和一份由本地安全策略分发给用户以及用户安全组的特权列表。此后,这份访问令牌的拷贝会跟每个代表用户执行的线程和进程链接。
如果拿到访问令牌,登录成功,要是访问某台本域内计算机的共享资源时,则必须出示访问令牌。从而来决定将拥有何种权限来访问。
因此在涉及跨进程内存的操作需要了解是否具备相应的权限,没有的话则需要先进行权限提升。
回到OpenProcessToken的分析
关于OpenProcessToken API的官方文档中对于API的描述有这样的说法:The OpenProcessToken function opens the access token associated with a process.
因此OpenProcessToken 是用来返回具备权限(DesiredAccess)目标进程(notepad.exe)的访问令牌(access token)。
第一个参数传递的是想要获取的access token对应的进程句柄。
第二个参数需要指出将对打开的access token期望得到的权限
第三个参数则是操作成功后返回的access token
这里列举第二个参数可能的取值,来源:
Value | Meaning |
---|---|
TOKEN_ADJUST_DEFAULT | Required to change the default owner, primary group, or DACL of an access token. |
TOKEN_ADJUST_GROUPS | Required to adjust the attributes of the groups in an access token. |
TOKEN_ADJUST_PRIVILEGES | Required to enable or disable the privileges in an access token. |
TOKEN_ADJUST_SESSIONID | Required to adjust the session ID of an access token. The SE_TCB_NAME privilege is required. |
TOKEN_ASSIGN_PRIMARY | Required to attach a primary token to a process. The SE_ASSIGNPRIMARYTOKEN_NAME privilege is also required to accomplish this task. |
TOKEN_DUPLICATE | Required to duplicate an access token. |
TOKEN_EXECUTE | Same as STANDARD_RIGHTS_EXECUTE. |
TOKEN_IMPERSONATE | Required to attach an impersonation access token to a process. |
TOKEN_QUERY | Required to query an access token. |
TOKEN_QUERY_SOURCE | Required to query the source of an access token. |
TOKEN_READ | Combines STANDARD_RIGHTS_READ and TOKEN_QUERY. |
TOKEN_WRITE | Combines STANDARD_RIGHTS_WRITE, TOKEN_ADJUST_PRIVILEGES, TOKEN_ADJUST_GROUPS, and TOKEN_ADJUST_DEFAULT. |
TOKEN_ALL_ACCESS | Combines all possible access rights for a token. |
源码中第二个参数被设置为:TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY。表明对于打开的access token我们需要能够访问access token并作启用或禁用access token的某些特权。
PS:需要具备TOKEN_ADJUST_PRIVILEGES权限的access token才有提升权限的能力
如期执行的话将返回目标进程(notepad.exe)的access token(具备TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY的权限)
SetPrivilege -> LookupPrivilegeValue
1 | BOOL LookupPrivilegeValueA( |
官方文档中有描述:The LookupPrivilegeValue function retrieves the locally unique identifier (LUID) used on a specified system to locally represent the specified privilege name.指出该API用于取回用于描述指定系统所所具备权限的LUID结构,大致作用是查看系统上所允许的令牌权限
第一个参数是一个以NULL结尾的字符串,指出想查看的系统。当第一个参数设置为NULL表明查询的是本地系统localhost
第二个参数指定特权名,马上提到
第三个参数用于接收指定权限的相关信息
1 | //特权名,来源:winnt.h |
例子中传给SetPrivilege -> LookupPrivilegeValue的参数是LookupPrivilegeValue(NULL,,lpszPrivilege,&luid) )
其中第二个参数是传递进来的 SE_DEBUG_NAME。所以完成的是查看当前系统是否具备SE_DEBUG_NAME的权限。LookupPrivilegeValue成功查询到,则返回非0值,并且保存 LUID在变量luid中,否则返回0,说明不具备相应权限。
关于权限SE_DEBUG_NAME:
If the debugging process has the SE_DEBUG_NAME privilege granted and enabled, it can debug any process.
当具备权限SE_DEBUG_NAME并且处于enabled状态,则能够调试任何进程
结构体TOKEN_PRIVILEGES
这里需要补充的是一个结构体:TOKEN_PRIVILEGES
1 | typedef struct _TOKEN_PRIVILEGES { |
官方描述中给出:The TOKEN_PRIVILEGES structure contains information about a set of privileges for an access token.大致是说结构体TOKEN_PRIVILEGES包含给定的access token所具备的权限信息
第一个成员PrivilegeCount,标识access token所具备的权限条目数量,是必须要有的
第二个成员是指向结构体LUID_AND_ATTRIBUTES 的数组,每一个数组成员包含LUID 以及特权的属性
LUID_AND_ATTRIBUTES结构体
1 | typedef struct _LUID_AND_ATTRIBUTES { |
特权属性
Value | Meaning |
---|---|
SE_PRIVILEGE_ENABLED | The privilege is enabled. |
SE_PRIVILEGE_ENABLED_BY_DEFAULT | The privilege is enabled by default. |
SE_PRIVILEGE_REMOVED | Used to remove a privilege. For details, see AdjustTokenPrivileges. |
SE_PRIVILEGE_USED_FOR_ACCESS | The privilege was used to gain access to an object or service. This flag is used to identify the relevant privileges in a set passed by a client application that may contain unnecessary privileges. |
接着分析
后续对声明的结构体TOKEN_PRIVILEGES tp
赋值:
1 | tp.PrivilegeCount = 1; |
随后
1 | if( true ) |
完成的将luid中具备的SE_DEBUG_NAME的权限打开(enable)
SetPrivilege -> AdjustTokenPrivileges
1 | BOOL AdjustTokenPrivileges( |
官方描述:The AdjustTokenPrivileges function enables or disables privileges in the specified access token. Enabling or disabling privileges in an access token requires TOKEN_ADJUST_PRIVILEGES access.
用于激活或禁止access token的相关特权,禁止或激活特权需要access token具备TOKEN_ADJUST_PRIVILEGES特权
第一个参数指向即将要修改的access token,这里是由前面OpenProcessToken中返回的access token并且是具备TOKEN_ADJUST_PRIVILEGES权限的
第二个参数用于指示是否关闭access token的全部权限,显然这里与我们目的相悖,因此为false
第三个参数的指针指向结构体TOKEN_PRIVILEGES,当第二个参数设置为false则AdjustTokenPrivileges根据这个结构体的内容关闭或禁止其中设置的权限,可以取值:SE_PRIVILEGE_ENABLED 或者 SE_PRIVILEGE_REMOVED 或者NONE(关闭),总的来说就是新特权的指针
第四个参数为字节为单位的PTOKEN_PRIVILEGES NewStateS所占的大小,如果NewState为空,该参数应为NULL
第五个参数也是一个指向 TOKEN_PRIVILEGES结构的指针,存放修改前的访问权限的信息,可空;
最后一个参数为实际PreviousState结构返回的大小,可空
1 | AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), |
如期执行函数则可以提升OpenProcessToken返回的access token的相关权限
回顾
- 利用OpenProcessToken,返回一个能访问目标进程并且具备提升权限的access token
- LookupPrivilegeValue,返回一个具备指定权限的LUID(local unique identifier)
- 设置结构体TOKEN_PRIVILEGES
- AdjustTokenPrivileges,利用上述步骤完成的参数铺垫设置,完成权限提升