The code is vulnerable to hash length extension attack. The hash for a message with a new block appended can be computed from the hash of the old message, and xor with the result of encrypting the old hash with the key as the new block. Denote $H$ as the old hash, $H’$ as the new hash, the newly appended block as $B$, and encryption as $E$, we have the relation:

$$ H' = H \oplus E(H, B) $$

Hence the task is simply to reconstruct the correct state of the hash algorithm, then do a simple xor with the new block and we should obtain a valid hash. The padding is a bit tricky to get it correct, so I recommend testing the challenge code out locally first.

Python Implementation:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from pwn import * 
import json 
import os 
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad

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

io = remote('socket.cryptohack.org', 13388)
io.recvline()

# Randomly 15 bytes for less painful padding issues
r = os.urandom(15)
to_send = {'option': 'sign', 'message': r.hex()}
io.sendline(json.dumps(to_send).encode())
sig = json.loads(io.recvline().decode())['signature']
sig = bytes.fromhex(sig)

payload = b'admin=True' 

# Message is the padded random 15 bytes earlier plus the payload
m = pad(r, 16) + payload

sig = bxor(AES.new(pad(payload, 16), AES.MODE_ECB).encrypt(sig), sig)
to_send = {'option': 'get_flag', 'message': m.hex(), 'signature': sig.hex()}
io.sendline(json.dumps(to_send).encode())
io.interactive()