D

Deleted member 65228

Guest
#1

Introduction
Token Privileges are assigned to a process which helps determine if code executing can perform certain tasks or not. Tasks from the following list (but not limited to) are enforced by token privileges: accessing processes running on another user account (e.g. SYSTEM); loading device drivers; generation of audit-log entries; creation of a page file; creation of a symbolic link; impersonation of a client; shutdown the system; modification of the system time; appear from a trusted base. You can find the full list of privileges over on Microsoft Developer Network (MSDN): Privilege Constants (Windows)

A Windows process which is ran under the NT Authority Account (SYSTEM - Session 0) named lsass.exe is the Local System Authority Sub-System. It has many tasks however among holding opened handles to processes running on the system with sufficient access rights for virtual memory operations/thread creation, one of them is managing access tokens for processes. Lsass.exe is started by userinit.exe at system boot.


Documentation

Usually when someone wishes to adjust the token privileges of their process, they will call several APIs part of the Win32 API. These include: OpenProcessToken; LookupPrivilegeValue(W); and AdjustTokenPrivileges. The purpose of the call to OpenProcessToken is to open an access token for that process, the purpose of the call to LookupPrivilegeValue (defined for LookupPrivilegeValueW) is to retrieve the LUID (Locally Unique Identifier) for a privilege (e.g. SeDebugPrivilege which is for debugging rights), and the purpose of the call to AdjustTokenPrivileges is to actually adjust the token privileges to the new one using a modified structure of TOKEN_PRIVILEGES.

Behind the scenes, there is a lot more going on. When you call OpenProcessToken (ADVAPI32), it will result in a Native API function being invoked called NtOpenProcessToken. When you call LookupPrivilegeValue, it is actually a definition which will cause LookupPrivilegeValueW (ADVAPI32) to be called (LookupPrivilegeValue is not a function itself - just a definition for the Unicode version of the function) which will rely on multiple APIs starting with the Lsa* prefix. When you call AdjustTokenPrivileges (ADVAPI32), it will result in another Native API function being invoked called NtAdjustPrivilegesToken.

There is an Rtl* function (Rtl functions aren't supported by the kernel themselves because they do not actually perform any system calls within their own function stubs) called RtlAdjustPrivilege and this can be used instead of NtAdjustPrivilegesToken, however when you use it, it will still result in NtAdjustPrivilegesToken being called.

LookupPrivilegeValueW will call two Lsa* functions: LsaOpenPolicy and LsaLookupPrivilegeValue. Of course these will call other routines though, most likely internal non-exported functions though.


Source code demonstration

I have written an example source-code for demonstration on how you can adjust the token privileges for an privilege of your own process (locally) with NtOpenProcessToken, a custom wrapper for LookupPrivilegeValueW (calls LsaOpenPolicy and LsaLookupPrivilegeValue) and NtAdjustPrivilegesToken. For demonstration sake, I targeted SeDebugPrivilege for debugging rights. It also makes use of LsaClose, used to close a handle with data-type of LSA_HANDLE.

stdafx.h
Code:
#pragma once
#include <Windows.h>
#include <iostream>

#include "ntapi.h"
#include "tokenprivileges.h"
ntapi.h
Code:
#pragma once
#include "stdafx.h"

typedef _Return_type_success_(return >= 0) LONG NTSTATUS;
typedef NTSTATUS *PNTSTATUS;

#define STATUS_SUCCESS 0x00000000
#define STATUS_UNSUCCESSFUL 0xC0000001

#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)

#define NtCurrentProcess() ((HANDLE)-1)

typedef PVOID LSA_HANDLE, *PLSA_HANDLE;

typedef struct _LSA_UNICODE_STRING {
    USHORT Length;
    USHORT MaximumLength;
    PWSTR  Buffer;
} LSA_UNICODE_STRING, *PLSA_UNICODE_STRING, UNICODE_STRING, *PUNICODE_STRING;

typedef struct _OBJECT_ATTRIBUTES {
    ULONG           Length;
    HANDLE          RootDirectory;
    PUNICODE_STRING ObjectName;
    ULONG           Attributes;
    PVOID           SecurityDescriptor;
    PVOID           SecurityQualityOfService;
}  OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;

#define InitializeObjectAttributes(p, n, a, r, s) { \
    (p)->Length = sizeof(OBJECT_ATTRIBUTES); \
    (p)->RootDirectory = r; \
    (p)->Attributes = a; \
    (p)->ObjectName = n; \
    (p)->SecurityDescriptor = s; \
    (p)->SecurityQualityOfService = NULL; \
}

