PicoCTF 2025 Reverse Engineering—Quantum Scrambler Writeup
Welcome back to my writeup! Today, I will show the step-by-step on how I solved Quantum Scrambler from picoCTF 2025!

In this challenge we are provided with a Python code, let's analyze it
import sys
def exit():
sys.exit(0)
def scramble(L):
A = L
i = 2
while (i < len(A)):
A[i-2] += A.pop(i-1)
A[i-1].append(A[:i-2])
i += 1
return L
def get_flag():
flag = open('flag.txt', 'r').read()
flag = flag.strip()
hex_flag = []
for c in flag:
hex_flag.append([str(hex(ord(c)))])
return hex_flag
def main():
flag = get_flag()
cypher = scramble(flag)
print(cypher)
if __name__ == '__main__':
main()
This Python script reads a flag from flag.txt
, converts each character of the flag into its hexadecimal representation (as a list of one string), and passes that list to the scramble
function. The scramble
function then modifies the list in-place by popping and merging elements while inserting sublists, effectively scrambling the structure and contents of the flag list in a non-obvious way. The scrambled result is printed to the console.
This is the challenge

At first glance, this might look really confusing, right? Haha. But the challenge is intentionally designed like this because of the scramble
function in the Python code. So, the real question is—how do we go about retrieving the original flag?
When analyzing the challenge, the first thing I noticed was that the scramble()
function heavily modified the structure of the original list of hex-encoded flag characters. Instead of encrypting or transforming the actual contents, it nested and reshaped the list using pop()
and append()
, making it appear confusing and unreadable. So, my first goal in planning the solution is to understand what kind of transformation is happening—it's not encryption, it's obfuscation through deep list nesting.
Given that, my strategy is to write a function that can reverse the nesting caused by scramble()
. I plan to treat the scrambled list as a tree-like structure and traverse it completely to extract all leaf values. Since we’re dealing with potentially multiple layers of lists within lists, recursion or queue-based traversal will help. Once I gather all the hex values from the deeply nested list, I can then convert them back to ASCII characters using chr(int(hex_value, 16))
.
Before that, I’ll need to connect to the CTF server, receive the scrambled output, and parse it properly. Since the output seems to be a Python-like list, I can safely evaluate it using ast.literal_eval()
to convert the string into an actual Python data structure. Once that's done, I can feed it into my unscramble()
function, collect the characters, and reconstruct the original flag. So, the plan is: receive → parse → flatten → decode → print.
Here's our solution
from pwn import *
import ast
def unscramble(L):
result = []
queue = [L]
hex_values = []
while queue:
item = queue.pop(0)
if isinstance(item, list):
for sub_item in item:
queue.append(sub_item)
else:
try:
hex_values.append(item)
result.append(chr(int(item, 16)))
except ValueError as e:
print(f"Error converting {item}: {e}")
continue
print(f"Extracted hex values: {hex_values}")
return ''.join(result)
host = 'verbal-sleep.picoctf.net'
port = 60759
print(f"[+] Opening connection to {host} on port {port}")
conn = remote(host, port)
print("[+] Receiving all data")
ciphertext = conn.recvall().decode().strip()
print("[*] Closing connection")
conn.close()
with open('ciphertext.txt', 'w') as f:
f.write(ciphertext)
print("[+] Ciphertext saved to 'ciphertext.txt'")
try:
scrambled_flag_list = ast.literal_eval(ciphertext)
except Exception as e:
print(f"Error converting ciphertext to list: {e}")
exit(1)
print(f"cipher[15]: {scrambled_flag_list[15]}")
print(f"cipher[16]: {scrambled_flag_list[16]}")
flag = unscramble(scrambled_flag_list)
print(f"Unscrambled Flag: {flag}")
Run and we should get the decoded version of the flag!

The flag is actually right at the very top, haha. Even the decoded output looks like a complete mess, xD.
Last updated