Quantum Scrambler

Published: April 2, 2025

Description

"Quantum Scrambler" is nothing more than a deterministic shuffle of a list of hex bytes. Capture the remote output, re-run the loops in reverse, and you recover the original flag.

Connect to `verbal-sleep.picoctf.net 53222` and save the shuffled output to a file (the server only scrambles the list and prints it).

Download `quantum_scrambler.py` so you can study the `scramble` function and confirm no actual encryption took place.

nc verbal-sleep.picoctf.net 53222 > result
wget https://challenge-files.picoctf.net/c_verbal_sleep/27d1d27147deac5835e1ef9633cf1858c89bf32b14e2f4fbac72b6ca093f6d27/quantum_scrambler.py

Solution

  1. Step 1Understand the scramble routine
    The provided code repeatedly pops elements and appends prefixes, but never modifies the underlying data. It simply reorders sublists, meaning the plaintext bytes are still present.
    Learn more

    Security through obscurity is the antipattern this challenge demonstrates. The "Quantum Scrambler" name sounds sophisticated, but examining the source reveals it is purely a permutation - a reordering of data with no cryptographic key, no mixing of values, and no information loss. Every original byte is present in the output; only their arrangement changed.

    True encryption transforms data in a way that is computationally infeasible to reverse without the key. Permutation-only schemes fail this test because the number of possible arrangements is finite (factorial of input length), and more importantly, in this challenge the permutation algorithm is deterministic and provided in source. Anyone who reads quantum_scrambler.py can reverse it instantly.

    This is a common mistake in amateur cryptography: building elaborate pipelines of bit shifts, rotations, and shuffles that look complex but lack true randomness or key material. Claude Shannon's formal definition of confusion (substitution that hides the relationship between key and ciphertext) and diffusion (spreading plaintext influence across many ciphertext bits) are the properties that distinguish real ciphers from shuffles like this one.

  2. Step 2Iterate through the nested lists
    Parsing the result with `eval` yields a nested Python list. Walking each sublist, append the first and last element to a buffer, then append the remaining bytes stored in the final two lists. This recreates the original sequence of hex strings.
    with open('result') as f: data = eval(f.read())
    print(''.join(chr(int(chunk[2:], 16)) for chunk in rebuilt_bytes))
    Learn more

    Using Python's eval() to parse the output is a quick CTF trick because the server's output is a valid Python literal (a nested list of strings). In production code, eval is dangerous - it executes arbitrary Python, so it should never be used on untrusted input. The safe alternative is ast.literal_eval(), which parses only Python literals (strings, numbers, lists, dicts, tuples) without executing arbitrary expressions.

    The nested list structure the scrambler produces is an artifact of repeated prepend operations - each step wraps the current list as an element inside a new outer list. Reversing this by walking the nesting depth and collecting elements in the correct order is a tree traversal problem. Understanding the forward algorithm precisely enough to invert it is the core skill tested here.

    In real reverse engineering, you often encounter proprietary serialization formats or obfuscated data structures that must be parsed before the payload can be analyzed. Fluency with Python for rapid data structure manipulation - list comprehensions, slicing, zip, map - is invaluable for quickly prototyping parsers and decoders during CTF competitions.

  3. Step 3Decode to ASCII
    Convert each hex chunk (e.g., `0x70`) back to a character via `chr(int(chunk[2:], 16))`. Joining the characters prints the picoCTF flag.
    Learn more

    Hexadecimal representation of bytes is ubiquitous in low-level security work. Each byte (0–255) maps to a two-digit hex value (0x000xff). The int(chunk[2:], 16) idiom strips the 0x prefix and converts the remaining hex digits to an integer in base 16. chr() then maps the integer to its Unicode character - for values 32–126 (printable ASCII), this is the familiar character set.

    The inverse operations are equally important: hex(ord('A')) gives 0x41, and '{:02x}'.format(65) gives '41'. These conversions appear constantly in cryptography, binary exploitation, and network protocol analysis. Python's bytes type and its .hex() / bytes.fromhex() methods are often more ergonomic for bulk conversions than character-by-character loops.

    The lesson here is that adding visual complexity to data (wrapping bytes in 0x-prefixed hex strings and nesting them in lists) does not add security. Any transformation that is purely cosmetic and reversible without a key provides zero cryptographic protection. Recognizing "fake encryption" quickly is a valuable CTF skill that also translates to evaluating real-world security claims about data "obfuscation" products.

Alternate Solution

Once you have reassembled the hex byte sequence, use the Binary to Hex tool on this site to convert the hex chunks to ASCII characters one by one - or paste the entire hex string to decode the flag without writing any additional Python.

Flag

picoCTF{python_is_weird9ece...}

Any faithful translation of the scramble loop works; the key insight is that nothing was encrypted, only permuted.

Want more picoCTF 2025 writeups?

Useful tools for Reverse Engineering

Related reading

What to try next