Red Team Notes logo Red Team Notes

It is common to some defenses to detect patterns, one typicall is create a handle of a remote process. The idea is to enumerate them and find a process who has full access 0x1fffff to a process, use that handle to inject our code.

NtQuerySystemInformation (NativeAPI)

The NtQuerySystemInformation function can return a list of all open handles in the system, including those associated with a specific process. This technique uses the SystemHandleInformation (code 16) to obtain information about handles.

__kernel_entry NTSTATUS NtQuerySystemInformation(
  [in]            SYSTEM_INFORMATION_CLASS SystemInformationClass,
  [in, out]       PVOID                    SystemInformation,
  [in]            ULONG                    SystemInformationLength,
  [out, optional] PULONG                   ReturnLength
);

Since SYSTEM_HANDLE_INFORMATION is not defined we should define the structs.

typedef struct _SYSTEM_HANDLE {
    ULONG ProcessId;
    BYTE ObjectTypeNumber;
    BYTE Flags;
    USHORT Handle;
    PVOID Object;
    ACCESS_MASK GrantedAccess;
} SYSTEM_HANDLE, *PSYSTEM_HANDLE;

typedef struct _SYSTEM_HANDLE_INFORMATION {
    ULONG HandleCount;
    SYSTEM_HANDLE Handles[1];
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;

There are different types of Handles (directory, token, file, events, keys…) each one has a different ObjectTypeNumer.

ObjectTypeNumber ObjectType Description
0x07 Process Open process
0x08 Thread Tread of a process
0x05 Token Security token
0x25 File Open file
0x2C Key Windows registry key
0x0D FileMap File mapping (shared memory)
0x03 Directory Directory object

To query information of a handle of other process we need to duplicate it NtDuplicateObject on the current process and query it NtQueryObject.

Here an example of how to list all the handles of a specified PID process:

#include <Windows.h>
#include <stdio.h>
#include <winternl.h>

#define SystemHandleInformation 16
#define ObjectTypeInformation 2

#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)

#define HANDLE_TOKEN 0x04

typedef struct _SYSTEM_HANDLE {
    ULONG ProcessId;
    BYTE ObjectTypeNumber;
    BYTE Flags;
    USHORT Handle;
    PVOID Object;
    ACCESS_MASK GrantedAccess;
} SYSTEM_HANDLE, *PSYSTEM_HANDLE;

typedef struct _SYSTEM_HANDLE_INFORMATION {
    ULONG HandleCount;
    SYSTEM_HANDLE Handles[1024];
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;


typedef enum _POOL_TYPE {
    NonPagedPool,
    PagedPool,
    NonPagedPoolMustSucceed,
    DontUseThisType,
    NonPagedPoolCacheAligned,
    PagedPoolCacheAligned,
    NonPagedPoolCacheAlignedMustS
} POOL_TYPE, *PPOOL_TYPE;

typedef struct _OBJECT_TYPE_INFORMATION {
    UNICODE_STRING Name;
    ULONG TotalNumberOfObjects;
    ULONG TotalNumberOfHandles;
    ULONG TotalPagedPoolUsage;
    ULONG TotalNonPagedPoolUsage;
    ULONG TotalNamePoolUsage;
    ULONG TotalHandleTableUsage;
    ULONG HighWaterNumberOfObjects;
    ULONG HighWaterNumberOfHandles;
    ULONG HighWaterPagedPoolUsage;
    ULONG HighWaterNonPagedPoolUsage;
    ULONG HighWaterNamePoolUsage;
    ULONG HighWaterHandleTableUsage;
    ULONG InvalidAttributes;
    GENERIC_MAPPING GenericMapping;
    ULONG ValidAccess;
    BOOLEAN SecurityRequired;
    BOOLEAN MaintainHandleCount;
    USHORT MaintainTypeList;
    POOL_TYPE PoolType;
    ULONG PagedPoolUsage;
    ULONG NonPagedPoolUsage;
} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION;

typedef NTSTATUS (NTAPI * t_NtQuerySystemInformation)(
    SYSTEM_INFORMATION_CLASS SystemInformationClass,
    PVOID SystemInformation,
    ULONG SystemInformationLength,
    PULONG ReturnLength
);
typedef NTSTATUS (NTAPI * t_NtQueryObject)(
    HANDLE Handle,
    ULONG ObjectInformationClass,
    PVOID ObjectInformation,
    ULONG ObjectInformationLength,
    PULONG ReturnLength
);

typedef NTSTATUS (NTAPI * t_NtDuplicateObject)(
    HANDLE SourceProcessHandle,
    HANDLE SourceHandle,
    HANDLE TargetProcessHandle,
    PHANDLE TargetHandle,
    ACCESS_MASK DesiredAccess,
    ULONG Attributes,
    ULONG Options
);

int ListHandles(int pid) {
    DWORD sysHandleSize = 0x10000;
    SYSTEM_HANDLE_INFORMATION * sysHandle;
    NTSTATUS status;

    DWORD hinfoSize = 0x100;
    POBJECT_TYPE_INFORMATION hInfo;
    NTSTATUS status2;

    HANDLE hProc;
    HANDLE dupHandle;

    // Link NtAPIS 
    t_NtQuerySystemInformation pNtQuerySystemInformation;
    pNtQuerySystemInformation = (t_NtQuerySystemInformation)GetProcAddress(GetModuleHandle("ntdll.dll"),"NtQuerySystemInformation");
    t_NtDuplicateObject pNtDuplicateObject;
    pNtDuplicateObject = (t_NtDuplicateObject) GetProcAddress(GetModuleHandle("ntdll.dll"), "NtDuplicateObject");
    t_NtQueryObject pNtQueryObject;
    pNtQueryObject =(t_NtQueryObject)GetProcAddress(GetModuleHandle("ntdll.dll"),"NtQueryObject");

    // Get INITIAL size
    sysHandle = (SYSTEM_HANDLE_INFORMATION*)malloc(sysHandleSize);
    while((status = pNtQuerySystemInformation((SYSTEM_INFORMATION_CLASS) SystemHandleInformation, sysHandle, sysHandleSize, NULL))==STATUS_INFO_LENGTH_MISMATCH){
        sysHandle = (SYSTEM_HANDLE_INFORMATION *)realloc(sysHandle,sysHandleSize*=2);
    }

    printf("PID\tAddress\t\tObjectTypeNumber\tGrantedAccess\tObjectType\n");
    printf("---------------------------------------------------\n");
    for (int i=0; i<sysHandle->HandleCount; i++){
        SYSTEM_HANDLE handle = sysHandle->Handles[i];
        if(handle.ProcessId == pid){
            // Duplicate the handle to query it
            hProc = OpenProcess(PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION, FALSE, handle.ProcessId);
            if((pNtDuplicateObject(hProc, (void *) handle.Handle, GetCurrentProcess(), &dupHandle, 0, 0, DUPLICATE_SAME_ACCESS)) < 0){
                continue;
            }
            // Get info about the handle
            hInfo = (OBJECT_TYPE_INFORMATION *)malloc(hinfoSize);
            while((status2 = pNtQueryObject((HANDLE)dupHandle, ObjectTypeInformation, hInfo, hinfoSize, &hinfoSize))==STATUS_INFO_LENGTH_MISMATCH){
                hInfo = (OBJECT_TYPE_INFORMATION *)realloc(hInfo, hinfoSize*=2);
            }
            UNICODE_STRING typeName = (UNICODE_STRING)hInfo->Name;
            wprintf(L"%#5d\t%#10p\t%#16x\t%#13x\t%.*s\n", handle.ProcessId, handle.Object, handle.ObjectTypeNumber,handle.GrantedAccess,typeName.Length/sizeof(WCHAR), typeName.Buffer);    
        }
    }	
	return 0;
}

int main(int argc, char ** argv) {
    
    int pid = 5816;
    ListHandles(pid);
    return 0;
}