If I wanted to make a bot that plays/issues automatic actions/moves in a online browser game, whose communication is realized over HTTP1/1 and WebSocket (https and wws respectively), all TLS encrypted, what approach would you take?
Additionally the following constraints need to be considered:
- The bot should work while the player is actually playing. That means: Without any real action by a human, the bot won't do anything at all. But every action taken by the player should be modified on the fly by the bot, which calculates a better move/action and updates the packets accordingly (On which layer [TCP, HTTP, WebSocket this happens becomes important on the bottom approaches). Hence the size of the TCP packets won't change, but the checksum (and other fields that depend on the payload) may so.
- The source of the bot calulated actions is a local process. This means we need to obtain the data from a local process and can't integrate it into the browser process space (for example). We can interact with the local process in any possible way, such as IPC, sockets (client-server-architecture). But for the sake of simplicity I won't inject the process into the broser process space, since it seems hard and I don't know how to do it.
I have compiled the following list of procedures that differentiate in the layer or abstraction in where they alter the flow of communication of the game:
A high level approach would be to inject custom javascript into the DOM of a running browser session. This piece of javascript then connects to a local server (e.g. to a simple server listening on localhost:8888) that responses with a bot calculated move when requesting with the current game action history.
- Advantages: Comparably easy implementation (javascript is easy to code in). Works for all platforms and all browsers, because javascript is universally runnable.
- Drawbacks: Maybe we can't build a connection to localhost due to the same-origin policy (Can we?) This would render this approach impossible, since we absolutely need a way to communicate with the local program.
Another high level technique would be to write a custom browser extension that intercepts the browser game traffic and injects bot calculated traffic.
- Advantages: We don't need to hassle with SSL/TLS decryption because our application is executed in the browser process. Furthermore it must be somehow possible to communicate with a local process that supplies the extension with bot knowledge.
- Disadvantages: Platform and browser dependent code. When going down that road, I would need to write platform independent C code for chromium and firefox (Because I consider these two browsers to be most used). Additionally, it's questionable if we can speek to different processes, because of the browser sandboxes. What do you think?
Low level network stack interception technique: I could sniff on the interface where the TCP packets of the communication sessions are exchanged with a appropriate API like scapy or libpcap (When writing C directly). Because the communication is encrypted with SSL/TLS needs to be a reliable way to decrpyt the TCP packets. This is not a trivial task, since different browsers use differnt ciphers/HMACS to encrypt the traffic. This issue is alleviated by the fact that some browsers can be started with a option (environment variable SSLKEYLOGFILE) to dump the current key secrets of a SSL/TLS session relevant mdn link for SSLKEYLOGFILE to a file which my bot could use to decrypt the traffic. Once decrypted, I will modify the HTTP POST PAYLOAD (json data) and replace the action I made, with the bot calculated move/action. Then I need to encrypt the packet again with the appropriate cipher. But this implies several issues as for example:
- How can my sniffer/bot know which cipher was used? He only knows the current keys for the session, but not the ciphers. In the worst case, we need to also sniff the SSL/TLS handshake and connection start. This again is a very tedious process since the many quirks and combinatinos of a TLS session beginning.
- Padding shouldn't be a bigger issue because the moves in the packet are just replaced and no additional data is injected or deleted. So the TCP packets should stay the same. We only need to recalculate the checksums.
Another really really low level approach: Hooking! This might be the hardest approach in the investigation and research phase (It's not a easy task to find the correct hooking points in the HUGE chromium or firefox code base, but it looks like the NSS library is a good starting point), but also the most elegant way, because we don't need to hassle with SSL/TLS quirks and can work with plaintext HTTP packets, when we find a good hook point. This approach, although very elegant and straightforward comes with huge drawbacks:
- Hooking is very platform depended. On linux we could hook with kernel modules or the LD_PRELOAD trick. On Windows we'd need other hooking techniques like IAT hooking, using the microsoft hooking library or something else. Additionally, we need to know the target architecutre (Different between x86 and amd64). But from what I know now, this approch seems to be the best compared to the others, because we can work in the target process space (The browser userland process space itself) and we do not reassemble/decrypt packets ourselves (like in the previous technique). Form grabbers are a related technique used by blackhats for their trojans.
The traditional way: Reimplement the high level browser game protocol. This is not very elegant because our goal is to just modify packets. But when following this approach, we'd need to rebuilt the whole game logic (or at least many parts). This'd look something like the following:
import requests
login = requests.post('https://browsergame.com', {'login': user, 'password': pass}) if login.read().success(): requeset = request.get('https://browsergame.com/action.php?gamelogic=blabla') # Do implement game logic
So what do you think? I'm heavily inclined to the hooking approach, just because it is so elegant and involves sexy low level stuff...