
POCs لإثبات الثغرة CVE-2026-42167 في ProFTPD
براهين إثبات مفهوم لثغرة CVE-2026-42167، وهي ثغرة حقن SQL في مسار تسجيل mod_sql في ProFTPD تتيح لمهاجم غير مصادَق تنفيذ SQL عشوائي — وحقن مستخدمين خلفيين في قاعدة بيانات مصادقة FTP أو تحقيق تنفيذ برمجيات عن بُعد على مضيف قاعدة البيانات.
جميع براهين إثبات المفهوم خاصة بـ PostgreSQL، لكن براهين المستخدم الخلفي تعمل مع خلفيتي MySQL وsqlite مع بعض التعديلات على الاستعلام المُحقن (ونتركها كتمرين للقارئ).
is_escaped_text() في SQLLog الخاص بـ mod_sql (CVE-2026-42167, CWE-89)يقوم mod_sql في ProFTPD بتسجيل كل أمر FTP عبر آلتي SQLLog / SQLNamedQuery. عند تحليل متغيرات التنسيق مثل %U (اسم المستخدم الأصلي) أو %{basename} (مكوّن اسم الملف)، يستدعي الإطار is_escaped_text() ليقرر ما إذا كان التهريب (escaping) مطلوبًا — ويتخطى التهريب تمامًا لأي قيمة تبدأ وتنتهي بعلامة اقتباس مفردة ولا تحتوي على علامات اقتباس مفردة داخلية (مثل '|| (SELECT 1) ||'). الفحص نحوي بحت ويُطبَّق على مدخلات خام يتحكم فيها المهاجم من جلسة FTP، لذا لا يمكنه التمييز بين "تم تهريبها مسبقًا بواسطة كود موثوق" و"صُممت بواسطة مهاجم لتبدو مهربة مسبقًا".
النمط الموثق القياسي يغلّف متغيرات التنسيق بعلامات اقتباس مفردة لضمان سلامة SQL:
SQLNamedQuery log_activity INSERT "'%U', '%r', '%m'" activity_log
SQLLog * log_activity
SQLLog ERR_* log_activity
عندما يوفّر مهاجم قيمة بالشكل '<payload>'، ينتج عن الاستبدال ''<payload>'' في SQL النهائي — حيث تُغلق حرفية السلسلة الفارغة علامات الاقتباس المحيطة ويُنفَّذ الحمولة كـ SQL خام. مع PostgreSQL (PQexec) أو SQLite (sqlite3_exec) فإن دعم الاستعلامات المكدّسة يعني أن الحمولة يمكن أن تكون عبارة INSERT أو UPDATE أو CREATE TABLE أو COPY TO PROGRAM كاملة.
الكود الثغري موجود في contrib/mod_sql.c (إطار SQL المشترك)، وليس في أي خلفية محددة، لذا تتأثر جميع خلفيات SQL — لكن سطح الهجوم يعتمد على كيفية تكوين المسؤول للتسجيل. يكون الخادم قابلاً للاستغلال عندما يتحقق كلاهما مما يلي:
عرّف المسؤول SQLNamedQuery INSERT (أو UPDATE) تحتوي سلسلة تنسيقه على أحد هذه المتغيرات التي يتحكم فيها المهاجم، مغلّفًا بعلامات اقتباس مفردة — مثل "'%U', '%m'". المتغيرات التي تأتي من مدخلات المهاجم هي:
يحوّل التجاوز مسار التسجيل إلى بدائية SQL عشوائية على الخلفية. السيناريوهان الأكثر تأثيرًا:
حقن مستخدم خلفي بصلاحيات عشوائية (تجاوز المصادقة). يقرأ كود مصادقة SQL في ProFTPD أسماء المستخدمين وتجزئات كلمات المرور وuid وgid ودليل المنزل والصدفة من نفس جدول users الذي يمكن أن يكتب فيه سجل INSERT الآن. إن INSERT INTO users المكدّس يزرع حسابًا يختاره المهاجم — uid=0، homedir=/، كلمة مرور نصية صريحة — ثم يسجّل المهاجم الدخول بشكل طبيعي مع وصول كامل لنظام الملفات عبر خادم FTP. يُستغل قبل المصادقة عبر مسار %U + SQLLog ERR_*، أو بعد المصادقة عبر أي متغير يتحكم فيه المهاجم ومرتبط بأمر يمكن للمهاجم إصداره (مثل %{basename} + SQLLog STOR). يعمل على PostgreSQL وSQLite (كلاهما يدعم الاستعلامات المكدّسة).
تنفيذ برمجيات عن بُعد على مضيف قاعدة البيانات عبر COPY TO PROGRAM. ينفّذ أمر PostgreSQL COPY (SELECT …) TO PROGRAM '<cmd>' الوسيط <cmd> عبر صدفة على خادم قاعدة البيانات. إن حقن استعلام مكدّس يُصدر COPY TO PROGRAM يمنح تنفيذ أوامر نظام تشغيل عشوائية بصلاحية مستخدم نظام postgres، وهو ما يكفي لسرقة بيانات الاعتماد والحركة الجانبية والثبات. يمكن الوصول إليه عبر نفس مسارات التشغيل كحالة المستخدم الخلفي (قبل المصادقة عبر أو بعدها عبر وما إلى ذلك). خاص بـ PostgreSQL، ويتطلب أن يتمتع دور قاعدة بيانات ProFTPD بصلاحيات المستخدم الفائق (الشرط المسبق لـ ) — وهو شائع في النشر أحادي المستأجر حيث يكون الدور هو مالك قاعدة البيانات.
تركز براهين إثبات المفهوم في هذا المستودع على PostgreSQL تحديدًا — الحالة الأقوى، لأن PQexec() يدعم الاستعلامات المكدّسة وCOPY TO PROGRAM يوفر تنفيذ أوامر نظام تشغيل مباشر. التجاوز نفسه موجود في إطار SQL المشترك ويؤثر على جميع الخلفيات:
PQexec، RCE عبر COPY TO PROGRAM.sqlite3_exec، وتعمل تحت PRIVS_ROOT في عامل proftpd — حقن المستخدم الخلفي يعمل بنفس الطريقة؛ بدائية RCE مختلفة (لا يوجد مكافئ لـ COPY TO PROGRAM، لكن جدول users القابل للكتابة على خلفية SQL تعمل بصلاحيات الجذر كافٍ تمامًا).users أو تنفيذ أوامر نظام التشغيل من داخل عبارة INSERT الواحدة أصعب. التلاعب بعبارة واحدة (استخراج عبر استعلام فرعي في خانة VALUES، أو أعمى زمني، أو أعمى قائم على الأخطاء) مباشر؛ تحويل ذلك إلى كتابة في جدول مختلف يتطلب تجاوز عدة عقبات:
mod_sql_mysql الدالة mysql_real_query() دون CLIENT_MULTI_STATEMENTS، لذا فإن ; INSERT INTO users … اللاحقة تُرفض من الاتصال.في إعداد PostgreSQL، توضح البراهين مساري تشغيل تمثيليين. المجموعات الأخرى من الجدول أعلاه مكافئة من حيث المبدأ:
%U + USER. يجعل SQLLog ERR_* هذا المسار غير مصادَق بالكامل.%{basename} + STOR. يتطلب أي بيانات اعتماد FTP لكن لا يتطلب صلاحيات تتجاوز القدرة على رفع ملف.setup/ — إعداد تلقائي للبيئة. يستنسخ شجرة مصدر ProFTPD المثبّتة على الالتزام الذي أُبلغت هذه النتائج ضده، ويبني الخادم مع mod_sql + mod_sql_postgres، ويقيم مجموعة Docker Compose (ProFTPD + PostgreSQL) مع بيانات أولية (seed data) وإعدادات SQLLog الثغرة.
pocs/ — خمسة سكربتات استغلال. جميعها سكربتات Python قائمة بذاتها ولا تتطلب سوى المكتبة القياسية.
preauth_user_backdoor.py — تشغيل قبل المصادقة عبر %U → حقن مستخدم خلفي (uid=0، homedir=`) في قاعدة بيانات المصادقة. لا يتطلب بيانات اعتماد ولا مستخدمًا فائقًا لقاعدة البيانات. هذا البرهان خاص بـ PostgreSQL، لكن يمكن استغلال المشكلة مع خلفية mysql أو sqlite أيضًا.preauth_user_rce.py — تشغيل قبل المصادقة عبر %U → RCE على مضيف PostgreSQL عبر COPY TO PROGRAM. دون بيانات اعتماد. يتطلب أن يكون دور قاعدة بيانات ProFTPD مستخدمًا فائقًا في PostgreSQL.postauth_stor_backdoor.py — تشغيل بعد المصادقة عبر %{basename} → حقن مستخدم خلفي. يتطلب أي مستخدم FTP مصادَق. هذا البرهان خاص بـ PostgreSQL، لكن يمكن استغلال المشكلة مع خلفية mysql أو sqlite أيضًا.المتطلبات الأساسية: Docker وGit وPython 3.10+ وuv.
cd setup
./setup.sh
التشغيل الأول يستنسخ شيفرة مصدر ProFTPD ويبني الخادم (من 2 إلى 3 دقائق تقريبًا). التشغيلات اللاحقة تعيد استخدام ذاكرة التخزين المؤقت للبناء وتبدأ خلال ثوانٍ.
عند اكتمال الإعداد، يطبع تفاصيل البيئة الكاملة (نقاط نهاية FTP وقاعدة البيانات، وبيانات اعتماد الاختبار، وأوامر براهين جاهزة للصق). اتبع تلك التعليمات لتشغيل البراهين.
لإيقاف البيئة وتفكيكها:
cd setup
./teardown.sh
| المتغير | المعنى |
|---|
%A | سلسلة كلمة مرور تسجيل الدخول المجهول (anonymous) |
%J | معاملات الأمر (كل ما بعد الفعل) |
%S | سلسلة رسالة الاستجابة (قد تتضمن إدخال المهاجم المعاد في الأخطاء) |
%U | اسم المستخدم الأصلي من USER (يُعيَّن قبل المصادقة، ومتاح حتى عند فشل تسجيل الدخول) |
%d | اسم الدليل (آخر مكوّن في المسار) |
%l | استجابة RFC 1413 ident (يتحكم فيها المهاجم إذا كان يشغّل identd) |
%m | أسلوب/فعل FTP (المهاجم يختار الأمر الذي يرسله) |
%r | أمر FTP الكامل (الفعل + الوسائط) |
%u | اسم المستخدم المصادَق |
%{basename} | مكوّن اسم الملف من وسيطة المسار، دون بادئة دليل |
تبدو %f و%F و%D وكأنها يتحكم فيها المهاجم لكنها تتحلل دائمًا إلى مسارات مطلقة تبدأ بـ /، لذا لا يمكنها تلبية اشتراط is_escaped_text() بالبدء بعلامة اقتباس مفردة.
ربط المسؤول SQLNamedQuery بتوجيه SQLLog لأمر FTP (أو فئة أوامر) يمكن للمهاجم الوصول إليه. إن حرفَي البدل الموثقَين على نطاق واسع SQLLog * وSQLLog ERR_* هما الحالة الأوسع، وهما ما يجعل مسار %U متاحًا قبل المصادقة: ERR_* يُطلق عند فشل USER، لذا لا حاجة لأي بيانات اعتماد. التوجيهات الخاصة بأمر معين مثل SQLLog STOR تغطي أي مستخدم مصادَق.
%U%{basename}COPY TO PROGRAMuserspostauth_stor_rce.py — تشغيل بعد المصادقة عبر %{basename} → RCE على مضيف PostgreSQL. يتطلب أي مستخدم FTP مصادَق ودور قاعدة بيانات مستخدمًا فائقًا في PostgreSQL.postgres_blind_dump.py — تشغيل قبل المصادقة عبر %U → استخراج أعمى زمني لجدول users في قاعدة بيانات المصادقة. لا يستخدم الاستعلامات المكدّسة، لذا يعمل في النشرات التي يتمتع فيها دور قاعدة بيانات proftpd بصلاحيات ضئيلة فقط (مجرد INSERT على جدول السجل) وحيث تفشل براهين المستخدم الخلفي / RCE أعلاه بأمان. يسحب كل بايت من كل عمود — بما في ذلك عمود passwd — عبر بحث ثنائي بتًّا بتًّا بواسطة pg_sleep(). يحاكي ما كانت ستعمله sqlmap تلقائيًا، لكنه مكتوب يدويًا دون أي اعتماديات خارجية.