
تقرير حول "Dirty Frag"، وهي سلسلة ثغرات تصعيد الامتيازات المحلية (LPE) في لينكس تسمح لمستخدم غير مميز بالحصول على صلاحيات الجذر.
مختبر إعادة إنتاج وكشف لسلسلة تصعيد صلاحيات محلية في نواة لينكس.
الحالة: تم التحقق. أكملت إعادة الإنتاج والتحقق بدون ملفات والكشف على مستوى استدعاءات النظام في المختبر (النواة 6.18.9+kali-amd64). هذه الوثيقة سجل مخبري. كل ادعاء أدناه تمت ملاحظته أثناء تشغيل إعادة الإنتاج. لقطات الشاشة والقطع الأثرية هي التقاطات حقيقية من الجهاز الافتراضي.
يجمع Dirty Frag بين خطأين منطقيين حتميين في نواة لينكس. تسمح هذه الأخطاء لمستخدم محلي غير مميز بالكتابة فوق ذاكرة التخزين المؤقت للصفحات (page cache) للملفات القابلة للقراءة فقط (مثل /usr/bin/su) والحصول على قشرة صلاحيات الجذر:
يستخدم كلا المتغيرين نفس النمط الجذري مثل Dirty Pipe وCopy Fail. يضع استدعاء النظام splice(2) مرجعًا إلى صفحة من ذاكرة التخزين المؤقت للصفحات لملف في فتحة frag الخاصة بـ sk_buff على جانب المرسل. يمكن للمهاجم قراءة هذا الملف فقط. ثم يقوم كود النواة على جانب الاستقبال بتنفيذ عملية تخزين تشفيرية في المكان فوق هذا الجزء. يؤدي هذا إلى تغيير ذاكرة التخزين المؤقت للصفحات في الذاكرة العشوائية (RAM). لا يحدث أي كتابة على القرص، لذا لا يمكن لمراقبة سلامة الملفات (AIDE, Tripwire) رؤيتها. الهجوم حتمي. لا يحتوي على نافذة سباق ولا يسبب انهيار النواة عند الفشل.
cac2661c53f3 (2017‑01) إلى f4c50a4034e6 (تم التصحيح 2026‑05‑05)2dc334f1a63a (2023‑06) إلى aa54b1d27fe0 (تم التصحيح 2026‑05‑10)CVE-2026-43284 = 8.8 (عالية)، CVE-2026-43500 = 7.8 (عالية)Dirty Frag هو تصعيد صلاحيات محلي بدون ملفات. يفسد ذاكرة التخزين المؤقت للصفحات في الذاكرة، وليس الملف على القرص. لا يمكن لمراقبة سلامة الملفات التقليدية رؤيته. يجب أن يحدث الكشف على طبقة استدعاءات النظام. تستخدم السلسلة بدائيات استدعاءات النظام التالية: socket(AF_ALG)/socket(AF_RXRPC)، وsplice، وunshare(CLONE_NEWUSER|CLONE_NEWNET). ينشئ مسار ESP أيضًا مقابس UDP من نوع AF_INET ومقابس netlink. هذه الطبقة هي محور هندسة الكشف في هذا المستودع.
يستخدم كلا المتغيرين نفس نقطة الاستغلال: تشفير في المكان يقوم بتخزين بايتات على صفحة من ذاكرة التخزين المؤقت للصفحات يضعها المهاجم باستخدام splice(2).
UDP_ENCAP_ESPINUDP.vmsplice، ثم 16 بايت من /usr/bin/su عند إزاحة الملف المستهدفة باستخدام splice.splice واحدة تدفع الأنبوب إلى مقبس الإرسال. يضبط splice_to_socket() علامة MSG_SPLICE_PAGES. يضع هذا صفحة ذاكرة التخزين المؤقت لـ /usr/bin/su مباشرة في skb->frags[0].xfrm4_udp_encap_rcv، ثم xfrm_input، ثم esp_input(). يتجاوز فرع skip_cow الضعيف () دالة . يقوم بتنفيذ مع استخدام صفحة ذاكرة التخزين المؤقت كمصدر ووجهة معًا.يتحكم المهاجم في كل من الموقع (إزاحة splice) والقيمة (4 بايتات). يعمل التحقق من المصادقة بعد عملية التخزين، لذا لا تكتشف طبقة التشفير الكتابة أبدًا. يتطلب هذا المتغير CAP_NET_ADMIN ويستخدم unshare(CLONE_NEWUSER|CLONE_NEWNET).
تنفذ rxkad_verify_packet_1() فك تشفير كتلة واحدة pcbc(fcrypt) مباشرة على جزء skb المثبت بـ splice. لا تنسخ البيانات أولاً. يختار المهاجم مفتاح جلسة (add_key("rxrpc", …)) بحيث يساوي decrypt(ciphertext) قيمة desired_plaintext. ينتج عن هذا تخزين 8 بايتات. يستهدف هذا المتغير /etc/passwd. لا يحتاج إلى مساحة أسماء مستخدمين. يتطلب وحدة rxrpc.ko (محملة افتراضيًا على Ubuntu).
يستهدف إثبات المفهوم العام /usr/bin/su. يكتب 48 عملية تخزين ESP بحجم 4 بايتات لكل منها (192 بايتًا عند إزاحة الملف 0). يستبدل أول بايتات ذاكرة التخزين المؤقت للصفحات بملف ELF ثابت لقشرة الجذر. نقطة دخول ELF تنفذ setgid(0); setuid(0); setgroups(0,NULL); execve("/bin/sh", …). ثم يعطي تنفيذ واحد لـ execve("/usr/bin/su") قشرة صلاحيات الجذر.
تصحيح ESP (الرئيسي f4c50a4034e6) يضع علامة على أجزاء الصفحات التي تصل عبر splice() بعلامة SKBFL_SHARED_FRAG. يتحقق فرع skip_cow في esp_input() الآن أيضًا من هذه العلامة. تمر مقابس skb ذات الأجزاء المشتركة عبر skb_cow_data() قبل فك تشفير AEAD في المكان.
تصحيح RxRPC (الرئيسي aa54b1d27fe0) يضيف فحص skb->data_len بجوار فحص skb_cloned() الموجود. تنسخ النواة مقبس skb غير الخطي مع بيانات مقسمة إلى صفحات قبل فك تشفير pcbc(fcrypt) في المكان.
لقطة شاشة لإعداد مختبر VirtualBox:

