gRPC-Pentest-Suite is set of tools for pentesting / hacking gRPC Web applications.
Available Content Types:
- application/grpc-web-text
- application/grpc-web+proto
gRPC-Pentest-Suite contains these 2 tools:
- grpc_scan scanning the gRPC-web javascript webpacked files to detect grpc endpoints, services, messages and field types
- grpc_web_burp_extension.py extension for burp suite to easily using gRPC-Coder tool
- application/grpc-web-text support
- application/grpc-web+proto support
- You can download this one using BApp
- grpc_coder encoding and decoding gRPC-web payloads for pentesting (manipulating payloads)
- only application/grpc-web-text support
- grpc_protobuf_decoder standalone pure-Python protobuf decoder/encoder (no dependencies, no protoscope needed)
- decodes & encodes the protobuf wire format directly
- supports grpc-web-text, grpc-web+proto, raw protobuf bytes and hex (e.g. a Kafka protobuf message)
- big_string_chunker this tool chunks a big string into pieces of 80 characters, so that gRPC-coder can encode it (also reverse)
- old_grpc_web_burp_extension_with_dependency.py old extension for burp suite which has some dependencies
- only application/grpc-web-text support
grpc_web_burp_extension.py file is new extension which does not have any dependencies and all dependencies are in this repo (in lib dir).
- Main Settings:
- Message Editor:
- Message Editor Decoded:
- Create tutorial video for using new extension
- The extension automatically gets enabled when the request has
Content-Typeorx-grpc-content-typeheaders (based on the settings you have set) with value of:- application/grpc-web-text (extension automatically decodes and encodes the body)
- application/grpc-web+proto (extension automatically decodes and encodes the body)
- Also, there is new burp tab which you can enable or disable encoding format checkboxes
- Download the Whole Repository (the extension needs files in this repo)
- add grpc_web_burp_extension.py in Burp Extensions.
- New tab in repeater message editor
- edit proto type definition
- automatically detect grpc-web-text requests (via
Content-Typeorx-grpc-content-typeheaders)- note:
x-grpc-content-typeis for when you want manually enable gRPC-Web Decoder Tab - note: if you set value of headers to application/grpc-web-text the extension automatically decodes and encodes the payloads
- note:
after installing the extension it adds to menu items into extensions menu item:
- gRPC Coder Decode
- gRPC Coder Encode
Steps:
- select the gRPC-Web base64 payload in burp interceptor or repeater and click on Decode item for decoding to human-readable format
- edit the text and select the new edited text and click on Encode item for encoding to gRPC-Web base64 format
This article includes the methodology for pentesting gRPC-Web and a methodology for finding hidden servies and endpoints. Read Hacking into gRPC-Web article and for application/grpc-web+proto see this article Hacking into gRPC-Web : Part 2.
This video includes using both gRPC Scan tool and gRPC Coder Burp Suite Extension: How to manipulate gRPC-Web payloads and analyse the JavaScript webpacked files to find hidden endpoints, services and messages. Watch
- Download the Whole Repository (the extension needs some files in this repo)
- add old_grpc_web_burp_extension_with_dependency.py in Burp Extensions.
- This extension has some dependencies which need to be installed: Requirements
Note: protoscope and python3 must be system globally installed.
pip3 install -r requirements.txt
for grpc_coder.py you need to install protoscope in system gloablly.
go install github.com/protocolbuffers/protoscope/cmd/protoscope...@latest
for gRPC Coder Burp Extension you need to have these requirements:
- download the whole repository (because the script needs grpc.coder.py)
- jython must be installed and configured in burp
- protoscope must be installed globally on system (because the extension runs a protoscope command)
- python3 must be installed to run the grpc_coder.py script (because the gRPC-Coder is written in python3)
- in windows python 3 binary name is python and in linux and mac the binary name is python3
the extension runs two safe commands to work with grpc_coder.py and protoscope tools.
New Features:
- Automatically Encode/Decode by New Decoded Protobuf Tab (you can directly view the decoded protobuf in the Burp tool (Repeater, Proxy, Intruder...) AND automatically encode it back if we changed anything.)
-
Scanner Insertion Points (now if you right-click on an application/grpc-web-text HTTP request / host -> Scan -> Active Scan, Burp will manage to recognize the format, decode it, insert payloads in any field, and encode it back.)
1: { 9: 0 10: 0 19: {"test"} 25: { "#{\"\".getClass().forName(\"java.net.URL\").getConstructors()[2].newInstance(\"http:/" "/xxxx.oastify.com.\").hashCode()}" } } 10: {2: 20}
grpc_coder.py has two options:
python3 grpc_coder.py --help
echo payload | python3 grpc_coder.py [--encode OR --decode]
General Arguments:
--encode encode protoscope binary output to application/grpc-web-text
--decode decode application/grpc-web-text base64 encoded payload to protoscope format
--type content-type of payload [default: grpc-web-text] available types: [grpc-web-text, grpc-web+proto]
Input Arguments:
Default Input is Standard Input
--file to get input from a file
Help:
--help print help message
In Burp Suite when you intercept the request, get the gRPC-Web base64 encoded payload and give it to the script as standard input:
echo "AAAAABYSC0FtaW4gTmFzaXJpGDY6BVhlbm9u" | python3 grpc_coder.py --decode --type grpc-web-text | protoscope > out.txt
cat out.txt
content of out.txt:
2: {"Amin Nasiri"}
3: 54
7: {"Xenon"}
vim out.txt
... edit the file
content of edited out.txt:
cat out.txt
2: {"Amin Nasiri Xenon GRPC"}
3: 54
7: {"<script>alert(origin)</script>"}
now you have to encode the new payload: Encode
after editing decoded payload you have to encode it:
protoscope -s out.txt | python3 grpc_coder.py --encode --type grpc-web-text
Output:
AAAAADoSFkFtaW4gTmFzaXJpIFhlbm9uIEdSUEMYNjoePHNjcmlwdD5hbGVydChvcmlnaW4pPC9zY3JpcHQ+
Then you put the new base64 payload into Burp Suite intercepted request.
grpc_protobuf_decoder.py is a pure-Python tool that decodes and encodes the protobuf wire format itself, with no dependencies and without needing protoscope.
Use this when you do not want to install protoscope, or when the payload is plain protobuf that is not wrapped in a gRPC-Web frame (for example a protobuf message taken from a Kafka topic, a file, a .bin dump, etc.).
Note: protobuf wire bytes do not contain field names, so output is field-number based (the same as protoscope).
python3 grpc_protobuf_decoder.py --help
--decode decode a payload into readable text (default)
--encode encode the readable text format back into a protobuf payload
--type payload format:
grpc-web-text base64, default e.g. AAAAAAUKA2Zvbw==
grpc-web+proto raw gRPC framed bytes
raw raw protobuf bytes (no gRPC frame)
hex hex string of raw protobuf bytes
--file read input from a file instead of stdin
The readable text format (printed by --decode, consumed by --encode):
1: "a string" # length-delimited string
2: 150 # varint
3: 0xdeadbeef # raw bytes (hex)
4: fixed64=123 # 8-byte fixed (wire type 1)
5: fixed32=42 # 4-byte fixed (wire type 5)
6: { # nested message
1: "nested"
}
echo "AAAAABYSC0FtaW4gTmFzaXJpGDY6BVhlbm9u" | python3 grpc_protobuf_decoder.py --decode
Output (varints also show alternative interpretations, since the wire type alone is ambiguous):
2: "Amin Nasiri"
3: 54, zigzag=27
7: "Xenon"
When you re-encode, only the leading integer is used and the hints are ignored, so you can leave them or delete them.
A protobuf message stored in a Kafka topic is just raw protobuf bytes (there is no gRPC application/grpc-web-text framing), so use --type raw for a binary file or --type hex for a hex string.
Decode a raw protobuf value dumped from Kafka into a file:
# message.bin holds the raw protobuf bytes of a Kafka record value
python3 grpc_protobuf_decoder.py --decode --type raw --file message.bin
# or as a hex string:
echo '0a03666f6f1096011a070a036261721001' | python3 grpc_protobuf_decoder.py --decode --type hex
Output:
1: "foo"
2: 150, zigzag=75
3: {
1: "bar"
2: 1, zigzag=-1, bool=true
}
Edit the fields you want (change a value, inject a payload, etc.), then encode it back to raw protobuf bytes you can produce/publish to Kafka:
printf '1: "foo INJECTED"\n2: 9999\n3: {\n 1: "bar"\n 2: 1\n}\n' \
| python3 grpc_protobuf_decoder.py --encode --type raw > new_message.bin
# or get a hex string instead of a binary file:
printf '1: "foo INJECTED"\n2: 9999\n' \
| python3 grpc_protobuf_decoder.py --encode --type hex
To turn the same edited text back into a gRPC-Web payload (re-adds the frame + base64), just drop --type (defaults to grpc-web-text):
printf '2: "Amin Nasiri Xenon GRPC"\n3: 54\n7: "<script>alert(origin)</script>"\n' \
| python3 grpc_protobuf_decoder.py --encode
Confluent Schema Registry note: if your Kafka producer uses the Confluent wire format, each value has a 1-byte magic (
0x00) + 4-byte schema id + a protobuf message-index header before the protobuf bytes. Strip that prefix before decoding with--type raw, and re-add it after encoding.
When you have a big string that you want to put it into a value in protobuf fields, you have to make that string into some pieces of characters using big_string_chunker.py.
For Example:
This String is big:
"T2dnUwACAAAAAAAAAABzFQAAAAAAAAAJCzcBE09wdXNIZWFkAQE4AYC7AAAAAABPZ2dTAAAAAAAAAAAAAHMVAAABAAAAo2rOoQE3T3B1c1RhZ3MPAAAAbGlib3B1cyB1bmtub3duAQAAABQAAABFTkNPREVSPU1vemlsbGExMjQuME9nZ1MAAMAwAAAAAAAAcxUAAAIAAAD1DNygG//T/yb/KP//CP8h/yT/JP8k/yX/JP8l/yj/Kfh4/5AiWRn+hxCNu1lGW1E1RpFlgncP1g3KdvtuuhDanwxtyvMzTX/X3ain7fAXGnRupDzl9oir"jHtN7BZBGZZW9Vkyv2oBhgfnGhJPxrf7RJ9D4e2AABS0iAuHWWWzs0UZpgwlqMwOZ+w4PIymRYPzCB5q9C9JFVUjdihmqLbP8WICC+0eSFmUO+lM4PYiVprOWgfbwTcNqaYdZSKT3fp2pjNuTJzyvEO/t2Dg1TnCwjoq0veEM1YcRx4polaFw/au+FdceT13SuK8ehmSEHPyLB1H2lUAAAAAAAAAAaBfGjYa5md8lEWEol5mykby0OgcohE0KzMpefR9SiVHFG7sL0r7JrAeot6SRV1x1iWWVBejRscEDQA0gyXKQnrH1P+/cIqNOLFZzHVfcTfCbDASrlauLF5i9eLUEFv289im/BQqPPGkld7iwBlOA5zZz4ysnRYDv8VytH9F9vLqNgpiWqNO0pgr+4Dl9i4vtxgCYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEEH5ttS9etaTCa18br69R/RM6tCIKjxjEULqgaJJQkCBwJxDR9kAsol/Xymr7cFKgJ+0crArSf9IqQ/WgqEAgEtmTqgwA0BkOTT4q2YhAygIak+pZZu654kaBYG+9Hag=="
the tool converts it to this:
1: {
"T2dnUwACAAAAAAAAAABzFQAAAAAAAAAJCzcBE09wdXNIZWFkAQE4AYC7AAAAAABPZ2dTAAAAAAAAAAAA"
"AHMVAAABAAAAo2rOoQE3T3B1c1RhZ3MPAAAAbGlib3B1cyB1bmtub3duAQAAABQAAABFTkNPREVSPU1v"
"emlsbGExMjQuME9nZ1MAAMAwAAAAAAAAcxUAAAIAAAD1DNygG//T/yb/KP//CP8h/yT/JP8k/yX/JP8l"
"/yj/Kfh4/5AiWRn+hxCNu1lGW1E1RpFlgncP1g3KdvtuuhDanwxtyvMzTX/X3ain7fAXGnRupDzl9oir"
"jHtN7BZBGZZW9Vkyv2oBhgfnGhJPxrf7RJ9D4e2AABS0iAuHWWWzs0UZpgwlqMwOZ+w4PIymRYPzCB5q"
"9C9JFVUjdihmqLbP8WICC+0eSFmUO+lM4PYiVprOWgfbwTcNqaYdZSKT3fp2pjNuTJzyvEO/t2Dg1TnC"
"wjoq0veEM1YcRx4polaFw/au+FdceT13SuK8ehmSEHPyLB1H2lUAAAAAAAAAAaBfGjYa5md8lEWEol5m"
"ykby0OgcohE0KzMpefR9SiVHFG7sL0r7JrAeot6SRV1x1iWWVBejRscEDQA0gyXKQnrH1P+/cIqNOLFZ"
.
.
.
"zHVfcTfCbDASrlauLF5i9eLUEFv289im/BQqPPGkld7iwBlOA5zZz4ysnRYDv8VytH9F9vLqNgpiWqNO"
"0pgr+4Dl9i4vtxgCYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEEH5ttS9etaTCa18br69R/R"
"M6tCIKjxjEULqgaJJQkCBwJxDR9kAsol/Xymr7cFKgJ+0crArSf9IqQ/WgqEAgEtYmTqgwA0BkOTT4q2"
"YhAygIak+pZZu654kaBYG+9Hag=="
}
- Note: Do not forget to change the field number. The tool uses field number 1 by default.
cat bigString.txt | python3 big_string_chunker.py --stdin --chunk
python3 big_string_chunker.py --file bigString.txt --chunk
cat chunkedString.txt | python3 big_string_chunker.py --stdin --un-chunk
python3 chunkedString.py --file bigString.txt --un-chunk
python3 grpc_scan.py --file main.js
OR
cat main.js | python3 grpc_scan.py --stdin
For saving javascript files, you have to open them in browser and save the file or download it directly.
Do not copy and paste the javascript content.
ProtoBuf Version Support:
- Version 3 [OK]
- Version 2 [Some Features do not work]
pip install -r requirements.txt
python3 grpc_scan.py --help
python3 grpc_scan.py [INPUT]
Input Arguments:
--file file name of js file
--stdin get input from standard input
Help:
--help print help message
python3 grpc_scan.py --file main.js
Found Endpoints:
/grpc.gateway.testing.EchoService/Echo
/grpc.gateway.testing.EchoService/EchoAbort
/grpc.gateway.testing.EchoService/NoOp
/grpc.gateway.testing.EchoService/ServerStreamingEcho
/grpc.gateway.testing.EchoService/ServerStreamingEchoAbort
Found Messages:
grpc.gateway.testing.EchoRequest:
+------------+--------------------+--------------+
| Field Name | Field Type | Field Number |
+============+====================+==============+
| Message | Proto3StringField | 1 |
+------------+--------------------+--------------+
| Name | Proto3StringField | 2 |
+------------+--------------------+--------------+
| Age | Proto3IntField | 3 |
+------------+--------------------+--------------+
| IsAdmin | Proto3BooleanField | 4 |
+------------+--------------------+--------------+
| Weight | Proto3FloatField | 5 |
+------------+--------------------+--------------+
| Test | Proto3StringField | 6 |
+------------+--------------------+--------------+
| Test2 | Proto3StringField | 7 |
+------------+--------------------+--------------+
| Test3 | Proto3StringField | 16 |
+------------+--------------------+--------------+
| Test4 | Proto3StringField | 20 |
+------------+--------------------+--------------+
grpc.gateway.testing.EchoResponse:
+--------------+--------------------+--------------+
| Field Name | Field Type | Field Number |
+==============+====================+==============+
| Message | Proto3StringField | 1 |
+--------------+--------------------+--------------+
| Name | Proto3StringField | 2 |
+--------------+--------------------+--------------+
| Age | Proto3IntField | 3 |
+--------------+--------------------+--------------+
| IsAdmin | Proto3BooleanField | 4 |
+--------------+--------------------+--------------+
| Weight | Proto3FloatField | 5 |
+--------------+--------------------+--------------+
| Test | Proto3StringField | 6 |
+--------------+--------------------+--------------+
| Test2 | Proto3StringField | 7 |
+--------------+--------------------+--------------+
| Test3 | Proto3StringField | 16 |
+--------------+--------------------+--------------+
| Test4 | Proto3StringField | 20 |
+--------------+--------------------+--------------+
| MessageCount | Proto3IntField | 8 |
+--------------+--------------------+--------------+
grpc.gateway.testing.ServerStreamingEchoRequest:
+-----------------+-------------------+--------------+
| Field Name | Field Type | Field Number |
+=================+===================+==============+
| Message | Proto3StringField | 1 |
+-----------------+-------------------+--------------+
| MessageCount | Proto3IntField | 2 |
+-----------------+-------------------+--------------+
| MessageInterval | Proto3IntField | 3 |
+-----------------+-------------------+--------------+
grpc.gateway.testing.ServerStreamingEchoResponse:
+------------+-------------------+--------------+
| Field Name | Field Type | Field Number |
+============+===================+==============+
| Message | Proto3StringField | 1 |
+------------+-------------------+--------------+
grpc.gateway.testing.ClientStreamingEchoRequest:
+------------+-------------------+--------------+
| Field Name | Field Type | Field Number |
+============+===================+==============+
| Message | Proto3StringField | 1 |
+------------+-------------------+--------------+
grpc.gateway.testing.ClientStreamingEchoResponse:
+--------------+----------------+--------------+
| Field Name | Field Type | Field Number |
+==============+================+==============+
| MessageCount | Proto3IntField | 1 |
+--------------+----------------+--------------+
For testing this tool and getting familiar with gRPC-Web, I made a lab for gRPC & gRPC-Web.
This repo uses two other library directly in lib directory to remove some dependencies for new burp extension:










