Reverse Engineering onemillionchessboards.com Frontend with DevTools to Build a Bot

A twitter user by the name of nolen has once again released another banger website, onemillionchessboards.com. Its not to hard to understand what this website is about….

TLDR: its 1 million chessboards… with some caveat rules that apply to make it more fun.

Nolen twitter post onemillionchessboards.com

I played around with it for a bit and enjoyed it but I was more interested about how things were built so I decided to look under the hood.

The creator shared on twitter a little of how is app works on the backend:

nolen twitter 'runs on a single server'

nolen twitter 'zstd-compressed protobufs'

nolen twitter onemillionchessboards.com backend details

On the front-end, I wanted to see if the user checks matched the back-end checks and while I didn’t verify everything, it was pretty robust.

So I did the next best thing and decided I would make a bot instead. The bot would make moves so fast it can survive easily and eat as many pieces as possible in the process.

Reversing applications using local JavaScript file overrides

The frontend is using React which we can see easily from the React Developer Tools Extension lighting up:

onemillionchessboards.com built in react

I actually don’t know how to use the react developer tools plugin so instead I decided to look at the raw JavaScript using the built-in Chromium DevTools and modify the functionality from there by overwriting the JavaScript file myself with a local file:

DevTools file override

With this I now have the ability to modify front-end code, add functionality, remove client-side security checks and what not. This makes it very convenient especially with applications that deal with web sockets as having to recreate the full initialisation, security checks and compression might not be easy to see from a simple web-socket proxy (ie. Burpsuite).

There s also a few built-in functionalities that help with reversing the code.

Pretty Printing

The code is minified but you have the ability to unminify it (pretty print) so that its easier to read and modify:

DevTools pretty print

Using JavaScript breakpoints

You can also add breakpoints in your custom file as such:

DevTools insert breakpoint

They will trigger when the code reaches your breakpoint and you can use it to see the values of each variables, which could help reversing the application:

DevTools trigger breakpoint

Note

It’s not a foolproof method and may end up crashing the tab when you make changes. In this case, its usually easier to close the tab and reopen it:

DevTools may cause tab crashes

Reversing the onemillionchessboards application

With these functionalities in mind, we can now look at reversing the frontend application code. I usually like to start with functions that talk to the backend since they’re the ones that will have an impact on the application in the end. So I search for any backend requests via WebSockets, HTTP(S) requests or else.

For this application, I found the function that sends a msg via a websocket to the backend when a move is made, the $1a465e5e0e1a643e$var$protoSendMove function:

onemillionchessboards.com protoSendMove function

The arguments are not minified so its fairly easy to guess what each argument does:

  • ws is the websocket itself
  • piece is the piece we want to move
  • toX is the x location we want to move to
  • toY is the y location we want to move to
  • moveType and moveToken are the only two interesting ones here since its uncertain what they mean.

By doing a bit more digging, we can find references to MoveType around the application and see that it’s basically an integer value that can denote a normal move, a castle move or en passant move:

onemillionchessboards.com MoveType constant

By checking where the $1a465e5e0e1a643e$var$protoSendMove function is called, we can see what moveToken is generated from the getIncrMoveToken function:

onemillionchessboards.com protoSendMove function call

Going one step further, we can see that it’s basically a token that gets sent with our request and gets incremented on every move made:

onemillionchessboards.com getIncrMoveToken function

So now we know how to make moves via code directly.

From here, I looked around and found a getMoveableSquares function that returns which squares you are allowed to move to but also which squares will end up capturing a piece which is really convenient since we can use that to prioritise our moves later on:

onemillionchessboards.com getMoveableSquares function

When looking at the Map returned by the getMoveableSquares function we can see the captured pieces:

onemillionchessboards.com getMoveableSquares returned value

With all that in mind we can make a function that moves a piece we selected to a nearby moveable square, prioritising squares that end up capturing an opponent’s piece:

onemillionchessboards.com custom move piece code

Note

This function could obviously be improved a lot more to make a more intelligent bot (eg. retrieve the full map and optimise moves based on that). Nevertheless, this is only an example to showcase what can be done by simply using built-in DevTools.

makeMove is a custom function which reuses the code from the message create function we found earlier:

onemillionchessboards.com custom makeMove function

Now that we have this, we just need to wrap the code in an interval so that it triggers often (while making sure we don’t trigger too much to overwhelm the server or have our moves dropped).

In the end, I decided to modify the $a2d3bef833187ce9$export$474cd6ee072cf5a4 function which is called when you select a chess piece as it already has all the handlers I need to make the bot work. It also allows means I don’t have to create a button or anything and can simply select any piece to start the bot.

I added a startConquest function here:

onemillionchessboards.com startConquest call inside `$a2d3bef833187ce9$export$474cd6ee072cf5a4` function

The function is basically an “interval” version of the function described previously:

onemillionchessboards.com custom startConquest function

gws is a global variable which holds the websocket object that I populate during initialisation. Tried to get it dynamically using $parcel$interopDefault but since its a react minified file, scopes are protected by exports and what not and I couldnt make it work so resorted to using a global variable (not ideal but its also only a PoC so not a huge problem).

The bot in action

In the end the result is pretty cool, I can just select any piece and they will start zooming around randomly, eating as many of the opposing pieces as possible.

Conclusion

DevTools are great to create PoCs, reverse engineer applications and can help when validating security checks, especially when dealing with web-sockets and the likes. They give a lot of power to the user and can drastically increase reverse engineering speed as they don’t require you to recreate the whole functionality to test things.

Overall, a great tool to have in your arsenal.