# TUM CTF 2016 - haggis

The challenge is to create a plaintext with a given prefix that will result in a AES CBC-MAC mode using PKCS#7 padding.

### Description

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

(Fun fact: Haggis hurling is a thing.)

nc 104.198.243.170 2501

download

### Details

Points: 100 Basepoints + 100 Bonuspoints * min(1, 3/93 Solves)

Category: crypto

Validations: 93

### Solution

The server is sending 16 bytes of random data:

$nc 104.198.243.170 2501 6c762738c13149013a6ca5b30e8935df  According to the script, we had to respond by a message starting by ‘I solemnly swear that I am up to no good.\0’ and when authenticated with AES CBC-MAC using PKCS#7 padding the last ciphertext block matches these 16 bytes. Hopefully the key is known and it is all zero. So we can decrypt the given random byte with the zero key. We xored it with the previous ciphertext block and it will gave us the last plaintext block. The only problem is that the last byte of the block of the plaintext must finished by the byte 0x01 to have a correct padding. Thus I decided to bruteforce over the last byte of the previous plaintext byte until I got 0x01 at the end of the plaintext. Here is the complete solution: #!/usr/bin/env python3 import os, binascii, struct import socket from Crypto.Cipher import AES from Crypto.Util import strxor pad = lambda m: m + bytes([16 - len(m) % 16] * (16 - len(m) % 16)) def encrypt(m, length): crypt0r = AES.new(bytes(0x10), AES.MODE_CBC, bytes(0x10)) cipher = crypt0r.encrypt(length.to_bytes(0x10, 'big') + m) return cipher[-0x10:] def haggis(m): crypt0r = AES.new(bytes(0x10), AES.MODE_CBC, bytes(0x10)) cipher = crypt0r.encrypt(len(m).to_bytes(0x10, 'big') + pad(m)) return cipher[-0x10:] s = socket.socket() host = "104.198.243.170" port = 2501 s.connect((host, port)) target = binascii.unhexlify(s.recv(1024)[:-1]) print(binascii.hexlify(target).decode()) crypt1r = AES.new(bytes(0x10), AES.MODE_ECB) tail = crypt1r.decrypt(target) msg = b'I solemnly swear that I am up to no good.\0.....' for i in range(256): b = bytes([i]) block = encrypt(msg+b, len(msg+tail)) if strxor.strxor(tail, block)[15] == 1: break tail= strxor.strxor(tail, block) msg = msg + b + tail[:-1] if msg.startswith(b'I solemnly swear that I am up to no good.\0') and haggis(msg) == target: print("Sending to server...") s.send(binascii.hexlify(msg) + b"\n") target = s.recv(1024) print(target) s.close  It was not working every time since for some inputs one byte bruteforce is not sufficient. However, after few tried I was able to get the flag: $ ./solution.py
2ccf9b7c2bc5617c26f2110975f9008e
Sending to server...
b'hxp{PLz_us3_7h3_Ri9h7_PRiM1TiV3z}\n'
Done

Written on October 1, 2016