Today’s article is about exploiting web game design’s flaws. We will be using the web game, Corsairs, to do a walk through of the common analysis method I used to find flaws in the game.
Corsairs is a web game that is usually played by Telegram messaging app users. When users click on the play button in Telegram, a tab will spawn in the browser, directing the user to the website. URL to Corsairs will embed the user’s game user ID.
For web games, the first thing we have to remember is that most games do not send scores back to the server all the time. Usually, the scores are sent after game over occurs. This approach will also be feasible so that the network traffic will not be that heavy. With this in mind, let us dive straight into the analysis steps that I did.
1. Network analysis
The first thing I did was to analyze the network files from the server. This can be achieved by right clicking on the page, select “Inspect Element (Q)” and navigate to “Network”.
Next, refresh the page and look at the files that are sent from the server.
After analysis, I wanted to look at the files send to the server to see the content and the address of the server when sending the high score. To do so, I tried to play a round and let the boat crash to end the game. However, no files were sent to the server (see Figure 1).
Figure 1: Network analyzed for high score file sent
Alternatively, you use Wireshark to see if there are any requests being sent out. I didn’t do that analysis but even if you do, you won’t find anything if that score is less than your highest score. In further analysis, you will know why.
2. HTML file analysis
To look at the source code of the page, you are able to see it in Inspector which can be navigated to from “Inspect Element (Q)”. After looking at the HTML source code, there is nothing much in the body section (see Figure 2a).
Figure 2a: HTML body section
Figure 2b: game.js?19 found in the head section of the HTML document
Figure 3a: nextLevel() function to go to the next level of the game
To see where is the started variable set to true, I used Ctrl+F to traverse quickly using “started” as the search word. After searching, I discovered that start() function is the main function to start the game and set started to true (see Figure 3b).
Figure 3b: start() start the game and set started to true
Remember earlier on we were wondering why the high score wasn’t sent? This is because it only sends the high score if the score is higher than the highest score the user has. This can be found near the end of the function die() (see Figure 3c).
Figure 3c: Send score restriction in die()
4 Test discovery
Despite discovering these function, testing have to be done to make sure it is able to work. To do so, navigate to “Inspect Element (Q)” and go to “Console”. In the console, input start() twice and nextLevel(). During this stage, I noticed that start() has to be called twice. The first time is to ready the game from the loading screen. The 2nd time start() is called will being that level. nextLevel() has to be called quickly before the bullet hits the boat. After inputting start() twice and nextLevel() quickly, we have moved on to level 2 successfully (see Figure 4).
Figure 4: Testing of start() and nextLevel() on console
To make this proof more concrete, I made a Proof-of-Concept (PoC) code and a video of it in action which can be found on my github. Click here to go the the PoC. Although in the code I tried to traverse the levels to get a high score, you can too use other methods such as modifying the score variable directly and send the score to the server using sendScore(). Since this is just a PoC, I coded in such a way that users will be able to see the process more clearly.
I hope this article will be useful for you. Feel free to comment below for any queries you have. You may also send me some tips if you like my work and want to see more of such content. Funds will mostly be used for my boba milk tea addiction. The link is here. 🙂