
PoC Docker лаборатория: связывание обхода загрузки файлов + хранимая XSS для создания учетных записей администратора. Образовательный ресурс для пентестеров.
Намеренно уязвимое веб-приложение, демонстрирующее, как обход загрузки файлов сочетается с хранимой XSS для создания бэкдор-учетных записей администратора, даже при наличии CSP, CORS и CSRF-защиты.
Полная статья в блоге: KurtiseBear Blog
Это образовательный лабораторный стенд для обучения защите. Не разворачивайте его в общедоступном месте.
Приложение имеет реальные средства защиты:
'self' (но с разрешёнными 'unsafe-inline' и 'unsafe-eval')Злоумышленник с учётной записью низкопривилегированного пользователя объединяет две уязвимости, чтобы обойти все эти защиты:
Обход загрузки файлов — Форма загрузки ограничивает выбор .pdf с помощью клиентского атрибута , но сервер не выполняет никакой проверки типа файла. Злоумышленник загружает -файл, содержащий JavaScript. Конечная точка загрузки отдаёт его с того же источника, поэтому CSP и CORS не блокируют его.
accept.jsХранимая XSS через тему сообщения — Функция отправки сообщений сохраняет пользовательский ввод без санитизации. Папка входящих администратора отображает тему сообщения как сырой HTML. XSS-пейлоад использует обработчик `` для загрузки загруженного скрипта и выполнения его через eval(). CSP разрешает это, так как 'unsafe-inline' и 'unsafe-eval' разрешены.
Отсутствие CSRF на конечной точке API — API управления пользователями (/api/manage-user.php) не проверяет CSRF-токены, хотя конечные точки форм это делают. XSS-пейлоад вызывает этот API, используя сессию администратора с тем же источником. Даже если бы CSRF присутствовал, JavaScript с того же источника мог бы прочитать токен из DOM.
Результат: когда администратор открывает свою папку входящих, XSS срабатывает, JavaScript создаёт бэкдор-учётную запись администратора, используя сессию администратора. Все средства защиты включены и работают. Цепочка работает, потому что никогда не покидает источник.
docker-compose up -d
Подождите 10-15 секунд, пока инициализируется MySQL, затем перейдите на http://localhost:8080
| Роль | Пароль | |
|---|---|---|
| Админ | [email protected] | admin |
| Пользователь | [email protected] | user |
Перейдите на http://localhost:8080 и войдите с [email protected] / user.
Перейдите в Upload Files. Форма говорит «Только PDF», но это ограничение реализовано только на стороне клиента. Либо:
accept=".pdf" из поля выбора файла, илиЗагрузите предоставленный payload.js (или свой собственный). Запомните возвращённый ID файла (например, 1).
Теперь загруженный файл доступен по адресу /api/download.php?file_id=1 на том же источнике. CSP не будет блокировать запросы к этой конечной точке, потому что она находится в 'self'.
Перейдите в Send Message. В поле темы введите:
r.blob()).then(b=>b.text()).then(eval)">
(Замените 1 на фактический ID файла из шага 2.)
Поместите что угодно в тело сообщения. Отметьте приоритет, если хотите, чтобы оно оказалось вверху папки входящих. Отправьте.
Обработчик onerror срабатывает, потому что CSP разрешает 'unsafe-inline'. Функция eval() работает, потому что CSP разрешает 'unsafe-eval'. Fetch к конечной точке загрузки работает, потому что она с того же источника.
Выйдите из системы. Войдите как [email protected] / admin. Перейдите в Inbox.
Тема сообщения отображается как сырой HTML. Тег `` не загружается, срабатывает обработчик onerror, загружает загруженный пейлоад и выполняет его через eval(). Пейлоад отправляет POST-запрос на /api/manage-user.php, используя куки сессии администратора (автоматически прикрепляются для запросов с того же источника). CSRF-токен не требуется, потому что конечная точка API его не проверяет.
Перейдите в Users. Вы должны увидеть нового пользователя: BackdoorAdmin с ролью admin и email [email protected].
Выйдите и войдите с [email protected] / Compromised1!, чтобы подтвердить.
CSP блокирует внешние скрипты
--> Но пейлоад размещён на том же источнике через загрузку файла
--> И unsafe-inline/unsafe-eval позволяют обработчику onerror и eval()
CORS блокирует кросс-доменные запросы
--> Но все запросы в цепочке выполняются с того же источника
CSRF-токены защищают отправку форм
--> Но конечная точка API их не проверяет
--> И даже если бы проверяла, JS с того же источника может читать токены из DOM
Куки сессии имеют стандартные защиты
--> Но запросы с того же источника передают их автоматически
Все защиты работают корректно. Они предназначены для остановки кросс-доменных атак. Эта цепочка никогда не покидает источник.
Что действительно разорвало бы эту цепочку:
htmlspecialchars() для всех данных, контролируемых пользователем. Папка входящих отображает $row['subject'] как сырой код. Это полностью устраняет XSS.'unsafe-inline' и 'unsafe-eval'. Используйте nonce или хеши для легитимных встроенных скриптов. Это блокирует обработчик onerror и eval().docker-compose down -v
Это приложение намеренно уязвимо. Оно предназначено только для образовательных целей и обучения защите. Не разворачивайте его в сети, доступной для недоверенных пользователей. Не используйте эти методы против систем без явного письменного разрешения.