diff --git a/src/pentesting-web/grpc-web-pentest.md b/src/pentesting-web/grpc-web-pentest.md index 3ea9c8e6712..2b52bfc837b 100644 --- a/src/pentesting-web/grpc-web-pentest.md +++ b/src/pentesting-web/grpc-web-pentest.md @@ -1,155 +1,251 @@ -# Pentesting gRPC-Web - -{{#include ../banners/hacktricks-training.md}} - -## **Manipulating gRPC-Web Payloads** - -gRPC-Web uses Content-Type: `application/grpc-web-text` in requests which is kind of protobuf in base64 encoded form, you can use [gprc-coder](https://github.com/nxenon/grpc-pentest-suite) tool ,and you can also install its [Burp Suite Extension](https://github.com/nxenon/grpc-pentest-suite). - -### **Manual with gGRPC Coder Tool** - -1. First decode the payload: - -```bash -echo "AAAAABYSC0FtaW4gTmFzaXJpGDY6BVhlbm9u" | python3 grpc-coder.py --decode --type grpc-web-text | protoscope > out.txt -``` - -2. Edit the content of decoded payload - -``` -nano out.txt -2: {"Amin Nasiri Xenon GRPC"} -3: 54 -7: {""} -``` - -3. Encode the new payload - -```bash -protoscope -s out.txt | python3 grpc-coder.py --encode --type grpc-web-text -``` - -4. Use output in Burp interceptor: - -``` -AAAAADoSFkFtaW4gTmFzaXJpIFhlbm9uIEdSUEMYNjoePHNjcmlwdD5hbGVydChvcmlnaW4pPC9zY3JpcHQ+ -``` - -### **Manual with gRPC-Web Coder Burp Suite Extension** - -You can use gRPC-Web Coder Burp Suite Extension in [gRPC-Web Pentest Suite](https://github.com/nxenon/grpc-pentest-suite) which is easier. You can read the installation and usage instruction in its repo. - -## **Analysing gRPC-Web Javascript Files** - -There is at least one Javascript file in every gRPC-Web application. You can analyse the file to find new messages, endpoints and services. Try using [gRPC-Scan](https://github.com/nxenon/grpc-pentest-suite) tool. - -1. Download the Javascript gRPC-Web File -2. Scan it with grpc-scan.py: - -```bash -python3 grpc-scan.py --file main.js -``` - -3. Analyse output and test the new endpoints and new services: - -``` -Output: -Found Endpoints: - /grpc.gateway.testing.EchoService/Echo - /grpc.gateway.testing.EchoService/EchoAbort - /grpc.gateway.testing.EchoService/NoOp - /grpc.gateway.testing.EchoService/ServerStreamingEcho - /grpc.gateway.testing.EchoService/ServerStreamingEchoAbort - -Found Messages: - -grpc.gateway.testing.EchoRequest: -+------------+--------------------+--------------+ -| Field Name | Field Type | Field Number | -+============+====================+==============+ -| Message | Proto3StringField | 1 | -+------------+--------------------+--------------+ -| Name | Proto3StringField | 2 | -+------------+--------------------+--------------+ -| Age | Proto3IntField | 3 | -+------------+--------------------+--------------+ -| IsAdmin | Proto3BooleanField | 4 | -+------------+--------------------+--------------+ -| Weight | Proto3FloatField | 5 | -+------------+--------------------+--------------+ -| Test | Proto3StringField | 6 | -+------------+--------------------+--------------+ -| Test2 | Proto3StringField | 7 | -+------------+--------------------+--------------+ -| Test3 | Proto3StringField | 16 | -+------------+--------------------+--------------+ -| Test4 | Proto3StringField | 20 | -+------------+--------------------+--------------+ - -grpc.gateway.testing.EchoResponse: -+--------------+--------------------+--------------+ -| Field Name | Field Type | Field Number | -+==============+====================+==============+ -| Message | Proto3StringField | 1 | -+--------------+--------------------+--------------+ -| Name | Proto3StringField | 2 | -+--------------+--------------------+--------------+ -| Age | Proto3IntField | 3 | -+--------------+--------------------+--------------+ -| IsAdmin | Proto3BooleanField | 4 | -+--------------+--------------------+--------------+ -| Weight | Proto3FloatField | 5 | -+--------------+--------------------+--------------+ -| Test | Proto3StringField | 6 | -+--------------+--------------------+--------------+ -| Test2 | Proto3StringField | 7 | -+--------------+--------------------+--------------+ -| Test3 | Proto3StringField | 16 | -+--------------+--------------------+--------------+ -| Test4 | Proto3StringField | 20 | -+--------------+--------------------+--------------+ -| MessageCount | Proto3IntField | 8 | -+--------------+--------------------+--------------+ - -grpc.gateway.testing.ServerStreamingEchoRequest: -+-----------------+-------------------+--------------+ -| Field Name | Field Type | Field Number | -+=================+===================+==============+ -| Message | Proto3StringField | 1 | -+-----------------+-------------------+--------------+ -| MessageCount | Proto3IntField | 2 | -+-----------------+-------------------+--------------+ -| MessageInterval | Proto3IntField | 3 | -+-----------------+-------------------+--------------+ - -grpc.gateway.testing.ServerStreamingEchoResponse: -+------------+-------------------+--------------+ -| Field Name | Field Type | Field Number | -+============+===================+==============+ -| Message | Proto3StringField | 1 | -+------------+-------------------+--------------+ - -grpc.gateway.testing.ClientStreamingEchoRequest: -+------------+-------------------+--------------+ -| Field Name | Field Type | Field Number | -+============+===================+==============+ -| Message | Proto3StringField | 1 | -+------------+-------------------+--------------+ - -grpc.gateway.testing.ClientStreamingEchoResponse: -+--------------+----------------+--------------+ -| Field Name | Field Type | Field Number | -+==============+================+==============+ -| MessageCount | Proto3IntField | 1 | -+--------------+----------------+--------------+ -``` - -## References - -- [Hacking into gRPC-Web Article by Amin Nasiri](https://infosecwriteups.com/hacking-into-grpc-web-a54053757a45) -- [gRPC-Web Pentest Suite](https://github.com/nxenon/grpc-pentest-suite) - -{{#include ../banners/hacktricks-training.md}} +# Pentesting gRPC-Web +{{#include ../banners/hacktricks-training.md}} +## Quick protocol recap and attack surface +- Transport: gRPC‑Web speaks a browser‑compatible variant of gRPC over HTTP/1.1 or HTTP/2 via a proxy (Envoy/APISIX/grpcwebproxy/etc.). Only unary and server‑streaming calls are supported. +- Content-Types you will see: + - application/grpc-web (binary framing) + - application/grpc-web-text (base64-encoded framing for HTTP/1.1 streaming) +- Framing: every message is prefixed with a 5‑byte gRPC header (1‑byte flags + 4‑byte length). In gRPC‑Web, trailers (grpc-status, grpc-message, …) are sent inside the body as a special frame: first byte with MSB set (0x80) followed by a length and a HTTP/1.1‑style header block. +- Common request headers: x-grpc-web: 1, x-user-agent: grpc-web-javascript/…, grpc-timeout, grpc-encoding. Responses expose grpc-status/grpc-message via trailers/body frames and often via Access-Control-Expose-Headers for browsers. +- Security‑relevant middleware often present: + - Envoy grpc_web filter and gRPC‑JSON transcoder (HTTP<->gRPC bridge) + - Nginx/APISIX gRPC‑Web plugins + - CORS policies on the proxy + +What this means for attackers: +- You can craft requests by hand (binary or base64 text), or let tooling generate/encode them. +- CORS mistakes on the proxy can allow cross‑site, authenticated gRPC‑Web calls (similar to classic CORS issues). +- JSON transcoding bridges may unintentionally expose gRPC methods as unauthenticated HTTP endpoints if routes/auth are misconfigured. + +## Testing gRPC‑Web from the CLI + +### Easiest: buf curl (speaks gRPC‑Web natively) + +- List methods via reflection (if enabled): + +```bash +# list methods (uses reflection) +buf curl --protocol grpcweb https://host.tld --list-methods +``` + +- Call a method with JSON input, auto‑handling gRPC‑Web framing and headers: + +```bash +buf curl --protocol grpcweb \ + -H 'Origin: https://example.com' \ + -d '{"field":"value"}' \ + https://host.tld/pkg.svc.v1.Service/Method +``` + +- If reflection is disabled, provide a schema/descriptor set with --schema or point to local .proto files. See buf help curl. + +### Raw with curl (manual headers + framed body) + +For binary mode (application/grpc-web), send a framed payload (5‑byte prefix + protobuf message). For text mode, base64‑encode the framed payload. + +```bash +# Build a protobuf message, then gRPC-frame it (1 flag byte + 4 length + msg) +# Example using protoscope to compose/edit the message and base64 for grpc-web-text +protoscope -s msg.txt | python3 grpc-coder.py --encode --type grpc-web-text | \ + tee body.b64 + +curl -i https://host.tld/pkg.svc.v1.Service/Method \ + -H 'Content-Type: application/grpc-web-text' \ + -H 'X-Grpc-Web: 1' \ + -H 'X-User-Agent: grpc-web-javascript/0.1' \ + --data-binary @body.b64 +``` + +Tip: Force base64/text mode with application/grpc-web-text when HTTP/1.1 intermediaries break binary streaming. + +### Check CORS behavior (preflight + response) + +- Preflight: + +```bash +curl -i -X OPTIONS https://host.tld/pkg.svc.v1.Service/Method \ + -H 'Origin: https://evil.tld' \ + -H 'Access-Control-Request-Method: POST' \ + -H 'Access-Control-Request-Headers: content-type,x-grpc-web,x-user-agent,grpc-timeout' +``` + +- A vulnerable setup often reflects arbitrary Origin and sends Access-Control-Allow-Credentials: true, allowing cross‑site authenticated calls. Also check Access-Control-Expose-Headers includes grpc-status, grpc-message (many deployments expose these for client libs). + +For generic techniques to abuse CORS, check [CORS - Misconfigurations & Bypass](cors-bypass.md). + +## Manipulating gRPC‑Web payloads + +gRPC‑Web uses Content-Type: application/grpc-web-text as a base64‑wrapped gRPC frame stream for browser compatibility. You can decode/modify/encode frames to tamper with fields, flip flags, or inject payloads. + +Use the [gprc-coder](https://github.com/nxenon/grpc-pentest-suite) tool (and its Burp extension) to speed up round‑trips. + +### Manual with gGRPC Coder Tool + +1. Decode the payload: + +```bash +echo "AAAAABYSC0FtaW4gTmFzaXJpGDY6BVhlbm9u" | python3 grpc-coder.py --decode --type grpc-web-text | protoscope > out.txt +``` + +2. Edit the content of decoded payload + +``` +nano out.txt +2: {"Amin Nasiri Xenon GRPC"} +3: 54 +7: {""} +``` + +3. Encode the new payload + +```bash +protoscope -s out.txt | python3 grpc-coder.py --encode --type grpc-web-text +``` + +4. Use output in Burp interceptor: + +``` +AAAAADoSFkFtaW4gTmFzaXJpIFhlbm9uIEdSUEMYNjoePHNjcmlwdD5hbGVydChvcmlnaW4pPC9zY3JpcHQ+ +``` + +### Manual with gRPC‑Web Coder Burp Suite Extension + +You can use gRPC‑Web Coder Burp Suite Extension in [gRPC‑Web Pentest Suite](https://github.com/nxenon/grpc-pentest-suite) which is easier. You can read the installation and usage instruction in its repo. + +## Analysing gRPC‑Web JavaScript files + +Web apps using gRPC‑Web ship at least one generated JS/TS bundle. Reverse them to extract services, methods, and message shapes. + +- Try using [gRPC-Scan](https://github.com/nxenon/grpc-pentest-suite) to parse bundles. +- Look for method paths like /./, message field numbers/types, and custom interceptors that add auth headers. + +1. Download the JavaScript gRPC‑Web file +2. Scan it with grpc-scan.py: + +```bash +python3 grpc-scan.py --file main.js +``` + +3. Analyse output and test the new endpoints and new services: + +``` +Output: +Found Endpoints: + /grpc.gateway.testing.EchoService/Echo + /grpc.gateway.testing.EchoService/EchoAbort + /grpc.gateway.testing.EchoService/NoOp + /grpc.gateway.testing.EchoService/ServerStreamingEcho + /grpc.gateway.testing.EchoService/ServerStreamingEchoAbort + +Found Messages: + +grpc.gateway.testing.EchoRequest: ++------------+--------------------+--------------+ +| Field Name | Field Type | Field Number | ++============+====================+==============+ +| Message | Proto3StringField | 1 | ++------------+--------------------+--------------+ +| Name | Proto3StringField | 2 | ++------------+--------------------+--------------+ +| Age | Proto3IntField | 3 | ++------------+--------------------+--------------+ +| IsAdmin | Proto3BooleanField | 4 | ++------------+--------------------+--------------+ +| Weight | Proto3FloatField | 5 | ++------------+--------------------+--------------+ +| Test | Proto3StringField | 6 | ++------------+--------------------+--------------+ +| Test2 | Proto3StringField | 7 | ++------------+--------------------+--------------+ +| Test3 | Proto3StringField | 16 | ++------------+--------------------+--------------+ +| Test4 | Proto3StringField | 20 | ++------------+--------------------+--------------+ + +grpc.gateway.testing.EchoResponse: ++--------------+--------------------+--------------+ +| Field Name | Field Type | Field Number | ++==============+====================+==============+ +| Message | Proto3StringField | 1 | ++--------------+--------------------+--------------+ +| Name | Proto3StringField | 2 | ++--------------+--------------------+--------------+ +| Age | Proto3IntField | 3 | ++--------------+--------------------+--------------+ +| IsAdmin | Proto3BooleanField | 4 | ++--------------+--------------------+--------------+ +| Weight | Proto3FloatField | 5 | ++--------------+--------------------+--------------+ +| Test | Proto3StringField | 6 | ++--------------+--------------------+--------------+ +| Test2 | Proto3StringField | 7 | ++--------------+--------------------+--------------+ +| Test3 | Proto3StringField | 16 | ++--------------+--------------------+--------------+ +| Test4 | Proto3StringField | 20 | ++--------------+--------------------+--------------+ +| MessageCount | Proto3IntField | 8 | ++--------------+--------------------+--------------+ + +grpc.gateway.testing.ServerStreamingEchoRequest: ++-----------------+-------------------+--------------+ +| Field Name | Field Type | Field Number | ++=================+===================+==============+ +| Message | Proto3StringField | 1 | ++-----------------+-------------------+--------------+ +| MessageCount | Proto3IntField | 2 | ++-----------------+-------------------+--------------+ +| MessageInterval | Proto3IntField | 3 | ++-----------------+-------------------+--------------+ + +grpc.gateway.testing.ServerStreamingEchoResponse: ++------------+-------------------+--------------+ +| Field Name | Field Type | Field Number | ++============+===================+==============+ +| Message | Proto3StringField | 1 | ++------------+-------------------+--------------+ + +grpc.gateway.testing.ClientStreamingEchoRequest: ++------------+-------------------+--------------+ +| Field Name | Field Type | Field Number | ++============+===================+==============+ +| Message | Proto3StringField | 1 | ++------------+-------------------+--------------+ + +grpc.gateway.testing.ClientStreamingEchoResponse: ++--------------+----------------+--------------+ +| Field Name | Field Type | Field Number | ++==============+================+==============+ +| MessageCount | Proto3IntField | 1 | ++--------------+----------------+--------------+ +``` + +## Bridging and JSON transcoding gotchas + +Many deployments put an Envoy (or similar) proxy in front of the gRPC server: + +- grpc_web filter translates HTTP/1.1 POSTs into HTTP/2 gRPC. +- gRPC‑JSON Transcoder exposes gRPC methods as HTTP JSON endpoints when .proto options (google.api.http) are present. + +From a pentesting perspective: +- Try direct HTTP JSON calls to /./ with application/json when a transcoder is enabled (auth/route mismatches are common): + +```bash +curl -i https://host.tld/pkg.svc.v1.Service/Method \ + -H 'Content-Type: application/json' \ + -d '{"field":"value"}' +``` + +- Review whether unknown methods/parameters are rejected or passed through. Some configs forward unmatched paths upstream, occasionally bypassing auth or request validation. +- Observe x-envoy-original-path and related headers added by proxies. Upstreams that trust these may be abusable if the proxy fails to sanitize them. + +## References + +- [Hacking into gRPC‑Web Article by Amin Nasiri](https://infosecwriteups.com/hacking-into-grpc-web-a54053757a45) +- [gRPC‑Web Pentest Suite](https://github.com/nxenon/grpc-pentest-suite) +- [gRPC‑Web protocol notes (PROTOCOL‑WEB.md)](https://chromium.googlesource.com/external/github.com/grpc/grpc/%2B/v1.16.1/doc/PROTOCOL-WEB.md) + +{{#include ../banners/hacktricks-training.md}}