.
├── README.md # سجل المختبر هذا
├── detection/
│ ├── dirtyfrag.rules # قواعد كشف على مستوى استدعاءات النظام لـ auditd
│ ├── ausearch_dirtyfrag_observed.txt # مخرجات كشف الاستغلال الحقيقية
│ ├── sigma/
│ │ └── dirty_frag_exploit.yml # قاعدة Sigma لكشف SIEM
│ └── yara/
│ └── dirty_frag_exploit.yar # قاعدة YARA لكود إثبات المفهوم على القرص/الذاكرة
├── mitigation/
│ └── dirtyfrag_mitigation.sh # قائمة حظر الوحدات + مسح ذاكرة التخزين المؤقت للصفحات
├── poc/
│ └── check_vulnerable.py # فاحص أولي غير مدمر
├── reports/
│ └── incident-dirtyfrag.md # دليل الاستجابة للحوادث
└── screenshots/ # التقاطات حقيقية من الجهاز الافتراضي للمختبر
poc/check_vulnerable.py وتأكيد النواة/الوحدات/مساحة أسماء المستخدمينtestuser غير مميز/usr/bin/su التالفة والمستعادةdrop_caches أو إعادة التشغيل)detection/dirtyfrag.rules والتحقق من تنبيهات auditdcat /etc/os-release | head -3
uname -r
لقطة شاشة للتحقق من إصدار النواة:

