Reputation: 1753
OpenProcess(PROCESS_QUERY_INFORMATION, TRUE, pid)
returns successfully for, for example, an Administrator cmd.exe and non-Administrator cmd.exe. However, when I pass in the pid for a node.exe process, it fails and GetLastError()
returns ERROR_ACCESS_DENIED
. Can someone tell me how to get access to this process object?
UPDATE: I believe I've done all that @ErykSun suggested in the following code. I've gotten further, but, now, AdjustTokenPrivileges()
fails with ERROR_ACCESS_DENIED
(I stripped out all error handling to make the code easier to understand here). Moreover, now my program cannot disable privileges for lower-integrity levels, e.g., non-Administrator cmd.exe. (The two function prototypes are for helper functions following the disable_all_privileges()
function. They are, hopefully, correct.)
BOOL set_privilege(HANDLE hToken, const char* privilege, BOOL bEnablePrivilege);
BOOL get_self_token(HANDLE* phToken);
int disable_all_privileges(DWORD pid)
{
int ret = 1;
HANDLE hSelfToken;
if (get_self_token(&hSelfToken)) {
if (set_privilege(hSelfToken, "SeDebugPrivilege", TRUE)) {
if (set_privilege(hSelfToken, "SeImpersonatePrivilege", TRUE)) {
const HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
if (hProcess) {
HANDLE hToken;
if (OpenProcessToken(hProcess, TOKEN_DUPLICATE | TOKEN_QUERY, &hToken)) {
print_privileges(hToken);
if (ImpersonateLoggedOnUser(hToken))
if (AdjustTokenPrivileges(hToken, TRUE, NULL, 0, NULL, NULL) && GetLastError() == ERROR_SUCCESS) {
print_privileges(hToken);
ret = 0;
}
CloseHandle(hToken);
}
}
set_privilege(hSelfToken, "SeImpersonatePrivilege", FALSE);
}
set_privilege(hSelfToken, "SeDebugPrivilege", FALSE);
}
CloseHandle(hSelfToken);
}
return ret;
}
BOOL set_privilege(HANDLE hToken, const char* privilege, BOOL bEnablePrivilege)
{
BOOL ok = FALSE;
LUID luid;
if (LookupPrivilegeValueA(NULL, privilege, &luid)) {
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = 0;
TOKEN_PRIVILEGES tpPrevious;
DWORD cbPrevious = sizeof tpPrevious;
AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof tp, &tpPrevious, &cbPrevious);
if (GetLastError() == ERROR_SUCCESS) {
tpPrevious.PrivilegeCount = 1;
tpPrevious.Privileges[0].Luid = luid;
if (bEnablePrivilege)
tpPrevious.Privileges[0].Attributes |= SE_PRIVILEGE_ENABLED;
else
tpPrevious.Privileges[0].Attributes ^= SE_PRIVILEGE_ENABLED & tpPrevious.Privileges[0].Attributes;
AdjustTokenPrivileges(hToken, FALSE, &tpPrevious, cbPrevious, NULL, NULL);
ok = GetLastError() == ERROR_SUCCESS;
}
}
return ok;
}
BOOL get_self_token(HANDLE* phToken)
{
BOOL ok = FALSE;
if (OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, phToken))
ok = TRUE;
else
if (GetLastError() == ERROR_NO_TOKEN)
if (ImpersonateSelf(SecurityImpersonation))
if (OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, phToken))
ok = TRUE;
return ok;
}
Upvotes: 0
Views: 1637
Reputation: 1753
I finally got it working. Here's the code for the little program that disables all of a process's privileges. The pid is passed on the command line. I wouldn't have been able to do it without Eryk Sun's help (thanks!). (See UPDATE at bottom.)
#include <windows.h>
#include <stdio.h>
#include <TlHelp32.h>
void print_privileges(HANDLE hToken)
{
DWORD size;
if (!GetTokenInformation(hToken, TokenPrivileges, NULL, 0, &size) && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
PTOKEN_PRIVILEGES tp = malloc(size);
if (tp != NULL && GetTokenInformation(hToken, TokenPrivileges, tp, size, &size)) {
size_t i;
for (i = 0; i < tp->PrivilegeCount; ++i) {
char name[64];
DWORD NAME_SIZE = sizeof name;
LookupPrivilegeNameA(0, &tp->Privileges[i].Luid, name, &NAME_SIZE);
BOOL fResult;
PRIVILEGE_SET ps = {
1, PRIVILEGE_SET_ALL_NECESSARY, {
{ { tp->Privileges[i].Luid.LowPart, tp->Privileges[i].Luid.HighPart } }
}
};
PrivilegeCheck(hToken, &ps, &fResult);
printf("%-*s %s\n", 42, name, fResult ? "Enabled" : "Disabled");
}
}
free(tp);
}
}
BOOL set_privilege(HANDLE hToken, const char* privilege, BOOL bEnablePrivilege)
{
BOOL ok = FALSE;
LUID luid;
if (LookupPrivilegeValueA(NULL, privilege, &luid)) {
// first pass. get current privilege setting
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = 0;
TOKEN_PRIVILEGES tpPrevious;
DWORD cbPrevious = sizeof tpPrevious;
AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof tp, &tpPrevious, &cbPrevious);
if (GetLastError() == ERROR_SUCCESS) {
// second pass. set privilege based on previous setting
tpPrevious.PrivilegeCount = 1;
tpPrevious.Privileges[0].Luid = luid;
if (bEnablePrivilege)
tpPrevious.Privileges[0].Attributes |= SE_PRIVILEGE_ENABLED;
else
tpPrevious.Privileges[0].Attributes ^= SE_PRIVILEGE_ENABLED & tpPrevious.Privileges[0].Attributes;
AdjustTokenPrivileges(hToken, FALSE, &tpPrevious, cbPrevious, NULL, NULL);
ok = GetLastError() == ERROR_SUCCESS;
}
}
return ok;
}
BOOL get_current_token(HANDLE* phToken)
{
BOOL ok = FALSE;
if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, phToken)) {
if (GetLastError() == ERROR_NO_TOKEN)
if (ImpersonateSelf(SecurityImpersonation))
if (OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, phToken))
ok = TRUE;
} else
ok = TRUE;
return ok;
}
BOOL impersonate_process(HANDLE hProcess)
{
BOOL ok = FALSE;
HANDLE hToken;
if (OpenProcessToken(hProcess, TOKEN_DUPLICATE | TOKEN_QUERY, &hToken)) {
if (ImpersonateLoggedOnUser(hToken))
ok = TRUE;
CloseHandle(hToken);
}
return ok;
}
BOOL disable_all_privileges(HANDLE hProcess)
{
BOOL ok = FALSE;
HANDLE hToken;
if (OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
puts("\nBefore:");
print_privileges(hToken);
/* disable all privileges */
if (AdjustTokenPrivileges(hToken, TRUE, NULL, 0, NULL, NULL))
/* AdjustTokenPrivileges() could succeed but still not disable all privileges */
if (GetLastError() != ERROR_NOT_ALL_ASSIGNED) {
puts("\nAfter:");
print_privileges(hToken);
ok = TRUE;
}
CloseHandle(hToken);
}
return ok;
}
BOOL reduce_privileges(DWORD pid)
{
BOOL ok = FALSE;
/* in case target process has higher integrity level than this process, e.g., system versus
* high integrity, enable SeDebugPrivilege and impersonate target process */
HANDLE hCurrentToken;
if (get_current_token(&hCurrentToken)) {
if (set_privilege(hCurrentToken, "SeDebugPrivilege", TRUE)) {
if (set_privilege(hCurrentToken, "SeImpersonatePrivilege", TRUE)) {
/* get process handle with only the least access needed */
const HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
if (hProcess) {
if (impersonate_process(hProcess))
/* now we should be able to adjust the privileges of any target process */
if (disable_all_privileges(hProcess))
ok = TRUE;
CloseHandle(hProcess);
}
/* disable privileges we no longer need */
set_privilege(hCurrentToken, "SeImpersonatePrivilege", FALSE);
}
set_privilege(hCurrentToken, "SeDebugPrivilege", FALSE);
}
CloseHandle(hCurrentToken);
}
return ok;
}
int main(int argc, char* argv[])
{
DWORD pid = 0;
if (argc > 1)
sscanf_s(argv[1], "%u", &pid);
return pid && reduce_privileges(pid) ? 0 : 1;
}
UPDATE: Here is the improved reduce_privilege()
with Eryk's further suggestions. This way, we only enable and use the impersonate privilege if absolutely necessary.
int reduce_privileges(DWORD pid)
{
int ret = 1;
HANDLE hCurrentToken;
if (get_current_token(&hCurrentToken)) {
const BOOL debug_set = set_privilege(hCurrentToken, "SeDebugPrivilege", TRUE);
/* regardless of whether we got debug privilege, try to get process handle with the least access needed */
const HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
if (hProcess) {
/* see if we can disable privileges of target process without the impersonate privilege;
if we can't, enable that privilege, impersonate, and try again */
if (disable_all_privileges(hProcess))
ret = 0;
else
if (set_privilege(hCurrentToken, "SeImpersonatePrivilege", TRUE)) {
if (impersonate_process(hProcess))
/* now we should be able to adjust the privileges of any target process */
if (disable_all_privileges(hProcess))
ret = 0;
/* impersonate privilege no longer needed */
set_privilege(hCurrentToken, "SeImpersonatePrivilege", FALSE);
}
CloseHandle(hProcess);
}
/* debug privilege no longer needed */
if (debug_set)
set_privilege(hCurrentToken, "SeDebugPrivilege", FALSE);
CloseHandle(hCurrentToken);
}
return ret;
}
Upvotes: 1