
تنفيذ لاستغلال ثغرة Fusee Gelee (CVE-2018-6242) لنينتندو سويتش، بالإضافة إلى حمولة مخصصة.
تطبيق لاستغلال Fusee Gelee (CVE-2018-6242) لجهاز Nintendo Switch، مبني على الثغرة التي كشفت عنها Kate Temkin / ReSwitched في مارس 2018.
يشغّل حمولة عشوائية (مثل hekate) على جهاز Tegra X1 في وضع الاسترداد عبر USB (RCM)، متجاوزًا التحقق من توقيعات ذاكرة الإقلاع (boot ROM) تمامًا.
RCM (وضع الاسترداد) هو بروتوكول استرداد قائم على USB مدمج في ذاكرة الإقلاع (boot ROM) لمعالج Tegra X1. صممته NVIDIA بحيث يمكنها تحميل برامج صغيرة («applets») على الجهاز لأغراض التشخيص أو الإصلاح — مثلًا عندما لا يعثر Switch على مُحمّل إقلاع (bootloader) صالح في وحدة التخزين الخاصة به.
في Nintendo Switch، يُدخل إلى RCM عن طريق جسر الدبوسين 1 و10 على سكة joycon اليمنى أثناء الإقلاع. في التشغيل العادي، لا يمكن لأحد استخدام RCM سوى NVIDIA — إذ يجب أن تكون جميع الأوامر موقّعة بالمفتاح الخاص RSA الخاص بـ NVIDIA، وتتحقق ذاكرة الإقلاع من التوقيعات قبل تنفيذ أي شيء.
يعمل الاستغلال عبر طبقتين مستقلتين من البروتوكول تتشاركان في IRAM (ذاكرة الوصول العشوائي على الشريحة):
طبقة USB (قياسية، EP0): كل جهاز USB لديه نقطة نهاية تحكم (EP0) تتعامل مع الطلبات القياسية مثل GET_STATUS وGET_DESCRIPTOR وSET_ADDRESS. تنفذ ذاكرة الإقلاع هذه الطلبات وفق ما تتطلبه مواصفة USB 2.0. تكون EP0 ضمنية — إذ لا تظهر في واصفات نقاط النهاية الخاصة بالجهاز.
طبقة RCM (خاصة بـ NVIDIA، EP1): تُعرِّف NVIDIA نقطة نهاية bulk (EP1) لنقل أوامر RCM والحمولات. تظهر EP1 في واصف الجهاز باتجاهين:
0x01 = OUT (يرسل المضيف البيانات إلى الجهاز)0x81 = IN (يرسل الجهاز البيانات إلى المضيف)تُنظَّم البيانات المرسلة عبر EP1 بالشكل التالي:
[680-byte RCM command header] [payload bytes]
الترويسة ذات 680 بايت هي بنية rcm_msg_t — بنية خاصة بـ NVIDIA تحتوي على معامل RSA والتوقيع، وECID، ورمز التشغيل (opcode)، وحقول أخرى. حُدد هذا الحجم عبر الهندسة العكسية لذاكرة الإقلاع الخاصة بـ Tegra X1 (انظر قاعدة بيانات IDA الخاصة بـ q3k). يوثق tegrarcm مفتوح المصدر من NVIDIA حتى 644 بايت فقط (Tegra124)؛ بينما إصدار T210 أكبر بـ 36 بايت.
يحتوي معالج طلبات التحكم EP0 في ذاكرة الإقلاع على خلل في تنفيذه لـ GET_STATUS للمستقبلات من نوع ENDPOINT. من الورقة البيضاء التي نشرتها Temkin:
// BUG: should be size_to_tx = sizeof(status), i.e. 2 bytes
size_to_tx = length_read; // attacker-controlled via wLength, up to 65535
data_to_tx = &status; // a uint16_t on the stack
memcpy(dma_buffer, data_to_tx, size_to_tx);
تقرأ memcpy من &status (متغير في المكدس يقع مباشرة أسفل 0x40010000) وتكتب في مخزن DMA (عند 0x40009000). ومع طول مفرط:
&status، عبر بقية المكدس، وصولًا إلى منطقة الحمولة التي يتحكم فيها المهاجم عند 0x40010000+ (التي وُضعت هناك بواسطة كتابات bulk عبر EP1).تتضمن بيانات المصدر "رش المكدس" (0x40010000 مكررًا)، وهي تُكتب فوق عناوين الإرجاع في المكدس. وعندما يعود المعالج، يقفز التنفيذ إلى 0x40010000 — حيث وضعنا كعب نقل صغير (relocator stub) يُدعى intermezzo.
يحدث كل هذا أثناء حلقة استقبال RCM (داخل handle_control_requests)، قبل أن تتحقق ذاكرة الإقلاع من التوقيعات على الإطلاق. بروتوكول RCM هو آلية التسليم؛ أما معالج التحكم في USB فهو المشغّل.
0x40005000 +------------------+
| DMA buffer LOW | USB controller writes odd packets here
0x40009000 +------------------+
| DMA buffer HIGH | USB controller writes even packets here
+------------------+
| execution stack | grows downward toward DMA buffers
0x40010000 +------------------+ <-- stack ends here / payload starts here
| intermezzo | small relocator stub (124 bytes)
0x40010E40 +------------------+
| user payload pt1 | first ~16KB of the user payload
0x40014E40 +------------------+
| stack spray | 0x40010000 repeated (8640 bytes)
0x40017000 +------------------+
| user payload pt2 | remainder of user payload
+------------------+
تُقسَّم حمولة المستخدم إلى جزأين حول رش المكدس، لأن الرش يجب أن يكون موضعًا بحيث ينسخه الفائض فوق عناوين الإرجاع في المكدس. يعيد intermezzo تجميع النصفين في كتلة متصلة عند 0x40010000 ثم يقفز إليها.
0x0955:0x7321) عبر تعداد USB القياسي0x40010000+، واضعةً intermezzo وحمولة المستخدم ورش المكدس0x40009000)wLength=0x7000 — يؤدي هذا إلى تفعيل memcpy الهشّة، فيعيد رش المكدس كتابة عناوين الإرجاع، ويعود المعالج إلى intermezzopip install pyusb
python launcher.py
يتطلب جهاز Switch في وضع RCM موصولًا عبر USB. على macOS، قد تحتاج إلى brew install libusb.
ضع ملف الحمولة الثنائي في binaries/payload.bin. يتولى ملف intermezzo.bin المرفق معالجة نقل الحمولة (relocation) ولا ينبغي استبداله.
launcher.py — سكربت الاستغلالbinaries/intermezzo.bin — كعب نقل (124 بايت)، يعيد تجميع الحمولة المقسمةbinaries/payload.bin — حمولة المستخدم المراد تنفيذها (مثل hekate)