CSRF: SameSite=Lax Bypass через GET-запрос
Критичное действие смены пароля выполняется через GET-запрос. SameSite=Lax не защищает от top-level навигации. Сменив пароль администратора через CSRF, залогинься под admin и забери флаг.
mediumphpPro
Задача
# CSRF: SameSite=Lax Bypass через GET-запрос
## Сценарий
Интернет-магазин **SecureShop** позволяет авторизованным пользователям менять пароль в настройках профиля через API быстрого доступа. Разработчик ознакомился с современными рекомендациями и выставил атрибут `SameSite=Lax` на сессионную cookie, посчитав, что этого достаточно для защиты от CSRF. Поэтому он не стал добавлять отдельную проверку CSRF-токена для эндпоинта смены пароля и оставил его максимально простым — на стороне сервера запрос принимается обычным GET с параметром `password` в URL. В систему встроен бот-администратор: он принимает ссылку через форму `Report to Admin` и переходит по ней в своей авторизованной сессии.
Ваша цель — заставить бота-администратора сменить свой пароль на подконтрольное вам значение, после чего войти под учётной записью `admin` с этим паролем и забрать флаг со страницы профиля.
## Теория
**CSRF (Cross-Site Request Forgery)** — атака, при которой злоумышленник заставляет браузер жертвы выполнить запрос на целевой сайт от её имени. Браузер автоматически прикрепляет cookies к любому запросу на нужный домен, поэтому залогиненная сессия жертвы передаётся вместе с запросом, инициированным внешней страницей.
Атрибут **`SameSite`** управляет тем, прикрепляются ли cookies к cross-site запросам:
* `None` — cookies прикрепляются всегда (требует `Secure`).
* `Lax` (значение по умолчанию в современных браузерах) — cookies прикрепляются к **top-level GET-навигации** (переход по ссылке, редирект, `<form method=GET>`, `window.location`). Cross-site POST/PUT/DELETE/AJAX отбрасываются.
* `Strict` — cookies прикрепляются **только** если запрос инициирован с того же origin.
Главная ошибка в этой лабе — сочетание двух решений, по отдельности нормальных:
1. Мутирующий эндпоинт смены пароля привязан к GET (изменение состояния через идемпотентный метод — антипаттерн).
2. Cookie выставлена с `SameSite=Lax`, который **разрешает** top-level GET-переход.
Lax не блокирует top-level GET, поэтому атакующий заставит браузер жертвы перейти по `/api/account/change?password=attacker_value` любым способом — автокликающая `<a href>`, `<meta http-equiv="refresh">`, `window.location = ...`. Cookie уйдёт на сервер вместе с запросом, эндпоинт изменит пароль.
**Уязвимый паттерн (двойная ошибка):**
В этой лабе сошлись два решения, по отдельности нормальных:
1. **Мутирующая операция смены пароля привязана к GET.** Эндпоинт читает новое значение пароля из параметров URL, хеширует его и пишет в таблицу пользователей. RFC 7231 явно запрещает мутации через GET (§4.2.1), но на уровне кода это просто другой кейс роутера.
2. **Сессионная cookie выставлена с `SameSite=Lax`.** Lax — современное по умолчанию, защищает от cross-site POST/PUT/AJAX, но разрешает top-level GET-навигацию (переход по ссылке, `window.location`, `<meta refresh>`).
В обработчике смены пароля **нет** проверки CSRF-токена — разработчик посчитал `SameSite=Lax` достаточным. Поскольку Lax не блокирует top-level GET, любая атакующая страница, открытая в браузере жертвы, может инициировать GET-переход на эндпоинт — и cookie сессии уйдёт вместе с запросом.
## Цели
1. Найдите эндпоинт смены пароля и определите HTTP-метод, который он использует.
2. Подготовьте на exploit-server HTML-страницу, инициирующую top-level GET-навигацию к эндпоинту с подконтрольным значением пароля.
3. Доставьте ссылку через форму `Report to Admin` — бот-администратор посетит её в авторизованной сессии. В результате пароль `admin` сменится на ваш.
4. Войдите в учётную запись `admin` с новым паролем.
5. Заберите флаг со страницы профиля администратора.
## Точка входа атаки
| Параметр | Значение |
|----------|----------|
| Ваш аккаунт | `demo` / `demo` |
| Уязвимый эндпоинт | `GET /api/account/change?password=...` |
| Cookie защита | `SameSite=Lax` (не блокирует top-level GET) |
| Доставка боту | `POST /report` (бот залогинен как admin) |
| Проверка текущего пользователя | `GET /api/account/info` — JSON с username и role |
| Вход под admin | `POST /login` с новым паролем |
| Где появится флаг | `GET /profile` после входа под `admin` (admin владеет товаром «CTF Flag») |
| Exploit Server | внешний хостинг для HTML-payload (см. панель лабы) |