Skip to main content

babygame01 picoCTF 2023 Solution

Walking off the left edge puts the player outside the map array, and the built-in auto-solve then carries you to the exit.

Published: April 26, 2023Updated: August 25, 2026

Description

Navigate the BabyGame binary to obtain the flag. Move to the top-left corner of the map and walk off the left edge to write your player character's value into the win variable. Press P to auto-navigate to the exit and collect the flag.

Run the binary locally to understand the map mechanics (chmod +x game && ./game).

When ready, connect to nc saturn.picoctf.net <PORT_FROM_INSTANCE> to obtain the real flag.

bash
chmod +x game && ./game
bash
nc saturn.picoctf.net <PORT_FROM_INSTANCE>

Solution

Want to try it yourself first?

The guided walkthrough reveals hints one step at a time.

Walk me through it
  1. Step 1Move to the top-left corner of the map
    Observation
    The win variable sits at a negative offset from the map array base. So reaching column 0 comes first, before any out-of-bounds write can land in the right place.
    Use 'w' to move up and 'a' to move left until you reach position (0, 0) - the top-left corner. The player starts at (4, 4), so press 'w' four times and 'a' four times to reach the corner. Your default player character is '@' (ASCII 0x40 = 64), which is exactly the value needed to satisfy the win condition.
    bash
    # After connecting, navigate to the top-left corner:
    bash
    wwwwaaaa
    What didn't work first

    Tried: Patching the binary so the player character is drawn as some other glyph before navigating to the corner.

    The win condition checks for exactly 64, which is the ASCII value of the default player character. Change that character to anything else and the out-of-bounds write stamps the wrong value, so the game never grants the flag. Leave it alone; the default already satisfies the check.

    Tried: Navigate to the bottom-right corner or some other edge instead of the top-left.

    The out-of-bounds write only fires when moving left past column 0, which means starting from the leftmost column. The top-left corner is the place, because the win variable sits at a negative offset from the map base. Other edges either have bounds checks or write into memory that matters to nothing.

    Learn more

    Reverse-engineering the binary in Ghidra shows that the player character is initialized to @ (0x40 = 64) by default. The win condition checks whether a variable on the stack equals 64, so no modification of the player character is needed at all - the default value already satisfies the check.

  2. Step 2Walk off the left edge of the map
    Observation
    There is no bounds check on the left edge, and the win variable sits exactly 4 bytes before the map array base. Four more left presses from the corner stamp the player character straight onto it.
    From the top-left corner at (0, 0), press 'a' four more times. The game has no bounds check on the left edge, so your position goes negative and the player stamp writes your character value (64) into the win variable, which sits 4 bytes before the start of the map array. The game prints Player has flag: 64 to confirm.
    bash
    # From the top-left corner, press 'a' four more times:
    bash
    aaaa

    Expected output

    Player has flag: 64
    What didn't work first

    Tried: Press 'a' only once or twice after reaching (0, 0) to trigger the win condition sooner.

    The win variable sits exactly 4 bytes before the map array base, so the write only reaches it on the fourth left press. Stop one, two, or three short and the player byte lands in a different stack slot while the win check still reads zero. All four are needed.

    Tried: Press 'a' five or more times after reaching (0, 0) to be safe.

    A fifth press writes past the win variable into adjacent stack memory, which usually segfaults and drops the server connection. The game has no recovery, so the session restarts from scratch. Exactly four presses, no more.

    Learn more

    The stack layout places the win variable 4 bytes before the start of the map array. Moving left from column 0 without a bounds check decrements the x coordinate to -1, -2, -3, -4, and at each step writes the player character byte to that negative offset relative to the map base. After 4 left presses, the write lands on the win variable and sets it to 0x40 (64). A fifth press causes a segmentation fault.

    This is an out-of-bounds write bug. The fix is a bounds check: if (x > 0) x--;. Without it, the player coordinates can go negative and clobber adjacent stack memory.

  3. Step 3Press P to auto-solve and reach the exit
    Observation
    After the out-of-bounds write, the game reports the player holding the flag value. The win condition is satisfied, so the built-in auto-navigate shortcut can walk the rest of the way to the exit.
    Once the win variable is set, press P to auto-navigate to the exit. Locally you will see "flag.txt not found", so repeat the moves remotely via netcat to have the server print the flag.
    Learn more

    Netcat (nc) is a raw TCP/UDP utility that connects stdin/stdout to a network socket. When a CTF challenge runs a binary on a remote server, nc host port lets you interact with it exactly as if you were at a local terminal - every keypress you type is sent over the wire and the server's output is streamed back.

    The reason the flag only appears remotely is that flag.txt exists only on the server's filesystem. Local runs teach you the mechanics; the remote session is where the actual secret is stored. This pattern is extremely common in CTF binary challenges: download and analyze locally, exploit remotely.

    For challenges that require many precise keystrokes, players often script the interaction with pwntools in Python (e.g., p.send(b'a' * 4)), which provides reliable, repeatable input over the same TCP connection.

Interactive tools
  • Cyclic Pattern GeneratorGenerate de Bruijn cyclic patterns and find buffer overflow offsets. The browser equivalent of pwntools cyclic and cyclic_find.
  • pwntools Payload BuilderPack integers into little-endian bytes (p32 / p64), unpack bytes back to integers, and build flat ROP payloads with offset-based insertion.

Flag

Reveal flag

picoCTF{gamer_m0d3_enabl...8e6}

The netcat session is required; local runs simply teach you the map layout.

Key takeaway

An out-of-bounds array write happens when a program updates an index without checking it stays inside the allocation. Adjacent stack variables sit at predictable offsets from the array base, so an unchecked negative or oversized index clobbers them with attacker-controlled data. The same root cause runs through Heartbleed's read overrun and countless CVEs in game engines, image parsers, and network daemons, wherever index arithmetic goes unvalidated.

Related reading

Useful tools for Binary Exploitation

Where to go next