Ananth Vivekanand

Reverse-engineering Spotify traffic to block ads

I am a morning person and I love spotify. When I want to crack down on homework in the silent morning hours, Spotify wakes me up and gets me active. Although Spotify is great, its shocking amount of ads is not. This is a project to test out whether we can successfully block ads and other trackers in Spotify using some reverse-engineering and networking magic.

Note: this is 100% legal, just like using a browser ad-blocker. However, it violates Spotify TOS. This is mostly just for the lols.

Step 1: Reconnaissance #

Spotify loads its ads from the internet so we need to intercept the traffic to the spotify application and see if we can spot the ads in the captured traffic.

Spotify has a handy “proxy” setting, so I wrote a quick proxy in nodejs that’ll log all the traffic to the console. Then I pointed Spotify at this proxy via its settings.

🎉

Logging basic traffic

We now have a list of what requests spotify is making, now we just need to spot the ads.

Step 2: Filter #

Now, we need to find out which domains serve music and which domains serve the advertisements. If we figure it out, we can just have our proxy block the requests to the ad-domains. Luckily for us, there have been projects in the past to find out Spotify ad-domains. We just need to implement our filter now.

This is going to be our whitelist:

const whitelist = [
  "ap.spotify.com", // audio (access point)
  "*.ap.spotify.com", // resolved access points
  "apresolve.spotify.com", // access point resolving
  "audio-fa.scdn.co", // audio
  "audio-sp-*.pscdn.co", // audio
  "audio-sp-*.spotifycdn.net", //audio
  "audio4-fa.scdn.co", // audio
  "audio.simplecast.com", // podcast
  "charts-images.scdn.co", // charts images
  "content.production.cdn.art19.com", // podcast
  "daily-mix.scdn.co", // daily mix images
  "dailymix-images.scdn.co", // daily mix images
  "dealer.spotify.com", // discord rich presence
  "i.scdn.co", // cover art
  "image-upload.spotify.com", // image uploading
  "lineup-images.scdn.co", // playlists lineup images
  "media.simplecast.com", // podcast
  "merch-img.scdn.co", // merch images
  "mosaic.scdn.co", // playlist mosaic images
  "newjams-images.scdn.co", // release radar images
  "o.scdn.co", // cover art
  "pl.scdn.co", // playlist images
  "profile-images.scdn.co", // artist profile images
  "rss.art19.com", // podcast
  "seeded-session-images.scdn.co", // radio images
  "spclient.wg.spotify.com", // ads/tracking (blocked in blacklist), radio, recently played, friend activity,...
  "t.scdn.co", // background images
  "thisis-images.scdn.co", // 'this is' playlists images
  "video-fa.scdn.co" // videos
];

Any domain on this list is going to be serving music, so if we get a request for such a domain, we’ll proxy it through, no questions asked. Filtering on this list is as simple as a loop with an if statement.

🎉

Basic filtering!

No more ads!

Limitations #

It turns out that one domain, spclient.wg.spotify.com serves a lot of different things, including ads, music, and friends functionality. Similarly, login5.spotify.com serves some music but triggers the former domain to serve ads. We can have one but not both.

Our current setup already blocks 90% percent of ads, although with caveats like breaking friends functionality. However, let’s go for perfection! Right now, we’re filtering based off the domains that each request is made to. It would be nice if we could see the exact URL of each request, not just the domain.

However, SSL (the same lock in a browser that shows that a connection is secure) prevents middlemen from seeing the exact URL of a request and other details, such as the contents of a request.

Breaking SSL #

This is pretty tricky to setup. This is how to break SSL assuming that the target doesn’t already use certificate pinning:

  1. Create a fake Root Certificate Authority and trust it on the local computer
  2. Use the trusted CA to generate a certificate for domains.
  3. Now, when Spotify initiates an encrypted SSL connection to a domain, we use our proxy to masquerade as the domain and we feed spotify our fake SSL cert. Since we trusted our root CA, Spotify will accept our certificate and think its talking to the real domain, when in reality its the proxy.
  4. Since Spotify thinks its talking to the real domain, it’ll make its request as per normal. But since we now have the decryption key, we can see the full url, which we’ll use for filtering.
  5. If we decide to proxy the connection, we’ll ask the real domain for the resource (it’ll think that its talking to Spotify) and once we get the resource from the remote domain, we’ll feed it to Spotify using our fake certificate.

Man-in-the-middle filtering!

No more ads! For real this time! Where do we go from here? Turn it into a real project of course!