Azure's Front Door WAF WTF: IP Restriction Bypass

Table of contents
The Azure Front Door Web Application Firewall (WAF) has an "IP restriction" option that can be bypassed with the inclusion of an HTTP header.
What's worse? This is actually expected behavior.

Background
There are two (2) WAFs available within Azure—Front Door WAF and the Application Gateway WAF. The Front Door WAF is Azure's Global WAF and the Application Gateway WAF is the Regional WAF.

This particular IP restriction bypass only works in the Front Door WAF. Within the Front Door WAF, if you choose to restrict access by IP address, the default choice is a variable called RemoteAddr.

If the 'Match Variable' drop-down is selected, we can see our options: RemoteAddr or SocketAddr.

We have two (2) ambiguously named variables to match against. So, what's the difference?

Well, within the Front Door WAF documentation, Microsoft lays it out plainly: the RemoteAddr variable matches the original client's IP address, and it does this by respecting the X-Forwarded-For HTTP header, if set. The SocketAddr variable, on the other hand, is the IP address that the WAF sees, i.e., the real IP address. This is what any sane person would expect the filtering to take place on.

So, to be clear: the default selection for IP address restrictions within Front Door WAF can be bypassed simply by adding an X-Forwarded-For header with an appropriate IP address. Further, there's no clear indication that one of the two options is terrible.

The RemoteAddr variable also exists as an option within the Geo location matching rules, but it is not the default selection there.

Sure, admins SHOULD carefully read all documentation, but would anybody have guessed that one of the options is not secure in the slightest? It's completely unreliable for securing anything. It shouldn’t even be labeled as an IP restriction, as it gives the illusion of adding security.
While not explicitly noted in the documentation, RemoteAddr will check both the X-Forwarded-For value as well as the actual connecting IP address to see if either matches. This means that if an admin configures the WAF with RemoteAddr and does a test connect, it would allow the connection whether the X-Forwarded-For header were set or not, so long as the actual IP address matched. This could further muddy the waters when realizing the difference between RemoteAddr and SocketAddr and could lead to a dangerous 'sleeper' rule that appears to work as intended but can be bypassed easily.
During my testing, it was possible to brute-force a /16 network space within about 40 minutes and gain access to a site protected by RemoteAddr IP address restrictions. I performed this test using Burp Intruder. This is made easier if the attacker is able to get an email sent from a user in order to get some originating IP address ranges or if the target has CIDRs registered.

You Know What Happens When You Assume…
Things get really confusing if you have ever used the Application Gateway WAF (the other Azure WAF). Within the Application Gateway WAF, the default (and only) variable for IP restriction is RemoteAddr.

However, in the Application Gateway WAF, the RemoteAddr variable refers to the IP address that the WAF sees (equivalent to the SocketAddr variable within the Front Door WAF).
That's right! Microsoft used the same variable name in related products to mean entirely different things!
Service | Variable Name | Description |
Front Door | RemoteAddr | IP address set by X-Forwarded-For or |
Front Door | SocketAddr | IP address that the WAF sees |
Application Gateway | RemoteAddr | IP address that the WAF sees |

It's a Feature, not a Bug
A tale as old as MSRC: this is intended behavior. On the plus side, it had a pretty fast turnaround.
2025.05.14: Submitted to MSRC
2025.05.15: MSRC Reviewing
2025.06.03: MSRC declines to address, as this behavior is intentional

Testing Setup
Alright, let's demo! First, set up a simple site that we will protect with the Front Door WAF and use to demonstrate the bypass. For our test, we'll use a site that says, "Hello world, [name]".

Then, configure Azure Front Door with a route to the test site.

Next, configure a Security Policy and a WAF policy.

Then, configure a custom rule to allow traffic from a specific IP address (in this case, 123.45.67.89) and match on the RemoteAddr variable. This is the only variable susceptible to this bypass; I was unable to identify a bypass that worked when SocketAddr is chosen.

The RemoteAddr variable is likely chosen more often because the differences are not clearly defined on the page, and the RemoteAddr variable is pre-selected when IP address is chosen to filter on.
Finally, set the WAF to Prevention mode.

Verification of Blocking Rule
Now, verify that it is blocking any normal traffic, not originating from 123.45.67.89.

Perfect. Our request is blocked, as expected.
We can confirm the blocking again, with curl:

