Skip to content
KitploitKITPLOIT
أدواتعمليات الاستغلالالمدونة
Log in
إرسال
أدواتعمليات الاستغلالالمدونة
إرسال

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

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

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

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
cve-2019-1458_POC — إثبات المفهوم لـ cve-2019-1458 | Kitploit
أدوات/GitHubGitHub/piotrflorczyk/cve-2019-1458_poc
تحليل الثغرات الأمنيةالاستغلالالهندسة العكسيةالتعلم والتعليماستغلال الملفات الثنائية
GitHubpiotrflorczyk/cve-2019-1458_poc

cve-2019-1458_POC

إثبات المفهوم لـ cve-2019-1458

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

الأكثر شعبية

عرض الكل →

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

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

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

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

CVE-2019-1458: الانتقال من 'تقرير الاستغلال في البرية' إلى POC

مقدمة

في ديسمبر نشرت كاسبرسكي تدوينة حول [استغلال 0day المستخدم في البرية][1]. أثار ذلك اهتمامي لأنه على الرغم من وصفهم لكيفية عمل الاستغلال، لم يقدموا أي POC في تحليلهم. لهذا قررت محاولة كتابة POC لهذه الثغرة بالاعتماد على تدوينة كاسبرسكي وتحليل التصحيح.
تصف هذه التدوينة رحلتي في فعل ذلك.

جمع المعلومات:

أول شيء كان جمع أكبر قدر ممكن من المعلومات حول هذه الثغرة. بقراءة التدوينة المذكورة استخرجت المعلومات التالية:

  • الثغرة مرتبطة بوظيفة تبديل النوافذ
  • تتطلب محاكاة ضغطات مفتاح ALT لتفعيلها
  • يجب أن يكون هناك استدعاءان لواجهة برمجة التطبيقات غير الموثقة NtUserMessageCall
  • يجب إنشاء نافذة تبديل خاصة
  • كانت هناك إشارة إلى دالة النواة win32k!DrawSwitchWndHilite

بالإضافة إلى ذلك، هناك لقطة شاشة جميلة للكود المُفكك تُظهر بعض الأشياء المذكورة سابقًا. على وجه الدقة، تُظهر: إنشاء نافذة التبديل، استدعاء دالة باسم toggle_alt_key واستدعاءات متعددة لـ NtUserMessageCall.

جزء من كود الاستغلال المُفكك [مصدر الصورة][1]

الكثير من المعلومات المفيدة، لكنها لا تزال لا تصف كيفية عمل هذه الثغرة بالضبط وكيفية تفعيلها.

مقارنة التصحيحات

[الوحدة المتأثرة كانت win32k.sys][2]. قمت بتحميل النسختين المصححة وغير المصححة من هذه الوحدة.
بالنسبة لـ Win7 x64 كانت هاتان:

  • المصححة: KB4530692
  • غير المصححة: KB4525233

يمكن تنزيلهما من [Microsoft Update Catalog][3]

إليك نتيجة bindiff لمقارنة النسختين

مقارنة win32k

بعد استبعاد الدوال المرتبطة بوظيفة DebugHook، كل ما يتبقى لنا حقًا هو هذه الدالة المتغيرة قليلًا InitFunctionTables()

تغييرات InitFunctionTables

بالتأكيد ليس أكبر تصحيح في العالم.
لن يساعد هذا في تحديد السبب الجذري لهذه الثغرة فورًا. لكن من الجدير بالملاحظة أنه تمت إضافة بعض القيم الأولية للمتغيرات عند *(gpsi+0x14E), *(gpsi+0x154), *(gpsi+0x180). لذلك قد يكون هذا خطأ متعلقًا بمتغير غير مهيأ.

بناء POC - خطوة بخطوة

في هذا القسم سأعرض كيف بنيت POC الذي يفعّل هذه الثغرة تدريجيًا، مع اكتشاف ماهية الثغرة في الوقت نفسه.

من أين نبدأ

