
CVE-2026-74945، كشف غير مهيأ للذاكرة المكدسة (heap) عبر خط ويب مُصمَّم خصيصًا (sec-high)
يحتوي هذا الدليل على حزمة إعادة إنتاج صادقة لكشف ذاكرة غير مهيأة
في معالجة الخطوط في Firefox. يقوم خط @font-face مُصمَّم بدفع
تدفق مخرجات OTS إلى فجوة في كومة غير مهيأة، ويقرأ المحتوى العادي بايتات
تلك الفجوة مرة أخرى عبر واجهة measureText في canvas. الغرض من ذلك هو تشغيله ضد
نسخة Firefox المصابة التي تم تنزيلها محليًا باستخدام بيانات اصطناعية فقط. وهو
لا يمس أي نظام حي أو تابع لطرف ثالث.
مستودع PoC المطابق: https://github.com/defineid/Palimpsest
التحليل المكتوب: writeup-font-cmap-leak.html في هذه المجموعة.
| الحقل | القيمة |
|---|---|
| CVE | CVE-2026-74945 |
| المكوّن | Core / Graphics: Text |
| الفئة | كشف ذاكرة غير مهيأة (CWE-908 · CWE-200) |
| الخطورة | sec-high (كشف ذاكرة يمكن الوصول إليه عبر الويب) |
| تم إصلاحه في | Firefox 154 · ESR 140.14 |
| العلّة | bugzilla.mozilla.org/show_bug.cgi?id=2057808 |
| التفاعل | لا يوجد · تحميل الصفحة فقط |
يقوم Firefox بتعقيم خطوط الويب عبر OTS قبل استخدامها. تتوافق ثلاثة عيوب مستقلة بحيث يترك خط مُصمَّم فجوة في كومة غير مهيأة داخل المخرجات المعقّمة، ويمكن للمحتوى قراءتها مرة أخرى:
moz_xrealloc) لا يملأ المساحة الجديدة بالأصفار.cmap بصيغة 14 مع
nonDefaultUVSOffset كبير يجعل OTS ينقل تدفق مخرجاته للأمام عندما
يعيد تسلسل الخط المعقّم. يُسلَّم النطاق المتخطّى بين البايتات المكتوبة
وهدف seek إلى المستدعي حاملًا كومة غير مهيأة،
ويضبط forget() حجم المخزن المؤقت إلى أعلى إزاحة تم الوصول إليها.cmap بأكمله بدلًا من
الجدول الفرعي، لذا فإن مقطعًا يتجاوز idRangeOffset الخاص به نهاية بيانات
صيغة 4 لا يزال يجتاز التحقق ويقرأ في بايتات الجدول الفرعي المجاور
غير المهيأة.يجمع الخط المُصمَّم بين جدول فرعي بصيغة 4، يستخدم مقطعه الأخير (0xFFFF)
قيمة idRangeOffset فردية ومتجاوزة، مع جدول فرعي بصيغة 14 يحمل
nonDefaultUVSOffset كبيرًا، بالإضافة إلى maxp.numGlyphs = 65535 وقيمة hmtx
advance مميزة لكل معرّف glyph (advance == glyph id). لذلك فإن قياس U+FFFF يُحلّ
إلى معرّف glyph يُقرأ من الفجوة غير المهيأة، ويكشف عرض advance الخاص به، القابل للقراءة
من السكربت، عن نحو بايتين من الكومة لكل قياس على نافذة تبلغ نحو
64 كيلوبايت.
تخدم صفحة ويب عادية الخط المُصمَّم عبر @font-face وتستدعي
ctx.measureText(""). لا حاجة إلى نقرة أو إذن أو إضافة أو تغيير في التفضيلات.
يعمل OTS داخل العملية وليس ضمن المكتبات المعزولة، لذا فإن
البايتات المكشوفة هي كومة عملية المحتوى التي قد تحتوي على مؤشرات أو أسرار.
| الملف | الغرض |
|---|---|
make_font.py | يبني leak.otf وleak.woff، وهما الخطّان المُصمَّمان. |
trigger.html | صفحة مجرّدة تُحمّل الخط عبر @font-face وتقرأ advance الخاص بـ U+FFFF مرة أخرى عبر measureText. |
https://ftp.mozilla.org/pub/firefox/releases/140.9.0esr/.pip install fonttools brotli
python3 make_font.py # writes leak.otf and leak.woff in this directory
ثم افتح trigger.html في البناء المصاب، من مسار file:// أو من خادم محلي ثابت تتحكم به، وانقر على Measure.
.notdef) ويكون التقدم 0. لا تُسرَّب أي فجوة، لأن تدفق الإخراج يُصفَّر وبحث الصيغة 4 محصور في جدوله الفرعي الخاص.make_font.py هنا باستخدام fontTools 4.63.0 وأنتج leak.otf (393816 بايت) وleak.woff (132372 بايت). أُعيد تحليل الخط المُولَّد من البايتات الخام وتأكَّد كل مكوّن مُصمَّم: maxp.numGlyphs = 65535، وhhea.numberOfHMetrics = 65535، وتقدم hmtx مميز لكل معرّف glyph (التقدم == معرّف glyph)، وجدولان فرعيان لـ cmap (platform 3 encoding 1 format 4، وplatform 0 encoding 5 format 14)، والمقطع الأخير للصيغة 4 endCode = 0xFFFF مع idRangeOffset = 0x4001 فردي متجاوز، وnonDefaultUVSOffset = 0x8000 للصيغة 14.trigger.html بنفسك مقابل بناء مصاب محلي لملاحظة التسريب. تُظهر هذه الحزمة المدخل المُصمَّم ومسار إعادة القراءة؛ وهي ليست استغلالاً مسلَّحاً ولا تتضمن أي بدائية مُتحكَّم بها لتنفيذ الشيفرة.يستهدف هذا بناءً مُنزَّلاً محلياً ببيانات خطوط اصطناعية مُولَّدة ذاتياً، من أجل تقرير عن ثغرة CVE أبلغ عنها المؤلف بنفسه وتم ترقيعها بالفعل. وهو لا يهاجم أي خدمة حية أو تابعة لطرف ثالث.