IDOR: Просмотр чужих инвойсов
Эндпоинт /invoice?id=N возвращает инвойс без проверки владельца. Атакующий перебирает ID и читает чужие данные.
easyphpPro
Задача
# IDOR: Просмотр чужих инвойсов
## Сценарий
Перед вами интернет-магазин SecureShop на PHP. У каждого зарегистрированного пользователя есть личные финансовые документы — инвойсы (счета на оплату). Список своих инвойсов и страница деталей конкретного инвойса доступны только авторизованным пользователям.
Авторизация на этих страницах реализована, но это только **аутентификация** — сервер проверяет, что у пользователя есть валидная сессия. **Авторизация конкретного ресурса** — то есть проверка «вправе ли именно этот пользователь читать именно этот документ» — реализована не везде. Эта пара ошибок и есть классическая уязвимость **Insecure Direct Object Reference (IDOR)** из категории Broken Access Control (OWASP Top 10 A01:2021).
## Теория: горизонтальный vs вертикальный IDOR
IDOR бывает двух видов:
- **Горизонтальный IDOR** — пользователь получает доступ к ресурсу того же уровня привилегий, что и его собственные ресурсы. Например, обычный пользователь `alice` читает счёт обычного пользователя `bob`. Уровень роли не меняется, меняется владелец объекта. Это самый частый случай IDOR.
- **Вертикальный IDOR** — пользователь получает доступ к ресурсу более высокого уровня привилегий (доступ к админскому ресурсу из обычного аккаунта). Это уже privilege escalation и часто пересекается с Broken Access Control в более широком смысле.
В этой лабе цель — извлечь чужой счёт из системы. Среди этих чужих счетов есть и счета обычных пользователей, и счёт администратора с CTF-флагом, поэтому атака сочетает в себе оба измерения: вы читаете и горизонтальный ресурс (счёт другого user) и вертикальный (счёт admin'а), но **механизм** уязвимости один — отсутствие проверки владельца.
## Уязвимый паттерн (PHP)
```php
// SQL-запрос фильтрует ТОЛЬКО по id, без привязки к текущему пользователю
$stmt = $db->prepare('SELECT ... FROM invoices WHERE id = ?');
$stmt->execute([$_GET['id']]);
$invoice = $stmt->fetch();
```
Сервер берёт `id` напрямую из параметра запроса `$_GET['id']`, выполняет SELECT и возвращает результат. Условие `AND user_id = ?` отсутствует — поэтому при наличии любой валидной сессии запрос возвращает любую запись таблицы, существующую в системе.
В системе есть административный пользователь. Среди обычных инвойсов администратора находится конфиденциальный документ, в содержимом которого спрятан CTF-флаг (значение, форматированное как `flag{...}`). Ваша задача — найти узкое место в проверке доступа и через IDOR прочитать чужой счёт администратора, содержащий флаг.
## Что нужно сделать
1. Войти в систему обычным пользователем `demo` / `demo` и изучить раздел «Мои счета».
2. Проанализировать структуру URL-адресов страниц деталей: какой параметр определяет, какой именно счёт показывает сервер, и как он формируется.
3. Использовать найденный изъян в проверке доступа, чтобы получить содержимое счетов, которые не принадлежат пользователю `demo`.
4. Среди этих чужих счетов отыскать тот, который содержит маркер CTF-флага в виде `flag{...}`.
5. Скопировать значение флага и отправить его в форму проверки на платформе.
## Ожидаемый результат
Доказательство того, что обычный авторизованный пользователь, не имея никаких административных прав, может прочитать произвольный инвойс администратора, просто меняя значение параметра в URL. Финальное доказательство — извлечённое значение `flag{...}` из тела ответа сервера на запрос чужого счёта.
## Данные для входа
| Параметр | Значение |
|----------|----------|
| Обычный пользователь | `demo` / `demo` |
## Подсказка по разведке
Полезные инструменты для решения: браузер с панелью разработчика (DevTools, вкладка Network), curl или Python-requests для автоматизации перебора, и любой текстовый редактор для просмотра ответов сервера. Никаких специальных инструментов вроде Burp Suite или sqlmap для этой лабы не требуется — достаточно понять, что параметр в URL не проверяется на принадлежность ресурса текущему пользователю.