For this challenge, we were given access to a service. The description told us that it was an encryption oracle, which used AES to encrypt what we sent it, concatenated with critical data.

I’m writting this after the challenge ended, so I can’t include demos.

The description should make us think about byte-at-a-time ECB decryption attacks, which are well explained here:

I never used this attack before, but heard about it, so I decided to try it. It only works with the ECB mode, which I guessed was what the service was running. I could have tested this.

The premise is really simple: the ECB mode slices up the data in blocks, which are encrypted separately. Imagine we have encrypt(input || secret). If the block size is 4, what we could do is:

  • send aaa, and retrieve the first block data ; the first character of the secret will get added to form 'aaa' + secret[0],
  • send aaaa, and compare the first block data with the previous value ; if it matches, the first character of the secret is a,
  • otherwise, retry with aaab, aaac, …

We can script this out:

import string
from pwn import *

context.log_level = 'ERROR'

def service(inp):
    r = remote('', 29292)
    prompt = r.recvuntil(': ')
    output = r.recvall()
    return output

known = b""

while not known.endswith(b'}'):
    # One char short
    l = 64 - 1 - len(known)
    target = service(b'0' * l)[:64]

    for b in string.printable.encode():
        b = bytes([b])
        block = service(known.zfill(64 - 1) + b)[:64]

        if target == block:
            print(b.decode(), end='')
            known += b

I used 64 chars because that’s the size of the returned data. I’ve failed to retrieve the flag once or twice before because I tried with the block size (16) instead, which only retrieved the start of the secret.

$ python

The flag was CYBERTF{WTF_It’s_u$eless_in_real_w0rld}.

You can view the sources on github or read other writeups.