-
-
Notifications
You must be signed in to change notification settings - Fork 88
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Investigation on command acknowledgment request #140
Comments
Stopped working already. |
@sashahilton00 I have the ping/pong mechanism already figured out and also the messages/requests. I can assure you that the desktop client has a dealer connection because of the DNS requests (and further traffic) and the presence of related strings in the binary. The client simply refuses to connect the dealer via the proxy and bypasses it: I had the idea to place an "hardware proxy", but then the requests will be encrypted. The last idea that came to my head is to patch the executable just like it has been done for mercury requests: they don't seem to use a library and that makes everything more difficult. I also tried investigating through the help of an old Android device, but the device is kind of broken and I can't use it (could be a valid entry point). |
Hmmm, interesting. Will investigate further then. It sounds like patching and a nic proxy are the only options then. Just a thought, spin up macOS in VMWare and run Spotify in that (with the charles proxy cert installed and certificate pinning disabled, but without a system proxy), then use vmware to proxy all traffic from the vm through charles on the host machine. simulates a hardware proxy but without the messing around with extra hardware. |
You can't do that because (on Windows) the VM connects directly to the network via an adapter. |
Ah right. VMWare on mac gives the option of a shared mode where all the traffic is run through the host os network stack. Under what situation does the client connect to dealer.spotify.com? is it only when using spotify connect or something? |
It connects upon opening the client. |
and you're sure it's not just a DNS lookup then nothing else? I've just captured using wireshark on the mac version the client startup, and there's |
Ok can confirm the same behaviour on mac as well. weird. looks like it's going to be a case of an upstream proxy to get this working, will try the VM approach a bit later and see if we have any luck with that. I note that the dealer traffic appears not to exist on the iOS version of Spotify (at least when tested via Internet Sharing). |
No luck on the VM approach. I did manage to get the client to attempt to connect whilst running MITM proxy by running it in transparent mode with a few tweaks, but more bad news in that it didn't accept the mitmproxy certificate, whereas the other domains did, hence there's probably more patching needed to get the dealer traffic. |
Same on Android.
Yeah, that's what I am afraid of. It clearly uses another API of some sort. At one point I thought that we could check every string that is created by the application and the message would be somewhere in there. Don't quite know how to achieve such result. |
I've found a workaround. Just pretend that |
It's still happening for me with 3088373, each command is received three times |
|
I've built it myself on top of 3088373 and ran it with |
@sashahilton00 Not true. They are using LWS (https://libwebsockets.org/) on Windows and Mac. This should be patchable. Example client and Logging functions, will work on that tomorrow. |
Not sure which other API you're referring to, looks like a normal API to me. will look into it, at this point though I think we're definitely into patch territory :( |
I meant library, not API, sorry. At this point I think patching is more reliable when working. |
Have just done a bit of investigating, it looks like the reason behind the ssl certificate not being accepted is down to the fact that chromium embedded doesn't use the system keystore at all, instead they bundle all the root ca certificates with the framework. I think that we might be able to get around that by just creating a new root ca with the same parameters, then just swap it in for one of the existing ones. |
I am not entirely sure how the chromium platform works and how to replace the certificates, but if you could create a script that autonomously patches the binary, that would be awesome. |
Will look into it. Am busy for the next few days though. CEF (chromium embedded framework) is what Spotify desktop uses; the app is basically a series of browser windows. CEF is just the chromium engine packaged up in library format. You can see the library in Spotify > Contents > Frameworks on macOS. A quick analysis of the binary reveals that all the root certificates are included in the CEF library, so what I’ll try and do is create a new root CA and swap it in, then mitm. Will let you know how it goes
…--
Sent from Canary (https://canarymail.io)
On Monday, Oct 21, 2019 at 2:44 pm, Gianlu ***@***.*** ***@***.***)> wrote:
I am not entirely sure how the chromium platform works and how to replace the certificates, but if you could create a script that autonomously patches the binary, that would be awesome.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub (#140?email_source=notifications&email_token=AA752EV7FTVH74ZR4HWDX33QPWP2TA5CNFSM4I2ER6BKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEB2F5PI#issuecomment-544497341), or unsubscribe (https://github.com/notifications/unsubscribe-auth/AA752ETT4XWFEOJEKCW6DHTQPWP2TANCNFSM4I2ER6BA).
|
I really hope that your approach works because hooking LWS is more difficult than I thought since the library is statically linked and therefore the method names haven't been kept. There are some strings that reveal some debugging features that aren't enabled by default, but I have no idea how to enable it. |
I've run
|
Have tried a quick swap of the MITM proxy certificate into the framework, unfortunately no luck there. It looks like the client is pinning against a specific certificate issuer as opposed to anything in the CEF trust store. The next step in this is probably to just nuke all of the certificate pinning logic. There will be a function somewhere in Chromium that is basically an equivalence check that determines whether a certificate is valid or not, easiest approach would be to method swizzle that and have it always return true. Tried it with the usual suspects for macOS ( |
I have reverse engineered a bit the LWS library and can reliably intercept the so called callbacks, but I am yet to figure out how where the outgoing messages are in memory (due to lack of experience in assembly). I am afraid that openssl is statically linked inside the binary and therefore cannot intercept the calls to the most vulnerable functions. As Spotify loads it's own certificates when setting up (updated the main post), we could exploit Currently my best bet is to use https://github.com/arvinddoraiswamy/slid. |
Just had a look at the LWS code, |
@devgianlu which callbacks are you referring to? have just tried patching out the pinning checks, though i'm not sure if they're using LWS on macOS? |
It is, but you can't patch the functions directly because the library is statically linked. If you start it with |
That's a neat trick. I missed the part where you mentioned it is statically linked, though you might have some luck patching using The way LWS implemented events is somewhat irritating, as it's 1 callback function that handles all events, so |
The command ack string is as following:
Where P.S. I've noticed another message being sent, not sure what's about:
|
@devgianlu how did you mitm it in the end? Might be useful to know for future work |
No MITM, I had to debug step by step and look at the assembly to figure where the |
Hmm, unfortunate. Let’s hope we don’t need to deal with more of this nonsense anytime soon. |
librespot-java
receives commands from a Websocket endpoint called "dealer". These commands tell it what to do (load a playlist, pause/resume, change volume and more), but since 1.1.14 the client must send back "something" to let the server know that it has received the data correctly (I think). Not sending that ack results in receiving the command three times which is a bit annoying for next or previous actions, for example. I found some proto definitions inside the executable, that relate to my idea:There's also an options called
command_acks
in the device information (unluckily toggling it doesn't do anything):At this point, we need to find where the request should be sent which has been the major issue in a couple of weeks. Spotify seems to be working very hard on securing the desktop API: it uses TLS, certificate pinning and a Diffie-Hellman cipher.
There are two places where to search: the HTTPS API and the Hermes one. I've already inspected the second one and there's not a single call which may correlate to what I've said above.
The HTTPS is much harder to inspect, some calls are made with HTTP2 and can be decrypted with theThe "dealer" is suspected to be used as the ack endpoint as a packet is sent right after receiving the command. ATM no technique can be used to intercept that traffic.SSLKEYLOGFILE
technique, but others can't (I did not find any evidence in those ones that are being decrypted by Wireshark). I am now running out of ideas, but #105 won't be complete until this issue has been solved.I am mainly asking for help, if someone knows anything more than me about TLS and how to decrypt or even how to patch the executable. Thank you for your help, ask below if there's anything unclear. ANY help is appreciated.
MITM ideas
The
SSLKillSwitch
tool used to intercept the HTTPS traffic on Mac doesn't work with WebSocket probably because the certificate verification doesn't use the OpenSSL api.Patching ideas
The client uses the LWS library (https://libwebsockets.org/) on all clients. The library is statically linked inside the executable: patching is noticeably more difficult.
By starting Spotify with
/Applications/Spotify.app/Contents/MacOS/Spotify --show-console
, we get a pretty useful output which includes some "LWS got ..." corresponding to each LWS callback being called. We can confirm that the client sends something when receiving a command (LWS_CALLBACK_CLIENT_WRITEABLE
appears, indicating data being written). The client apparently loads its own certificates asLWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS
is being called too.The text was updated successfully, but these errors were encountered: