{"id":710,"date":"2024-10-18T10:08:42","date_gmt":"2024-10-18T10:08:42","guid":{"rendered":"https:\/\/reversea.me\/?p=710"},"modified":"2024-10-18T10:10:35","modified_gmt":"2024-10-18T10:10:35","slug":"when-geolocation-based-media-streaming-blocking-goes-bad","status":"publish","type":"post","link":"https:\/\/reversea.me\/index.php\/when-geolocation-based-media-streaming-blocking-goes-bad\/","title":{"rendered":"When Geolocation-based Media Streaming Blocking Goes Bad"},"content":{"rendered":"<span class=\"span-reading-time rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">Reading Time: <\/span> <span class=\"rt-time\"> 8<\/span> <span class=\"rt-label rt-postfix\">minutes<\/span><\/span>\n<p><strong>TL;DR<\/strong> Protocols like HTTP Live Streaming and MPEG-DASH are essential for efficient multimedia delivery and a seamless user experience, as they adjust content quality based on bandwidth. Streaming services using these protocols often impose regional content restrictions due to copyright or streaming rights. In this blog post, we explore how to analyze (with Burp) streaming services to detect what geolocation mechanisms exist. We also show a case study involving RTVE, Spain&#8217;s largest public media company, showing how improper implementation of regional content blocking allows unauthorized access to its content. Finally, we propose a simple solution for RTVE to prevent this issue.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Context<\/h2>\n\n\n\n<p>In the streaming era, protocols such as <a href=\"https:\/\/datatracker.ietf.org\/doc\/html\/rfc8216\">HTTP Live Streaming (HLS)<\/a> and <a href=\"https:\/\/www.iso.org\/obp\/ui#iso:std:iso-iec:23009:-1:ed-5:v1:en\">MPEG-DASH<\/a> are essential for streaming multimedia content efficiently. HLS, developed by Apple, uses M3U8 files to list video segments and adjust the quality based on the available bandwidth. Similarly, MPEG-DASH uses MPD files for the same purpose. These protocols enable a seamless user experience through efficient content delivery.<\/p>\n\n\n\n<p>Content delivery is often tied to copyright or broadcasting rights, which result in limitations on the regions where it can be played. A common mechanism used to enforce these restrictions is regional content blocking (or geoblocking). In this blog post, we analyze how regional content blocking was improperly implemented by <a href=\"https:\/\/www.rtve.es\/play\/\">RTVE<\/a>, allowing media content to be accessed in regions where it should not be available. RTVE, Spain&#8217;s largest state-owned public media company, provides multi-station television (<em>Televisi\u00f3n Espa\u00f1ola<\/em>) and radio services (<em>Radio Nacional de Espa\u00f1a<\/em>), as well as online and streaming services (<em>RTVE Play<\/em>), which is the focus of our analysis.<\/p>\n\n\n\n<p>Below, we present our threat model and detailed analysis to determine whether geo-blocking is correctly implemented at RTVE and identify any software flaws exist in their streaming protocol (<em>spoiler alert<\/em>: <strong>flaws do exist, or else there wouldn&#8217;t be a need for this post<\/strong>).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Threat Model<\/h2>\n\n\n\n<p>Many of RTVE&#8217;s streaming services are accesible only from Spain. Therefore, we consider an adversary located outside of Spain who has access to a VPN with a Spanish IP address. The adversary can turn the VPN on and off at will, using free VPN services that allow limited data usage. In vulnerable scenarios like the one we discuss here, the adversary can start streaming content while connected to the VPN and then disconnect, maintaining access to the content despite being outside the allowed region.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Streaming Protocol Identification<\/h2>\n\n\n\n<p>The first step is to identify which streaming protocol is being used. This can be done by inspecting the requests made by the web application. For this task, we use <a href=\"https:\/\/portswigger.net\/burp\/communitydownload\">Burp Suite Community Edition<\/a> (or simply Burp).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Setting Up Burp<\/h3>\n\n\n\n<p>After installing Burp,  create a project with the standard settings (select &#8220;Use Burp Defaults&#8221;) &#8212; this is enough for our purposes. If you haven&#8217;t done yet, you need to configure Burp\u2019s certificate in the browser as a trusted certificate to intercept and analyze HTTPS traffic. After importing the certificate and marking it as trusted, restart your browser. Verify that Burp is correctly intercepting HTTPS traffic before proceeding (e.g., try to navigate to an HTTPS website and check that Burp is intercepting traffic correctly). Once everything is set, we can start reviewing different streaming services :).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">RTVE Streaming Protocol<\/h3>\n\n\n\n<p>Using the Proxy tool allows you to identify which requests are being used to detect the region where the content is being played. <\/p>\n\n\n\n<p>We start by looking at the HTTP history window in the Proxy tab when connecting to some streaming content at <a href=\"https:\/\/www.rtve.es\/play\/\">RTVE<\/a>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"25\" src=\"https:\/\/reversea.me\/wp-content\/uploads\/2024\/07\/rtve_protocol-1024x25.png\" alt=\"\" class=\"wp-image-716\" srcset=\"https:\/\/reversea.me\/wp-content\/uploads\/2024\/07\/rtve_protocol-1024x25.png 1024w, https:\/\/reversea.me\/wp-content\/uploads\/2024\/07\/rtve_protocol-300x7.png 300w, https:\/\/reversea.me\/wp-content\/uploads\/2024\/07\/rtve_protocol-768x19.png 768w, https:\/\/reversea.me\/wp-content\/uploads\/2024\/07\/rtve_protocol.png 1126w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Figure 1: Burp Suite HTTP Proxy window: RTVE Play website.<\/figcaption><\/figure>\n\n\n\n<p>In the HTTP history window of the Proxy tab, we observe that an MPD file (file with extension <code>.mpd<\/code>) is received just before the stream starts (see Figure 1). This <em>Media Presentation Description<\/em> (MPD) file contains the metadata required by a <a href=\"https:\/\/www.iso.org\/obp\/ui#iso:std:iso-iec:23009:-1:ed-5:v1:en\">DASH client<\/a> to begin streaming. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Netflix Streaming Protocol<\/h3>\n\n\n\n<p>The logs sent to <a href=\"https:\/\/www.netflix.com\">Netflix<\/a> via the <code>HTTP\/1.1 POST<\/code> request <code>\/log\/www\/cl\/2<\/code> show the information about the Playback features detected by Netflix. In this case, the DASH protocol is (probably) used, since HLS is not supported.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">{\n  \"name\": \"Playback FeatureDetection\",\n  \"data\":{\n    \"supportsHTML5withDRM\": \"yes\",\n    \"supportsHTML5\": \"maybe\",\n    \"determinedCapabilities\":true,\n    \"canPlayHTML5\":true,\n    \"canPlayHTML5WithDRM\":true,\n    \"canPlayFlash\": false,\n    \"TAG\": \"yes\",\n    \"TAG UA\":\"maybe\",\n    \"H264\": \"yes\",\n    \"WEBM\": \"yes\",\n    \"HLS\":\"no\",\n...\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">X Streaming Protocol<\/h3>\n\n\n\n<p>As for <a href=\"https:\/\/www.x.com\/\">X<\/a>, files with the <code>m3u8<\/code> extension are received (see Figure 2). These types of files provide the presentation description of the transport stream (<code>.ts<\/code> files), which will be presented for video display using the <a href=\"https:\/\/datatracker.ietf.org\/doc\/html\/rfc8216\">HLS protocol<\/a>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"948\" height=\"13\" src=\"https:\/\/reversea.me\/wp-content\/uploads\/2024\/07\/x_streaming.png\" alt=\"\" class=\"wp-image-715\" srcset=\"https:\/\/reversea.me\/wp-content\/uploads\/2024\/07\/x_streaming.png 948w, https:\/\/reversea.me\/wp-content\/uploads\/2024\/07\/x_streaming-300x4.png 300w, https:\/\/reversea.me\/wp-content\/uploads\/2024\/07\/x_streaming-768x11.png 768w\" sizes=\"auto, (max-width: 948px) 100vw, 948px\" \/><figcaption class=\"wp-element-caption\">Figure 2: Burp Suite HTTP Proxy window: X website.<\/figcaption><\/figure>\n\n\n\n<p>This is an example of the data transmitted:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">#EXTM3U\n#EXT-X-VERSION: 6\n#EXT-X-MEDIA-SEQUENCE: 0\n#EXT-X-TARGETDURATION: 4\n#EXT-X-PLAYLIST-TYPE: VOD\n#EXT-X-ALLOW-CACHE: YES\n#EXTINF:3.000,\n\/amplify_video\/1307522080829837313\/vid\/0\/3000\/480x480\/GBf65hNrxw6evl34.ts\n#EXTINF:3.000,\n\/amplify_video\/1307522080829837313\/vid\/3000\/6000\/480x480\/15HzH5hyk5r3KJQk.ts\n#EXTINF:3,000<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">YouTube Streaming Protocol<\/h3>\n\n\n\n<p>When accessing a <a href=\"https:\/\/www.youtube.com\/\">YouTube<\/a> video to watch it (for instance, <a href=\"https:\/\/www.youtube.com\/watch?v=dQw4w9WgXcQ\">this video<\/a>), the <code>HTTP\/2 GET<\/code> request returns an HTML document containing the following information about the webPlayer being used:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">\"webPlayerConfig\":{\n\"useCobaltTvosDash\":true,\n...\n}<\/code><\/pre>\n\n\n\n<p>Based on this information, we can deduce that <a href=\"https:\/\/www.iso.org\/obp\/ui#iso:std:iso-iec:23009:-1:ed-5:v1:en\">DASH<\/a> is used as the streaming protocol. No <code>mpd<\/code>, <code>m3u8<\/code>, or similar file sharing has been observed on YouTube.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">France TV Streaming Protocol<\/h3>\n\n\n\n<p><a href=\"https:\/\/www.france.tv\/\">France T\u00e9l\u00e9visions<\/a> (or France TV) is the French national public television broadcasting (it would be the equivalent of RTVE, but in France). It uses the DASH streaming protocol, since, as in RTVE, an <code>.mpd<\/code> file is received prior to the transmission of the content (see Figure 3).<\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"9\" src=\"https:\/\/reversea.me\/wp-content\/uploads\/2024\/07\/francetv_streaming-1024x9.png\" alt=\"\" class=\"wp-image-714\" style=\"width:2276px;height:auto\" srcset=\"https:\/\/reversea.me\/wp-content\/uploads\/2024\/07\/francetv_streaming-1024x9.png 1024w, https:\/\/reversea.me\/wp-content\/uploads\/2024\/07\/francetv_streaming-300x3.png 300w, https:\/\/reversea.me\/wp-content\/uploads\/2024\/07\/francetv_streaming-768x7.png 768w, https:\/\/reversea.me\/wp-content\/uploads\/2024\/07\/francetv_streaming-1536x13.png 1536w, https:\/\/reversea.me\/wp-content\/uploads\/2024\/07\/francetv_streaming-1440x12.png 1440w, https:\/\/reversea.me\/wp-content\/uploads\/2024\/07\/francetv_streaming.png 1867w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Figure 3: Burp Suite HTTP Proxy window: France TV website.<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Region Authorization Mechanisms<\/h2>\n\n\n\n<p>We will now explore how these platforms handle the process of determining whether the content the user wants to watch is allowed (or not) in the region they are connecting from.<\/p>\n\n\n\n<p>To check how these mechanisms work, we have connected from different IP addresses, located in different countries, as appropriate.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Netflix Region Authorization Mechanism<\/h3>\n\n\n\n<p>On Netflix, region checking is constantly done using <code>HEAD<\/code> requests to check if the client&#8217;s location is valid. For instance:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">HEAD \/?o=1&amp;v=111&amp;e=1718833213&amp;t=tJH7OwzGznYX... HTTP\/1.1<\/code><\/pre>\n\n\n\n<p>This request validates the IP and returns a response indicating whether it is valid or not. For instance, a response to a valid request looks like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">HTTP\/1.1 200 OK\nServer: nginx\nDate: Wed, 19 Jun 2024 09:44:09 GMT\nContent-Type: application\/octet-stream\nContent-Length: 90903911\nLast-Modified: Fri, 01 Dec 2023 06:03:52 GMT\nConnection: keep-alive\nTiming-Allow-Origin: *\nCache-Control: no-store\nPragma: no-cache\nAccess-Control-Allow-Origin: *\nAccess-Control-Expose-Headers: X-TCP-Info\nX-TCP-Info: addr=IP-VPN-VALID;port=48105;sc=\nAccept-Ranges: bytes<\/code><\/pre>\n\n\n\n<p>The <code>X-TCP-Info<\/code> header shows that the client IP is valid (i.e., <code>addr=IP-VPN-VALID<\/code>) and the streaming connection can continue. In contrast, a response to an invalid request looks like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">HTTP\/1.1 420 \nServer: nginx\nDate: Wed, 19 Jun 2024 09:52:59 GMT\nContent-Length: 0\nConnection: keep-alive\nX-Netflix-Geo-Check: failed\nTiming-Allow-Origin: *\nCache-Control: no-store\nPragma: no-cache\nAccess-Control-Allow-Origin: *\nAccess-Control-Expose-Headers: X-TCP-Info\nX-TCP-Info: addr=IP-NO-VALID;port=50912;sc=<\/code><\/pre>\n\n\n\n<p>The <code>X-TCP-Info<\/code> header now shows that the client IP is invalid (i.e., <code>addr=IP-NO-VALID<\/code>) and the streaming connection cannot continue. Note also that we now have another header, <code>X-Netflix-Geo-Check<\/code>, reporting that the geolocation check performed by Netflix appears to have failed.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">X Region Authorization Mechanism<\/h3>\n\n\n\n<p>X performs a region check on every <code>GET<\/code> request made to obtain the <code>.m4s<\/code> fragments (recall that X uses the <a href=\"https:\/\/datatracker.ietf.org\/doc\/html\/rfc8216\">HLS protocol<\/a>). If this request is made from an allowed region, X returns a response code <code>200<\/code>, transmitting part of the content. Additionally, one of the headers in this response (specifically, <code>X-Allow-Country<\/code>) indicates the regions where playback of the content is allowed. For instance:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">X-Allow-Country: us,um,id,ph,vn,th,mm,co,ar,pe,my,ve,au,tw,cl,ec,gt,ht,bo,do,hn,hk,py,la,sv,ni,sg,cr,nz,pa,uy,pr,mo,bn,gp,mq,gf,mf,bl<\/code><\/pre>\n\n\n\n<p>As shown, the content we use here as a case study can be broadcast in many countries. However, if the request is made from a restricted location, you will get a <code>403<\/code> response code. This response code indicates that the content is not available at that specific location. The <code>X-Allow-Country<\/code> is also contained in the response. In particular, an example of the response obtained is the following:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">HTTP\/2 403 Forbidden\nAccept-Ranges: bytes\nAccess-Control-Allow-Origin: *\nAccess-Control-Expose-Headers: Content-Length\nAge: 430505\nCache-Control: max-age=604800, must-revalidate\nContent-Type: text\/html\nDate: Wed, 19 Jun 2024 10:03:32 GMT\nLast-Modified: Sat, 06 Apr 2024 19:49:31 GMT\nPerf: 7402827104\nServer: ECAcc (mdr\/67D3)\nServer-Timing: x-cache;desc= ,x-tw-cdn;desc=VZ\nSurrogate-Key: amplify_video amplify_video\/bucket\/0 amplify_video\/1776699274350206976\nTiming-Allow-Origin: https:\/\/twitter.com, https:\/\/mobile.twitter.com\nX-Allow-Country: us,um,id,ph,vn,th,mm,co,ar,pe,my,ve,au,tw,cl,ec,gt,ht,bo,do,hn,hk,py,la,sv,ni,sg,cr,nz,pa,uy,pr,mo,bn,gp,mq,gf,mf,bl\nX-Cache: MISS\nX-Connection-Hash: d6a042657d63b53180289774a208a3fbb63176c9672b244744d0d4d4f4fb34e2\nX-Content-Type-Options: nosniff\nX-Response-Time: 13\nX-Transaction-Id: 807a16693aec5940\nX-Tw-Cdn: VZ\nX-Tw-Cdn: VZ\nContent-Length: 345\n\n&lt;?xml version=\"1.0\" encoding=\"iso-8859-1\"?&gt;\n&lt;!DOCTYPE html PUBLIC \"-\/\/W3C\/\/DTD XHTML 1.0 Transitional\/\/EN\"\n         \"http:\/\/www.w3.org\/TR\/xhtml1\/DTD\/xhtml1-transitional.dtd\"&gt;\n&lt;html xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\" xml:lang=\"en\" lang=\"en\"&gt;\n    &lt;head&gt;\n        &lt;title&gt;403 - Forbidden&lt;\/title&gt;\n    &lt;\/head&gt;\n    &lt;body&gt;\n        &lt;h1&gt;403 - Forbidden&lt;\/h1&gt;\n    &lt;\/body&gt;\n&lt;\/html&gt;<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">YouTube Region Authorization Mechanism<\/h3>\n\n\n\n<p>In the case of YouTube, the client makes a <code>POST<\/code> request when accessing the video content. The response includes an object called <code>playabilitystatus<\/code>, which indicates whether the content is allowed at the client&#8217;s location. If it is allowed, the state attribute of this object will be <code>OK<\/code>. On the contrary, if the content is restricted in that region, the status attribute will be <code>UNPLAYABLE<\/code>. Regardless of whether the content is allowed or not, the HTTP response code is always <code>200<\/code>. Therefore, the blocking status is determined on the client side by examining the content of the response rather than indicating it directly by its response code.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">France TV Region Authorization Mechanism<\/h3>\n\n\n\n<p>Geolocation authorization on France TV occurs continuously during <code>GET<\/code> requests made by clients to access <code>.dash<\/code> files. When these files are requested from a restricted location, the server responds with an HTTP error code <code>403<\/code>. Additionally, the <code>X-Errortype<\/code> header included in this response indicates the reason for the error. In this case, it is specifically tagged as <code>geo<\/code>. For instance, the response received from a restricted location looks like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">HTTP\/2 403 Forbidden\nServer: AkamaiGHost\nMime-Version: 1.0\nContent-Type: text\/html\nContent-Length: 752\nExpires: Thu, 20 Jun 2024 07:49:10 GMT\nCache-Control: max-age=0, no-cache\nPragma: no-cache\nDate: Thu, 20 Jun 2024 07:49:10 GMT\nAkamai-Mon-Iucid-Del: 624506\nAccess-Control-Allow-Origin: https:\/\/www.france.tv\nAccess-Control-Expose-Headers: X-ErrorType,X-Reference-Error,Akamai-Mon-Iucid-Ing,Akamai-Mon-Iucid-Del,Akamai-Request-BC\nAccess-Control-Allow-Headers: Range,CMCD-Request,CMCD-Object,CMCD-Status,CMCD-Session\nAccess-Control-Allow-Methods: GET,OPTIONS\nX-Errortype: geo<\/code><\/pre>\n\n\n\n<p>In this case, the method used to perform this control cannot be described in more detail, but it is evident that said control exists and that it is on the server side. This is evidenced by the fact that when the location changes to a restricted location, the streaming is interrupted.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">RTVE Region Authorization Mechanism<\/h3>\n\n\n\n<p>And how does RTVE work to block content? When you start playing content on RTVE Play, the following sequence occurs:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"54\" src=\"https:\/\/reversea.me\/wp-content\/uploads\/2024\/07\/rtve_geoblocking-1024x54.png\" alt=\"\" class=\"wp-image-712\" style=\"width:840px;height:auto\" srcset=\"https:\/\/reversea.me\/wp-content\/uploads\/2024\/07\/rtve_geoblocking-1024x54.png 1024w, https:\/\/reversea.me\/wp-content\/uploads\/2024\/07\/rtve_geoblocking-300x16.png 300w, https:\/\/reversea.me\/wp-content\/uploads\/2024\/07\/rtve_geoblocking-768x40.png 768w, https:\/\/reversea.me\/wp-content\/uploads\/2024\/07\/rtve_geoblocking-1536x81.png 1536w, https:\/\/reversea.me\/wp-content\/uploads\/2024\/07\/rtve_geoblocking-1440x76.png 1440w, https:\/\/reversea.me\/wp-content\/uploads\/2024\/07\/rtve_geoblocking.png 1830w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Figure 4: Geoblocking API calls in the first RTVE requests.<\/figcaption><\/figure>\n\n\n\n<p>As seen in Figure 4, several requests are made to the Geoblock API, which returns a JSON object indicating whether the content is available in the user&#8217;s region. For instance, a response from a valid location looks like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">\"page\": {\n  \"items\": [\n   {\n     \"id\":\"16143443\",\n     \"pubState\": \"ENPUB\",\n     \"disponible\": \"Y\",\n     \"desde\": \"01\/01\/1900 00:00\",\n     \"hasta\":\"01\/01\/2901 00:01\",\n     \"geolocalizadoCMS\":true\n   }<\/code><\/pre>\n\n\n\n<p>Note that the value of the <code>disponible<\/code> field (Spanish translation for <em>available<\/em>) is <code>Y<\/code>. In contrast, a response from an invalid location looks like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">\"page\": {\n  \"items\": [\n   {\n     \"id\":\"16143443\",\n     \"pubState\": \"ENPUB\",\n     \"disponible\": \"N\",\n     \"desde\": \"01\/01\/1900 00:00\",\n     \"hasta\":\"01\/01\/2901 00:01\",\n     \"geolocalizadoCMS\":true\n   }<\/code><\/pre>\n\n\n\n<p>After these Geoblocking API calls, as shown in Figure 4, there is a request for the <code>.mpd<\/code> file, which corresponds to the content you want to display. Region verification is also performed in this request. For instance, for the following request:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">GET \/43\/34\/16143443\/16143443_drm.mpd?idasset=16143443 HTTP\/2<\/code><\/pre>\n\n\n\n<p>We get a response similar to this (we left out some parts that are not interesting for our post). See the <code>X-Country<\/code> header:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">X-Cache: MISS, HIT\nX-Cache-Hits: 0, 3\nX-Timer: S1718719439.709317,VSO, VEO\nVary: Accept-Encoding\nX-Idasset: 16143443\nX-Country: es\nX-Original-Url: \/43\/34\/16143443\/16143443_drm.mpd?idasset=16143443\nX-Vel: 85\nX-Auth-Path: \/api\/geoblock\/16143443\/es.json<\/code><\/pre>\n\n\n\n<p>However, <em>this check is only done when the <code>.mpd<\/code> file is requested<\/em>. Subsequent content transmissions do not perform additional geolocation checks. As a result, if a user changes their location after the initial request for the MPD file, they can continue viewing the content without any further checks.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Quick Fix<\/h2>\n\n\n\n<p>To prevent unauthorized access, we propose modifying RTVE&#8217;s region authorization mechanism to ensure continuous location verification throughout subsequent content requests. A quick-and-dirty (but functional) solution would involve <em>adding the header<\/em> <code>X-Auth-Path: \/api\/geoblock\/XXX\/es.json<\/code> <em>to each content request<\/em>, where <code>XXX<\/code> represents the identifier of the updated geoblock API call. This would allow RTVE to detect changes in the client&#8217;s location and enforce geoblocking during content playback.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Responsable disclosure<\/h2>\n\n\n\n<p>Following the discovery of this vulnerability, we contacted the RTVE security team via email on July 5, 2024, providing technical details and reproduction steps. We allowed RTVE a three-month window to investigate and address the issue. Our goal was to ensure that RTVE could effectively rectify the vulnerability prior to the publication of this post, thereby safeguarding its content and maintaining compliance with regional access restrictions. After initial discussions where they acknowledged our disclosure, RTVE ceased further communication. As a result, we decided to proceed with the publication after waiting for the full three-month period.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p><em>And that\u2019s all, guys &amp; gals! In this post, we have analyzed the streaming protocols of several media companies <em>using Burp Suite<\/em> and identified issues with RTVE<\/em>&#8216;<em>s implementation of geoblocking. We discovered that geoblocking checks occur only during the initial MPD request, enabling users to bypass restrictions by changing their location after this verification. To fix this, we proposed adding continuous location verification throughout subsequent requests.<strong>About the authors: this post was originally written by <a href=\"https:\/\/www.linkedin.com\/in\/mario-romeo-lazaro\/\">Mario Romeo<\/a> and reviewed\/edited by Ricardo J. Rodr\u00edguez.<\/strong><\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p><span class=\"span-reading-time rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">Reading Time: <\/span> <span class=\"rt-time\"> 8<\/span> <span class=\"rt-label rt-postfix\">minutes<\/span><\/span>TL;DR Protocols like HTTP Live Streaming and MPEG-DASH are essential for efficient multimedia delivery and a seamless user experience, as they adjust content quality based on bandwidth. Streaming services using these protocols often impose regional content restrictions due to copyright or streaming rights. In this blog post, we explore how to analyze (with Burp) streaming [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[40,42,41],"tags":[43,45,44],"class_list":["post-710","post","type-post","status-publish","format-standard","hentry","category-network","category-vulnerability-analysis","category-web-apps","tag-unauthorized-access","tag-vulnerability-disclosure","tag-web-apps","no-featured-image"],"_links":{"self":[{"href":"https:\/\/reversea.me\/index.php\/wp-json\/wp\/v2\/posts\/710","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/reversea.me\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/reversea.me\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/reversea.me\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/reversea.me\/index.php\/wp-json\/wp\/v2\/comments?post=710"}],"version-history":[{"count":7,"href":"https:\/\/reversea.me\/index.php\/wp-json\/wp\/v2\/posts\/710\/revisions"}],"predecessor-version":[{"id":758,"href":"https:\/\/reversea.me\/index.php\/wp-json\/wp\/v2\/posts\/710\/revisions\/758"}],"wp:attachment":[{"href":"https:\/\/reversea.me\/index.php\/wp-json\/wp\/v2\/media?parent=710"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/reversea.me\/index.php\/wp-json\/wp\/v2\/categories?post=710"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/reversea.me\/index.php\/wp-json\/wp\/v2\/tags?post=710"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}