gRPC-Web 流量解密与协议逆向:如何从 Protobuf 编码的 gRPC-Web 请求中提取有效负载?

各位观众老爷,大家好!今天咱们来聊聊 gRPC-Web 的那些事儿,尤其是关于如何扒开它的外衣,看看里面到底装了些啥。如果你曾经被 gRPC-Web 搞得头晕脑胀,不知道怎么解密它的流量,逆向它的协议,那这篇文章绝对能帮到你。

前言:啥是 gRPC-Web?

简单来说,gRPC-Web 就是 gRPC 的一个变种,专门为浏览器环境量身定制。由于浏览器天然的限制,无法直接使用标准的 gRPC 协议,所以 Google 大佬们搞出了 gRPC-Web 这么个东西。它通过一个 Envoy 之类的代理服务器,将浏览器发出的 HTTP/1.1 请求转换成标准的 gRPC 请求,然后再发送给后端的 gRPC 服务。

一、为什么要解密 gRPC-Web 流量?

你可能会问,好好的流量,干嘛要解密?原因有很多:

  • 调试: 当你的前端和后端联调出现问题时,解密流量可以让你清晰地看到客户端发了什么,服务端回了什么,从而快速定位问题。
  • 安全分析: 如果你需要分析 gRPC-Web 应用的安全性,解密流量是必不可少的。你可以检查客户端是否发送了敏感信息,服务端是否返回了不安全的数据等等。
  • 协议逆向: 假设你想要开发一个 gRPC-Web 客户端,但没有官方文档,或者官方文档不全,那解密流量就是你了解协议细节的唯一途径。
  • 渗透测试: 作为一名光荣的渗透测试工程师,解密 gRPC-Web 流量可以帮助你发现潜在的安全漏洞。

二、gRPC-Web 流量长啥样?

gRPC-Web 流量本质上是 HTTP/1.1 请求,只不过它的 Body 部分是用 Protobuf 编码的。我们可以用 Wireshark、Fiddler 或者 Charles 这些抓包工具来捕获流量。

一个典型的 gRPC-Web 请求看起来大概是这个样子:

POST /your.service.YourMethod HTTP/1.1
Host: your.domain.com
Content-Type: application/grpc-web+proto
Content-Length: 30

x00x00x00x00x0ax0ax08x08x01x10x02x18x03

响应大概是这样:

HTTP/1.1 200 OK
Content-Type: application/grpc-web+proto
Trailer: Grpc-Status, Grpc-Message

x00x00x00x00x08x0ax06x08x04x10x05x18x06
Grpc-Status: 0

注意几个关键点:

  • Content-Type: 必须是 application/grpc-web+proto,或者 application/grpc-web-text+proto (base64 编码)。
  • POST 请求: gRPC-Web 通常使用 POST 请求。
  • 请求路径: 请求路径通常是 /package.Service/Method 这种形式。
  • Body: 请求和响应的 Body 部分都是 Protobuf 编码的数据。
  • Trailer: 响应的 Trailer 部分包含 gRPC 的状态码和错误信息。

三、解密 gRPC-Web 流量的步骤

解密 gRPC-Web 流量,主要分两步:

  1. 找到 .proto 文件: 这是解密的关键,没有 .proto 文件,一切都是白搭。.proto 文件定义了 gRPC 服务的接口和消息格式。
  2. 使用工具解码 Protobuf 数据: 有了 .proto 文件,就可以使用工具将 Protobuf 编码的数据解码成可读的 JSON 格式。

3.1 找到 .proto 文件

找到 .proto 文件,有以下几种方法:

  • 询问开发者: 最直接的方法,直接问开发人员要。
  • 从源代码中获取: 如果你能拿到 gRPC 服务的源代码,那 .proto 文件肯定就在里面。
  • 反编译: 如果服务端是编译型的语言(比如 Go、Java),你可以尝试反编译服务端程序,从中提取 .proto 文件。
  • 猜测: 如果以上方法都行不通,那就只能靠猜了。根据接口名称和参数类型,猜测 .proto 文件的内容。这种方法比较困难,但也不是完全不可能。

3.2 解码 Protobuf 数据

有了 .proto 文件,就可以使用工具来解码 Protobuf 数据了。常用的工具有:

  • protoc 编译器: Google 官方提供的 Protobuf 编译器,可以将 .proto 文件编译成各种语言的代码。同时,它也可以用来解码 Protobuf 数据。
  • protoc-gen-decode 插件: 一个 protoc 插件,可以将 Protobuf 数据解码成 JSON 格式。比直接用 protoc 方便。
  • grpcurl 工具: 一个命令行工具,可以发送 gRPC 请求,并显示响应结果。它也支持解码 Protobuf 数据。
  • 在线 Protobuf 解码器: 网上有很多在线的 Protobuf 解码器,可以直接将 Protobuf 数据粘贴到网页上,然后解码成 JSON 格式。

