/ crypto

TUMCTF - Haggis

Enoncé:
OMG, I forgot to pierce my haggis’ sheep stomach before cooking it, so it exploded all over my kitchen. Please help me clean up!

nc 104.198.243.170 2501

Fichier: vuln.py

On commence par lancer la commande netcat :

$ nc 104.198.243.170 2501
ca9cad4d435395b10e8ee8eff4495ff5

Puis on ouvre le code source du serveur (vuln.py) :

#!/usr/bin/env python3
import os, binascii, struct
from Crypto.Cipher import AES

pad = lambda m: m + bytes([16 - len(m) % 16] * (16 - len(m) % 16))
def haggis(m):
    crypt0r = AES.new(bytes(0x10), AES.MODE_CBC, bytes(0x10))
    return crypt0r.encrypt(len(m).to_bytes(0x10, 'big') + pad(m))[-0x10:]

target = os.urandom(0x10)
print(binascii.hexlify(target).decode())

msg = binascii.unhexlify(input())

if msg.startswith(b'I solemnly swear that I am up to no good.\0') \
        and haggis(msg) == target:
    print(open('flag.txt', 'r').read().strip())

Ok nous devons donc envoyer au serveur une chaine en hexa qui une fois décodée, commence par 'I solemnly swear that I am up to no good.\0' et dont le dernier bloc du chiffré en AES (fonction haggis) soit égal à ce que le serveur nous envoie en hexa.

On peut remarquer que dans la fonction haggis, du padding est ajouté à msg avant d'être chiffré.

Faisons en sorte la longueur de msg soit un multiple de 16 pour que le padding soit egal à \x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10.

msg = b'I solemnly swear that I am up to no good.\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'
len(msg) = 64
pad(msg) = b'I solemnly swear that I am up to no good.\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10'

A ce stade si on chiffre msg on obtient ceci :

target = '\xca\x9c\xadMCS\x95\xb1\x0e\x8e\xe8\xef\xf4I_\xf5'
cipher = haggis(msg)
'^\x07\n\xdeS=.\t\x0e\xd0\xf5\xbe\x13\xbc\t\x83I\x9f\xa9\x11\xcbb\xdei\x80\xd2\xe3\x17\xf6\xe0\xac\xa2\xaeHfN\xb7OQ\x1d\xea\xc1^E\xe9~\xf2\xf3\x01v\x97\xc4\x11\x0f\xa9\xb0\x82I\x94\xeb{\xa4\xa45O\xe0\\\xe4\xd4\x0eR\x00<\x1c\x99\xdd\x85\x13\x16&\x81\x02\x80P\xbcD\x14\xbbl\x1bYffE\x02\x01'
cipher[-16:] != target

Maintenant calculons l'iv qui déchiffre ce que nous à envoyé le serveur en \x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10. L'iv calculé correspondra à l'avant dernier bloc du chiffré cible. (https://upload.wikimedia.org/wikipedia/commons/thumb/2/2a/CBC_decryption.svg/601px-CBC_decryption.svg.png)

def dehaggis(m):
    decrypt0r = AES.new(bytes(0x10), AES.MODE_CBC, bytes(0x10))
    return decrypt0r.decrypt(m)

plainlast = b'\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10'

plaintarget = dehaggis(target)
hextarget = binascii.hexlify(plaintarget)
hexplain = binascii.hexlify(plainlast)

int_enc = int(hextarget, 16) ^ int(hexplain, 16)
hex_enc = "%x" % int_enc

prelast = binascii.unhexlify(hex_enc)
prelast = 'z\xabgnu\xa8S\x8c8\xd4\xad\x03\x17\xe4\x8f\xa1'

Maintenant il ne reste plus qu'à remplacer les deux derniers blocs du chiffré de msg par l'iv qu'on vient de calculer et le message du serveur :

cipher = cipher[:-32]+prelast+target
cipher = '^\x07\n\xdeS=.\t\x0e\xd0\xf5\xbe\x13\xbc\t\x83I\x9f\xa9\x11\xcbb\xdei\x80\xd2\xe3\x17\xf6\xe0\xac\xa2\xaeHfN\xb7OQ\x1d\xea\xc1^E\xe9~\xf2\xf3\x01v\x97\xc4\x11\x0f\xa9\xb0\x82I\x94\xeb{\xa4\xa45z\xabgnu\xa8S\x8c8\xd4\xad\x03\x17\xe4\x8f\xa1\xca\x9c\xadMCS\x95\xb1\x0e\x8e\xe8\xef\xf4I_\xf5'

On déchiffre le tout pour obtenir notre msg en clair :

msg = unpad(dehaggis(cipher))[16:]
msg = 'I solemnly swear that I am up to no good.\x00\x00\x00\x00\x00\x00\x00Fn\xfeq2T[\x95\xde\x1c\x88\x8f@\xe4\xd53'

On enlève le premier bloc ([:16]) qui correspond à len(m).to_bytes(0x10, 'big') dans la fonction haggis.
Enfin on envoie msg en hexa au serveur pour obtenir le flag :

$ nc 104.198.243.170 2501
ca9cad4d435395b10e8ee8eff4495ff5
4920736f6c656d6e6c792073776561722074686174204920616d20757020746f206e6f20676f6f642e00000000000000466efe7132545b95de1c888f40e4d533
hxp{PLz_us3_7h3_Ri9h7_PRiM1TiV3z}

Le script complet pour générer l'hexa à envoyer en fonction du urandom envoyé par le serveur :

#!/usr/bin/env python3
import binascii, sys
from Crypto.Cipher import AES

pad = lambda m: m + bytes([16 - len(m) % 16] * (16 - len(m) % 16))
unpad = lambda m: m[0:-m[-1]]

def haggis(m):
    crypt0r = AES.new(bytes(0x10), AES.MODE_CBC, bytes(0x10))
    return crypt0r.encrypt(len(m).to_bytes(0x10, 'big') + pad(m))

def dehaggis(m):
    decrypt0r = AES.new(bytes(0x10), AES.MODE_CBC, bytes(0x10))
    return decrypt0r.decrypt(m)
    
target = binascii.unhexlify(sys.argv[1])
#print (target)

msg = b'I solemnly swear that I am up to no good.\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'
cipher = haggis(msg)
# print (cipher)

plainlast = b'\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10'

plaintarget = dehaggis(target)
#print (plaintarget)
hextarget = binascii.hexlify(plaintarget)
hexplain = binascii.hexlify(plainlast)
int_enc = int(hextarget, 16) ^ int(hexplain, 16)
hex_enc = "%x" % int_enc
prelast = binascii.unhexlify(hex_enc)
#print (prelast)

cipher = cipher[:-32]+prelast+target
msg = unpad(dehaggis(cipher))[16:]
print (msg)
print (haggis(msg))
print (target)
print ('\n')
print (binascii.hexlify(msg))