يجب أن تكون النواة أقدم من إصلاحات مايو 2026 (f4c50a4034e6 / aa54b1d27fe0). إذا قمت بتشغيل apt upgrade بعد ذلك التاريخ، سيفشل الاستغلال. راجع استكشاف الأخطاء وإصلاحها.
يُبلغ فاحص آمن عما إذا كان الجهاز الافتراضي هدفًا محتملاً. يتحقق من النواة قيد التشغيل، ووجود وحدات esp4/esp6/rxrpc، وما إذا كانت مساحات أسماء المستخدمين غير المميزة متاحة. يتطلب متغير ESP هذه المساحات.
python3 poc/check_vulnerable.py
الحكم المتوقع: [*] potentially vulnerable -- proceed in a disposable VM only
يحاكي testuser غير المميز مهاجمًا بدون صلاحيات خاصة.
sudo useradd -m testuser
sudo passwd testuser
su - testuser
id
لقطة شاشة: يُظهر id UID 1001. يؤكد هذا وصولًا غير جذري.

من الحساب غير المميز:
git clone https://github.com/V4bel/dirtyfrag.git
cd dirtyfrag
gcc -O0 -Wall -o exp exp.c -lutil
./exp
عند النجاح، يصحح الاستغلال ذاكرة التخزين المؤقت للصفحات لـ /usr/bin/su. يكتب 48 عملية تخزين ESP بحجم 4 بايتات لكل منها (ملف ELF لقشرة الجذر بحجم 192 بايتًا عند إزاحة الملف 0). ثم يُسقط قشرة جذر تفاعلية باستخدام forkpty.
لقطة شاشة لتوفر الوحدات ومراجعة المصدر والتجميع النظيف:

id
whoami
لقطة شاشة: يُظهر id قيمة uid=0(root) بعد ./exp.

يقرأ sha256sum من خلال ذاكرة التخزين المؤقت للصفحات. بينما تكون كتابة الاستغلال نشطة، يختلف التجزئة عن الأصلية. بعد المسح، يعود التجزئة إلى الأصلية. يثبت هذا الزوج قبل/بعد أن الملف الثنائي على القرص لم يُلمس أبدًا.
sha256sum /usr/bin/su # 1) بينما ذاكرة التخزين المؤقت للصفحات ملوثة -> تجزئة مختلفة
لقطة شاشة: التجزئة مختلفة عن تجزئة الحزمة (الذاكرة العشوائية مسمومة، القرص سليم).

التجزئة التالفة المرصودة (ذاكرة التخزين المؤقت للصفحات): 3fc29078bd77150b5d6fbb368f632bf02c1a3704816f03fb694ec2e81e77bde4
بعد الاستغلال، تحتوي ذاكرة التخزين المؤقت للصفحات على البيانات التالفة. امسحها دائمًا:
echo 3 | sudo tee /proc/sys/vm/drop_caches
# أو أعد تشغيل الجهاز الافتراضي
ثم تحقق من الاستعادة (باسم testuser):
sha256sum /usr/bin/su # الآن يطابق تجزئة الحزمة الأصلية
su - # يطلب كلمة المرور مرة أخرى — لا جذر تلقائي
لقطة شاشة: التجزئة المستعادة (الأصلية على القرص).

