Welcome, challenger, to an intriguing analysis of a seemingly simple Rock-Paper-Scissors game. Our objective was to uncover a hidden flag, promised to anyone who could beat the computer five times in a row. What we found was a clever vulnerability stemming from string manipulation in C.

Understanding the Game’s Core

The game is implemented in C, featuring standard libraries for input/output, string handling, and time. Key arrays define the game’s logic:
char* hands[3] = {"rock", "paper", "scissors"};
char* loses[3] = {"paper", "scissors", "rock"};

The loses array is crucial: loses[0] is “paper” (what beats rock), loses[1] is “scissors” (what beats paper), and loses[2] is “rock” (what beats scissors). The game’s main loop allows players to either play a round or exit, tracking consecutive wins to award the flag after five victories.

The play() Function: Where the Magic (and Flaw) Happens

The heart of each round lies within the play() function. Here’s a breakdown:
1. Player Input: The game prompts the user for their selection (“rock”, “paper”, or “scissors”) and reads it into player_turn.
2. Computer’s Move: A random number generator (srand(time(0)) and rand() % 3) determines the computer’s move, stored in computer_turn.
3. Determining the Winner: The crucial line that decides victory is:
if (strstr(player_turn, loses[computer_turn])) { puts("You win! Play again?"); return true; }

The strstr() Vulnerability

The strstr() function in C searches for the first occurrence of a substring within another string. In this game, strstr(player_turn, loses[computer_turn]) checks if the player_turn string contains the string that would beat the computer’s choice.

Here lies the vulnerability: if the computer plays “rock” (computer_turn = 0), then loses[computer_turn] is “paper”. The strstr() function will then check if player_turn contains “paper”. It doesn’t verify if player_turn is exactly “paper” or “rock” or “scissors”. It simply looks for the substring.

Crafting the Winning Exploit

Given this behavior, we can construct an input that always results in a win, regardless of the computer’s random choice. Consider the string “rockpaperscissors”.
– If the computer plays “rock” (loses[0] is “paper”), “rockpaperscissors” contains “paper”. Player wins.
– If the computer plays “paper” (loses[1] is “scissors”), “rockpaperscissors” contains “scissors”. Player wins.
– If the computer plays “scissors” (loses[2] is “rock”), “rockpaperscissors” contains “rock”. Player wins.

By inputting “rockpaperscissors”, we essentially provide an input that satisfies the win condition for any possible computer move!

Achieving Victory and Retrieving the Flag

With this exploit in hand, the path to the flag was clear. We simply needed to repeatedly enter “rockpaperscissors” as our move. After successfully winning five consecutive rounds, the game recognized our persistent victory and revealed the elusive flag.

This exercise serves as a great reminder of the importance of precise string comparisons in programming, especially in contexts where game logic or security might be at stake. A simple strcmp() or exact match could have prevented this loophole, but strstr() opened the door for an unexpected win.

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.
You need to agree with the terms to proceed