Description
Win 5 rounds of rock-paper-scissors against a server-side randomized opponent. The vulnerability: the server checks if your move is a substring of the winning move strings, not an exact match.
Sending a string like 'rockpaperscissors' contains 'rock', 'paper', AND 'scissors' as substrings - so it always wins regardless of the server's choice.
Setup
Connect to the challenge server via netcat.
Enter 'rockpaperscissors' (or any string containing all three) as your move for each round.
nc saturn.picoctf.net <PORT_FROM_INSTANCE>Solution
Walk me through it- Step 1Understand the flawed input validationThe server checks for winning moves with strstr() or a substring test instead of an exact equality check, so any input that contains a winning word beats any choice.
Learn more
The vulnerable check in C looks something like:
if (strstr(player_input, "rock")) { /* player chose rock */ }strstr(haystack, needle)returns a pointer if needle appears anywhere inside haystack, not just if they match exactly. A string like"rockpaperscissors"passes all three checks simultaneously.The correct check would use
strcmp(player_input, "rock") == 0for exact equality, or in Pythonif player_input == "rock". This type of bug - using a substring test where equality is intended - is a common logic error. - Step 2Win all 5 roundsSend 'rockpaperscissors' for each round. The server picks randomly among rock, paper, or scissors; your input always beats all three.bash
# Manual: connect with nc and type 'rockpaperscissors' for each roundpythonpython3 -c " from pwn import * p = remote('saturn.picoctf.net', <PORT_FROM_INSTANCE>) for _ in range(5): p.sendlineafter(b'choice:', b'rockpaperscissors') print(p.recvall().decode()) "Learn more
The winning logic for standard rock-paper-scissors is: rock beats scissors, scissors beats paper, paper beats rock. Normally, guessing the right move against a random opponent gives you a 1-in-3 chance per round, for a 1/243 chance of winning 5 in a row. The substring bug eliminates all randomness.
This type of vulnerability - where input validation logic can be bypassed by crafting input that satisfies multiple conditions simultaneously - appears in real-world web applications too. For example, WAF (Web Application Firewall) bypass techniques often embed the forbidden string inside a larger string that the WAF's regex doesn't match but the backend does process.
- Step 3Collect the flag after winning 5 roundsAfter winning 5 consecutive rounds, the server prints the flag.
Learn more
The automated Python script loops 5 times, sending the magic string each round.
sendlineafter(b'choice:', ...)waits for the server's prompt before sending the next input, handling any timing between rounds.For interactive manual play, you can also type
rockpaperscissorseach time when prompted. Either approach works in under a minute.
Flag
picoCTF{50M3_3X7R3M3_1UCK_C8...}
The server uses strstr() instead of strcmp() to check your move. 'rockpaperscissors' contains all three winning substrings and beats any choice.