This is a fork of a burp extension called Blazor Traffic Processor made by Aon plc. This fork was created to add support to WebSocket messages. Originally, this extension only supported BlazorPack requests through HTTP by forcing the application to use Long Pooling. Although this method might work for some applications, it causes issues for the majority of BlazorPack applications. Thus, I have made changes to the original source code to add support for WebSockets messages.
A BurpSuite extension to aid pentesting web applications that use Blazor Server/BlazorPack. Primary functionality includes converting BlazorPack messages to JSON and vice versa, introduces tamperability for BlazorPack serialized messages.
- Install Java 18 on your building machine.
- Install Gradle on your building machine.
- Ensure the
JAVA_HOME
environment variable is set to the JDK 18 path if you have multiple versions of Java installed.- NOTE: This project requires Java 17+.
- Clone the repository with
git clone https://github.com/AonCyberLabs/BlazorTrafficProcessor
cd BlazorTrafficProcessor
gradle build
- The built JAR file will be located at
BlazorTrafficProcessor/build/libs/BlazorTrafficProcessor-1.0.jar
- Download the latest
.jar
from the Releases page or build the project manually.- The project has been submitted to the BApp store and is pending review.
- Load the extension into Burp
- Click "Extender"
- Under "Extensions", click "Add"
- In the file selector, choose the downloaded/built
.jar
file
NOTE: it is recommended to check "Other Binary" in your Burp History filter, this will allow you to see data returned by the application.
- If you are using Long pooling, then all BlazorPack-enabled requests or responses will be highlighted as Cyan within the "Http History" tab in Burpsuite.
- If you are using WebSockets, then only important BlazorPack functions will be highlighted as Cyan within the "WebSockets history" tab in Burpsuite.
- The "BTP" request/response editor tab, which appears on each in-scope request or response that contains BlazorPack messages.
- Clicking on this tab will convert the serialized data from BlazorPack to JSON.
- After editing the JSON (either in Intercept or Repeater), click the "Raw" tab to re-serialize with your payloads
- The "BTP" Burpsuite tab, which allows for ad-hoc conversions of Blazor->JSON and JSON->Blazor
- The left-hand editor is for your input (JSON or raw Blazor)
- The right-hand editor is for the results of the conversion
- A drop-down menu on the bottom of the window lets you select "Blazor->JSON" or "JSON->Blazor"
- The Serialize/Deserialize button at the top is how you trigger the conversion
- Right-click menu option called "Send body to BTP tab" (Only works for HTTP history at the moment)
- You can right-click any request or response and select "Extensions" -> "BlazorTrafficProcessor" -> "Send body to BTP tab"
- This sends either the selected request or response body to the BTP tab, so you don't have to worry about copying/pasting raw bytes
** Note: When using WebSockets the extension only highlights below functions
- BeginInvokeDotNetFromJS
- ReceiveJSDataChunk
- ReceiveByteArray
- StartCircuit
- ConnectCircuit
At the moment there is no user friendly way to change the default highlight filters, but I might add it in the future as part of the UI for this extension.
You can learn about other BlazorPack functions here: https://github.com/dotnet/aspnetcore/blob/main/src/Components/Server/src/ComponentHub.cs
Blazor server normally communicates via WebSockets, though it supports other protocols such as LongPolling over HTTP. During the connection initiation between your browser and the server, one of the first requests sent will look like the following:
POST /_blazor/negotiate?negotiateVersion=1 HTTP/1.1
Host: localhost:5003
[...]
X-Requested-With: XMLHttpRequest
X-SignalR-User-Agent: Microsoft SignalR/0.0 (0.0.0-DEV_BUILD; Unknown OS; Browser; Unknown Runtime Version)
[...]
The response will contain the available transports as follows:
HTTP/1.1 200 OK
Content-Length: 316
Connection: close
Content-Type: application/json
Date: Thu, 22 Sep 2022 13:30:17 GMT
Server: Kestrel
{"negotiateVersion":1,
"connectionId":"XXX",
"connectionToken":"XXX",
"availableTransports":[
{"transport":"WebSockets","transferFormats":["Text","Binary"]},
{"transport":"ServerSentEvents","transferFormats":["Text"]},
{"transport":"LongPolling","transferFormats":["Text","Binary"]}
]
}
This negotiation determines how the client and server will establish their connection. WebSockets is the preferred method, however, you can force the connection over HTTP if you prefere using burp match and replace feature.
Type: Response body
Match: {"transport":"WebSockets","transferFormats":["Text","Binary"]},
Replace:
** Note: Support for BlazorPack over WS is currently partially supported; if a Blazor message is sent through WebSockets over multiple messages of size 4096 each, then the conversion of serialized BlazorPack messages to JSON won't function. At the moment this is not a priority to implement, as important message types for Blazor are all sent in a single WebSockets message.
Request Body:
º����À·BeginInvokeDotNetFromJS�¡2À²DispatchEventAsync�Ù�[{"eventHandlerId":4,"eventName":"change","eventFieldInfo":{"componentId":27,"fieldValue":"asdfasdfasdf"}},{"value":"asdfasdfasdf"}]
Deserialized:
[
{
"Target":"BeginInvokeDotNetFromJS",
"Headers":0,
"Arguments":[
"2","null","DispatchEventAsync",1, [
{"eventFieldInfo": {"componentId":27,"fieldValue":"asdfasdfasdf"},
"eventHandlerId":4,"eventName":"change"},
{"value":"asdfasdfasdf"}
]
],
"MessageType":1
}
]
Request body:
����À±OnRenderCompleted��À
Deserialized:
[
{
"Target":"OnRenderCompleted",
"Headers":0,
"Arguments":[5,"null"],
"MessageType":1
}
]
Request body (What you'll see in Burp):
+���ÀµEndInvokeJSFromDotNet��Ã[3,true,null]
Request body bytes (What you'll see in the "Inspector" tab if you highlight the request body)
\x2b\x95\x01\x80\xc0\xb5EndInvokeJSFromDotNet\x93\x03\xc3\xad[3,true,null]
[length][MessageType,Headers,InvocationId,Target,[Arguments]]
[\x2b][MessageType=\x01,Headers=\x80,InvocationId=\xc0,Target=\xb5EndInvokeJSFromDotNet,Arguments=[One=\x03,Two=\xc3,Three=\xad[3,true,null]]]
InvocationMessage Encoding Spec
\x2b
- the size byte for this payload, value = 43\x95
- an array header, representing a 5-element array\x01
- integer w/ value of 1, representing the message type (Invocation)\x80
- Map of length 0, representing the headers (only seen empty map while testing)\xc0
- NIL, representing the invocationId is null\xb5
- Raw string header of length 21, representing the "Target"EndInvokeJSFromDotNet
- the "Target" raw string\x93
- an array header, representing a 3-element array for the arguments\x03
- integer w/ value of 3, first argument to the "Target" function\xc3
- boolean w/ value of true, second argument to the "Target" function\xad
- Raw string header of length 13, representing the third argument to the "Target" function[3,true,null]
- the third argument raw string
Deserialized:
[
{
"Target":"EndInvokeJSFromDotNet",
"Headers":0,
"Arguments": [
3,true,
[3,true,null]
],
"MessageType":1
}
]