四、实战演练:使用 protoc-gen-decode 解码 gRPC-Web 流量

咱们以 protoc-gen-decode 为例,演示如何解码 gRPC-Web 流量。

4.1 安装 protocprotoc-gen-decode

首先,你需要安装 protoc 编译器。安装方法根据你的操作系统而定,可以参考 Protobuf 官方文档。

然后,安装 protoc-gen-decode 插件:

go install github.com/jhump/protoc-gen-decode@latest

确保 $GOPATH/bin 目录在你的 PATH 环境变量中。

4.2 准备 .proto 文件

假设我们有一个简单的 gRPC 服务,定义如下:

syntax = "proto3";

package your.service;

service YourService {
  rpc YourMethod (YourRequest) returns (YourResponse);
}

message YourRequest {
  int32 id = 1;
  string name = 2;
  bool enabled = 3;
}

message YourResponse {
  int32 code = 1;
  string message = 2;
}

将以上内容保存为 your.proto 文件。

4.3 抓取 gRPC-Web 流量

使用 Wireshark 或者 Fiddler 抓取 gRPC-Web 请求和响应的 Body 部分。假设我们抓到的请求 Body 是:

x00x00x00x00x0ax0ax08x08x01x10x02x18x03

响应 Body 是:

x00x00x00x00x08x0ax06x08x04x10x05x18x06

4.4 解码 Protobuf 数据

使用以下命令解码请求 Body:

protoc --decode_json=your.service.YourRequest --proto_path=. your.proto < request.bin

其中,request.bin 文件包含请求 Body 的内容。

使用以下命令解码响应 Body:

protoc --decode_json=your.service.YourResponse --proto_path=. your.proto < response.bin

其中,response.bin 文件包含响应 Body 的内容。

如果一切顺利,你将会看到类似以下的 JSON 输出:

请求:

{
  "id": 1,
  "name": "2",
  "enabled": true
}

响应:

{
  "code": 4,
  "message": "5"
}

4.5 处理 gRPC-Web 帧格式

gRPC-Web 使用一种特殊的帧格式,需要在解码之前进行处理。每个 gRPC-Web 消息都包含一个或多个帧。每一帧的格式如下:

字段 大小 (bytes) 说明
Compression Flag 1 如果设置为 1,则表示消息体被压缩。通常使用 gzip 压缩。
Length 4 消息体的长度 (不包括 Compression Flag 和 Length 字段)。
Message Length 消息体,通常是 Protobuf 编码的数据。

所以,我们需要跳过 Compression Flag 和 Length 字段,才能得到真正的 Protobuf 数据。在上面的例子中,x00x00x00x00x0ax0ax08x08x01x10x02x18x03 这段数据,前 5 个字节 x00x00x00x00x0a 是帧头,后面的 x0ax08x08x01x10x02x18x03 才是真正的 Protobuf 数据。

我们可以使用 dd 命令来提取 Protobuf 数据:

dd if=request.bin bs=1 skip=5 of=protobuf_data.bin

然后再使用 protoc 命令解码 protobuf_data.bin 文件。

五、处理 Base64 编码的 gRPC-Web 流量

如果 gRPC-Web 请求的 Content-Typeapplication/grpc-web-text+proto,那么请求和响应的 Body 部分会被 Base64 编码。你需要先将 Base64 编码的数据解码,然后再进行 Protobuf 解码。

可以使用 base64 命令来解码 Base64 编码的数据:

base64 -d < base64_data.txt > binary_data.bin

然后再使用 protoc 命令解码 binary_data.bin 文件。

六、代码示例:使用 Python 解码 gRPC-Web 流量

下面是一个使用 Python 解码 gRPC-Web 流量的示例代码:

import base64
import struct
from google.protobuf import json_format
from google.protobuf import descriptor_pb2
from google.protobuf import message_factory

