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.
Setup
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).
wget https://artifacts.picoctf.net/c_rhea/12/challenge.zip && \
unzip challenge.zip && \
cd drop-inSolution
Want to try it yourself first?
The guided walkthrough reveals hints one step at a time.
Step 1
Identify the correct fileObservationI noticed checksum.txt contained a single SHA-256 hash and the files/ directory held many identically named hex files, which suggested I needed to hash every file at once with sha256sum and filter for the matching digest to avoid manually comparing 64-character strings.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).bashsha256sum files/* | grep 03b52eabed517324828b9e09cbbf8a7b0911f348f76cf989ba6d51acede6d5d8Expected 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
With dozens of identically named hex files in files/, doing this one at a time means reading checksum.txt, reading the output, comparing by eye, then repeating. It is easy to mis-read a 64-character hex string and mark the wrong file as a match. Hashing all files at once with sha256sum files/* and piping to grep lets the tool do the comparison reliably in one shot.
Tried: Using md5sum files/* instead of sha256sum to find the matching file
checksum.txt contains a SHA-256 digest (64 hex characters). MD5 produces a 32-character shorter digest, so no md5sum output will ever match the expected value. The grep returns no results and it looks like none of the files match. The hash algorithm must match what was used to produce the checksum.
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 filecomputes the SHA-256 hash of a file.sha256sum files/*computes hashes for all files in the directory - pipe throughgrepto 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).
Step 2
Fix and run the decrypt scriptObservationI noticed decrypt.sh was provided alongside the encrypted file and that running it as-is returned a 'not a valid file' error, which suggested the script had a broken hardcoded path prefix that needed to be removed before the correct openssl decryption flags it contained could be used.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.bashcat decrypt.shbash./decrypt.sh files/00011a60The 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 prepends a hardcoded home-directory path to the argument, so the actual path passed to openssl becomes something like /home/ctf-player/drop-in/files/00011a60 when it is already being called from drop-in/. The result is a "not a valid file" or "no such file" error. You must open decrypt.sh, remove or comment out the prefix line, and then re-run it.
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 -iter 100000, openssl uses its old default key-derivation method (EVP_BytesToKey) which produces a different key from the same password. The result is garbled binary output rather than the flag plaintext. The exact flags used to encrypt must be mirrored during decryption; reading decrypt.sh reveals the full flag set required.
Learn more
openssl encis OpenSSL's symmetric encryption/decryption command. Breaking down the flags:-dmeans decrypt,-aes-256-cbcspecifies AES-256 in Cipher Block Chaining mode,-pbkdf2uses the PBKDF2 key derivation function (more secure than the old default),-iter 100000runs 100,000 iterations of PBKDF2 to slow brute-force attacks,-saltincludes a random salt, and-k picoCTFprovides 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.shwraps 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.Step 3
Alternate brute-forceObservationI noticed the password (picoCTF) was already known from decrypt.sh and the file count was small, which suggested that attempting decryption on every file in a loop and suppressing errors would be a valid shortcut that bypasses the hash verification step entirely.Skip the hashing step and just try every file. Suppress stderr (most fail with bad-magic-number errors), pipe throughstringsto keep only printable runs, then grep for the flag prefix.bashfor f in files/*; do openssl enc -d -aes-256-cbc -pbkdf2 -iter 100000 -salt -in "$f" -k picoCTF 2>/dev/null; done | strings | grep picoCTFWhat didn't work first
Tried: Running the brute-force loop without 2>/dev/null, then piping everything including error messages into grep
When decryption fails, openssl prints "bad decrypt" and "error reading input file" lines to stderr. In some shells, piping a command that mixes stdout and stderr causes error text to appear interleaved in the output, making grep results hard to read or missing entirely. Redirecting stderr to /dev/null silences the failures so only successful decryptions reach strings and grep.
Tried: Omitting | strings and piping raw openssl output directly into grep for the flag prefix
A failed decryption sometimes produces a short burst of binary garbage bytes that happen to contain the ASCII sequence picoCTF as a coincidence. Without strings filtering for printable runs, grep can match junk output and report a false positive from the wrong file. The strings step ensures only real human-readable text reaches grep.
Learn more
The
forloop iterates over every file matchingfiles/*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/nullhides 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.