Prevent MITM by authenticating Spotify servers #641
Replies: 15 comments 2 replies
-
Comment by plietar The connection does not use TLS at all, so the certificate Spotify uses for their website is irrelevant. |
Beta Was this translation helpful? Give feedback.
-
Comment by s1lvester
Did you eavesdrop on the connection and auth-negotiation of a certified device? Our lab just got an onkyo-avr that has built-in spotify-support - so if need be I could try to set up wireshark to have a look at the connections beeing made during zeroconf auth. |
Beta Was this translation helpful? Give feedback.
-
Comment by plietar I'm not quite sure which part of the protocol you're referring to. There are basically 3 network connections :
Zeroconf authentication only runs on your local network, and is encrypted (not authenticated, but there's no way of doing that since you don't know the device anyway). Your password is never sent though, only a token. Resolving the AP uses http, with optional TLS. librespot currently does not use https for reasons explained in #124. Encryption is pretty pointless, and like I explained there, there's no point authenticating that part if the connection to the AP in not authenticated, and similarily, authenticating it isn't necessary if we authenticate the next part. Finally authenticating the connection to the AP is what I would like to implement, and is what this issue is about. This is a proprietary protocol, with it's own exotic encryption and authentication. It isn't based on SSL/TLS at all, and makes no use of certificates. Encryption is already implemented (that was necessary to get it working), but authentication isn't, and I'm not 100% sure how it is done, but my comment just above describes my initial thoughts on it. The next steps would be disassemble the relevant part from Spotify's binaries, figure out the crypto algorithm, where the inputs come from, build a simple MITM and see how the client behaves, and implement it here. |
Beta Was this translation helpful? Give feedback.
-
Comment by timniederhausen If you're still interested in this:
Here's a simplified version of the code I use to verify the signature: static const unsigned char server_key_0_n[] = {
0xac, 0xe0, 0x46, 0x0b, 0xff, 0xc2, 0x30, 0xaf, 0xf4, 0x6b, 0xfe, 0xc3,
0xbf, 0xbf, 0x86, 0x3d, 0xa1, 0x91, 0xc6, 0xcc, 0x33, 0x6c, 0x93, 0xa1,
0x4f, 0xb3, 0xb0, 0x16, 0x12, 0xac, 0xac, 0x6a, 0xf1, 0x80, 0xe7, 0xf6,
0x14, 0xd9, 0x42, 0x9d, 0xbe, 0x2e, 0x34, 0x66, 0x43, 0xe3, 0x62, 0xd2,
0x32, 0x7a, 0x1a, 0x0d, 0x92, 0x3b, 0xae, 0xdd, 0x14, 0x02, 0xb1, 0x81,
0x55, 0x05, 0x61, 0x04, 0xd5, 0x2c, 0x96, 0xa4, 0x4c, 0x1e, 0xcc, 0x02,
0x4a, 0xd4, 0xb2, 0x0c, 0x00, 0x1f, 0x17, 0xed, 0xc2, 0x2f, 0xc4, 0x35,
0x21, 0xc8, 0xf0, 0xcb, 0xae, 0xd2, 0xad, 0xd7, 0x2b, 0x0f, 0x9d, 0xb3,
0xc5, 0x32, 0x1a, 0x2a, 0xfe, 0x59, 0xf3, 0x5a, 0x0d, 0xac, 0x68, 0xf1,
0xfa, 0x62, 0x1e, 0xfb, 0x2c, 0x8d, 0x0c, 0xb7, 0x39, 0x2d, 0x92, 0x47,
0xe3, 0xd7, 0x35, 0x1a, 0x6d, 0xbd, 0x24, 0xc2, 0xae, 0x25, 0x5b, 0x88,
0xff, 0xab, 0x73, 0x29, 0x8a, 0x0b, 0xcc, 0xcd, 0x0c, 0x58, 0x67, 0x31,
0x89, 0xe8, 0xbd, 0x34, 0x80, 0x78, 0x4a, 0x5f, 0xc9, 0x6b, 0x89, 0x9d,
0x95, 0x6b, 0xfc, 0x86, 0xd7, 0x4f, 0x33, 0xa6, 0x78, 0x17, 0x96, 0xc9,
0xc3, 0x2d, 0x0d, 0x32, 0xa5, 0xab, 0xcd, 0x05, 0x27, 0xe2, 0xf7, 0x10,
0xa3, 0x96, 0x13, 0xc4, 0x2f, 0x99, 0xc0, 0x27, 0xbf, 0xed, 0x04, 0x9c,
0x3c, 0x27, 0x58, 0x04, 0xb6, 0xb2, 0x19, 0xf9, 0xc1, 0x2f, 0x02, 0xe9,
0x48, 0x63, 0xec, 0xa1, 0xb6, 0x42, 0xa0, 0x9d, 0x48, 0x25, 0xf8, 0xb3,
0x9d, 0xd0, 0xe8, 0x6a, 0xf9, 0x48, 0x4d, 0xa1, 0xc2, 0xba, 0x86, 0x30,
0x42, 0xea, 0x9d, 0xb3, 0x08, 0x6c, 0x19, 0x0e, 0x48, 0xb3, 0x9d, 0x66,
0xeb, 0x00, 0x06, 0xa2, 0x5a, 0xee, 0xa1, 0x1b, 0x13, 0x87, 0x3c, 0xd7,
0x19, 0xe6, 0x55, 0xbd
};
bssl::UniquePtr<RSA> rsa(RSA_new());
rsa->n = BN_bin2bn(server_key_0_n, sizeof(server_key_0_n), nullptr);
rsa->e = BN_new();
BN_set_word(rsa->e, 65537);
std::uint8_t gs_hash[20];
SHA1(gs.data(), gs.size(), gs_hash);
if (1 != RSA_verify(NID_sha1, gs_hash, sizeof(gs_hash),
gs_sig.data(), gs_sig.size(), rsa.get())) {
// failed
} |
Beta Was this translation helpful? Give feedback.
-
We should probably implement this at some point. |
Beta Was this translation helpful? Give feedback.
-
@timniederhausen Can you confirm this still works? The server key seems to be the same as it's still present inside the client. |
Beta Was this translation helpful? Give feedback.
-
Yes, nothing's changed. My MITM tool still works (if I replace the public key in the client). |
Beta Was this translation helpful? Give feedback.
-
What do you mean by that? |
Beta Was this translation helpful? Give feedback.
-
Instead of hooking functions in the client to dump the packets it sends/receives, I built a small tool that acts as a MITM between the Spotify client and the AP. For that to work, I have to replace the public key in the client with my own, as otherwise the MITM tool cannot create a correct All of that still works, which means they haven't changed that process. |
Beta Was this translation helpful? Give feedback.
-
Got it. Are you able to provide some debug values ( Sorry for bothering you. |
Beta Was this translation helpful? Give feedback.
-
I'm not sure what you mean. If you really need some example values (all hex-encoded):
I guess you could use Java's Signature class to do the verification but I haven't really looked into that. |
Beta Was this translation helpful? Give feedback.
-
@timniederhausen the proxy you have sounds interesting. I assume you're using it in conjunction with sp-analyze to examine the packets in realtime? |
Beta Was this translation helpful? Give feedback.
-
Thank you @timniederhausen, I finally managed to implement it (librespot-org/librespot-java@aa17da7) |
Beta Was this translation helpful? Give feedback.
-
So far I'm only dumping all packets (including the initial ones, i.e. ClientHello etc.) to a I could post some binaries of the MITM tool if you're interested. |
Beta Was this translation helpful? Give feedback.
-
Hi there, I wrote a MITM proxy a while back and have just opened sourced it. As part of that, I wanted to figure out how to calculate gs_signature. Here it is! https://github.com/ahixon/spotify-mitm-proxy#gs_signature The code probably does a better job of describing how it fits together, though. It's been a while since I wrote that code (2017!) so I only really remember bits of pieces. Thankfully that bit is annotated fairly clearly. Best of luck! :) |
Beta Was this translation helpful? Give feedback.
-
Issue by plietar
Thursday Oct 27, 2016 at 12:29 GMT
Originally opened as plietar/librespot#125
librespot doesn't have any way of authenticating Spotify's server, and will happily send the username and password to a MITM proxy. I don't know what the official client does about it.
Beta Was this translation helpful? Give feedback.
All reactions