
تحليل تفصيلي وتنفيذ استغلال لثغرة Windows PrintNightmare (CVE-2021-1675/34527) مع تصعيد الامتيازات القائم على RPC وتنفيذ التعليمات البرمجية عن بُعد عبر تثبيت برنامج تشغيل طابعة خبيث.
= تقرير تحليل Print Nightmare :imagesdir: Figures :toc: :icons: font :figure-caption: الشكل :xrefstyle: short :pdf-theme: basic-theme.yml
في 29 يونيو 2021، تم الكشف عن ثغرة أمنية خطيرة جدًا في خدمة الطباعة في Windows كـ 0day، حصلت على تصنيف أساسي 8.8، وتم نشرها على GitHub (وتم حذفها لاحقًا). هذه الثغرة هي الشهيرة PrintNightmare: CVE-2021-34527، وخطورتها تتجاوز حتى EternalBlue.
== معلومات أساسية عن الثغرة
تعمل ثغرة 34527 تقريبًا على جميع إصدارات Windows بدءًا من Windows 7 و Windows Server 2008، يمكن الاطلاع على التفاصيل في <> .
من ناحية الضرر، يمكن للمهاجم استخدام مصادقة مستخدم عادي لتنفيذ تعليمات برمجية عشوائية عن بُعد بصلاحيات المسؤول. من حيث صعوبة الاستغلال، من السهل جدًا استغلال هذه الثغرة، وبالتالي فهي خطيرة للغاية.
من ناحية خصائص الثغرة، تستند ثغرة 34527 إلى الثغرة CVE-2021-1675. وثغرة 1675 هي ثغرة رفع صلاحيات محلي وتنفيذ تعليمات برمجية عن بُعد، وتتشابه بشكل كبير مع ثغرة 34527.
قبل فهم كيفية عمل الثغرة، يجب أن يكون لدينا فكرة عامة عن بنية برنامج الطباعة الخلفي في Windows، مما يسهل فهم العلاقات بين الوحدات المختلفة المشاركة في الثغرة.
== مسار استدعاء CVE-2021-1675
=== بنية برنامج الطباعة الخلفي في Windows
يمكن تمثيل بنية spooler باستخدام <<spooler_arch>> :
[[spooler_arch]] .هندسة برنامج الطباعة الخلفي image::Print Spooler Architecture.png[]
بالتحديد، يدير برنامج الطباعة الخلفي مهام الطباعة، ويتكون من المكونات التالية:
winspool.drv:: ملف مكتبة الارتباط الديناميكي المقدم للمستخدم. يحدد هذا الملف واجهات برمجة التطبيقات Win32 المتعلقة بـ spooler ليستخدمها المستخدم. تستخدم جميع واجهات API داخله أسلوب استدعاء الإجراء عن بُعد للحصول على الخدمة.
spoolsv.exe:: يعمل spoolsv.exe كخادم في الهيكل، كأول برنامج يعالج استدعاءات API. تم تصميم هذا لتمكين print spooler من معالجة مهام الطباعة المحلية والبعيدة دون تمييز.
spoolsv.dll:: برنامج التوجيه. يقوم بتوجيه طلبات الطباعة التي يستقبلها spoolsv.exe إلى مختلف موفري الطباعة، ويقرر أي موفر سيعالج الطلب في النهاية. دوره هو التمييز بين مهام الطباعة البعيدة والمحلية. على الجهاز البعيد، يقوم بتعيين المهمة ثابتًا إلى موفر الطباعة المحلي.
localspl.dll:: موفر الطباعة المحلي. المهمة الرئيسية لموفري الطباعة هي التعامل مع متطلبات إدارة مهام الطباعة، ويتم تنفيذ معظم واجهات API داخل هذه الوحدة.
بناءً على النظرية المذكورة أعلاه، لنكمل الدراسة: على سبيل المثال، عندما أستدعي الدالة AddPrinterDriverEx (CVE-2021-1675)، سيمر عبر المسار التالي:
=== اختيار إصدار الدالة
أولاً، هذه الدالة هي في الواقع ماكرو، يختار إصدار Unicode (W) أو إصدار Ansi (A) بناءً على بيئة الترجمة المحلية، كما هو في <>:
[[AddPrinterDriverEx]] .AddPrinterDriverEx image::AddPrinterDriverEx.png[]
ولكن سواء كان إصدار أحرف عريضة أو ضيقة، فالنتيجة واحدة، لأن السلاسل النصية في نواة Windows مشفرة بـ Unicode، لذا في النهاية يتم تحويل استدعاء إصدار Ansi إلى استدعاء إصدار Unicode، كما في <>:
[[AnsiToUnicode]] .AnsiToUnicode image::AnsiToUnicode.png[]
عندما يتم تحويل جميع معاملات دالة Ansi إلى إصدار Unicode، يتم استدعاء دالة (<>):
[[AnsiCallUnicode]] .AnsiCallUnicode image::AnsiCallUnicode.png[]
وهذه الدالة هي في الواقع إصدار Unicode من AddPrinterDriverEx (<>):
[[GetUnicodeProcAddress]] .GetUnicodeProcAddress image::GetUnicodeProcAddress.png[]
=== ترسل دالة API طلب RPC إلى خادم spooler
بعد الدخول إلى الدالة الداخلية لإصدار Unicode، أولاً، يتم تحديد نوع معامل الدالة بناءً على قيمة Level:
image::pDriverInfo.png[]
في هذه الثغرة، سنقوم بتعيين Level إلى 2، أي اختيار نوع المعامل pDriverInfo كبنية DRIVER_INFO_2. ثم يقوم Windows بمعالجة معاملات الدالة، وبعد اكتمال المعالجة، يتم متابعة معالجة API عبر استدعاء إجراء عن بُعد:
image::set arguments.png[]
image::NdrClientCall3.png[]
=== آلية MSRPC
تعتمد آلية استدعاء الإجراء عن بُعد من Microsoft على معيار DCE. بشرح بسيط، استدعاء الإجراء عن بُعد هو تشغيل عمليات على نظام بعيد، وهذه العمليات محددة مسبقًا من قبل المبرمج أو النظام.
الطريقة المحددة لـ RPC هي إجراء تسلسل للدالة المطلوب استدعاؤها عن بُعد، ونقلها عبر الشبكة إلى النظام البعيد، ثم يقوم النظام البعيد بإلغاء التسلسل وتنفيذها. في الهيكل الذي بنته Microsoft، يتم عادةً اختيار TCP/IP و SMB لنقل استدعاءات RPC.
لاستخدام MSRPC، يجب أولاً تعريف واجهة IDL للدالة المراد استدعاؤها، ثم استخدام أداة MIDL لإنشاء كعب البرنامج التسلسلي (stub) الخاص بالعميل والخادم. بالنسبة لبعض Win32 API، فهي تحتوي بالفعل على stub الخادم المحدد مسبقًا، لذا يمكننا فقط إنشاء واستخدام stub العميل.
يستخدم MSRPC UUID لتعريف نوع معين من البروتوكولات، مثل MS-RPRN الذي يصف بروتوكول الطباعة عن بُعد، جميع الدوال المتعلقة بالطباعة عن بُعد تنتمي إلى هذا البروتوكول، ويستخدم MSPRC UUID 12345678-1234-ABCD-EF00-0123456789AB لتعريف هذا البروتوكول (<<rprn_uuid>>):
[[rprn_uuid]] .UUID MS-RPRN image::spoolss uuid.png[]
ثم، يمكن الاستمرار في هذا الاتصال، باستخدام رقم العملية (operator number) لتعريف الدوال داخل البروتوكول، وبالتالي استدعاؤها عن بُعد. على سبيل المثال، تستخدم AddPrinterDriverEx الرقم 89 لتعريف نفسها (<<addPrinterDriverEx_opnum>>):
[[addPrinterDriverEx_opnum]] .رقم عملية AddPrinterDriverEx image::AddPrinterDriverEx Opnum.png[]
عند استخدام MSRPC، هناك نقطتان جديرتان بالملاحظة:
"As you can see in your output, the scripts are trying to connect to port 135 (endpoint mapper) in order to get the TCP/IP port where the DCOM endpoint is listening (that is a dynamic port)." -- SecureAuthCorp/impacket issue #412
=== يعالج spoolsv.exe طلب API
[[call_flow]] .تسلسل استدعاء RpcAddPrinterDriverEx image::Function Calls.png[]
من <<call_flow>> يمكن ملاحظة أن spoolsv.exe يستدعي هذه الدوال، ومن تحليل الدوال داخليًا، لا تقوم هذه الوحدة بأي شيء سوى التهيئة. في النهاية، تستدعي هذه الوحدة الدالة المشار إليها بواسطة pLocalProvidor، وهي الدالة LocalAddPrinterDriverEx الموجودة في وحدة localspl.dll. باعتبار localspl موفر طباعة محلي، فهو بالفعل الوحدة التي تنفذ وظيفة API.
=== منطق تنفيذ الدالة في موفر الطباعة المحلي
[[LocalAddPrinterDriverEx]] .LocalAddPrinterDriverEx image::LocalAddPrinterDriverEx.png[]
أولاً، يوضح <> أن الوحدة تتحقق مما إذا كان spooler يعمل بشكل صحيح، ثم تنتقل إلى الدالة SplAddPrinterDriverEx.
[[SplAddPrinterDriverEx]] .SplAddPrinterDriverEx image::SplAddPrinterDriverEx.png[]
داخل الدالة <> يوجد موقع مهم لتحديد ما إذا كانت الدالة AddPrinterDriverEx يمكن تنفيذها بنجاح. النصف الأول لا حاجة لفحصه، لأن WPP هي تقنية متعلقة بالتسجيل، يمكن تخطيها مؤقتًا.
في النصف الثاني، يعرّف Microsoft متغيرًا v12، وهو علامة لتحديد ما إذا كانت الدالة ستستمر في التنفيذ أم ستخرج مباشرة.
[[bittest]] .bittest dwFileCopyFlags image::bittest in spl.png[]
من <> يمكن ملاحظة أن هناك شرطين للاستمرار: الأول هو أن تكون v12 تساوي 0 أي نجاح bittest، والثاني هو نجاح Validate. و Validate هو التحقق من الصلاحيات، ولا يمكن تجاوزه بسهولة. يوجد بداخله API هو OpenProcessToken، مما يعني ضرورة رفع الصلاحيات في العملية التالية، وهذا غير ممكن إذا لم يكن المستخدم مسؤولاً.
لذا، للاستمرار في التنفيذ، يجب تجاوز فحص bittest. والمتغير الذي يتم فحصه a4 — المعامل الرابع للدالة — هو المعامل الموضح في الموقع الرسمي تحت <> :
[cols="2,5a"] |=== |الاسم/القيمة |الوصف
|APD_STRICT_UPGRADE
0x00000001
|إضافة برنامج تشغيل الطابعة البديل فقط إذا لم يكن أي من ملفات برنامج التشغيل البديل أقدم من أي ملفات مقابلة لبرنامج التشغيل المثبت حاليًا.
|APD_STRICT_DOWNGRADE
0x00000002
|إضافة برنامج تشغيل الطابعة البديل فقط إذا لم يكن أي من ملفات برنامج التشغيل المثبت حاليًا أقدم من أي ملفات مقابلة لبرنامج التشغيل البديل.
|APD_COPY_ALL_FILES
0x00000004
|إضافة برنامج تشغيل الطابعة ونسخ جميع الملفات في دليل برنامج التشغيل. يجب تجاهل طوابع الوقت للملفات.
|APD_COPY_NEW_FILES
0x00000008
|إضافة برنامج تشغيل الطابعة ونسخ الملفات في دليل برنامج التشغيل الأحدث من أي من الملفات المقابلة قيد الاستخدام حاليًا.
|APD_COPY_FROM_DIRECTORY
0x00000010