Decrypting Mystery: Alex’s Journey Through the Not-Quite-Caesar Challenge

Bikram Dhimal ( zeroair )
4 min readJun 28, 2024

--

Chapter 1: The Challenge Begins

Alex, a cybersecurity enthusiast with a knack for cracking the toughest challenges, was always on the lookout for his next adrenaline rush. His eyes sparkled with excitement as he scrolled through the latest Capture The Flag (CTF) competitions on vsCTF, the thrill of the hunt pulsing through his veins.

Suddenly, a challenge stood out among the rest like a hidden treasure waiting to be unearthed. It was a cryptography puzzle intriguingly named “Not-Quite-Caesar.” The name alone piqued his curiosity, suggesting a twist on the classic Caesar cipher, but with a modern, more devious spin. Intrigued and eager to dive in, Alex’s fingers danced across the keyboard as he clicked the link, downloading the mysterious not-quite-caesar.zip file.

The file downloaded, Alex felt the familiar rush of anticipation. He knew that inside that ZIP file lay a puzzle designed to stump even the sharpest minds, and he couldn’t wait to unravel its secrets. Little did he know, this challenge would take him on a journey through the depths of cryptographic intricacies, testing his skills and wit like never before.

Chapter 2: Unzipping the Mystery

Sitting at his computer, Alex extracted the contents of the ZIP file. Two files appeared before him: nqc.py and out.txt. He quickly opened both files to understand what he was up against.

out.txt: This file contained a list of encrypted byte values, clearly the result of the nqc.py script.

[354, 112, 297, 119, 306, 369, 111, 108, 333, 110, 112, 92, 111, 315, 104, 102, 285, 102, 303, 100, 112, 94, 111, 285, 97, 351, 113, 98, 108, 118, 109, 119, 98, 94, 51, 56, 159, 50, 53, 153, 100, 144, 98, 51, 53, 303, 99, 52, 49, 128]

nqc.py: This script seemed to contain the encryption algorithm used to generate the out.txt file.

Chapter 3: Understanding the Script

Alex first examined the nqc.py file. It was a Python script, and after reading through the code, he realized it wasn’t a typical Caesar cipher. Here’s what the script looked like:

import random
random.seed(1337)
ops = [
lambda x: x+3,
lambda x: x-3,
lambda x: x*3,
lambda x: x^3,
]


flag = list(open("flag.txt", "rb").read())
out = []
for v in flag:
out.append(random.choice(ops)(v))
print(out)

Chapter 4: Deciphering the Code

Alex understood that the script applied random operations to each byte of the original flag. The operations were:

  • Addition (+ 3)
  • Subtraction (- 3)
  • Multiplication (* 3)
  • XOR (^ 3)

To decrypt the message, he needed to reverse these operations in the exact sequence they were applied. But how could he do that if the operations were chosen randomly?

Chapter 5: The Eureka Moment

The script used a fixed random seed (1337). This meant the sequence of random operations was predictable and reproducible. Alex knew he could exploit this to reverse the operations.

He wrote the following script to reverse the encryption:

import random

# Set the seed to match the original script
random.seed(1337)

# Define the original operations
ops = [
lambda x: x + 3,
lambda x: x - 3,
lambda x: x * 3,
lambda x: x ^ 3,
]

# Define the reverse operations
reverse_ops = [
lambda x: x - 3,
lambda x: x + 3,
lambda x: x // 3 if x % 3 == 0 else None,
lambda x: x ^ 3,
]

# The 'out' list from out.txt
out = [354, 112, 297, 119, 306, 369, 111, 108, 333, 110, 112, 92, 111, 315, 104, 102, 285, 102, 303, 100, 112, 94, 111, 285, 97, 351, 113, 98, 108, 118, 109, 119, 98, 94, 51, 56, 159, 50, 53, 153, 100, 144, 98, 51, 53, 303, 99, 52, 49, 128]

# List to store the recovered flag bytes
flag = []

# Iterate over the output and reverse the applied operations
for v in out:
op_index = random.randint(0, 3) # Get the index of the operation applied
reverse_op = reverse_ops[op_index] # Get the corresponding reverse operation
reversed_value = reverse_op(v) # Apply the reverse operation

# Check if reverse operation was valid
if reversed_value is not None:
flag.append(reversed_value)
else:
print(f"Failed to reverse operation for value {v} with operation index {op_index}")
flag.append(ord('?')) # Use '?' for unknown characters

# Convert the flag list back to bytes and decode, replacing invalid bytes with '?'
flag_bytes = bytes(flag)
print(flag_bytes.decode('utf-8', 'replace'))

Chapter 6: The Solution

With anticipation, Alex ran his script. The console output displayed:

vsctf{looks_like_ceasar_but_isnt_a655563a0a62ef74}

Alex had successfully deciphered the message!

Chapter 7: The Lesson

Through this challenge, Alex learned valuable lessons about cryptography, particularly about the importance of random number generation and how it can be leveraged both for encryption and decryption. He appreciated the beauty and complexity of cryptographic algorithms, realizing that even simple transformations could pose significant challenges without the right approach.

Chapter 8: Sharing the Knowledge

Inspired by his success, Alex decided to write a blog post on Medium, detailing his journey through the “Not-Quite-Caesar” challenge. He wanted to share his experience and insights with other cybersecurity enthusiasts, helping them understand and tackle similar challenges in the future.

Alex’s story was not just about solving a cryptographic puzzle; it was about the thrill of the chase, the joy of discovery, and the endless quest for knowledge in the world of cybersecurity.

Stay curious, keep learning, and happy hacking!

--

--

Bikram Dhimal ( zeroair )
Bikram Dhimal ( zeroair )

Written by Bikram Dhimal ( zeroair )

Cyber Security Researcher / CTF Player / Ethical Hacker / Learner

No responses yet