/attacking-grpc-protobuf
Test gRPC and Protocol Buffers services — recovering .proto definitions from server reflection or compiled descriptors, calling methods with grpcurl and grpcui, intercepting HTTP/2 and gRPC-Web traffic, and fuzzing unknown message schemas with protobuf-inspector. Use when a
$ npx -y skills add trilwu/secskills --skill attacking-grpc-protobuf --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/attacking-grpc-protobuf
Context preview
The summary Claude sees to decide when to auto-load this skill.
Test gRPC and Protocol Buffers services — recovering .proto definitions from server reflection or compiled descriptors, calling methods with grpcurl and grpcui, intercepting HTTP/2 and gRPC-Web traffic, and fuzzing unknown message schemas with protobuf-inspector. Use when a
SKILL.md
attacking-grpc-protobuf.SKILL.mdname: attacking-grpc-protobuf
description: Test gRPC and Protocol Buffers services — recovering .proto definitions from server reflection or compiled descriptors, calling methods with grpcurl and grpcui, intercepting HTTP/2 and gRPC-Web traffic, and fuzzing unknown message schemas with protobuf-inspector. Use when a target speaks gRPC, HTTP/2 with application/grpc, or when a request body is opaque binary protobuf rather than JSON.
verified: 2026-07-27
Attacking gRPC and Protobuf Services
gRPC breaks the tooling assumption that a request is readable text over HTTP/1.1. Burp shows a binary blob or nothing at all, and without the schema you cannot even name the methods. Recover the schema and it becomes an ordinary API test — with the twist that gRPC services are frequently internal services newly exposed, which means their authorization is often weaker than the REST API in front of them.
When to Use
- Traffic uses HTTP/2 with `content-type: application/grpc`
- A request or response body is opaque binary with no JSON structure
- The app is a mobile or desktop client talking to a backend over protobuf
- You find `.proto` files, `*_pb2.py`, `*.pb.go`, or `grpc` in a codebase
- `application/grpc-web` or `application/grpc-web+proto` appears in a browser app
When NOT to Use
- **REST or GraphQL** — use `testing-apis`
- **The proxy cannot see the traffic at all** on mobile — use
`bypassing-mobile-pinning`; gRPC clients often ignore system proxy settings
- **Source code is available** — use `auditing-code-for-vulnerabilities`, and
read the `.proto` files directly
- **Binary protocol that is not protobuf** — use `analyzing-binaries`
Recover the Schema
Everything depends on this. Four routes, in order of cost.
**1. Server reflection.** Many services ship it enabled, often unintentionally.
grpcurl -plaintext target:50051 list
grpcurl -plaintext target:50051 list package.ServiceName
grpcurl -plaintext target:50051 describe package.ServiceName.MethodName
grpcurl -plaintext target:50051 describe package.RequestMessage
# TLS
grpcurl target:443 list
grpcurl -insecure target:443 list # self-signed / proxy in path
Reflection enabled on an internet-facing service is itself worth reporting — it is the gRPC equivalent of GraphQL introspection in production.
**2. Descriptors compiled into a client.** Protobuf embeds a serialized `FileDescriptorProto` in generated code, so the schema is recoverable from any client binary.
# Mobile/desktop client: find and extract descriptor blobs
rg -a -o 'google/protobuf/descriptor.proto|\.proto' target_binary | head
# protobuf-inspector and protod can reconstruct .proto from embedded descriptors
protod target_binary -o ./protos
# JS/web clients: the descriptor is usually in the bundle as base64 or an array
rg -n 'grpc-web|serializeBinary|deserializeBinary' bundle.js | head
**3. From the app's source or artifacts.** `.proto` files in a repo, a Swagger gateway config, or generated stubs in a package.
**4. Field-by-field inference.** When you have neither, decode the wire format directly. Protobuf is self-describing enough to recover structure without the schema:
protoc --decode_raw < message.bin
protobuf-inspector < message.bin
# Output: field numbers, wire types, and values — enough to fuzz and to
# recognize strings, nested messages, and integers
You lose field *names* but keep field *numbers*, which is all the wire format needs. That is sufficient to modify values and to add fields the client never sends.
Calling Methods
# With reflection
grpcurl -plaintext -d '{"user_id": 1}' target:50051 package.Service/GetUser
# With a local .proto
grpcurl -import-path ./protos -proto api.proto \
-d '{"user_id": 1}' target:50051 package.Service/GetUser
# Interactive browser UI, good for exploring
grpcui -plaintext target:50051
# Headers, including auth
grpcurl -H 'authorization: Bearer eyJ...' -plaintext target:50051 package.Service/ListFor streaming methods, `grpcurl` accepts newline-delimited JSON on stdin for client streaming, and prints each message for server streaming. Streaming endpoints are frequently less-tested than unary ones and worth specific attention.
Intercepting Traffic
# mitmproxy speaks HTTP/2 and can decode gRPC with a schema
mitmproxy --mode regular --set http2=true
# the gRPC content-view renders protobuf; supply .proto for field names
# Burp: enable HTTP/2, and use a protobuf decoder extension
# without one, you see length-prefixed binary frames
# gRPC-Web is easier — it rides HTTP/1.1 with base64 or binary framing,
# so a normal proxy sees the requests
**gRPC framing**: each message is a 1-byte compression flag, a 4-byte big-endian length, then the protobuf bytes. When a decoder shows nothing, strip those five bytes before feeding the payload to `protoc --decode_raw`.
python3 -c "
import sys
d = sys.stdin.buffer.read()
sys.stdout.buffer.write(d[5:])" < frame.bin | protoc --decode_raw
Clients that ignore the system proxy need a network-layer redirect; see `bypassing-mobile-pinning`.
What to Test
The bug classes are the same as any API, but gRPC changes where they hide.
**Authorization per method.** gRPC has no path-based access control, so a reverse proxy or WAF that filters `/admin/*` does nothing. Each method must check authorization itself — enumerate every method from reflection and call each one with a low-privilege token.
for m in $(grpcurl -plaintext target:50051 list package.Service | tail -n +2); do
echo "== $m"; grpcurl -H "authorization: Bearer $LOW_PRIV" -plaintext -d '{}' target:50051 "$m" 2>&1 | head -3
done**Internal services exposed.** gRPC is a service-mesh protocol, so many services were written assuming only other services would call them. If you can reach one directly, expect no authentication at all — and expect it to trust identity claims passed as ordinary request fields
Read more
name: attacking-grpc-protobuf description: Test gRPC and Protocol Buffers services — recovering .proto definitions from server reflection or compiled descriptors, calling methods with grpcurl and grpcui, intercepting HTTP/2 and gRPC-Web traffic, and fuzzing unknown message schemas with protobuf-inspector. Use when a target speaks gRPC, HTTP/2 with application/grpc, or when a request body is opaque binary protobuf rather than JSON. verified: 2026-07-27
Attacking gRPC and Protobuf Services
gRPC breaks the tooling assumption that a request is readable text over HTTP/1.1. Burp shows a binary blob or nothing at all, and without the schema you cannot even name the methods. Recover the schema and it becomes an ordinary API test — with the twist that gRPC services are frequently internal services newly exposed, which means their authorization is often weaker than the REST API in front of them.
When to Use
- Traffic uses HTTP/2 with `content-type: application/grpc`
- A request or response body is opaque binary with no JSON structure
- The app is a mobile or desktop client talking to a backend over protobuf
- You find `.proto` files, `*_pb2.py`, `*.pb.go`, or `grpc` in a codebase
- `application/grpc-web` or `application/grpc-web+proto` appears in a browser app
When NOT to Use
- **REST or GraphQL** — use `testing-apis`
- **The proxy cannot see the traffic at all** on mobile — use
`bypassing-mobile-pinning`; gRPC clients often ignore system proxy settings
- **Source code is available** — use `auditing-code-for-vulnerabilities`, and
read the `.proto` files directly
- **Binary protocol that is not protobuf** — use `analyzing-binaries`
Recover the Schema
Everything depends on this. Four routes, in order of cost.
**1. Server reflection.** Many services ship it enabled, often unintentionally.
grpcurl -plaintext target:50051 list grpcurl -plaintext target:50051 list package.ServiceName grpcurl -plaintext target:50051 describe package.ServiceName.MethodName grpcurl -plaintext target:50051 describe package.RequestMessage # TLS grpcurl target:443 list grpcurl -insecure target:443 list # self-signed / proxy in path
Reflection enabled on an internet-facing service is itself worth reporting — it is the gRPC equivalent of GraphQL introspection in production.
**2. Descriptors compiled into a client.** Protobuf embeds a serialized `FileDescriptorProto` in generated code, so the schema is recoverable from any client binary.
# Mobile/desktop client: find and extract descriptor blobs rg -a -o 'google/protobuf/descriptor.proto|\.proto' target_binary | head # protobuf-inspector and protod can reconstruct .proto from embedded descriptors protod target_binary -o ./protos # JS/web clients: the descriptor is usually in the bundle as base64 or an array rg -n 'grpc-web|serializeBinary|deserializeBinary' bundle.js | head
**3. From the app's source or artifacts.** `.proto` files in a repo, a Swagger gateway config, or generated stubs in a package.
**4. Field-by-field inference.** When you have neither, decode the wire format directly. Protobuf is self-describing enough to recover structure without the schema:
protoc --decode_raw < message.bin protobuf-inspector < message.bin # Output: field numbers, wire types, and values — enough to fuzz and to # recognize strings, nested messages, and integers
You lose field *names* but keep field *numbers*, which is all the wire format needs. That is sufficient to modify values and to add fields the client never sends.
Calling Methods
# With reflection
grpcurl -plaintext -d '{"user_id": 1}' target:50051 package.Service/GetUser
# With a local .proto
grpcurl -import-path ./protos -proto api.proto \
-d '{"user_id": 1}' target:50051 package.Service/GetUser
# Interactive browser UI, good for exploring
grpcui -plaintext target:50051
# Headers, including auth
grpcurl -H 'authorization: Bearer eyJ...' -plaintext target:50051 package.Service/ListFor streaming methods, `grpcurl` accepts newline-delimited JSON on stdin for client streaming, and prints each message for server streaming. Streaming endpoints are frequently less-tested than unary ones and worth specific attention.
Intercepting Traffic
# mitmproxy speaks HTTP/2 and can decode gRPC with a schema mitmproxy --mode regular --set http2=true # the gRPC content-view renders protobuf; supply .proto for field names # Burp: enable HTTP/2, and use a protobuf decoder extension # without one, you see length-prefixed binary frames # gRPC-Web is easier — it rides HTTP/1.1 with base64 or binary framing, # so a normal proxy sees the requests
**gRPC framing**: each message is a 1-byte compression flag, a 4-byte big-endian length, then the protobuf bytes. When a decoder shows nothing, strip those five bytes before feeding the payload to `protoc --decode_raw`.
python3 -c " import sys d = sys.stdin.buffer.read() sys.stdout.buffer.write(d[5:])" < frame.bin | protoc --decode_raw
Clients that ignore the system proxy need a network-layer redirect; see `bypassing-mobile-pinning`.
What to Test
The bug classes are the same as any API, but gRPC changes where they hide.
**Authorization per method.** gRPC has no path-based access control, so a reverse proxy or WAF that filters `/admin/*` does nothing. Each method must check authorization itself — enumerate every method from reflection and call each one with a low-privilege token.
for m in $(grpcurl -plaintext target:50051 list package.Service | tail -n +2); do
echo "== $m"; grpcurl -H "authorization: Bearer $LOW_PRIV" -plaintext -d '{}' target:50051 "$m" 2>&1 | head -3
done**Internal services exposed.** gRPC is a service-mesh protocol, so many services were written assuming only other services would call them. If you can reach one directly, expect no authentication at all — and expect it to trust identity claims passed as ordinary request fields
**Security skills for Claude Code that encode the judgment professionals bring, not another wrapper around a scanner.** Offense, defense, and the reverse-engineering and code-audit core both sides share. Every skill is fact-checked against primary sources.

