login picoMini by redpwn Solution

Published: April 2, 2026

Description

My dog-sitter's brother made this website but I can't log in; can you help?

Remote

Open the challenge URL in your browser.

Open the browser developer tools (F12) and navigate to the Sources or Debugger tab.

Solution

Want to try it yourself first?

The guided walkthrough reveals hints one step at a time.

Walk me through it
There is no database here and no SQL injection. The entire login check runs in client-side JavaScript, and the password it compares against is just base64. For the encoding background, see Base64, Hex, and Common CTF Encodings.
  1. Step 1
    Read the page source
    Observation
    I noticed the login form submitted with no visible network request to a backend server, which suggested that all authentication logic was running client-side in JavaScript and would be visible in the page source or loaded scripts.
    View the page source or open the Network tab and find index.js. All authentication logic is written in client-side JavaScript - there is no server-side validation.
    Learn more

    Everything a web browser renders is sent to the client - HTML, CSS, and JavaScript are all downloadable and readable. When authentication logic is implemented entirely in client-side JavaScript (as opposed to a server-side check), the credentials and validation rules are necessarily included in that code. An attacker simply reads the script to understand exactly what the application checks.

    Browser developer tools (F12) are the primary instrument for client-side web analysis. The Sources tab shows all loaded JavaScript files, the Network tab shows every HTTP request and response, and the Console lets you run arbitrary JavaScript in the page's context. Pressing Ctrl+U shows the raw HTML source including inline scripts.

    Real applications should always perform authentication on the server, where the code is not exposed to users. Client-side checks are appropriate only for UI feedback (like disabling a submit button before all fields are filled) - they must never be the only gate protecting sensitive resources.

  2. Step 2
    Decode the username
    Observation
    I noticed the script compared btoa(username) against the string 'YWRtaW4=' with its telltale '=' padding, which identified it as base64 encoding and indicated the plaintext username could be recovered by simply decoding it.
    The script compares btoa(username) === 'YWRtaW4=' - decode this base64 string to get the required username.
    python
    python3 -c "import base64; print(base64.b64decode('YWRtaW4=').decode())"

    Expected output

    admin
    What didn't work first

    Tried: Use atob() in the browser console to decode the username instead of Python

    atob('YWRtaW4=') works fine in the browser and returns 'admin' - the issue is that some learners confuse btoa (encode) with atob (decode) and call btoa('YWRtaW4=') instead, which double-encodes the string and produces 'WVdSdGFXND0=' rather than the plaintext. Always use atob() to go from base64 to plaintext.

    Tried: Treat the base64 string as an MD5 or SHA hash and try to crack it with a tool like hashcat

    Hash outputs are fixed-length hex strings (32 hex chars for MD5, 64 for SHA-256) and are not reversible. Base64 strings end in = padding and decode instantly because they are an encoding, not a one-way function. Feeding 'YWRtaW4=' to hashcat produces no result because base64 is not a hash algorithm.

    Learn more

    Base64 is an encoding scheme that converts binary data (or arbitrary text) to a string using only 64 printable ASCII characters (A-Z, a-z, 0-9, +, /). It is used for data transmission contexts that require text-safe encoding, such as HTTP Basic Auth headers, data URIs, and email attachments. Critically, it is not encryption - it is trivially reversible by anyone, as this step demonstrates.

    btoa() is a browser JavaScript function (Base64 encode) and atob() is its inverse (Base64 decode). The names derive from the historical Unix programs btoa (binary to ASCII) and atob (ASCII to binary). Encoding a password or username in base64 and comparing it provides zero security - it is security theater that only stops someone who has never heard of base64.

    The = or == at the end of a base64 string is padding: base64 encodes 3 bytes into 4 characters, so strings whose length is not a multiple of 3 are padded to reach the next boundary. Recognizing this padding is one of the simplest ways to identify base64-encoded data in the wild.

  3. Step 3
    Decode the password / flag
    Observation
    I noticed the same btoa-comparison pattern applied to the password field, with a longer base64 string that matched the length of a picoCTF flag, which suggested decoding it directly would yield the flag without needing to submit the form.
    The password is compared against a base64 string. Decoding it in the browser console reveals the flag directly - the password itself is the flag.
    bash
    # In browser console:
    atob('cGljb0NURns1M3J2M3JfNTNydjNyXzUzcnYzcl81M3J2M3JfNTNydjNyfQ==')
    What didn't work first

    Tried: Try to log in using the decoded username and password through the actual form to get the flag

    Submitting the form is not required because the password itself is the flag - decoding the base64 string gives you picoCTF{...} directly. If you do try the form, it may still work, but you have already captured the flag by decoding the value, so the form submission step is unnecessary.

    Tried: Attempt SQL injection on the login form (e.g. entering ' OR 1=1 --) hoping to bypass authentication

    SQL injection only works when user input is interpolated into a database query server-side. This login has no server component at all - the comparison is done by JavaScript running entirely in your browser. SQL injection payloads are passed as strings to btoa() which encodes them, and the resulting base64 will never match the hardcoded expected value.

    Learn more

    The browser console is itself a full JavaScript REPL (Read-Eval-Print Loop) running in the page's security context. Calling atob() there is equivalent to calling it in the application code - you get the same decoded string the application would compare against. This makes the console the fastest way to evaluate JavaScript expressions you find in the page source without writing a separate script.

    The password being the flag itself is a common CTF shortcut that avoids the need to log in at all - once you decode the password, you have the flag regardless of whether the login form actually works. In real-world penetration testing, hardcoded credentials found in client-side JavaScript are a critical finding even when they are obfuscated beyond simple base64, because automated tools and reverse engineering can always extract them.

    This pattern - client-side-only authentication with hardcoded or client-visible credentials - falls under CWE-798 (Use of Hard-coded Credentials) and CWE-602 (Client-Side Enforcement of Server-Side Security). Both appear regularly in real-world web application security assessments, particularly in older or hobby-built sites.

Interactive tools
  • 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.
  • Regex TesterTest regular expressions against a string with live match highlighting, flag toggles, and common CTF pattern shortcuts.

Flag

Reveal flag

picoCTF{53rv3r_53rv3r_53rv3r_53rv3r_53rv3r}

Client-side authentication is trivially bypassable - all validation logic and credentials are sent to the user's browser and can be read directly from the JavaScript source.

Key takeaway

Client-side authentication is not authentication: any logic the browser executes is code the user can read, modify, and bypass. Credentials hardcoded in JavaScript, credential comparisons done in the browser, and base64 'obfuscation' all provide no security because the attacker receives the full source. Authentication decisions must always be enforced server-side, where the code and secrets are never transmitted to the client.

Related reading

Want more picoMini by redpwn writeups?

Useful tools for Web Exploitation

What to try next