MACdonalds
MACdonalds
Platform: hackerlab | Category: Crypto | Type: Challenge | Difficulty: Medium | OS: NA | Author: XESXOR | Date: 2026-01-02 | Status: Solved Techniques: CBC-MAC length extension attack, MAC collision/forgery via message extension, XOR manipulation of cipher blocks
Summary
Task: CBC-MAC implementation without proper finalization, need to forge two different messages with the same MAC. Solution: Length extension attack - create m2 = m1 || (m1 XOR MAC(m1)) which produces the same MAC as m1.
MACdonalds - HackerLab
Description
"В один прекрасный день один из сотрудников МАКдональдса заявил что написал собственную неуязвимую версию MAC и спрятал какой-то флаг. После чего сообщил о своем желании стать программистом и уволился. Я поспорил, если найду флаг, он вернется на прежнюю работу, в качестве подсказки он оставил исходный код..."
Server: 192.169.2.100:11001
Analysis
Source Code
python read-only • copy alloweddef sign(k, m): ECB = AES.new(k, AES.MODE_ECB) mb = [m[i:i + 16] for i in range(0, len(m), 16)] t = ECB.encrypt(mb[0]) for i in range(1, len(mb)): t = ECB.encrypt(strxor(mb[i], t)) return hexlify(t)
Server Interface
- Option 0: Get MAC signature for a hex-encoded message
- Option 1: Submit forgery - two DIFFERENT messages with the SAME MAC signature
Vulnerability
This is a classic CBC-MAC implementation without proper finalization. CBC-MAC is vulnerable to length extension attack:
- If we know
MAC(m1) = t1, we can construct a longer messagem2that has the same MAC.
Attack Mathematics
For a single-block message m1 (16 bytes):
t1 = AES_k(m1)
Create a two-block message:
m2 = m1 || (m1 XOR t1)
When computing MAC(m2):
- First block:
t1 = AES_k(m1) - Second block:
AES_k((m1 XOR t1) XOR t1) = AES_k(m1) = t1
Thus:
MAC(m1) = MAC(m2) = t1
But m1 != m2 (different length), which is exactly what's required for forgery.
Solution
python read-only • copy allowed#!/usr/bin/env python3 from pwn import * from Crypto.Util.strxor import strxor # Connect and get MAC for m1 r = remote('192.169.2.100', 11001) r.recvuntil(b'!') r.sendline(b'0') r.recvuntil(b':') # Single-block message (16 bytes) m1 = b'A' * 16 r.sendline(m1.hex().encode()) r.recvline() t1 = bytes.fromhex(r.recvline().decode().strip().split(': ')[1]) # Create m2 = m1 || (m1 XOR t1) m2 = m1 + strxor(m1, t1) r.close() # Submit forgery r = remote('192.169.2.100', 11001) r.recvuntil(b'!') r.sendline(b'1') r.recvuntil(b'!') r.recvline() r.sendline(m1.hex().encode()) r.sendline(m2.hex().encode()) print(r.recvall().decode())
Defense
To protect against length extension attack in CBC-MAC:
- Use CMAC/OMAC - adds finalization with an additional key
- Encrypt-last-block (ECBC) - encrypts the last block with a different key
- Fix message length - include length in the first block
- HMAC - use HMAC instead of CBC-MAC for variable length messages
References
- CBC-MAC Length Extension Attack
- CMAC (NIST SP 800-38B)