Process authorization of process injection

Author: Gality
Number of words in this article: 7928 words
Reading duration: 20 minutes
This article belongs to the original reward plan of [wolf group safety community], and it is prohibited to reprint it without permission

Any direct or indirect consequences and losses caused by the dissemination and use of the information provided in this article shall be borne by the user himself, and the wolf group security team and the author of the article shall not bear any responsibility for this.
Wolf group security team has the right to modify and explain this article. If you want to reprint or disseminate this article, you must ensure the integrity of this article, including the copyright statement and other contents. Without the permission of wolf group security team, the content of this article shall not be modified or increased or decreased arbitrarily, and it shall not be used for commercial purposes in any way.

Recently, relevant things have been injected into the research process, mainly for reference process-inject Project, want to realize all the above process injection methods, and find out the principle. In process injection, the current process needs to be authorized first, so as to obtain the write permission to the memory of other processes and complete process injection. Therefore, this article mainly focuses on the relevant knowledge of process injection.

Conventional power raising method

The first is the conventional process right raising method. You can not understand the meaning of the following code, but only need to obtain it in the process SeDebugPrivilege Copy the code and call it at the beginning to get permission
BOOL EnableDebugPrivilege() {
    HANDLE TokenHandle = NULL;
    TOKEN_PRIVILEGES TokenPrivilege;

    LUID uID;
    if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &TokenHandle)) {
        if (LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &uID)) {
            TokenPrivilege.PrivilegeCount = 1;
            TokenPrivilege.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
            TokenPrivilege.Privileges[0].Luid = uID;
            if (AdjustTokenPrivileges(TokenHandle, FALSE, &TokenPrivilege, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) {
                CloseHandle(TokenHandle);
                TokenHandle = INVALID_HANDLE_VALUE;
                return TRUE;
            }
            else
                goto Fail;

        }
        else
            goto Fail;
    }
    else
        goto Fail;

Fail:
    CloseHandle(TokenHandle);
    TokenHandle = INVALID_HANDLE_VALUE;
    return FALSE;
}

explain

During process injection, any process (including system security process and service process) should be specified**Write correlation**Of access rights OpenProcess Operation, as long as the current process has SeDeDebug Permission is OK. If a user is Administrator Or given the corresponding permission, you can have the permission. But even if we use Administrator The account is used to execute a system security process OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwProcessID)You will still encounter an "access denied" error.

This is because some access permissions of the process are not Enabled by default, so the first thing we need to do is to enable these permissions. Some API functions related to this include OpenProcessToken, LookupPrivilegevalue and AdjustTokenPrivileges. If we want to modify the access token of a process

windows After each user logs into the system, the system will generate an access token( access token),It is associated with the permission information of the current user. Each process created after the user logs in contains the user access token When a process tries to perform some operations that require special permissions or access protected kernel objects, the system will check it acess token To determine whether to authorize the operation.

The access token of members of the Administrator group contains some privileges that can perform system level operations, such as terminating any process, shutting down / restarting the system, loading device drivers and changing system time. However, these privileges are disabled by default. When the process created by members of the Administrator group contains some operations that require privileges, The process must first open these disabled privileges to enhance its permissions, otherwise the system will refuse the operation of the process. Note that processes created by non members of the Administrator group cannot elevate their permissions

First, obtain the handle of the process access token, which can be obtained through OpenProcessToken. The prototype of the function is as follows:

