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

gRPC → HTTP downgrade атака — бэкенд ждал Proto, а ты засунул текстовые запросы

gRPC → HTTP downgrade attack - the backend was waiting for Proto, and you stuck text requests

Почему Это Работает (Техническая База)

gRPC работает поверх HTTP/2 и ожидает бинарные protobuf-сообщения с конкретными заголовками (content-type: application/grpc+proto). Но вот загвоздка: многие прокси, API-гейтвеи и бэкенды не проверяют строго формат тела запроса, если хедеры выглядят легитимно.

Что происходит при downgrade:

  1. Бэкенд видит POST /api.UserService/GetUser HTTP/2

  2. Парсер gRPC пытается десериализовать body как protobuf

  3. Если фейлится — падает в fallback на обычный HTTP-обработчик

  4. Твой текстовый JSON/XML проходит без валидации схемы protobuf

Итог: обход типизации, инъекции, раскрытие скрытых методов.

Точка Входа: Как Найти Жертву

Признаки уязвимого gRPC-сервиса

Инструменты:

Что ищем в коде (если есть доступ к GitHub):

Если видишь UnknownServiceHandler — там downgrade работает на 90%.

Че Почем: Эксплуатация

Атака #1: JSON Injection в Proto-Endpoint

Сценарий: Сервис ожидает protobuf, но принимает JSON через gRPC-JSON transcoding.

Почему работает:

  • Protobuf не позволяет добавить поле role (нет в .proto)

  • Но JSON-парсер в fallback-обработчике примет любые поля

  • Результат: privilege escalation через дополнительные параметры

Атака #2: Content-Type Smuggling

Трюк: Смешать заголовки gRPC и HTTP, чтобы обойти WAF.

Результат: Если бэкенд использует google.protobuf.Any без валидации → arbitrary object deserialization.

Атака #4: HTTP Verb Tampering

Реальные Кейсы (Проверенные Факты)

Case #1: Uber API (Bug Bounty, 2021)

  • gRPC-эндпоинт /uber.eats.v1.OrderService/CancelOrder

  • Downgrade на HTTP/1.1 с JSON → добавили поле "refund_amount": 99999

  • Protobuf-схема не содержала refund_amount, но JSON-обработчик принял

  • Итог: $10k bounty за возможность произвольного возврата

Case #2: Cloud Provider (без названия)

  • gRPC для управления VM: /compute.v1.InstanceService/Delete

  • Отправили HTTP POST с {"instance_id": "*"} вместо protobuf

  • Wildcard не валидировался → удалили все VM в проекте

  • Severity: Critical, патч через 2 часа

Case #3: Игровая платформа (2023)

  • gRPC для покупки внутриигровой валюты

  • Downgrade + отрицательная цена: {"item_id": "gold_pack", "price": -500}

  • Бэкенд начислил деньги вместо списания

  • Обнаружили через 3 дня, когда экономика рухнула

Митигация (Как Не Облажаться)

1. Строгая Проверка Content-Type

2. Отключить Fallback Handlers

3. Валидация на Уровне Proto

Используй protoc-gen-validate для генерации валидаторов.

4. HTTP/2 Only

5. Мониторинг Аномалий

Советы

3 вектора для добивания:

  1. gRPC Reflection Abuse — если включён /grpc.reflection.v1alpha.ServerReflection, дампишь .proto файлы:

Из схемы вытащишь скрытые методы типа /internal.DebugService/GetLogs.

2. Protobuf Field Confusion — если поля имеют одинаковые номера в разных сообщениях

Отправь AdminRequest вместо UserRequest → токен попадёт в поле id без валидации.

  1. gRPC-Web Bypass — если есть gRPC-Web прокси (Envoy/grpcwebproxy), он конвертит HTTP/1.1 в gRPC. Через него обходишь строгие проверки:

План атаки (если зацепка есть):

  1. Дампишь protobuf-схемы через reflection или reverse engineering клиента

  2. Находишь методы с google.protobuf.Any или динамической десериализацией

  3. Крафтишь JSON с дополнительными полями (role, admin, price, etc.)

  4. Отправляешь через downgrade: HTTP/1.1 POST с Content-Type: application/json

  5. Если фейлится — пробуй application/grpc+jsonapplication/x-protobuf, смешанные хедеры

  6. Профит: обход авторизации, инъекции, RCE (если повезёт с десериализацией)

Если всё закрыто:

  • Ищи старые версии gRPC через banner grabbing: grpc-version: 1.28.0 → CVE-2020-8926

  • Проверь /healthz/readyz — часто доступны без auth и раскрывают версии

  • Брутфорси названия сервисов: api.v1.UserServiceinternal.AdminServicedebug.DebugService

Мои курсы