
ممارسة تحليل ثغرة التلاعب بـ Linux Page Cache القائمة على AF_ALG/splice والاستجابة لها
هذا المشروع عبارة عن مشروع مصغّر لتحليل مبدأ عمل ثغرة Copy Fail (CVE-2026-31431) في نواة Linux، ومقارنة الحالة قبل وبعد الترقيع باستخدام أداة الفحص غير المدمرة من مستودع PoC العام.
لم يتم تنفيذ أي استغلال فعلي لتعديل ثنائيات setuid أو تعديل /etc/passwd، بل تم التركيز على التحقق الآمن من وجود الثغرة باستخدام ملف testfile مؤقت، وتحليلها من منظور الاكتشاف والتخفيف.
الهدف من هذا المشروع ليس مجرد تشغيل استغلال (exploit)، بل فهم تركيبة البنى الداخلية التي تؤدي إلى ظهور ثغرة في نواة Linux، وتوثيق كيفية التحقق منها والاستجابة لها من المنظور التشغيلي.
نطاق العمل كما يلي:
تحليل مبدأ الثغرة
↓
تحليل بنية كود PoC
↓
ممارسة باستخدام أداة فحص غير مدمرة
↓
المقارنة قبل وبعد الترقيع
↓
توثيق خطط الاكتشاف/التخفيف
في هذه الممارسة العملية، تم تشغيل vulnerable.c فقط من مستودع copy-fail-c.
معلومات النواة قبل الترقيع:
Linux ubuntu-server 6.8.0-53-generic #55-Ubuntu SMP PREEMPT_DYNAMIC Fri Jan 17 15:37:52 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux
معلومات النواة بعد الترقيع:
Linux ubuntu-server 6.8.0-134-generic #134-Ubuntu SMP PREEMPT_DYNAMIC Fri Jun 26 18:43:11 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux
ملخص تغييرات حزمة النواة:
- linux-image-6.8.0-53-generic 6.8.0-53.55
- linux-image-generic 6.8.0-53.55+1
+ linux-image-6.8.0-134-generic 6.8.0-134.134
+ linux-image-generic 6.8.0-134.134
+ linux-generic 6.8.0-134.134
+ linux-headers-generic 6.8.0-134.134
ثغرة Copy Fail هي ثغرة تنشأ عند اجتماع مسار معالجة AEAD في AF_ALG بنواة Linux مع سلوك النسخ الصفري (zero-copy) في splice()، مما قد يسمح باستخدام page cache لملف قابل للقراءة فقط كوجهة كتابة خاطئة.
المكوّنات الأساسية كما يلي:
التدفق الأساسي للثغرة كما يلي:
ملف قابل للقراءة
↓
يُحمَّل في Linux page cache
↓
تمرير مرجع page cache إلى مسار التشفير AF_ALG عبر splice()
↓
ترابط scatterlist للإدخال والإخراج عبر معالجة AEAD في نفس المكان (in-place)
↓
حدوث كتابة مؤقتة (scratch write) بحجم 4 بايت أثناء معالجة authencesn
↓
الكتابة تحدث في page cache وليس في مخزن إخراج منفصل
↓
حدوث طفرة page cache (mutation)
بمعنى أنه بينما ينقل splice() مرجع page cache، وتربط معالجة AEAD في نفس المكان بين الإدخال والإخراج عبر المسار نفسه، فإن authencesn هو ما يسبب الكتابة الفعلية بحجم 4 بايت.
بنية المستودع المستخدم في الممارسة كما يلي:
copy-fail-c/
├── exploit.c
├── exploit-passwd.c
├── vulnerable.c
├── payload.c
├── utils.c
├── utils.h
├── Makefile
└── nolibc/
utils.cيتمثل جوهر utils.c في primitive لطفرة page cache من عائلة patch_chunk(). تربط هذه الدالة page cache للملف المستهدف بمسار المعالجة المشفرة عبر AF_ALG وsplice()، وتبيّن ما إذا كان جزء من page cache يُستبدل أثناء معالجة AEAD في النواة غير المُرقّعة.
vulnerable.cلا يلمس vulnerable.c أي ملفات نظام فعلية. فهو ينشئ testfile مؤقتًا في الدليل الحالي ويتحقق مما إذا كانت page cache الخاصة بهذا الملف قد تعرّضت للتعديل.
في هذا المشروع، تم تشغيل هذا الملف فقط.
قبل تنفيذ تحديث النواة، تم تسجيل حالة نظام التشغيل والنواة والحزم.
mkdir -p ~/copyfail-mini/{before,after,logs}
cd ~/copyfail-mini
uname -a | tee before/uname.txt
cat /etc/os-release | tee before/os-release.txt
dpkg -l | grep -E 'linux-image|linux-headers|linux-generic|linux-virtual' | tee before/kernel-package.txt
git clone https://github.com/jihwan77/copy-fail-c.git
cd copy-fail-c
make clean
make vulnerable
في هذه الممارسة، لم يتم بناء ثنائيات الاستغلال عبر make الافتراضي، بل تم استخدام target vulnerable فقط.
./vulnerable > ../before/vulnerable-output.txt 2>&1
echo $? >> ../before/vulnerable-output.txt
cat ../before/vulnerable-output.txt
نتيجة التشغيل قبل الترقيع:

