
Безопасный локальный proof of concept для уязвимости инъекции PHP-объектов в Cotonti CommentsWidget (CAN-2026-2035973 / CVE-2026-71294).
Безопасный локальный proof of concept для проблемы инъекции PHP-объектов, выявленной в реализации CommentsWidget в Cotonti.
plugins/comments/inc/CommentsWidget.phpУязвимый путь кода принимает GET-параметр ci и передаёт контролируемые злоумышленником данные через base64_decode() в PHP-функцию unserialize() без ограничения классов, которые могут быть инстанцированы.
Концептуально уязвимая операция выглядит так:
$ci = @unserialize(base64_decode($ci));
Когда передаётся сериализованный объект, PHP может инстанцировать контролируемый злоумышленником класс и вызвать магические методы, такие как . В более крупном приложении это может стать опасным при наличии подходящих gadget-классов.
__wakeup()Предложенное исправление меняет десериализацию на:
$ci = @unserialize(base64_decode($ci), ['allowed_classes' => false]);
и проверяет, что полученное значение имеет ожидаемую структуру массива, прежде чем использовать его.
poc.php выполняет три локальных теста:
unserialize() инстанцирует DangerousGadget и вызывает его метод __wakeup().allowed_classes => false предотвращает инстанцирование gadget-класса и, следовательно, предотвращает побочный эффект __wakeup().PoC использует /tmp/pwned_by_cotonti_poc.txt только как безвредный sentinel-файл, показывающий, что __wakeup() выполнился. Он не выполняет команду операционной системы. Строка id && whoami существует только как значение-заполнитель в свойстве command gadget-объекта.
serialize() / unserialize()Установка Cotonti не требуется, поскольку уязвимое и исправленное поведение десериализации воспроизводится напрямую.
Из каталога репозитория:
php poc.php
Ожидаемый вывод примерно такой:
[!] DangerousGadget::__wakeup() triggered
[TEST 1] VULNERABLE: __wakeup() executed during unrestricted unserialize().
[TEST 2] FIXED: object instantiation was blocked; no sentinel was created.
[TEST 3] FIXED: invalid serialized structure rejected by validation.
Summary:
- Vulnerable path allows a serialized attacker-controlled object to be instantiated and invokes __wakeup().
- Fixed path uses allowed_classes=false, preventing the gadget class from being instantiated.
- Structural validation rejects values that do not match the expected array shape.
- No real command execution is performed by this PoC; the command property is only a placeholder.
Скрипт удаляет свой sentinel-файл перед завершением.
Не десериализуйте контролируемые злоумышленником данные с помощью неограниченного PHP unserialize().
Для этого пути кода продемонстрированная мера защиты — отключить инстанцирование классов:
unserialize($data, ['allowed_classes' => false]);
Также следует проверять десериализованное значение на точное соответствие структуре, ожидаемой приложением, прежде чем использовать его содержимое.
Этот репозиторий предназначен для исследований в области безопасности, верификации и защитного тестирования. Запускайте PoC только в средах, которыми вы владеете или которые вам явно разрешено тестировать. Не используйте его для атаки на системы без разрешения.
Harsh Raj Singhania — исследование уязвимости, proof of concept и раскрытие информации.