Ecb Oracle

The service takes user input as plain text, appends the flag to it to make a padded string for ``AES_ECB``` to encrypt.

def encrypt(plaintext):
    plaintext = bytes.fromhex(plaintext)

    padded = pad(plaintext + FLAG.encode(), 16)
    cipher = AES.new(KEY, AES.MODE_ECB)

    try:
        encrypted = cipher.encrypt(padded)
    except ValueError as e:
        return {"error": str(e)}

    return {"ciphertext": encrypted.hex()}

According to the way ECB mode works, if the input is “111111111111111”, as the flag startswith “crypto{” plain text + FLAG becomes “111111111111111crypto{…}”. The first 16 bytes of cipher text of “111111111111111ccrypto{…}” and the first 16 bytes of cipher text of “111111111111111crypto{…}” should be the same.

print('  ', end='')
print_blk(encrypt(bytes.hex(b'1'*15)), 32)

for i in range(ord('a'),ord('z')):
    print(chr(i), '', end='')
    print_blk(encrypt(bytes.hex(b'1'*15+int.to_bytes(i, 1, 'little'))), 32)
  220a332f2bf0be3f1c3973707be93dba  5245173a0a15bd37034d6761ed2a7fba  6397a55a1e310cbc60120becb2d9453f  <= cipher text of "111111111111111crypto{...}"
a 33a2d339a8fab0ae628424d69a015106  d0312d0b8a979846ca0bb572e29eff03  dd362b0c5b430f4c2aaec01ee288ce24  
b dd498d35ca964fd218ce3e7bc3cca1cc  d0312d0b8a979846ca0bb572e29eff03  dd362b0c5b430f4c2aaec01ee288ce24  
c 220a332f2bf0be3f1c3973707be93dba  d0312d0b8a979846ca0bb572e29eff03  dd362b0c5b430f4c2aaec01ee288ce24  <= cipher text of "111111111111111ccrypto{...}"
d 91a7b888c4f4f3be960ab9a7f3460776  d0312d0b8a979846ca0bb572e29eff03  dd362b0c5b430f4c2aaec01ee288ce24
...

By adding verified characters one by one shall be able to get the flag. As the length of flag is unknown, compare the second block, cipher[32:64] by guessing. Also needed to add time to sleep to avoid request rate exceeded error from server.

def bruteforce():
    flag = ''
    total = 32 - 1
    alphabet = '_'+'@'+'}'+string.digits+string.ascii_lowercase+string.ascii_uppercase

    while True:
        payload = '1' * (total-len(flag))
        expected = encrypt(payload.encode().hex())
        print('E', '', end='')
        print_blk(expected, 32)
        
        for c in alphabet: 
            res = encrypt(bytes.hex((payload + flag + c).encode()))
            print(c, '', end='')
            print_blk(res, 32)
            if res[32:64] == expected[32:64]:
                flag += c
                print(flag)
                break
            time.sleep(1)

        if flag.endswith('}'): break

    print(flag)

Waited for a while the flag came out to be crypto{p3n6u1n5_h473_3cb}.

The full code is here.

TOP