BOOL OpenProcessToken(
  HANDLE  ProcessHandle, //Process handle to modify access rights
  DWORD   DesiredAccess, //Specify the type of operation you want to perform. If you want to modify the token, we need to specify the second parameter as TOKEN_ADJUST_PRIVILEGES
  PHANDLE TokenHandle    //Returned access token pointer
);
 Through this function, we can get the handle of the access token of the current process (the first parameter of the specified function is GetCurrentProcess()It's OK). Then we can call AdjustTokenPrivileges Modify this access token. AdjustTokenPrivileges The prototype is as follows:
BOOL AdjustTokenPrivileges(
  HANDLE            TokenHandle,           //Handle to access token
  BOOL              DisableAllPrivileges,  //Decide whether to modify permissions or Disable all permissions
  PTOKEN_PRIVILEGES NewState,              //Indicates the permission to modify. It is a pointer to token_ Pointer to the privileges structure, which contains an array. Each item of the data group indicates the type of permission and the operation to be performed
  DWORD             BufferLength,          //The length of the structure PreviousState. If the PreviousState is empty, the parameter should be NULL
  PTOKEN_PRIVILEGES PreviousState,         //It is also a pointer to token_ The pointer of the privileges structure, which stores the information of the access permission before modification. It can be null
  PDWORD            ReturnLength           //The size returned for the actual PreviousState structure
);
 There are two data structures to be mentioned here. One is PTOKEN_PRIVILEGES,The structure is defined as follows:
typedef struct _TOKEN_PRIVILEGES {
  DWORD               PrivilegeCount;              //Refers to the number of array elements
  LUID_AND_ATTRIBUTES Privileges[ANYSIZE_ARRAY];   //A luid_ AND_ Array of types attributes
} TOKEN_PRIVILEGES, *PTOKEN_PRIVILEGES;
 So let's move on to the second structure, which is LUID_AND_ATTRIBUTES Structure:
typedef struct _LUID_AND_ATTRIBUTES {
  LUID  Luid;              //The type of permission is a value of LUID
  DWORD Attributes;        //Indicates the type of operation to be performed. There are three options: SE_PRIVILEGE_ENABLED,SE_PRIVILEGE_ENABLED_BY_DEFAULT,SE_PRIVILEGE_USED_FOR_ACCESS
} LUID_AND_ATTRIBUTES, *PLUID_AND_ATTRIBUTES;
Luid Means locally unique identifier,contrast Guid,that is global unique identifier,and GUID The requirements of ensure that the overall situation is unique, LUID As long as local uniqueness is guaranteed, it means that it is guaranteed to be unique during each operation of the system.

In addition, the same as GUID, luid is also a 64 bit value. You can query the luid value corresponding to a permission through the API LookupPrivilegeValue:

BOOL LookupPrivilegeValueA(
  LPCSTR lpSystemName,  //The name of the system. If it is a local system, just indicate NULL
  LPCSTR lpName,        //Indicates the name of the permission, such as "SeDebugPrivilege"
  PLUID  lpLuid         //Returns a pointer to the LUID
);

Winnt.h also defines macros with other permission names, such as:

#define SE_CREATE_TOKEN_NAME                         TEXT("SeCreateTokenPrivilege")
#define SE_ASSIGNPRIMARYTOKEN_NAME                   TEXT("SeAssignPrimaryTokenPrivilege")
#define SE_LOCK_MEMORY_NAME                          TEXT("SeLockMemoryPrivilege")
#define SE_INCREASE_QUOTA_NAME                       TEXT("SeIncreaseQuotaPrivilege")
#define SE_UNSOLICITED_INPUT_NAME                    TEXT("SeUnsolicitedInputPrivilege")
#define SE_MACHINE_ACCOUNT_NAME                      TEXT("SeMachineAccountPrivilege")
#define SE_TCB_NAME                                  TEXT("SeTcbPrivilege")
#define SE_SECURITY_NAME                             TEXT("SeSecurityPrivilege")
#define SE_TAKE_OWNERSHIP_NAME                       TEXT("SeTakeOwnershipPrivilege")
#define SE_LOAD_DRIVER_NAME                          TEXT("SeLoadDriverPrivilege")
#define SE_SYSTEM_PROFILE_NAME                       TEXT("SeSystemProfilePrivilege")
#define SE_SYSTEMTIME_NAME                           TEXT("SeSystemtimePrivilege")

....
//For a complete list of privileges, please refer to the security chapter of msdn

By calling these three functions, we can use OpenProcess to open the handle of any process and specify write access for process injection

Advanced power raising technique

The advanced rights raising method involves a system that has not been disclosed by Microsoft API,Which means you're msdn I can't find him on the Internet. It's encapsulated in ntdll In (all programs will load the Dll),That is, the legendary RtlAdjustPrivilege. 

Function prototype:

NTSTATUS RtlAdjustPrivilege
(
    ULONG Privilege,   // The required permission name can be found in MSDN. You can find the content about process token & privilege
    BOOLEAN Enable,    // If True, the corresponding permission is opened; if False, the corresponding permission is closed
    BOOLEAN CurrentThread,  // If True, only the permissions of the current thread will be promoted; otherwise, the permissions of the whole process will be promoted
    PBOOLEAN Enabled   // Output the status of the original corresponding permission (open | close)
)

We can analyze the function through IDA analysis ntdll. First analyze the general process without relying on F5:
![](https://img-blog.csdnimg.cn/img_convert/11e154a411814399f0cc960f88de23bc.png#clientId=u5614b3a2-1fdd-4&from=paste&height=619&id=u8f7621aa&margin=[object Object]&originHeight=619&originWidth=1024&originalType=url&status=done&style=none&taskId=u073b3ffe-cd60-48a4-8a6f-9a2381148d6&width=1024)
The definitions of the two native functions used are as follows:

pub unsafe extern "system" fn NtOpenThreadToken(
    ThreadHandle: HANDLE, 
    DesiredAccess: ACCESS_MASK, 
    OpenAsSelf: BOOLEAN, 
    TokenHandle: PHANDLE
) -> NTSTATUS

pub unsafe extern "system" fn ZwOpenProcessToken(
    ProcessHandle: HANDLE, 
    DesiredAccess: ACCESS_MASK, 
    TokenHandle: PHANDLE
) -> NTSTATUS

After obtaining the token, you can upgrade the permission
![](https://img-blog.csdnimg.cn/img_convert/888fd28e32188788a94f434e191cccdb.png#clientId=u5614b3a2-1fdd-4&from=paste&height=594&id=u4f12d909&margin=[object Object]&originHeight=594&originWidth=1024&originalType=url&status=done&style=none&taskId=uba2730e9-a823-4e6b-8e30-f8c11f73904&width=1024)
Among them, the function prototype of ZwAdjustPrivilegesToken is:

pub unsafe extern "system" fn ZwAdjustPrivilegesToken(
    TokenHandle: HANDLE, 
    DisableAllPrivileges: BOOLEAN, 
    NewState: PTOKEN_PRIVILEGES, 
    BufferLength: ULONG, 
    PreviousState: PTOKEN_PRIVILEGES, 
    ReturnLength: PULONG
) -> NTSTATUS

Later, the status of the original corresponding permission will be assigned to the enabled address
![](https://img-blog.csdnimg.cn/img_convert/465c6e3789d9e0e121f124a4c8702b39.png#clientId=u5614b3a2-1fdd-4&from=paste&height=625&id=u4ea0548e&margin=[object Object]&originHeight=625&originWidth=730&originalType=url&status=done&style=none&taskId=ub883c3f3-e186-4380-9388-0840d4972e6&width=730)
This is the general function. After the analysis, you can F5 look at the function:

int __stdcall RtlAdjustPrivilege(int Privilege, bool Enable, char CurrentThread, int *Enabled)
{
  int result; // eax
  int v5; // esi
  int isEnabled; // [esp+8h] [ebp-2Ch] BYREF
  HANDLE TokenHandle; // [esp+Ch] [ebp-28h] BYREF
  int OldState[3]; // [esp+10h] [ebp-24h] BYREF
  int v9; // [esp+1Ch] [ebp-18h]
  int NewState; // [esp+20h] [ebp-14h] BYREF
  int dwPrivilege; // [esp+24h] [ebp-10h]
  int v12; // [esp+28h] [ebp-Ch]
  int v13; // [esp+2Ch] [ebp-8h]

  if ( CurrentThread == 1 )
    result = NtOpenThreadToken(-2, 40, 0, &TokenHandle);
  else
    result = ZwOpenProcessToken(-1, 40, &TokenHandle);
  if ( result >= 0 )
  {
    dwPrivilege = Privilege;
    NewState = 1;
    v12 = 0;
    v13 = Enable ? 2 : 0;
    v5 = ZwAdjustPrivilegesToken((int)TokenHandle, 0, (int)&NewState, 16, (int)OldState, (int)&isEnabled);
    NtClose(TokenHandle);
    if ( v5 == 0x106 )
      v5 = 0xC0000061;
    if ( v5 >= 0 )
    {
      if ( OldState[0] )
        *(_BYTE *)Enabled = (v9 & 2) != 0;      // Actually (oldstate. Privileges [0]. Attributes & se_privilege_enabled)
      else
        *(_BYTE *)Enabled = Enable;
    }
    result = v5;
  }
  return result;
}

Relatively speaking, it is very clear, so how to use this function to raise the weight? Here is a demo code:

#include <iostream>
#include <windows.h>
#include <string>

using namespace std;

const unsigned long SE_DEBUG_PRIVILEGE = 0x13;
typedef int(_stdcall *_RtlAdjustPrivilege)(int, BOOL, BOOL, int *);

int main(int argc, char* argv[])
{
     HMODULE hNtDll = LoadLibrary("NTDLL.dll"); //Import ntdll
     if (!hNtDll)
         cout << "Error.." << endl;
     _RtlAdjustPrivilege pfnRtlAdjustPrivilege = (_RtlAdjustPrivilege)GetProcAddress(hNtDll, "RtlAdjustPrivilege");//Get the address of RtlAdjustPrivilege function
 
     int nEn = 0;
     pfnRtlAdjustPrivilege(SE_DEBUG_PRIVILEGE, TRUE, FALSE, &nEn); //Use this function

    return 0;
}

Added by anfo on Wed, 02 Feb 2022 07:57:59 +0200