picoCTF
Rps [200 pts]
Challenge Description:
Here’s a program that plays rock, paper, scissors against you. I hear something good happens if you win 5 times in a row.
Connect to the program with netcat:
$ nc saturn.picoctf.net [port #]
The program’s source code with the flag redacted can be downloaded here.
We’re given a C source file to analyze. Taking a look at the play function, I noticed something interesting:
bool play () {
char player_turn[100];
srand(time(0));
int r;
printf("Please make your selection (rock/paper/scissors):\n");
r = tgetinput(player_turn, 100);
// Timeout on user input
if(r == -3)
{
printf("Goodbye!\n");
exit(0);
}
int computer_turn = rand() % 3;
printf("You played: %s\n", player_turn);
printf("The computer played: %s\n", hands[computer_turn]);
if (strstr(player_turn, loses[computer_turn])) {
puts("You win! Play again?");
return true;
} else {
puts("Seems like you didn't win this time. Play again?");
return false;
}
}
But, here’s what the strstr
function does according to documentation:
strstr
const char * strstr ( const char * str1, const char * str2 ); char * strstr ( char * str1, const char * str2 );
Locate substring
Returns a pointer to the first occurrence of str2 in str1, or a null pointer if str2 is not part of str1.
The matching process does not include the terminating null-characters, but it stops there.
So strstr
isn’t checking if the strings are equal. Rather, it’s checking if str2
is in str1
! In other words, it’s checking if loses[computer_turn]
is contained within player_turn. If it is, then we win that round.
We are also given the following:
char* loses[3] = {"paper", "scissors", "rock"};
Hence, the value of loses[computer_turn]
can only ever be one of these. Well, what if we just input paperscissorsrock? Then, the value of loses[computer_turn]
will always be a substring of the player input, and we will win every time!
Connect to the service and input paperscissorsrock 5 times in a row to get the flag!
picoCTF{50M3_3X7R3M3_1UCK_C85AF58A}