لم تقدم مقارنة التصحيحات الكثير من المعلومات المفيدة في البداية، لذلك اعتمدت بشكل أساسي على تدوينة كاسبرسكي في المرحلة الأولى من التطوير.
للحصول على بيئة اختبار جيدة، أعددت جهازًا افتراضيًا (VM) بنظام Win7 SP1 x64 يعمل بأحدث نسخة معرضة للثغرة من win32k. بالإضافة إلى ذلك، ربطت Windbg بهذا الجهاز الافتراضي للقيام بتصحيح أخطاء النواة، وأثناء ذلك قمت أيضًا بإعداد مسار خادم الرموز.
بدأت تحقيقي بالنظر إلى win32k!DrawSwitchWndHilite التي ذُكرت في التدوينة. يتم استدعاؤها من مكانين: xxxMoveSwitchWndHilite و xxxPaintSwitchWindow، والأخيرة لفتت انتباهي فورًا بسبب استدعاءات GetKeyState/GetAsyncKeyState المحيطة بها والتي ذُكرت في التقرير الأصلي. والأكثر من ذلك أن هذه الاستدعاءات تتحقق من كون مفتاح ALT مضغوطًا.

موقع استدعاء مثير للاهتمام لـ DrawSwitchWndHilite
استدعاء DrawSwitchWndHilite من xxxPaintSwitchWindow

بمتابعة مراجع الاستدعاء المتقاطعة (xxxWrapSwitchWndProc->xxxSwitchWndProc->xxxPaintSwitchWindow->DrawSwitchWndHilite) وجدت أن العنصر الأول في تلك السلسلة مُشار إليه في InitFunctionTables، الدالة التي تم إصلاحها في التصحيح.

بعد ذلك نظرت في NtUserMessageCall من لقطة شاشة الكود المُفكك. إليك تعريف هذه الدالة```cpp NtUserMessageCall(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, ULONG_PTR ResultInfo, DWORD dwType, BOOLEAN bAnsi)

يستدعيها الاستغلال مع `msg = 0x14` و `dwType = 0xE0`. لنرَ ما الذي يفعله.```cpp
HINSTANCE hInstance = GetModuleHandle(NULL);
WNDCLASSEX wcx;
ZeroMemory(&wcx, sizeof(wcx));
wcx.hInstance = hInstance;
wcx.cbSize = sizeof(wcx);
wcx.lpszClassName = L"SploitWnd";
wcx.lpfnWndProc = DefWindowProc;

printf("[*] Registering window\n");
ATOM wndAtom = RegisterClassEx(&wcx);
if (wndAtom == INVALID_ATOM) {
    printf("[-] Failed registering SploitWnd window class\n");
    exit(-1);
}

printf("[*] Creating instance of this window\n");
HWND sploitWnd = CreateWindowEx(0, L"SploitWnd", L"", 0, 0, 0, 0, 0, NULL, NULL, hInstance, NULL);
if (sploitWnd == INVALID_HANDLE_VALUE) {
    printf("[-] Failed to create SploitWnd window\n");
    exit(-1);
}
NtUserMessageCall(sploitWnd, WM_ERASEBKGND, 0, 0, 0, 0xE0, 1);

هنا قمت بتسجيل فئة نافذة بسيطة وأنشأت نافذة من تلك الفئة. ثم استدعيت NtUserMessageCall بنفس معاملات الاستغلال. لمعرفة ما يحدث داخل الكود، قمت بتعيين نقطة توقف kd> ba e 1 win32k!NtUserMessageCall وتشغيل الكود. يتم إجراء عدد غير قليل من الاستدعاءات لهذه الدالة، لذا كان عليّ التقاط الاستدعاء الصحيح، لكن الأمر لم يكن بهذه الصعوبة؛ كان الاستدعاء ذا مكدس استدعاء قصير جدًا.

NtUserMessageCall
NtUserMessageCall

عند التنقل في الكود تبيّن أنها تستدعي دالة من مصفوفة gapfnMessageCall؛ يُحسب الفهرس بناءً على قيمة msg ويساوي 0، لذلك يتم الاستدعاء إلى NtUserfnDWORD.

