Pachinko picoCTF 2025 Solution

Published: April 2, 2025

Description

The Pachinko exhibit hides two separate artifacts; this page covers flag one. Explore the website, submit circuits until the curators finally acknowledge you, and capture the flag that appears in the success toast.

Launch the instance to obtain your personalized website URL and port.

Optionally pull the provided server.tar.gz to inspect the frontend code, although it is not required for flag one.

Browse to the site and locate the "Submit Circuit" interaction.

bash
wget https://challenge-files.picoctf.net/c_activist_birds/7eac27979c12e4bd449f03e40a8492044221b7d2a96ac85f1150e30983c56eac/server.tar.gz
bash
tar -xvf server.tar.gz

Solution

Want to try it yourself first?

The guided walkthrough reveals hints one step at a time.

Walk me through it
Probabilistic-reward endpoints are a real (and weird) pattern in production web apps - the Web Challenges and Real-World Bug Patterns guide covers the DevTools workflow that catches the response no matter how fast the UI clears.
  1. Step 1
    Submit circuits and watch the network panel
    Observation
    I noticed the challenge description said to 'submit circuits until the curators finally acknowledge you,' which suggested the flag was gated behind a probabilistic server-side condition, making repeated automated requests via curl faster and more reliable than clicking by hand.
    Open DevTools > Network before clicking anything (closing DevTools or refreshing wipes the panel). Each "Submit Circuit" click POSTs JSON; on the order of 1 in 100 to 1 in 1000 submissions returns the flag, so a curl loop with a few hundred iterations is faster than clicking. A failed POST returns a payload like {"result":"You missed!"}; a winning POST contains picoCTF{...}.
    bash
    # Capture headers from one real submit (DevTools > right-click > Copy > Copy as cURL),
    # then fire many requests with timeout + retry + explicit success check:
    for i in $(seq 1 500); do
      body=$(curl -s --max-time 5 --retry 2 -X POST https://<host>/submit \
        -H 'Content-Type: application/json' -d '{}')
      echo "$body" | grep -ao 'picoCTF{[^}]*}' && break
    done

    Expected output

    picoCTF{p4ch1nk0_f146_0n3_e947...}
    What didn't work first

    Tried: Open DevTools after submitting a few requests and look for the flag in the Network tab.

    The Network panel only records requests made while it is open. Any submit that returned the flag before you opened DevTools is gone permanently. You must open DevTools and keep it docked before the very first submit; reopening it mid-session leaves a blind spot for all earlier responses.

    Tried: Run the curl loop without the grep check and inspect the output visually at the end.

    With hundreds of iterations the terminal output is hundreds of lines long and the flag line is easy to miss. The loop also keeps running past the winning request, wasting time. Piping each response through grep -ao 'picoCTF{[^}]*}' and breaking on match terminates the loop immediately and prints only the flag.

    Learn more

    Many web challenges implement probabilistic or counter-based reward logic server-side. The server may award a flag after a fixed number of submissions, at random with a certain probability, or when some hidden internal state is reached. Reading the server source (when available) lets you understand exactly which condition triggers the reward rather than clicking blindly.

    When source isn't available, browser DevTools are your best friend. The Network tab records every HTTP request and response, including the full JSON body. The capture is tab-scoped and tab-lifetime: closing DevTools clears the panel, refreshing the page clears it, and navigating away clears it. Keep DevTools docked open from the moment you start testing so nothing slips past.

    For challenges that require repeated interactions, curl in a shell loop is far faster than clicking manually. Always set --max-time per request and check the response body for an explicit success substring (here, picoCTF{) so the loop terminates as soon as a flag arrives instead of running to completion.

  2. Step 2
    Replay from the console
    Observation
    I noticed that the Network panel only records requests made while DevTools is open, so if the curl loop was unavailable or the session context was already established in the browser, pasting a fetch loop into the DevTools Console offered a way to automate submissions while preserving cookies and CSRF headers automatically.
    If you want to keep clicking through the real UI but speed it up, paste a fetch loop into the DevTools Console. Use the same fetch shape DevTools shows under "Copy as fetch" so cookies and CSRF headers are preserved.
    js
    // In DevTools Console, after at least one real submit:
    for (let i = 0; i < 500; i++) {
      const r = await fetch('/submit', { method: 'POST' });
      const t = await r.text();
      if (t.includes('picoCTF{')) { console.log(t); break; }
    }
    What didn't work first

    Tried: Use fetch('/submit', { method: 'POST' }) without copying the real request shape from the Network panel first.

    If the application requires a CSRF token or a specific JSON body structure, a bare POST to '/submit' will return a 400 or 403 rather than a valid game response. Right-clicking the real request in the Network panel and choosing 'Copy as fetch' gives you the exact headers, body, and credentials mode the server expects.

    Tried: Run the console loop while DevTools is undocked in a separate window, then close DevTools to check the UI for the winning toast.

    Closing the DevTools window terminates the console execution context - the loop stops immediately and any in-flight fetch is abandoned. Keep DevTools docked or in a side-by-side pane so the console JavaScript context stays alive for the full loop duration.

    Learn more

    The browser Network panel (accessible via F12 or right-click then Inspect) is one of the most powerful tools in web security research. It captures all HTTP/HTTPS traffic between the browser and server, including request headers, cookies, request bodies, response status codes, response headers, and full response bodies, even for requests that completed instantly.

    Clicking on any entry in the Network panel reveals a detail view with tabs for Headers, Payload, Preview, Response, Timing, and (for WebSocket connections) Messages. The Response tab shows the raw server response, making it possible to recover flag strings from dynamically generated content that was only briefly visible in the UI.

    For automated analysis, tools like Burp Suite or mitmproxy act as intercepting proxies between your browser and the server, logging every exchange with search and filter capabilities. These are standard tools in web penetration testing and are particularly useful when the flag appears in a response header or a non-displayed JSON field rather than in visible page content.

  3. Step 3
    Record flag one
    Observation
    I noticed the winning POST response contained the picoCTF flag string and the UI displayed it briefly as a toast, so capturing and copying the exact value immediately was necessary to avoid losing it when the toast dismissed.
    Once the pop-up finally includes picoCTF{...}, copy that value. That's flag one for Pachinko. Flag two appears only in the follow-up challenge, Pachinko Revisited.
    Learn more

    Multi-flag challenges are common in CTF competitions and mirror real-world multi-stage attack chains. Each flag typically unlocks the next phase of investigation, ensuring players understand each concept before moving on. In Pachinko, flag one demonstrates understanding of web interactions and response monitoring, while the revisited challenge presumably requires deeper source analysis or a different exploit vector.

    Keeping detailed notes during CTF challenges is valuable practice - writing down every endpoint you discover, every unusual response, and every hypothesis you explore. Many experienced CTF players maintain a structured notes file per challenge, which helps consolidate what you learned and makes the solution reproducible later.

Interactive tools
  • Regex TesterTest regular expressions against a string with live match highlighting, flag toggles, and common CTF pattern shortcuts.
  • Strings ExtractorPull printable text from any binary, library, or image. ASCII and UTF-16 detection, configurable minimum length, flag-like highlight, no command line needed.

Flag

Reveal flag

picoCTF{p4ch1nk0_f146_0n3_e947...}

There's no trick. The site randomly decides when to hand you the flag, and keeping the browser devtools open prevents you from missing it.

Key takeaway

Server responses are the ground truth for what an application actually returned, and the browser UI is only a filtered view of that truth. When a flag or sensitive value appears transiently in a JSON response body but the UI clears it immediately, browser DevTools Network panel or an intercepting proxy like Burp Suite is the reliable way to capture it. Scripting repeated requests with curl or a fetch loop in the DevTools console turns a tedious manual task into an automated one, and checking the raw response body for a known prefix eliminates false positives from UI state.

Related reading

Want more picoCTF 2025 writeups?

Useful tools for Web Exploitation

What to try next