
PoC RCE для CVE-2026-111497 в одном закреплённом официальном Docker-образе WordPress
Этот PoC, содержащий только исходный код, воспроизводит неаутентифицированное выполнение команд в одном закреплённом Docker Official Image для WordPress.
Один запрос pingback.ping заставляет WordPress дважды обратиться к URL атакующего в рамках одного PHP-запроса. Первый ответ повреждает обработчик PHP через Brotli. Заголовок повторной попытки запускает Bash и возвращает флаг лаборатории.
Профиль привязан к конкретной цели. Он ничего не утверждает о текущих образах WordPress, других дайджестах или ошибке памяти в WordPress Core.
ВНИМАНИЕ: Запускайте этот PoC только в прилагаемой лаборатории на системе, которой вы владеете. Он содержит рабочий RCE-пейлоад.
/flag.txt, и флаг возвращается в ответе XML-RPC.sh → bash → sleep.| Элемент | Значение |
|---|---|
| Цель | docker.io/library/wordpress@sha256:806680ba62232c4c300fb9dcc9f89d08e9e8c2bfc59837a1f58716f8648306d5 |
| ID образа | c64dd89c00079939f18d70489e7c3e30cb071e8581824ab2de688f9f4d70d076 |
| Платформа | Linux amd64, Debian 13.7 |
| Приложение | WordPress 7.1.3, Apache 2.4.69 prefork |
| Среда выполнения | PHP 8.3.36 NTS |
| Декодер | libbrotli1:amd64 1.1.0-2+b7 |
Этот манифест Linux/amd64 выбирался тегом wordpress:latest на момент публикации. Текущий тег latest больше его не выбирает.
Лаборатория запускает этот неизменённый Docker Official Image с помощью rootless Podman. Она не внедряет библиотеку Brotli.
CVE-2026-111497 обеспечивает повреждение декодера. WordPress предоставляет неаутентифицированный путь загрузки через pingback.ping.
Некорректный поток копирует шесть байт из указателя живого обработчика system в обработчик strtolower. Оба указателя используют одну и ту же загруженную ASLR базу libphp.so. Поэтому профилю не нужна утечка абсолютного адреса.
Профиль по-прежнему зависит от точных версий Brotli, PHP, glibc, cURL, Requests, Apache, аллокатора и истории запросов. Первый ответ использует 161-байтовый заголовок и тело размером 23 069 816 байт. Источник отправляет 23 069 752 байта, ждёт одну секунду и отправляет финальные 64 байта.
Декодер возвращает ошибку после повреждения. Requests повторяет попытку без декодирования содержимого после CURLE_BAD_CONTENT_ENCODING. Первое имя заголовка достигает изменённого обработчика и запускает фиксированную команду Bash.
Путь приложения требует следующих условий:
/xmlrpc.php доступен, и pingback.ping включён.Лаборатория создаёт эти условия без плагина и без инструментирования на стороне жертвы.
generate_payloads.py — авторитетный источник пейлоадов. Репозиторий не содержит предсобранных бинарных пейлоадов.
Генератор использует только стандартную библиотеку Python. Он работает офлайн и не отправляет трафик и не выполняет команды. Он определяет константы профиля и этапы сериализации согласно RFC 7932.
verify.sh заново генерирует все фрагменты. Он проверяет их размеры, хеши, промежуточные битовые смещения и связь однобитного селектора.
Тестируемый хост использовал Ubuntu 24.04.4 под WSL2 на amd64. Использовались rootless Podman 4.9.3, Bash 5.2.21 и Python 3.12.3.
Минимальные требования: Linux amd64, rootless Podman, Bash, Python 3.8 или новее, режим ASLR 2 и стандартные утилиты GNU. Освободите порт 18170 и подсеть 93.184.218.0/24 или выберите альтернативы. Хост с включённым режимом SELinux enforcing может отклонять bind-монтирования.
Перед запуском лаборатории загрузите неизменяемые образы:
podman pull docker.io/library/wordpress@sha256:806680ba62232c4c300fb9dcc9f89d08e9e8c2bfc59837a1f58716f8648306d5
podman pull docker.io/library/wordpress@sha256:65d5f185a6f539803a177ea9cdd5ad632ae24427bea114447e67a6fcd5d8bfe8
podman pull docker.io/library/mariadb@sha256:33a6653ab666774f48818120045f9367a81cd4c54c027412fe294a7d1274e2dc
Сделайте скрипты исполняемыми, затем проверьте исходный пакет:
chmod +x generate_payloads.py verify.sh run.sh
./verify.sh
Запускайте каждый режим с новым тегом:
| Режим | Команда | Требуемый результат |
|---|---|---|
| Positive | ./run.sh standard | Возвращает флаг и выводит PoC positive: |
| Observable | ./run.sh observable | Также фиксирует видимые процессы bash и sleep |
| Selector | ./run.sh selector | Повторяет попытки, но не допускает флаг в ответ |
Селектор изменяет один бит по смещению 1066 байт. Это причинный контроль в пределах одного образа, а не контроль с фиксированным декодером.
Необязательные аргументы — это режим, тег, порт хоста и третий октет сети:
./run.sh standard trial2 18171 219
Раннер записывает доказательства в runs/<tag>/. Основные файлы:
VERDICT.txt: результат и счётчики запросовclient-request.raw и client-response.raw: входящие данные на уровне протоколаorigin-evidence/origin-events.json: оба исходящих запроса и заголовки повторных попытокoracle-match.txt: совпадение флага в ответеpayloads/identity.json: метаданные сгенерированного профиляMANIFEST.sha256: хеши всех файлов доказательствРаннер использует внутреннюю сеть Podman и публикует WordPress только на 127.0.0.1. Клиент не принимает адрес цели. Origin не принимает ввод команд. Фиксированная команда читает только синтетический флаг.
Раннер удаляет значение флага из окружения Apache до запуска Apache. Он также создаёт новые учётные данные и удаляет только ресурсы текущего запуска.
Разделение ответа и история процессов являются входными данными для эксплойта. Если запуск завершается неудачей, повторите неизменённый профиль с новым тегом. Не подстраивайте адреса или байты полезной нагрузки после неудачного запуска.
Этот пакет не обеспечивает переносимость между дайджестами образов, сборками PHP, архитектурами, аллокаторами или расписаниями ответов. Другая цель может остановить выполнение или вызвать лишь сбой.
Upstream исправил поддерживаемые ветки Brotli в версиях 1.0.10, 1.1.1 и 1.2.1. Дистрибьюторы могут перенести исправление (backport). Используйте рекомендации производителя и журнал изменений пакета, а не только отображаемую версию.