In my opinion one of the most annoying things in games with online mode that after a while the publishers turn the servers off which makes the online part unusable in some cases. I thought this time I’ll save one game from extinction by creating a fake authentication server for it, though it turned out to be a much bigger effort than I first expected. But let’s start at the beginning…
I haven’t mentioned the game yet (which was the source of my motivation but by the end I was more proud of the solution 🙂 ) : Minecraft. Actually, you can use Minecraft multiplayer servers without
Mojang’s Microsoft’s authentication servers (offline mode) but in that way you have to give up whitelists, banlists and other lists which work with user ID-s. So I’ve decided to create an authentication server for it.
I knew I have complete control over the clients and the servers as the users want to use this alternative solution so there is no sneaky Man-in-the-middle solution in it (though this is the main concept). Apart from this, I wanted to create a patcher which works for many versions, even versions which come out in the future.
What did I have?
Fortunately, Mojang documented almost everything publicly to help the modder community and this helped me a lot. First, I’ve found the complete API documentation of the authentication server. Apart from that, I know the architecture which is similar to this figure:
What did I have to figure out?
Though I had the documentation, it wasn’t complete at all. There were quite a few missing parts, especially around the encryption and validation… obviously. And from the technical point of view, I still had to find a proper way for my Main-in-the-middle.
Creating my fake authentication server
I wanted to create an insanely fast server so I chose NodeJs for the job… and anyway, which other language would be more suitable for creating a REST-JSON endpoint?! For the first version I simply used a config file for the users as “database backend” but in the future integrating a MongoDB backend is obvious.
During the development I realized that the patching of the system is too complex for a simple step-by-step guide so I created a small application in Java to patch and manage everything. I picked Java, because it’s cross-platform and it’s probably installed on a system which runs Minecraft.
Breaching layers of security
The authentication API was created with security in mind, which is a good thing, though it caused me a few days of mindwork. 🙂 Let’s take a look at it one-by-one.
Domain + HTTPS (SSL)
The first layer of defense was the hard coded domain of the endpoint. As I wrote a bit earlier, I had complete control above the operating system so this one was not a big issue. I just added a new entry to my
hosts file and redirected the traffic for every endpoint to my server (to
localhost in this case).
Getting through the SSL layer was a bit trickier. I could only create a self-signed certificate as no one will sign my fake certificate… 🙂 obviously. Unfortunately, the Java client validated the certificate by default and it didn’t accept any traffic without a valid certificate. This issue took quite a time to solve but finally I found a loophole: you can trust in self-signed certificates. Actually, the solution is to download the server’s certificate and store it in the trusted hosts keychain. Kabumm, finally Java accepted my forged certificate. I had to lie about it a bit only. 🙂
As a little sidenote: here I started using Apache in front of NodeJs to create redirections. The complete API consists 3 domain names which require 3 different certificates. Splitting up the server wasn’t an option as NodeJs uses one port per instance (and I needed 3 virtual servers on port 80) and by using virtual servers I wouldn’t be able to provide different certificates for different domains. For this reason I chose Apache to be a proxy between the clients and the fake authentication server.
Okay, now we’ve arrived to a tougher layer: two method packed in a little piece of JSON response in BASE64 and signed the packet with a private key. I hoped that the client downloads the public key for the signature validation but unfortunately I found this in the Java Archive:
It looked easy to get by but replacing the key caused another issue.
Repacking the Jar file
First, I created a new entry in the patcher application to download a vanilla jar, unpack it, replace the public key with a new one and repack it. It wasn’t easy but at least it didn’t work at the end. Dammit. Then I saw that a hash is stored for every file in the jar:
So back to the patcher, calculate a new hash for my new public key, replace it in the manifest file and… my jar is still invalid. You may have noticed something more on the previous figure than just the hash. Yes, that’s right, more signatures!
Okay, so I had another problem. A different key (that .rsa file is a binary file, probably a key) and more hashes. I thought no way to recalculate and re-pack this one too, I’m too lazy for that. I knew these files are usually not part of a regular jar file so what would happen if I accidentally left them out of the archive? I’ll show you what happened:
Packet signature – again
So by finally replacing the public key I managed to sign the packets by myself with my own private key. With the replacement of the private key in two different jars I managed to bypass this layer completely. Wonderful.
With this step, I defeated every layer and my patched client worked with my patched server and my own fake authentication server like a charm. Let’s sum up what we have now.
What’s in the box?
At the end of the project 2 tools were created:
- The backbone of the system is the fake authentication server itself. You can add users, skins and capes to the server.
- A utility tool with which you can patch your own system and/or various other files which are involved in the authentication processes.
Of course there is a complete documentation about it so… what are you waiting for?! Go and try it out!