التقييم:
exit code 100
→ تم تأكيد طفرة page cache
→ تم تأكيد عمل primitive الخاصة بـ Copy Fail في النواة قبل الترقيع
بعد حفظ النتائج قبل الترقيع، تم تنفيذ تحديث حزم Ubuntu.
sudo apt update
sudo apt full-upgrade -y
sudo reboot
بعد إعادة التشغيل، تغيّرت النواة على النحو التالي:
Before: 6.8.0-53-generic
After : 6.8.0-134-generic
cd ~/copyfail-mini/copy-fail-c
make clean
make vulnerable
./vulnerable > ../after/vulnerable-output.txt 2>&1
echo "exit_code=$?" >> ../after/vulnerable-output.txt
cat ../after/vulnerable-output.txt
نتيجة التشغيل بعد الترقيع:

مقارنة النتائج:
إن exit_code=2 بعد الترقيع لا يعني ببساطة «غير قابلة للاستغلال». التفسير الدقيق كما يلي:
نظرًا لعدم تسجيل template الخاص بـ authencesn(hmac(sha256),cbc(aes)) في AF_ALG،
لم تتمكن أداة الفحص من الحكم مباشرة على وجود الثغرة
وأظهر فحص إضافي أنه بعد تحديث Ubuntu، تم حظر تحميل وحدة algif_aead.
lsmod | grep -E 'af_alg|algif_aead'
النتيجة:
af_alg 32768 0
لم تكن وحدة algif_aead محمّلة.
sudo modprobe algif_aead
النتيجة:
modprobe: ERROR: ../libkmod/libkmod-module.c:1084 command_do() Error running install command '/bin/false' for module algif_aead: retcode 1
modprobe: ERROR: could not insert 'algif_aead': Invalid argument
التحقق من إعداد الحظر:
grep -R "algif_aead" /etc/modprobe.d /lib/modprobe.d 2>/dev/null
النتيجة:
/etc/modprobe.d/disable-algif_aead.conf:# Disable algif_aead module due to CVE-2026-31431 (AKA copy.fail)
/etc/modprobe.d/disable-algif_aead.conf:install algif_aead /bin/false
لذلك، من الأدق تفسير النتيجة بعد الترقيع على النحو التالي:
بعد تحديث Ubuntu الأمني، تغيّرت النواة إلى 6.8.0-134-generic،
وتم تطبيق إجراء تخفيف بحظر وحدة algif_aead عبر kmod.
ونتيجة لذلك، لم تتمكن أداة الفحص vulnerable من مستودع copy-fail-c
من الربط (bind) إلى template الخاص بـ authencesn(hmac(sha256),cbc(aes)) في AF_ALG الذي يتطلبه PoC،
ولم يتم التقدم إلى مرحلة طفرة page cache.
بمعنى أن ما تم تأكيده في هذه الممارسة ليس «تأثير ترقيع كود النواة وحده»، بل أن تحديث النواة وتطبيق إجراء تخفيف بحظر وحدة algif_aead بعد تحديث Ubuntu الأمني أدى إلى عدم تقدم مسار PoC نفسه.
نظرًا لأن Copy Fail يمكنها تعديل page cache دون تعديل ملف القرص مباشرة، فإن الاكتشاف المعتمد على تجزئة الملفات فقط له حدود. لذلك يُعد الاكتشاف المعتمد على سلوك استدعاءات النظام (syscall) أمرًا مهمًا.
في هذه الممارسة، تمت مراقبة استدعاءات النظام التالية باستخدام auditd:
sudo auditctl -a always,exit -F arch=b64 -S socket -F a0=38 -k copyfail_afalg
sudo auditctl -a always,exit -F arch=b64 -S bind -k copyfail_bind
sudo auditctl -a always,exit -F arch=b64 -S splice -k copyfail_splice
sudo auditctl -a always,exit -F arch=b64 -S sendmsg -k copyfail_sendmsg
socket(AF_ALG)تم في السجلات تأكيد أن العملية vulnerable أنشأت socket من نوع AF_ALG.
comm=vulnerable
syscall=socket
success=yes
a0=alg
key=copyfail_afalg
bind()في بيئة ما بعد الترقيع/التخفيف، حاولت العملية vulnerable الربط (bind) إلى template الخاص بـ authencesn لكنها فشلت.
comm=vulnerable
syscall=bind
success=no
exit=ENOENT(No such file or directory)
saddr_fam=alg
key=copyfail_bind
يعني ذلك أن PoC في بيئة ما بعد الترقيع تمكن من إنشاء socket من نوع AF_ALG، لكنه فشل في مرحلة الربط إلى template الخاص بـ authencesn(hmac(sha256),cbc(aes)).
splice() / sendmsg()بعد الترقيع، ونظرًا للفشل في مرحلة bind()، لم تتمكن أداة الفحص من التقدم إلى مرحلتي splice() وsendmsg(). لذلك لم يُلاحَظ تدفق تنفيذ vulnerable بشكل ذي دلالة في سجلات استدعاءات النظام تلك.
عند مراقبة ثغرات من نوع Copy Fail في البيئات التشغيلية، يمكن ملاحظة توليفة السلوكيات التالية:
في هذه الممارسة، تم تأكيد أحداث socket(AF_ALG) وفشل bind() عبر auditd.
أبسط استجابة هي تطبيق التحديثات الأمنية من التوزيعة.
sudo apt update
sudo apt full-upgrade -y
sudo reboot
في هذه الممارسة، تم الانتقال بعد التحديث إلى بيئة Ubuntu 24.04.4 / kernel 6.8.0-134-generic.
algif_aeadبعد تحديث Ubuntu، تم تأكيد الإعداد التالي:
/etc/modprobe.d/disable-algif_aead.conf
install algif_aead /bin/false
يمنع هذا الإعداد تحميل وحدة algif_aead، وبالتالي يمنع الدخول إلى مسار AF_ALG AEAD الذي يتطلبه PoC.
نظرًا لأن استخدام AF_ALG مباشرةً قد لا يكون شائعًا في تطبيقات الخوادم العادية، يمكن اعتماد استدعاء socket(AF_ALG) كنقطة اكتشاف.
يمكن تلخيص نتائج هذه الممارسة على النحو التالي:
قبل الترقيع:
Ubuntu 24.04.2 / kernel 6.8.0-53-generic
vulnerable checker exit code 100
تأكيد طفرة page cache
→ تأكيد عمل primitive الخاصة بـ Copy Fail
بعد الترقيع:
Ubuntu 24.04.4 / kernel 6.8.0-134-generic
vulnerable checker exit code 2
فشل الربط إلى template الخاص بـ authencesn
تأكيد إعداد حظر وحدة algif_aead
→ عدم تقدم مسار PoC نفسه إلى مرحلة طفرة page cache
وبالتالي فإن خلاصة هذا المشروع كما يلي:
في النواة قبل الترقيع، عملت primitive الخاصة بطفرة page cache في Copy Fail فعليًا.
بعد تطبيق تحديث Ubuntu الأمني، تغيّرت النواة إلى6.8.0-134-generic، وتُطبّق إعدادات تمنع وحدةalgif_aead(تبدو معتمدة على kmod).
ونتيجة لذلك، لم تتمكن أداة الفحص نفسها من الربط إلى template الخاص بـauthencesn(hmac(sha256),cbc(aes))في AF_ALG الذي يتطلبه PoC، لذا لم تتقدم إلى مرحلة طفرة page cache.
بمعنى أنه لا يمكن القول من نتائج هذه الممارسة إن «ترقيع كود النواة بحد ذاته منع طفرة page cache مباشرة»، لكن ما يمكن تأكيده قطعًا بناءً على السجلات والنتائج حتى الآن هو أنه بعد تحديث Ubuntu الأمني تم تطبيق إجراء تخفيف بحظر وحدة algif_aead، مما أدى إلى حظر مسار PoC.
Ubuntu Security Notice - USN-8226-1: kmod update
https://ubuntu.com/security/notices/USN-8226-1
Ubuntu Blog - Fixes available for CVE-2026-31431 Copy Fail
https://ubuntu.com/blog/copy-fail-vulnerability-fixes-available
copy-fail-c PoC repository
https://github.com/jihwan77/copy-fail-c
النقاط الجوهرية التي تم تأكيدها عبر هذا المشروع كما يلي:
1. Copy Fail هي ثغرة في النواة تجمع بين AF_ALG وsplice() ومعالجة AEAD في نفس المكان (in-place) وauthencesn وpage cache.
2. في بيئة Ubuntu 24.04.2 / kernel 6.8.0-53 قبل الترقيع، أكدت أداة الفحص غير المدمرة حدوث طفرة page cache.
3. بعد الترقيع، في بيئة Ubuntu 24.04.4 / kernel 6.8.0-134، حدث فشل في مرحلة الربط إلى authencesn.
4. أظهر الفحص الإضافي أن تحميل وحدة algif_aead كان محظورًا عبر إعداد /bin/false.
5. أمكن عبر auditd ملاحظة أحداث socket(AF_ALG) وفشل bind.
6. يمكن تلخيص الاستجابة التشغيلية في: تحديث النواة/حزم الأمان، تقييد algif_aead، مراقبة استدعاءات AF_ALG، وفحص ثنائيات setuid.
| البند | قبل الترقيع | بعد الترقيع |
|---|
| نظام التشغيل | Ubuntu 24.04.2 LTS | Ubuntu 24.04.4 LTS |
| النواة | 6.8.0-53-generic | 6.8.0-134-generic |
| الحساب | مستخدم عادي client | مستخدم عادي client |
| أداة الفحص | vulnerable | vulnerable |
| طريقة الاختبار | فحص غير مدمر باستخدام testfile مؤقت | إعادة تشغيل نفس أداة الفحص |
| المكوّن | الدور |
|---|
| Linux page cache | آلية نواة تخزّن محتوى ملفات القرص مؤقتًا في RAM |
splice() | استدعاء نظام بنسخ صفري يربط البيانات بمرجع داخل النواة دون نسخها إلى مساحة المستخدم |
AF_ALG | واجهة تتيح استخدام واجهة برمجة التشفير (crypto API) لنواة Linux من مساحة المستخدم مثل socket |
| معالجة AEAD في نفس المكان (in-place) | تحسين يعالج المخازن المؤقتة في نفس المخزن بدلًا من فصل مخزن الإدخال عن مخزن الإخراج |
authencesn | قالب تشفير (crypto template) يحدث كتابة مؤقتة (scratch write) بحجم 4 بايت أثناء معالجة AEAD |
| الملف | الدور | الاستخدام في هذه الممارسة |
|---|
utils.c, utils.h | تنفيذ primitive لطفرة page cache المعتمدة على AF_ALG/splice | استخدامها في التحليل وتشغيل vulnerable |
vulnerable.c | أداة فحص غير مدمرة للتحقق من الثغرة باستخدام testfile مؤقت | تم تشغيله |
exploit.c | متغيّر (variant) لتعديل page cache لثنائي setuid root | لم يتم تشغيله |
exploit-passwd.c | متغيّر لتعديل page cache لملف /etc/passwd | لم يتم تشغيله |
payload.c | الحمولة (payload) التي ستُنفَّذ بصلاحيات root | لم يتم تشغيله |
Makefile | أتمتة البناء | استخدام target vulnerable فقط |
nolibc/ | كود بديل خفيف عن libc لبناء حمولة ELF ثابتة صغيرة | للتحليل فقط |
| البند | قبل الترقيع | بعد الترقيع |
|---|
| النواة | 6.8.0-53-generic | 6.8.0-134-generic |
| نتيجة أداة الفحص | VULNERABLE | authencesn template not registered |
| رمز الخروج | 100 | 2 |
| طفرة page cache | تم تأكيدها | لم تصل أداة الفحص إلى مرحلة الطفرة |
| التفسير | عمل primitive الخاصة بـ Copy Fail | فشل الدخول إلى مسار AEAD/authencesn الذي يتطلبه PoC |
| سلوك الاكتشاف | المعنى |
|---|
socket(AF_ALG, ...) | محاولة استخدام واجهة crypto API للنواة |
bind() مع authencesn | محاولة استخدام template التشفير AEAD/authencesn |
splice() | تمرير مرجع page cache لملف إلى مسار داخلي في النواة |
sendmsg() / recvmsg() | تنفيذ طلب تشفير AF_ALG |
| تشغيل ثنائي setuid | احتمالية تحويلها إلى رفع صلاحيات (cashout) |