
# PoC لاستغلال ثغرة في نواة لينكس مكتوب بلغة Rust لـ CVE-2026-31431 (كتابة ذاكرة التخزين المؤقت للصفحات في AF_ALG) بالإضافة إلى دفاع وقت التشغيل عبر eBPF يمنع إنشاء مآخذ AF_ALG عبر LSM أو kprobe دون إعادة تشغيل النظام.
CVE-2026-31431 (Copy Fail) هي ثغرة تصعيد صلاحيات محلية في وحدة algif_aead في نواة لينكس (نظام AF_ALG الفرعي). خلل منطقي أُدخل في أغسطس 2017 يسمح لأي مستخدم محلي غير مميز بكتابة 4 بايتات مُتحكم بها في ذاكرة التخزين المؤقت للصفحات لأي ملف قابل للقراءة عبر AF_ALG + splice()، ثم تنفيذ ثنائي setuid تالف للحصول على صلاحيات الجذر. الاستغلال حتمي — لا سباقات، لا إزاحات نواة، لا انهيار للنظام. يؤثر على جميع توزيعات لينكس الرئيسية التي تشحن أنوية من 4.14 حتى 7.0-rc.
CVSS: 7.8 | تم الإصلاح في: النواة 7.0، 6.19.12، 6.18.22 | إصلاح الخط الرئيسي: commit a664bf3d603d
المصادر: copy.fail، The Hacker News، تنبيه CloudLinux
crates/
├── exp/ # استغلال PoC (إعادة تنفيذ بلغة Rust)
├── copy_fail_guard/ # مُحمّل eBPF في مساحة المستخدم — وضع LSM
├── copy_fail_guard-ebpf/ # برنامج eBPF LSM (ليس في مساحة العمل)
├── copy_fail_guard_kprobe/ # مُحمّل eBPF في مساحة المستخدم — وضع kprobe
└── copy_fail_guard_kprobe-ebpf/ # برنامج eBPF kprobe (ليس في مساحة العمل)
scripts/
└── build_guard.sh # بناء بنقرة واحدة → المخرجات إلى dist/
السبب الجذري هو سلسلة من ثلاث ميزات نواة مستقلة تتفاعل بشكل غير آمن:
AF_ALG — يعرض واجهة برمجة التشفير للنواة لمساحة المستخدم غير المميزةsplice() — ينقل بيانات الملف بنسخ صفرية كمراجع لصفحات ذاكرة التخزين المؤقت (وليس نسخاً) إلى قائمة التشتت للتشفيرauthencesn — يستخدم مخزن الإخراج الخاص بالمستدعي كمساحة مؤقتة، ويكتب 4 بايتات عند dst[assoclen + cryptlen]في 2017، جعل تحسين في المكان في algif_aead.c (72548b093ee3) req->src == req->dst، مما ربط صفحات ذاكرة التخزين المؤقت في قائمة التشتت الوجهة القابلة للكتابة. عندما يكتب authencesn بايتاته المؤقتة، يتجاوز مخزن الإخراج إلى صفحات ذاكرة التخزين المؤقت المرتبطة. يتحكم المهاجم في:
الصفحة التالفة لا تُعلَّم أبداً كمتسخة — الملف على القرص يبقى دون مساس، لكن execve() يقرأ من ذاكرة التخزين المؤقت للصفحات. إتلاف ثنائي setuid → صلاحيات الجذر.
إعادة تنفيذ بلغة Rust لـ PoC العامة المكونة من 732 بايت بلغة Python. يستهدف /usr/bin/su، ويربط صفحات ذاكرة التخزين المؤقت الخاصة به في مقبس AF_ALG AEAD، ويكتب فوقها حمولة قشرة مضغوطة.
يتطلب Rust 1.85+ (إصدار 2024).
cargo build --release -p copy_fail
الثنائي مخصص للينكس فقط. على المنصات الأخرى يخرج برسالة خطأ Unsupported.
تحذير: هذا يستغل ثغرة نواة حقيقية. شغّله فقط على أنظمة تملكها وتتحكم بها، ويفضل أن تكون جهازاً افتراضياً يمكن التخلص منه.
شغّل جهازاً افتراضياً بنواة قابلة للاستغلال (أي توزيعة رئيسية بنواة < 7.0 / < 6.19.12 / < 6.18.22).
انسخ الثنائي المبني إلى الجهاز الافتراضي وشغّله كمستخدم غير جذر:
./target/release/copy_fail
قابل للاستغلال: تظهر قشرة جذر (#). شغّل whoami للتأكد من أنك root.
غير قابل للاستغلال (نواة مُصلحة): تفشل عملية AF_ALG أو يتصرف ثنائي su بشكل طبيعي. سترى خطأً أو طلب كلمة مرور su عادياً.
دفاع نواة وقت التشغيل يمنع CVE-2026-31431 دون ترقية النواة أو إعادة التشغيل. يستخدم eBPF لاعتراض إنشاء مقبس AF_ALG، مما يقطع الخطوة الأولى للاستغلال.
يتم توفير وضعين. المُحمّل بنقرة واحدة يختار تلقائياً أفضل وضع متاح:
┌──────────────────────────────────────────────────┐
│ run_guard.sh │
│ • يكتشف دعم BPF LSM │
│ • LSM متاح → copy_fail_guard (EPERM) │
│ • LSM غير متاح → copy_fail_guard_kprobe (KILL) │
└──────────────┬───────────────────────────────────┘
│
┌──────────────▼───────────────────────────────────┐
│ برنامج eBPF │
│ إذا كانت عائلة المقبس == 38 (AF_ALG) │
│ → منع (EPERM أو SIGKILL) │
│ وإلا │
│ → السماح │
└──────────────────────────────────────────────────┘
منع AF_ALG له تأثير شبه معدوم على الأنظمة النموذجية:
AF_ALGAF_ALG (مثل OpenSSL مع محرك afalg المفعل، بعض مسارات تفريغ التشفير المدمجة)socket(AF_ALG, ...)بناء بنقرة واحدة (موصى به):
./scripts/build_guard.sh
هذا يثبّت تلقائياً سلاسل الأدوات المفقودة (nightly، bpf-linker)، ويجمع جميع برامج eBPF ومُحمّلات مساحة المستخدم، ويخرج كل شيء إلى dist/:
dist/
├── copy_fail_guard.bpf.o # برنامج eBPF LSM
├── copy_fail_guard_kprobe.bpf.o # برنامج eBPF kprobe
├── copy_fail_guard # مُحمّل مساحة المستخدم (LSM)
├── copy_fail_guard_kprobe # مُحمّل مساحة المستخدم (kprobe)
└── run_guard.sh # اختيار تلقائي: sudo ./run_guard.sh
انسخ دليل dist/ إلى أي جهاز مستهدف وشغّل sudo ./run_guard.sh لتفعيل الحماية.
بناء يدوي (خطوة بخطوة):
الخطوة 1: تجميع برامج eBPF (يجب أن يتم على لينكس):
# نسخة LSM
cd crates/copy_fail_guard-ebpf
cargo +nightly build --target bpfel-unknown-none -Z build-std=core --release
# نسخة kprobe
cd crates/copy_fail_guard_kprobe-ebpf
cargo +nightly build --target bpfel-unknown-none -Z build-std=core --release
الخطوة 2: بناء مُحمّلات مساحة المستخدم:
cargo build --release -p copy_fail_guard -p copy_fail_guard_kprobe
موصى به — يختار تلقائياً أفضل وضع:
sudo ./dist/run_guard.sh
يدوي — تشغيل وضع محدد:
# وضع LSM (يتطلب lsm=bpf)
sudo GUARD_BPF_OBJ=path/to/copy_fail_guard.bpf.o RUST_LOG=info ./copy_fail_guard
# وضع kprobe (يعمل في كل مكان)
sudo GUARD_BPF_OBJ=path/to/copy_fail_guard_kprobe.bpf.o RUST_LOG=info ./copy_fail_guard_kprobe
اضغط Ctrl-C لفصل برنامج eBPF واستعادة السلوك الطبيعي.
مع تشغيل الحارس في طرفية واحدة:
# في طرفية أخرى، جرّب الاستغلال:
./target/release/copy_fail
# وضع LSM: "error: Operation not permitted"
# وضع kprobe: "已杀死" / "Killed"
# أو اختبر مباشرة مع Python:
python3 -c "import socket; socket.socket(38, 5, 0)"
# وضع LSM: PermissionError: [Errno 1] Operation not permitted
# وضع kprobe: Killed
AF_ALG في مساحة المستخدم دون قيد أو شرط. لا توجد آلية لإعفاء عمليات محددة بواسطة PID أو cgroup أو اسم الأمر. هذا مخطط له في نسخة مستقبلية (عبر خرائط eBPF HashMap). بالنسبة للغالبية العظمى من الأنظمة هذا جيد لأن لا شيء تقريباً يستخدم AF_ALG.bpf-linker (تستهدف bpfel-unknown-none ولا يمكن أن تكون أعضاء عاديين في مساحة العمل).معظم التوزيعات لا تفعّل BPF LSM افتراضياً. إذا أردت وضع LSM الأنظف (EPERM بدلاً من SIGKILL):
# تحقق من LSMs الحالية:
cat /sys/kernel/security/lsm
# إذا كان "bpf" مفقوداً، أضفه:
sudo sed -i 's/^GRUB_CMDLINE_LINUX="\(.*\)"/GRUB_CMDLINE_LINUX="\1 lsm=lockdown,capability,yama,apparmor,bpf"/' /etc/default/grub
sudo update-grub && sudo reboot
وضع kprobe يعمل بدون هذه الخطوة.
بعد ترقية النواة إلى ≥ 7.0 / ≥ 6.19.12 / ≥ 6.18.22:
# تأكد من إصدار النواة
uname -r
# أعد تشغيل الاستغلال — يجب ألا ينتج قشرة جذر بعد الآن
./target/release/copy_fail
بدلاً من ذلك، تأكد من تحييد الوحدة القابلة للاستغلال:
modinfo algif_aead | grep filename
# إذا كانت مدمجة، احظر initcall (يتطلب إعادة تشغيل)
sudo grubby --update-kernel=ALL --args="initcall_blacklist=algif_aead_init"
sudo reboot
| وضع LSM | وضع kprobe |
|---|
| كيف يمنع | يعيد -EPERM (رفض إنشاء المقبس) | SIGKILL (قتل العملية) |
| متطلب النواة | ≥ 5.7 مع معامل الإقلاع lsm=bpf | ≥ 5.3، بدون معاملات خاصة |
| يحتاج إعادة تشغيل للتفعيل | ربما (إذا لم يكن lsm=bpf مضبوطاً) | لا |
| نقطة الخطاف | خطاف LSM socket_create | kprobe __sys_socket |