← Back to Writeups
CUSTOMMediumCrypto

MACdonalds

XESXOR9/10/20262 min read
#crypto#custom#medium#cbc-mac-length-extension-attac#mac-collision/forgery-via-mess#xor-manipulation-of-cipher-blo

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
def 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:

  1. If we know MAC(m1) = t1, we can construct a longer message m2 that 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):

  1. First block: t1 = AES_k(m1)
  2. 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
#!/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:

  1. Use CMAC/OMAC - adds finalization with an additional key
  2. Encrypt-last-block (ECBC) - encrypts the last block with a different key
  3. Fix message length - include length in the first block
  4. HMAC - use HMAC instead of CBC-MAC for variable length messages ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

References