
منفذ C عبر المنصات لـ Copy Fail Linux LPE (CVE-2026-31431). تم الإفصاح عنه في 2026-04-29 بواسطة Theori / Xint.
English (en) ∙ 日本語 (ja) ∙ 简体中文 (zh-cn) ∙ 한국어 (ko) ∙ Русский (ru)
إعادة تنفيذ بلغة C عبر الأنظمة لثغرة Copy Fail Linux LPE (CVE-2026-31431)، التي تم الكشف عنها في 2026-04-29 بواسطة Theori / Xint. انظر إلى الشرح الرسمي على copy.fail للوصف الكامل للثغرة، والجدول الزمني، وعملية اكتشاف Theori.
الإثبات العملي المنشور هو سكريبت بايثون بحجم 732 بايت. هذه النسخة بلغة C تُظهر أن نفس الاستغلال يمكن التعبير عنه بلغة C محمولة يمكن تجميعها لأي بنية يدعمها nolibc، دون وجود كتل سداسية عشرية خاصة بكل بنية أو تجميع مباشر في المصدر الخاص بالمشروع.
مؤلف هذه النسخة: Tony Gies [email protected]. الاكتشاف والإفصاح الأصلي: Theori / Xint.
copy-fail-c/
├── exploit.c the dropper (binary-mutation variant)
├── exploit-passwd.c the dropper (/etc/passwd UID-flip variant)
├── vulnerable.c non-destructive vulnerability checker
├── payload.c the body that gets dropped (setgid+setuid+execve sh)
├── utils.c, utils.h shared AF_ALG/splice page-cache mutation primitive
├── Makefile build orchestration
├── nolibc/ vendored from torvalds/linux tools/include/nolibc
└── README.md this file
بعد make:
├── payload tiny static ELF, embedded into the dropper as bytes
├── payload.o payload wrapped as a relocatable .o by `ld -r -b binary`
├── exploit dropper, binary-mutation variant
├── exploit-passwd dropper, /etc/passwd UID-flip variant
└── vulnerable non-destructive vulnerability checker
exploit.c يفتح الهدف الثنائي للقراءة فقط، ثم لكل نافذة بحجم 4 بايت من الحمولة المضمنة يقوم بتنفيذ فك تشفير AEAD زائف واحد عبر AF_ALG يتم تغذية مدخلات النص المشفر له عن طريق splice() من صفحات مخبأ الصفحات للهدف. التحسين الموضعي لقالب authencesn يعالج صفحات المصدر المنقولة كمدخلات النص المشفر ووجهة النص العادي في نفس الوقت، لذا فإن فك التشفير (الفاشل) قد قام بالفعل بالكتابة فوق صفحة مخبأ الصفحات بحلول الوقت الذي يرفض فيه التحقق من المصادقة الطلب. بعد 4 * N تكرار، تم استبدال الصورة المخبأة للهدف بايت ببايت بالحمولة. يؤدي تنفيذ execve() على الهدف إلى تحميل الصفحات المحورة؛ لا يزال inode على القرص setuid root، لذا يمنح النواة صلاحيات root ويشغل الحمولة.
payload.c عبارة عن C محمولة عادية: setgid(0); setuid(0); execve("/bin/sh", ...). يوفر nolibc _start، وآلية استدعاء النظام، والتلاعب بالسجلات الخاص بكل بنية.
نموذج ثانٍ، exploit-passwd.c، يحوّر أربعة بايتات من مخبأ صفحات /etc/passwd بدلاً من صورة الهدف الثنائي setuid. لا يحتاج إلى حمولة مضمنة ويعمل على الأنظمة التي يكون فيها مسار تحوير الثنائي محظورًا، ولكن سطح السحب الخاص به أضيق بكثير.
vulnerable.c ليس استغلالًا. يقوم بإنشاء ملف محلي testfile يحتوي على السلسلة init، ثم يشغل نفس الأولية patch_chunk() ضد مخبأ صفحات ذلك الملف الخاص به لاستبدال البايتات بـ vulnerable. إذا تطابقت المحتويات المقروءة، فإن النواة قيد التشغيل في نطاق CVE-2026-31431. لا يتم تعديل inode على القرص أبدًا؛ تتم إزالة testfile عند الخروج؛ يتبخر تحوير مخبأ الصفحات معه. يعمل بدون صلاحيات. يخرج برمز 100 إذا كان ضعيفًا، و0 إذا تم تشغيل الأولية لكن التحوير لم ينجح، و2 إذا كانت عائلة مقبس AF_ALG أو قالب authencesn غير متوفرة بحيث لا يمكن تحديد حالة التصحيح، و1 لأخطاء تشغيلية أخرى.
الافتراضي (محلي للبنية المضيفة):
make
ترجمة متقاطعة لـ aarch64 (أو أي بنية Linux أخرى تم تثبيت سلسلة أدوات تقاطع لها):
make CC=aarch64-linux-gnu-gcc LD=aarch64-linux-gnu-ld
البنى المدعومة بواسطة nolibc المضمنة (وفقًا للمصدر الأعلى): x86_64، i386، arm، aarch64، riscv32/64، mips، ppc، s390x، loongarch، m68k، sh، sparc. يقوم nolibc بالتوجيه بناءً على ماكرو بنية المترجم، لذا فإن اختيار CC/LD المناسب كافٍ.
المتطلبات للبناء:
cc، gcc، أو أي متغير تقاطع)ld -r -b binary (كل من binutils ld و lld يدعمانه)linux/if_alg.h و <asm/unistd.h> (Debian/Ubuntu: linux-libc-dev؛ المتغيرات المتقاطعة: عادة ما يتم جلبها بواسطة حزمة سلسلة الأدوات المتقاطعة)مجموعات الرؤوس الأقدم من Linux 5.6 تسبق __kernel_old_time_t و struct __kernel_old_timespec، والتي يستخدمها nolibc المضمن. يوفر compat.h (المضمن قسرًا في بناء الحمولة) هذه الأنواع عند غيابها، لذا فإن linux-libc-dev الأقدم لا يزال يبني. إنه لا يفعل شيئًا على الرؤوس 5.6+.
لا توجد تبعيات مكتبة خارجية. يتم بناء الحمولة بشكل مستقل ضد nolibc؛ يرتبط القاذف بمكتبة C المضيفة فقط من أجل fprintf و perror.
تحمل بعض ميزات سلسلة الأدوات الصغيرة معظم الثقل في الحفاظ على المصدر محمولاً والحمولة صغيرة.
nolibc/ هو بديل النواة الصغير لمكتبة C المعتمد على الرؤوس فقط، والمضمن من torvalds/linux tools/include/nolibc/. يوفر _start، وماكرو syscall() محمول، وواجهات استدعاء نظام مضمنة، مع ترميز اصطلاحات السجلات الخاصة بكل بنية في nolibc/arch-*.h. بناء الحمولة باستخدام -nostdlib -static -ffreestanding -Inolibc ينتج ELF ثابت صغير يستدعي النواة مباشرة دون جر بدء تشغيل glibc، أو تهيئة TLS، أو أنابيب حماية المكدس. النتيجة بعد التعبئة وإزالة أقسام الرأس (كلاهما أدناه): حوالي 720 بايت على x86_64، وحوالي 1.2 كيلوبايت على aarch64، مقابل حوالي 17 كيلوبايت لنفس payload.c المرتبط بـ musl-static أو حوالي 700 كيلوبايت مقابل glibc-static.
ld -r -b binary للتضمينيقوم ملف Makefile بتحويل الحمولة المبنية payload ELF إلى payload.o عبر ld -r -b binary -o payload.o payload. يصدر الرابط بايتات الإدخال حرفيًا كقسم بيانات لملف كائن قابل لإعادة الترحيل ويصنع ثلاثة رموز من اسم ملف الإدخال:
_binary_payload_start عنوان أول بايت من الحمولة
_binary_payload_end عنوان بعد آخر بايت من الحمولة
_binary_payload_size رمز مطلق قيمته الحجم بالبايتات
يعلن exploit.c عن الأولين كـ extern const unsigned char[] ويحسب الحجم كـ _binary_payload_end - _binary_payload_start.
-Wl,-N بالإضافة إلى max-page-size الضيقيتم ربط الحمولة بشكل ثابت مع -Wl,-N -Wl,-z,max-page-size=0x10، مما يدمج .text/.rodata/.data في مقطع LOAD واحد بمحاذاة ملف 16 بايت بدلاً من المحاذاة الافتراضية 4 كيلوبايت لكل مقطع. ينتج عن هذا تحذير "RWX permissions" من ld، وهو إعلامي فقط - حماية ذاكرة الحمولة الزمنية لا تهم برنامجها ذو الغرض الوحيد. بدون هذا العلم، يرتبط نفس الكود بحوالي 13 كيلوبايت على x86_64 (معظمها حشو صفري بين المقاطع)؛ معه، حوالي 1.3 كيلوبايت قبل إزالة رأس القسم أدناه.
بعد الربط، يقوم objcopy --strip-section-headers بإزالة جدول رأس قسم الحمولة و .shstrtab. يقوم محمل ELF للنواة بتعيين البرنامج من رؤوس البرنامج فقط، لذا فإن هذه البايتات لا تُحمّل أبدًا في وقت التشغيل، ولأن payload مضمن حرفيًا فإنها تضخم أيضًا عملية الإسقاط وعدد تكرارات patch_chunk. إزالتها تقلل حمولة x86_64 من حوالي 1.3 كيلوبايت إلى 720 بايت (من 322 تكرارًا بأربعة بايتات إلى 180). علمان في وقت الربط يحلقان الباقي: -Wl,--build-id=none يسقط ملاحظة build-id، و -fcf-protection=none يسقط ملاحظة x86 CET حيث يدعم المترجم ذلك.
يحتاج الشريط إلى binutils >= 2.40. تمرر البنيات المتقاطعة objcopy الهدف عبر OBJCOPY= (مثال: OBJCOPY=aarch64-linux-gnu-objcopy)؛ عندما لا يستطيع objcopy القيام بذلك، يطبع البناء ملاحظة ويحتفظ بحمولة صالحة وأكبر.
يحتوي هذا المستودع على نموذجين للاستغلال يشتركان في أولية تحوير مخبأ الصفحات AF_ALG/splice لكنهما يسحبان إلى تنفيذ root بشكل مختلف. ملفات الاعتمادية الخاصة بهما غير متكافئة، وهذا الاختلاف مهم عند التفكير في نماذج التهديد الواقعية.
exploit)يحوّر مخبأ صفحات هدف ثنائي setuid ببايتات الحمولة المضمنة، ثم ينفذ الثنائي. تمنح النواة صلاحيات root من بت setuid للثنائي غير المتغير على القرص، وتحمّل الصورة المعدلة في الذاكرة، وتشغّل الحمولة.