5 min read

Watching movies legally from a subscription based service, without paying

Watching movies legally from a subscription based service, without paying
Photo by Glenn Carstens-Peters / Unsplash

When you start writing small programs and fix printers, its just inevitable when someone comes to you and asks if you can grab a season of a TV show they want to watch.

Now here's the catch: The TV show had their latest season in a weird streaming service that you've never heard of, but that's mainly because of your ad blockers working very well.

But eh, you say. It'll be fun.

Yeah... It will be.

Step 1. Getting an account

Obviously, on a streaming service, you would make it so it's not that easy to crack passwords, etc. Well, I have no idea how, but with good googling skills, you can find a few username & password combinations.

Now, just like Netflix or Disney+ or whatever, this small streaming service has decided to have profiles that you can lock. And 2 of the aforementioned logins had 5 profiles that were all locked. Now, here would be a great place to just give up and work on something else.

But come on, it's way too early to give up and call it a day. I've got some JSON to read!

Step 2. Getting into an account

There are 2 ways of checking passwords. The fastest way is checking it in client-side, which makes it so the profile's PIN is sent to the client.

Thankfully, the service we're cracking has not done this. So, the next step is figuring out a way to get into one of these profiles. I don't know about you, but I'm all up for writing a quick C# script that will try all possible options one by one.

Yes, it's inefficient.

Yes, I might get IP banned for spamming their API. Like there has to be a rate limit at some point.

Spoiler alert: if you send the requests one after another, you'll be fine. However, you use like 10 threads to maximize the speed, get ready to meet an nginx error page.

Oh, to give respect, they DID stop me. Twice. Even if I sent the requests the slow way.

The first one was saying that the endpoint I was using didn't support POST requests. Well that's weird, I've been sending POST requests all this time, why not now? Welp, I refreshed the cookies in my script, and were back to work.

The second time is when the estimated finish time went from 40 minutes to 7 minutes. There's no way this endpoint would take less than a second to pass. The solution to that? pause the script for 2 seconds and start again. don't ask how.

Eventually, in like 25 minutes, we managed to brute force a 4 digit pin. Now let's use this pin and- uh oh.

The account doesn't have a subscription available.

Step 3. Time to have fun with the API

When life gives you lemons, you make lemonade.

When a website gives you a way to watch the first episode of the TV series you're trying to download without a subscription, you take the API, change some variables and hope it works.

And come on, there's just NO way they would return a valid HLS manifest URL when i change the episode ID to an episode that requires a subscription-

Request & response of an API request
I deleted some parts to hide the name of the service. Note the `isFree` property in the response

... I'm speechless. There is it, the MPD manifest, right over the drm: true field-the what?

Step 4. Breaking DRM

Here's something I have never done before. So far, if anything I'm trying to download had DRM, I would've just gave up. But not today. I have a mission to complete, and it is now too late to stop.

But how do we crack this Widevine? Well, instead of cracking, lets just get the decryption keys and decrypt it ourselves, because turns out a tool for that had existed for a long time!

In the MPEG-DASH manifest, there's a value called PSSH. The video players send this value to the license server, which sends back the decryption keys. So let's send that to the license server!

A Widevine license request

Completely unrelated, but let's not do that!

After doing some google-fu, I have found a website that will communicate with a license server for you. You just send them the license server and the PSSH, and you'll get the decryption keys. Easy as that!

Now, I don't know about you, but I'm not doing this manually. Do y'all remember the good ol' C# script we used to crack the pin of a profile? Well I still have Rider open on the background so lets comment out all the old code and add onto it.

After using QuickType to parse the JSON into a class, I spent 30 minutes trying to understand how System.Text.Xml worked, just so I could take out the PSSH. Now while we're here, let's also get the decryption keys automatically. Thankfully, the website I'm using has an API that is free to use. I mean it wouldn't matter if there wasn't an API at all. But this still makes my job easier, so let's not complain.

So, now we have the encrypted audio & video files, and the keys to decrypt them. Is this it? Is this the end of our long journey?

Well, mp4decrypt didn't return any kind of status message, so it most likely didn't work, let's check the output file anyway.

Holy crap it has passed the 20 second test.

Now, you might be asking: "What the hell is the '20 second test'?"

Most DRM protected media have some part at the beginning that isn't protected. I have no idea why, but it is usually between 10-30 seconds. After that, it's all garbled data, and whatever youre using to watch the video will abrubtly skip to the end.

Coming back to the file, surprisingly this is it! Now I can just put these through ffmpeg to merge the audio and video files into a single file and send them to the friend who asked me to do this!

Thank you, developers of some random streaming service for not making your app not secure enough, I really appreciated it! I really hope this wouldn't randomly break because you updated your site.