Dylan's Blog

HTB Cyber Apocalypse 2023 - Crypto Challenge

Category: CTF

Description

Challenge Description

source.py

from os import urandom
from Crypto.Cipher import AES
from secret import MESSAGE

assert all([x.isupper() or x in '{_} ' for x in MESSAGE])


class Cipher:

    def __init__(self):
        self.salt = urandom(15)
        key = urandom(16)
        self.cipher = AES.new(key, AES.MODE_ECB)

    def encrypt(self, message):
        return [self.cipher.encrypt(c.encode() + self.salt) for c in message]


def main():
    cipher = Cipher()
    encrypted = cipher.encrypt(MESSAGE)
    encrypted = "\n".join([c.hex() for c in encrypted])

    with open("output.txt", 'w+') as f:
        f.write(encrypted)


if __name__ == "__main__":
    main()

Solution

Since each character of the secret message is encrypted individually and with the same salt, we can do frequency analysis on each line of ciphertext and map them back to the most common letters that show up in English text.

I create a mapping of the ciphertext to english letters and in this case, I set a space as the most common character since I assumed we’re dealing with a paragraph of text. I know I’m also missing some extra chars like “_”, “{“ and “}”, so I’ll just substitute ‘x’ in for now and see what comes back.

Solution Script

lookup = {
    '61331054d82aeec9a20416759766d9d5': ' ',
    'c87a7eb9283e59571ad0cb0c89a74379': 'T',
    '200ecd2657df0197f202f258b45038d8': 'A',
    '5f122076e17398b7e21d1762a61e2e0a': 'O',
    '68d763bc4c7a9b0da3828e0b77b08b64': 'I',
    '305d4649e3cb097fb094f8f45abbf0dc': 'N',
    'e9b131ab270c54bbf67fb4bd9c8e3177': 'S',
    '34ece5ff054feccc5dabe9ae90438f9d': 'H',
    '8cbd4cfebc9ddf583a108de1a69df088': 'R',
    'f89f2719fb2814d9ab821316dae9862f': 'D',
    '457165130940ceac01160ac0ff924d86': 'L',
    '3a17ebebf2bad9aa0dd75b37a58fe6ea': 'C',
    'd178fac67ec4e9d2724fed6c7b50cd26': 'U',
    'e23c1323abc1fc41331b9cdfc40d5856': 'M',
    '5d7185a6823ab4fc73f3ea33669a7bae': 'W',
    'dfc8a2232dc2487a5455bda9fa2d45a1': 'F',
    '2190a721b2dcb17ff693aa5feecb3b58': 'G',
    '78de2d97da222954cce639cc4b481050': 'Y',
    '9673dbe632859fa33b8a79d6a3e3fe30': 'P',
    '4a3af0b7397584c4d450c6f7e83076aa': 'B',
    '66975492b6a53cc9a4503c3a1295b6a7': 'V',
    'fb78aed37621262392a4125183d1bfc9': 'K',
    '0df9b4e759512f36aaa5c7fd4fb1fba8': 'J',
    '293f56083c20759d275db846c8bfb03e': 'X',
    '60e8373bfb2124aea832f87809fca596': 'Q',
    '2fc20e9a20605b988999e836301a2408': 'Z'
}

result = ""

with open("output.txt", "r") as f:
    lines = f.readlines()

    for l in lines:
        try:
            result += lookup[l.strip()]
        except:
            result += "x"

print(result)

Running the above script produces the following text, which looks like it might be a substitution cipher. Since the challenge description mentioned quipqiup, let’s plug it in and see what it produces.

python3 solve.py 
FNTQUTHLW OHODWASA SA POATM RH ICT FOLI ICOI SH OHW YSJTH AINTILC RF VNSIITH DOHYUOYT LTNIOSH DTIITNA OHM LRBPSHOISRHA RF DTIITNA RLLUN VSIC JONWSHY FNTQUTHLSTA BRNTRJTN ICTNT SA O LCONOLITNSAISL MSAINSPUISRH RF DTIITNA ICOI SA NRUYCDW ICT AOBT FRN ODBRAI ODD AOBGDTA RF ICOI DOHYUOYT SH LNWGIOHODWASA FNTQUTHLW OHODWASA ODAR KHRVH OA LRUHISHY DTIITNA SA ICT AIUMW RF ICT FNTQUTHLW RF DTIITNA RN YNRUGA RF DTIITNA SH O LSGCTNITZI ICT BTICRM SA UATM OA OH OSM IR PNTOKSHY LDOAASLOD LSGCTNA FNTQUTHLW OHODWASA NTQUSNTA RHDW O POASL UHMTNAIOHMSHY RF ICT AIOISAISLA RF ICT GDOSHITZI DOHYUOYT OHM ARBT GNRPDTB ARDJSHY AKSDDA OHM SF GTNFRNBTM PW COHM IRDTNOHLT FRN TZITHASJT DTIITN PRRKKTTGSHY MUNSHY VRNDM VON SS PRIC ICT PNSISAC OHM ICT OBTNSLOHA NTLNUSITM LRMTPNTOKTNA PW GDOLSHY LNRAAVRNM GUXXDTA SH BOxRN HTVAGOGTNA OHM NUHHSHY LRHITAIA FRN VCR LRUDM ARDJT ICTB ICT FOAITAI ATJTNOD RF ICT LSGCTNA UATM PW ICT OZSA GRVTNA VTNT PNTOKOPDT UASHY FNTQUTHLW OHODWASA FRN TZOBGDT ARBT RF ICT LRHAUDON LSGCTNA UATM PW ICT xOGOHTAT BTLCOHSLOD BTICRMA RF DTIITN LRUHISHY OHM AIOISAISLOD OHODWASA YTHTNODDW CIPxOxASBGDTxAUPAISIUISRHxSAxVTOKx LONM IWGT BOLCSHTNW VTNT FSNAI UATM SH VRNDM VON SS GRAASPDW PW ICT UA ONBWA ASA IRMOW ICT CONM VRNK RF DTIITN LRUHISHY OHM OHODWASA COA PTTH NTGDOLTM PW LRBGUITN ARFIVONT VCSLC LOH LONNW RUI AULC OHODWASA SH ATLRHMA VSIC BRMTNH LRBGUISHY GRVTN LDOAASLOD LSGCTNA ONT UHDSKTDW IR GNRJSMT OHW NTOD GNRITLISRH FRN LRHFSMTHISOD MOIO GUXXDT GUXXDT GUXXDT

Looks like it managed to solve it.

quipqiup

flag

HTBzAzSIMPLEzSUBSTITUTIONzISzWEAKz

I’ll just edit it to match the known flag format and we have our flag: HTB{A_SIMPLE_SUBSTITUTION_IS_WEAK}