The Problem with Blacklisting; A Look at Weird Cross Site Scripting Injections

Views: 782

Written By: Ryan Borden January 23, 2017

You implemented a blacklist. That’s good enough, right?

So you’ve been testing the web application you built for Cross Site Scripting vulnerabilities, and you’re pretty sure that your defenses are solid. You’ve checked all of the inputs to make sure that an attacker couldn’t break out of the source code in a way that leads to XSS. You even dusted off your cheat sheet to help you build out a really comprehensive blacklist. Your site is bullet-proof, right? Well, probably not.  

The problem with relying on a blacklist is that you don’t know, and can’t know every possible injection which exists and which will exist in the future. While blacklists are easy to implement and even come bundled with a WAF or framework, they also tend to be easy for attackers to by-pass.  Technologies are always changing, and with those changes come introductions to the HTML standards and to browser parsers which attackers can exploit. As a hacker, one of my favorite things to do is to find a way to by-pass your blacklist. Your blacklist is a challenge to my skills, and I will defeat it. Over the years I have encountered several really good blacklists, and I have defeated the overwhelming majority of them. With those victories has come a list of unusual injections which I have added to my own cheat sheet, and now I am going to share some of my favorites with you.

Really Internet Explorer? Really?!

Let’s start off by picking on everyone’s favorite browser to download other browsers, Internet Explorer. This notoriously insecure browser has been a favorite target for attackers over the last few decades. Poor IE just wants to be user friendly to both programmers and users, but in doing so it has historically opened the floodgates for hackers. We’ll start off by looking at injections which exploit IE’s loose rendering rules.

Injection: <%foo onmouseover=“confirm(1)”>

Wait, that’s not a real tag! Yet Internet Explorer 9 will still treat this as valid HTML and allow the onEvent to execute JavaScript on any subsequent plaintext characters. Now, I am sure you are thinking, “Yeah, but IE 9 is old. We’re on Edge now, so who cares about an old IE injection?” However, you may be surprised to learn that IE 9 is a more popular browser globally than Firefox and Edge. According to the latest data on https://www.netmarketshare.com, IE 9 continues to control a 5.28% global market share of all Desktop browsers.

Beyond that, this injection will execute in any IE 9+ browser as long as the Document Mode is set to 9. I was recently testing an application which was nice enough to set this Document Mode by default, and this is not an uncommon configuration. If an older application was built back in the IE 9 days, programmers are going to be incline to keep the Document Mode in-line with the old standards so that they don’t have to rebuild the whole application.

Injection: <img src="x` `<script>alert(1)</script>"` `>

This injection takes advantage of Internet Explorer 8’s parser by creating an unbalanced attribute delimiter. IE 8 treats any tag as plaintext in cases where the attribute delimiters are unbalanced; in this example caused by the ` `. By injecting backticks into the src attribute the attacker can put their injection in plaintext, and once the attacker has placed themselves into plaintext space it is game over.  

IE only onEvents

I’m not going to spend too much time on this because we all know how onEvents work. Microsoft introduced a short list of onEvents which are exclusive to their IE 10 & 11 browsers. This is great for hackers because these onEvent tend to be lesser known, so a lot of blacklists fail to include them.

IE 10 & 11 onevents

onmspointermove

onmspointerdown

onmspointerup

onmspointerover

onmspointerout

 

IE 11 onevents

onpointermove

onpointerdown

onpointerup

onpointerover

onpointerout

 

Move over IE, here comes Firefox.

Apparently the developers at Mozilla were absent on the day when their teacher went over George Santayana’s famous quote:

“Those who cannot remember the past are condemned to repeat it”.

They are beginning to repeat some of IE’s mistakes. In an attempt to be more developer/user friendly, Firefox has introduced some new elements and attributes which are exploitable and unique to their browser.

Injection: <math href=“javascript:confirm(1)”>XSS</math>

As part of HTML5, the MathML elements have been introduced. These elements were intended for displaying complex mathematic formulas, equations, and notation. Well, Firefox allows for the Math tag to include the href attribute which, as we know, can include any valid URI scheme including JavaScript.

Injection: <input type="hidden" vulnerable-parameter=”XSS” accesskey="x" onclick="alert(1)”>

