This is a tutorial detailing various non-conventional methods of circumventing signature based WAF or IDS software. Rather than focusing on signature evasion, and bypassing of blacklisted characters or keywords, this will instead be focusing on techniques that can allow an attacker to completely prevent the checks in place within the WAF from being applied at all. Using these techniques means that an attacker no longer has to focus on crafting a payload that slips through any signature-based checks in place, but instead they can sidestep the entire bypass process altogether, rendering the WAF ineffective.
Although it is certainly useful to have an understanding of the methodologies used for crafting payloads that circumvent WAF implementations, there are some simple methods that can be used to skip this process entirely. I will give an overview of each of these techniques, and explain how they can be used individually or in combination to skip the filter evasion process.
Initiating connection via direct IP access:
This is a well-known and well-documented method for bypassing cloud-based WAF solutions, but I will be covering it anyway (along with some variations of it) — Cloud-based WAF solutions are tied to the DNS of the website, meaning that if an attacker loads the URL via its direct IP address, the entire process of having to find a payload which bypasses their signature checks is skipped. You would just access the vulnerable URL containing your payload, but with the direct IP address of the server in place of the DNS.
For example, imagine there is an RCE bug affecting host.com:
By getting the direct IP of the website, you could simply navigate to the same URL, except this time replacing the hostname with the direct IP address. If the website is using a CDN with their own custom nameservers, then you’ll need to find a method to leak the direct IP in use. Take Cloudflare for example — sure, you could go with the traditional route of signature evasion to attempt to find a bypass for their WAF… or, alternatively, you could spend considerably less time finding a method to grab the backend IP address from behind cloudflare, then once again it’d merely be a case of switching the DNS to the direct IP, resulting in cloudflare’s protections being circumvented, like so:
protip: rather than entering the IP address in place of the URL each time you make a request, you can just make an edit to your hosts file (/etc/hosts on Linux) in order to speed up the process.
If you can find the backend IP address through means of a simple Ping request or something along those lines, then you’re lucky. Generally, more complex techniques will be required in order to reveal the IP. This is because with CDN’s and DDoS protection solutions that also offer WAF protection (CloudFlare being a prime example) will display the IP address of their servers as the ping response, rather than revealing the true IP address of the website. It’s worth noting that if we’re discussing CloudFlare specifically, websites already exist (such as CrimeFlare) that allow people to query the DNS against a list of backend IP addresses associated with such websites, if you’re lucky then the correct IP will already be stored in one of these databases and viewable through these free services. Below I will explain a few different methods that can be used to leak the correct backend IP address of websites using cloud-based protections like this.
Some basic recon is probably the best way to go about revealing the backend IP of a website. Historic records can reveal the backend IP, so try inputting the URL to services such as Netcraft. Misconfigurations are also often common in services like this, so that’s always worth checking for. Sure, their main DNS is routed through CloudFlare and you can’t get the backend IP, but what about subdomains? There’s a chance that some of these subdomains are pointed to the same server as the main website, and maybe someone forgot to route them through CloudFlare’s nameservers. Similar checks should be done for FTP servers, MX servers, and so on. It is also worth checking the likes of Shodan and Censys to see if any records expose the origin server of the domain.
If all reconnaissance techniques have been exhausted and the backend IP address is yet to be revealed, then you need to figure out a way to make the target server initiate a connection to a page on a server of your own, causing the IP address to end up in your access_log — there are a few different viable ways of doing this. mod_rewrite can be used with .htaccess and some of the tricks described in my “utilizing htaccess for exploitation purposes” blog post can be applied in order to manipulate the server into revealing its real IP.
Ideally, you want to be finding some way of making the server initiate a direct connection to you. This could be through means of an SSRF Vulnerability (y’know, like those SSRF with “no impact” that allow you only to make the request and almost nothing else) or it could be intended functionality of the site that will cause a direct connection to be made. For example, if the site is a forum and users have a profile with a customizable avatar image which can be changed to a new image which is submitted via a remote URL, then you could host an image on a server belonging to you, request that image from the target website and grep your access_log for the IP. Another similar technique would be if the website had an email newsletter or something similar. You could subscribe to it, then check the mail headers when you receive an email from them in order to get the IP address associated with their mail server.
Switching up protocols:
In addition to changing the IP address, techniques such as loading the page over https when it loads through http by default (or vice versa)- sounds ridiculous, but this will prevent some WAF’s from applying their blocks. Try accessing things over a different available port, and specifying the port within the URL, for example:
You can also add or remove ‘www’ to or from the URL and this can achieve the same result (this particular method allowed me to get a working XSS on eBay’s main domain) — or if the target has mirrors setup for server load balancing (like when you see ‘www2’, ‘www3’, and so on), then load one of the mirrors (e.g. change ‘www’ to ‘www2’) as sometimes the WAF will not be applied to these.
another semi-related and often overlooked method to bypass a WAF is to send your request over IPv6 rather than IPv4 — there are many Intrusion Detection Systems in place which also only monitor IPv4 traffic whereas any IPv6 traffic can slip through undetected.
Manipulation of HTTP headers:
There has been cases where certain HTTP headers can be manipulated in order to trick a WAF into believing it is talking to either:
- A whitelisted host
- A machine on the local network
A user can set the value of specific headers to localhost (127.0.0.1), local addresses (for example 192.168.1.2), or the remote IP address of the target site. The following headers can be manipulated in this manner:
So for example, if a HTTP request was sent with the following header value:
Then, in some cases, the WAF would be tricked into thinking it was communicating with itself, and would therefore treat the HTTP request as if it were whitelisted. I first saw this method documented by Jason Haddix, although I’m not certain as to whether he was responsible for finding it.
Manipulation of the content-type header with each sent request can also result in a bypass. This can be achieved in a number of ways. This value can be stripped from requests, malformed through means of modified characters, or spoofed to another content type, for example, take the following content-type:
- Content-Type: text/html
Here are a few examples of how it could be modified:
- Content-Type: (Removing content-type value)
- Content-Type: text/htmlzzzzzzzzz (Malforming content-type value)
- Content-Type: application/octet-stream (Modifying content-type value)
There has also been cases where sending a different valid content-type with each HTTP request can result in a bypass, like so:
- Content-Type: application/octet-stream (request #1)
- Content-Type: text/html (request #2)
- Content-Type: text/xml (request #3)
I’m not sure why this one works exactly, but sending a varying content type alongside each HTTP request has confused WAF’s for me in the past and got the exact end-goal that I was looking for.
Setting the MIME type to multipart/form data then malforming the request has resulted in bypass for many commercial WAF in the past, including mod_security, this can be done like so (these examples are from Ivan Ristic’s Blackhat USA talk):
- Content-Type: multipart/form-data ; boundary=0000
- Content-Type: mUltiPart/ForM-dATa; boundary=0000
- Content-Type: multipart/form-datax; boundary=0000
- Content-Type: multipart/form-data, boundary=0000
- Content-Type: multipart/form-data boundary=0000
- Content-Type: multipart/whatever; boundary=0000
- Content-Type: multipart/; boundary=0000
The HTTP Host Header can also be manipulated in some cases to attempt to circumvent WAF software. Sometimes simply adding a dot to the host value can result in a bypass. Removing or modifying the host header can result in a bypass — sometimes the WAF will choose which security policies to implement based upon the hostname. If the hostname is not recognized (due to manipulation from the user), then in some cases the WAF will not be applied at all. Some WAF’s will stop monitoring requests (for performance reasons) after being tricked into believing that the requests aren’t intended for that site due to discrepancies in the host header, but then further requests can be submitted using the correct hostname without being monitored whatsoever.
Manipulation of parameter names:
Parameter names can be manipulated in various ways, depending upon the server-sided languages running on the vulnerable server. In this post I will be giving examples of both PHP and ASP, followed by an explanation as to why manipulation of parameter names within a URL can result in a valid bypass.
This is especially useful for bypassing virtual patching, for example similar to that which can be often applied to mod_security with a hardcoded path as part of the ‘patch’ — ability to manipulate this hardcoded value can result in a bypass. The + symbol in PHP can be used to achieve this, whereas in ASP the % symbol will achieve a similar result.
Within ASP, invalid URL encoding can be added to parameter names and it will “clean up” for you and remove any invalid encoding. Note that the encoding does have to be invalid for this to work:
In PHP, the plus sign can be used to achieve the same effect:
Here, PHP will clean up in a similar sense to that of ASP. The purpose here is to create what is still a valid path and parameter name, while not matching the path hardcoded within the virtual patch. These techniques can be combined with the path manipulation section listed further on in this blog.
CR/LF, Null terminators, and other control chars:
Certain control characters can cause WAF’s to act in unexpected ways, in some cases a mere nullbyte (%00) is enough to stop them from working entirely — although it should be noted that any WAF worth its salt will not only remain unaffected by an injected nullbyte or null terminator, but will also have signatures in place to cause the input of such characters to result in a WAF response. That being said, some control characters can often yield fruitful results.
To see how the server handles nullbytes, try adding one before the extension to a file that exists on the server, for example, change:
In addition to nullbytes, the percent-encoded equivalents for CR/LF and other similar control characters can trigger unexpected results regarding some WAF software. Just append them to the end of your payload, I’ve seen it working with the following characters:
- %0d (CR)
- %0a (LF)
- %0d%0a (CRLF)
Note that not only will the methods in this section fail to work with any modern WAF worth it’s salt, but any modern WAF will also likely block them. That being said, I’ve came across plenty custom WAF’s where these methods do indeed work, not only not being caught via their signatures, but also resulting in the WAF preventing additional signature checks from being applied.
HTTP Parameter Pollution:#
I’m assuming those reading this already have an understanding of HTTP parameter pollution, but for those who aren’t, I will offer a quick explanation.
HTTP parameter pollution is the name used to describe the unexpected behaviour that can result from the same HTTP parameter being supplied multiple times within an application. This can refer to a multitude of security vulnerabilities, ranging from the ability to make modifications to internal variable values, to full-blown authentication bypass. The severity of HTTP parameter pollution is entirely dependant on the unique nature of the application and the actions that are generated as a result of the pollution of HTTP parameters.
There are a few primary ways in which HTTP parameter pollution can be used to circumvent a WAF. The first way, is just simply including the payload twice (once in each parameter), like so:
http://example.com/vuln.php?page=cat /etc/passswd&page=cat /etc/passwd
A variation of this would be to include the parameter twice, but only contain the payload within one of the parameters while having no value assigned to the other:
The reasoning here is that it attempts to validate the empty ‘page’ parameter while allowing the other identically named parameter to slip through without detection.
One effective bypass technique involving HTTP parameter pollution is to split your payload into multiple parts (if permitted), here is an example:
HTTP Verb Tampering:
Remember that your request does not necessarily need to be sent with a valid HTTP verb. Malformed verbs can work fine, for example, the request could be sent via ‘GETS’ or ‘LOLOL’ rather than ‘GET’ and would still run as expected in many cases. People expect using malformed HTTP verbs to result in an error, but this is not what happens in many cases.
Using uncommon HTTP verbs is also a possibility, especially in cases where the WAF or IDS is only monitoring POST or GET requests. For example, if your malicious GET request is triggering a WAF response, then try sending that same request via HEAD or PUT wherever possible. For example, if you are trying to initiate a GET request by loading the vulnerable site in your address bar with your payload stored in URL-based GET parameters, then try something like the following instead:
PUT /path/vuln.php?value=PAYLOAD HTTP/1.1
If the WAF is being applied to your GET request, try sending it via POST or vice-versa. For example, if you had a vulnerable URL parameter (GET) that was triggering the WAF, try sending a POST request too (alongside the URL to the vulnerable script, but containing no GET parameters in the URL)
Within PHP specifically, depending on configuration, cookie values can be treated as parameters. So for example, if you had the following vulnerability:
Assume the WAF was blacklisting your GET requests that you were supplying as part of the URL. You could call the parameter through a cookie value, by setting a cookie like so:
Cookie: cmd1=;cat /etc/passwd
HTTP Request Smuggling:
HTTP request smuggling, as the aptly-named terminology would suggest, is a method of smuggling HTTP requests which can result in detection being avoided.
This attack vector is achieved by an attacker modifying a HTTP request as it is on its route to its destination. Such a vulnerability exists as a direct result of improper handling of deformed inbound HTTP requests. I will not be going into detail here (although you will find a detailed explanation on our upcoming wiki) — this would require a blog post of its own to explain it in detail along with the possible outcomes (web cache poisoning, XSS, session fixation, and more). The reason it’s being mentioned here is because it allows an attacker to ‘smuggle’ a request to one device without the other device being aware of it. Meaning if properly crafted, an attacker can ‘smuggle’ a request to the target server while slipping through the WAF or IDS undetected. I would definitely suggest researching this technique and its applications for WAF/IDS evasion. Despite me not going into detail (due to this needing a blog post of its own), a simple Google search alongside a primitive understanding of how HTTP works as a protocol should explain to you why this is so useful for bypassing WAF or IDS software. For the readers of this guide, your homework is to read up on HRS (HTTP Request Smuggling) and attempt to pull off a real-world attack that allows you to bypass a firewall (legally, of course!)
Defeating Virtual Patching:
Some poorly-written virtual patches will rely on path data in order to employ their patch. Paths can be manipulated in a manner that they won’t match the path specified by the virtual patch, but will still be interpreted as that same path.
For example, take the following mod_security rule for Apache. This is a generic virtual patch which has been applied:
SecRule REQUEST_FILENAME “@streq /path/vuln.php” \
SecRule ARGS:user “!^[a-zA-Z0–9]+$”
There are a number of ways in which this path could be accessed in a manner that still loads the intended path, while bypassing the virtual patch in mod_security, for example:
- /path//vuln.php (additional slashes)
- /////////////////path/////////////////vuln.php?value=PAYLOAD (more slashes -this is fine in PHP)
- /path/blah/../vuln.php?value=PAYLOAD (traversing back a directory + adding arbitrary non-existent directory)
- /path/blah/blah/blah/../../../vuln.php?value=PAYLOAD (same as before but traversing multiple times)
- /PaTh/VULN.PHP?VaLuE=PAYLOAD (variations in case-sensitivity)
There are some language-specific quirks which can be used to circumvent virtual patching. For example, trailing slashes are fine in PHP but that is not necessarily the case in other languages. The example below is working for Apache Tomcat specifically:
- /path;/vuln.php?value=PAYLOAD (adding semi-colon)
- /path/;lol=lol/vuln.php?value=PAYLOAD (adding arbitrary parameter “;lol=lol” and applying to semi-colon)
PATH_INFO (the environment variable set via Apache) can nalso be manipulated in the following fashion:
- /vuln/vuln.php/lolol?value=PAYLOAD (adding arbitrary value to path)
- /path/vuln.php;lol=lol?value=PAYLOAD (adding arbitrary parameter to path)
Just a reminder that these techniques can be used in conjunction with the parameter name manipulation methods described previously within this guide:
This can allow for an extra layer of obfuscation for defeating virtual patching. I’m not going to go in-depth relating to virtual patches here, but feel free the Google the subject or DM any additional questions if necessary. All that you really need to know is that they are sometimes implemented in a fashion that has a hardcoded path which can be bypassed using many of the methods described above.
Splicing or fragmentation of sessions involves splitting your payload into more manageable chunks.
A few techniques already listed in this post attempt to achieve a similar end goal to that of session splicing or session fragmentation. Some examples of this would be, as already mentioned, HTTP Parameter Pollution (wherein the payload is split into chunks and each chunk is ‘injected’ via the same GET parameter), or HTTP request smuggling.
One well-known tool for performing Session Splicing is called Whisker and it will help automate such a process for an attacker. In effect, what this does is split the payload into multiple smaller packets, allowing them to slip through undetected — meaning that in order for the IDS to detect this, it must reassemble the packet stream. Additional evasion techniques can be implemented by sending the packets out of order and having delays between each transmitted packet. There is a lot of technical information behind this technique, and to explain it in full detail would warrant a blog post of its own. Just remember that this is a potential technique, and that there are tools available to automate such a process. I won’t be going into much detail here but look into the Whisker tool if you wish to learn more. Once again, this is bordering on signature-evasion so I will not be going in-depth regarding this topic.
Denial-of-Service and Resource Exhaustion:
Whereas a WAF or Intrustion Prevention System will block malicious requests, an Intrusion Detection System will simply alert an administrator or any malicious requests. This means an attacker can trigger false positives through means of Denial of Service or other similar techniques, in order to confuse or manipulate the System Administrator — for example, if they get an alert for each attempted DoS attack, alongside an alert for RCE attempts, then an attacker with good timing could trigger a bunch of alerts via DoS, while exploiting the RCE. If all goes to plan, the admin will fail to notice the RCE attempt due to human error, assuming all of the alerts were generated as a result of DoS attempts.
Triggering the likes of Resource Exhaustion or Buffer Overflows within the IDS/IPS or WAF is also a viable and somewhat effective technique.
One method in which resource exhaustion can be achieved is if an attacker takes advantage of the fact that signature-based WAF/IDS software uses pattern matching algorithms to check the signature. Some signatures require more computational power than others to get a match, so, with this in mind, an attacker can send a bunch of specially-crafted packets containing attack signatures that will use up the most CPU power. The end goal here is to cause high load on the CPU, filling up the buffer and causing any new packets sent to be ignored by the IDS due to the fact that the buffer is full.
An attacker can also force the IDS to consume all of its RAM, in which case it reverts to virtual memory on the HDD (drastically slower than RAM) which can result in potentially malicious packets being dropped (read: ignored) by the IDS. This achieves a similar end-result to resource exhaustion through means of an algorithmic complexity attack as described above. Memory exhaustion could be achieved by fragmenting a large amount of packets into an even larger amount of smaller packets, or by initiating a very large number of TCP connections all at once (best to do this in conjunction with out-of-order TCP segments being transmitted).
The purpose of attempting resource exhaustion is to make the IDS ignore any incoming malicious packets due to its buffer being filled up or performance problems created as a result of the IDS reverting from RAM to virtual memory.
That’s all for now, although I’ll follow up with a part 2 on this eventually. Meanwhile though, I’m going to be focusing on a guide for defeating WAF/IDS software using more traditional method (e.g. filter evasion / signature evasion)