gRPC Pentesting
gRPC uses Protocol Buffers (Protobuf) for serialization and HTTP/2 for transport. Testing requires tools that can understand the binary format and interact with the service definitions.
Authorization Required
Service Discovery & Reflection
gRPC Reflection lets a client ask the server which services and methods it exposes, plus their message
schemas. That removes the need for a .proto file and is the first thing to check.
Using grpcurl
A single list call answers both questions — if it returns, reflection is enabled and
that is the service inventory:
# Succeeds → reflection is enabled; the output is the service list
grpcurl -plaintext localhost:50051 listThen pull the method and message definitions for one of the services it named:
grpcurl -plaintext localhost:50051 describe com.example.UserServiceDescribing a method gives you the full request/response schema, which is what you need to build the JSON bodies used later on this page:
grpcurl -plaintext localhost:50051 describe com.example.UserService.GetUserWithout Reflection
If reflection is disabled, you need to find the .proto files. Look in mobile app packages, web assets, or public repositories. You can also try to reconstruct them from binary traffic using protoc --decode_raw.
Interacting with gRPC
Invoking Methods
Call methods using grpcurl or Postman.
grpcurl -plaintext -d '{"id": 1}' localhost:50051 com.example.UserService/GetUserIntercepting Traffic
Burp Suite proxies HTTP/2 but does not decode gRPC bodies: each frame is a 5-byte header followed by length-delimited Protobuf, which Burp shows as opaque bytes. You need an extension that turns those bytes into editable fields.
Burp extensions for Protobuf
blackboxprotobuf is the extension most often used for this: it decodes a body without a
.proto file and re-encodes your edits. It is a Jython extension, so see the note on Jython's
status in the field-tampering section below before you build a workflow around it.
gRPC-UI
A web-based UI for interacting with gRPC services, similar to Swagger UI. It reflects the schema and gives you a form per method, which is faster than hand-writing JSON for large messages.
grpcui -plaintext localhost:50051Protobuf Field Tampering
Protobuf fields are identified by field numbers, not names. If you can identify the encoding, you can inject fields the client would not normally send (undocumented fields, admin flags).
# Decode raw protobuf bytes to inspect field numbers
protoc --decode_raw < captured.bin
# Decode using .proto definition
protoc --decode=com.example.UserRequest user.proto < captured.bin
# Replay a modified request as JSON. grpcurl's -d @ reads JSON from
# stdin and has no raw-bytes mode, so this only reaches fields the
# loaded schema declares.
echo '{"userId": 1, "isAdmin": true}' | grpcurl -plaintext \
-d @ localhost:50051 com.example.UserService/GetUser
Note that limit: -d takes a JSON body, and protojson rejects fields that are absent from the
loaded schema. To add a field number the schema does not declare, edit the wire bytes instead: decode the
capture with protoc --decode_raw, append the tag for the field you want (
field_number << 3 | wire_type) plus its value, and replay the modified frame through Burp
Repeater with a Protobuf-decoding extension installed. grpcui has the same JSON-only constraint.
Intercepting gRPC in Burp
mitmproxy sees the same HTTP/2 traffic and its addon API can
decode and rewrite gRPC frames, though you are still editing bytes by hand.
Enum Value & Boundary Fuzzing
Protobuf enums travel as integers and the schema only documents the values the developer intended. Many servers reject an unknown value or coerce it to the default, but some let it fall through to an internal branch. Treat "an undefined enum reaches privileged logic" as a hypothesis to test, not a guaranteed finding: send the out-of-range values, record whether the response actually changes, and report only what you observed.
# Normal request with role=USER (enum value 0)
grpcurl -plaintext -d '{"user_id": 1, "role": 0}' localhost:50051 UserService/GetProfile
# Try enum value 1 (ADMIN), 2 (SUPERADMIN), 99 (undefined)
grpcurl -plaintext -d '{"user_id": 1, "role": 1}' localhost:50051 UserService/GetProfile
grpcurl -plaintext -d '{"user_id": 1, "role": 99}' localhost:50051 UserService/GetProfile
# Server-side streaming — test for resource exhaustion
grpcurl -plaintext -d '{"query": "", "page_size": 99999}' \
localhost:50051 DataService/StreamAllAuthentication & mTLS Testing
gRPC services often use mTLS or JWT metadata for authentication. Test for missing or weak enforcement.
JWT in gRPC Metadata
gRPC uses metadata headers (similar to HTTP headers) for auth tokens. Test if the service validates them.
# Call with valid JWT in metadata
grpcurl -plaintext \
-H "Authorization: Bearer eyJhbGciOi..." \
-d '{"user_id": 1}' localhost:50051 UserService/GetProfile
# Call WITHOUT auth — does it still work?
grpcurl -plaintext -d '{"user_id": 1}' localhost:50051 UserService/GetProfile
# Tampered JWT (change role claim)
grpcurl -plaintext \
-H "Authorization: Bearer TAMPERED_TOKEN" \
-d '{"user_id": 1}' localhost:50051 UserService/GetProfilemTLS Certificate Testing
If mTLS is required, test whether the server validates the client certificate properly.
# Connect with client certificate
grpcurl -cert client.crt -key client.key -cacert ca.crt \
target:443 list
# Test with self-signed cert (should be rejected)
openssl req -x509 -newkey rsa:2048 -keyout fake.key -out fake.crt -days 1 -nodes -subj "/CN=attacker"
grpcurl -cert fake.crt -key fake.key -cacert ca.crt \
target:443 list
# Test without any cert (should fail if mTLS enforced)
grpcurl -cacert ca.crt target:443 listMetadata Injection
gRPC metadata headers can be target vectors for injection if the server processes them as database queries or commands.
# Inject SQL via metadata
grpcurl -plaintext \
-H "X-User-Id: 1' OR 1=1--" \
-d '{}' localhost:50051 UserService/ListUsers
# Inject via gRPC deadline metadata (unusual vector)
grpcurl -plaintext \
-H "grpc-timeout: 999999999n" \
-d '{"query": "*"}' localhost:50051 DataService/SearchService Mesh Considerations
In Kubernetes environments, gRPC services are often behind service meshes like Istio or Linkerd. Test whether sidecar proxies properly enforce authorization policies.
# If you have pod access, try calling services directly (bypassing mesh)
# Find service IPs
kubectl get svc -A | grep grpc
# Direct call bypassing Istio sidecar
grpcurl -plaintext SERVICE_IP:50051 list
# Check if AuthorizationPolicy is enforced
# Try accessing admin services from non-admin namespace podsEvidence Collection
For a gRPC finding, capture:
Discovery output: The grpcurl list and describe output, or the reconstructed proto file, with the endpoint and whether reflection was enabled in production.
Message and status: The request payload in protobuf JSON form, the response, and the gRPC status code with its message from the trailers — a gRPC failure is not an HTTP body.
Certificate identity: For mTLS: which client certificate was presented, whether a self-signed or foreign-service certificate was accepted, and whether a certificate was required at all.
False Positive Identification
- Reflection inside a trusted network: A reflection-enabled service unreachable from an untrusted network is a hardening note, not an exposure. Establish where you are on the network before recording it.
- A field that "disappeared": Protobuf omits fields at their default value, so an absent field in the response is not proof a write was dropped. Set a non-default value, then read it back.
Testing Checklist
Surface
- ☐ Enumerate services and methods by reflection, then confirm which are reachable from your network position and whether the gateway enforces auth the upstream service also enforces.
Messages and transport
- ☐ Tamper every field: out-of-range integers, unknown enum values, wrong wire types, oversized strings and omitted required fields.
- ☐ Test the handshake with no certificate, a self-signed one, an expired one and one issued for another service.
- ☐ Inject into metadata headers (identity, tenant, role) and check the mesh policy from a non-privileged workload.
Remediation & Defense
gRPC has no browser to lean on: every control has to be enforced by the service or the mesh in front of it.
Expose less
- • Disable gRPC reflection in production, and keep admin and internal services off the same host and certificate as customer-facing ones.
Enforce at the transport and message layers
- • TLS everywhere, mTLS or a JWT for every service-to-service call, and validation of every protobuf field — including enum values, rejecting undefined numbers rather than falling through to the default branch.
- • Strip or sanitize gRPC metadata before it reaches backend logic, and enforce mesh authorization policies (Istio AuthorizationPolicy, Linkerd ServerAuthorization) as defence in depth rather than as the only check.
gRPC Security Practice
There is no public gRPC lab we can vouch for here — the repository this page used to link was removed upstream. Run a throwaway reflection-enabled gRPC service locally and work through the Protobuf Field Tampering and Authentication sections above against it.