This is one of my new favorites because it helps a hacker get a relevant injection into those troublesome hidden inputs. I’m sure we all know the tired style expression injection which only executes in IE 7, and has been used to death for hidden inputs because it was the only thing that worked. Well Firefox has changed the rules of the game and given us a fresh new way to inject malicious payloads into hidden input fields with the accesskey attribute. The accesskey attribute allows for onEvents in a hidden input to be triggered when a user presses a specific key combination. In Windows environments the key combination is alt + shift + the specified access key (in this case “x”). In OS X environments the key combination is ctrl + alt + the specified access key. Yes, this will require a little more social engineering in order to get a victim to select the specified key combination, but someone will fall for it and an attacker will get their victim.

Injection: <script>confirm`1`</script>

This is not really a Firefox issue, but rather it is a security hole which has been opened up by the 6th Edition of the ECMAScript standard (ES6 for short). This is a series of new features which have been added to the JavaScript library, and are now being supported by both Firefox and Chrome. The main issue here is that the backtick character is now able to be used in JavaScript to delimit string literals. The implication of this is that any blacklists which filter out parenthesis in order to prevent XSS attacks have been rendered useless.

Hack on all the browsers!

These next few injections are not limited to a specific browser, and help to further highlight why a blacklist is insufficient.

Injection: <marquee behavior=“alternate” onbounce=“confirm(1)”>XSS</marquee>

Break out your flannel shirts and that copy of “Jagged Little Pill” because we’re going back to the 90’s for this one! That’s right, this injection is using a Marquee tag. Yes, it does still work in the modern world, and its associated onEvents still work with it. The great thing about this injection is that it’s so old almost everyone has forgotten about it.

Injection: data:text/html;base64,PHNjcmlwdD5wcm9tcHQoKTwvc2NyaXB0Pg==

The oft overlooked data scheme URI has allowed me to get past several blacklists. This injection allows for URI space to become whitespace, and then allows for the injection to be further obfuscated by encoding HTML code using Base64. Whether an attacker is landing in a space where they can define a URI (such as a redirect parameter), or they have created the URI space with a tag, this injection will prove useful for exploiting an application.

Injection: !()+[]

I am sure that right now you are asking yourself if I passed out on the keyboard while typing the injection. Don’t worry, I did not. Those characters represent an esoteric Javascript programming style called JSFuck (don’t blame me, I didn’t name it). This programming style allows for code to be executed using only 6 characters, so alert(1) can be converted to the following:

[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+(![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]+[+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]])()

Don’t ask me how this works, because I honestly don’t know. I tried to understand it once and I got a nose bleed from thinking too hard. Lucky for me and all of the other hacks who don’t understand how or why this works, there are helpful websites which will convert traditional JavaScript code into JSFuck code.

All hope is lost! (Just kidding)

So now that you have seen that your blacklist is probably not going to be sufficient what are you supposed to do? No, you cannot just copy my injections into your blacklist and call it a day. Wait! Don’t rm –rf your whole server! There is another answer; a solution which will protect your application and its users from all the bad guys who want to ruin all of your hard work. The best solution is to output encode all user supplied input appropriately based on the context in which the input is being used, and where possible to validate the input against a whitelist of acceptable characters. I know, that solution sounds too simple but that’s really all it takes to prevent 90% of the XSS attacks your application will see (that other 10% is made up of DOM XSS which is a whole different topic). If you ensure that all of the input which comes from the client is properly validated and output encoded then the chances are, XSS payloads will never execute in your application. As a final note I would like to emphasize that ALL inputs need to be validated and checked, not just the ones where you are expecting user supplied data. Parameters which are supplied input via drop-down menus in the UI, hidden input fields, user-agent strings, cookies, and more are all viable attack surfaces to a hacker.

Ryan Borden

Ryan Borden is an Application Security Consultant at AppSec Consulting who specializes in testing the security of applications.  He has extensive expertise in application penetration testing, including the use of manual techniques and automated tools to perform detailed assessments. Ryan’s strong attention to detail, and out-of-the-box thinking has allowed him to identify and report numerous unique vulnerabilities which can only be discovered by a skilled human tester, such as logical issues, filter evasion, DOM based and Stored Cross Site Scripting attacks, and Authorization by-pass attacks.

Ryan earned his undergraduate degree from Keene State College and has 4 years of Application Security experience. Before working in security, he was a professional chef and spent several years honing his culinary skills. In his spare time he enjoys spending time with his family and friends, gaming, and playing guitar.

read more articles by Ryan Borden

© Copyright 2017 AppSec Consulting, All Rights Reserved