←К лабораториямCSRF: Атака через JSON API без кастомных заголовков
+60 XP
Задача
# CSRF: подделка запроса к JSON API
## Сценарий
Вы проводите аудит безопасности **BitForge** — внутреннего магазина компании, выпускающей инструменты для разработчиков. За API профиля отвечал бэкендер Глеб: он уверен, что JSON-эндпоинты защищены от CSRF «по самой своей природе», ведь классическая HTML-форма JSON не отправляет. Ревью показало обратное: эндпоинт обновления профиля парсит JSON-тело, не требуя ни CSRF-токена, ни специального заголовка, а среди обновляемых полей есть пароль. Администратор сервиса просматривает ссылки, присылаемые через форму жалоб, оставаясь при этом залогиненным.
## Цель
1. Убедитесь, что эндпоинт обновления профиля не проверяет CSRF-токен и не требует заголовков, недоступных обычной HTML-странице.
2. Подготовьте страницу, которая без ведома посетителя отправит на этот эндпоинт JSON-тело, меняющее пароль аккаунта жертвы.
3. Доставьте страницу боту-администратору через форму жалоб, чтобы пароль администратора сменился на известное вам значение.
4. Войдите как `admin` с новым паролем и заберите флаг из админ-панели.
## Теория
Многие команды верят: если эндпоинт принимает только JSON, CSRF невозможна — стандартная HTML-форма отправляет другой формат. Заблуждение рушится на двух фактах. Во-первых, браузер умеет отправлять кросс-доменные POST-запросы отдельных типов без предварительного CORS-разрешения — вместе с cookies жертвы. Во-вторых, если сервер парсит тело как JSON, не сверяя заголовок `Content-Type`, формат отправки не имеет значения: подойдёт и текстовый, лишь бы содержимое было корректным JSON. В браузере есть несколько механизмов, позволяющих чужой странице отправить произвольное тело в таком запросе.
Надёжная защита JSON API — обязательный кастомный заголовок: браузер не добавит его в кросс-доменный запрос без предварительного разрешения сервера, а HTML-формы такие заголовки не отправляют вовсе. Альтернатива — классический CSRF-токен, привязанный к сессии.
**Уязвимый паттерн:**
```go
// API проверяет сессию, но не источник запроса:
body, _ := io.ReadAll(r.Body)
json.Unmarshal(body, &req) // JSON парсится независимо от Content-Type
```
Если среди обновляемых полей есть `password`, чужая страница способна сменить пароль залогиненного пользователя: браузер приложит его cookies, а сервер обновит учётную запись и ответит успехом.
## Точка входа атаки
| Параметр | Значение |
|----------|----------|
| Ваш аккаунт | `demo` / `demo` |
| Уязвимый эндпоинт | `POST /api/profile` (JSON: `email`, `fullname`, `password`) |
| Доставка боту | `POST /report`, поле `url` |
| Целевая учётная запись | `admin` — вход после смены пароля |
| Где забрать флаг | `GET /admin` |
Форма администратора не защищена CSRF-токеном. Заставь бота выполнить действие от имени admin.
Форма администратора не защищена CSRF-токеном. Заставь бота выполнить действие от имени admin.
+60 XP
Node.js Pro
ExploitFix
Мы используем cookies —··
Cookies и аналитика.Технически необходимые cookies (вход, безопасность, работа сайта) — всегда; Яндекс Метрика и Вебвизор — только с вашего согласия. Подробнее: cookies, приватность.