
Высокоточный сканер для CVE-2026-41940 (cPanel & WHM обход аутентификации)
Высокоточный сканер для обхода аутентификации cPanel/WHM, отслеживаемого как CVE-2026-41940. Он выявляет уязвимые хосты без ложных отрицательных результатов, характерных для публичных PoC и детектов, и без срабатывания механизмов блокировки учётных записей и белого списка IP-адресов root, которые мешают наивному сканированию.
Инструмент также включает в себя отдельную, опциональную цепочку эксплуатации
для баги обхода пути CalDAV того же семейства на cpdavd (порты 2079 plain / 2080 TLS) —
CVE-2026-29205 —
которая позволяет удалённому атакующему читать произвольные файлы от root,
как только выполнится небольшой шаг настройки через SMTP. cPanel 11.134.0.26
исправляет базовую ошибку времени жизни RAII, так что чтение теперь выполняется
от непривилегированного владельца учётной записи; обход по-прежнему достигает
cpdavd, но не может выйти за пределы того, что уже может прочитать эта учётная
запись. Цепочка CalDAV включается флагом --exploit и по умолчанию отключена,
поскольку она отправляет настоящие письма и читает файлы с подтверждённых целей —
см. раздел Режим эксплуатации (активный).
У большинства публичных детектов для CVE-2026-41940 есть три проблемы. Этот сканер решает каждую из них.
Вы можете прочитать нашу статью об этой технике обнаружения здесь: https://slcyber.io/research-center/high-fidelity-check-for-the-cpanel-authentication-bypass-cve-2026-41940/
Per-vhost конфигурация Apache от cPanel устанавливает ProxyPass, который
направляет /___proxy_subdomain_whm на 127.0.0.1:2086 и /___proxy_subdomain_cpanel
на 127.0.0.1:2080 независимо от заголовка Host запроса.
RewriteCond ограничивает только перезапись, которая сопоставляет поддомен управления
с путём прокси; сам ProxyPass безусловен. Обращение к этим путям на любом
vhost, обслуживаемом управляемым cPanel Apache, достигает того же уязвимого
бэкенда, что и порты управления.
Сканеры, которые проверяют только порты 2082/2083/2086/2087, сообщат, что хост не уязвим, когда эти порты закрыты брандмауэром, хотя ошибка полностью доступна через 443. Этот сканер по умолчанию проверяет 2087, 2083 и два прокси-пути на 443.
cPanel поставляется с cphulkd, который блокирует учётные записи после
небольшого числа неудачных попыток входа, и authorized_whm_root_ips, который
ограничивает входы root настроенным списком исходных адресов. Сканер, который
эксплуатирует обход путём попытки внедрить сессию для root, будет:
Этот сканер избегает обеих проблем на стороне WHM, внедряя expired=1 в
полезную нагрузку сессии под случайно сгенерированным именем пользователя.
Внедрение сессии проверяется посещением полученного URL с cpsessXXXX и
поиском msg_code:[expired_session] в теле ответа, который присутствует только
при успешном внедрении. Никакая реальная учётная запись не является целью,
поэтому ни одна реальная учётная запись не может быть заблокирована, а белый
список root не имеет значения, поскольку попытка входа от root не производится.
Демон cPanel (cpaneld, порты 2083 и путь /___proxy_subdomain_cpanel)
требует, чтобы предоставленное имя пользователя соответствовало существующей
учётной записи cPanel на диске (-f /var/cpanel/users/$user). Имя root
никогда не пройдёт эту проверку, потому что root — системный пользователь, а не
пользователь cPanel. Детекты, которые пробуют только root, дают ложные
отрицательные результаты на этой поверхности. Этот сканер использует настраиваемый
список распространённых имён пользователей cPanel для поверхности cPanel и
возвращается к пути со случайным именем на поверхности WHM, где такого
ограничения нет.
Для каждой цели сканер выполняет следующие шаги для каждой поверхности:
GET /login и читает заголовок Set-Cookie для
whostmgrsession (WHM) или cpsession (cPanel). Куки содержит
компонент имени сессии, разделённый запятыми.GET / с заголовком Authorization: Basic, декодированное
значение которого равно <user>:\xff\nexpired=1. Завершающее
\nexpired=1 — это полезная нагрузка для внедрения сессии. Куки сессии
из шага 1 воспроизводится без изменений.Location из ответа и извлекает токен cpsessXXXX.GET /<cpsessXXXX>/ с исходной куки и ищет
msg_code:[expired_session] в теле. Его наличие доказывает, что внедрение
сессии прошло успешно и хост уязвим.На WHM (порт 2087 и путь /___proxy_subdomain_whm на 443) имя пользователя —
это случайное u, за которым следуют десять шестнадцатеричных символов. На
cPanel (порт 2083 и путь /___proxy_subdomain_cpanel на 443) сканер проходит
по списку имён пользователей и останавливается на первом совпадении.
По умолчанию сканер проверяет 2087, 2083 и 443 в указанном порядке и останавливается, как только любая поверхность подтвердит уязвимость.
--exploit)CVE-2026-29205 — cPanel/WHM WP2 Security Update, 13 мая 2026. Исправлено в cPanel 11.134.0.26. В уведомлении отслеживается та же регрессия понижения привилегий
cpdavd, которую использует эта цепочка эксплуатации.
Полное описание ошибки и цепочки эксплуатации: https://slcyber.io/research-center/new-age-of-collisions-reading-arbitrary-files-pre-auth-as-root-in-cpanel-cve-2026-29205
cpdavd на портах 2079 (plain HTTP) и 2080 (TLS) доверяет пути
<principal>/<collection>/..., который он строит при обслуживании ресурсов
CalDAV/CardDAV. Создав запрос, компонент пути которого кодирует сегменты ..,
и направив его на папку maildir, имя которой на диске также кодирует обход
(x-attachment-1-y), можно заставить cpdavd прочитать любой файл на диске
от root, независимо от владельца или прав доступа — включая /etc/shadow,
/etc/passwd и пользовательские почтовые ящики.
Защита в глубине, которая должна была понизить привилегии до владельца учётной
записи перед чтением, молчаливо не сработала: объект
Cpanel::AccessIds::ReducedPrivileges был создан в void-контексте, поэтому его
деструктор восстанавливал привилегии root до того, как выполнялось чтение.
cPanel 11.134.0.26 привязывает объект к лексической переменной my $privs,
чтобы он жил на протяжении всей цепочки -f / stat / open / read; на
исправленных хостах чтение поэтому выполняется от непривилегированного
владельца учётной записи вместо root.
Уязвимая папка должна существовать на диске до того, как чтение сработает.
cPanel автоматически создаёт папку с именем .x-attachment-1-y для получателя
<user>+x-attachment-1-y@<domain> при первом поступлении письма на этот
субадрес. Таким образом, цепочка выглядит так:
info, admin, webmaster).<prefix>+x-attachment-1-y@<domain> каждому
кандидату. Отслеживаются принятые ответы RCPT TO.GET с обходом пути на
cpdavd на портах 2080 (TLS) и 2079 (plain) под обоими префиксами
коллекций /calendar/ и /addressbook/.Успех возвращает байты файла; находка записывает использованный email, коллекцию, количество байт и первые 200 байт в качестве превью.
Эта проверка отключена, если не передан --exploit. При включении она
требует работающего внешнего SMTP-релея (см. Файл конфигурации
ниже), поскольку шаг создания папки нельзя пропустить.
Эксплойт работает только против реальных виртуальных почтовых учётных записей,
настроенных через функцию Email Accounts cPanel. Адреса «catch-all» не
работают — catch-all маршрутизируется через маршрутизатор system_aliases
Exim, никогда не достигает транспорта dovecot_virtual_delivery и поэтому
никогда не запускает путь lda_mailbox_autocreate, который создаёт папку
.x-attachment-1-y/.
Если вы уже знаете действительный виртуальный email на цели, передайте его
с помощью --email:
python scanner.py --config scanner.ini --email [email protected] target.com
--email полностью пропускает перечисление SAN сертификата и список префиксов
и отправляет ровно одно сообщение на указанный вами адрес. Его можно повторять
для targeting нескольких известных учётных записей. Целевой путь гораздо более
надёжен, чем массовая рассылка.
Без --email сканер возвращается к массовой рассылке ~15 распространённых
префиксов (info, admin, webmaster и т.д.) на каждый домен, извлечённый из
TLS-сертификата хоста. Измерение Assetnote на выборке из 200 хостов с
открытым cpdavd показало ~10% вероятность попадания при массовой рассылке:
чистый результат в режиме массовой рассылки является слабым свидетельством
того, что хост исправлен, и повторный запуск с --email против реальной
учётной записи — единственный надёжный способ подтвердить.
Режим эксплуатации читает INI-файл через --config:
python scanner.py --config scanner.ini --exploit example.com
Скопируйте scanner.ini.example в scanner.ini, заполните учётные данные
SMTP и при необходимости настройте параметры CalDAV по умолчанию. scanner.ini
находится в .gitignore, поэтому заполненная копия остаётся локальной. Пароль
SMTP также можно передать через переменную окружения SCANNER_SMTP_PASSWORD,
которая имеет приоритет только когда поле password в файле пусто.
pip install -r requirements.txt
Требуется Python 3.8 или новее.
Одна цель:
python scanner.py example.com
Несколько целей через позиционные аргументы:
python scanner.py host-a.example.com host-b.example.com:2083
Файл с целями, по одной на строку. Строки, начинающиеся с #, игнорируются:
python scanner.py -f targets.txt
Чтение целей из stdin:
cat targets.txt | python scanner.py
Цель может быть либо именем хоста, либо host:port. Если указан порт, сканер
проверяет только его; в противном случае он проверяет 2087, 2083 и 443.
-u, --users — разделённые запятыми имена пользователей cPanel для
проверки на поверхности cPanel. По умолчанию используется небольшой
встроенный список.-U, --users-file — файл с одним именем пользователя cPanel на строку.-p, --ports — разделённые запятыми порты для проверки, если на цели не
указан порт. По умолчанию 2087,2083,443.-t, --threads — количество потоков на цель для обхода списка имён
пользователей на поверхности cPanel. По умолчанию 10.-c, --concurrency — количество целей, сканируемых параллельно. По
умолчанию 20.-T, --timeout — таймаут на один запрос в секундах. По умолчанию 15.-o, --output — добавлять уязвимые цели, по одной на строку, в этот файл
по мере обнаружения.--json — записывать запись в формате JSON Lines для каждой цели в этот
файл.-q, --quiet — выводить на stdout только уязвимые цели. Ошибки соединения
и чистые цели по-прежнему записываются в --json и учитываются в сводке.Одна строка выводится на stdout на каждую находку, поэтому цель, проходящая обе проверки, напечатает дважды:
[!] host cve-2026-41940 VULNERABLE (port 443)
[!] host caldav-traversal VULNERABLE via admin@host (read 1842b from /etc/passwd)
[+] host cve-2026-41940 NOT VULNERABLE
[?] host cve-2026-41940 CONNECTION FAILED
Вывод --json — это одна запись на цель с массивом findings:
{"target": "host", "status": "VULNERABLE", "findings": [
{"check": "cve-2026-41940", "status": "VULNERABLE", "detail": {"port": 443}},
{"check": "caldav-traversal", "status": "VULNERABLE",
"detail": {"email": "admin@host", "domain": "host", "collection": "calendar",
"file": "/etc/shadow", "bytes": 1218, "preview": "root:$6$..."}}
]}
status на верхнем уровне — наихудший случай среди всех находок.
Сводная строка с итогами выводится в stderr в конце. Индикатор прогресса выводится в stderr и автоматически отключается, если stderr не является терминалом.
Код возврата: 0, если любая цель уязвима; 1, если все достижимые цели
чисты; 2, если ни одна цель не была достижима.
Сканировать список целей, записывать попадания в файл и молчать на stdout:
python scanner.py -f targets.txt -o vulnerable.txt -q
Сканировать с пользовательским списком имён пользователей для поверхности cPanel, повышенным параллелизмом и выводом JSON для дальнейшей обработки:
python scanner.py -f targets.txt -U cpanel-users.txt -c 100 --json results.jsonl
Проверить нестандартный набор портов:
python scanner.py -p 2083,2087,8443 -f targets.txt
Запустить цепочку CalDAV от начала до конца для одной цели с запасным
вариантом массовой рассылки (требуется заполненный scanner.ini):
python scanner.py --config scanner.ini --exploit example.com
Целевая эксплуатация против известного виртуального email — гораздо выше вероятность попадания, чем при массовой рассылке:
python scanner.py --config scanner.ini --email [email protected] example.com
Запустить только цепочку CalDAV против списка подтверждённых хостов cpdavd,
выгружая превью /etc/passwd в JSON:
python scanner.py --config scanner.ini --caldav-only \
-f cpdavd-hosts.txt --json caldav-results.jsonl
Вызов scanner.py <target> по умолчанию запускает только детектор
CVE-2026-41940. Он отправляет запросы, необходимые для подтверждения внедрения
сессии, и ничего больше. Он не входит в систему как реальный пользователь, не
нацеливается на учётную запись root, не повышает привилегии до оболочки и не
накапливает события неудачных паролей для какой-либо действительной учётной
записи на целевой системе. Маркер, который он ищет
(msg_code:[expired_session]), генерируется самим приложением в ответ на
внедрённое поле сессии expired=1 и является тем же индикатором, который
использует страница входа в cPanel при воспроизведении легитимно истёкшей
сессии.
Передача --exploit (или --caldav-only) открывает цепочку обхода пути
CalDAV. Это уже не детектор — это работающий эксплойт. При включении сканер
будет для каждой цели, где удалось перечислить домены:
scanner.ini, и
отправлять одно короткое сообщение на каждого кандидата (обычно 10–15 на
домен, 3 домена на цель);--read-file с каждой подтверждённой цели.Файл по умолчанию — /etc/shadow. Успешное чтение возвращает байты файла;
пустое тело означает либо недостижимую цель, либо хост, на котором применено
исправление понижения привилегий (cPanel 11.134.0.26, my $privs = …).
Детекция сравнивает длину полученного тела с нулём, а не с объявленным
Content-Length в ответе (который берётся из stat() на выбранном атакующим
пути и будет заполнен, даже если последующий open() был запрещён).
Чтобы проверить достижимость обхода независимо от исправления понижения
привилегий — например, когда вы хотите узнать, доступен ли cpdavd и
выполнено ли предусловие с maildir на исправленном хосте — запустите повторно
с --read-file /etc/passwd. /etc/passwd читается всеми пользователями,
поэтому возвращает байты как на до-патчевых, так и на пост-патчевых хостах;
комбинирование двух сигналов (/etc/shadow с содержимым = до-патчевое чтение
от root; /etc/shadow пусто + /etc/passwd есть = обход достижим, но
исправление понижения привилегий применено) однозначно классифицирует хост.
Содержимое и превью из 200 байт записываются в вывод JSONL и выводятся на stdout. Эксплуатация CalDAV по доменам может занимать минуту или более — это лестница повторных попыток в ожидании доставки почты, а не зависание.
Запускайте --exploit только против ресурсов, которые вам принадлежат или на
которые у вас есть явное письменное разрешение на тестирование. SMTP-трафик
логируется настроенным релеем и каждой почтовой системой получателя; чтение
файлов логируется cpdavd.
--no-progress — отключить индикатор прогресса.--exploit — включить цепочку обхода пути CalDAV. Отключена по умолчанию;
см. Режим эксплуатации (активный) для
побочных эффектов, которые открывает этот флаг.--config — INI-файл с учётными данными SMTP-релея и настройками CalDAV.
См. scanner.ini.example.--read-file — файл для эксфильтрации при успешной цепочке CalDAV.
Переопределяет значение в файле конфигурации. По умолчанию /etc/shadow —
файл, доступный только root, поэтому успешное чтение позволяет отличить
до-патчевую версию (возвращает содержимое shadow) от пост-патчевой (доступ
запрещён, тело пусто → сообщается НЕ УЯЗВИМ). Используйте
--read-file /etc/passwd, чтобы проверить доступность обхода без
различения исправленных/неисправленных.--caldav-only — пропустить проверку 41940 и выполнить только цепочку
CalDAV. Подразумевает --exploit. Полезно для повторного запуска цепочки
против списка целей, уже известных как достижимые по CalDAV.--email ADDR — известная виртуальная учётная запись email на цели.
Пропускает перечисление SAN сертификата и список массовой рассылки; отправляет
ровно одно сообщение на ADDR и читает от имени этого principal. Может
повторяться. Подразумевает --exploit. См.
Целевая эксплуатация vs массовая рассылка.-v, --verbose — выводить прогресс по доменам для цепочки CalDAV (список
SAN сертификата, количество рассылаемых, лестница повторных попыток).