➡️ by @willyvaessen
📅 Written: 6 July 2025
Platform: TryHackMe
Room: Cipher's Secret Message
Introduction
One of the Ciphers' secret messages was recovered from an old system alongside the encryption algorithm, but we are unable to decode it.
Order: Can you help void to decode the message?
Message : a_up4qr_kaiaf0_bujktaz_qm_su4ux_cpbq_ETZ_rhrudm
Encryption algorithm :
from secret import FLAG
def enc(plaintext):
return "".join(
chr((ord(c) - (base := ord('A') if c.isupper() else ord('a')) + i) % 26 + base)
if c.isalpha() else c
for i, c in enumerate(plaintext)
)
with open("message.txt", "w") as f:
f.write(enc(FLAG))
Note: Wrap the decoded message within the flag format THM{}
Solving the puzzle
Step 1: Understanding the encoding.
The code above basically does the following:
The function takes your original string and iterates over every position in the string.
For every position it checks whether the character is in the alphabet (a-z or A-Z) or not. In case the character is not alphabet (.isalpha() == False
), it gets appended to the new (encoded) string as is, but if it IS in fact alphabetic (.isalpha() == True
) it is shifted based on the index (i
), starting from the base
depending on uppercase or lowercase. The resulting (encoded) string is then returned by the function.
Step 2: Rewriting the algorithm
To better understand how this algorithm works, I decided to copy the code into PyCharm first and run it with a simple string that I knew: abcde
.
The import line can safely be ignored and commented out. Even though there is a package called secret
I noticed that there was no such thing as FLAG
in that package. I declared a variable FLAG = "abcde"
and ran the code. The resulting message.txt
file contains the encoded string acegi
so it quickly became clear to me that the character is shifted by its position in the string.
Now to unravel the algorithm.
To better understand what happens behind the scenes, I rewrote the existing enc(plaintext)
function into something more readable:
def encode(plaintext):
encoded_string = ""
replacement_char = ""
for i, c in enumerate(plaintext):
if c.isalpha():
if c.isupper():
base=ord('A')
else:
base=ord('a')
replacement_char = chr(((ord(c) - base + i) % 26) + base)
else:
replacement_char = c
encoded_string += replacement_char
print(f"Replacement string is {encoded_string}")
return encoded_string
FLAG = "abcde"
print(encode(FLAG)) # Output: acegi
Step 3: Decoding the message
Once I understood what the algorithm did exactly, it became a lot easier to see that the decoding algorithm was a matter of simply inverting one +
into a -
def decode(encoded_text):
decoded_string = ""
replacement_char = ""
for i, c in enumerate(encoded_text):
if c.isalpha():
if c.isupper():
base=ord('A')
else:
base=ord('a')
replacement_char = chr(((ord(c) - base - i) % 26) + base)
else:
replacement_char = c
decoded_string += replacement_char
print(f"Replacement string is {decoded_string}")
return decoded_string
Run that with the encoded message provided in the puzzle, wrap the result in the THM{...}
format, and voilà:
Congratulations on completing Cipher's Secret Message!!! 🎉
Lessons learned
- To me it really helped understand the code by rewriting it. I've experienced this before in cases where 'Lambda functions' were used. Writing shorter code might be good performance-wise, but it makes understanding the code a lot more difficult, at least in my case.
- Writing a decoding algorithm can be surprisingly easy, once you understand the encoding process.
Top comments (0)