
Воспроизводит неаутентифицированную RCE-уязвимость ClamAV в ZendTo и повышение привилегий до root в авторизованной лабораторной среде, с закреплённым Docker-образом цели, проверкой по принципу fail-closed, привязанным к nonce выводом и очисткой.
Этот каталог — автономный комплект воспроизведения для авторизованной лаборатории: цепочка выполнения кода от ZendTo к ClamAV без учётной записи и её отдельное продолжение Smarty/root-cron на профиле по умолчанию. В комплект входят:
Начальный PoC выполняет выбранную вызывающей стороной команду от имени штатной служебной учётной записи clamav. Опциональный вариант продолжает эту безучётную точку опоры до выбранной вызывающей стороной команды от имени root. Оба по умолчанию используют id, отклоняют буквальный путь /root/flag и возвращают привязанный к nonce вывод по HTTPS.
Уязвимые связи группы, каталога, Smarty и root-cron являются значениями по умолчанию Debian-пакета/установщика ZendTo. Тем не менее достижимость root обусловлена окружением: этот точный положительный стенд использует том /var/zendto на ext4, работоспособные PHP CLI FFI/POSIX/exec и неограниченный процесс clamd. Включение AppArmor/SELinux или несовместимой семантики файловой системы может заблокировать продолжение на другой в остальном штатной установке.
Используйте это только на прилагаемом одноразовом стенде или другой системе, которую вам явно разрешено тестировать.
Авторитетная точная цель — локальный, намеренно неверсионируемый артефакт по пути:
image/zendto-installer-systemd-debian12.tar.zst
Git игнорирует архивы образов Docker. Перед точным регрессионным тестированием разместите авторизованную локальную копию по этому пути; scripts/setup.sh требует её записанные SHA-256 и ID образа. В source-recipe/README.md описано, как был собран и сохранён образ, производный от установщика. Новая сборка из живых репозиториев пакетов полезна для тестирования топологии, но не считается побайтно идентичной зафиксированной цели.
Скрипт настройки и точка входа контейнера отвергают любое несоответствие следующему профилю:
Полные значения находятся в PROFILE.json.
Это строже, чем фиксация только версии пакета ClamAV. Конструкция аллокатора также зависит от glibc, трафика парсера, управляемого CVD, настроек рабочих процессов и точной целевой библиотеки. FreshClam отключён в сохранённом образе, и запуск завершается сбоем, если daily.cvd изменился или появился daily.cld.
Текущий долго работающий исследовательский контейнер в родительском рабочем пространстве не является чистым стендом: более позднее тестирование отключило его форму внешнего отправителя и обновило ежедневную базу данных. Эти изменения намеренно исключены здесь. Этот комплект использует чистую базовую линию, на которой была продемонстрирована нативная эксплуатация.
/sys/fs/cgroupzstd, Python 3 с venv, компилятор C, file и GNU readelfЦель разделяет ядро хоста и реализацию ASLR. Поэтому поведение отображений, зависящее от ядра, остаётся переменной переносимости.
Одновременно с пространством имён cgroup хоста должен работать только один systemd-стенд. На исходной исследовательской машине остановите более старый стенд, не удаляя его:
docker stop zendto-installer-systemd-native
Позже его можно восстановить командой docker start zendto-installer-systemd-native.
Из этого каталога:
./scripts/verify-bundle.sh
./scripts/setup.sh
Скрипт настройки:
.venv с зафиксированными Python-зависимостями PoC;/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, письма, capability, загрузки, login POST или содержимое сканера. --skip-clamd-tcp-probe также отключает опциональную read-only проверку нативного 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
Перечисление по-прежнему создаёт обычные журналы веб-доступа и может инициализировать штатные кэши приложения. Его результат по Debian/Ubuntu-раскладке Apache не подтверждает нативный пакет, Build ID, libc, CVD, политику сокета или состояние MAC. Локальный скрипт проверки Docker предоставляет эти доказательства для данного стенда.
Это проверяет лабораторный bearer-токен и политику получателя, но не отправляет ни файл, ни содержимое сканера:
.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 перед отправкой одного носителя повреждения. Текущий агрегат из трёх раскладок — более поздняя коррекция жизненного цикла; его исторические этапы оракула с одной раскладкой и 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 или веб-шелла. DSO создаёт отдельный bearer AuthData только для вывода на 30 минут, выполняет /bin/sh -c от имени clamav и возвращает ограниченные stdout/stderr через анонимную страницу ZendTo. Успех требует одного оформленного результата со всеми следующими согласованными элементами:
zendto_unauth_clamav_rce_result=1Отсутствующее, дублированное, усечённое или несовпадающее доказательство — это неудачный прогон.
Запускайте это только после того, как ./scripts/verify-target.sh сообщит об успехе и [target], и [privesc-target]. Обёртка сначала выполняет тот же этап ClamAV без учётной записи, требует его точного ограниченного доказательства взвода, затем ожидает неизменённого штатного root-потребителя 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/электронной почты. chain-unauth не использует имя пользователя или пароль ZendTo.
Root-этап намеренно отдельный, хотя обёртка автоматизирует его передачу. Он:
savechunk.php;clamav;/zendto-root-result-<nonce>.json с verify=False, требуя euid=0, точный хеш команды, маркер восстановления и nonce завершения.Cron может заставить финальный опрос ждать почти час; таймаут по умолчанию — 3900 секунд. Root-команда ограничена 1024 байтами и 30 секундами, с не более чем 1 МиБ для stdout и stderr. Её JSON, адресуемый по nonce, намеренно доступен через веб в этом одноразовом лабе, поэтому используйте только безвредный тестовый вывод.
Для ручной оркестрации emit выводит точную команду для передачи в --command PoC без учётной записи; после доказательства взвода используйте poll с напечатанными ID запуска и SHA-256 точной root-команды. См. docs/SMARTY_ROOT_BRIDGE.md о конструкции и стенограмме аудита.
Ожидается, что этот вариант безопасно остановится при отсутствии предварительного условия. Не накладывайте попытки друг на друга и не повторяйте неоднозначный прогон: неудачные этапы могут оставить входящий PHP-файл, скрытый клон, блокировку или подменённый кэш. Для каждого root-теста используйте новый стенд:
./scripts/reset-lab.sh --yes-delete-lab-volume
./scripts/mint-lab-auth.py [email protected]
На один процесс clamd поддерживается одно успешное повреждение. Второй вызов оправдан только после того, как разрушительный зонд перезапуска свежего прогона вернёт ожидаемый ответ о сбое сканера, а EICAR докажет восстановление через активный сокет. Не переиспользуйте ID запуска, имя подготовленного чанка, bearer результата или клиентский рабочий каталог.
Немедленно останавливайтесь при любом перенаправлении, сетевом таймауте, неоднозначном теле/статусе, неожиданном чистом ответе или неудачном восстановлении EICAR. Никогда не отправляйте вручную предыдущий запрос повреждения повторно недостоверному демону.
Упакованная служба имеет Restart=no. Повторяемость обеспечивается всё ещё активным clamav-daemon.socket, а не политикой автоматического перезапуска службы. Если сокет отключён или ограничен на запуск, намеренные падения могут оставить сканер недоступным.
Ожидаемые остаточные данные цели включают журналы доступа/ошибок, записи о падении clamd или core-файлы, ограниченные файлы осиротевших заявок от чистых сторожей оракула, один DSO только на добавление /var/zendto/incoming/Bridge<nonce>.1 и недолговечные строки AuthData. Штатная очистка удаляет обычные осиротевшие заявки только по истечении более чем одного дня.
Root-вариант дополнительно оставляет nonce PHP-армер, скрытый клон и блокировку, публичный JSON-результат, принадлежащий root, и приватные /run stdout/stderr. Успешная обёртка восстанавливает подлинный templates_c перед выполнением root-команды, но эти файлы доказательств остаются до сброса контейнера и тома.
Отзовите лабораторный upload bearer-токен после тестирования:
./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__ игнорируются. Настройка пересобирает помощник из его C-исходника, указанного в манифесте, и отдельно проверяет локальный образ.
Статические проверки при упаковке, удалённая предварительная проверка, вероятностная передискретизация и инструментированные сквозные проверки зафиксированы в 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 |
| основная база данных | v63, SHA-256 0b2182d2…365 |
| ежедневная база данных | v28082, SHA-256 cddbcccf…906 |
| база данных байт-кода | 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-стенд |
| root-мост | штатный clamav в www-data; /var/zendto root:www-data 0775 |
| root-потребитель | штатный root-cron очистки, ежечасно на 25-й минуте |
| кэш Smarty | детерминированный Smarty 4.5.4, скомпилированный zendto.conf |
| файловая система exchange | именованный том /var/zendto на ext4 |