Авторские курсы Михаила Тарасова

Взлом GraphQL через alias/dos — обрушение бекенда рекурсивными query-алиасами

Взлом GraphQL через alias/dos — обрушение бекенда рекурсивными query-алиасами

Ну что, брат, сейчас разберём, как угробить GraphQL-бек тупой арифметикой. Это когда разрабы думают, что GraphQL = REST с сахаром, а по факту получают DoS через один POST-запрос.

Что за хрень с алиасами
GraphQL позволяет давать кастомные имена одному и тому же полю в запросе — это алиасы. Казалось бы, фича для фронтов. Но нет. Это твой золотой билет на бесплатный stress-test чужого сервера.
Базовый пример (лайт-версия)

Что происходит: Сервер обрабатывает 10к идентичных запросов к базе. CPU взлетает в космос. Если нет rate-limit на количество полей (только на requests/sec) — бекенд ляжет через 5-10 секунд.
Рекурсивная бомба (hardcore)
Если есть вложенные типы (например, User -> Posts -> Comments), можно замутить рекурсивную петлю:

Что горит: N+1 queries умножаются на глубину. Если база без индексов — всё. Если есть JOIN’ы — RAM утекает за 20-30 секунд. Если нет maxDepth в схеме — поздравляю, ты только что устроил ССДОС руками.
Инструменты для автоматизации
1. BatchQL (мой любимый)

Сгенерит query с 5000+ алиасами автоматом. Смотри htop на сервере — если load average > 20, ты в дамках.
2. GraphQL Cop (detect + exploit)

Чекает maxDepth, maxComplexity, queryTimeout. Если всё null — Welcome to DDoS-land.
3. Ручная кастомизация (для души)

Если ответ приходит через 30+ секунд — сервер уже умирает.
Почему это работает
Типичная мисконфигурация

Нет:
validationRules для глубины (depthLimit(5))
complexity калькулятора (пример: graphql-query-complexity)
• Rate-limit на количество полей (только на IP-уровне)
Где искать уязвимость
1. Introspection query (если включён):

1. Ищи типы с циклическими связями (User → Friends → User).
2. Поиск в waybackmachine/паблик-репах:

3. Fuzzing endpoint’ов:

3. Чекай /graphql, /api/graphql, /v1/gql, /query.
Обход защит
Если есть maxDepth
Обойди через фрагменты (GraphQL fragments):

Старые парсеры (Apollo < 3.0) не считают это за глубину.
Если есть complexity limit
Атакуй через batch-запросы (массив query):

Complexity считается на один query, но не на весь массив.
Если есть WAF/Rate-Limit
1. Rotator IP через прокси:

2. User-Agent rotation + X-Forwarded-For:

3. Slowloris для GraphQL (медленная отправка):

Доказательство (PoC для баунти)
Запрос

Ожидаемый результат
• Время ответа: > 30 секунд (норма: < 200ms)
• CPU на сервере: 100% (мониторь через htop или CloudWatch)
• Логи: Ошибки типа FATAL: terminating connection due to timeout (PostgreSQL) или OOM killed (Docker)
Скриншот 
Покажи:
1. Время выполнения запроса (через Burp Suite → Duration: 45.3s)
2. Ответ с ошибкой ("errors": {"message": "Query timeout"})
3. Графики нагрузки (если есть доступ к Grafana/Datadog)

Советы:
Три вектора для добивания
1. Batch + Alias combo:
Зашли 1000 batch-запросов, в каждом по 500 алиасов = 500к операций. Если бек не лёг — это не бек, это танк.
2. Subscription DoS:
Если есть WebSocket-подписки:

2. Открой 500 соединений через wscat — RAM сожрётся за минуту.
3. Mutation flooding:
Если можно делать POST через mutation:

3. База без transaction limit’ов умрёт от I/O-стресса.

План атаки (если зацепка есть)
1. Introspection: Найди циклические типы.
2. PoC с 100 алиасами: Проверь, что сервер тормозит.
3. Скейлинг: Подними до 5000, замерь время.
4. Докажи impact: Если prod — аккуратно (1-2 запроса), если test — уроби нахрен.
5. Репорт : CVSS 7.5 (High), пиши “Resource Exhaustion via GraphQL Query Complexity”.

Если застрял
• Чекни /graphiql или /playground — там может быть дефолтный интерфейс без авторизации.
• Попробуй GET вместо POST: https://target.com/graphql?query={...} — иногда WAF не фильтрует GET.
• Если всё закрыто — ищи leaked API keys в JS-бандлах: curl https://target.com/app.js | grep -Eo '"A-Za-z0-9_-{20,}"'.

Иди ломай, бро. Только не забудь — это legal только на баунти-программах или с письменным permission. Иначе привет от АУЕ. 🏴‍☠️

Мои курсы