Type Juggling: JSON-аутентификация с loose comparison
JSON-API логина сравнивает пароль с хешем через loose == . В БД хранится хеш-строка вида 0e830... (magic hash), которая коэрсится в число 0.0. Отправив {"password": 0}, атакующий заходит как admin.
mediumphpPro
Задача
# SecureShop — Legacy mobile API login
Магазин SecureShop много лет выпускал мобильное приложение на старом стеке, и у него остался отдельный endpoint для логина — `POST /api/login`. Он принимает JSON-тело вида:
```json
{"username": "admin", "password": "<md5 пароля>"}
```
Исторически пароли для мобильного API хранились отдельной колонкой `password_md5` — legacy-миграция из старого магазина, когда пароль приходил с клиента уже как MD5-строка.
## Что доступно
- `POST /api/login` — JSON-логин (Content-Type: application/json). При успехе возвращает `{"token": "...", "role": "..."}` и ставит cookie `session_token`.
- `GET /api/me` — возвращает текущего пользователя (по cookie или заголовку `Authorization: Bearer <token>`). Если роль `admin` — в ответе лежит поле `flag`.
- Также есть обычный HTML-UI (`/login`, `/catalog`, `/profile`, `/admin`) — он тебе для эксплойта не нужен, но посмотреть можно.
## Пользователи
| Пользователь | Пароль | Роль |
|--------------|--------|------|
| `admin` | неизвестен (длинный, случайный) | admin |
| `demo` | `demo` | user |
## Данные
| Параметр | Значение |
|----------|----------|
| Уязвимый эндпоинт | `POST /api/login` (Content-Type: application/json) |
| Эндпоинт текущего пользователя | `GET /api/me` (cookie или `Authorization: Bearer ...`) |
| HTML-UI | `/login`, `/catalog`, `/profile`, `/admin` (для атаки не нужны) |
## Цель
Зайти как `admin` через `POST /api/login` **не зная его пароля**. После успешного логина вызвать `GET /api/me` и получить `flag` из ответа.
## Вектор
Загляни в обработчик JSON-логина. Подумай, что происходит, когда `json_decode` встречает **числовое** значение в поле `password`, и как это значение потом сравнивается с хешем из БД.
Уязвимость известна как **PHP type juggling / "magic hash"** — класс ошибок, где loose-сравнение `==` между строкой и числом коэрсит строку в число по неочевидным правилам.