mitmproxy grpc 抓包

黑水發表於2020-10-07

mitmproxy 5.2 支援了 http trailer,可以正常代理 grpc 請求了。
grpc 原始資訊不可讀,可以寫個自定義檢視顯示反序列化後的內容。

先用 grpc 官方示例啟動一個 grpc 服務:

python greeter_server.py

客戶端就用 bloomrpc,不另外寫程式碼了。匯入對應的 proto 檔案,向 localhost:50051 傳送請求驗證服務有沒有正常工作:

因為 mitmproxy 目前只支援 TLS 上的 HTTP/2,還需要再用 nginx 轉發並加上 TLS。
參考這篇文章生成自簽名證照:

openssl genrsa -des3 -out rootCA.key 4096
openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1024 -out rootCA.crt
openssl genrsa -out localhost.key 2048
openssl req -new -key localhost.key -out localhost.csr # Common Name 輸入域名 localhost,其他隨便填一下
openssl req -in localhost.csr -noout -text
openssl x509 -req -in localhost.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out localhost.crt -days 500 -sha256
openssl x509 -in localhost.crt -text -noout
cat localhost.crt localhost.key > localhost.pem

配置 nginx:

server {
listen 6556 ssl http2;
server_name localhost;

ssl_certificate ~/localhost.pem;
ssl_certificate_key ~/localhost.pem;

location / {
grpc_pass grpc://localhost:50051;
}
}

配置好啟動 nginx,在 bloomrpc 裡匯入證照:

向 localhost:6556 傳送請求驗證 nginx 有沒有正常工作:

啟動 mitmproxy,因為是自簽名證照,多加兩個引數:

mitmweb --certs localhost=~/localhost.pem -k

MacOS 上,通過下面的命令重新啟動 bloomrpc,可以讓 bloomrpc 發出的請求經過代理:

export http_proxy=http://127.0.0.1:8080;export https_proxy=http://127.0.0.1:8080;
open /Applications/BloomRPC.app

再次向 localhost:6556 傳送請求,驗證 mitmweb 有沒有正常工作:

然後寫自定義檢視,先安裝 mitmproxy 依賴:

pip install mitmproxy

新建 grpc_addon.py 內容如下:

from mitmproxy import contentviews, http
import helloworld_pb2

class GrpcView(contentviews.View):
name = 'grpc'

def __call__(self, data, **metadata) -> contentviews.TViewResult:
# 參考 https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md,grpc 請求 body 的第一個位元組代表有沒有被壓縮,示例中沒有壓縮,所以不需要額外處理。二到五位元組是 protobuf 內容的長度。
compressed_flag = data[0]
message_length = int.from_bytes(data[1:5], 'big')
message = data[5:message_length + 5]
# 之後就是反序列化 protobuf
if isinstance(metadata['message'], http.HTTPRequest):
message_method = helloworld_pb2.HelloRequest
elif isinstance(metadata['message'], http.HTTPResponse):
message_method = helloworld_pb2.HelloReply

target = message_method()
target.ParseFromString(message)

return 'deserialized grpc', contentviews.format_text(str(target))

grpc_view = GrpcView()

def load(l):
contentviews.add(grpc_view)

def done():
contentviews.remove(grpc_view)

重新啟動 mitmproxy,載入 grpc_addon.py

mitmweb --certs localhost=~/localhost.pem -k -s grpc_addon.py

再次向 localhost:6556 傳送請求,可以檢視反序列化後的內容了:

也可以改寫響應內容,在 grpc_addon.py 加上如下內容後再傳送請求看看:

class Rewrite:
def response(self, flow):
data = flow.response.raw_content
compressed_flag = data[0]
message_length = int.from_bytes(data[1:5], 'big')
message = data[5:message_length + 5]
target = helloworld_pb2.HelloReply()
target.ParseFromString(message)
target.message = 'Hello, rewrited'
modified_message = target.SerializeToString()
# 改寫後內容長度可能會變,所以要重新計算長度
modified_response = data[0].to_bytes(1, byteorder='big') + len(modified_message).to_bytes(4, byteorder='big') + modified_message
flow.response.headers['content-length'] = str(len(modified_response))
flow.response.raw_content = modified_response

addons = [
Rewrite()
]

相關文章