Challenge Description

Bob wanted to make his own CBC but he might have mixed something up.

Flag format: CTF{sha256}

Flag proof

CTF{5e67e23419ed20e412b6ccff67e1bb0eae02c71c2f6c7a6260af406858332c90}

Summary

Figure out that Bob XORed the ciphertext instead of the ciphertext with the AES blocks, and use the chosen ciphertext attack on the encrypted string to find the flag. (due to it still being ECB)

Details

I'd like to start of by saying this challenge was amazing. I really enjoyed sitting still trying my hardest to solve it, and learning new things. The experience was unforgettable.

I started off by doing some research on the ECB mode vs the CBC mode. The important parts that I found out about ECB were:

I already knew what CBC was supposed to do from the last UNbreakable 2021 Individual, so I started checking out the code:

import base64 as b64
from Crypto.Cipher import AES
from Crypto import Random
from Crypto.Util.Padding import pad
import math

with open('flag.txt') as f:
    prefix = f.readline().rstrip()
    flag = f.readline().rstrip()

rgen = Random.new()
BLOCK_SIZE = [16,32][ord(rgen.read(1))%2]
key = rgen.read(BLOCK_SIZE)
iv = rgen.read(BLOCK_SIZE)

def split_blocks(x):
    n = math.ceil(len(x)/BLOCK_SIZE)
    return [x[BLOCK_SIZE*i:BLOCK_SIZE*(i+1)] for i in range(n)]

def xor(x, y):
    return bytes(a ^ b for (a, b) in zip(x, y))

def encrypt(msg):
    ora = AES.new(key, AES.MODE_ECB)
    msg = prefix+msg+flag
    msg = bytes(msg,'raw_unicode_escape')

    cip = ora.encrypt(pad(msg,BLOCK_SIZE))
    blocks = [iv,]+split_blocks(cip)

    for i in range(1,len(blocks)):
        blocks[i]=xor(blocks[i-1],blocks[i])

    return b''.join(blocks)

def menu():
    print("Enter message to encrypt:")

    while True:
        try:
            msg = input('Msg:')
            cipher = b64.b64encode(encrypt(msg))
            print(cipher.decode('utf-8'))
        except:
            exit()

if __name__ == '__main__':
    menu()

I noticed that the BLOCK_SIZE can be either 32 or 16 bytes long. This made me lose waayyyy too much time, but turned out it wasn't significant for the exploit.

I created a diagram to show how the program creates it's CBC, and placed it next to the way CBC is actually supposed to work: