
استغلال لإثبات المفهوم لـ CVE-2026-29923، وهو تصعيد امتياز BYOVD في pstrip64.sys. يُظهر قراءة/كتابة الذاكرة الفعلية عبر IOCTL لسرقة رمز SYSTEM وإنشاء شل مرتفع الصلاحية.
إخلاء مسؤولية: هذا الكود مقدم لأغراض تعليمية وبحثية دفاعية فقط. تم كتابته لتعزيز فهم استغلال النواة ومساعدة المدافعين في حماية بيئاتهم من ثغرات مماثلة. أي استخدام غير مصرح به أو غير قانوني أو خبيث لهذا المشروع ممنوع منعًا باتًا.
التجزئة (Hash): ab01485bb7c8bc1a9c86096eeea6d31d8fad557bf4d44072b46373d2203faa6e
اسم المُشغّل (Driver): pstrip64.sys
CVE: CVE-2026-29923
هجوم "إحضار مُشغّل ثغري خاص بك" (BYOVD) هو أسلوب قديم لكنه فعال جدًا يستخدمه المهاجمون لتجاوز حماية الأمان الحديثة في ويندوز، وذلك باستخدام مُشغّل قديم لا يزال نظام التشغيل يثق به رسميًا. بمجرد تحميل المُشغّل، يستغل المهاجم نقاط ضعفه لسد الفجوة بين عملية قياسية غير مُمتازة والتحكم الكامل على مستوى النظام.
في وقت سابق من هذا الأسبوع، تم الكشف عن ثغرة جديدة في المُشغّل pstrip64.sys، مُسجّلة باسم CVE-2026-29923. هذه المقالة توضح دورة حياة الاستغلال بالكامل: من بحثي الأولي عن الثغرة وتطوير الإثبات العملي (PoC) إلى استراتيجيات التخفيف القابلة للتنفيذ للمدافعين الذين يؤمّنون بيئاتهم.
المُشغّل pstrip64.sys هو مكوّن نواة قديم مرتبط بـ EnTech Taiwan PowerStrip (حتى الإصدار 3.90.736). بينما الغرض الشرعي منه هو تمكين تعديل إعدادات شاشة بطاقة الرسوميات المتقدمة، فإن صلاحياته العميقة على النظام تجعله هدفًا جذابًا جدًا للمهاجمين.
عندما تم الكشف عن الثغرة لأول مرة، بدأت بتحليل دالة DriverEntry الخاصة بها. تعمل هذه الدالة كإجراء التهيئة الرئيسي لمُشغّل النواة، حيث تنشئ كائن الجهاز \Device\PSTRIP64 وتُعرّضه لتطبيقات وضع المستخدم عبر الرابط الرمزي \DosDevices\PSTRIP64. والأهم من ذلك، أنها تُهيئ جدول إرسال المُشغّل. الإدخال الذي لفت انتباهي فورًا كان في الفهرس 14 (IRP_MJ_DEVICE_CONTROL)، والذي يُوجّه جميع طلبات IOCTL المقدمة من المستخدم مباشرة إلى دالة المعالج sub_11340، وهي منطقة اهتمامنا الأساسية.
تعمل الدالة sub_11340 كموزّع IOCTL رئيسي، حيث تفسّر الطلبات القادمة من وضع المستخدم.
من بين جميع IOCTLs المكشوفة، فإن 0x80002008 هو بلا شك الأكثر إثارة للاهتمام. بينما تتعامل الحالات الافتراضية مع تفاعلات منافذ الإدخال/الإخراج البسيطة، فإن 0x80002008 يعمل كبوابة إلى sub_11000، وذلك بتمرير SystemBuffer مباشرة إلى هذه الدالة.
هذه الروتين sub_11000 هو الدليل القاطع. أولاً، تستخدم HalTranslateBusAddress لأخذ العنوان المُقدم من المستخدم وترجمته إلى عنوان فيزيائي صالح للنظام. ثم، تفتح \Device\PhysicalMemory وتُخططه باستخدام ZwMapViewOfSection. بتحديد مقبض العملية الهدف بشكل ثابت إلى (HANDLE)0xFFFFFFFFFFFFFFFFLL (الذي يمثل ZwCurrentProcess())، يُخطط المُشغّل هذه الذاكرة الفيزيائية مباشرة في مساحة العنوان الافتراضية لعملية الاستدعاء. والأهم من ذلك، أنه يكتب هذا العنوان الافتراضي المُخطط حديثًا مرة أخرى إلى SystemBuffer لإعادته إلى المستخدم، مما يُسلّم تطبيقنا مؤشرًا مباشرًا لقراءة وكتابة الذاكرة الفيزيائية.
مع فهم الثغرة بالكامل والحصول على أساسيات القراءة/الكتابة الفيزيائية، أصبحت لدي جميع القطع اللازمة. حان الوقت الآن لبدء كتابة الإثبات العملي.
ملاحظة: تم تطوير هذا الإثبات العملي واختباره تحديدًا على بيئة Windows 10 22H2. نظرًا لأن الاستغلال يعتمد على التلاعب بالذاكرة الفيزيائية الخام، فإن إزاحات بنية النواة وحدود الذاكرة الفيزيائية مُشفّرة حاليًا لإعداداتي. لاختبار هذا على جهازك الخاص، يجب عليك تحديث إزاحات نواة ويندوز وضبط نطاقات فحص العناوين الفيزيائية لتتناسب مع إصدار نظام التشغيل الخاص بك وتكوين الذاكرة العشوائية (RAM).
الخطوة الأولى في استغلالي هي إنشاء اتصال مع المُشغّل. قمت بذلك عن طريق استدعاء CreateFileA على الرابط الرمزي للمُشغّل (\\.\PSTRIP64). بمجرد حصولي على مقبض صالح، احتجت إلى طريقة نظيفة لاستغلال IOCTL 0x80002008 الذي حللته سابقًا. أنشأت دالة مغلفة أسميتها MapPhysicalMemory(). تقوم هذه الدالة بتعبئة البنية المخصصة PSTRIP_MAP_REQUEST بالعنوان الفيزيائي الهدف وطول كتلة الذاكرة التي أرغب في قراءتها.
ثم أرسل هذه البنية مباشرة إلى المُشغّل عبر DeviceIoControl. إذا نجحت العملية، يقوم المُشغّل بتخطيط تلك الذاكرة الفيزيائية مباشرة في تطبيق وضع المستخدم الخاص بي ويعيد العنوان الأساسي الافتراضي في حقل OutputResult. يمكنني الآن تحويل هذا العنوان المُعاد إلى مؤشر C++ قياسي، مما يمنحني وصولًا خامًا وغير مُمتاز إلى ذاكرة الوصول العشوائي الفيزيائية للنظام.
مع تشغيل أساسيات القراءة/الكتابة الفيزيائية بالكامل، كان هدفي هو العثور على هياكل بيانات النواة التي تحتوي على امتيازات العملية. في ويندوز، يتم تمثيل كل عملية قيد التشغيل بهيكل EPROCESS.
يقوم ويندوز بتخصيص هياكل EPROCESS في تجمع النواة باستخدام معرف فريد مكون من 4 بايتات يُسمى Pool Tag. بالنسبة للعمليات، هذه العلامة هي السلسلة Proc (والتي تُترجم إلى 0x636F7250 بالنظام الست عشري). بمسح الذاكرة الفيزيائية للنظام، يمكنني البحث عن هذه السلسلة بالضبط.
يقوم استغلالي بالتكرار عبر مساحة الذاكرة الفيزيائية من 0x10000000 إلى 0x140000000، مُخططًا الذاكرة على شكل كتل بحجم 2 ميغابايت (STEP_SIZE = 0x200000). أقوم بتحويل كل كتلة مخططة إلى مصفوفة بايت خام وأفحصها على شكل كتل بحجم 16 بايت (sizeof(_POOL_HEADER)).
ومع ذلك، مجرد العثور على العلامة Proc في الذاكرة الفيزيائية ليس كافيًا. الذاكرة فوضوية، قد تكون هذه العلامة بقايا من عملية منتهية، أو مجرد بيانات عشوائية تصادف أنها تطابق القيمة السداسية العشرية. إذا افترضت بشكل أعمى أن كل علامة Proc هي هيكل EPROCESS صالح وبدأت في تعديل الذاكرة، فسأتسبب فورًا في حدوث شاشة زرقاء (BSOD).
لضمان الاستقرار، كان علي التحقق من صحة الهيكل باستخدام إرشادات استكشافية (heuristics). أولاً، أحسب بداية هيكل EPROCESS (الذي يقع على بُعد إزاحة طفيفة عن Pool Tag). من هناك، أتحقق من بعض الثوابت المعروفة لعملية قيد التشغيل:
0x2 (أولوية عادية).0x0.إذا اجتازت جميع هذه الإرشادات الاستكشافية، يمكنني أن أكون واثقًا جدًا من أنني أنظر إلى عملية صالحة ونشطة. ثم أقرأ معرف العملية الفريد (PID) الخاص بها. إذا كان PID يطابق عملية الاستغلال الخاصة بي، أحفظ العنوان الفيزيائي لمؤشر الرمز (token) الخاص بها. إذا كان PID هو 4 (عملية System في ويندوز)، أستخرج وأحفظ القيمة الفعلية للرمز عالي الامتياز الخاص بها.
أخيرًا، أقوم بمحاذاة العنوان الفيزيائي المحفوظ لمؤشر رمز عمليتي إلى أقرب حد لصفحة بحجم 4 كيلوبايت وأستخدم MapPhysicalMemory() مرة أخيرة لتخطيط تلك الصفحة المحددة فقط.
بعد ذلك، أنتقل إلى الإزاحة الدقيقة وأستبدل رمزي بقيمة رمز النظام. على الفور، يعامل ويندوز عملية الاستغلال الخاصة بي على أنها NT AUTHORITY\SYSTEM.
بعد إلغاء تخطيط الصفحة لضمان استقرار النظام، أقوم ببساطة باستدعاء CreateProcessA لتشغيل cmd.exe. نظرًا لأن عمليتي الحالية مرفوعة الامتيازات، فإن موجه الأوامر الجديد يرث هذه الامتيازات العليا، مما يكمل الهجوم بنجاح!
ملاحظة: تفصيل مهم اكتشفته خلال مرحلة التصحيح المبكرة هو كيفية تعامل المُشغّل مع المؤشر المُخطط. بتنفيذ SystemBuffer->LowPart = (unsigned int)BaseAddress;، يقوم المُشغّل بتحويل العنوان الأساسي الافتراضي 64 بت إلى قيمة 32 بت قبل إعادته. هذا الاقتطاع يفقد البتات العليا للعنوان، مما أدى إلى انتهاكات وصول فورية عندما حاولت إلغاء الإشارة إليه في استغلالي 64 بت. لتجاوز هذه المشكلة بشكل نظيف، قمت ببساطة بتجميع الإثبات العملي لوضع المستخدم الخاص بي كتطبيق 32 بت، مما يضمن بقاء المؤشر المُعاد صالحًا تمامًا.
ملاحظة: خلال اختباري الأولي، واجهت حالة هامشية مثيرة للاهتمام: نجح الإثبات العملي الخاص بي في تحديد موقع عملية الاستغلال في الذاكرة، لكنه فشل في العثور على عملية System (PID 4).
لفهم السبب، احتجت إلى فحص الذاكرة الفيزيائية مباشرة. قمت بإرفاق مصحح نواة (WinDbg) واستخدمت أوامر لاسترداد العنوان الافتراضي وقاعدة الدليل لعملية System. ثم استخدمت !vtop لترجمة ذلك العنوان الافتراضي إلى عنوانه الفيزيائي الدقيق في ذاكرة الوصول العشوائي.
عدت إلى مصحح وضع المستخدم الخاص بي المُرفق بالإثبات العملي. قمت بتعيين نقطة توقف شرطية على حلقة فحص الذاكرة الخاصة بي، مع توجيهها لإيقاف التنفيذ في اللحظة التي تلتقط فيها دالة MapPhysicalMemory() الكتلة ذات 2 ميجابايت التي تحتوي على العنوان الفيزيائي لعملية System.
بمجرد الوصول إلى نقطة التوقف، بدأت في فحص البايتات الخام للذاكرة المخططة يدويًا. وهنا اكتشفت تفصيلًا مهمًا حول تخصيصات تجمع نواة ويندوز.
عندما يخصص ويندوز الذاكرة لعملية ما، يبدأ بـ _POOL_HEADER (الذي يحتوي على علامتنا Proc)، يليه _OBJECT_HEADER، وأخيرًا هيكل EPROCESS نفسه. بالنسبة لتطبيقات وضع المستخدم القياسية، تحتوي هذه الرؤوس على بيانات تتبع إضافية، مما يعني أن هيكل EPROCESS الفعلي يبدأ بعد 0x80 بايت من Pool Tag.
ومع ذلك، كشف فحص ذاكرة عملية System عن تخطيط مختلف. تفتقر عملية System إلى بعض رؤوس التتبع القياسية هذه. كانت الإزاحة من العلامة Proc إلى بداية هيكل EPROCESS فقط 0x40 بايت!
كان الإصلاح مباشرًا. قمت بتحديث الإثبات العملي الخاص بي ليتعامل مع حجمي رأس التجمع من خلال التكرار عبر مصفوفة من الإزاحات المحتملة (0x40 و 0x80) كلما واجه علامة Proc.
الأمن السيبراني هو لعبة قط وفأر لا تنتهي بين المهاجمين والمدافعين. بينما يبحث المهاجمون باستمرار عن مشغّلات ثغرية، تمتلك منتجات الأمان الحديثة وفرق الاستجابة الزرقاء عدة طرق قوية لكشف ومنع هذه العملية بالضبط.
الطريقة الأكثر فعالية لإيقاف هجوم BYOVD هي منع تحميل المُشغّل في المقام الأول.
pstrip64.sys إلى قوائم الحظر الخاصة بهم.إذا كان المُشغّل محملاً بالفعل، لا يزال بإمكان منتجات الأمان اكتشاف الاستغلال خلال مرحلة التلاعب بالرمز (token).
NT AUTHORITY\SYSTEM دون سلسلة مصادقة مشروعة هو علامة حمراء ضخمة.cmd.exe)، خاصة عندما لا يكون للعملية الأم علاقة بالعمل كـ SYSTEM.