التجزئة الأصلية المرصودة (على القرص): 2b4f8770bd35bba5cdc5cfe292bc1d988e92ec1786bf91cf83e0e86fac056eb6
بعد إعادة التشغيل، لم يُرجع dpkg -V util-linux أي مخرجات. يطابق /usr/bin/su على القرص الحزمة تمامًا، لذا فإن التجزئة التالفة 3fc29078… كانت موجودة فقط في ذاكرة التخزين المؤقت للصفحات.
قد لا يُخرج
drop_cachesالصفحة المسمومة. يحدث هذا إذا كانت عملية قيد التشغيل لا تزال تثبت الصفحة. في هذه الحالة، يستمرsha256sumوdpkg -Vفي إظهار المحتوى التالف. الإصلاح الموثوق هو إعادة التشغيل. لاحظ أنdpkg -Vيقرأ أيضًا من خلال ذاكرة التخزين المؤقت للصفحات. بينما الصفحة مسمومة، يُبلغ عن??5??????(MD5 مختلف؛ الحجم والوضع والمالك ووقت التعديل كلها متطابقة). يصبح صامتًا مرة أخرى بعد إعادة التشغيل. يثبت هذا أن الملف على القرص لم يُعدل أبدًا.
لا يؤدي المسح إلى تعطيل الاستغلال. يمسح فقط ذاكرة التخزين المؤقت للصفحات المسمومة. يجب تعطيل الوحدات بشكل منفصل (راجع التخفيف). تتطلب إزالة الوحدات المحملة بالفعل على مضيف مستغَل إعادة تشغيل.
Dirty Frag غير مرئي لمراقبة سلامة الملفات. يركز الكشف على بدائيات استدعاءات النظام التي يجب أن تستخدمها السلسلة.
detection/dirtyfrag.rules)# /etc/audit/rules.d/dirtyfrag.rules
-a always,exit -F arch=b64 -S socket -F a0=38 -F uid!=0 -k dirtyfrag_af_alg
-a always,exit -F arch=b64 -S socket -F a0=33 -F uid!=0 -k dirtyfrag_rxrpc
-a always,exit -F arch=b64 -S splice -F uid!=0 -k dirtyfrag_splice
-a always,exit -F arch=b64 -S unshare -F uid!=0 -k dirtyfrag_namespace
-w /usr/bin/su -p r -k dirtyfrag_suid_read
ملاحظة:
AF_ALG = 38وAF_RXRPC = 33على لينكس (راجع/usr/include/bits/socket.h). استخدمت المسودات السابقةa0=21. تلك القيمة غير صحيحة. AF_RXRPC هو 33، وليس 21.
النشر والتحقق:
sudo cp detection/dirtyfrag.rules /etc/audit/rules.d/
sudo systemctl restart auditd # أو: sudo auditctl -D && sudo auditctl -R /etc/audit/rules.d/dirtyfrag.rules
sudo auditctl -l
التنبيهات المتوقعة بعد إعادة تشغيل الاستغلال (اربط حسب PID):
ausearch -k dirtyfrag_af_alg
ausearch -k dirtyfrag_rxrpc
ausearch -k dirtyfrag_splice
ausearch -k dirtyfrag_namespace
تم تحميل جميع القواعد الخمس (auditctl -R، مع res=1 لكل CONFIG_CHANGE). التقطت القواعد إعداد مساحة أسماء الاستغلال:
time->Wed Aug 5 08:26:37 2026
type=PROCTITLE msg=audit(1785932797.328:592): proctitle="./exp"
type=SYSCALL msg=audit(1785932797.328:592): arch=c000003e syscall=272 success=yes exit=0 a0=50000000 a1=0 a2=0 a3=0 items=0 ppid=5198 pid=5199 auid=1000 uid=1001 gid=1001 euid=1001 suid=1001 fsuid=1001 egid=1001 sgid=1001 fsgid=1001 tty=pts2 ses=2 comm="exp" exe="/home/testuser/dirtyfrag/exp" subj=unconfined key="dirtyfrag_namespace"
مفكك الترميز: syscall=272 (unshare)، وa0=50000000 يساوي CLONE_NEWUSER | CLONE_NEWNET، وuid=1001 (testuser غير مميز)، وcomm="exp". السجل الكامل في detection/ausearch_dirtyfrag_observed.txt.
لقطة شاشة: يُظهر مخرج ausearch تحميل القاعدة وحدث الاستغلال.

