Rapid succession of POST request to forms
Rapid succession of GET requests to * most * pages
Lack of a CSRF token
* Bad * characters
Testing the security of an app in its production deployment is important. While this app might have been assessed in development or QA, when you deploy live you might introduce new attack vectors due to configuration issues. Such was the case with our fictional customer Heisenberg Bank.
While firing up the assessment I quickly ran into issues fuzzing all the application points I usually go after first, see below (screenshot approximated):
that’s both humorous and unsatisfying.
After a bit of investigating I knew that I was up against a WAF that was triggering on a few things:
After one of these conditions were met it would block me with said error code for 5 minutes.
So, how to proceed?
The normal method of encoding payloads to bypass WAF regexes is hit or miss these days. WAF’s have come a long ways. Still, I gave it a shot, no dice.
While waiting in one of my *timeouts* I decided to do some WAF research. While going through several WAF implementation guides I found a forum that mentioned integrating a WAF with your caching service/device. It described a user’s trouble with standing up something *like* Varnish or a proxy/accelerator appliance running on a different host, and that the WAF was blocking that server. Of course the vendor promptly replied that you can whitelist devices based on IP, allowing them not be inspected by the WAF.
At this point, everything is still fine. Below is where things went bad for Heisenberg Bank and the WAF.
After reading more, I found that instead of doing a real lookup on incoming requests (something akin to REMOTE_ADDR or something similar), the WAF was looking at a *custom* HTTP header.
This is how it’s supposed to work if a user or other server contacts the WAF:
Instead, the WAF checks the requests HTTP Headers. The specific implementation was checking the request for the header X-originating-IP.
So, who would this WAF be configured to trust? In this instance the default was… Itself!
Since I control all HTTP requests sent out of my browser I can easily add this header fooling the WAF to think I was itself, allowing me to bypass its protections completely:
After further research there are several headers that can be defined for WAF’s to whitelist (instead of doing a proper lookup):
- X-forwarded-for
- X-remote-IP
- X-originating-IP
- x-remote-addr
There is also a hit-list of *types* of addresses/configurations that *might* be whitelisted/vulnerable. (some fictitious examples below):
After figuring out the bypass, the rest of the assessment yielded many other vulnerabilities and was expedited due to the fact that I could bypass the WAF. It was as simple as having my inline interception proxy add the header to all requests.
A simple solution is having your frontend proxy strip all *non-standard* headers but then you’re still playing the cat and mouse game of blacklisting. Better yet, consult with your WAF vendor and see what headers are accepted and defaults are enabled. Then find a solution that doesn’t rely on information that the attacker can forge.
In general you can also audit the security of HTTP headers on your site using Gethead, a project from our dynamic testing team’s leader, Nathan LaFollette (@httphacker).