typedef VOID(WINAPI *pRtlInitUnicodeString)(
    PUNICODE_STRING DestinationString,
    PCWSTR SourceString
    );

typedef NTSTATUS(NTAPI *pNtOpenProcessToken)(
    HANDLE ProcessHandle,
    ACCESS_MASK DesiredAccess,
    PHANDLE TokenHandle
    );

typedef NTSTATUS(NTAPI *pLsaOpenPolicy)(
    PLSA_UNICODE_STRING SystemName,
    POBJECT_ATTRIBUTES ObjectAttributes,
    ACCESS_MASK DesiredAccess,
    PLSA_HANDLE PolicyHandle
);

typedef NTSTATUS(NTAPI *pLsaLookupPrivilegeValue)(
    LSA_HANDLE PolicyHandle,
    PUNICODE_STRING Name,
    PLUID Value
    );

typedef NTSTATUS(NTAPI *pLsaClose)(
    LSA_HANDLE ObjectHandle
    );

typedef NTSTATUS(NTAPI *pNtAdjustPrivilegesToken)(
    HANDLE TokenHandle,
    BOOLEAN DisableAllPrivileges,
    PTOKEN_PRIVILEGES TokenPrivileges,
    ULONG PreviousPrivilegesLength,
    PTOKEN_PRIVILEGES PreviousPrivileges OPTIONAL,
    PULONG RequiredLength OPTIONAL
    );
tokenprivileges.h
Code:
#pragma once
#include "stdafx.h"

BOOL EnableDebuggingRights();
main.cpp
Code:
#include "stdafx.h"
using namespace std;

int main()
{
    BOOL bStatus = EnableDebuggingRights();

    if (bStatus)
    {
        cout << "Debugging rights enabled\n";
    }
    else
    {
        cout << "Debugging rights could not be enabled\n";
    }

    getchar();
    return 0;
}
tokenprivileges.cpp
Code:
#include "stdafx.h"

HMODULE hNtdll = GetModuleHandle("NTDLL"),
        hSechost = LoadLibrary("SECHOST"),
        hAdvapi32 = LoadLibrary("ADVAPI32");

pRtlInitUnicodeString fRtlInitUnicodeString = (pRtlInitUnicodeString)GetProcAddress(hNtdll,
    "RtlInitUnicodeString");

pNtOpenProcessToken fNtOpenProcessToken = (pNtOpenProcessToken)GetProcAddress(hNtdll,
    "NtOpenProcessToken");

pLsaOpenPolicy fLsaOpenPolicy = (pLsaOpenPolicy)GetProcAddress(hSechost,
    "LsaOpenPolicy");

pLsaLookupPrivilegeValue fLsaLookupPrivilegeValue = (pLsaLookupPrivilegeValue)GetProcAddress(hAdvapi32,
    "LsaLookupPrivilegeValue");

pLsaClose fLsaClose = (pLsaClose)GetProcAddress(hSechost,
    "LsaClose");

pNtAdjustPrivilegesToken fNtAdjustPrivilegesToken = (pNtAdjustPrivilegesToken)GetProcAddress(hNtdll,
    "NtAdjustPrivilegesToken");