NtUserfnDWORD
NtUserfnDWORD

الاستدعاء التالي يتم باستخدام قيمة dwType، والآن إزاحة gpsi تساوي 0x40، ويؤدي الاستدعاء إلى xxxWrapSwitchWndProc (هذه الدالة ظهرت بالفعل عندما كنت أفحص سلسلة استدعاءات DrawSwitchWndHilite).
xxxWrapSwitchWndProc تستدعي ببساطة xxxSwitchWndProc.

xxxSwitchWndProc
xxxSwitchWndProc

وهذه هي النهاية؛ يفشل الكود هنا ولا يتقدم أكثر إلى xxxPaintSwitchWindow، وهو المكان الذي نريد الوصول إليه بناءً على قيمة msg (0x14). دعنا نتحقق من السبب.

تفعيل المسار الصحيح

يفشل الكود في هذه المرحلة لأنه، كما هو موضح في الصورة السابقة، قيمة fnid لنافذتنا لا تساوي 0x2A0 (FNID_SWITCH) والرسالة التي نرسلها لا تساوي 1، وبالتالي ننتهي في xxxDefWindowProc. لتجنب هذا السيناريو، يجب علينا استدعاء xxxSwitchWndProc مع ضبط قيمة fnid إلى FNID_SWITCH، حتى ننتقل مباشرة إلى جملة switch ثم إلى xxxPaintSwitchWindow.
كيف نضبط قيمة fnid الصحيحة؟ في الواقع، الدالة نفسها تقوم بذلك في كتلة if الأولى؛ كل ما علينا فعله هو إفشال جميع الفحوصات داخلها للوصول إلى التعليمة التي تضبط fnid.

إليك الشروط التي يجب تحقيقها لإفشال فحوصات if الثلاثة:

  • fnid == 0 و cbwndExtra + 0x128 >= *(gpsi + 0x154)
    قيمة fnid تساوي 0 لكل نافذة مستخدم منشأة حديثًا. *(gpsi+0x154) تساوي 0 في win32k! غير المُرقّع. ولكن حتى لو تم تعيينها إلى 0x130، كما في الإصدار المُرقّع، يمكننا ضبط cbwndExtra إلى 8 أو أكثر وما زال بإمكاننا تجاوز الفحص الأول.
  • msg == 1
    يمكن تعيين ذلك في استدعاء NtUserMessageCall. ومع ذلك، مع ضبط msg إلى 1، يمر تدفق التحكم عبر NtUserfnINLPCREATESTRUCT بدلاً من NtUserfnDWORD، لكنه لا يزال ينتهي في xxxSwitchWndProc.
  • extraData == 0
    يمكن ضبط حجم ExtraData عند تسجيل فئة النافذة باستخدام cbwndExtra المذكور. يتم إلحاق ExtraData مباشرة بعد بنية tagWND (أضفت هذا الحقل إلى بنية tagWND في IDA كـ QWORD عند الإزاحة sizeof(tagWND)، لجعل الكود المُفكك أفضل قليلًا). يمكن تعيين قيمته عبر استدعاء SetWindowLongPtr.

إذا تحققت جميع هذه الشروط، سيتم تعيين قيمة fnid للنافذة إلى FNID_SWITCH.
إذن نحتاج الآن إلى استدعاء NtUserMessageCall مرتين: الأولى بقيمة msg تساوي 1 لتعيين fnid المطلوب، والثانية للوصول إلى xxxPaintSwitchWindow.```cpp HINSTANCE hInstance = GetModuleHandle(NULL); WNDCLASSEX wcx; ZeroMemory(&wcx, sizeof(wcx)); wcx.hInstance = hInstance; wcx.cbSize = sizeof(wcx); wcx.lpszClassName = L"SploitWnd"; wcx.lpfnWndProc = DefWindowProc; wcx.cbWndExtra = 8; //to pass check in xxxSwitchWndProc

تنزيل الأداة