
CVE-2026-50416: تجاوز KASLR في Windows 11
في إصدار Windows 11 Insider 10.0.28020.2149، يمنح تعيين وضع المستخدم لكومة سطح مكتب win32k مؤشرًا خامًا لتجمع جلسة النواة عند الإزاحة 0x100. أي عملية لديها سطح مكتب يمكنها قراءته. يتضمن ذلك عمليات منخفضة السلامة (Low integrity)، وحاويات التطبيقات (AppContainers)، وLPAC بدون أي صلاحيات، وحاوية تطبيقات منخفضة السلامة تشبه تمامًا عارض المتصفح. من تلك القيمة المفردة المسربة (QWORD) يمكنك استعادة قاعدة كومة سطح مكتب النواة، ومن هناك، بمساعدة gSharedInfo، حساب العنوان الافتراضي الدقيق للنواة لكل نافذة وقائمة وكائن فئة على سطح المكتب.
هذه ثغرة تجاوز لـ KASLR، وهي كشف معلومات. وهي بحد ذاتها ليست تنفيذًا للكود. أريد أن أقول ذلك مقدمًا لأن المبالغة في تأجيج خطأ ما هي أسرع طريقة لجعل التقرير غير قابل للقراءة. ما هي عليه حقًا هو تسريب نظيف جدًا من أماكن لا يُفترض أن يصل إليها.
CVE-2026-50416. يوثّق هذا المنشور الخلل، وإثبات المفهوم، والجزء الذي جعلني أنتبه حقًا: حدود صندوق الرمل التي يظل مؤشر النواة مكشوفًا عبرها.
كل عملية تنضم إلى سطح مكتب تحصل على كومة سطح المكتب معيّنة كقراءة فقط في مساحة عناوينها. تكتب النواة كائنات النوافذ والقوائم في هذه الكومة ويقرأها وضع المستخدم، وهذا أمر طبيعي وضروري. ما ليس ضروريًا هو أن الإزاحة 0x100 من ذلك التعيين تحتوي على مؤشر نواة حي إلى تجمع الجلسة. لا أحد يعقّمه قبل أن يصبح مرئيًا لوضع المستخدم. تعرف Windows بالفعل كيفية تعقيم مؤشرات النواة في هذه الكومة، فهي تستخدم قيمة حارسة 0x6000000000 للحقول الأخرى. هذا الحقل فقط تم تفويته.
win32k هو مكوّن النواة الذي يمتلك النوافذ والقوائم والمؤشرات والخطافات، عالم الكائنات الرسومية بأكمله. الكثير من حالة win32k يعيش لكل سطح مكتب في بنية تسمى كومة سطح المكتب (desktop heap). لتسريع عمليات النوافذ، تقوم النواة بتعيين جزء من هذه الكومة في كل عملية تنضم إلى سطح المكتب، كقسم مشترك للقراءة فقط. تقرأ عمليتك نص النافذة أو نمطها مباشرة من هذا التعيين بدون استدعاء نظام (syscall). هذا التعيين هو الوهم المشترك الذي يجعل الواجهة الرسومية تبدو فورية.
العثور عليه من وضع المستخدم أمر تافه وموثّق. يحتوي TEB على بنية ClientInfo. عنوان كومة سطح المكتب في وضع المستخدم موجود في ClientInfo[5]. في الكود:
PVOID teb = (PVOID)__readgsqword(0x30);
PVOID* clientInfo = (PVOID*)((BYTE*)teb + 0x800);
BYTE* desktopHeap = (BYTE*)clientInfo[5];
هذا هو المقبض كله. لا ثغرة بعد. هذا الجزء بتصميم.
الثغرة هي ما يوجد عند الإزاحة 0x100 من تلك الكومة.
ULONG64 leaked = *(ULONG64*)(desktopHeap + 0x100);
في الإصدار المختبر، تحمل تلك القيمة QWORD شيئًا مثل 0xFFFFA804DDE00040. هذا عنوان نواة قانوني (canonical). إنه محاذٍ لثماني بايتات. يشير إلى تجمع الجلسة، على بعد 0x40 بايتًا في جانب النواة من نفس كومة سطح المكتب هذه.
مررت بالفحوصات المنطقية المعتادة قبل تصديق ذلك.
إذن فهو عنوان نواة حقيقي ومستمر على مستوى جلسة الإقلاع، وليس بيانات تالفة ولا قيمة قديمة. الخاصيتان الرابعة والخامسة معًا هما بصمة عنوان عشوائي عبر KASLR ثابت داخل إقلاع واحد. هذا هو بالضبط الشيء الذي يُفترض أن يخفيه KASLR.
للمهتمين، كانت القيمة في جلسة الاختبار الفعلية لدي 0xFFFFC600DCC00040. نفس الشكل، نفس السلوك. قيمتك ستختلف بسبب KASLR. هذا هو الهدف نوعًا ما.
العنوان المسرب المفرد جميل. تحويله إلى عنوان أي كائن محدد هو حيث يصبح مفيدًا.
حقيقتان تقومان بالعمل.
أولاً، تشير القيمة المسربة إلى 0x40 بايت داخل كومة سطح مكتب النواة. إذن قاعدة كومة سطح مكتب النواة هي القيمة المسربة ناقص 0x40.
kernel desktop heap base = leaked minus 0x40
ثانيًا، يُصدّر user32 بنية تسمى gSharedInfo. من بين أشياء أخرى، يمنحك جدول المقابض العام (aheList) وحجم إدخال جدول مقابض واحد (HeEntrySize, 0x20 على x64). كل HWND هو في الحقيقة فهرس في ذلك الجدول. خذ البتات الست عشرة السفلية من HWND، وانتقل إلى ذلك الإدخال، ويكون أول QWORD في الإدخال هو إزاحة كائن النافذة داخل كومة سطح المكتب.
اجمعهما معًا:
offset = gSharedInfo.aheList[HWND & 0xFFFF].offset
kernel addr = kernel desktop heap base plus offset
هذا هو العنوان الافتراضي الدقيق للنواة لكائن النافذة المرتبط بـ HWND معين. ليس تخمينًا. ليس رشًا. العنوان الفعلي.
ينشئ إثبات المفهوم ست نوافذ من فئات مختلفة (STATIC, BUTTON, EDIT, LISTBOX, SCROLLBAR, COMBOBOX) ويحللها جميعًا. كما يفحص الكومة ويعثر على نصف دزينة أخرى من مؤشرات النواة غير المعقّمة بعد 0x100، لذا فإن 0x100 هو الأكثر موثوقية فقط، وليس الوحيد.
هنا الجزء الذي رفع هذا من مثير للاهتمام إلى مقلق حقًا.
كتبت برنامجًا ثانيًا يطلق عمليات فرعية في سياقات أكثر تقييدًا تدريجيًا ويسأل كل واحدة عما إذا كانت تستطيع قراءة نفس المؤشر. السياقات، بترتيب مدى ضآلة ما يُفترض أن تكون قادرة على فعله:
CreateDesktop. يُسرب، بقيمة مختلفة لأنه يملك كومة سطح مكتب خاصة به.كل السياقات من واحد إلى خمسة أعادت نفس عنوان النواة، لأنها تتشارك كومة سطح المكتب الافتراضية. أعاد السياق السادس عنوانًا مختلفًا لأنه كومة مختلفة، لكن التقنية عملت بشكل مماثل.
الخامس هو الذي يجب التحديق فيه. عملية تعمل بسلامة منخفضة، داخل حاوية تطبيقات، بدون أي صلاحيات، هي تقريبًا أقصى تقييد ممكن لعملية وضع مستخدم على Windows. هذا هو صندوق الرمل الذي يضع المتصفح فيه العارض الخاص به. يمكن لهذا الصندوق الرملي قراءة عنوان نواة.
هناك سبب يجعل هذا مؤلمًا. نموذج الدفاع المتعمق بأكمله للمتصفحات يفترض أنه حتى لو حصل المهاجم على تنفيذ كود داخل العارض عبر خطأ منفصل في المحرك، فإن صندوق الرمل يحتوي الضرر ويخفي النواة. يشكل KASLR جزءًا كبيرًا من هذا الإخفاء. يصل هذا التسريب إلى داخل صندوق الرمل ويسلّم المهاجم عنوان نواة بدون استدعاء نظام إضافي وبدون تصعيد صلاحيات. قام صندوق الرمل بعمله بشكل مثالي. ومع ذلك تسربت المعلومات من تحته، عبر قسم مشترك يعامله نموذج صندوق الرمل على أنه حميد.
ظللت أنتظر المشكلة. بالتأكيد عليك إنشاء نافذة أولاً، أو استدعاء بعض استدعاءات النظام الرسومية التي سيلاحظها صندوق الرمل. لا.
في هذا الإصدار، يكون تعيين كومة سطح المكتب موجودًا بالفعل في مساحة عنوان العملية قبل تحميل user32.dll حتى. يقرأ الإثبات desktop_heap[0x100] قبل وبعد تحميل user32، وتعيد القراءتان نفس مؤشر النواة. لا CreateWindow. ولا GetDesktopWindow. لا شيء. أي عملية لها ارتباط بسطح مكتب، بما في ذلك الخدمات بدون واجهة والعمال الخلفيون، يمكنها قراءته فور بدء تشغيلها.
يعتمد إثبات العارض على هذا. يطلق عملية فرعية بسلامة منخفضة في حاوية تطبيقات بدون صلاحيات، ويجعلها تحمّل user32 فقط لا غير، وتقرأ المؤشر فور بدء التشغيل. الإخراج حرفيًا:
RENDERER|LEAKED|0xffffc600dcc00040|AC=1|IL=0x1000|NoWindowCreated
IL=0x1000 تعني سلامة منخفضة. AC=1 تعني داخل حاوية تطبيقات. NoWindowCreated تعني تمامًا ما يبدو عليه.
بينما كنت هناك، وجّهت ماسحًا إلى الكومة لأرى ما الذي كان موجودًا هناك أيضًا. إنه أكثر من مجرد مؤشرات.
عناوين نوافذ العمليات الأخرى. وجد الماسح عشرين عنوانًا فريدًا من عمليات أخرى موجودة في الكومة بصيغة UTF16، كل عنوان تم التحقق منه مقابل EnumWindows لتأكيد أن PID المالك يملك بالفعل نافذة بهذا العنوان. تبويبات Chrome، Discord، Explorer، Spotify، نوافذ علبة النظام. الكومة هي لوحة إعلانات صغيرة لما يفعله الجميع على سطح المكتب، وأي عملية معزولة يمكنها قراءتها بدون أي اتصال بين العمليات (IPC).
معرّفات العمليات. تم التحقق من أكثر من ستمائة قيمة DWORD في الكومة على أنها معرّفات PIDs حقيقية قيد التشغيل، كل واحدة مؤكدة بطريقتين، بنجاح OpenProcess وبظهور المعرّف في قائمة EnumWindows. لذا يمكن لعملية معزولة تعداد من لديه نوافذ على سطح المكتب، بصمت.
المزيد من مؤشرات النواة. من ستة إلى عشرة في كل تشغيل، تختلف مع نشاط سطح المكتب، وليس فقط المؤشر عند 0x100.
شيء واحد لا يكشفه، وقد تحققت منه عمدًا: نص حقل تحرير كلمة المرور. أنشأت حقل EDIT مع ES_PASSWORD، وضبطت نصه إلى SecretPassword123، وبحثت في الكومة. ليس موجودًا. تخفي Windows محتوى حقل كلمة المرور في كومة سطح المكتب. جيد. رحمة صغيرة.
إذن الكومة تسرب العناوين وعناوين النوافذ، لكنها على الأقل تحتفظ بكلمات مرورك. سأقبل بالفوز حيث أجده.
يوجد KASLR لجعل استغلال النواة احتماليًا. بدون معرفة العنوان، تتحول ثغرة الاستخدام بعد التحرير (use after free) في win32k إلى رش عشوائي أعمى في التجمع. تغمر مُخصِّص النواة، وتأمل أن يهبط كائنك البديل حيث يشير المؤشر المعلق، وتدعو. في تطبيقات التجمع الحديثة، معدل النجاح هذا منخفض ويستمر في الانخفاض.
الآن أعطِ المهاجم هذا التسريب. إنهم يعرفون عنوان النواة الدقيق لكائن النافذة المحرر. يمكنهم صياغة تخصيص بديل على هذا العنوان بالضبط والتحكم بدقة فيما يشير إليه المؤشر المعلق. يصبح الإفساد الأعمى حتميًا. هذه هي القيمة الحقيقية لتجاوز KASLR نظيف، إنه مضاعف الموثوقية لثغرة إفساد ذاكرة غير مرتبطة، وليس ثغرة بحد ذاتها.
التخفيفات التي يؤثر عليها هذا التسريب، بشكل ملموس:
gSharedInfo لمعرفة الإزاحات الدقيقة لكل كائن نافذة، ثم يحسب عناوين النواة مباشرة.الكائنات التي ينتج عنوان نواتها عن هذه القراءة الواحدة: كائنات النوافذ (tagWND)، وكائنات القوائم (tagMENU)، وكائنات الفئات (tagCLS)، وأي شيء آخر على كومة سطح المكتب يمكن الوصول إليه عبر جدول المقابض. قراءة واحدة، سطح المكتب بأكمله مُعيّن.
تحتوي مجلد إثبات المفهوم على compile.bat يجد Visual Studio ويبني الهدف الذي تختاره.
compile.bat then choose a number from the menu
الأهداف، بترتيب مقدار ما تظهره لك:
kaslr_bypass_poc.exe. الهدف الرئيسي. يتنقل عبر التسريب، ويحسب قاعدة النواة، ويحل عناوين النوافذ الحية، ويفحص عن مؤشرات إضافية، ويطبع السلسلة كاملة. شغّل هذا أولاً.kaslr_sandbox_proof.exe. يطلق عمليات فرعية بسلامة منخفضة (Low IL)، في حاوية تطبيقات، في LPAC، بسلامة منخفضة مع حاوية تطبيقات، وعلى سطح مكتب بديل، ثم يُبلغ عما إذا كان كل واحد قد سرب وما إذا كانت جميعها متطابقة.supporting_proof_no_window.exe. يثبت أن التسريب يعمل قبل وجود أي نافذة.supporting_proof_no_caps_lpac.exe. يثبت ذلك من حاوية تطبيقات وLPAC بدون أي صلاحيات.supporting_proof_sensitive_data.exe. يثبت كشف البيانات عبر العمليات، العناوين ومعرّفات PIDs، وأن نص كلمة المرور غير مكشوف.supporting_proof_exploitability.exe. يحل عناوين النواة لست فئات نوافذ ويُظهر إبطال التخفيفات.supporting_proof_remote_trigger.exe. السياق المكافئ للعارض.ثلاثة فحوصات سريعة لإقناع نفسك أنه حقيقي:
الفحصان الأول والثاني يؤكدان أنه عنوان مستقر ومشترك وحقيقي. الثالث يؤكد أنه عشوائي عبر KASLR. معًا يشكلان الثغرة.
المتوقع. يجب ألا يكشف تعيين كومة سطح المكتب في وضع المستخدم أبدًا عن مؤشرات نواة خام. يجب تعقيم أي مؤشر نواة في بيانات وصف كومة سطح المكتب قبل أن يتمكن وضع المستخدم من رؤيته.
الفعلي. تكشف الإزاحة 0x100 عن مؤشر لتجمع جلسة النواة. كل عملية لها سطح مكتب تقرؤه، بما في ذلك عمليات السلامة المنخفضة وحاويات التطبيقات وLPAC. في الجلسة المختبرة، أعاد كل سياق 0xFFFFC600DCC00040.
أرخص إصلاح يطابق ما تفعله Windows بالفعل في أماكن أخرى من هذه الكومة. تعقيم مؤشرات النواة في رأس كومة سطح المكتب قبل وصولها إلى تعيين وضع المستخدم، نفس معاملة القيمة الحارسة 0x6000000000 المستخدمة للحقول الأخرى.
الإصلاح الأقوى، إذا كان وضع المستخدم لا يحتاج فعليًا إلى صفحة الرأس، هو التوقف عن كشف تلك الصفحة عبر تعيين وضع المستخدم تمامًا. ليس لدي رأي قوي حول أي إصلاح ستختاره Microsoft. لدي رأي قوي بأن مؤشر النواة الخام يجب ألا يكون على بُعد __readgsqword واحدة وإلغاء إشارة مؤشر واحدة من عارض بدون صلاحيات.
أظل أعود إلى السياق الخامس. حاوية تطبيقات بسلامة منخفضة بدون صلاحيات يُفترض أن تكون الصندوق الذي لا يهرب منه شيء مثير. تعيين كومة سطح المكتب هو النوع من الموارد المشتركة الذي قرر نموذج صندوق الرمل منذ زمن طويل أنه آمن لتركه معينًا، لأنه للقراءة فقط ولأن قراءة نص نافذة غير ضارة. كل هذين الأمرين لا يزالان صحيحين. ما تغير هو أن حقل رأس واحد حوّل تلك النافذة غير الضارة للقراءة فقط إلى ثقب بصري مباشر إلى تجمع الجلسة.
لم يفشل صندوق الرمل. الافتراض الذي يقوم عليه صندوق الرمل هو الذي فشل. هذان فشلان مختلفان، والثاني أصعب في توقعه، ولهذا على الأرجح لم يكتشفه أحد. حتى تقرأ الإزاحة 0x100.