WebSocket Message Injection (XSS via innerHTML)
WebSocket chat broadcasts unsanitized messages. Client renders via innerHTML, enabling XSS.
mediumgolangPro
Задача
# XSS через WebSocket: HTML-инъекция в чат
## Сценарий
Вы аудируете интернет-магазин **SecureShop**. Команда добавила фичу чата в реальном времени на основе WebSocket: сервер принимает сообщения от подключённых клиентов и рассылает их всем активным сессиям. Команда уверена, что фича безопасна — для использования чата требуется авторизация. По данным внутреннего аудита, в админ-панели хранится конфиденциальная метка, и в системе есть бот-администратор, периодически проверяющий чат через сервисную форму отчётов; её требуется получить, имея только обычную учётную запись.
## Цель
1. Изучите, как сервер обрабатывает входящие сообщения чата перед рассылкой подключённым клиентам, и как клиентский JavaScript отображает полученные сообщения.
2. Найдите способ внедрить активный контент (HTML/JavaScript) через сообщение чата так, чтобы оно исполнялось в браузерах других подключённых клиентов.
3. Заманите бота-администратора в чат через сервисную форму отчётов и эксфильтруйте флаг из admin-панели на ваш Exploit Server.
## Теория
Stored XSS через real-time канал (WebSocket-чат, push-уведомления, SSE) — особенно опасный класс уязвимостей: сообщение сразу попадает к **всем** подключённым клиентам, включая привилегированных пользователей и ботов-администраторов с активными сессиями. При этом уязвимость требует двух «забытых» защитных слоёв одновременно: серверной санитизации входящих сообщений (превращение `<`, `>`, `&`, кавычек в HTML-сущности) и клиентской вставки в DOM безопасным способом (через `textContent`, не через `innerHTML`). Если хотя бы один из слоёв на месте — атака не работает; если оба отсутствуют — любой клиент чата может выполнить произвольный JavaScript у других участников.
**Уязвимый паттерн (сервер):**
```go
// Сервер рассылает сообщение клиентам без экранирования HTML
for client := range chatClients {
client.WriteMessage(websocket.TextMessage, msg) // msg as-is
}
```
**Уязвимый паттерн (клиент):**
```javascript
// Клиент вставляет сообщение в DOM как HTML, а не как текст
ws.onmessage = (e) => { div.innerHTML = e.data; }
```
При наличии stored XSS в admin-сессии атакующий может выполнить **same-origin-запросы** от лица admin-сессии (`fetch('/admin')` отправит admin-cookies автоматически — `HttpOnly`-флаг этому не мешает) и эксфильтровать ответ на любой внешний домен через `fetch` или `Image()`-загрузку.
## Точка входа атаки
| Метод | Путь | Назначение |
|-------|------|-----------|
| GET / WS | `/chat` | WebSocket-чат, точка инъекции |
| GET | `/admin` | Панель администратора с флагом (доступна только admin-сессии) |
| GET / POST | `/report` | Форма отчёта — заманивает бота-администратора в указанный URL |
**Учётные данные:** `demo` / `demo`