TryHackMe: w1seguy

A w1se guy 0nce said, the answer is usually as plain as day.

TryHackMe: https://tryhackme.com/room/w1seguy

Source Code

Yes, it's me again with another crypto challenge!

Have a look at the source code before moving on to Task 2.

You can review the source code by clicking on the Download Task Files button at the top of this task to download the required file.

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()

Get those flags!

Your friend told me you were wise, but I don't believe them. Can you prove me wrong?

When you are ready, click the Start Machine button to fire up the Virtual Machine. Please allow 3-5 minutes for the VM to start fully.

The server is listening on port 1337 via TCP. You can connect to it using Netcat or any other tool you prefer.

echo "{MACHINE-IP} w1seguy w1seguy.thm" | sudo tee -a /etc/hosts

What is the first flag?

From the python script above we expect the target port to be 1337 but lets run a rustscan just incase there are any other interesting ports open.

└─$ rustscan -a w1seguy --ulimit 5000 -- -sC -sV -oA w1seguy
.----. .-. .-. .----..---.  .----. .---.   .--.  .-. .-.
| {}  }| { } |{ {__ {_   _}{ {__  /  ___} / {} \ |  `| |
| .-. \| {_} |.-._} } | |  .-._} }\     }/  /\  \| |\  |
`-' `-'`-----'`----'  `-'  `----'  `---' `-'  `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: http://discord.skerritt.blog         :
: https://github.com/RustScan/RustScan :
 --------------------------------------
Scanning ports like it's my full-time job. Wait, it is.

[~] The config file is expected to be at "/home/apjone/.rustscan.toml"
[~] Automatically increasing ulimit value to 5000.
Open {MACHINE-IP}:22
Open {MACHINE-IP}:1337
[~] Starting Script(s)
[>] Running script "nmap -vvv -p {{port}} -{{ipversion}} {{ip}} -sC -sV -oA w1seguy" on ip {MACHINE-IP}
Depending on the complexity of the script, results may take some time to appear.
[~] Starting Nmap 7.98 ( https://nmap.org ) at 2026-01-06 22:44 +0000
NSE: Loaded 158 scripts for scanning.
NSE: Script Pre-scanning.
NSE: Starting runlevel 1 (of 3) scan.
Initiating NSE at 22:44
Completed NSE at 22:44, 0.00s elapsed
NSE: Starting runlevel 2 (of 3) scan.
Initiating NSE at 22:44
Completed NSE at 22:44, 0.00s elapsed
NSE: Starting runlevel 3 (of 3) scan.
Initiating NSE at 22:44
Completed NSE at 22:44, 0.00s elapsed
Initiating Ping Scan at 22:44
Scanning {MACHINE-IP} [4 ports]
Completed Ping Scan at 22:44, 0.11s elapsed (1 total hosts)
Initiating SYN Stealth Scan at 22:44
Scanning w1seguy ({MACHINE-IP}) [2 ports]
Discovered open port 1337/tcp on {MACHINE-IP}
Discovered open port 22/tcp on {MACHINE-IP}
Completed SYN Stealth Scan at 22:44, 0.10s elapsed (2 total ports)
Initiating Service scan at 22:44
Scanning 2 services on w1seguy ({MACHINE-IP})
Completed Service scan at 22:45, 10.65s elapsed (2 services on 1 host)
NSE: Script scanning {MACHINE-IP}.
NSE: Starting runlevel 1 (of 3) scan.
Initiating NSE at 22:45
Completed NSE at 22:45, 2.35s elapsed
NSE: Starting runlevel 2 (of 3) scan.
Initiating NSE at 22:45
Completed NSE at 22:45, 0.16s elapsed
NSE: Starting runlevel 3 (of 3) scan.
Initiating NSE at 22:45
Completed NSE at 22:45, 0.00s elapsed
Nmap scan report for w1seguy ({MACHINE-IP})
Host is up, received reset ttl 62 (0.078s latency).
Scanned at 2026-01-06 22:44:51 GMT for 13s

PORT     STATE SERVICE REASON         VERSION
22/tcp   open  ssh     syn-ack ttl 62 OpenSSH 8.2p1 Ubuntu 4ubuntu0.13 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 37:ea:0c:b2:5e:44:19:ac:02:d6:4b:0c:19:fd:a9:73 (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDtnT4ZhRT8UBpyWlzDWTgWUqFfTBtx4ih502jle+VUtNtHxxa+I1EINePsMrDiJzJOKjLZBM91ObT/LTjXM+BugerpQ5rgk4g5XjOja02aSguO1D97cGkSr+gLidn0fywFkBzdNxUu+k4w7LCwI/uwRggIw5JoFF7SdbaQdi2/wFeBYL5nKNNjnYWfB6r7qvo2IWzfdhFBy6/4yA8dxf09FmANLww8ZklGAJz+GYrcfzMfquK3GOh8dDbgp6GbXw2hL/CYz09RtGsHNcqSD2LjUSQJDCTs4WBPcEGOxwil42cG4wasRmMSFGNALOlt58XS3efA1CR0Os4rk3Y3xzRLpbBWrNDC91lcd+KKdAJQb+3ZrIhwnWX2LTwpHZUk/Ouxw0ObIZP65s7XYwsEJ3FlFcBnZZQ4xuEz1s5jdxwESeA/HHRMu6E8BFDCJ/JwR7VvH6RvzUlVUnG4KlpOt749fG1NHWJC0mmoBppzVu0RFDNPzGXiclh51UEAKg8apJk=
|   256 ab:7c:52:dc:18:e8:a8:04:41:2c:96:af:d5:fc:92:74 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCYaG/N3J+FltmslZ+TmKdTMRcvQjv7eo7/x56RV9ir3ABzpIuQ/OaNPW1qp6D44YBYLDEGRqLHMHPxMo9J/d1E=
|   256 d3:c5:b4:da:9d:63:9a:64:9d:7e:6d:0c:4d:23:ef:29 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHoVLJ9HZPGazHhE3K1h+7vi71w5ThXPgP52dVbXGlZd
1337/tcp open  waste?  syn-ack ttl 62
| fingerprint-strings: 
|   DNSStatusRequestTCP: 
|     This XOR encoded text has flag 1: 2d3a093f284813282a2c3c0a30052c0d46272f3b381c367739153e3d2c0d0b063d742d0b0a0b3625
|     What is the encryption key? Nope nope nope
|   DNSVersionBindReqTCP: 
|     This XOR encoded text has flag 1: 3e7123313a5b5802243e2f411a0b3e1e0d0d21292b571c792b067517221f184d177a3f1841213837
|     What is the encryption key? Close but no cigar
|   GenericLines: 
|     This XOR encoded text has flag 1: 610d394d450424185841703d0077414171175d56742b06055459090d5e6047310d0640473d3b4448
|     What is the encryption key? Close but no cigar
|   GetRequest: 
|     This XOR encoded text has flag 1: 002d2f124965040e074d111d16284d205101025a150b105a5838291b016c26111b594c261d2d1b44
|     What is the encryption key? No way you got it! Here is your flag THM{Try_Again} :)
|   HTTPOptions: 
|     This XOR encoded text has flag 1: 0327194d03660e3858071217207707235b375d1016012605123b232d5e26251b2d060625171b440e
|     What is the encryption key? Close but no cigar
|   Help: 
|     This XOR encoded text has flag 1: 1771081118725829041c0641312b1c370d26010b02573759092f753c023d314d3c5a1d31410a1815
|     What is the encryption key? Nope nope nope
|   NULL: 
|     This XOR encoded text has flag 1: 610d394d450424185841703d0077414171175d56742b06055459090d5e6047310d0640473d3b4448
|     What is the encryption key?
|   RPCCheck: 
|     This XOR encoded text has flag 1: 10182f0a0475310e1f0001281630003064011a17053e104215281c1b192136241b410136282d0309
|     What is the encryption key?
|   RTSPRequest: 
|     This XOR encoded text has flag 1: 01072e2926642e0f3c221037171322217b003935142111613739031a3a03273b1a622327372c202b
|_    What is the encryption key? No way you got it! Here is your flag THM{Try_Again} :)
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port1337-TCP:V=7.98%I=7%D=1/6%Time=695D9069%P=x86_64-pc-linux-gnu%r(NUL
SF:L,8F,"This\x20XOR\x20encoded\x20text\x20has\x20flag\x201:\x20610d394d45
SF:0424185841703d0077414171175d56742b06055459090d5e6047310d0640473d3b4448\
SF:nWhat\x20is\x20the\x20encryption\x20key\?\x20")%r(GenericLines,A2,"This
SF:\x20XOR\x20encoded\x20text\x20has\x20flag\x201:\x20610d394d450424185841
SF:703d0077414171175d56742b06055459090d5e6047310d0640473d3b4448\nWhat\x20i
SF:s\x20the\x20encryption\x20key\?\x20Close\x20but\x20no\x20cigar\n")%r(Ge
SF:tRequest,C6,"This\x20XOR\x20encoded\x20text\x20has\x20flag\x201:\x20002
SF:d2f124965040e074d111d16284d205101025a150b105a5838291b016c26111b594c261d
SF:2d1b44\nWhat\x20is\x20the\x20encryption\x20key\?\x20No\x20way\x20you\x2
SF:0got\x20it!\x20Here\x20is\x20your\x20flag\x20THM{Try_Again}\x20:\)\n")%
SF:r(HTTPOptions,A2,"This\x20XOR\x20encoded\x20text\x20has\x20flag\x201:\x
SF:200327194d03660e3858071217207707235b375d1016012605123b232d5e26251b2d060
SF:625171b440e\nWhat\x20is\x20the\x20encryption\x20key\?\x20Close\x20but\x
SF:20no\x20cigar\n")%r(RTSPRequest,C6,"This\x20XOR\x20encoded\x20text\x20h
SF:as\x20flag\x201:\x2001072e2926642e0f3c221037171322217b00393514211161373
SF:9031a3a03273b1a622327372c202b\nWhat\x20is\x20the\x20encryption\x20key\?
SF:\x20No\x20way\x20you\x20got\x20it!\x20Here\x20is\x20your\x20flag\x20THM
SF:{Try_Again}\x20:\)\n")%r(RPCCheck,8F,"This\x20XOR\x20encoded\x20text\x2
SF:0has\x20flag\x201:\x2010182f0a0475310e1f0001281630003064011a17053e10421
SF:5281c1b192136241b410136282d0309\nWhat\x20is\x20the\x20encryption\x20key
SF:\?\x20")%r(DNSVersionBindReqTCP,A2,"This\x20XOR\x20encoded\x20text\x20h
SF:as\x20flag\x201:\x203e7123313a5b5802243e2f411a0b3e1e0d0d21292b571c792b0
SF:67517221f184d177a3f1841213837\nWhat\x20is\x20the\x20encryption\x20key\?
SF:\x20Close\x20but\x20no\x20cigar\n")%r(DNSStatusRequestTCP,9E,"This\x20X
SF:OR\x20encoded\x20text\x20has\x20flag\x201:\x202d3a093f284813282a2c3c0a3
SF:0052c0d46272f3b381c367739153e3d2c0d0b063d742d0b0a0b3625\nWhat\x20is\x20
SF:the\x20encryption\x20key\?\x20Nope\x20nope\x20nope\n")%r(Help,9E,"This\
SF:x20XOR\x20encoded\x20text\x20has\x20flag\x201:\x201771081118725829041c0
SF:641312b1c370d26010b02573759092f753c023d314d3c5a1d31410a1815\nWhat\x20is
SF:\x20the\x20encryption\x20key\?\x20Nope\x20nope\x20nope\n");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

NSE: Script Post-scanning.
NSE: Starting runlevel 1 (of 3) scan.
Initiating NSE at 22:45
Completed NSE at 22:45, 0.00s elapsed
NSE: Starting runlevel 2 (of 3) scan.
Initiating NSE at 22:45
Completed NSE at 22:45, 0.00s elapsed
NSE: Starting runlevel 3 (of 3) scan.
Initiating NSE at 22:45
Completed NSE at 22:45, 0.00s elapsed
Read data files from: /usr/share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 13.76 seconds
           Raw packets sent: 6 (240B) | Rcvd: 3 (128B)

As expected we have 1337 open, hmmm some interesting output...... did nmap just get the first flag for us ?

1337/tcp open  waste?  syn-ack ttl 62
| fingerprint-strings: 
|   DNSStatusRequestTCP: 
|     This XOR encoded text has flag 1: 2d3a093f284813282a2c3c0a30052c0d46272f3b381c367739153e3d2c0d0b063d742d0b0a0b3625
|     What is the encryption key? Nope nope nope
|   DNSVersionBindReqTCP: 
|     This XOR encoded text has flag 1: 3e7123313a5b5802243e2f411a0b3e1e0d0d21292b571c792b067517221f184d177a3f1841213837
|     What is the encryption key? Close but no cigar
|   GenericLines: 
|     This XOR encoded text has flag 1: 610d394d450424185841703d0077414171175d56742b06055459090d5e6047310d0640473d3b4448
|     What is the encryption key? Close but no cigar
|   GetRequest: 
|     This XOR encoded text has flag 1: 002d2f124965040e074d111d16284d205101025a150b105a5838291b016c26111b594c261d2d1b44
|     What is the encryption key? No way you got it! Here is your flag THM{Try_Again} :)
|   HTTPOptions: 
|     This XOR encoded text has flag 1: 0327194d03660e3858071217207707235b375d1016012605123b232d5e26251b2d060625171b440e
|     What is the encryption key? Close but no cigar
|   Help: 
|     This XOR encoded text has flag 1: 1771081118725829041c0641312b1c370d26010b02573759092f753c023d314d3c5a1d31410a1815
|     What is the encryption key? Nope nope nope
|   NULL: 
|     This XOR encoded text has flag 1: 610d394d450424185841703d0077414171175d56742b06055459090d5e6047310d0640473d3b4448
|     What is the encryption key?
|   RPCCheck: 
|     This XOR encoded text has flag 1: 10182f0a0475310e1f0001281630003064011a17053e104215281c1b192136241b410136282d0309
|     What is the encryption key?
|   RTSPRequest: 
|     This XOR encoded text has flag 1: 01072e2926642e0f3c221037171322217b003935142111613739031a3a03273b1a622327372c202b
|_    What is the encryption key? No way you got it! Here is your flag THM{Try_Again} :)

NOPE!, oh well ....... Let's get hacking, first thing I do is look at the source code above and throw in some print statements

─$ diff -y source.orig.py source.py 
#!/usr/bin/env python                                           #!/usr/bin/env python
import random                                                   import random
import socketserver                                             import socketserver 
import socket, os                                               import socket, os
import string                                                   import string

flag = open('flag.txt','r').read().strip()                      flag = open('flag.txt','r').read().strip()

def send_message(server, message):                              def send_message(server, message):
    enc = message.encode()                                          enc = message.encode()
    server.send(enc)                                                server.send(enc)

def setup(server, key):                                         def setup(server, key):
    flag = 'THM{thisisafakeflag}'                                   flag = 'THM{thisisafakeflag}' 
    xored = ""                                                      xored = ""

    for i in range(0,len(flag)):                                    for i in range(0,len(flag)):
        xored += chr(ord(flag[i]) ^ ord(key[i%len(key)]))               xored += chr(ord(flag[i]) ^ ord(key[i%len(key)]))

    hex_encoded = xored.encode().hex()                              hex_encoded = xored.encode().hex()
                                                              >     print(hex_encoded)
    return hex_encoded                                              return hex_encoded                                                                                  

def start(server):                                              def start(server):
    res = ''.join(random.choices(string.ascii_letters + strin       res = ''.join(random.choices(string.ascii_letters + strin
    key = str(res)                                                  key = str(res)
                                                              >     print(key)
    hex_encoded = setup(server, key)                                hex_encoded = setup(server, key)                                                                    
    send_message(server, "This XOR encoded text has flag 1: "       send_message(server, "This XOR encoded text has flag 1: "
                                                                    
    send_message(server,"What is the encryption key? ")             send_message(server,"What is the encryption key? ")
    key_answer = server.recv(4096).decode().strip()                 key_answer = server.recv(4096).decode().strip()

    try:                                                            try:
        if key_answer == key:                                           if key_answer == key:
            send_message(server, "Congrats! That is the corre               send_message(server, "Congrats! That is the corre
            server.close()                                                  server.close()
        else:                                                           else:
            send_message(server, 'Close but no cigar' + "\n")               send_message(server, 'Close but no cigar' + "\n")
            server.close()                                                  server.close()
    except:                                                         except:
        send_message(server, "Something went wrong. Please tr           send_message(server, "Something went wrong. Please tr
        server.close()                                                  server.close()

class RequestHandler(socketserver.BaseRequestHandler):          class RequestHandler(socketserver.BaseRequestHandler):
    def handle(self):                                               def handle(self):
        start(self.request)                                             start(self.request)

if __name__ == '__main__':                                      if __name__ == '__main__':
    socketserver.ThreadingTCPServer.allow_reuse_address = Tru       socketserver.ThreadingTCPServer.allow_reuse_address = Tru
    server = socketserver.ThreadingTCPServer(('0.0.0.0', 1337       server = socketserver.ThreadingTCPServer(('0.0.0.0', 1337
    server.serve_forever()                                    /     server.serve_forever()

I then create a flag.txt file and run my version python3 source.py with the print statements which starts the TCP service, I then use nc 127.0.0.1 1337 to connect to the server

  • Client
|└─$ nc 127.0.0.1 1337       
This XOR encoded text has flag 1: 193c022b31251d3c39362c122e3b202b182e3738
What is the encryption key
  • Server Output
└─$ python3 ./source.py 
MtOPE
193c022b31251d3c39362c122e3b202b182e373

I then take the output of the server and throw it into CyberChef, this then returns the string we expect (THM{thisisafakeflag})

cyberchef.png

This means that we know we need to decode the cipher from hex and then bruteforce the key to decode the XOR. Now to brute force it can take a while and the cipher changes each time you connect to the server which poses a bit of an issue. We also only know the first 4 characters THM{ which will then return allot of possible options (65) for the key. I went back and to with an AI to vibe code the below solution....

# Import pwn to allow connecting to the remote server 
from pwn import *
# Import cycle to loop through possible solutins
from itertools import cycle

# This function attempts to decrypt the XOR with the provided cipher and key
def decrypt_xor(hex_str: str, key: str) -> str:
    ct = bytes.fromhex(hex_str)
    return ''.join(
        chr(c ^ k)
        for c, k in zip(ct, cycle(key.encode()))
    )

# This check if the decrypted XOR starts with THM{ and end with } which should be the format we are expecting
def is_thm_flag(s: str) -> bool:
    return s.startswith("THM{") and s.endswith("}")

# Connect to the remote server
r = remote("w1seguy", 1337)
# Put the server response into a variable
data = r.recvline().decode().strip()
# Print the response
print(data)
# Get the cipher from the response
cipher_hex = data.split("flag 1: ")[1]

# Convert to bytes from hex 
cipher = bytes.fromhex(cipher_hex)

# The bit of the decoded text we know
known = "THM{"             # first 4 characters

# Create a blank key
key = ""
# loop through the known bit of the key and recovering the start of the key
for i in range(4):
    k = cipher[i] ^ ord(known[i])
    key += chr(k)

# Print the start of the key we recovered
print("Partial key (first 4 chars):", repr(key))

# As we dont know the 5th char but do know it is either letter or number we need to loop through what is possible
for c in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789":
    # set test key to be the known bit from above and the character we are testing
    testkey = (key + c)
    # Set the output to decryted XOR of the cipher and out test key
    output = decrypt_xor(cipher_hex, testkey)
    # use the above function to test if output starts with `THM{` and ends with `}`
    if is_thm_flag(output) == True:
        # If it does we will print the testkey and output which is flag1
        print(testkey + " " + output)
        # we will then send this testkey to the server
        r.sendline(testkey.encode())
        # we then grab the return from the server and hope we hit the jackpot and output what should include flag2
        data = r.recvline().decode()
        print(data)

Now let's run our script

└─$ python3 xor.py
[+] Opening connection to w1seguy on port 1337: Done
This XOR encoded text has flag 1: [REDACTED]
Partial key (first 4 chars): 'REDACTED'
Possible full keys:
REDACTED THM{[REDACTED]}
What is the encryption key? Congrats! That is the correct key! Here is flag 2: THM{[REDACTED]}

[*] Closed connection to w1seguy port 1337

BOOM!!!!! Both flags recovered!

Show Comments