# shatranj

shatranj is a service, which consists of two parts:

• shatranj-server: backend written in Java which heavily relies on the Spring Framework.
• shatranj-frontend: frontend written in NodeJS. We didn’t look further into it.

## Backend

### Endpoints

The backend implements two unauthenticated endpoints defined in org.shatranj.diwana.shatranjserver.ShatranjServerController:

• POST /api/register: Registers a new user. (Who would have guessed)
• Body: username and password
• POST /api/players: This endpoint is not implemented at all.

And some authenticated (only accessible by logged-in users) ones (Authentication is done via Basic access Authentication. Username and Password must match a registered user):

• POST /api/initialize: Initialize a new game. (The game is basically chess against a computer. The only difference is you only play with one figure that can move like a queen and a knight and also acts as king)

• Body: name of the game (this is a flag store!)
• Response: Empty
• POST /api/move: Perform a move in the current game.

• Body: message parsed in a weird way to a DTO (more on that later). Intended to look like "move": "<from><to>" , e.g. "move": "e1e3".
• Response: Return value dependent on executed DTO.
• GET /api/pastgames: List past games for current user’s username.

• Response: List of information about all past games of current user’s name (including game’s name).
• GET /api/replay/{id}: Gets a replay of a game with given id.

• Response: List of all moves and game-states (does not include game’s name).
• POST /api/strategynote: Adds a note for the current user.

• Body: Content of the note. (Again parsed to a DTO in a weird way. This is a flag store!)
• Response: Created note DTO.
• GET /api/strategynote/{id}: Gets a note from the current user with given id.

• Response: Note DTO
• GET /api/Strategynotes: Gets all notes for the current user.

• Response: List of note DTOs

## Bug 0: Docker misconfiguration (Exposed Database)

When we look at the docker-compose.yml, we can see multiple interesting things:

1. The mysql database is exposed publicly (ports is used instead of expose)
2. The default root password is 123

Using this knowledge, it is easy to read all flags from the database. (It is also possible to change the database password 😉.)

## Bug 1: Duplicate usernames + /api/pastgames

This bug is pretty simple, yet we took way to long to find it:

When creating a user, there is no check whether a user with this name already exists:

and /api/pastgames returns all past games of all users with the same username as ours:

### Exploitation

Say a flag-id is abc. Then user abc has a flag stored as name of a past game. To get this flag, we register a new user with the same name (abc) and query /api/pastgames authorized as the new user to get all past games for username abc which will include the flag!

### Fix

The easiest way to fix this issue is to change /api/pastgames to match the full user instead of just the username:

## Bug 2: Improper DTO de-serialization

Before we look at the bug itself, let’s take a step back and check out the DTOs.

### DTOS

There are several DTOs defined:

• GameDTO: Game result.
• boardState
• winner
• name
• MoveDTO: Two moves.
• whiteMove
• blackMove
• boardStateBefore
• MoveReqDTO: Requested move.
• move
• StrategyNoteDTO: Note.
• message
• StrategyNoteReqDTO: Requested Note.
• id
• UserDTO: User.
• username
• UserRegisterDTO: Register User.
• username
• password

### DTO de-serialization: A right way

When we look at the the /api/register endpoint, we can see an example for proper DTO parsing:

This will automagically use the Spring framework to correctly parse a UserRegisterDTO from the Request body.

### DTO de-serialization: A wrong way

Now that we saw a right way to do it, let’s look at the /api/move endpoint:

move

1. \" is replaced with " in our message (quotes are un-escaped).
2. The resulting string is passed to the removeQuotes method which will remove the first and last " if the message starts and ends with "".
3. After this, it is checked, that the message contains "move": . (If it does not, the method returns an error.)
4. A JSON string input is created from our message: { message, "@class":"org.shatranj.diwana.shatranjserver.dto.MoveReqDTO"}
5. input is passed to handleDTOInput

handleDTOInput

1. Input is parsed as json to DTO.

2. Depending on subclass, different actions are chose.

• UserDTO: Return ids of all notes for username specified in DTO.

• StrategyNoteReqDTO: Return content of Note with id specified in DTO. (This might be a flag.)
• (…)

As the message is attacker controlled (passed as request body) and the first @class field determines the actual class during de-serialization, we can modify the JSON string, such that input will no longer be a serialized MoveReqDTO, but a DTO of our choosing.

To exploit this, we first craft a message that will be de-serialized into UserDTO (username is a flag-id again). From this, we will get the ids of all notes for this user. Then we can craft a StrategyNoteReqDTO to read the content of this note.

### Fix

The proper way to fix this issue would be handling the input the same way as the /api/register endpoint does. However, this is our CTF-grade fix: