loading...

Cracking Spotify Codes and making a quest out of it

ale profile image Alexander Pushkov ・5 min read

One long sleepless night, me and a friend of mine (@koteeq) had a discussion about Spotify Codes. Those are the barcodes that you can scan with a Spotify app on your phone:

Firefly by Think Twice

#np: Firefly by Think Twice (which is also available on Soundcloud if you don't have Spotify but that's not the point)

There's a dedicated website for generating these сodes, and an URL of the image above looks like this: https://scannables.scdn.co/uri/plain/png/000000/white/640/spotify:track:1er51HCEBhIAtoEemKuC3M. You can even get an SVG, which is way easier to analyze:

<rect x="100.00" y="44.50" width="6.71" height="11.00" rx="3.36" ry="3.36" fill="#ffffff"/>
<rect x="112.42" y="23.50" width="6.71" height="53.00" rx="3.36" ry="3.36" fill="#ffffff"/>
<rect x="124.84" y="37.50" width="6.71" height="25.00" rx="3.36" ry="3.36" fill="#ffffff"/>
<rect x="137.27" y="23.50" width="6.71" height="53.00" rx="3.36" ry="3.36" fill="#ffffff"/>
<rect x="149.69" y="34.00" width="6.71" height="32.00" rx="3.36" ry="3.36" fill="#ffffff"/>
<rect x="162.11" y="41.00" width="6.71" height="18.00" rx="3.36" ry="3.36" fill="#ffffff"/>
<rect x="174.53" y="23.50" width="6.71" height="53.00" rx="3.36" ry="3.36" fill="#ffffff"/>
<rect x="186.96" y="44.50" width="6.71" height="11.00" rx="3.36" ry="3.36" fill="#ffffff"/>
<rect x="199.38" y="34.00" width="6.71" height="32.00" rx="3.36" ry="3.36" fill="#ffffff"/>
<rect x="211.80" y="41.00" width="6.71" height="18.00" rx="3.36" ry="3.36" fill="#ffffff"/>
<rect x="224.22" y="27.00" width="6.71" height="46.00" rx="3.36" ry="3.36" fill="#ffffff"/>
<rect x="236.64" y="20.00" width="6.71" height="60.00" rx="3.36" ry="3.36" fill="#ffffff"/>
<rect x="249.07" y="37.50" width="6.71" height="25.00" rx="3.36" ry="3.36" fill="#ffffff"/>
<rect x="261.49" y="44.50" width="6.71" height="11.00" rx="3.36" ry="3.36" fill="#ffffff"/>
<rect x="273.91" y="37.50" width="6.71" height="25.00" rx="3.36" ry="3.36" fill="#ffffff"/>
<rect x="286.33" y="37.50" width="6.71" height="25.00" rx="3.36" ry="3.36" fill="#ffffff"/>
<rect x="298.76" y="23.50" width="6.71" height="53.00" rx="3.36" ry="3.36" fill="#ffffff"/>
<rect x="311.18" y="41.00" width="6.71" height="18.00" rx="3.36" ry="3.36" fill="#ffffff"/>
<rect x="323.60" y="20.00" width="6.71" height="60.00" rx="3.36" ry="3.36" fill="#ffffff"/>
<rect x="336.02" y="37.50" width="6.71" height="25.00" rx="3.36" ry="3.36" fill="#ffffff"/>
<rect x="348.44" y="30.50" width="6.71" height="39.00" rx="3.36" ry="3.36" fill="#ffffff"/>
<rect x="360.87" y="27.00" width="6.71" height="46.00" rx="3.36" ry="3.36" fill="#ffffff"/>
<rect x="373.29" y="44.50" width="6.71" height="11.00" rx="3.36" ry="3.36" fill="#ffffff"/>

Here we can see that there's 8 variants for each bar: 11, 18, 25, 32, 39, 46, 53, and 60 pixels tall. We noticed that the first and the last bar are always 11 px (most certainly used along with the logo size to calibrate the scanner), so this leaves us with 21 bars × 3 bits per bar = 63 bits of information per barcode – clearly less than in a Spotify track ID, so some kind of a lookup table must be used.

It was at this point we ran into Spotify's Patent (US, EU) with more details about the actual encoding (e. g. Gray codes are used, which reduce possible errors when a bar's height is off by one).

But wait, what if we try to get a barcode for some invalid data?

So we did:

  • an empty string didn't work,

  • a random word didn't work,

  • https://dev.to/ didn't work too, however

  • spotify:track:helloDEV works!

helloDEV by ??????

This one will most certainly not lead anywhere meaningful, but is technically a correct Spotify Code

Although it doesn't open up a Spotify page, at least it should query the code database (that looktype from earlier, remember?). So my friend ran Spotify through a reverse proxy and tried scanning one of those invalid codes, and

some Fiddler probably idk

It worked! So you really can use these codes for anything, although Spotify can revoke them at any time, so beware.

For bonus points, I tried to get a working barode (i. e. pointing to an actual track) with some extra information in there. My guess was that, given that spotify:something:something strings are called URIs, they would be parsed as URIs and that we can append a query string to it. Obviously, just appending ?hello=dev would send the query string to the scannables.scdn.co server and it would just return the original code, but even URL-encoding the parameter didn't help. Strangely enough though, after double URL-encoding:

https://scannables.scdn.co/uri/plain/png/000000/white/640/spotify:track:1er51HCEBhIAtoEemKuC3M%253Fhello=dev

Firefly but a bit extra

it worked! You can scan it with an app and get a track, and if you look at the traffic you'll see our secret message inside.

This led us to creating a couple mini-quests: I posted one to a local DEFCON chat (37517), and @koteeq to her telegram channel. Our riddle was cracked in mere 42 minutes, which was really impressive (but we didn't expect it to last longer that a couple hours anyway :-)

Practical implications

Storing garbage in Spotify this way is unlikely to make Spotify Codes overflow, as it would require sending 2⁶³ HTTP queries which doesn't really seem practical (and besides, Spotify can revoke codes at any point, so even if you try that, your codes would likely be erased soon anyway).

In theory, one could write up an unofficial scanner and make it read these embedded messages (e. g. to make Spotify Codes for arbitrary HTTP links), but this is already solved by more open and independent systems like QR (albeit not as fancy-looking).

In my opinion, this is only useful as an exercise in systems security and maybe data encoding, but was still a fun thing to play around with!


Loosely based on @koteeq's post on Habr (in Russian).

Posted on by:

Discussion

pic
Editor guide