TryHackMe W1seGuy—Writeup
Hey there, welcome back to another write-up! Today, I’ll be sharing how I solved the W1seguy room on TryHackMe. It was a pretty interesting challenge with a mix of fun twists and learning moments. I’ll walk you through everything I did! Let's get started!

For this challenge, we’re given a Python script to look at.
import random
import socketserver
import socket, os
import string
flag = open('flag.txt','r').read().strip()
def send_message(server, message):
enc = message.encode()
server.send(enc)
def setup(server, key):
flag = 'THM{thisisafakeflag}'
xored = ""
for i in range(0,len(flag)):
xored += chr(ord(flag[i]) ^ ord(key[i%len(key)]))
hex_encoded = xored.encode().hex()
return hex_encoded
def start(server):
res = ''.join(random.choices(string.ascii_letters + string.digits, k=5))
key = str(res)
hex_encoded = setup(server, key)
send_message(server, "This XOR encoded text has flag 1: " + hex_encoded + "\n")
send_message(server,"What is the encryption key? ")
key_answer = server.recv(4096).decode().strip()
try:
if key_answer == key:
send_message(server, "Congrats! That is the correct key! Here is flag 2: " + flag + "\n")
server.close()
else:
send_message(server, 'Close but no cigar' + "\n")
server.close()
except:
send_message(server, "Something went wrong. Please try again. :)\n")
server.close()
class RequestHandler(socketserver.BaseRequestHandler):
def handle(self):
start(self.request)
if __name__ == '__main__':
socketserver.ThreadingTCPServer.allow_reuse_address = True
server = socketserver.ThreadingTCPServer(('0.0.0.0', 1337), RequestHandler)
server.serve_forever()
This Python script sets up a TCP server that presents a simple XOR encryption challenge. When a client connects, the server generates a random 5-character alphanumeric key and uses it to XOR-encrypt a hardcoded fake flag (THM{thisisafakeflag}
). The result is then hex-encoded and sent to the client. The client is then prompted to guess the encryption key. If the correct key is submitted, the server responds with the actual flag read from flag.txt
. If the guess is incorrect, the connection is closed with a failure message. The core challenge here is for the client to reverse the XOR encryption using the given ciphertext to retrieve the original key.
WHAT THE HELL IS XOR??
XOR, short for "exclusive or", is a logical operation used often in cryptography. It compares two bits and returns:
1
if the bits are different0
if the bits are the same
Here’s a simple truth table:
0
0
0
0
1
1
1
0
1
1
1
0
In the context of encryption:
XOR is useful because it's reversible:
If
C = A XOR B
, then:A = C XOR B
B = C XOR A
This makes it perfect for simple encryption like in the provided code. The flag is XORed with a key to hide its contents. If you know the key, you can XOR the result again to get the original flag back.
Think of it like flipping a light switch: flip it once to turn the light on (encrypt), and flip it again to turn it off (decrypt).
Solution
I knew I had to reverse the XOR encryption used to encode the fake flag. The server sends a hex-encoded string, which is the result of XORing a known flag format (THM{...}
) with a randomly generated 5-character key. Since the format of the flag is predictable, I could take advantage of that known structure to recover the encryption key — a common approach in known-plaintext attacks.
The core idea here is simple: if you know part of the original message (like "THM{"
at the start and "}"
at the end), and you have the encrypted version, you can recover the key used for XORing. Since XOR is reversible, you can isolate each key character using:
key_char = ciphertext_byte XOR known_plaintext_char
In this challenge:
The key is 5 characters long.
The ciphertext is hex-encoded.
The beginning of the plaintext is always
"THM{"
.The last character is always
"}"
.
By XORing the known parts of the plaintext with the corresponding bytes of the ciphertext, we can figure out the characters of the key at those positions. Once the key is known, we can decrypt the entire message.
Here's the code I've made to test that:
def snatch_key(cipher: str, known_start: str, known_end: str, key_len: int) -> str:
# Hex to bytes for the hack
bytez = bytes.fromhex(cipher)
key = [""] * key_len
# Snag key chars using known plaintext
for i in range(len(known_start)):
key[i % key_len] = chr(bytez[i] ^ ord(known_start[i]))
# Grab last key char from known end
end_pos = len(cipher) // 2 - 1
key[end_pos % key_len] = chr(bytez[end_pos] ^ ord(known_end[0]))
return "".join(key)
def break_cipher(cipher: str, key: str) -> str:
# Convert hex to bytes for decryption
bytez = bytes.fromhex(cipher)
decrypted = ""
# XOR with repeating key to expose the loot
for i in range(len(bytez)):
decrypted += chr(bytez[i] ^ ord(key[i % len(key)]))
return decrypted
def execute_hack() -> None:
# Prompt for the hex cipher
raw_hex = input("[*] Inject hex cipher: ")
key_size = 5 # Hardcoded key length
# Known plaintext for key extraction
flag_start = "THM{"
flag_end = "}"
# Derive the master key
master_key = snatch_key(raw_hex, flag_start, flag_end, key_size)
# Crack the cipher
plaintext = break_cipher(raw_hex, master_key)
# Dump results
print("-" * 30)
print(f"[+] Master Key: {master_key}")
print(f"[+] Decrypted Data: {plaintext}")
if __name__ == "__main__":
execute_hack()
Output:

Server:

This solution efficiently cracks the XOR encryption using logical deduction and the predictability of the flag format. It’s a great example of how simple cryptographic mistakes — like reusing short keys and not randomizing output formats — can be exploited. That's all^^
Last updated