For part 1 of this series, please click here.
Methods for Bypassing a Filter
There is a number of different attack strings that can be used to bypass a filter and still pass malicious data to the target application. Before looking at them, it's better to first look at the methods of how the filters can detect malicious input data. The filters usually contain the following methods to detect malicious input data:
- blacklists (difficult, because programmers don't know all the rules that shouldn't match)
- whitelists (difficult, because programmers don't know all the rules that should match)
- regular expressions (are quite complex, can't really detect all strings, and the regex themselves can have vulnerabilities)
- reassembly and fragmentation of subsequent packets (needs to be present because otherwise the data can be divided into multiple packets which are then sent one after another with a constant delay, which successfully bypasses the filters)
Methods for Evading Filters
Common tactics that are used to evade filters are listed below:
- Pattern Matching: Usually the packet filters use pattern matching to search the packet for known strings in the packet data. This can be easily circumvented by the techniques listed here:
- Use different HTTP method: (HEAD instead of GET). The HEAD method is identical to the GET method except that the GET method also returns the message body, while the HEAD method does not.
- URL encoding: %xx
- Double slashes: / becomes //
- Double encoding: If packet filter is blocking %27, we can encode it again, so the string doesn't encode the %27 substring anymore. If we double encode %27 we get %2527.
- Stripping: If packet filter is stripping invalid characters, like %27, we can bypass it by using something like %%2727, which would leave exactly %27 after stripping the %27 substring.
- Reverse path traversal: /dir1/dir2/../
- Parameter hiding: %3f becomes ?
- Different whitespaces: %20 (space) becomes %09 (tab)
- Long URLs: GET /<long_random>/../dir/a.cgi
- Windows-style slashes: / becomes \
- Null bytes: GET0. This method effectively ends the string where the NULL byte is encountered. If the string filename is being appended to some file extension (let’s say .aspx) and if we can control the filename, we can effectively input filename.php0, which will end the string and request the filename.php file instead of one ending with .aspx (as it should).
- Different case sensitive letters: 'ab' becomes 'AB'
- Character Encoding: All the characters can be represented by different strings. They can be represented through URL encoding, unicode encoding, HTML encoding, etc. With different encodings we can change certain strings to represent the same encoded string. The new encoded string can then bypass the packet filter, because it doesn’t contain any malicious strings, but is decoded by the target application. In cases where the target application doesn’t decode the encoded string, that string doesn’t represent the string equivalent.
- Session Splicing: We send parts of the request in different packets."GET / HTTP/1.0" is split into multiple packets. This is not the same as IP fragmentation. IP fragmentation happens automatically, because the whole packet can’t be sent over the wire because it’s too big; the routers have to split the packet into multiple parts. Session Splicing is where we intentionally split the payload over multiple packets to evade detection. The IDS/filters can still detect that with: fragment reassembly, session reassembly and sending a RST to reset connection upon detection of such activity.
- Time Splicing: If the IDS/filter doesn’t look at a subsequent number of packets, but rather waits some period of time to reassemble packets that were received in that time, we can evade it by sending multiple packets with time intervals, so the previous packet is already accepted and will not be joined with current packet to do inspection on. We need to send packets with time delays between them.
- Fragmentation**: Fragmentation happens when we split the whole packet into multiple parts on a network layer (not only the application layer). Normally, IP fragmentation happens automatically, because the packets are too big to send them on the wire (normally, the MTU – Maximum Transmission Unit – is only 1500 bytes). But we can also divide the packets into smaller chunks manually; those chunks can then be really small.
- TTL Attacks: Whenever we want to use the TTL attack, we must know where the packet filter and the target node are located in the target network. With that knowledge we can send packets that will only reach the packet filter, resulting in a packet to be dropped (because the TTL value has decreased to 0). But other packets have a big enough TTL value that are normally forwarded to the target node. The problem arises when the packet filter also uses the dropped packets to reconstruct the data stream. The reconstruction process then forms a new data stream that doesn’t contain any malicious data, but because some packets are dropped, only other packets are sent to the target node. Thus, the target node sees only some packets (with big enough TTL), which when reconstructed hold the malicious data.
We also need to keep in mind that if the packet filters can’t effectively protect whatever they are protecting, this is because the packet filters are interpreting the stream of data in some way, but the protected application is interpreting it in another way. Such an example would be web servers that tend to fix malformed requests, and there are modules to correct some URLs to specific documents and paths, such as mod_spelling. So you can try to send "broken" requests hoping that the packet filter will consider it harmless, but the web server will still interpret it correctly. This can be summarized with one sentence: the target application doesn’t see the same as the packet filter.
In the previous section, we presented character encoding, which can be used to encode certain characters or strings to bypass the packet filter. Here we’ll present the basic character encoding methods, which can be used in real-world applications.
With URL encoding, we can encode URL. This is necessary because URL can only be constructed from ASCII characters 0x20 to 0x7e. All other characters have special purposes and we need to encode them if we want to use them directly in the URL as data. Those characters need to be represented with a %xx (space is %20 and also +, but this is an exception; all the other characters correspond to the %xx rule). We need to encode the following characters if we want to use them in URL as data: space % ? & = ; + #.
With unicode encoding, we can encode any kind of data. Unicode encoding can also be used to represent any kind of characters in the world, English as well as Chinese. If a web page is using a packet filter, which filters/blocks certain characters, and if a component accepting input data understands unicode, then we can unicode encode certain characters. Those characters will then be unicode encoded, which would effectively bypass the packet filter. At the end, the target application will receive the unicode encoded input, which will be automatically decoded back into normal form and used in an application.
With HTML encoding, we can encode the problematic characters within the HTML code. We can’t use certain characters as part of the data, that are used to form the HTML code, because that would break the HTML syntax. Instead, the characters need to be HTML encoded. An example of HTML encoded character " is ".
With base64 encoding we can present any kind of binary data. It enables us to use only ASCII characters to represent any hexadecimal data block.
XSS Validation Example in ASP.NET
The ASP.NET Framework contains the XSS Validation feature that was added into the framework on April 2003 and is available in ASP.NET versions 1.1 in 2.0, 3.0, 3.5, 4.0, 4.5. The XSS validation feature checks every request for malicious characters and filters them.
A good example of this feature is when a form field contains an XSS vulnerability. Whenever a user enters his/her name into the input field, his/her name is printed into the web page. But if an attacker enters a string like <SCRIPT>alert("hello from script")</SCRIPT>, then the XSS is possible and should normally happen. But the ASP.NET blocks the attack, because it validates the input string and detects that it contains malicious characters. In such cases the ASP.NET framework might display the following error to us when we try to input malicious data into an input field.
In the picture above, we can see that the ASP.NET displays an error about dangerous invalid input being used in the Test parameter. Some ASP.NET applications don’t check for invalid input values, because the XSS validation is disabled. To enable XSS validation, we must configure the web.config application configuration file to contain the following:
To bypass the above ASP.NET XSS validation, we must enter such an input value that the ASP.NET will not recognize it as malicious, but the underlying application will be able to handle it, and will include it into the HTML code.
XSS Validation Example in IE
This is because the IE uses an XSS filter that can be enabled/disabled if we go into the Internet Options and click on the Security tab. Then we must choose the "Custom Level" button as show in the picture below:
After that we need to scroll down a bit to enable/disable the XSS filter.
At the end of the exploitation, the web browser is just doing what it can to help detect and block the XSS attacks and protect the users. But we can’t rely on anti-XSS filters to block the XSS attacks; we should rely on the developers to write security conscious code that doesn’t include XSS vulnerabilities in the first place.
You can read more about defeating the anti-XSS filters being defeated in the following link: http://soroush.secproject.com/blog/2012/06/browsers-anti-xss-methods-in-asp-classic-have-been-defeated/ .
Bypassing the XSS Validation Filters
But at the end of the day we need to bypass all of the packet filters, anti-XSS validators, etc. The picture below shows a great example of what really happens when we send a malicious request to the vulnerable web application. First, the request is constructed and sent over the Internet to the vulnerable machine, where it is accepted by the application’s web server. The request is then handed to the ASP.NET web framework, which matches the request against its own database of malicious strings. We already described the ASP.NET XSS validation, which is exactly what is happening at this level.
After the ASP.NET validator checks, it can determine if it is malicious or not. We need to construct such a request that won’t be detected by any kind of packet filter, so the ASP.NET XSS validator checks the input value and passes it on to the application, because it didn’t detect anything malicious. In a web application the developer can have a custom filter, which additionally checks the request for anything weird.
In the above example, we included the <script>alert(‘Hi’);</script> into the malicious requests, which was then checked and displayed back to us. But that doesn’t really work nowadays anyhow, because the <script> substring is detected by most of the packet filters as malicious. This article wasn’t meant to provide any new XSS bypassing techniques, but merely to present the whole process of packet filtering in easy to understand tutorial for the reader.
: Rob Ragan, Filter Evasion, Houdini on the Wire. 2007.