Corsairs power level

Dear readers,

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.

About Corsairs

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.

Analysis approach

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

The next thing that comes to my mind is the click event bind to the play button could be done in the JavaScript section as it was not found in the body section of the HTML code. Therefore, I looked at the head section of the HTML code and found the JavaScript file, game.js?19,  being used (see Figure 2b).


Figure 2b: game.js?19 found in the head section of the HTML document

3.   JavaScript file analysis

To analysis the file, right click on the page and select “View Page Source”. Scroll down the the <script> tag and click on game.js?19. Scroll around and look at the JavaScript file. While scrolling, I noticed variables of the game are declared in the JavaScript file. Scrolling down further, I noticed a very interesting function, nextLevel() (see Figure 3a red box).


Figure 3a: nextLevel() function to go to the next level of the game

This strikes me that the game logic and game data/variables are all in the JavaScript file without any obfuscation done. We can use nextLevel() to keep going to the next level to get a high score. Before trying to run this file, I studies nextLevel() to see if there are any conditions to run it. True enough, the started variable must be true for it run successfully (see Figure 3a blue box).

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. 🙂


Leave a Reply

Please log in using one of these methods to post your comment: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.