Перейти к содержимому
← Каталог php Deserialization

Deserialization: POP-цепочка → RCE через file write

unserialize() без allowed_classes + классы с magic-методами (__destruct, __toString) позволяют собрать POP-цепочку, которая пишет PHP webshell в /static и даёт RCE.

hardphpPro
Задача
# Lab: POP-цепочка через unserialize() ## Сценарий SecureShop — небольшой интернет-магазин на нативном PHP. Чтобы помнить выбранную пользователем тему оформления и язык интерфейса между запросами, приложение сохраняет настройки в cookie `user_prefs` как `base64(serialize($object))`. При заходе на `/profile` приложение декодирует cookie и вызывает `unserialize()` без `allowed_classes`, чтобы восстановить объект `UserPreferences`. Это классический антипаттерн: `unserialize()` поверх данных, которые контролирует клиент (cookie). PHP по дороге восстановления может инстанциировать **любой** класс из autoload — не только `UserPreferences`. Если у такого класса есть magic-методы (`__destruct`, `__wakeup`, `__toString`, `__call`), они автоматически отработают по ходу жизненного цикла объекта. ## Что такое POP-цепочка **Property-Oriented Programming (POP)** — техника эксплуатации, которая использует уже существующие в кодовой базе классы как «гаджеты». Атакующий не пишет свой PHP-код на сервере — он лишь собирает «нужный набор объектов с нужными полями», и при их восстановлении PHP сам по цепочке вызывает методы: 1. Создаётся объект `A` — срабатывает `A::__destruct()`. 2. `__destruct()` вызывает метод другого объекта `B`, ссылка на который лежит в поле `A->next`. 3. Метод `B->doSomething()` использует поле `B->target` как имя файла / shell-команду / SQL-запрос. Итог: ни одной собственной строки PHP-кода атакующий на сервер не отправлял — он лишь подобрал значения полей. Это и есть POP — программирование «через свойства объектов», не через инструкции. ## Magic-методы PHP, которые автоматически запускаются - `__wakeup()` — вызывается сразу после восстановления объекта `unserialize()`. - `__destruct()` — вызывается при завершении скрипта или сборке мусора. - `__toString()` — вызывается при приведении объекта к строке (`(string)$o`, конкатенация, `echo`). - `__call($name, $args)` — вызывается при обращении к несуществующему методу. Чем больше магии у класса, тем выше шанс, что он годится как gadget. Готовые цепочки для популярных фреймворков (Laravel, Symfony, Monolog, Guzzle) собраны в проекте **PHPGGC** — атакующий может выбрать нужную одной командой. ## Гаджеты в SecureShop В исходном коде магазина остался старый cache-слой с двумя классами: - `CacheEntry` — элемент файлового кэша, в `__destruct()` сбрасывает буфер на диск через помощник: вызывает `$this->writer->persist($this->key, (string)$this->data)`. - `FileWriter` — helper, метод `persist($key, $data)` вызывает `file_put_contents($this->dir . '/' . $key, $data)`. Эти классы больше нигде не инстанцируются, но остались в autoload (зарегистрированы в `composer.json` → `autoload.files`). PHP-сериализация позволяет собрать вложенный объект `CacheEntry`, внутри которого `writer = FileWriter(dir="/lab/static")`, `key = "shell.php"`, `data = "<?php system($_GET['c']); ?>"`. Когда скрипт завершает работу, PHP вызывает `__destruct()` у всех объектов — и `CacheEntry::__destruct()` через `FileWriter::persist()` запишет произвольный файл от имени `www-data`. Директория `/lab/static/` раздаётся Apache напрямую (без роутера), а `.htaccess` разрешает исполнение PHP в этой папке — значит, записанный `.php` файл будет исполнен при первом же HTTP-запросе. Дальше — обычный webshell с RCE. ## Цели 1. Получить RCE на сервере через подделанное cookie `user_prefs`. 2. Прочитать CTF-флаг из переменной окружения `LAB_FLAG` (команда `env` или `printenv` покажет его). ## Данные | Параметр | Значение | |----------|----------| | Обычный пользователь | `demo` / `demo` | | Директория static | `/lab/static/` (раздаётся Apache как `/static/...`) | | Endpoint логина | `POST /login` | | Endpoint, читающий cookie | `GET /profile` | | Cookie с payload | `user_prefs` (значение — `base64(serialize(...))`) |
🚧 Сайт в разработке. Полный функционал пока недоступен. Все вопросы — support@hackandfix.ru