提升的进程已链接令牌 - 它指的是非提升的用户会话。我们可以通过两种方式使用这个链接令牌:
第一种方式:
- 获取它为
TokenPrimary(为此我们需要SE_TCB_PRIVILEGE
当我们查询这个令牌时)
- 使用此令牌调用 CreateProcessAsUser。为此,我们还需要
SE_ASSIGNPRIMARYTOKEN_PRIVILEGE 和 SE_INCREASE_QUOTA_PRIVILEGE
- 为了获得所有这些特权 - 枚举进程,查询它的令牌,
并且如果进程令牌具有所有这 3 个权限 - 模拟
它,在致电
CreateProcessAsUser 之前。因为提升的令牌有
SE_DEBUG_PRIVILEGE这个任务是可以的
第二种方式:
从链接令牌(AuthenticationId
来自TOKEN_STATISTICS)
在进程令牌中找到具有相同 AuthenticationId 的进程。
通过帮助将此进程用作父进程
PROC_THREAD_ATTRIBUTE_PARENT_PROCESS
方式 1 的实现:
static volatile UCHAR guz;
ULONG RunNonElevated(HANDLE hToken, HANDLE hMyToken, PCWSTR lpApplicationName, PWSTR lpCommandLine)
{
ULONG err;
PVOID stack = alloca(guz);
ULONG cb = 0, rcb = FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges[SE_MAX_WELL_KNOWN_PRIVILEGE]);
union {
PVOID buf;
PTOKEN_PRIVILEGES ptp;
};
do
{
if (cb < rcb)
{
cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
}
if (GetTokenInformation(hToken, TokenPrivileges, buf, cb, &rcb))
{
if (ULONG PrivilegeCount = ptp->PrivilegeCount)
{
int n = 3;
BOOL fAdjust = FALSE;
PLUID_AND_ATTRIBUTES Privileges = ptp->Privileges;
do
{
switch (Privileges->Luid.LowPart)
{
case SE_ASSIGNPRIMARYTOKEN_PRIVILEGE:
case SE_INCREASE_QUOTA_PRIVILEGE:
case SE_TCB_PRIVILEGE:
if (!(Privileges->Attributes & SE_PRIVILEGE_ENABLED))
{
Privileges->Attributes |= SE_PRIVILEGE_ENABLED;
fAdjust = TRUE;
}
if (!--n)
{
err = NOERROR;
if (DuplicateTokenEx(hToken,
TOKEN_ADJUST_PRIVILEGES|TOKEN_IMPERSONATE,
0, SecurityImpersonation, TokenImpersonation,
&hToken))
{
if (fAdjust)
{
AdjustTokenPrivileges(hToken, FALSE, ptp, rcb, NULL, NULL);
err = GetLastError();
}
if (err == NOERROR)
{
if (SetThreadToken(0, hToken))
{
TOKEN_LINKED_TOKEN tlt;
if (GetTokenInformation(hMyToken, TokenLinkedToken, &tlt, sizeof(tlt), &rcb))
{
STARTUPINFO si = { sizeof (si) };
PROCESS_INFORMATION pi;
if (!CreateProcessAsUserW(tlt.LinkedToken, lpApplicationName, lpCommandLine,
NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
{
err = GetLastError();
}
CloseHandle(tlt.LinkedToken);
if (err == NOERROR)
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
}
else
{
err = GetLastError();
}
SetThreadToken(0, 0);
}
else
{
err = GetLastError();
}
}
CloseHandle(hToken);
}
else
{
err = GetLastError();
}
return err;
}
}
} while (Privileges++, --PrivilegeCount);
}
return ERROR_NOT_FOUND;
}
} while ((err = GetLastError()) == ERROR_INSUFFICIENT_BUFFER);
return err;
}
ULONG RunNonElevated(HANDLE hMyToken, PCWSTR lpApplicationName, PWSTR lpCommandLine)
{
static TOKEN_PRIVILEGES tp = {
1, { { { SE_DEBUG_PRIVILEGE } , SE_PRIVILEGE_ENABLED } }
};
AdjustTokenPrivileges(hMyToken, FALSE, &tp, sizeof(tp), NULL, NULL);
ULONG err = NOERROR;
// much more effective of course use NtQuerySystemInformation(SystemProcessesAndThreadsInformation) here
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0), hToken;
if (hSnapshot != INVALID_HANDLE_VALUE)
{
PROCESSENTRY32W pe = { sizeof(pe) };
if (Process32FirstW(hSnapshot, &pe))
{
err = ERROR_NOT_FOUND;
do
{
if (pe.th32ProcessID && pe.th32ParentProcessID)
{
if (HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pe.th32ProcessID))
{
if (OpenProcessToken(hProcess, TOKEN_QUERY|TOKEN_DUPLICATE, &hToken))
{
err = RunNonElevated(hToken, hMyToken, lpApplicationName, lpCommandLine);
CloseHandle(hToken);
}
else
{
err = GetLastError();
}
CloseHandle(hProcess);
}
else
{
err = GetLastError();
}
}
} while (err && Process32NextW(hSnapshot, &pe));
}
else
{
err = GetLastError();
}
CloseHandle(hSnapshot);
}
return err;
}
ULONG RunNonElevated(PCWSTR lpApplicationName, PWSTR lpCommandLine)
{
HANDLE hToken;
ULONG err = NOERROR;
if (OpenProcessToken(NtCurrentProcess(), TOKEN_QUERY|TOKEN_ADJUST_PRIVILEGES, &hToken))
{
TOKEN_ELEVATION_TYPE tet;
ULONG rcb;
if (GetTokenInformation(hToken, ::TokenElevationType, &tet, sizeof(tet), &rcb))
{
if (tet == TokenElevationTypeFull)
{
RunNonElevated(hToken, lpApplicationName, lpCommandLine);
}
else
{
err = ERROR_ALREADY_ASSIGNED;
}
}
else
{
err = GetLastError();
}
CloseHandle(hToken);
}
else
{
err = GetLastError();
}
return err;
}
方式 2 的实现:
ULONG CreateProcessEx(HANDLE hProcess,
PCWSTR lpApplicationName,
PWSTR lpCommandLine)
{
SIZE_T Size = 0;
STARTUPINFOEX si = { sizeof(si) };
PROCESS_INFORMATION pi;
InitializeProcThreadAttributeList(0, 1, 0, &Size);
ULONG err = GetLastError();
if (err = ERROR_INSUFFICIENT_BUFFER)
{
si.lpAttributeList = (PPROC_THREAD_ATTRIBUTE_LIST)alloca(Size);
if (InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &Size))
{
if (UpdateProcThreadAttribute(si.lpAttributeList, 0,
PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &hProcess, sizeof(hProcess), 0, 0) &&
CreateProcessW(lpApplicationName, lpCommandLine, 0, 0, 0,
EXTENDED_STARTUPINFO_PRESENT, 0, 0, &si.StartupInfo, &pi))
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
else
{
err = GetLastError();
}
DeleteProcThreadAttributeList(si.lpAttributeList);
}
else
{
err = GetLastError();
}
}
else
{
err = GetLastError();
}
return err;
}
ULONG CreateProcessEx(LUID AuthenticationId,
PCWSTR lpApplicationName,
PWSTR lpCommandLine)
{
ULONG err = ERROR_NOT_FOUND;
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot != INVALID_HANDLE_VALUE)
{
PROCESSENTRY32W pe = { sizeof(pe) };
ULONG rcb;
if (Process32First(hSnapshot, &pe))
{
err = ERROR_NOT_FOUND;
BOOL found = FALSE;
do
{
if (pe.th32ProcessID && pe.th32ParentProcessID)
{
if (HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION|PROCESS_CREATE_PROCESS, FALSE, pe.th32ProcessID))
{
HANDLE hToken;
if (OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
{
TOKEN_STATISTICS ts;
if (GetTokenInformation(hToken, TokenStatistics, &ts, sizeof(ts), &rcb))
{
if (ts.AuthenticationId.LowPart == AuthenticationId.LowPart &&
ts.AuthenticationId.HighPart == AuthenticationId.HighPart)
{
found = TRUE;
err = CreateProcessEx(hProcess,
lpApplicationName,
lpCommandLine);
}
}
CloseHandle(hToken);
}
CloseHandle(hProcess);
}
}
} while (!found && Process32Next(hSnapshot, &pe));
}
else
{
err = GetLastError();
}
CloseHandle(hSnapshot);
}
else
{
err = GetLastError();
}
return err;
}
ULONG CreateProcessEx(PCWSTR lpApplicationName,
PWSTR lpCommandLine)
{
HANDLE hToken;
ULONG err = NOERROR;
if (OpenProcessToken(NtCurrentProcess(), TOKEN_QUERY, &hToken))
{
union {
TOKEN_ELEVATION_TYPE tet;
TOKEN_LINKED_TOKEN tlt;
};
ULONG rcb;
if (GetTokenInformation(hToken, TokenElevationType, &tet, sizeof(tet), &rcb))
{
if (tet == TokenElevationTypeFull)
{
if (GetTokenInformation(hToken, TokenLinkedToken, &tlt, sizeof(tlt), &rcb))
{
TOKEN_STATISTICS ts;
BOOL fOk = GetTokenInformation(tlt.LinkedToken, TokenStatistics, &ts, sizeof(ts), &rcb);
CloseHandle(tlt.LinkedToken);
if (fOk)
{
err = CreateProcessEx(ts.AuthenticationId,
lpApplicationName,
lpCommandLine);
}
else
{
err = GetLastError();
}
}
else
{
err = GetLastError();
}
}
else
{
err = ERROR_ALREADY_ASSIGNED;
}
}
else
{
err = GetLastError();
}
CloseHandle(hToken);
}
else
{
err = GetLastError();
}
return err;
}
和测试:
WCHAR ApplicationName[MAX_PATH];
if (GetEnvironmentVariableW(L"ComSpec", ApplicationName, RTL_NUMBER_OF(ApplicationName)))
{
WCHAR cmdline[] = L"cmd.exe /k whoami /priv /groups\r\n";
CreateProcessEx(ApplicationName, cmdline);
RunNonElevated(ApplicationName, cmdline);
}
对于方式#2 理论上,我们找不到与我们链接的令牌中具有相同登录 ID (AuthenticationId) 的进程。但方式#1 必须始终有效。始终存在具有SeTcbPrivilege(用于获取链接令牌的主要形式)+SeAssignPrimaryTokenPrivilege(用于CreateProcessAsUser)的系统进程(SeIncreaseQuotaPrivilege在msdn中作为CreateProcessAsUser的典型要求进行侦听,但在我的测试中,即使此权限未启用)。但是所有系统进程(以LocalSystem 运行)在令牌中都具有这3 个特权(从smss.exe 开始),并且某些系统进程始终在系统中运行。
所以方式#1 绝不能失败和首选。我们也可以在这里使用例如从我们的进程继承的句柄,用于与子进程交互。这在方式 2 中是不可能的。它显示出来是为了图片的完整性
在开始时,我们检查TOKEN_ELEVATION_TYPE 并执行工作,前提是它是TokenElevationTypeFull。万一TokenElevationTypeLimited 我们没有提升进程 - 所以什么都不做。
case TokenElevationTypeDefault 表示或 UAC 如果关闭(禁用 LUA)或者我们以内置管理员身份运行,并且 lua 不过滤此帐户的令牌(因此所有进程都“提升”或更确切地说它没有通过 CreateRestrictedToken(..LUA_TOKEN..) 过滤的令牌) - 在这种情况下也没有意义尝试在此用户下运行“未提升”进程