W1seGuy

Lien vers l’épreuve : https://tryhackme.com/room/w1seguy

Easy

Sommaire

Préparation

Nous avons à notre disposition le programme tournant sur la machine cible.

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

Ce qui nous intéresse dans ce contexte, c’est que les données sont chiffrés avec la méthode XOR et une clé générée aléatoirement que nous devons trouver pour déchiffrer le premier flag avancer vers le second.

L’opération XOR étant symétrique et réversible, nous pouvons retrouver le message d’origine en réappliquant la même méthode. Mais il nous faut d’abord trouver la clé. Celle-ci est générée aléatoirement avec une suite de caractères ASCII et de chiffres, sur 5 caractères : res = ''.join(random.choices(string.ascii_letters + string.digits, k=5))

Voici une proposition de script permettant de rechercher la clé, de l’afficher, ainsi que le message déchiffré. Ce script à pour but d’être utilisé dans n’importe quel cas de figure et non seulement pour ce défi. Il en devient donc plus lourd.

#!/bin/env python3
import argparse

def get_input():
    parser = argparse.ArgumentParser(description="Trouver la clé d'un message chiffré avec XOR")
    parser.add_argument('--input', '-i', required=True, type=str, help="Message chiffré en XOR + hexadécimal")
    parser.add_argument('--begin', '-b', required=False, default=None, type=str, help="Premiers caractères connus du message initial")
    parser.add_argument('--end', '-e', required=False, default=None, type=str, help="Derniers caractères connus du message initial")
    parser.add_argument('--key_length', '-k', required=True, type=int, help="Longueur de la clé")
    
    args = parser.parse_args()
    return args
    
def convert_input(input_message):
    input_message_bytes = bytes.fromhex(input_message)
    return input_message_bytes

def guess_key(message_begin, message_end, key_length, input_message_bytes):
    key_bytes = bytearray(key_length)

    if message_begin is not None:
        message_begin_bytes = message_begin.encode('utf-8')
        for i in range(min(len(message_begin_bytes), len(input_message_bytes))):
            key_bytes[i % key_length] = input_message_bytes[i] ^ message_begin_bytes[i]

    if message_end is not None:
        message_end_bytes = message_end.encode('utf-8')
        input_message_length = len(input_message_bytes)
        end_pos = input_message_length - len(message_end_bytes)
        for i in range(len(message_end_bytes)):
            key_index = (end_pos + i) % key_length
            # On écrase toujours, même si déjà rempli par le début
            key_bytes[key_index] = input_message_bytes[end_pos + i] ^ message_end_bytes[i]

    key_str = bytes(key_bytes).decode('utf-8', errors='replace')
    key_hex = bytes(key_bytes).hex()
    return key_str, key_bytes

def verify_key(key_bytes, input_message_bytes):
    key_length = len(key_bytes)
    decrypted = bytearray(len(input_message_bytes))
    for i in range(len(input_message_bytes)):
        decrypted[i] = input_message_bytes[i] ^ key_bytes[i % key_length]
    return decrypted.decode('utf-8', errors='replace')

if __name__ == "__main__":
    args = get_input()
    input_message_bytes = convert_input(args.input)
    key_str, key_bytes = guess_key(args.begin, args.end, args.key_length, input_message_bytes)
    plaintext_message = verify_key(key_bytes, input_message_bytes)
    print(plaintext_message)
    print(key_str)

En l’appelant, nous obtenons la clé et le contenu du message chiffré.

python3 FindKey.py -i "391b7a4d[...expurgé...]2b78442c" -b "THM{" -e "}" -k 5
Afficher la réponse
THM{[...expurgé...]}
mS76Q


On peut ensuite utiliser cette clé dans le programme et récupérer le second flag.

nc 10.80.136.219 1337
Afficher la réponse
This XOR encoded text has flag 1: 391b7a4d[...expurgé...]2b78442c
What is the encryption key? mS76Q
Congrats! That is the correct key! Here is flag 2: THM{[...expurgé...]}