MODERATOR: Right now we have a first time DEF CON speaker, Franz Payer, speaking to
you about Exploiting Music Streaming with JavaScript. It's his first time at DEF CON.
Please give him a big round of applause. (Cheers and applause.)
No shots? FRANZ PAYER: Eighteen.
He's 18! (Laughter.)
MODERATOR: I did the same thing. Oh, we're going to give you shots!
He's like no! FRANZ PAYER: All right. I hope you guys
are all awake. So I'm Franz Payer, programmer at Tactical Network Solutions. I'm going to
go over exploiting music streaming with JavaScript. So a couple of acknowledgements before I start.
I would like to thank Zachary Collup and Craig Hefner for all their help and support, and
my employer, Tactical Network Solutions, for letting me to learn about security without
going to jail, which is great. A special thanks to Ronald Jenkies, who is
an independent artist who has given me permission to use his music in this presentation so I
don't get sued by the RIAA. Speaking of which, I want like to thank the EFF for helping me
address issues with the DMCA and the CFAA. However, the decision was made to not release
the original tool which I had planned to. That was a Google Chrome extension that would
showcase all the different exploits and vulnerabilities which I'm going to cover today. But I will
be releasing an alternative tool, which I'll get into later.
I want to state, the views expressed here are mine, not my employer's.
What am I going to be talking about? I'm going to give you background information, what my
project is and what I've done. You have context of like my approach and how I did it. Then
I'll go over the music streaming basics so you understand how it works and implementation
today and what you are going to be seeing. I'm going over my security investigation process
so kind of taking you from the beginning to the end of research to exploitation. And hopefully
by the end you will have a grasp on how you can do this by yourself.
I'll go over an exploit demo, assuming everything works out all right. If there's any time afterwards
I'll talk about my new alternative extension released soon. I'll take questions at the
end. The end goal. Well, originally I planned to
release the Google Chrome extension which will have the exploits which I'm going to
show you. The way it works, it mimics the music player whenever possible. When I was
smart enough to reverse engineer the code and generate the requests the way they did.
Otherwise I would just log whenever I saw some MP3 flying by and look at the syntax
and match that syntax. Every time something matches that syntax flies by, I can give it
that song. The end result you have something in the bag ground. Every time you listen to
a song you can download it. I'm not going to be releasing that. What am I releasing?
It's an alternative, not exactly the same. Forensics tool, not an exploitation tool.
It duplicates requests that it sees flying by and caches it in the RAM. This is helpful
for hex dump analysis afterwards. If you do malware analysis without putting it on your
hard drive you can do it entirely from your browser now.
And this is also helpful if you want to see exactly what is being loaded into your browser.
So here is the wall of shame, a bunch of services which I found vulnerabilities in. Some of
these have made fixes. Some of them haven't. Most of them haven't. So we have Pandora,
Amine, Sound Cloud, Grooveshark, Django, and Eight Tracks, quite a big list. So what is
streaming? Well, which can peed defines it as a way to present details delivered by provider.
So from a developer's point of view this means that you are going to be receiving data in
a really long stream and as soon as you get the first piece you can start processing it
and displaying it to the viewer. From a tactics point of view that means you get use of data
and at the end you have everything to reconstruct the file, a song or whatever.
It is only a matter of capturing the data pieces. Once you do that, there are two technical
major roadblocks from prevent you from playing it back. You have reassembly of the pieces,
typically if you --> like the way the Internet works, when you send data it is not always
received in the same order in which it was sent. This can be more difficult depending
on the type of protocol they use. If you see encryption, that's probably stopping you.
Usually if you want to break encryption, it's not to get music. It's to get someone's password.
That is your major roadblock. The protocols I have seen, typically when
you have some sort of desktop application like Spotify or Pandora 1, they use a custom
TPC protocol. This makes it incredibly difficult to reassemble. A, it's not documented or B,
it's proprietary and you don't know how it works. This probably will stop you dead in
your tracks. However, I noticed that some services like
last FM use http or https because they don't want to write their own custom protocol. and
this is typically what you see in some browser-based applications. So the regular Pandora app,
Sound Cloud, these guys are going to be using http and https. They don't want to do extra
coding. Browser does it for you; why would you want to do it yourself? If you're an attacker
or hacker or whatever, that means you can use the browser, too. Now you don't have to
worry about assembly and de-encryption. That's why I targeted these. They are extremely easy
to go after. And there are two different types of streaming
which I kind of named myself. There's static streaming where you have one URL per song.
So you will have to usually have the file name, know the directory. This is different
from a dynamic one where you have one page and depending on the parameters you send it
you get back a different file. We have a streamed .PHP page and depending on the key you send
it, you get a different file. It's one to one versus one to many. This is important
to keep track of when you're doing analysis. So there are two major types of music players.
Most common one is Flash. This is a majority of the Web players you'll see. However, they
may still use JavaScript. I have seen a lot of people be really lazy and use JavaScript
to pull back the data and then just pass it on to the Flash. All the Flash does is play
it back. Typically because you have Flash libraries made to play back music but not
necessarily customized to work with the interface. This is how they get around it.
However, some services you have to decompile them. I don't know any Flash myself. I skipped
anything that did this. But it's important to know if you are successful
at decompiling the Flash and want to exploit it using the extension in JavaScript, there
are security issues. If there's a key baked into the Flash, you are not going to get it
from JavaScript. The other one you see is HTML5, which is experimental right now. Not
all the browsers have full support for it. You see this in mobile based applications
because Flash is losing support there. This is entirely in JavaScript. So no decompiling
or anything. It's minified most of the time. It's obfuscated and hard to read.
So where is the vulnerability? I went over how the browser does the major work for you.
So what do you have to do? Well, there are two ways of going about this. I mentioned
this earlier. You can copy the requests by kind of just telling based on the syntax of
the URL and this is typically pretty easy. You look at one URL. There's a file name there.
This is the structure. You know, you can easily write an expression to do this.
However, this can be suspicious if they are doing service side logging and they see two
identical requests coming in within million any seconds of each other. They are saying
this is not normal activity. Why is this happening? I haven't seen it being an issue are in terms
of any red flags being thrown up. But this can be limiting. I found services
where they have one-time use tokens. You use a token to stream your music and after it
has been used it is no longer valid. By the time the second request gets there, it is
not valid and you don't get anything back. And the way you get around this is to generate
the requests yourself. This is going to be a little bit more difficult. Sometimes you
can tell based on the syntax of the URL what variables are needed and how you get them.
Other times you have to reverse engineer the code and figure out what they're doing. But
if you are successful you get past the limitation there and it's undetectable when there's sessions.
So if they have sessions, it looks from the service side that two requests are coming
in from two different people in the same IP address and they happen to be listening to
the same song. So how do you go about doing this? Well, it
is important to keep in mind that you have to do breadth before depth. You don't want
to dig yourself in, the first thing you see, waste two hours and find out it's the wrong
thing. Keep track of all your possible options and then take the path of least resistance.
And you want to remember breadth before --> oh, I did that, okay.
Once you keep that in mind, you want to locate the music file in the network traffic. So
you can do this in Chrome by opening up the developer console and then going to the network
tab and you can see all the traffic flying by and there's going to be a lot of traffic.
You want to filter based on exit traffic and sorting possibly by type. The reason you want
to do this, typically when music is loaded in a streaming service, especially Internet
radio service, the music isn't loaded when the page is sent to you originally. Like with
Pandora, the songs are actually loaded after the page has been loaded. This is because
they want to have time to look at your recommendations and figure out what song to give you next.
Also they don't know how many songs they are going to be listening to before but go away.
They have to load this before the fact. This is done through Ajax, which is showing up
as SHR traffic. You sort by type, audio files, because that's probably what you are going
to be finding. Once you find the actual request you want
to inspect any parameters in that request. Headers, any kind of parameters that they
send in the URL, stuff like that. Then you want to find out where those values
come from. And there are many different locations where these values are going to come from.
The first place, you want to do the easiest to the hardest. The first place that is easiest
is the page URL. Sometimes the song ID is in the URL of the page you're on. And you
can use that to get the song. After that you might want to look at the page source, do
a CTRL-F, look for the name of the parameter. You might be able to find it. Then you might
want to look at local storage and possibly cookies. With things like Grooveshark, if
you have a playlist they will send it all to you at once so you don't have to keep making
requests to find out what the next song is. At the end you want to look at JavaScript.
That's hard to read and you have to figure out what someone else's code is doing and
understand when you have everything, you can attempt to replicate the request. Based on
the syntax of the request you've seen as your example, take the parameters you have and
generate the same thing. First target is Amine, a great first target,
they are a Flash based service but use JavaScript to load. And they have almost no security.
I was able to exploit these guys without looking at any code.
So this is the page with the network traffic. I circled the network tab. And at the bottom
you can see that we have an audio/MP3 file which is actually what we're looking for.
So if you want to take the easy way out, you can actually right click this and open it
in your tab and then download it that way. So this is the cheap way out.
(Chuckles.) FRANZ PAYER: Okay. However, I was trying
to show you guys how to automate this with JavaScript. We are going to do more inspection.
Looking at the actual request we see there's actually only one parameter and I took out
all the headers. Those are the standard headers that the browser sends. We had this FID. I'm
going on a hunch and say that FID is file ID. They typically name things like this.
Now you look for the FID. The first place to look is going to be in the URL. Sure enough,
it's in the URL. Great! We have everything we needed. Now how
do I duplicate what they did? So you go and look back at the original request. You can
see that they actually have this weird subdomain thing going on. And I found out with deduction
that the first four characters of the FID are the first four characters of that subdomain
in reverse order. So it wasn't very difficult to figure that
one out. You can easily replicate this using JavaScript, but I'm not allowed to show you
guys exploit code. Next target is Grooveshark. This is quite
a step up. I chose these guys because they have HTML5 and I'm wondering how that would
play out in terms of difficulty. They use several factors of authentication and JavaScript
is minified. It is not for the faint of heart. You want to make sure that you keep track
of what you're doing the whole time. Know what parameters you have, what you are looking
for, what the next target is. Every time you do something new, update your progress so
you don't get lost. It can get confusing. So I went ahead and I want to tell you guys
that you want to have a JavaScript beautifier. It makes it look like the glob on the left
looks like the glob on the right. While you have underscore P, you can have proper spacing
and you can read functions. That's great. Here this is --> I skipped the network analysis
and this is the actual request itself. And I've highlighted that they have --> that's
the URL they have there. All they have is stream key as what you send to them. Off the
bat you think, hey, this is easy. I have one parameter which is stream key. That's what
I'm going to look for. So you also, I looked at all the traffic to
see what was coming. I found this more in the .PHP stream and there's an IDEX method
that takes several parameters. What this will do is return the stream key which is what
we are looking for. So we need the session, the token, the UUID and the song ID. The method
I highlighted. That changes all the time, but we know what it is because it says up
there. So we only need four parameters. I say only
because it actually is easier than it seems. And while I was looking at more .PHP I found
the get communication method which uses a secret key. And I like secrets so I'm going
to keep this in mind. What do we need? From the beginning we know
as soon as we get the stream key we can get the song. To get the stream key we call up
the point --> we need the four parameters and more PHP has the secret key so I'm interested.
I already looked through everything. It's time for JavaScript.
And this is what I get. And in the very first line you see this window.GS.GPL. I'm guessing
GS stands for Grooveshark. Cool, they're storing stuff in the JavaScript environment. Let's
see what they have. I have this window.GSconfig, which has the GS ID. One down. What else is
there? Window.Q.models, which turned out to be the entire playlist you have saved in memory.
Every single song in this playlist has an ID which is the song ID. Right off the bat
we are able to find two of the parameters we needed by looking at the first line of
the JavaScript file. Not bad, but we still need the rest of the parameters.
So I do --> so this is kind of keeping track of everything. We need the token in the UUID
now. I search for that because it was easier. Sure enough I find a function that takes no
parameters, which is good news. Now I don't have to find any more parameters. I can copy
this function and every time I need a new UUID, I call this function. This is an easy
copy and paste. Now we are left with token. This is where it gets more challenging. I
do a CTRL-F for token and I find this F.header.token which turns out to be the token being put
into the header of the request we saw. And so looking at it, it takes, there's a
bunch of stuff that we need. So going top down like you should read code there's this
R.randomizer, remember what I said about function that take no parameters. Copy and paste them.
Sure enough, that's the function right there. That's taken care of.
We need this R.revtoken. I do a CTRL-F for rev token and I find rev token is equal to
N, which is equal to GUI Flubber, which they hoped no one would find. Unfortunately they
shouldn't have put it right on top. So now we really need the current token. Method
was just the method we were calling the URL and we have that documented. So now we just
need the current token. I search for any instances of token because I couldn't find any instances
of current token and yeah, this is where the secret key comes in because get communication
token returns the token that we need for this request and so now we are on a hunt for the
secret key which CTRL-F shows that it is the hex MD5 of the session ID which we found on
the very first step. So we already have everything that we need.
And to just recap, we needed the stream key and we got that by finding these four variables
which were just in the JavaScript and the secret key was needed to get the token. We
have everything and with this information you can generate the request.
But I can't show you an exploit code. So we are just going straight to a demo.
All right. So this is Django.com. Wait, I have to switch.
All right. So this is Django.com, a small music streaming service. It's like an Internet
radio station. The first thing I'm going to do is open up the developer tools and go to
the network tab. And you want to do this before you actually play the song. If you do it after
you play the song you're not going to see it in network traffic because it's already
loaded. Important to keep that in mind. All right. So ...
(Music playing.) FRANZ PAYER: So it plays, which is good.
I'm going to filter based on XHR traffic down here and at the very bottom we have this audio/MPG
file which is what we are looking for. I'm going to click on it and look for anything
that we might need. And it turns out that this is a statically
assigned, static streaming website. So we just need this file name. And it's important
to keep in mind here we have this weird directory thing going on, which is also the first six
characters of the song ID. So if we need it, we have it right there. And because I don't
really know where to look for this file ID because it doesn't have a specific parameter
name in front of it. I can't do a CTRL-F. I'm going to look at the other traffic we
filter on. There's only four other requests, so it's not going to take us very long.
You can see here that this responds with a bunch of JavaScript files. And JavaScript,
it has a like, it has song ID here. But it doesn't actually correspond to the 08061 that
we have. That's dead end right there. But if you look here, this page actually returns
the URL without the file name. Just the whole URL. So this is our target. And if you look
at the headers, they take quite a few parameters. So we have first time, which is equal to one.
I'm guessing that means true. So that's going to be a binary flag. You can probably lie
on that if you want to. SID, which I'm not sure what it is, version number which is probably
going to be the same every time. SUW, which I'm not sure what that is either and CB which
I'm also not sure about. At least now we know what names we are looking
for. At this point I actually notice that I'm glad that Chrome has this, but they have
an initiator column which tells you exactly what script and what line made this request.
So if we click on this, it will actually take us to this line, which if you notice, this
is minified JavaScript. You won't read this easily. I've went and gone ahead and unbeautified
it. We can actually inspect the code. So now if you recall, the URL back here was streams
URL. So we are going to look for any instance of streams.
And sure enough, it takes us straight to the line that creates the request. We have this
underscore JM station ID and some parameters which are set right above it.
And so we can see first time, set it to 1. We have SID which is apparently the session
ID. We have the version number here. And SUW apparently stands for whether the sign up
window is visible or not. So we have everything. Then CB here is apparently
the date and time. So we have everything we need. And I've already
went ahead and wrote a one-line JavaScript which will generate this for us.
So ... this spits out the URL that was generating those new song locations. So I'm going to
copy that. It turns out they actually patched their service three days before I went to
DEF CON and this is freaking me out. Apparently they are doing some weird thing with checking
your session. But I found out to get around it, you can just refresh the radio station
and then it will work. So here we have the next song that would be
playing. If we actually just keep refreshing this, it will give us a different song every
time. (Chuckles.)
FRANZ PAYER: So we can actually get every single song in their music library.
(Applause.) FRANZ PAYER: But I did want to show you
guys an exploit through my Chrome extension. So although I won't be releasing it, I can
show you guys what it looks like. (Music playing.)
FRANZ PAYER: As you saw, there's a pop-up. I clicked on it and it takes us to my Chrome
extension and you can select it and hit download. It's right there.
(Chuckles.) FRANZ PAYER: So not very difficult. So
that was the Chrome extension. I did say that I was going to be releasing an alternative
tool. What is this tool? Well, I'm tentatively calling it Browser Shark until I get sued.
I already bought the domain name, so I'm good. Basically what it is going to do, if you recall
earlier I mentioned I had a method where I would copy any URL that matched the syntax
of the song and then go retrieve it myself. I decided wouldn't it be cool if I could record
all my traffic going through and cache that to the browser and what happens is now I can
-- I can just go to Google and all my traffic will show up right here. What you can do with
this is, you can actually analyze the hex of the request to make sure that you are not
getting any malware. It's nice enough to tell you what type of file it is. If they are lying
to you, you can tell. Like I said before, this would be really cool
with forensics and stuff like that and I'm planning on doing more of coding so you can
do a little bit who are with the hex editor. So this is a tool that I'm going to be releasing.
I will have a location to download it at the end of my Power Point.
All right. Things I learned. Downloading music is inconvenient.
I found that after I had music, I didn't know what to do with it because managing all this
music was a pain. So actually now I honestly just use Spotify. I don't like having to deal
with files. (Chuckles.)
FRANZ PAYER: But services were fairly easy to exploit. I think with all the different
services which I listed at the very beginning, I found exploits in them in three days total
for all of them. The hardest one was Grooveshark which took me a whole day. Pandora was surprisingly
easy. It was impossible, it's actually impossible
to completely protect streaming. Inherently at some point you are going to have your music
on my computer and I own my computer. Even if you use encryption you have to decrypt
it so you can play it back. At that point you can copy the files.
So inherently you can't protect streaming. And SMP things you should know. People have
bad security. This is a shocker. And some people will patch their code. Others will
not. This is the beast of security. It's just the way it works.
And the same Web traffic logging will work with video streaming services, too. Some of
them, not all of them. People ask if Netflix will work. No, it won't, but if you go to
a sketchy Chinese streaming network sites I'm sure this will work as well. But that's
a topic for another day. So I did a case study. Originally the very
first target I found was last F.M. which if you're not familiar with it, it's a British
streaming music service. I found the vulnerability and e-mailed them. I got no response. I made
the Chrome extension, I got no response, but apparently they were able to fix it without
my help. Good on them. These are things I noticed after I fixed it. They secured it
heavily. They capped the bandwidth to match the play back speed. It's impossible if you
wanted to download the whole music library, it's impossible. You have to wait as long
as it would take to play back all that music, which is years.
So that's a good way to prevent people from stealing all your music. They have one-time
use tokens. Like I said earlier, once your first request is made, the second request
is no longer valid so you can't get the music. I tried to do a weird sketchy thing where
I made sure my requests would get there almost at the exact same time. What would happen,
I would get a good ten seconds on my fake stream but it would cut out. They only allow
one stream open at a time. So that was pretty good.
And I couldn't exploit this. And if you wanted to, it would take a huge amount of time and
wouldn't really be worth it, they have obfuscated code and the bandwidth cap makes it so you
can't feasibly take all the music. Mitigations. Using current technology, the
one-time use tokens is definitely the best way to prevent people, like I showed you before,
right clicking and opening a new tab and saving it. The second request won't work.
I've also seen people use RTMPE streams. This is Adobe protocol which they use. E stands
for encrypted. I want everyone to know, because your protocol has an E in it doesn't mean
it's encrypted. I've seen services using non-encrypted traffic and put the E as a protocol. That
doesn't make it secure. (Laughter.)
FRANZ PAYER: And also returning the song in pieces really helps as well. Although so
sound coat actually did this a couple days before DEF CON, but they named all the pieces
in numerical order. That doesn't make it any more difficult for me to put them back together.
(Chuckles.) FRANZ PAYER: For future proofing, you can
take a look at the HTML5 audio tag with support. The guys from Virginia that Tech wrote a paper
on it. I haven't looked much into it. I know inherently that nothing is going to work.
But if someone is interested, that's there. And so these are the references. I actually
have uploaded the browser Shark thing to the Google play store, whatever. So that's the
long URL. I made a lit leaf. If you trust me enough to click on it, I assure you it's
the same link. I'm putting this project on GitHub because I want it to be open source.
It's up there right now, but I don't trust DEF CON WiFi.
That's my blog. I sometimes put interesting stuff on there, sometimes not. No guarantees.
There is my contact information if you want to talk to me.
And I'll take any questions now if anyone has any.
Yes? AUDIENCE: How do you deal with renaming
all the files? FRANZ PAYER: The question is how I dealt
with renaming. In the flat extension, what I did, I wrote a script which would hook on
to the page and I would use jQuery to take the file name and then the artist from the
page itself because they provide that information so the user knows. I actually spent a good
several hours trying to synchronize all the songs together, but that's how I did it.
Yes? AUDIENCE: Why aren't you releasing your
original file? FRANZ PAYER: Because I don't like losing
my money to lawsuit. I can get sued $20,000 per count of trafficking. They have millions
of songs, which would turn into billions of dollars, which I don't have. Yes?
AUDIENCE: (Speaker away from microphone.) FRANZ PAYER: I tried doing this for Spotify.
A guy in Norway or something did the same thing I was doing but happened to release
the code for it. Once before I gave this talk. So they fixed it. It does not work with Spotify.
Yes? AUDIENCE: Did you find that song you downloaded
had a direct ... (Speaker away from microphone.) FRANZ PAYER: It honestly depends on the
service. I think Pandora did a better job of that. Some of them allow users to upload
their own songs and you'll get really weird cover ads as your cover art. It just depends
on the service. Yes?
AUDIENCE: Are these files water marked? FRANZ PAYER: There is no DRM in any of
these. As you saw, I was playing it back in my music player when I downloaded it. There's
no DRM on any of these songs. Any other questions? All right, cool.
(Applause.)
*****