الدليل الكامل في reports/incident-dirtyfrag.md: الملخص التنفيذي، والجدول الزمني، ومؤشرات الاختراق (IoCs)، وربط MITRE ATT&CK، والاحتواء، والاستئصال، والاستعادة، والدروس المستفادة.
تخفيف فوري أثناء التشغيل. لا يبقى بعد إعادة التشغيل للوحدات المحملة بالفعل (راجع الملاحظة أدناه):
sudo mitigation/dirtyfrag_mitigation.sh
ما يفعله:
/etc/modprobe.d/dirtyfrag.conf لحظر esp4 وesp6 وrxrpc. يتضمن أسطر blacklist وalias … off. سطر واحد بسيط يفوت هذه الأسطر (التحميل التلقائي عبر الاسم المستعار كان يعمل بخلاف ذلك).التأثير: تعطيل هذه الوحدات يجعل وظائف شبكات VPN عبر IPsec (ESP) ونظام ملفات AFS (RxRPC) تتوقف عن العمل.
الإصلاح الدائم: الترقية إلى نواة تحتوي على التصحيحات الرسمية. أو حظر الوحدات عند الإقلاع (initcall_blacklist=esp4,esp6,rxrpc).
هذا المستودع مخصص للبحث والتدريب الأمني الدفاعي المصرح به فقط.
راجع LICENSE. هذا المختبر للاستخدام التعليمي في بيئتك المعزولة فقط. لا تشغل إثبات المفهوم على أنظمة غير مصرح لك باختبارها.
| المتغير | CVE | نقطة الاستغلال | مسار التشغيل | يتطلب مساحة أسماء مستخدمين غير مميزة |
|---|
| كتابة ذاكرة التخزين المؤقت للصفحات عبر xfrm‑ESP | CVE‑2026‑43284 | crypto_authenc_esn_decrypt() في esp_input() | socket(AF_INET) مع تغليف UDP، ثم xfrm_input() | نعم (CAP_NET_ADMIN) |
| كتابة ذاكرة التخزين المؤقت للصفحات عبر RxRPC | CVE‑2026‑43500 | rxkad_verify_packet_1() (pcbc(fcrypt)) | socket(AF_RXRPC) | لا |
!skb_cloned() && !skb_has_frag_list()skb_cow_data()crypto_authenc_esn_decrypt() عملية تخزين للبتات الـ 32 العليا من ESN. تلك القيمة هي replay_esn->seq_hi. يختار المهاجم هذه القيمة عند تسجيل SA باستخدام سمة netlink XFRMA_REPLAY_ESN_VAL.| المكوّن | التفاصيل |
|---|
| برنامج المحاكاة الافتراضية | VirtualBox |
| الجهاز الافتراضي المستهدف | Kali Linux 2026.1 (تمت استعادة لقطة إلى حالة ضعيفة) |
| النواة | 6.18.9+kali‑amd64 (أقدم من إصلاحات مايو 2026) |
| إثبات المفهوم | V4bel/dirtyfrag (ملف C واحد) |
| الكشف | auditd (القواعد في detection/dirtyfrag.rules) |
| الطبقة | ما تراه | الحالة |
|---|
| auditd | مقابس AF_ALG وAF_RXRPC، وsplice، وunshare، وقراءات SUID | نعم. تم النشر والتحقق (ملف القواعد detection/dirtyfrag.rules) |
| Sigma | أنماط استدعاءات النظام لـ SIEM | نعم. القاعدة جاهزة (detection/sigma/dirty_frag_exploit.yml) |
| YARA | كود إثبات المفهوم على القرص أو في الذاكرة | نعم. القاعدة جاهزة (detection/yara/dirty_frag_exploit.yar) |
| FIM (AIDE/Tripwire) | تغييرات الملفات | لا. أعمى، لأنه لا يحدث أي كتابة على القرص |
| العرض | السبب المحتمل | الإصلاح |
|---|
يطبع ./exp رسالة failed / post-write verify failed | النواة مصححة (من مايو 2026 أو أحدث) | أقلع بنواة أقدم أو استعد لقطة قبل التحديث. أعد الفحص باستخدام poc/check_vulnerable.py |
يُرجع unshare(CLONE_NEWUSER) خطأ EPERM | مساحات أسماء المستخدمين غير المميزة معطلة (AppArmor أو sysctl) | تحقق من sysctl kernel.unprivileged_userns_clone. على Ubuntu، تحقق من AppArmor. يسمح Kali بذلك افتراضيًا |
esp4/esp6/rxrpc غير محملة | الوحدات غير متاحة | sudo modprobe esp4 esp6 rxrpc (يتم تحميل RxRPC تلقائيًا مع socket(AF_RXRPC)) |
ثنائي su "مصحح" لكن لا توجد قشرة جذر | تم تشغيل drop_caches بالفعل، أو ذاكرة التخزين المؤقت للصفحات مثبتة بواسطة عمليات | أعد تشغيل الاستغلال. إذا علق، أعد تشغيل الجهاز الافتراضي |
| تم تشغيل الاستغلال، ثم لا يزال يمكن الحصول على الجذر بعد التخفيف | لم يتم مسح ذاكرة التخزين المؤقت للصفحات، أو الوحدات محملة بالفعل قبل قائمة الحظر | echo 3 > /proc/sys/vm/drop_caches. التوقف الكامل يتطلب إعادة تشغيل |