سبحان الله و بحمده سبحان الله العظيم ❤️

← BACK TO WRITEUPS
Cryptanalysis CTF MEDIUM

Custom Block Cipher — Reversing a 32-Round Feistel

Files: encrypt.py, flag.enc (32 bytes)

Analysis

The encrypt.py implements a custom 32-round block cipher operating on 16-byte blocks (4 × 32-bit integers). Each round consists of two sequential tuple assignments using bitwise operations, multiplication, and constants 31337 and 1337. No encryption key is used.

def F(w):
    return ((w * 31337) ^ (w * 1337 >> 16)) % 2**32

def encrypt(block):
    a, b, c, d = unpack("<4I", block)
    for rno in xrange(32):
        a, b, c, d = b ^ F(a | F(c ^ F(d)) ^ F(a | c) ^ d), \
                     c ^ F(a ^ F(d) ^ (a | d)), \
                     d ^ F(a | F(a) ^ a), \
                     a ^ 31337
        a, b, c, d = c ^ F(d | F(b ^ F(a)) ^ F(d | b) ^ a), \
                     b ^ F(d ^ F(a) ^ (d | a)), \
                     a ^ F(d | F(d) ^ d), \
                     d ^ 1337
    return pack("<4I", a, b, c, d)

Critical Insight

Python's simultaneous tuple assignment means all right-hand side expressions are evaluated using the OLD values of a, b, c, d before any assignment occurs. This allows us to mathematically reverse the algorithm using XOR's self-inverse property.

Decryption Implementation

def decrypt_block(ct):
    a, b, c, d = unpack("<4I", ct)
    for rnd in range(32):
        old_d = d ^ 1337
        old_a = c ^ F(old_d | F(old_d) ^ old_d)
        old_b = b ^ F(old_d ^ F(old_a) ^ (old_d | old_a))
        old_c = a ^ F(old_d | F(old_b ^ F(old_a)) ^ F(old_d | old_b) ^ old_a)
        a, b, c, d = old_a, old_b, old_c, old_d

        old_a = d ^ 31337
        old_d = c ^ F(old_a | F(old_a) ^ old_a)
        old_c = b ^ F(old_a ^ F(old_d) ^ (old_a | old_d))
        old_b = a ^ F(old_a | F(old_c ^ F(old_d)) ^ F(old_a | old_c) ^ old_d)
        a, b, c, d = old_a, old_b, old_c, old_d
    return pack("<4I", a, b, c, d)

Key Takeaways

Flag

FLAG{G3N3R4L123D_F31573L_EZ!}