Disclose private/scheduled streams of any Livestream user due to open .m3u8 endpoint

I recently found an issue on Livestream that led to disclose any users private(draft)/scheduled streams.

Intro

Livestream let’s a user create events on their account. The user then creates posts that can be video based posts ,image based posts or just text based posts under the created event. These posts can be saved as drafts or be scheduled to be made public.


I started off with a video post by uploading a video and scheduled it to be public only the next week. The intercept was on during the whole process of upload. One of the requests below showed the video id of the uploaded video and hinted the media services in use.

The Request :

POST /owner/accounts/28088370/events/8514908/videos HTTP/1.1
Host: api.new.livestream.com

The 28088370 referred to the acccount id and 8514908 referred to the event id. Both these id’s were publicly available on visiting the said account on Livestream.

The JSON response of the above request had the video id as seen below:

{"id":188597955,"event_id":8598251,"event":{"id":8598251,"short_name":null,"full_name":"testerx","description":null,"owner_account_id":28301059,"owner":{"id":28301059,"full_name":"abs ...................................
....................................
.........................."asset"{"qualities":null,"akamai_stream_id..................................................................................................................*{Lots of other initially unobserved stuff}*....................

Response gave me a hint of how the video id looked like and also that Akamai media services might be in use.

Now to gather more endpoints,click play on the uploaded video and intercept requests. One of the requests was to a m3u8 file and looked something like :

GET /owner/auth/b0128101059............................4bd802ced1b90c02e9c75e6284c87a02........................................1552398275601...........1552571075602...........4UEXC0RJKwnt0cXEI1y0yA~~00000000000000..................................../accounts/28088370/events/8514908/videos/185422358.secure.m3u8
Host: player-api.new.livestream.com

This returned an application/vnd.apple.mpegurl response with the video backup URLs as seen below:

HTTP/1.1 200 OK
Server: openresty
Content-Type: application/vnd.apple.mpegurl; charset=utf-8
{other headers..}
#EXTM3U
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=150000,RESOLUTION=480x272,CODECS="avc1.77.31,mp4a.40.2"
https://vod.livestream.com/5c87f404_a2ba525be7719d247853a38ab4fae8e260148a8e/events/00000000008332eb/be94b4a1-ee64-4159-a83e-2a8c42e28ce3_150.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=550000,RESOLUTION=768x432,CODECS="avc1.77.31,mp4a.40.2"
https://vod.livestream.com/5c87f404_a2ba525be7719d247853a38ab4fae8e260148a8e/events/00000000008332eb/be94b4a1-ee64-4159-a83e-2a8c42e28ce3_550.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1500000,RESOLUTION=848x480,CODECS="avc1.77.31,mp4a.40.2"
https://vod.livestream.com/5c87f404_a2ba525be7719d247853a38ab4fae8e260148a8e/events/00000000008332eb/be94b4a1-ee64-4159-a83e-2a8c42e28ce3_1500.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=2000000,RESOLUTION=1280x720,CODECS="avc1.77.31,mp4a.40.2"
https://vod.livestream.com/5c87f404_a2ba525be7719d247853a38ab4fae8e260148a8e/events/00000000008332eb/be94b4a1-ee64-4159-a83e-2a8c42e28ce3_2000.m3u8

This is how m3u8 files( m3u files that are utf-8 encoded) present. They are plain text files that tell you where the media is located and behave like a playlist.

Out of curiosity I also ran a search on the content type returned i.e application/vnd.apple.mpegurl . Reading the stackoverflow answer, the content type and m3u8 format is used for HLS(Http Live Streaming) formatted streams and would play given a player. Know more here.

When a m3u8 file is fed to a player like vlc, the player would first parse it and fetch(download) the video from the backup urls present in it. There cannot be any pre-fetched content when dealing with m3u8 files.


Now i happened to upload another video and everything was the same. But this time i noticed something else. The second video that was uploaded was just off by an offset of 3 w.r.t to the video id of the first uploaded video.

So the Video id’s were incremental by 1(knew this after uploading a third video).

After revisiting every request-response , i got another hint. On visiting the first request that initially returned the video_id and the media services in use i.e the below request (also shown at the beginning)

POST /owner/accounts/28088370/events/8514908/videos/
Host: api.new.livestream.com

The response along with the video_id and the media services also included the endpoint of the SMIL file which is a metadata file to handle different bit-rates.I did not observe this initially as the response was pretty big and I got hooked onto something else entirely that led me down a rabbit hole.

The unobserved part of the initial response was :

{rest of the response}..........................................................................{"smil_url":"http://api.new.livestream.com/accounts/28088370/events/8514908/videos/185422358.smil","secure_smil_url":"https://api.new.livestream.com/accounts/28088370/events/8514908/videos/185422358.secure.smil","secure_progressive_url":null,"m3u8_url":null,

On visiting the smil endpoint , i just got an unauthorized response like the one below:

So smil files were only available for public posts. Not much could be done here . But then I realized that .smil is basically a file format. The unobserved response (scroll up) also had a “m3u8_url”:null in it. So I thought of changing the format of the above endpoint.

So i changed the .smil to secure.m3u8/.m3u8 because the path was referenced right down to the video_id and the fact that a “m3u8_url” :null was one of the key/value in the Json response received with the smil endpoint so that could hint a possibility of having a m3u8 url similar to the above smil endpoint.

So, <video_id>.<format>,<format> was changed here and the video would actually download as m3u8!

The video could also be downloaded from the api.new.livestream.com endpoint

Common video formats such as mp4,flv etc were blocked and would present with just a blank screen. So I sort of had an open m3u8 endpoint!

So i could download the scheduled video from api.new.livestream.com with just 2 publicly available id’s and 1 incremental id.

At this point i was ready with the report because all i had to do was write a small script to just increment the video id for a said account , find their stream and download it.

iterate y requests to =>
http://api.new.livestream.com/accounts/public-account_id/events/public_event_id/videos/"+y+".m3u8"

Here y would be replaced by the iterator value. To ease things up,as a malicious user i would first upload a video in my account and know the high-point of the video ids. I can then iterate backwards from the high-point and wait for a 200 OK.


Reported: Jan 7

Resolved: Jan 21

Bounty : $1000


Thanks to Livestream for allowing disclosure on this issue. They are a pleasure to work with.

If you would like to collaborate , share ideas and be friends, find me here .

Thanks!