Description
We have a secret message and a binary that encrypts it. Reverse the cipher to decrypt enc.
Setup
Download both files: the rev binary and the enc ciphertext file.
wget <url>/revwget <url>/encchmod +x revSolution
Want to try it yourself first?
The guided walkthrough reveals hints one step at a time.
Step 1
Examine the binaryObservationI noticed the challenge provided a compiled binary called rev alongside an enc ciphertext file, which suggested that understanding the encryption logic inside rev was the necessary first step before any decryption could be attempted.Run strings on the binary to find any hardcoded values. Then open it in Ghidra or Radare2 to decompile the encryption logic. The encryption function applies a simple transformation to each character.bashstrings revbashfile revbashghidra rev &Expected output
rev: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, not stripped
What didn't work first
Tried: Run xxd or hexdump on the enc file hoping to spot a pattern without opening the binary.
xxd shows raw bytes but gives no hint about which indices are shifted and by how much. Without reading the decompiled logic, you cannot know the +5/-2 parity rule or the 8-byte untouched prefix. Ghidra is required to recover those exact constants.
Tried: Use ltrace or strace on the rev binary to intercept its file I/O and infer the cipher from system call arguments.
ltrace and strace show file opens and write sizes but not the arithmetic applied to each byte in user-space. The encryption loop never calls a library function - it is a plain indexed loop inside main - so no interceptable call reveals the +5/-2 transform. Static analysis in Ghidra is the correct path.
Learn more
Ghidra is a free reverse engineering tool from the NSA. It decompiles binaries to C-like pseudocode. Load the binary, let it auto-analyze, then navigate to the
mainfunction to see the encryption logic.Step 2
Understand the encryption algorithmObservationI noticed that Ghidra's decompiled main function showed a loop that treated bytes differently depending on their index parity, which indicated a non-uniform transform that required careful mapping of the exact boundary (index 8) and the per-parity constants (+5 and -2) before writing the inverse.In Ghidra, main reads 24 (0x18) bytes from flag.txt into a buffer. The first 8 bytes are copied to the output unchanged. From index 8 onward the transform depends on position parity: bytes at an EVEN index are increased by 5 (byte + 5), bytes at an ODD index are decreased by 2 (byte - 2). The result is written to enc.Learn more
This is not a uniform Caesar shift. The first 8 characters (the
picoCTF{prefix region) pass through untouched, and only from index 8 does the parity-based transform begin. Getting the index boundary and the per-parity operations right is the whole challenge.Step 3
Write a Python decryption scriptObservationI noticed the decompiled logic revealed two simple arithmetic operations (+5 at even indices, -2 at odd indices) applied from index 8 onward, which meant each operation had a direct arithmetic inverse that could be scripted in a few lines of Python to recover the plaintext flag.Apply the inverse transform: keep indices 0-7 as is, for indices 8 through 22 (0x17 exclusive) subtract 5 at even indices and add 2 at odd indices, then append the closing brace at index 23 unchanged.pythonpython3 - <<'EOF' data = open('enc', 'rb').read() out = bytearray(data[:8]) # first 8 bytes untouched for i in range(8, 0x17): # indices 8-22; index 23 (closing }) is unchanged if i % 2 == 0: out.append((data[i] - 5) & 0xff) # inverse of +5 at even index else: out.append((data[i] + 2) & 0xff) # inverse of -2 at odd index out.append(data[0x17]) # closing } passed through by the binary print(out.decode()) EOFWhat didn't work first
Tried: Apply the same shift to all bytes uniformly: subtract 5 from every character after index 8 regardless of parity.
This inverts only the even-index +5 half of the transform and leaves odd-index bytes two characters off. The output will contain garbage characters at every odd position from index 9 onward. The parity check (i % 2 == 0) must route even and odd indices to different operations - subtract 5 and add 2 respectively.
Tried: Start the inversion loop at index 0 instead of index 8, applying the parity transform to the entire ciphertext.
The binary copies the first 8 bytes (the picoCTF{ prefix) straight to enc without any modification. Inverting them shifts the p, i, c, o, C, T, F, { characters away from their correct ASCII values and produces a flag that does not start with picoCTF{. The loop must begin at index 8 to match where the binary's transform actually starts.
Learn more
Validate by running
revon a known test file and checking that your inverse takes its output back to your input. The recovered plaintext will be the flag in the formpicoCTF{r3v3rs3...}where the trailing hex suffix differs per instance.
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.
Note
Because the transform is position-dependent (not a single fixed shift), a generic ROT Cipher tool will not recover it in one pass. The short Python inverse above is the reliable route.
Flag
Reveal flag
picoCTF{r3v3rs3...}
The trailing hex suffix after r3v3rs3 is generated per instance (confirmed instances include r3v3rs39ba4806b, r3v3rs369806a41, r3v3rs3d0051a07). The decryption logic is static: main reads 24 bytes from flag.txt; indices 0-7 (picoCTF{) pass through unchanged, indices 8-22 are shifted (+5 at even, -2 at odd), and index 23 (closing }) is written as-is. Invert that to recover your instance's flag.