Who are you? picoCTF 2021 Solution

Published: April 2, 2026

Description

Only those who use the official PicoBrowser are allowed on this site.

Remote

Access the challenge URL and observe what the server demands.

bash
curl http://mercury.picoctf.net:38322/

Solution

Want to try it yourself first?

The guided walkthrough reveals hints one step at a time.

Walk me through it
  1. Step 1
    Set the User-Agent to picobrowser
    Observation
    I noticed the challenge description explicitly says 'Only those who use the official PicoBrowser are allowed on this site,' which suggested the server checks the User-Agent header and that setting it to 'picobrowser' would be the first gate to pass.
    The server checks User-Agent first. Set it to picobrowser. Each failed check returns the next requirement in plain text.
    bash
    curl --user-agent "picobrowser" http://mercury.picoctf.net:38322/
    What didn't work first

    Tried: Sending -H 'User-Agent: PicoBrowser' with capital letters

    The server does a case-sensitive string match against 'picobrowser' (all lowercase). Sending 'PicoBrowser' or 'Picobrowser' returns the same 'Sorry, you can only access this server with picobrowser.' error. The --user-agent curl flag sets the header value exactly as typed, so the exact lowercase string is required.

    Tried: Using a browser with a User-Agent switcher extension and navigating to the URL

    Browser extensions that spoof User-Agent work for the first check but the browser cannot easily set arbitrary Referer, Date, DNT, X-Forwarded-For, or Accept-Language headers on a plain navigation request. You will pass the first gate and stall at the second. curl with explicit -H flags is the right tool because it gives full control over every header in the request.

    Learn more

    The User-Agent header identifies the client software. Servers cannot trust it. Check responses look something like:

    # Wrong UA
    "Sorry, you can only access this server with picobrowser."
    
    # Right UA, wrong Referer
    "Sorry, you can only access this website by clicking through from our official site."
    
    # Right UA + Referer, wrong Date
    "Sorry, this site only worked in 2018."

    Each error message points at the next required header. Read it carefully and add one header per request. See web bug patterns for why header-based access controls always lose.

  2. Step 2
    Add Referer, Date, DNT, XFF, Accept-Language
    Observation
    I noticed the server returned a new plain-text error message after each header was accepted, describing the next missing requirement, which suggested working through the remaining five checks (Referer, Date, DNT, X-Forwarded-For, Accept-Language) one at a time until all gates passed.
    Six checks total. Add each header one at a time, reading the next error message between requests. The full set: Referer matches the site itself, Date in 2018, DNT 1, X-Forwarded-For from a Swedish IP, Accept-Language Swedish.
    bash
    curl http://mercury.picoctf.net:38322/ \
      --user-agent "picobrowser" \
      --referer "http://mercury.picoctf.net:38322/" \
      -H "Date: Mon, 23 11 2018 23:23:23 GMT" \
      -H "DNT: 1" \
      -H "X-Forwarded-For: 2.71.255.255" \
      -H "Accept-Language: sv-SE"

    Expected output

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

    Tried: Using -H 'X-Forwarded-For: 127.0.0.1' or a random private IP for the geo-check

    The server resolves X-Forwarded-For against a GeoIP database and checks that the result is Sweden. Private addresses (10.x, 192.168.x, 127.x) and most public IPs do not map to SE, so the server returns 'Sorry, you're not from the right place.' Any publicly routable IP assigned to a Swedish ISP passes - 2.71.255.255 falls in TeliaSonera's allocation and resolves to SE.

    Tried: Setting Date to a full RFC 7231 timestamp like 'Mon, 23 Nov 2018 23:23:23 GMT' and still getting rejected

    The server only checks the year portion of the Date header, not the full timestamp. However, if the format is malformed (wrong day-of-week for the given date, wrong month abbreviation, or extra spaces) some server-side parsers will reject the entire header and fall back to failing. The value shown in the commands - 'Mon, 23 11 2018 23:23:23 GMT' - uses a numeric month which is non-standard but accepted by this challenge's lenient parser. If you use the strict RFC format and it fails, try the numeric month form.

    Learn more

    Why a Swedish IP. The error message names Sweden. Confirm an IP's country with a quick whois lookup or a MaxMind GeoIP query. 2.71.0.0/16 through 5.150.0.0 contain Swedish ISP allocations; 2.71.255.255 works because it falls in TeliaSonera's assignment. Any IP that GeoIP resolves to SE passes.

    HTTP headers, by purpose:

    • Referer: the URL the client came from. Same-origin check here.
    • Date: request time. Server checks the year is 2018.
    • DNT: Do Not Track. 1 requests no tracking.
    • X-Forwarded-For: original client IP through a proxy. The server uses this for geo-restriction (a Swedish IP passes).
    • Accept-Language: preferred response language. sv-SE for Sweden.

    Case sensitivity. HTTP header names are case-insensitive (User-Agent, user-agent, and USER-AGENT all match). Values are case-sensitive: picobrowser and PicoBrowser are different strings as far as a strict equality check is concerned. Match the exact value the server demands.

Interactive tools
  • Flask Session DecoderDecode Flask / itsdangerous session cookies. Splits payload, decompresses zlib, parses JSON, and verifies the HMAC signature when given the secret.

Flag

Reveal flag

picoCTF{http_h34d3rs_v3ry_c0Ol_much_w0w_...}

The server validates six HTTP headers in sequence. Each wrong header reveals the next requirement, making this a progressive enumeration challenge.

Key takeaway

HTTP headers like User-Agent, Referer, X-Forwarded-For, and Accept-Language are all client-supplied and trivially forged with curl or a browser extension. Any server-side access control that relies solely on header values for identity or geo-restriction can be bypassed by any requester who knows what values to send. Real geo-restriction requires IP-level enforcement at the network layer, not trusting proxy headers that clients write themselves.

Related reading

Want more picoCTF 2021 writeups?

Tools used in this challenge

What to try next