Python Wrangling picoCTF 2021 Solution

Published: April 2, 2026

Description

Python scripts are invoked kind of like programs in the terminal. Download and use the provided python script, password file, and encrypted flag.

Download ende.py, pw.txt, and flag.txt.en from the challenge page.

bash
wget <url>/ende.py
bash
wget <url>/pw.txt
bash
wget <url>/flag.txt.en

Solution

Want to try it yourself first?

The guided walkthrough reveals hints one step at a time.

Walk me through it
  1. Step 1
    Read the password
    Observation
    I noticed the challenge provided a separate pw.txt file alongside the encrypted flag, which suggested the decryption password was stored there and needed to be retrieved before running the script.
    The decryption password is stored in pw.txt. Print it so you can supply it to the script.
    bash
    cat pw.txt
    What didn't work first

    Tried: Open pw.txt in a text editor or use less/more instead of cat

    The file opens fine, but copying the password from a pager or editor can silently include a trailing newline or extra whitespace. When that whitespace is pasted into the script's password prompt, Fernet rejects the key with an InvalidToken error. Using cat and copying from the terminal output avoids the issue because the password is printed cleanly on one line.

    Tried: Skip reading pw.txt and guess the password is 'password' or an empty string

    The script will accept any string at the prompt but Fernet will raise an InvalidToken or InvalidSignature error immediately on decryption because the HMAC check fails with the wrong key. The password is a random-looking Base64 string in pw.txt and cannot be guessed; you must cat the file and supply the exact value.

    Learn more

    Storing a password in a separate file is a common pattern in software - rather than hard-coding credentials in the main script, they're kept in a dedicated file that can be protected with file system permissions or excluded from version control. In this challenge, the password file is provided to you intentionally as part of the puzzle.

    In real applications you'd never want to store plaintext passwords in a file, but the pattern of reading configuration or secrets from a file is pervasive. Tools like .env files, secrets.json, or environment variables serve the same purpose in production code. The key is that the secret is separate from the logic that uses it.

  2. Step 2
    Decrypt the flag
    Observation
    I noticed ende.py accepts both -e and -d flags and that flag.txt.en carried an encrypted extension, which suggested passing -d along with the encrypted file and the password from pw.txt to recover the plaintext flag.
    Run ende.py with -d. The argument convention varies; try the script's interactive prompt first, then fall back to argv or stdin redirection if it expects the password directly.
    bash
    # Most common: prompt-driven, paste the password when asked
    python
    python3 ende.py -d flag.txt.en
    bash
    bash
    # Argv-style: password as second positional argument
    python
    python3 ende.py -d flag.txt.en "$(cat pw.txt)"
    bash
    bash
    # Stdin-style: pipe the password
    python
    python3 ende.py -d flag.txt.en < pw.txt

    Expected output

    picoCTF{4p0110_1n_7h3_h0us3_...}
    What didn't work first

    Tried: Run the script with -e instead of -d to try to decrypt

    The -e flag invokes the encrypt path, so the script reads flag.txt.en as plaintext and produces a doubly-encrypted blob - it does not print the flag. The correct flag is -d for decrypt. If you see a long Base64-looking string as output rather than picoCTF{...}, you have used -e by mistake.

    Tried: Run the script with python (Python 2) instead of python3

    On systems where python refers to Python 2, the cryptography module import will either fail with ImportError (if not installed for Python 2) or produce a bytes/str type mismatch inside the Fernet decode path. The script is written for Python 3 string semantics. Always use python3 explicitly to ensure the correct interpreter.

    Learn more

    Command-line flags (like -d) are arguments passed to a program to modify its behavior. The convention of -d for "decrypt" and -e for "encrypt" is common across many cryptographic tools. Python scripts parse these using the sys.argv list or the argparse module, which provides structured argument parsing with help text and type checking.

    The .en file extension likely stands for "encrypted" - a naming convention to distinguish encrypted files from their plaintext originals. The underlying cipher is almost always Fernet (AES-128-CBC + HMAC-SHA256 from the cryptography package), which is the canonical symmetric encryption primitive in modern Python. Since the password is given, no cracking is needed; this challenge is purely about reading the script and supplying its argument convention.

    Running Python scripts: python3 script.py invokes the Python 3 interpreter on script.py. The distinction between python and python3 matters on systems where Python 2 is still the default (older Linux distros). In CTF environments, always check python3 --version first. You can also make scripts directly executable with chmod +x ende.py and then run ./ende.py -d flag.txt.en if the script has a shebang line (#!/usr/bin/env python3).

Interactive tools
  • Base64 & Base32 DecoderDecode Base64 and Base32 strings with auto-detection. Multi-layer mode unwraps nested encodings automatically.
  • Recipe ChainStack decoders into a pipeline: Base64, hex, ROT, XOR, Morse, URL, Atbash, Vigenère, and more. Magic mode auto-discovers the chain. Bookmark the URL to save it.
  • Number Base ConverterConvert numbers between binary, octal, decimal, and hexadecimal instantly. Enter any value and see all four bases update in real time.

Flag

Reveal flag

picoCTF{4p0110_1n_7h3_h0us3_...}

The -d flag means decrypt; the password from pw.txt is the decryption key. The 8-character hex suffix at the end is generated per instance and will differ from the value shown here.

Key takeaway

Symmetric encryption with a password-derived key is only as secure as the secrecy of the key itself. When the key is stored alongside the ciphertext (as in a provided pw.txt file), the encryption provides no confidentiality at all. In real deployments, keys must be kept separate from encrypted data, distributed through secure channels, or derived from a secret the user knows and never writes down.

Related reading

Want more picoCTF 2021 writeups?

Useful tools for General Skills

What to try next