def decode_grpc_web(proto_file, message_type, data):
    """
    解码 gRPC-Web 流量。

    Args:
        proto_file: .proto 文件的路径。
        message_type: 消息类型,例如 "your.service.YourRequest"。
        data: gRPC-Web 流量的 Body 部分。

    Returns:
        解码后的 JSON 数据。
    """

    # 1. 解析 .proto 文件
    with open(proto_file, "rb") as f:
        proto_content = f.read()

    file_descriptor_set = descriptor_pb2.FileDescriptorSet()
    file_descriptor_set.ParseFromString(proto_content)

    # 2. 创建 MessageFactory
    factory = message_factory.MessageFactory(file_descriptor_set.file)

    # 3. 获取消息类
    message_class = factory.GetPrototype(message_type)
    if message_class is None:
        raise ValueError(f"Message type '{message_type}' not found in proto file.")

    # 4. 处理 gRPC-Web 帧格式
    compression_flag = data[0]
    length = struct.unpack(">I", data[1:5])[0]
    protobuf_data = data[5: 5 + length]

    # 5. 解码 Protobuf 数据
    message = message_class()
    message.ParseFromString(protobuf_data)

    # 6. 转换为 JSON
    json_data = json_format.MessageToJson(message)

    return json_data

# 示例用法
if __name__ == "__main__":
    proto_file = "your.proto"
    message_type = "your.service.YourRequest"
    grpc_web_data = b"x00x00x00x00x0ax0ax08x08x01x10x02x18x03"  # 替换为你的 gRPC-Web 数据

    try:
        json_data = decode_grpc_web(proto_file, message_type, grpc_web_data)
        print(json_data)
    except Exception as e:
        print(f"Error decoding gRPC-Web data: {e}")

七、总结与注意事项

  • .proto 文件是关键: 没有 .proto 文件,几乎不可能解密 gRPC-Web 流量。
  • 注意 gRPC-Web 帧格式: 需要跳过帧头才能得到真正的 Protobuf 数据。
  • 处理 Base64 编码: 如果 Content-Typeapplication/grpc-web-text+proto,需要先解码 Base64 数据。
  • 错误处理: 解码 Protobuf 数据时,可能会出现各种错误,比如 .proto 文件不正确,消息类型不存在等等。需要做好错误处理。
  • 加密: 如果 gRPC-Web 使用了 TLS 加密,你需要先解密 TLS 流量,才能看到 gRPC-Web 的内容。可以使用 Wireshark 或者 Fiddler 来解密 TLS 流量。

八、进阶技巧:使用 Burp Suite 插件进行 gRPC-Web 渗透测试

Burp Suite 是一款强大的 Web 应用安全测试工具。有一些 Burp Suite 插件可以帮助你进行 gRPC-Web 渗透测试。

  • protobuf-decoder: 可以自动解码 Protobuf 数据,并显示成可读的 JSON 格式。
  • grpc-burp-plugin: 可以发送 gRPC 请求,并显示响应结果。

这些插件可以大大提高你的 gRPC-Web 渗透测试效率。

九、实际案例:一个 gRPC-Web 漏洞挖掘的故事

曾经有位安全研究员,通过解密某个 gRPC-Web 应用的流量,发现了一个SQL注入漏洞。这个应用的 .proto 文件中,有一个接口接受用户输入的字符串作为参数,然后直接将这个字符串拼接到 SQL 查询语句中,导致了SQL注入漏洞。

这个案例告诉我们,即使是使用了 gRPC-Web 这种看似复杂的协议,也仍然可能存在传统的 Web 应用安全漏洞。

十、Q&A 环节

现在是大家提问的时间,有什么关于 gRPC-Web 解密和逆向的问题,都可以问我。我会尽力解答。

问: 如果我没有 .proto 文件,就真的没法解密 gRPC-Web 流量了吗?

答: 也不是完全没戏。你可以尝试猜测 .proto 文件的内容,或者使用一些高级的逆向技术来分析 gRPC-Web 应用。但这需要很强的技术功底和大量的耐心。所以,还是尽量找 .proto 文件吧。

问: gRPC-Web 流量解密会不会侵犯用户隐私?

答: 这是一个非常重要的问题。在解密 gRPC-Web 流量之前,一定要确保你获得了用户的授权,并且遵守相关的法律法规。如果你没有获得用户的授权,解密流量是违法的。

问: 除了 protoc-gen-decode,还有没有其他好用的 Protobuf 解码工具?

答: 当然有。grpcurl 也是一个不错的选择。它是一个命令行工具,可以发送 gRPC 请求,并显示响应结果。它也支持解码 Protobuf 数据。另外,网上也有很多在线的 Protobuf 解码器,可以直接将 Protobuf 数据粘贴到网页上,然后解码成 JSON 格式。

结束语

好了,今天的 gRPC-Web 流量解密与协议逆向讲座就到这里。希望这篇文章能够帮助你更好地理解 gRPC-Web,并在实际工作中应用这些技术。记住,技术是把双刃剑,一定要合理使用,遵守法律法规。

下次有机会,我们再聊聊其他的技术话题。祝大家工作顺利,生活愉快!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注