Laureline's Wiki

Laureline's Wiki

Labo 02 - Lizord

Labo 02 - Lizord

The objective is to win to consecutive games of Rock-paper-scissors-lizard-Spock with a remote server. After 20 wins the server sends back a flag.

Part 1

By connecting to the server and playing for a while we can see that the server always play the same set of moves.

With this information in hand we can deduce the server exposes a vulnerability of type CWE-330 Insufficiently Random Values. This means we can create a script that tries a move then stores the counter to this move when it loses and tries the whole sequence again. This should allow us to break the complete sequence in a maximum of 20 tries.

Exploit

To exploit this vulnerability I have written a script that learns the move sequence by playing iteratively against the server advance to the next move.

# Move counters
counters = {
  "Paper": ("Scissors", "Lizard"),
  "Rock": ("Paper", "Spock"),
  "Lizard": ("Rock", "Scissors"),
  "Spock": ("Lizard", "Paper"),
  "Scissors": ("Spock", "Rock")
}
 
# Sequence of moves
sequence = [None] * 25
attempt = 0
while(True):
    # open server connection
    sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sk.connect(('<addr>', 9991))
 
    rx_data(sk) # get welcome message
    rx_data(sk) # get first prompt
 
    attempt += 1
    print("starting attempt", attempt)
    for idx in range(0, 25):
        # Get the move to play from the sequence
        move = sequence[idx]
        # If the correct move is not known, pick one at random
        if move is None:
            move = choice(list(counters.keys()))
        print("move", idx, "play", move, end=" ")
 
        try:
            # send move to server
            tx_data(sk, move)
            # get move result
            result = rx_data(sk).strip()
            if 'Draw' in result:
                print("draw", end=" ")
                win_move = move
            else:
                win_move, _, _ = result.split(' ')
 
            prompt = rx_data(sk).strip()
            if 'You lost' in prompt:
                print("lost to", win_move, end=" ")
                counter = choice(counters[win_move])
                print("counter is", counter)
                sequence[idx] = counter
                break
            else:
                print("wins")
                if 'Choisir' not in prompt:
                    print (prompt)
                if sequence[idx] is None:
                    sequence[idx] = move
 
        except:
            print("Error, last data:", last_rx)
            exit()
 
    # close socket
    sk.close()

Result

starting attempt 11
move 0 play Lizard wins
move 1 play Scissors wins
move 2 play Spock wins
move 3 play Scissors wins
move 4 play Rock wins
move 5 play Spock wins
move 6 play Scissors wins
move 7 play Lizard wins
move 8 play Spock wins
move 9 play Rock wins
move 10 play Lizard wins
move 11 play Spock wins
move 12 play Lizard wins
move 13 play Scissors wins
move 14 play Rock wins
move 15 play Lizard wins
move 16 play Paper wins
move 17 play Lizard wins
move 18 play Lizard wins
move 19 play Paper wins
Congratulations, you won 20 times in a raw !

Wow, you're clearly the best !
Take this as a present.

Here is your winning number: 217'384'040'199'995

Part 2

Exploit