
حظر سلسلة DirtyFrag لتصعيد الصلاحيات المحلية (LPE) في لينكس (CVE-2026-43284 / CVE-2026-43500) في وقت التشغيل باستخدام TracingPolicy من Cilium Tetragon
حظر سلسلة تصعيد الامتيازات DirtyFrag في لينكس (CVE-2026-43284 / CVE-2026-43500) وقت التشغيل باستخدام TracingPolicy من Cilium Tetragon. تقوم السياسة بإنهاء إثبات المفهوم العام بإشارة SIGKILL في خطوة إعداد المقبس، قبل وصولها إلى كتابة الـ page-cache التي ستمنحه صلاحيات الجذر.
ما هذا. توثيق لمختبر. قمت بإعداد Tetragon للمرة الأولى وأردت معرفة ما إذا كان يمكنه فعلاً إيقاف ثغرة تصعيد صلاحيات محلية حقيقية وحالية في النواة. وقد استطاع. يوثّق هذا بالضبط ما شغّلته، وما الذي انطلق، والأهم من ذلك، حدود ما يثبته. وهو ليس بديلاً عن تثبيت التصحيحات.
الملفات: ملف README هذا (متكامل، مع الأدلة المضمّنة) · block-dirtyfrag.yaml (السياسة)
| الاستغلال | DirtyFrag - PoC عام: V4bel/dirtyfrag |
| ثغرات CVE | CVE-2026-43284 (كتابة page-cache عبر xfrm-ESP)، CVE-2026-43500 (كتابة page-cache عبر RxRPC) |
| المضيف | Ubuntu 24.04.4 LTS، Tetragon v1.7.0 (مستقل، systemd) |
خط الأساس - بدون سياسة، النواة 6.8.0-88 (قابلة للاستغلال) | PoC → uid=0(root) |
مع السياسة - النواة 6.8.0-88 | PoC أُنهي بـ SIGKILL عند socket(AF_RXRPC)؛ المستخدم يبقى uid=1000 |
النواة المُصحّحة 6.8.0-134 | PoC يفشل من تلقاء نفسه (rc=4)؛ خطاف المقبس ما زال ينطلق، كشف وليس تخفيفًا (ملاحظة) |
| طبيعة التحكم | تحكم تعويضي / تصحيح افتراضي، ليس إصلاحًا للنواة |
DirtyFrag هي سلسلة تصعيد صلاحيات محلية مبنية على بدائيتي كتابة مستقلتين في page-cache داخل نواة لينكس: واحدة في مسار فك التشفير الموضعي xfrm/ESP (IPsec) (CVE-2026-43284)، والأخرى في مسار RxRPC (CVE-2026-43500). تسمح كل واحدة منهما لمستخدم محلي غير مميز بكتابة بايتات يتحكم بها المهاجم في صفحات page-cache للقراءة فقط، مثل الصورة المخزنة مؤقتًا لثنائي setuid-root مثل /bin/su — ومن هناك الحصول على صلاحيات الجذر. تتم الكتابة في الذاكرة فقط؛ لا يتم تعديل الملف على القرص أبدًا، لذلك لا يرى مراقبو سلامة الملفات شيئًا. إنها نفس فئة الأخطاء البرمجية التي ينتمي إليها Dirty Pipe وCopy Fail. تم ربط الثغرتين عمدًا: إذا كان أحد المسارين غير متاح في بيئة معينة، يعمل المسار الآخر.
على هذا المضيف، يتم تقييد مساحات أسماء المستخدمين غير المميزة بواسطة AppArmor (kernel.apparmor_restrict_unprivileged_userns = 1)، وهو ما يحجب نصف سلسلة ESP. ويُبقي هذا مسار RxRPC، الذي يفتح مقبس AF_RXRPC (عائلة العناوين 33) — وهذه هي الخطوة التي تقتلها هذه السياسة.
الإصلاح الحقيقي هو نواة مُصحّحة. كل ما هنا هو حل مؤقت لمضيف لا يمكنك تحديثه بعد. أصدرت Ubuntu إصلاحات DirtyFrag قبل هذا الاختبار ببعض الوقت، لذا فهذه ثغرة N-day معروفة، وليست 0-day حية — الهدف هو إظهار ما يمكن أن يفعله تحكم وقت التشغيل على مضيف لا يزال، لسبب ما، يعمل بنواة قابلة للاستغلال.
السياسة كاملة: block-dirtyfrag.yaml. تُثبّت خطافي kprobe.
الخطاف 1 - تجهيز المقابس (sys_socket). التحكم الأساسي المعتمد عليه. يجب أن يستدعي مسار RxRPC في الاستغلال socket(AF_RXRPC, …) في كل تشغيل، سواءً كانت أي وحدة نواة محمّلة مسبقًا أم لا. تطابق السياسة على عائلة العناوين:
AF_RXRPC) → Sigkill. على أي مضيف ليس عميل AFS، لا يوجد في الواقع أي شيء شرعي يفتح مقبس AF_RXRPC، لذا فإن القتل الشامل هنا آمن وبثقة عالية.AF_ALG) → Post (تدقيق فقط، بدون قتل). AF_ALG هي واجهة برمجة التشفير للنواة في مساحة المستخدم، ولديها بالفعل مستخدمون شرعيون (cryptsetup، أدوات libkcapi، بعض سير عمل FIPS). القتل بناءً على العائلة وحدها سيسبب نتائج إيجابية خاطئة، لذا هذا الفرع يسجّل فقط. مسار التطبيق هو: قم بالتدقيق لفترة، وأنشئ قائمة سماح NotIn من الثنائيات المرصودة فعلًا، ثم فكر في ترقية هذا الفرع إلى Sigkill.الخطاف 2 - التحميل التلقائي للوحدات القابلة للاستغلال (security_kernel_module_request). دفاع في العمق. ينطلق عندما يُطلب من النواة تحميل عائلة وحدات قابلة للاستغلال تلقائيًا (esp4, esp6, rxrpc, الأسماء المستعارة للمقابس net-pf-33/net-pf-38, قوالب التشفير pcbc/fcrypt). القيد: ينطلق فقط عندما لا تكون الوحدة مقيمة بالفعل — بعد أول تشغيل للاستغلال في جلسة إقلاع، تكون تلك الوحدات محمّلة ويصمت هذا الخطاف. إنها طبقة إقلاع بارد، وليست التحكم الأساسي.
معًا: يلتقط الخطاف 1 الاستغلال سواء كانت الوحدات محمّلة أم لا؛ يضيف الخطاف 2 إنذارًا أبكر وأكثر تحديدًا على مضيف بارد.
6.8.0-88-generic القابلة للاستغلال (خط الأساس يصل إلى صلاحيات الجذر). يحتوي الجهاز أيضًا على 6.8.0-134-generic مثبتة — النواة المُصحّحة — وقد تم تأكيد ذلك مباشرةً: إعادة الإقلاع إلى -134 تجعل PoC يفشل من تلقاء نفسه (rc=4, بدون جذر)، مع أو بدون السياسة (انظر الملاحظة أدناه). لإعادة إنتاج عرض التخفيف، أقلع إلى 6.8.0-88 من GRUB (ما زالت مثبتة) — على أي نواة مُصحّحة لا يوجد شيء لتخفيفه.AF_ALG.lockdown,capability,landlock,yama,apparmor — بدون bpf). لذا يتم التنفيذ باستخدام Sigkill من kprobe، وليس رفضًا من LSM داخل النواة. يقع القتل عند استدعاء النظام socket() — قبل بدائية الكتابة — لذلك ينهي العملية في خطوة مبكرة إلزامية بدلاً من "حجب الثغرة" نفسها.الخطوات 1–5 كلها من نفس جلسة الإقلاع 6.8.0-88 (النواة القابلة للاستغلال).
1. البيئة - Ubuntu 24.04.4، النواة 6.8.0-88-generic، Tetragon v1.7.0 نشط تحت systemd، مساحات أسماء المستخدم غير المميزة مقيدة.
$ uname -r
6.8.0-88-generic
$ tetra version
CLI version: v1.7.0
$ systemctl is-active tetragon
active
2. الشروط المسبقة (السياسة معطلة OFF) - نقطة بداية نظيفة: لا توجد وحدات قابلة للاستغلال مقيمة، ولا حالة xfrm، ولا سياسة محمّلة.
$ lsmod | grep -E 'esp4|esp6|rxrpc' || echo "(none loaded)"
(none loaded)
$ sudo ip xfrm state # (empty)
$ sudo tetra tp list
ID NAME STATE FILTERID NAMESPACE SENSORS KERNELMEMORY MODE NPOST NENFORCE NMONITOR
3. خط الأساس (السياسة معطلة OFF) - الاستغلال يعمل. هذا ما يثبت أن النواة قابلة للاستغلال فعلًا؛ هذا التوثيق كله يعتمد عليه.
$ ./exp ; id
uid=0(root) gid=0(root) groups=0(root)
4. تحميل السياسة.
$ sudo tetra tp add /etc/tetragon/tetragon.tp.d/block-dirtyfrag.yaml
tracing policy "…/block-dirtyfrag.yaml" added
5. التطبيق الإجباري (السياسة مفعلة ON) - القتل. يتم إنهاء الاستغلال بإشارة عند استدعاء socket() ولا يصل أبدًا إلى su:
🚀 process /home/…/dirtyfrag/exp
❓ syscall /home/…/dirtyfrag/exp __x64_sys_socket
Kernel:
0x0: __x64_sys_socket+0x5
0x0: do_syscall_64+0x7f
0x0: entry_SYSCALL_64_after_hwframe+0x78
💥 exit /home/…/dirtyfrag/exp SIGKILL
الحدث المنظم يؤكد كلاً من التطابق والقتل — process_kprobe على استدعاء نظام المقبس، ثم process_exit بإشارة لنفس PID:
// process_kprobe — the AF_RXRPC match
{ "process_kprobe": {
"process": { "pid": 24083, "uid": 1000, "binary": ".../dirtyfrag/exp" },
"function_name": "__x64_sys_socket",
"args": [ { "int_arg": 33, "label": "family" } ],
"policy_name": "block-dirtyfrag",
"action": "KPROBE_ACTION_POST"
} }
// process_exit — same PID, killed by signal
{ "process_exit": {
"process": { "pid": 24083, "binary": ".../dirtyfrag/exp" },
"signal": "SIGKILL"
} }
يقرأ action الخاص بـ kprobe القيمة KPROBE_ACTION_POST لأن إجراء Post هو ما يُصدر الحدث المرئي؛ بينما إجراء Sigkill هو ما يُنتج خروج SIGKILL المنفصل. الحدثان معًا هما الإثبات، التطابق والقتل.
ملاحظة: يحتوي التقاط
07الخام أيضًا على سلسلةmessageمن مراجعة سابقة للسياسة (قبل تقسيم فرعAF_ALGإلى تدقيق فقط). تطابقfamily: 33ونتيجتهSIGKILLمتطابقان عبر المراجعات؛ يختلف ذلك النص فقط.
بعد تشغيل النواة القابلة للاستغلال، تمت إعادة إقلاع المضيف إلى 6.8.0-134-generic (نواة Ubuntu المُصحّحة) وتم تشغيل PoC مرة أخرى:
$ ./exp # policy OFF
dirtyfrag: failed (rc=4) # exploit fails on its own — no root
$ ./exp # policy ON
Killed # SIGKILL at socket(AF_RXRPC)
لنكون دقيقين بشأن ما يظهره هذا:
rc=4, بدون جذر) لأن النواة مُصحّحة. لا يوجد شيء تمنعه السياسة. المقارنة قبل/بعد التي تحمل ادعاء التخفيف موجودة فقط على النواة القابلة للاستغلال -88.socket(AF_RXRPC)، لذلك ما زال Tetragon ينهيه بـ SIGKILL ويُظهر المحاولة في السجل، على نواة مُصحّحة تمامًا كما على نواة غير مصحّحة. لهذا قيمة ككشف عن محاولة وكدفاع في العمق، لكنه ليس نفس إيقاف استغلال ناجح.AF_RXRPC شرعيًا وقد يكسره القتل الشامل. تحقق من ذلك لكل عقدة.AF_ALG مخصص للتدقيق فقط بحكم التصميم. أي متغير يستخدم مسار AF_ALG فقط مع وحدات محمّلة بالفعل سيتم تسجيله، وليس قتله، حتى ترقي هذا الفرع إلى وضع التنفيذ (بعد قائمة السماح).AF_RXRPC قبل بدائية الكتابة. هذا الترتيب هو ما يجعل القتل المبكر فعالاً؛ إنه ليس ضمانًا بشأن كل استغلال ممكن.الإعداد: Tetragon v1.7.0 مستقل، تم تشغيله عبر systemctl؛ تم تحميل السياسات باستخدام tetra tracingpolicy add.