New Burp Suite Extension: BlazorTrafficProcessor
Pentesting web applications that use Blazor server comes with unique challenges, especially without tooling. In this post, we discuss why such challenges exist and provide a Burp Suite Extension to address them.
Introduction
During a web application assessment, we encountered ASP.NET’s “Blazor” server for the first time. After attempting a few basic test cases, we realized this was not like any app we’ve tested before. For starters, all the messages transmitted by the application included seemingly random binary characters. You can make out some strings within these messages, but most of the data is not easily readable (Figure 1).
Figure 1 – Example Blazor Message
Additionally, any attempts we made to tamper with or replay requests resulted in an error and the connection being renegotiated (Figure 2).
Figure 2 – Error After Replaying Message
These initial observations proved to be major hindrances for testing a Blazor server application: limited readability and the inability to tamper with data.
Blazor Basics
Before we dive into addressing these obstacles, we first need to cover some of the basics. Blazor is a framework that comes with a variety of hosting options: WebAssembly (WASM), Server-Side ASP.NET, and Native Client. For the purposes of this blog post, we’ll focus on server-side Blazor, which integrates with SignalR to send browser events and receive pre-rendered page updates. By default, Blazor server applications communicate via WebSockets, though other transports such as Long Polling over HTTP are available as well. Since the end goal here is a Burp Suite extension, we’ll need to use HTTP as the Burp Suite extender APIs previously had little to no WebSockets support.
Note: Since this project began, Portswigger has released newer versions of the Montoya APIs which offer better extension support for WS. Handling Blazor messages over WS is currently under development for the BlazorTrafficProcessor extension.
Forcing a Blazor application to use Long Polling is possible within the negotiation process. When a web browser connects to a Blazor server, the first request is a version negotiation. This specifies the version and transports that will be used by the application.
An example request would look like:
POST /_blazor/negotiate?negotiateVersion=1 HTTP/1.1
Content-Length: 0
[...]
The response is as follows:
HTTP/1.1 200 OK
Content-Length: 316
Content-Type: application/json
[...]
{"negotiateVersion":1,"connectionId":"******","connectionToken"
:"******","availableTransports":[{"transport":"WebSockets","transferFormats":["Text","Binary"]},{"transport":"ServerSentEvents","transferFormats":["Text"]},{"transport":"LongPolling","transferFormats":["Text","Binary"]}]}
Using a Burp match and replace rule, you can remove the WebSockets transport from the response, forcing the browser to fall back to use Long Polling over HTTP. The BlazorTrafficProcessor extension will automatically downgrade any Blazor connections from WS to Long Polling.
After modifying the response to exclude WebSockets the browser console will show a warning to indicate a WebSocket connection failed and Long Polling will be used instead.
Warning: Failed to connect via WebSockets, using the Long Polling fallback transport. This may be due to a VPN or proxy blocking the connection. To trouble shoot this, visit https://aka.ms/blazor-server-using-fallback-long-polling.
By forcing the application to use Long Polling, all Blazor traffic will now occur over HTTP which makes Blazor data more accessible to prospective Burp Suite extensions. However, we still don’t know how the Blazor messages are formatted.
We know that Blazor applications expect messages to be in a particular format and order, with any deviations resulting in an error. If we turn to the documentation, Microsoft identifies this format as MessagePack and outlines how it can be used in ASP.NET applications.
MessagePack
MessagePack is another serialization format used to package structured data, like JSON, XML, etc. The key difference with MessagePack is that it is binary in nature, meaning specific bytes are used to indicate the types and length of serialized data.
While Blazor server uses MessagePack, the traffic is specifically formatted according to Blazor’s own Hub Protocol specification. Therefore, generic MessagePack parsers like the Burp Suite MessagePack extension available from the BApp Store will not work with Blazor traffic. Take the following BlazorPack message for example.
Figure 3 – Example BlazorPack Message Bytes
If we use the MessagePack extension on this message, the result is just 25 and the rest of the message is ignored. This is because \x00-\x7f represent positive integers in the MessagePack specification. The extension sees the first \x19, converts it to the decimal value of 25, and fails to parse the rest of the message. We’ll need a customized MessagePack parser to properly read these messages.
Blazor messages are formatted according to the Hub Protocol specification.
([Length] [Body])([Length] [Body])...
Length is a variable-size integer representing the size of Body, which is the actual message. A variable-size integer is one that can occupy a varying number of bytes, depending on the integer’s value. For example, a small integer may occupy a single byte whereas a larger integer may occupy up to five bytes. This value is crucial for parsing messages accurately and is one of the reasons that our tampering attempts failed. If you modify a string to be a different length than the original message, you’d need to update the Length variable-size integer as well. Manually performing these calculations for every request you want to tamper with is tedious and inefficient.
For the Body field, there are different types of messages supported by Blazor (i.e., Invocation, StreamInvocation, Ping, etc.). However, while proxying traffic and testing a sample Blazor application, we rarely saw any other types of messages being used other than Invocation. This is where we’ll focus for an example message breakdown.
InvocationMessage Analysis
The predominant message type observed while testing Blazor applications is an InvocationMessage, used to render page updates and submit data.
Taking a look at the specification, we see that there is the following structure:
[1, Headers, InvocationId, Target, [Arguments], [StreamIds]]
- 1 – the message type, InvocationMessage types are 1.
- Headers – a map containing string key-value pairs. During testing of a sample Blazor app, this was observed to only be equal to a null/empty map.
- InvocationId – this can either be NIL to indicate a lack of an invocation ID or a string that holds the value. Again, this was always NIL during testing.
- Target – a string representing the backend function to call.
- Arguments – an array of arguments to pass to that backend function.
- StreamIds – an array of strings representing unique stream identifiers. Again, this was always NIL or non-existent in the messages observed whilst testing.
InvocationMessage Example and Byte Breakdown
The following request body was sent after modifying a text input field to be foobar:
®•À·BeginInvokeDotNetFromJS•¡1À²DispatchEventAsyncÙx[{
"eventHandlerId":4,"eventName":"change","eventFieldInfo":{"componentId":20,"fieldValue":"foobar"}},{"value":"foobar"}]
Now, taking a look at the bytes:
\xae\x01 \x95 \x01 \x80 \xc0 \xb7 BeginInvokeDotNetFromJS \x95 \xa1 1 \xc0 \xb2 DispatchEventAsync \x01 \xd9\x78 [{"eventHandlerId":4,"eventName":"change","eventFieldInfo":{"componentId":20,"fieldValue":"foobar"}},{"value":"foobar"}]
- \xae\x01 – 174 Variable-sized integer corresponding to the Length parameter for the Hub Protocol.
- \x95 – 5 MessagePack array header representing the length of the whole InvocationMessage body (5).
- \x01 – 1 First element in the InvocationMessage array corresponding to the message type (InvocationMessage = 1).
- \x80 – 0 Second element in the InvocationMessage array indicating a map of length 0.
- \xc0 – NIL Third element in the InvocationMessage array corresponding to the InvocationID (NIL)
- \xb7 – 23 MessagePack header corresponding to the length of the fourth element in the InvocationMessage (23 bytes).
- BeginInvokeDotNetFromJS Fourth element in the InvocationMessage array representing the Target value (BeginInvokeDotNetFromJS).
- \x95 – 5 Fifth element in the InvocationMessage array corresponding to MessagePack array header length (5 bytes).
- \xa1 – 1 MessagePack array header representing the size in bytes for the first element in the Arguments array (1 byte).
- 1 Raw string representing the value of the first element in the Arguments array (1).
- \xc0 – NIL Second element in the Arguments array (NIL).
- \xb2 – 18 MessagePack header corresponding to the length in bytes of the third element in the Arguments array (18 bytes).
- DispatchEventAsync Raw string representing the value of the third element in the Arguments array (DispatchEventAsync).
- \x01 – 1 MessagePack integer representing the value of the fourth element in the Arguments array (1).
- \xd9\x78 – 120 MessagePack header corresponding to the length in bytes of the fifth element in the Arguments array (120 bytes).
- [{“eventHandlerId”:4,… MessagePack raw string corresponding to the value of the fifth element in the Arguments array ([{“eventHandlerId”:4,…)
- There are no StreamIds in this message.
Each string in a Blazor message is preceded by a MessagePack string header to indicate the size of the string. This adds another layer of complexity to manually tampering with messages; not only do you have to update the Length variable-size integer, but you’d have to update the size headers for each tampered string as well.
BlazorTrafficProcessor (BTP) Burp Suite Extension
In summary, the two biggest obstacles when it comes to testing Blazor applications are:
- Readability: the serialized messages can be difficult to read by themselves. As shown above, this is due to bytes in MessagePack representing specific values or types, resulting in seemingly random data to the naked eye.
- Tampering Difficulties: because the messages are serialized according to the Hub Protocol specification, tampering with a value without accounting for the respective size bytes will result in a malformed MessagePack stream that the server can’t read. When this happens, the Blazor application will often error out and renegotiate your connection.
While some data may be easily readable (i.e., the JSON array above containing user input), any attempt to modify these values will require modification to the preceding size bytes as well. This brings us to our next obstacle:
The BlazorTrafficProcessor (BTP) Burp Suite extension addresses both of these issues by providing testers with the ability to convert request bodies from BlazorPack to JSON and vice versa. JSON is more common in web applications and is easier to read due to being text-based rather than binary-based. By providing both deserialization and serialization functionality, testers can leverage this extension to capture and deserialize a request, modify the desired parameter(s), then serialize back to BlazorPack and submit the request.
Demo
The initial version of the extension has two primary features. The first of which is a BTP Request/Response tab that appears on every in-scope BlazorPack message with the ability to convert BlazorPack to JSON. The second is a BTP Burp Suite tab that provides a sandbox for users to serialize/deserialize BlazorPack messages at-will. Any requests/responses that contain BlazorPack serialized data will be highlighted in the HTTP History tab as Cyan.
Reading
The BTP tab will appear on every in-scope request and response in your Burp history that is serialized using BlazorPack.
Figure 4 – BTP Request Editor Tab
This tab simply displays the BlazorPack body converted into a JSON array of message objects.
Figure 5 – BTP Deserialization Example
Tampering
Within Blazor server applications, the JSInvokable attribute allows developers to expose DotNet functions to the client-side JavaScript. Take the following snippet for example:
[JSInvokable("CallMe")]
public static void hiddenFunc(String var)
{
Console.WriteLine("Hidden function called!");
Console.WriteLine(var);
}
This is a simple example that just logs some user input to the console, but there are no web pages that allow the user to call this function. Instead, the DotNet.invokeMethodAsync JavaScript function can be used (as outlined here).
Figure 6 – Invoke Hidden Function from Browser Console
After the function has been called from the browser, Burp captured the serialized message that was sent:
Figure 7 – Hidden Function Invocation Request
Sending the above payload to repeater and using the extension to deserialize yields the following:
Figure 8 – Deserialized Invocation Request
The input of foo is contained within a 1-element array, so let’s try tampering and replaying the request. For demo purposes, we’ll change the JSON payload in the BTP tab to include a newline to see if newlines get written to the console output:
[{"Target":"BeginInvokeDotNetFromJS","Headers":0,"Arguments":["3","BlazorServerTestApp","CallMe",0,["Line1\r\nLine2"]],"MessageType":1}]
Clicking on the Raw tab to serialize the data back into MessagePack yields:
Figure 9 – Reserialized Request with Custom Input
Upon sending the request, the response is a simple 200 OK:
HTTP/1.1 200 OK
Content-Length: 0
Connection: close
Content-Type: text/plain
Server: Kestrel
Important note about Blazor server Long Polling: sending any type of invocation POST request will not return data in the response. Instead, Long Polling keeps an open GET request that receives server updates as they become available. As such, if you send a request with malformed input that causes an error, you won’t see that error unless you look at subsequent GET requests.
Checking the console log and we see that the payload did in fact create two distinct lines with our input:
Figure 10 – Application Logs with Inserted Payload
Ad-Hoc Serialization & Deserialization
The BTP extension also includes a new tab added to Burp Suite, aptly named BTP. While you can copy and paste data into this tab, it is recommended to use the “Send body to BTP” right-click menu option (Figure 11) to ensure the whole message is copied appropriately. This feature applies to both requests and responses.
Figure 11 – Send to BTP Menu Item
The message will then appear in the BTP tab as shown below:
Figure 12 – BTP Tab Deserialization Example
This tab can be used for ad-hoc Blazor conversions with the following steps:
- Select the conversion type (JSON->Blazor or Blazor-JSON)
- Enter the message in the left-hand editor
- Click Serialize/Deserialize Button
- Results are shown in the right-hand editor
What are these “Target” values I keep seeing?
While testing Blazor server applications, you’re likely to run into various Target values such as: BeginInvokeDotNetFromJS, OnRenderCompleted, OnLocationChanged, etc. These functions themselves are not specific to the app that you’re testing. Rather, they are built-in to ASP.NET Core as part of ComponentHub and are used to facilitate communications between the frontend JavaScript and the backend .NET application. For example, OnLocationChanged is used to navigate between pages in the web application. The implementation of these functions can be found in the “aspnetcore” repository on GitHub here.
It is important to distinguish between what’s native and what’s custom in Blazor server applications, since your goal will likely be to find vulnerabilities in the app that you’re testing, not in Blazor itself. As such, focus your testing efforts on fields that contain your input (i.e., arguments to BeginInvokeDotNetFromJS) as opposed to normal Blazor operations (i.e., OnRenderCompleted or EndInvokeJSFromDotNet). With that said, security researchers can also utilize the BTP extension to test Blazor server itself since tampering inputs is now easier.
The following screenshot (Figure 13) displays a deserialized request that updates the value of a text box in the application to be asdf. The highlighted values are potential areas for tampering. The rest of the arguments are native to Blazor and tampering may cause a Blazor error, which would renegotiate your connection.
Figure 13 – Tamperable Values vs. Native Values
Consider a case where only client-side JavaScript is used to validate this input field. We could intercept the request, deserialize the message into JSON, enter a value that would otherwise fail validation, reserialize, and fire the request to bypass client-side input validation.
Further Research
Fuzzing
While this extension achieves the base proof-of-concept for a tool to process Blazor server traffic, a feature we believe would be valuable to testers is the ability to fuzz serialized inputs (similar to a Burp Suite intruder attack). However, application data is never returned in response to a POST request when using Long Polling. Instead, the client-side JavaScript polls the server with a GET request and passes an epoch timestamp. The response to these polling requests will contain app data and render updates, making it difficult to deduce which request caused an error or a specific response. For example, if you send a POST request that causes an error, the error would be returned to the next GET (“poll”) request. Sending multiple error-inducing requests could cause the application to return several errors in one response body or spread out across subsequent polling responses. With no clear way to map one input to one response/error, we decided it best to leave out fuzzing for the first iteration of this tool. We are still looking for effective ways to fuzz Blazor applications and a fuzzing feature is on the roadmap for the BlazorTrafficProcessor extension.
WebSockets
BTP is fully dependent on the ability to downgrade a Blazor server connection from WebSockets to Long Polling over HTTP. ASP.NET developers have considered removing Long Polling support altogether, which could render BTP unusable. Ultimately Long Polling was left in the framework due to “unforeseen effects” of removing it, though it remains possible that support will be discontinued in the future.
For BTP to be future proof, it will need WebSockets support eventually. However, Blazor messages can be distributed across different WebSocket frames with a max size of 4096 bytes per frame. In other words, a Blazor message could start in one WebSocket frame and end in another. With limited support for WebSockets in Burp Suite’s Montoya APIs, it was not plausible to include Blazor WebSocket parsing for the initial version of BTP. However, this feature is currently under development as there are new iterations of Burp Suite’s Montoya APIs being released frequently.
You can find the latest version of the extension, as well as setup and usage instructions at our GitHub Repository here: https://github.com/AonCyberLabs/BlazorTrafficProcessor
Thank you for reading and happy Blazor hunting!
Explore More
-
Capability Overview
Cyber Resilience
-
Product / Service
Penetration Testing Services
About Cyber Solutions:
Aon’s Cyber Solutions offers holistic cyber risk management, unsurpassed investigative skills, and proprietary technologies to help clients uncover and quantify cyber risks, protect critical assets, and recover from cyber incidents.
General Disclaimer
This material has been prepared for informational purposes only and should not be relied on for any other purpose. You should consult with your own professional advisors or IT specialists before implementing any recommendation or following the guidance provided herein. Further, the information provided and the statements expressed are not intended to address the circumstances of any particular individual or entity. Although we endeavor to provide accurate and timely information and use sources that we consider reliable, there can be no guarantee that such information is accurate as of the date it is received or that it will continue to be accurate in the future.
Terms of Use
The contents herein may not be reproduced, reused, reprinted or redistributed without the expressed written consent of Aon, unless otherwise authorized by Aon. To use information contained herein, please write to our team.
Aon's Better Being Podcast
Our Better Being podcast series, hosted by Aon Chief Wellbeing Officer Rachel Fellowes, explores wellbeing strategies and resilience. This season we cover human sustainability, kindness in the workplace, how to measure wellbeing, managing grief and more.
Aon Insights Series UK
Expert Views on Today's Risk Capital and Human Capital Issues
Construction and Infrastructure
The construction industry is under pressure from interconnected risks and notable macroeconomic developments. Learn how your organization can benefit from construction insurance and risk management.
Cyber Labs
Stay in the loop on today's most pressing cyber security matters.
Cyber Resilience
Our Cyber Resilience collection gives you access to Aon’s latest insights on the evolving landscape of cyber threats and risk mitigation measures. Reach out to our experts to discuss how to make the right decisions to strengthen your organization’s cyber resilience.
Employee Wellbeing
Our Employee Wellbeing collection gives you access to the latest insights from Aon's human capital team. You can also reach out to the team at any time for assistance with your employee wellbeing needs.
Environmental, Social and Governance Insights
Explore Aon's latest environmental social and governance (ESG) insights.
Q4 2023 Global Insurance Market Insights
Our Global Insurance Market Insights highlight insurance market trends across pricing, capacity, underwriting, limits, deductibles and coverages.
Regional Results
How do the top risks on business leaders’ minds differ by region and how can these risks be mitigated? Explore the regional results to learn more.
Human Capital Analytics
Our Human Capital Analytics collection gives you access to the latest insights from Aon's human capital team. Contact us to learn how Aon’s analytics capabilities helps organizations make better workforce decisions.
Insights for HR
Explore our hand-picked insights for human resources professionals.
Workforce
Our Workforce Collection provides access to the latest insights from Aon’s Human Capital team on topics ranging from health and benefits, retirement and talent practices. You can reach out to our team at any time to learn how we can help address emerging workforce challenges.
Mergers and Acquisitions
Our Mergers and Acquisitions (M&A) collection gives you access to the latest insights from Aon's thought leaders to help dealmakers make better decisions. Explore our latest insights and reach out to the team at any time for assistance with transaction challenges and opportunities.
Navigating Volatility
How do businesses navigate their way through new forms of volatility and make decisions that protect and grow their organizations?
Parametric Insurance
Our Parametric Insurance Collection provides ways your organization can benefit from this simple, straightforward and fast-paying risk transfer solution. Reach out to learn how we can help you make better decisions to manage your catastrophe exposures and near-term volatility.
Pay Transparency and Equity
Our Pay Transparency and Equity collection gives you access to the latest insights from Aon's human capital team on topics ranging from pay equity to diversity, equity and inclusion. Contact us to learn how we can help your organization address these issues.
Property Risk Management
Forecasters are predicting an extremely active 2024 Atlantic hurricane season. Take measures to build resilience to mitigate risk for hurricane-prone properties.
Technology
Our Technology Collection provides access to the latest insights from Aon's thought leaders on navigating the evolving risks and opportunities of technology. Reach out to the team to learn how we can help you use technology to make better decisions for the future.
Top 10 Global Risks
Trade, technology, weather and workforce stability are the central forces in today’s risk landscape.
Trade
Our Trade Collection gives you access to the latest insights from Aon's thought leaders on navigating the evolving risks and opportunities for international business. Reach out to our team to understand how to make better decisions around macro trends and why they matter to businesses.
Weather
With a changing climate, organizations in all sectors will need to protect their people and physical assets, reduce their carbon footprint, and invest in new solutions to thrive. Our Weather Collection provides you with critical insights to be prepared.
Workforce Resilience
Our Workforce Resilience collection gives you access to the latest insights from Aon's Human Capital team. You can reach out to the team at any time for questions about how we can assess gaps and help build a more resilience workforce.
More Like This
-
Cyber Labs 3 mins
Responding to the CrowdStrike Outage: Implications for Cyber and Technology Professionals
This client alert provides an overview of the current global IT outage that is related to a CrowdStrike update. We provide an overview of CrowdStrike's response and guidance, and Aon Cyber Solutions' recommendations for affected clients.
-
Cyber Labs 8 mins
A SIMple Attack: A Look Into Recent SIM Swap Attack Trends
Stroz Friedberg Digital Forensics and Incident Response has observed an uptick in SIM swapping across multiple industries, with several recent incidents targeting crypto and crypto-adjacent companies.