XOR Encoding Function

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

the server is generating a random key of len 5 and xoring the plaintext flag to provide an encrypted string.

in the setup function: if key len< flag len it repeats it

flag= 'THM{fakeflag}'
xored= ''

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

plaintext insight is first 4 letters are THM{ and last letter is }

We know, in xor encryption,

plaintext ^ key = cipher

also, cipher ^ plaintext= key

using 2nd eq to get the key part:

import argparse

def derive_key_part(hex_encoded, known_plaintext, start_index):
	encoded_bytes= bytes.fromhex(hex_encoded)
	derived_key=""
	for i in range(len(known_plaintext)):
		derived_key+=chr(encode_bytes[i+ start_index] ^ ord(know_plaintext[i])
		
	return derived_key

def xor_decrypt(hex_encoded, key):
	encrypted_bytes= bytes.fromhex(hex_encoded)
	decrypted_plaintext=""
	for i in range(len(hex_encoded)):
		decrypted_plaintext+= chr(encrypted_bytes[i] ^ ord(key[i%len(key)])
	return decrypted_plaintext
	
def main():
    parser = argparse.ArgumentParser(description='W1seGuy XOR Decryption')
    parser.add_argument('hex_encoded', type=str, help='Hex encoded string to decrypt')
    #parser.add_argument('key_length', type=int, help='Length of the encryption key')

    args = parser.parse_args()
    hex_encoded = args.hex_encoded
    key_length = 5

    # Example usage
    known_start_plaintext = 'THM{'
    known_end_plaintext = '}'

    # Derive the first part of the key using the known starting plaintext
    derived_key_start = derive_key_part(hex_encoded, known_start_plaintext, 0)
    print("Derived start of the key:", derived_key_start)

    # Derive the last character of the key using the known ending plaintext
    derived_key_end = derive_key_part(hex_encoded, known_end_plaintext, len(hex_encoded) // 2 - 1)
    print("Derived end of the key:", derived_key_end)

    # Since the key length is key_length, the derived key will repeat
    derived_key = (derived_key_start + derived_key_end)[0:key_length]
    print("Derived key:", derived_key)

    # Decrypt the full message using the derived key
    decrypted_message = xor_decrypt(hex_encoded, derived_key)
    print("Decrypted message:", decrypted_message)

if __name__ == '__main__':
    main()