关于 attacking-grpc-protobuf
面向 API 安全测试人员的 gRPC 与 Protobuf 测试技能,介绍如何恢复方法和消息 Schema,并据此检查服务。内容特别关注授权、元数据信任及内部服务暴露。
主要功能
- 01通过反射、客户端描述符等恢复 Schema
- 02使用 grpcurl 调用服务方法
- 03拦截并解码 gRPC 流量
- 04按方法检查授权与元数据信任
- 05测试未知字段、类型及传输安全
适用场景
- 01测试缺少 .proto 文件的 gRPC 服务
- 02检查低权限用户可访问的方法
- 03评估网关之后暴露的内部服务
- 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