Here is just the header with curl, showing a 403 response.

Identifying Front Door WAF
A quick note on identification of Front Door WAF vs the Application Gateway WAF. If it is a Front Door WAF, you should see both x-azure-ref and x-cache headers present.

If the target is using the Application Gateway WAF, there is no positive indicator that I could find. Your indicators in that case would be the Microsoft IP address and the lack of the headers noted above.
Bypassing IP Restriction
By simply adding X-Forwarded-For: [IP address], you can bypass this restriction.

That's it. Simple.
Again, this does not work if the rules are configured to use the SocketAddr variable, but the RemoteAddr variable is pre-selected when creating a new IP restriction and is likely chosen out of ignorance between the options. After all, in the Application Gateway, RemoteAddr is totally safe.

BONUS POWERUP!
Remember, when a Custom Rule is matched, no other WAF rules are checked. The EXTENSIVE list of OWASP attack detections is skipped entirely. There are no secondary checks, it's all or none. So, if you ARE able to use this to gain access to a dev site or something that the admins assumed was protected, you may be able to scan away with impunity.

Detection
I have put together a simple PowerShell script that you can run from Azure Cloud Shell. Simply go to "Manage files" and upload the script: https://github.com/nyxgeek/frontdoor_waf_wtf/.
Alternatively, you can curl the file with:
curl -O
https://raw.githubusercontent.com/nyxgeek/frontdoor_waf_wtf/refs/heads/main/azure_frontdoor_waf_wtf.ps1
Then run the script:
./azure_frontdoor_waf_wtf.ps1

This tool will report on whether any of the Front Door WAF rules use the insecure RemoteAddr variable.
This RemoteAddr WAF check has also been made into a module in GraphRunner (https://github.com/dafthack/GraphRunner). You will need to perform an extra step to check for this, because the GraphRunner tokens are Graph tokens by default.
After importing the GraphRunner module, get a token for the Management endpoint, using:
Get-GraphTokens -Resource 'https://management.azure.com/'

Next, run:
Check-FrontDoorWAF -Tokens $tokens

Note: After running this module, you'll need to run Get-GraphTokens again to get a normal Graph token again.
Remediation
Luckily, the fix is simple enough: simply update your Front Door WAF configuration to use SocketAddr. However, if you are actually using the functionality of the RemoteAddr and need to be able to differentiate between people on the other side of a proxy, it's easy enough to add a second "If" condition that requires the SocketAddr to match the IP address of your proxy server.
The following screenshot shows an example of a rule that will allow specific IP addresses from behind the proxy (e.g., X-Forwarded-For: 123.45.67.89) while still requiring that the actual IP address is your proxy address (e.g., 1.2.3.4)

Conclusion
Where to begin?
- The RemoteAddr option in Front Door WAF shouldn't be listed under IP restrictions because it's really just matching a header value. If Microsoft wanted to include a pre-built X-Forwarded-For match option, they should have labeled it that. Microsoft already gives the option to match a string in a header value, so what value does this RemoteAddr IP matching 'feature' add? Who's idea was it pretend HTTP header matching is an option for IP or region restrictions?
- The variable names should be consistent between products (RemoteAddr vs SocketAddr). What is safe and secure in one version of the product should not be the opposite in another. Why would they do that? Perhaps each WAF team was operating in a complete silo, or perhaps the Front Door WAF developers had never actually used the Application Gateway WAF.
- The huge security hole that is opened with RemoteAddr should be more clearly documented in the blade where you create the rule. If you're going to offer a faux IP restriction, it should have giant flashing letters saying that it's not to be trusted on its own.
It's silly to perform WAF filtering on a user-controlled-header and especially silly to call it an IP restriction. People will get the wrong idea that they're actually protected if they select the default option without digging deeper. This is especially true if they have previously used the Application Gateway WAF, where the RemoteAddr variable actually is secure.

This is like selling a front door that doesn't actually lock, but it looks as though it does. It will appear to work until somebody actually tries to break in. If an admin wants to filter solely on a user-supplied header, let them build their own string match against the header values, but don't offer it as a type of IP restriction, because it certainly does a poor job at that.
If you use Azure Front Door WAF, please make sure you are matching on the SocketAddr variable!
Special thanks to Aaron James @TrustedSec for his feedback.
Reference
Configure an IP restriction rule with a WAF for Azure Front Door