mcpField
A
attacking-grpc-protobuf

attacking-grpc-protobuf

github.com/trilwu安全17749 次安装12 周前更新
代码仓库

关于 attacking-grpc-protobuf

面向 API 安全测试人员的 gRPC 与 Protobuf 测试技能,介绍如何恢复方法和消息 Schema,并据此检查服务。内容特别关注授权、元数据信任及内部服务暴露。

主要功能

  1. 01通过反射、客户端描述符等恢复 Schema
  2. 02使用 grpcurl 调用服务方法
  3. 03拦截并解码 gRPC 流量
  4. 04按方法检查授权与元数据信任
  5. 05测试未知字段、类型及传输安全

适用场景

  1. 01测试缺少 .proto 文件的 gRPC 服务
  2. 02检查低权限用户可访问的方法
  3. 03评估网关之后暴露的内部服务
  4. 04分析客户端或浏览器中的 gRPC 流量

安装

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

$npx skills add https://github.com/trilwu/secskills --skill attacking-grpc-protobuf

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

详细介绍

攻击 gRPC 和 Protobuf 服务

gRPC 打破了一个工具层面的假设:请求是通过 HTTP/1.1 传输的可读文本。Burp 显示的可能是一段二进制数据,也可能什么都没有;没有 schema,你甚至无法确定方法名称。恢复 schema 后,它就成了普通的 API 测试——不同之处在于,gRPC 服务往往是新近暴露出来的内部服务,因此其授权通常弱于前端的 REST API。

适用场景

  • 流量使用 HTTP/2,且 content-type: application/grpc
  • 请求或响应正文是没有 JSON 结构的不可读二进制数据
  • 移动端或桌面客户端通过 protobuf 与后端通信
  • 在代码库中发现 .proto 文件、*_pb2.py、*.pb.go 或 grpc
  • 浏览器应用中出现 application/grpc-web 或 application/grpc-web+proto

不适用场景

  • REST 或 GraphQL——使用 testing-apis
  • 代理完全无法看到移动端流量——使用 bypassing-mobile-pinning;gRPC 客户端常常忽略系统代理设置
  • 有源代码可用——使用 auditing-code-for-vulnerabilities,并 直接阅读 .proto 文件
  • 非 protobuf 的二进制协议——使用 analyzing-binaries

恢复 Schema

一切都取决于此。按成本从低到高,有四种方法。

1. 服务端反射。 许多服务启用了反射,往往是无意为之。

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

面向互联网的服务若启用了反射,本身就值得报告—— 这相当于在生产环境中启用了 GraphQL 内省。

2. 编译进客户端的描述符。 Protobuf 会在生成的代码中嵌入序列化的 FileDescriptorProto,因此可以从任意客户端二进制文件中恢复 schema。

# 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. 从应用的源代码或构建产物中恢复。 例如代码库中的 .proto 文件、Swagger 网关配置,或包中的生成代码存根。

4. 逐字段推断。 如果以上方法都行不通,可以直接解码线格式。Protobuf 的自描述能力足以在没有 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

你会失去字段名称,但保留字段编号,而线格式只需要这些信息。这足以修改字段值,并添加客户端从未发送过的字段。

调用方法

# 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/List

对于流式方法,grpcurl 接受标准输入中的、以换行符分隔的 JSON 用于客户端流式传输,并会为服务端流式传输逐条打印消息。流式端点往往比一元端点更少经过测试,值得特别关注。

拦截流量

# 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 帧格式:每条消息由 1 字节压缩标志、4 字节大端序长度和 protobuf 字节组成。若解码器没有显示任何内容,请先剥除这 5 个字节,再将载荷传给 protoc --decode_raw。

python3 -c "
import sys
d = sys.stdin.buffer.read()
sys.stdout.buffer.write(d[5:])" < frame.bin | protoc --decode_raw

忽略系统代理的客户端需要在网络层进行重定向;参见 bypassing-mobile-pinning。

测试内容

漏洞类型与任何 API 相同,但在 gRPC 中,它们藏身的位置有所不同。

按方法授权。 gRPC 没有基于路径的访问控制,因此,反向代理或 WAF 过滤 /admin/* 毫无作用。每个方法都必须自行检查授权——通过反射枚举所有方法,并使用低权限令牌逐一调用。

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

内部服务暴露。 gRPC 是一种服务网格协议,因此许多服务在编写时都假定只有其他服务会调用它们。如果你能直接访问某个服务,就要做好它完全没有身份验证的准备——还要注意,它可能会信任作为普通请求字段或元数据传入的身份声明。

对元数据的信任。 查找服务用作身份信息的标头: x-user-id、x-tenant-id、x-forwarded-user。如果网关会设置这些标头,而服务信任它们,那么你自行发送这些标头就能完全绕过身份验证。

grpcurl -H 'x-user-id: 1' -H 'x-role: admin' -plaintext -d '{}' target:50051 package.Service/GetProfile

注入未知字段。 Protobuf 会忽略无法识别的字段,但中间服务可能会转发它们。更有效的做法是:添加 客户端 从未发送、但 服务端 schema 中定义的字段——is_admin、internal_notes、tenant_id——这相当于批量赋值攻击。

类型混淆和资源耗尽。 Wire 类型的约束较宽松;当预期类型为 string 时,发送 bytes;发送深度嵌套的消息以突破递归限制,或发送超大的 repeated 字段。检查是否配置了消息大小限制。

TLS 和 mTLS 安全状况。 许多 gRPC 部署在内部使用 insecure 通道。检查服务是否接受明文连接,以及是否强制要求 mTLS,还是仅将其设为可选。

应当摒弃的借口

  • “Burp 什么都没显示,应用没有发出请求。” Burp 需要启用 HTTP/2,而且 gRPC 客户端通常会完全绕过系统代理。
  • “我没有 .proto 文件,所以没法测试。” protoc --decode_raw 可以从任何消息中恢复结构。你只需要字段编号。
  • “反射已禁用,所以方法是隐藏的。” 客户端知道 schema。可以从客户端二进制文件中提取描述符。
  • “网关会强制执行授权。” 网关能看到方法名,却看不到意图;任何能直接到达服务的请求都能完全绕过网关。
  • “这仅供内部使用。” 请确认这一点。服务网格可能发生信息泄漏,而且“内部”通常意味着“没有身份验证”。
  • “客户端从不发送那个字段。” 你不是客户端。

ATT&CK 覆盖范围

由 secskills-core/ttp-index.json 生成——编辑该文件,然后运行 python3 scripts/sync_attack.py --write。在报告中引用 ID 前,请对照 当前 ATT&CK 版本重新核实。

初始访问(TA0001)

  • T1190 利用面向公众的应用程序——另请参阅 testing-web-applications、testing-apis、enumerating-network-services、attacking-graphql、exploiting-deserialization、exploiting-ssrf、exploiting-xxe

这些技术的检测内容:engineering-detections。主动搜索:hunting-threats。入侵后:responding-to-incidents。

参考资料

  • testing-apis — 本文专门针对的通用 API 方法
  • auditing-code-for-vulnerabilities — 阅读源码中的 .proto 文件和处理程序
  • bypassing-mobile-pinning — 移动 gRPC 客户端忽略你的代理时
  • exploiting-cloud-platforms — 服务网格和内部暴露相关背景
  • grpcurl、grpcui、protoc、protobuf-inspector、protod、mitmproxy

评论

支持 Markdown