
استغلال إثبات مفهوم لتصعيد الامتيازات محليًا لثغرة سباق في جدول معدلات qdisc في لينكس، باستخدام تهيئة الكومة عبر BPF وتسريب أنبوب لنيل صلاحيات الجذر.

يحتوي هذا المستودع على استغلال لإثبات المفهوم (PoC) لتصعيد الصلاحيات محلياً للثغرة CVE-2026-68138، وهي سباق في كود جدول معدل qdisc الخاص بالتحكم في حركة المرور في لينكس. في بيئة QEMU المُختبرة، ينتقل الـPoC من عملية عادية بمعرّف مستخدم خارجي (outer UID) 1000 إلى صدَفَة بمعرّف مستخدم 0 في مساحة اسم المستخدم الأولية.
تحذير
يتلف هذا الكود حالة كومة النواة (kernel heap) عمداً. استخدمه فقط في آلة افتراضية معزولة وقابلة للتخلص منها وتملكها أنت. قد يؤدي خطأ في السباق أو تنظيف مبكر إلى انهيار (panic) الضيف. لا تشغّله على محطة عمل أو خادم أو نظام تابع لطرف ثالث.
تُدير qdisc_get_rtab() وqdisc_put_rtab() قائمة مرتبطة أحادية الاتجاه عامة على مستوى العملية هي qdisc_rtab_list، وعدداً مرجعياً من النوع int عادياً غير ذرّي. تاريخياً، كان المستدعون يمسكون بقفل RTNL (RTNL mutex)، ما كان يسلسل الوصول إلى القائمة والعدد المرجعي.
يضبط مُصنِّف flower قيمة TCF_PROTO_OPS_DOIT_UNLOCKED. ونتيجة لذلك، يمكن لطلب RTM_NEWTFILTER لقاعدة flower أن يبلغ إجراء police ويستدعي أدوات جدول معدل qdisc دون RTNL:
tc_new_tfilter()
-> fl_change()
-> tcf_exts_validate_ex()
-> tcf_action_init()
-> tcf_police_init()
-> qdisc_get_rtab()/qdisc_put_rtab()
يمكن للطلبات المتزامنة التي تستخدم نفس جدول المعدل أن تتنافس على القائمة العامة وعددها المرجعي. النتيجة هي استخدام بعد التحرير (use-after-free) أو تحرير مزدوج لـstruct qdisc_rate_table، وهو كائن بحجم 1056 بايتاً يُخصَّص من kmalloc-2k على نواة x86-64 المُختبرة.
ولأن القائمة عامة لا خاصة بكل مساحة اسم شبكة، فإن الطلبات القادمة من مساحات أسماء شبكة منفصلة ما تزال تتنافس على الكائن نفسه.
يحدد سجل Linux CNA الالتزام المُقدِم 470502de5bdb، الذي صدر في لينكس 5.1.
غالباً ما تنقل نوى التوزيعات الإصلاحات (backport) دون التحول إلى الإصدار الأعلى (upstream) الموضح أعلاه. تحقّق مما إذا كان أحد التزامَي الإصلاح — أو تغيير قفل الدوران (spinlock) المكافئ لجدول معدل qdisc — موجوداً في مصدر النواة المحدد الذي يستخدمه النظام.
طُوِّر الاستغلال وتم التحقق منه مقابل الالتزام الضعيف 92d3817649df2b0b6a008a686c8275c88d7ef594، وهو الأب المباشر للإصلاح الرئيسي. أما مجموعة التحكم ذات النواة المُصلَحة فاستخدمت f43ee0c0730d6191629b5ee1ceae27b1ebfdc047.
اعتباراً من 2026-08-12، لم يُرجع البحث في متتبع CVEs لأوبونتو أي إدخال لهذه الثغرة. لذلك فإن الجدول أدناه هو فحص مباشر للمصدر، وليس تحديداً لحالة الأمان من Canonical. فكل وسم أوبونتو مرتبط ما يزال يحوي qdisc_rtab_list دون قفل ويفتقر إلى qdisc_rtab_lock المُصلِح.
لم يُحدَّد أي حزمة أوبونتو مصححة في تاريخ الفحص ذلك. ينبغي فحص حزم أوبونتو المستقبلية بحثاً عن backport مكافئ للإصلاحات المرتبطة أعلاه، لا الحكم عليها برقم إصدارها فقط.
استغلال أوبونتو 22.04 الخاص ببناء محدد، ومختبر QEMU، والمجموع الاختباري (checksum) الدقيق للصورة، والشروط — كلها موثقة في ubuntu/README.md. تم التحقق منه مقابل نواة 5.15.0-187-generic #197-Ubuntu الرسمية مع تفعيل محاسبة الذاكرة عبر cgroup.
للخلل الكامن وسلسلة الاستغلال هذه متطلبات مختلفة. تم التحقق من الـPoC مع:
CONFIG_USER_NS=y وCONFIG_NET_NS=y؛CONFIG_NET_CLS=y وCONFIG_NET_CLS_FLOWER=y وCONFIG_NET_CLS_ACT=y وCONFIG_NET_ACT_POLICE=y؛CONFIG_TMPFS_XATTR=y من أجل رشّ الكومة بـsimple_xattr؛CONFIG_MODULES=y ووجود /sbin/modprobe قابل للاستخدام لمساعد الجذر النهائي؛CONFIG_MEMCG=n، حتى تتشارك تخصيصات qdisc/BPF/pipe/xattr المستخدمة في هذه السلسلة في ذاكرة التخزين المؤقت kmalloc-2k المتوقعة؛كانت CONFIG_SLAB_BUCKETS=y وعشوائية القوائم الحرة (freelist randomization) وتحصين القوائم الحرة (freelist hardening) مفعّلة في نواة الاختبار الناجحة. لا يُتجاوز KASLR جوهرياً بعنوان مثبَّت (hard-coded): يحصل الـPoC على مؤشرَي الصفحة والعمليات المطلوبين من تسريب pipe. استخدم المختبر المرفق nokaslr لتبسيط التصحيح.
النوى التي تفعّل محاسبة مجموعة الذاكرة أو ذات تخطيطات مُخصِّص/ذاكرة تخزين مؤقت مختلفة تتطلب استراتيجية استرداد مختلفة. يرفض الـPoC عمداً ادعاء قابلية النقل عبر أي إعدادات توزيعية.
تنفذ النسخة المنفصلة الخاصة بأوبونتو استراتيجية الاسترداد المختلفة تلك؛ ومتطلباتها أضيق عمداً ومدرجة في ubuntu/README.md.
تدخل أربعة خيوط عاملة (worker threads) مساحات أسماء شبكة منفصلة وتُنشئ مرشحات flower بإجراءات police. تسلك ثلاثة خيوط مسار الإجراء الناجح، بينما يُمرِّر خيط واحد مُقدِّراً (estimator) غير صالح عمداً بعد حصوله على مرجعَي جدول المعدل، فيُجبر مسار التنظيف. هذا المزيج يجعل التلاعب المتزامن بالعدد المرجعي والقائمة قابلاً للتكرار دون مشاركة حالة مُصنِّف flower بين الخيوط.
qdisc_rate_table باستخدام BPF الكلاسيكيبعد كل طلب netlink، تثبّت وحدة المعالجة المركزية نفسها فوراً مرشح BPF كلاسيكياً من 133 تعليمة. تُخصَّص مصفوفة تعليماته البالغ حجمها 1064 بايتاً من kmalloc-2k، وتُشكَّل بحيث تبقى البايتات المتداخلة مع qdisc_rate_table.next وqdisc_rate_table.refcnt صالحة مبدئياً.
تحتوي التعليمة الأخيرة على علامة خاصة بكل مقبس. يتيح SO_GET_FILTER للـPoC اكتشاف متى أُعيد توجيه مؤشر orig_prog->filter لأحد المقابس إلى تخصيص BPF حي آخر. ينتج عن هذا كائنا مقبس يشيران إلى نفس مخزن التعليمات.
يؤجل إغلاق أحد المالكين التحرير الفعلي عبر sk_filter_release_rcu(). بعد فترة السماح (grace period)، يخصّص الـPoC حلقات pipe من 32 خانة:
32 * sizeof(struct pipe_buffer) = 32 * 40 = 1280 bytes -> kmalloc-2k
تستعيد إحدى الحلقات مخزن BPF الملقَّب. وتكشف قراءة المقبس الآخر باستخدام SO_GET_FILTER عن pipe_buffer حي، بما في ذلك مؤشرا page وops وإزاحة إدخال ذاكرة التخزين المؤقت للصفحات (page cache) الخاص بـ/sbin/modprobe.
يُحرر إغلاق المقبس الثاني حلقة pipe الحية بعد فترة سماح RCU أخرى، بينما ما يزال pipe_inode_info المقابل يشير إليها. يستعيد رشّ simple_xattr الحلقة البالغ حجمها 1280 بايتاً ويضع pipe_buffer المسرَّب في الخانة المتوقعة مع ضبط PIPE_BUF_FLAG_CAN_MERGE.
تؤدي الكتابة إلى كل pipe مرشح في النهاية إلى إلحاق بايتات يتحكم فيها المهاجم بصفحة ذاكرة التخزين المؤقت للصفحات الداعمة لنقطة دخول ELF في /sbin/modprobe. يعيد الـPoC قراءة الملف ولا يتابع إلا بعد تأكيد الكتابة الفوقية الدقيقة.
لم تعد النوى الحديثة تستخدم المسار الاحتياطي التاريخي للتحميل التلقائي لوحدات binfmt غير الصالح في search_binary_handler(). يُنشئ هذا الـPoC بدلاً من ذلك مقبس مخطط بيانات (datagram) من نوع AF_INET ببروتوكول 253 غير مدعوم. يطلب inet_create() الوحدة المفقودة net-pf-2-proto-253-type-2، مما يجعل النواة تنفذ /sbin/modprobe المكتوب فوقه بصلاحيات جذر مساحة الاسم الأولية.
تكتب حمولة نقطة الدخول المحقونة ملف إثبات وتفتح صدَفَة جذر على وحدة التحكم التسلسلية في QEMU.
الثنائي الضيف مربوط ارتباطاً ثابتاً:
./build.sh
الأمر المكافئ:
gcc -O2 -static -pthread -Wall -Wextra -Werror \
-o build/exploit exploit.c
يجب إتاحة الثنائي الناتج داخل الضيف بالمسار /exploit/CVE-2026-68138. استخدم المختبر المُختبر مشاركة 9p للقراءة والكتابة في QEMU. يتوقع مساعد الجذر تلك المشاركة عند /chroot/exploit من مساحة اسم التركيب (mount namespace) الأولية؛ عدِّل helper_command في exploit.c إذا كان المختبر يستخدم ترتيباً مختلفاً.
خيارات نواة الاختبار ذات الصلة مسجلة في config.fragment. إنها جزء (fragment) من إعداد، وليست إعداداً كاملاً لنواة إنتاجية.
من صدَفَة الضيف غير المميّزة:
id
/exploit/CVE-2026-68138
السباق احتمالي. عند الإخفاق يطبع الـPoC:
[-] no BPF alias found in this attempt
أعد تشغيل الآلة الافتراضية قبل إعادة المحاولة. لا تُعِد تشغيله في الضيف نفسه: فقد تكون قائمة qdisc العامة قد تلفت بالفعل.
يصل التنفيذ الناجح إلى:
[+] confirmed dangling orig_prog->filter pointer
[+] pipe leak: page=<kernel pointer> ops=<kernel pointer> off=0x60f len=1 flags=0
[+] page-cache overwrite via dangling pipe
[+] controlled page-cache write confirmed; triggering helper
[+] CVE-2026-68138 initial-namespace root shell
uid=0(root) gid=0(root) groups=0(root)
[+] ROOT PROOF: uid=0(root) gid=0(root) groups=0(root)
السجل الكامل المنقّى موجود في docs/example-output.txt.
اكتملت السلسلة الكاملة ثلاث مرات انطلاقاً من إقلاعات جديدة لآلة افتراضية ضعيفة. أنتج الثنائي النهائي:
outer identity: uid=1000(user)
alias: classic-BPF orig_prog->filter overlap
leak: live pipe_buffer page and ops pointers
write: verified /sbin/modprobe page-cache modification
final identity: uid=0(root) gid=0(root) groups=0(root)
نُفِّذ الثنائي نفسه مقابل الالتزام المُصلَح مع نفس طوبولوجيا وحدات المعالجة الأربع. اكتمل حوالي 126,000 طلب سباق دون ظهور اسم مستعار (alias) BPF، ولم تُبلَغ أي من مراحل pipe أو page-cache.
نُفِّذت النسخة الخاصة بأوبونتو أيضاً مقابل نواة 5.15.0-187.197 الرسمية العامة (generic) نفسها. أُقلِع الضيف بأربع وحدات vCPU، ومحاسبة ذاكرة cgroup عادية، مع nokaslr بوصفه وسيط النواة الوحيد الخاص بالاستغلال.
لم تُعَد ترجمة نواة أوبونتو: استخدم الاختبار نواة vmlinuz-5.15.0-187-generic الخاصة بالتوزيعة دون تغيير، مع الوحدات الرسمية المطابقة لها. يفصل جدول متطلبات أوبونتو المسبقة بين ميزات النواة الأصلية (stock) وتعديلات وقت التشغيل ومختبر QEMU.
لم يكن للحساب الابتدائي أي وصول إلى sudo أو مجموعات تكميلية:
uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu)
انطلاقاً من لقطة (snapshot) جديدة، حصل الاستغلال على الاسم المستعار BPF، واستعاده عبر كائنات simple_xattr في cgroup-v2 وخرائط بت select() المحظورة، وتحقّق من أن /proc/sys/kernel/modprobe يحتوي /tmp/x، ووصل إلى:
[+] CVE-2026-68138 Ubuntu initial-namespace root shell
uid=0(root) gid=0(root) groups=0(root)
root@cve-2026-68138-jammy:/# id
uid=0(root) gid=0(root) groups=0(root)
انظر سجل QEMU الحقيقي وتعليمات مختبر أوبونتو. تثبّت هذه النسخة عنوان modprobe_path يدوياً (hard-coded) لهذه الحزمة، ولذلك تتطلب nokaslr؛ وهي لا تدّعي تجاوز KASLR أو التوافق مع بنية أوبونتو أخرى.
التصحيح (patch) جزء من نواة لينكس بالفعل، ولذلك لا يُكرَّر في هذا المستودع. التزاما الإصلاح الأساسيان (المستقر والرئيسي) مربوطان في المراجع أدناه.
البحث وتنفيذ الاستغلال: A. Ramos <[email protected]> (تويتر: @aramosf).
| السلسلة | الحالة | الالتزام/الإصدار |
|---|
| لينكس قبل 5.1 | غير متأثر | التغيير المُقدِم غير موجود |
| لينكس من 5.1 حتى 7.1.5 | متأثر ما لم يوجد backport من المورّد | من 470502de5bdb حتى الالتزام الذي يسبق الإصلاح المستقر |
| لينكس 7.1.y | مُصلَح | 7.1.6، fb29e1b41052 |
| سلسلة تطوير لينكس 7.2 | متأثر قبل rc5 | من rc1 حتى rc4 |
| الخط الرئيسي (Mainline) | مُصلَح | 7.2-rc5، f43ee0c0730d |
| سلسلة أوبونتو | الحزمة/الوسم المُفحَص | نتيجة فحص المصدر |
|---|
| أوبونتو 22.04 GA | 5.15.0-187.197 | الكود الضعيف موجود؛ أُعيد إنتاج الاستغلال كاملاً في QEMU |
| أوبونتو 22.04 HWE | 6.8.0-136.136~22.04.1 | الكود الضعيف موجود؛ سلسلة الاستغلال غير مُختبرة |
| أوبونتو 24.04 HWE | 7.0.0-28.28~24.04.1 | الكود الضعيف موجود؛ سلسلة الاستغلال هذه غير متوافقة مع تحصين مُخصِّص الذاكرة (allocator hardening) لديه |
| أوبونتو 26.04 | 7.0.0-28.28 | الكود الضعيف موجود؛ سلسلة الاستغلال هذه غير متوافقة مع تحصين مُخصِّص الذاكرة لديه |