BOOL WrapperLookupPrivilegeValueW(
    WCHAR *lpSystemName,
    WCHAR *lpName,
    PLUID Luid
)
{
    NTSTATUS NtStatus = STATUS_SUCCESS;
    LSA_HANDLE LsaHandle = 0;
    OBJECT_ATTRIBUTES ObjectAttributes = { 0 };
    UNICODE_STRING usSystemName = { 0 },
        usName = { 0 };

    if (!fLsaOpenPolicy ||
        !fLsaLookupPrivilegeValue ||
        !fLsaClose)
    {
        return FALSE;
    }

    fRtlInitUnicodeString(&usSystemName,
        lpSystemName);

    fRtlInitUnicodeString(&usName,
        lpName);

    InitializeObjectAttributes(&ObjectAttributes,
        NULL,
        NULL,
        NULL,
        NULL);

    NtStatus = fLsaOpenPolicy(&usSystemName,
        &ObjectAttributes,
        2048,
        &LsaHandle);

    if (!NT_SUCCESS(NtStatus))
    {
        return FALSE;
    }

    NtStatus = fLsaLookupPrivilegeValue(LsaHandle,
        &usName,
        Luid);

    if (!NT_SUCCESS(NtStatus))
    {
        return FALSE;
    }

    fLsaClose(LsaHandle);

    return TRUE;
}

BOOL WrapperOpenProcessToken(
    HANDLE ProcessHandle,
    DWORD DesiredAccess,
    PHANDLE TokenHandle
)
{
    NTSTATUS NtStatus = STATUS_SUCCESS;

    if (!fNtOpenProcessToken)
    {
        return FALSE;
    }

    NtStatus = fNtOpenProcessToken(ProcessHandle,
        DesiredAccess,
        TokenHandle);

    return NtStatus == STATUS_SUCCESS ? TRUE : FALSE;
}

BOOL WrapperAdjustTokenPrivileges(
    HANDLE TokenHandle,
    BOOL DisableAllPrivileges,
    PTOKEN_PRIVILEGES NewState,
    DWORD BufferLength,
    PTOKEN_PRIVILEGES PreviousState,
    PDWORD ReturnLength
)
{
    NTSTATUS NtStatus = STATUS_SUCCESS;

    if (!fNtAdjustPrivilegesToken)
    {
        return FALSE;
    }

    NtStatus = fNtAdjustPrivilegesToken(TokenHandle,
        DisableAllPrivileges,
        NewState,
        BufferLength,
        PreviousState,
        ReturnLength);

    return NtStatus == STATUS_SUCCESS ? TRUE : FALSE;
}

BOOL EnableDebuggingRights()
{
    BOOL bStatus = FALSE;
    HANDLE TokenHandle = 0;
    TOKEN_PRIVILEGES TokenPrivileges = { 0 };
    LUID Luid = { 0 };

    bStatus = WrapperOpenProcessToken(NtCurrentProcess(),
        TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
        &TokenHandle);

    if (!bStatus)
    {
        goto cleanup;
    }

    bStatus = WrapperLookupPrivilegeValueW(NULL,
        L"SeDebugPrivilege",
        &Luid);

    if (!bStatus)
    {
        goto cleanup;
    }

    TokenPrivileges.PrivilegeCount = 1;
    TokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    TokenPrivileges.Privileges[0].Luid = Luid;

    bStatus = WrapperAdjustTokenPrivileges(TokenHandle,
        FALSE,
        &TokenPrivileges,
        sizeof(TOKEN_PRIVILEGES),
        NULL,
        NULL);

cleanup:
    if (TokenHandle)
    {
        CloseHandle(TokenHandle);
    }

    return bStatus;
}




After NtAdjustPrivilegesToken:




For the record, AdjustTokenPrivileges does not actually "exist" under ADVAPI32 - sure there is an export for it though. If you check the bytes at the function prologue, you'll see that they correspond to instructions for moving execution flow elsewhere (sechost.dll). Other functions are in the same boat, however this is why I target sechost.dll for some functions instead of advapi32.dll. The only exception in the demonstration source code was for LsaLookupPrivilegeValue, however the chances are that it does not actually exist there (you could check in a debugger if it moves execution flow elsewhere to wherever the function really exists). For a majority though, it is sechost.dll instead of advapi32.dll. Even for many service manager functions this is still valid.


Thank you for reading. :)
 

Attachments

Last edited by a moderator: