11 min read

Reverse Engineering an IPTV Service

Reverse Engineering an IPTV Service
Blake Patterson from Alexandria, VA, USA, CC BY 2.0, via Wikimedia Commons

...just because the IPTV STB broadcasts as a YouTube TV.

Okay, back to the topic. Let's talk about televisions.

⚠️
I did ZERO research for anything below this box

The television was invented by Grog Rockstone back in 24725 BC using fire and wood. Unfortunately, there weren't any TV channels active at that time, so the invention died rather quickly.

In the early 1900s (i think), the television was reinvented again by some very bored man, because he wanted to watch some funny game shows of people getting the simplest of questions wrong and being shamed in front of the public, because come on, nothing is funnier than that! He also didn't credit Grog Rockstone. Shame on him.

Back then, we used the state of the art, revolutionary way of getting the audiovisual signals off the air with these things:

Very old television antenna
Carnby, CC BY-SA 3.0 <https://creativecommons.org/licenses/by-sa/3.0>, via Wikimedia Commons

This technology was so great, people still use it!.. not really, because someone else was cooking some dishes.

Some people laid down looong cables to send the image directly to your TV but that's kinda expensive isn't it? There must be a way to stream from one source and catch it from multiple places. But what's something you can see from everywhere on the surface of the earth?

The space!

A bamboo house fitted with a TV satellite dish
Kwameghana, CC BY-SA 4.0 <https://creativecommons.org/licenses/by-sa/4.0>, via Wikimedia Commons

I have no clue how they thought this would work, I'm not a physicist, I'm just a nerd who cries while looking at JetBrains IDEs (except PyCharm), but somehow they did! And holy hell did they work fine. Until it was raining.

With the old cactus-like antennas, you would be lucky to see the details on someones face if they weren't completely in frame. With this new technology, you could make out at least 4 people from a crowd! Surely no one could make anything better right?

The internet was invented in 1983. The television was invented in 1927, according to online sources. If you add those two, you get 3910, which is when the Internet Televisions should have been invented. But people weren't gonna wait that long, there has to be a better way of watching the Wheel of Fortune!

In 2010, Türk Telekom, the biggest (and the worst) ISP in Turkey, had something very cool coming up. Named after the TV, tivibu (who makes their name lowercase smh) was a game changer in the history of the television – at least in Turkey. For example, you could pause the stream whenever you wanted!

I mean come on, there were cheap set top boxes that could do that, it's not special!

Oh that's not all! It's like watching a VCD – or DVD, I really just wanted to mention VCDs – you can pause AND resume whenever you want! Hell, you can even rewind.

Rewind? Rewind??? What kind of technology would let you rewind a live stream? Twitch came out in 2011 and you still can't rewind a stream there!

We will get into that very soon, but lets talk about how this tivibu works from an outside perspective first.

Unlike Netflix, and probably like 2 other streaming services existed back then, tivibu wasn't an app you would download to your Smart TV running the latest version of Android, which was 2.3. You had to buy a set top box specifically designed for it.

Now, obviously, a device that streams live TV over the internet has peaked the interest of the hacker community, right? Unsurprisingly, no. If you ask why, it's quite simple actually.

You had to buy internet from Turk Telekom, No one wants to do that. That's why I could find a total of nothing when I googled the domains I found. There was a single GitHub issue where someone wanted the live manifests of tivibu to be in an IPTV M3U8 repository, but it was declined, because of geo-blocking.

Back to the topic, what is this box exactly? Well this is where our story starts


A few days ago, I got a question from my mom who wanted to cast a YouTube video from her phone to the Smart TV running the latest version of Android, which wasn't 2.3. But there was a problem. In the devices list, there were 3 devices.

  • The smart TV (YouTube app)
  • The smart TV (Chromecast) (assumption, device is duplicate on the list and there are no other logical explanation)
  • TN2000HD

You see, according to the admin interface of my router, there shouldn't be more than one device which has the ability to advertise itself as a YouTube TV app... right? Unless if my printer was running Android with Google apps installed, which would mean that YouTube could advertise in the background. That would explain how Google Cloud Printing worked. If it did.

But then I realized something. What if, instead of the printer, it was the tivibu STB that was advertising. But that couldn't be it, they removed the YouTube app after having it in "maintenance mode" for years!

With that, I did the unthinkable. I got out of my room for the first time in years. I immediately walked into the living room, and took a look at the STB. Right under it was the holy symbols I've been looking for.

TN2000HD

It wasn't my printer running Android with GApps, it was the tivibu.

Well let's not rush into this, shall we? I mean we don't have any proof that it runs on Android. However...

If it was broadcasting itself as a YouTube client, what else would it advertise itself as?

With that newfound knowledge, I got back into my room – never to get out again – and started using my networking skills.

I don't have any networking skills. I just googled the commands. And found out that the STB had 5 ports open:

  • 111
  • 7547
  • 9090
  • 10013
  • 38520

Out of these 5 ports, I only knew what 2 of them were. 111 was for rpcbind, a thing for Linux that does things. 38520 was the port where the YouTube advertisements were coming from.

Which leaves us with the other 3, which were all HTTP servers. What if one of them could be used to remote control the device? Only if there was an Android app we could look at-

Introducing, our first step into this rabbithole

First of all, this app makes me question their branding. Is the T capitalized or not.

Secondly, the ratings. Just lmao.

Third, in the screenshots, it says that I can control my "home box" which is probably the STB. So what are we waiting for, lets MITM the requests!

You have to log in for this action. [Log in] [Cancel]
You have to log in for this action. [Log in] [Cancel]

I have to log in?? why would i do that! We're going to reverse engineer it with JADX!

Unfortunately, they didn't pay their developers enough. How do I know? The app is a React Native app. I don't want to read Hermes bytecode to understand how it works. But I took a peek into the strings and found out that the developer uses a Mac!.. they have the project path in the code for some reason.

Anyway, unfortunately, the app was a bust. I guess there's nothing else we can do. I mean I would love to MITM this app, but I couldn't get it to work for some reason.

Welp, time to never post for a whole year again. See you next year!


I have just been notified that there are two "Tivibu GO" apps on Google Play. TWO. ONE WASN'T ENOUGH?

Whatever, after taking a peek into the files of it, I have found out 2 things.

  1. This is ancient (minSdkVersion is Android 4.0)
  2. This is ancient (only supports armeabi devices)

After spending hours trying to install the APK (because it was ARM and I didn't know that API 30 AVDs can run armeabi APKs so I made an ARM AVD which was very slow, it took like 4 hours to install a single APK file, failed, then i just realized that I can just install it on my existing mitmproxy AVD) I realized something

Starting app. Please wait...
Starting app. Please wait...

This is exactly how the STB boots. What the f-

This is just another point for my "the STB is just a cheap Android box" theory. Unfortunately I can't look inside of the device to possibly get a flash dump or something, as it's being actively used.

After waiting a few seconds for the app to launch – where it probably logged my current location to the government for running it under an AVD – we see something unexpected.

This looks exactly like the STB interface. The only difference is that I can use it without logging in, while the STB requires a top secret, 6 (or was it 8?) digit code to sign you in on the first boot.

After messing around with JADX, I have good news and bad news.

Good news: The app is just a single WebView and ExoPlayer plus some connection logic.
Bad news: The app is just a single WebView and ExoPlayer plus some connection logic.

When you pop the first URL the app loads into WebView you get sent through 5 URLs and end up with a blank page. or do you?

A white HTML page, with developer tools open.
A white HTML page, with developer tools open.

After making login_container visible and setting the bgcolor to something like #555, you get meet with the login screen.

The hidden log in screen

Unfortunately, there aren't any "skip to the portal" button here, so we have to log in to use it in the website. At this moment I just said "fuck it", got a password to use with this thing and logged in to realize one thing.

There is a lot of AES going on here. And Chinese comments.

The password is sent to the server after being encrypted with aesKeyLogin
The password is sent to the server after being encrypted with aesKeyLogin

I could talk about how this code works, but if there's two things to do one thing, this code does the third, objectively the worst one. For example:

  • Storing & Reading the cookies through the API instead of document.cookie
  • Out of all possible AES modes, it chooses the one when thrown into Android Studio (don't ask) it says "hey uhh don't use this? please?"
  • Request queries and POST data (form body, not JSON) are built through string concatenation. You can find parts where you can inject stuff.

Coming back to the login page, its just... wild. Its either "security through obscurity" or they have the weirdest database ever. This is what happens when you click login.

  1. The password is encrypted with AES
  2. An "authinfo" is created, which is the STB mac address[assumption] or zeros if the device isn't an STB, current timestamp encoded in a weird way, and a six digit random number, joined with $$
  3. Encrypt the authinfo with AES
  4. Send the username, encrypted password, encrypted authinfo, and some device info to a very slow endpoint.
  5. If unsuccessful, show an error message and stop here
  6. If the "remember me" box is selected, set 4 cookies, username and password, encrypted with AES.
  7. If the "remember me" box isn't selected, clear those 4 cookies.
  8. Set the login type as a cookie. Surprisingly not encrypted
  9. Throw the username and password into another endpoint to get an "iemg", the next domain & address
  10. Throw the username and password into another endpoint, to get an "authenticator" token
  11. Throw the authenticator token into another endpoint to get a "user token"
  12. Throw the "user token" into another endpoint to finish this API hell and get sent to the portal page.

Let's get to the point. The portal is kinda broken. The UI works, but the player is broken no matter which JavaScript object I change to lie to the code and try to make it use HLS.js/DASH.js (they are supported by the portal). Though the code is readable and even documented in Chinese! I extracted a lot of endpoints using a single regex lookup. The API still works tho, tho some point.

From the API, we can look at 2 requests that pop out to the eye. GetStbChannelUrl and PpvTvLiveUrl. GetStbChannelUrl returns a list of all channels, a little bit of info about them, and HLS URLs for all of them.

And guess what, when you pop the HLS into MPV, it ✨ just works ✨... kinda.

Screenshot of a livestream
MPV didn't want to work for some reason when I was getting screenshots so you have to do with DASH.js Reference Player now

Remember when I said going back in time being a very huge technological leap? I want that. However, these can only leap back for 60 seconds.

If we look a little more into the API response, we can see another URL, named timeShiftUrl. Sounds exactly like what we're looking for... except... the domain leads to a private IP range, which I cannot connect, but for some reason the Android TV app can connect and play fine. Probably has some HTTP proxy behind the scenes, but I couldn't figure it out, even after reading some of the source code in JADX.

This is where we look into PpvTvLiveUrl.


I hate this endpoint. I hate it so, so, SO much.

That endpoint, unlike GetStbChannelUrl, needs authentication. And it checks if you have subscription to a specific channel. And like 5 other things.

Doesn't sound so bad so far? Remember when I talked about the 12 step authentication? No matter what I tried, I just couldn't get it to work. Even after recreating all of the requests in my own code.

I can authenticate fine, the "current user ID" matches the ID in the STB box that sits in the living room. But according to the API, I'm only subscribed to 2 channels, channel #1, which is a showcase channel that just plays movie trailers of the movies in its "Choose & Watch" VOD service, and the other one which is the sports channel. I don't care about those channels.

I spent THREE DAYS trying to get it to work, just to say "fuck it" and leave the authentication to a puppeteer instance which I just yoinked the JSESSIONID out from. It has an 80% success rate, sometimes it just gets stuck, but when it works, it just works. And that's what I want.

With that out the way, we can get the Live URLs!.. and also find some source code in the android app where it sets a Widevine License URL and headers. Doesn't look like it's required though, even though the code and the API responses say that it's required, since I can just throw the URL in MPV and play it fine!

Screenshot of a livestream
You can rewind up to 6 hours in most channels, except the channel #1, the trailer channel. aka this one.

With that, it's time to wrap this adventure up. It was definitely fun (no it wasn't) and taught me a lot about internet streaming (no it didn't). I don't think about fucking with this service more than I've done so far. I'll see you all next year where I reverse engineer a random ISP to get infinite bandwidth just to download a single Steam game, not knowing that my CPU isn't strong enough to decompress the file faster than 20 kilobytes/second.

Oh hey look, Android TV documentation about implementing an IPTV service into the system's own live channel list!