During the past ruCTF I found particularly interesting the ESP (CardGame) exercise. ESP is a java implemented client-server card game, in which you have to find the correct ascending sequence of the displayed cards.
As usually happens during the exploiting process the attacker needs to understand how the software correctly works. So lets try to play some games and lets find out where the game provides us the flags we need to win the challenge. The flags are provided only when you correctly order all the 54 cards in the deck.
Once understood the game we are ready to decompile our code. Using JAD and handily parsing the decompiled JAR files we want to focalize our attention on two classes:
- CardGameClient.jar -> org.ructf.cardgame.client -> CardGameClient.class
- CardGameServer.jar -> org.ructf.cardgame.network -> CardGameProtocol.class
The first interesting class "CardGameClient.class" is called by the CardGameClientApp for initialize the game an it's called each time the user clicks on a given card for picking up a card. The following image (click on it to make it bigger) describes the starting game procedure. I used 3 colors in order to distinguish between:
- Bytes sent (RED color)
- Bytes received (GREEN color)
- Bytes processed (BLUE color)
The first action performed by the startGame() function is to send to the CardGame Server the byte representation of the "reset" string. After sending the "reset" string, the application sends the byte representation of "PLAY" and gets back a stream of bytes that calls deck1. Immediately after, the application uses an internal function called shuffle() to (try to guess :) ) shuffle the received deck, it creates a key vector of 20 randomly generated bytes (chars lower and upper case, no number nor symbols) it encodes the received deck (deck1) and it sends to the CardGameServer the encoded deck1.
CardGameClient waits for another incoming stream of bytes that calls deck2. It decodes deck2 by re-using the "single key" generated randomly in during previous deck (deck1). Now it generates a new key vector by randomly generate 20 bytes for each card (54 is the size of the deck) like in the previous key vector generation. It now encodes the second deck (deck2) using the new key vector and sends it to the CardGameServer. Finally it decodes the deck2 and keeps it clearly into the memory for later.
This was the initialization of the game. Now lets see the main game function: playgame().
The following picture shows the game procedure playgame().
The palyGame procedure sends to the CardGameServer the byte representation of the "PICK" string followed by: a number, representing the picked card, and 20 byte representing the key to be used to decode the card symbol. If the picked up card, decoded through the given key, matches to the one in the server, the server sends back to the CardGameClient application the string "LUCK" (number 3, in the previous picture). If the user picks up all the 54 cards in the deck in the right order the server sends back to the CardGame Client the string "WIN" followed by the Flags we need. This is easy understandable from the CardGameServer.jar -> org.ructf.cardgame.network -> CardGameProtocol.class.
Now we know how the game works. What we need is to find a way to cheat the CardGameServer in the way we always reach the state "WIN". There are plenty of ways to cheat this CardGameServer, but my favorite one is based on the key generation procedure. In few words... both of the key vectors, but most important the second one, is randomly generated in the client procedure ( code lines number: 4 and 7 in the starGame procedure ) what if we generate a static string that encodes and decodes each card ? If you do like that you are breaking the assumption of picking up the card in the right order. Since the key vector is associated to a specific card order, and each entry of the key vector is randomly generated, if you put to each key a fixed 20 bytes sequence it doesn't matter what card you choose because every card has been encoded with the static 20 bytes sequence. In other words whatever you choose will always be decoded in the server side, and it will result as good as if you really picked up the right card. Following my exploit.
I decided to implement a small CardGameClient using Java technology. I decided to use Java because I was able to reuse all the libraries and the decompiled code without write it from scratch. The following image shows my startGame() procedure
I used a fixed single key "ciao" to exchange the first deck. I later on used a single 20 bytes string ( "aaaaaaaaaaaaaaaaaaaa" ) to populate the key vector . I finally implemented a loop over the 54 cards (DECK_SIZE) to get 53 "LUCK" strings reaching the "WIN" state and getting from the server side the Flags.
This little CardGame Client was always winning. How to fix it up ? Well, the right way should be to invert the paradigm of the key generation. Instead of letting the client to decide the key vector the programmer should let the server to shuffle and to generate the key vector. I am not going deeper in this, since the post is already pretty big and probably everybody is getting bored :( . I'll post my impression on ruCTF later in another post.