Skip to main content

Verify picoCTF 2024 Solution

Identify the correct file among many candidates using a checksum, then follow the provided steps to read the flag.

Published: April 3, 2024Updated: August 25, 2026

Description

People keep trying to trick my players with imitation flags. I want to make sure they get the real thing! I'm going to provide the SHA-256 hash and a decrypt script to help you know that my flags are legitimate.

Hash + decrypt

Download/ssh into the drop-in directory and note checksum.txt, decrypt.sh, and files/.

Have sha256sum and openssl available (both are standard on Linux).

bash
wget https://artifacts.picoctf.net/c_rhea/12/challenge.zip && \
unzip challenge.zip && \
cd drop-in

Solution

Want to try it yourself first?

The guided walkthrough reveals hints one step at a time.

Walk me through it
  1. Step 1Identify the correct file
    Observation
    checksum.txt holds one SHA-256 hash, and files/ holds dozens of similarly named hex files. Hash them all at once and grep for the match rather than comparing 64-character strings by eye.
    Hash every file under files/, then grep for the value in checksum.txt. The single line of output names the matching file (files/00011a60 in the canonical drop-in).
    bash
    sha256sum files/* | grep 03b52eabed517324828b9e09cbbf8a7b0911f348f76cf989ba6d51acede6d5d8

    Expected output

    03b52eabed517324828b9e09cbbf8a7b0911f348f76cf989ba6d51acede6d5d8  files/00011a60
    What didn't work first

    Tried: Running sha256sum on just one file and manually comparing the hash string to checksum.txt

    One at a time means reading checksum.txt, reading the output, comparing by eye, and repeating for dozens of files. Misreading a 64-character hex string is easy. Hash everything at once and let grep do the comparison.

    Tried: Using md5sum files/* instead of sha256sum to find the matching file

    checksum.txt holds a 64-character SHA-256 digest. MD5 produces 32 characters, so nothing from md5sum can ever match and the grep comes back empty, as if no file fits. Use the algorithm the checksum was made with.

    Learn more

    SHA-256 is a cryptographic hash function that takes any input and produces a fixed 256-bit (64 hex character) digest. It has three critical properties: it is deterministic (same input always produces the same hash), collision-resistant (it is computationally infeasible to find two different inputs with the same hash), and one-way (you cannot reverse the hash to find the input).

    Hash verification is the standard method for confirming file integrity. When you download software, operating system images, or forensic evidence files, you compare the downloaded file's hash against the expected value to confirm nothing was tampered with or corrupted in transit. This is called a checksum verification.

    • sha256sum file computes the SHA-256 hash of a file.
    • sha256sum files/* computes hashes for all files in the directory - pipe through grep to find the matching one.
    • Other common hash tools: md5sum (MD5, weak - avoid for security), sha1sum (SHA-1, deprecated), sha512sum (SHA-512, stronger than SHA-256).
  2. Step 2Fix and run the decrypt script
    Observation
    decrypt.sh ships with the challenge but fails with a 'not a valid file' error. It has a hardcoded path prefix that needs removing; the openssl flags inside it are correct.
    Open decrypt.sh in a text editor. The script contains a line that prepends /home/ctf-player/drop-in/ (or similar) to the argument, creating an invalid path. Remove or comment out that prefix so the script just receives the filename directly. Then run it with the matched file.
    bash
    cat decrypt.sh
    bash
    ./decrypt.sh files/00011a60

    The decrypted output starts with picoCTF{. If you see a "not a valid file" error, the script is still prepending the broken prefix; edit it and try again.

    What didn't work first

    Tried: Running ./decrypt.sh files/00011a60 without editing the script first

    The script glues a hardcoded home-directory path onto your argument, so openssl receives a doubled path when you are already inside that directory. You get a 'not a valid file' error. Open decrypt.sh, drop the prefix line, and run it again.

    Tried: Trying to decrypt with openssl enc -d -aes-256-cbc -in files/00011a60 -k picoCTF without the -pbkdf2 and -iter flags

    Without -pbkdf2 and the iteration count, openssl falls back to its old key derivation and computes a different key from the same password, so you get binary garbage. Decryption has to mirror the flags used to encrypt, and decrypt.sh lists them.

    Learn more

    openssl enc is OpenSSL's symmetric encryption/decryption command. Breaking down the flags: -d means decrypt, -aes-256-cbc specifies AES-256 in Cipher Block Chaining mode, -pbkdf2 uses the PBKDF2 key derivation function (more secure than the old default), -iter 100000 runs 100,000 iterations of PBKDF2 to slow brute-force attacks, -salt includes a random salt, and -k picoCTF provides the password.

    PBKDF2 (Password-Based Key Derivation Function 2) deliberately makes password-to-key derivation slow and computationally expensive. If an attacker obtains the ciphertext, they cannot quickly brute-force the password because each guess requires 100,000 hash iterations. Modern alternatives include Argon2 and bcrypt.

    The shell script decrypt.sh wraps this command but contains a path-construction bug in this challenge instance. Reading and fixing wrapper scripts is a common CTF skill: the script reveals the exact OpenSSL flags, and removing the broken path prefix is a trivial one-line edit. Always read the provided scripts before running them.

  3. Step 3Alternate brute-force
    Observation
    decrypt.sh already gives away the password, and there are not many files. Loop over all of them with errors suppressed and skip the hash step entirely.
    Skip the hashing step and just try every file. Suppress stderr (most fail with bad-magic-number errors), pipe through strings to keep only printable runs, then grep for the flag prefix.
    bash
    for f in files/*; do openssl enc -d -aes-256-cbc -pbkdf2 -iter 100000 -salt -in "$f" -k picoCTF 2>/dev/null; done | strings | grep picoCTF
    What didn't work first

    Tried: Running the brute-force loop without 2>/dev/null, then piping everything including error messages into grep

    A failed decryption sends 'bad decrypt' and 'error reading input file' to stderr, and in some shells that text interleaves with the real output, burying the grep result. Redirect stderr to /dev/null so only successful decryptions reach the pipeline.

    Tried: Omitting | strings and piping raw openssl output directly into grep for the flag prefix

    A failed decryption can emit binary garbage that happens to contain the bytes for picoCTF. Without strings filtering to printable runs, grep matches that junk and points you at the wrong file.

    Learn more

    The for loop iterates over every file matching files/* and attempts to decrypt each with the known password. Only the correct file decrypts to readable plaintext; the others fail with an error or produce garbage. Sending stderr to /dev/null hides the noise so the surviving stdout lines come from the one valid decrypt.

    This brute-force approach trades CPU time for the convenience of skipping the hash computation step. It is a valid strategy when the file count is small. With hundreds of files and slow PBKDF2 iterations the hash-first approach is faster.

    For the broader shell toolkit used here, see the Linux command line guide for CTFs.

Interactive tools
  • File Magic IdentifierIdentify file types from magic numbers. Paste hex bytes or drop a file to detect PNG, JPEG, ZIP, PDF, ELF, PCAP, SQLite, and dozens of other formats.
  • Hex ViewerView text or raw hex bytes as a xxd-style hex dump with byte offset, hex columns, and ASCII sidebar. Highlights printable characters and null bytes.
  • 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.

Flag

Reveal flag

picoCTF{trust_but_verify_0...}

Only the file whose hash matches checksum.txt decrypts to the flag.

Key takeaway

A hash like SHA-256 produces a fixed-size digest that changes completely if one input byte changes, which is what makes it useful for integrity. Package managers, OS image downloads, digital signatures, and forensic chain of custody all rest on that. It gives no confidentiality on its own; pairing it with symmetric encryption covers both, provided the key travels over a separate trusted channel.

Related reading

Useful tools for Forensics

Where to go next