plong
plong

Reputation: 1753

Why does OpenProcess() return ERROR_ACCESS_DENIED only for some processes?

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

Answers (1)

plong
plong

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

Related Questions