$ Crypto: Super DES

BITSCTF 2026 By TaklaMan

Key Points

  • The server uses a fixed secret DES key k1 and lets us choose k2 and k3.
  • In ultra secure v1, encryption is:
    • C = E_k1(E_k2(E_k3(P)))
  • In ultra secure v2, encryption is:
    • C = D_k1(E_k2(E_k3(P)))
  • If we choose a DES semi-weak pair (k2, k3) such that E_k2 = D_k3, then:
    • E_k2(E_k3(X)) = X
  • So with that choice:
    • v1 becomes C = E_k1(P)
    • v2 becomes C = D_k1(P)

This gives a direct encryption/decryption oracle for the unknown k1.

Exploit Strategy

  1. Choose semi-weak DES keys:
    • k2 = 01fe01fe01fe01fe
    • k3 = fe01fe01fe01fe01
  2. Request option=2 (ultra secure v1) with option_=1 (encrypt flag):
    • Receive enc_flag = E_k1(pad(flag))
  3. Split enc_flag into 8-byte blocks.
  4. For each block B, call option=3 (ultra secure v2) with option_=2 (encrypt own text) and pt=B:
    • Output block is D_k1(B)
  5. Reassemble decrypted blocks and remove PKCS#7 padding.

Recovered Flag

BITSCTF{5up3r_d35_1z_n07_53cur3}

Main Program (solve_super_des.py)

from pwn import remote

HOST = "20.193.149.152"
PORT = 1340

# Known DES semi-weak pair with odd parity bits.
K2 = "01fe01fe01fe01fe"
K3 = "fe01fe01fe01fe01"

def pkcs7_unpad(data: bytes, block_size: int = 8) -> bytes:
    if not data or len(data) % block_size != 0:
        raise ValueError("invalid padded length")
    n = data[-1]
    if n < 1 or n > block_size:
        raise ValueError("invalid padding byte")
    if data[-n:] != bytes([n]) * n:
        raise ValueError("invalid padding contents")
    return data[:-n]

def recv_menu(io):
    io.recvuntil(b"enter k2 hex bytes >")

def do_query(io, option, option_, pt_hex=None):
    recv_menu(io)
    io.sendline(K2.encode())
    io.recvuntil(b"enter k3 hex bytes >")
    io.sendline(K3.encode())

    io.recvuntil(b"enter option >")
    io.sendline(str(option).encode())
    io.recvuntil(b"enter option >")
    io.sendline(str(option_).encode())

    if option_ == 2:
        io.recvuntil(b"enter hex bytes >")
        io.sendline(pt_hex.encode())

    io.recvuntil(b"ciphertext : ")
    return bytes.fromhex(io.recvline().strip().decode())

def decrypt_block(io, block8: bytes) -> bytes:
    # In option 3 with this semi-weak pair, first block equals D_k1(block8)
    ct = do_query(io, option=3, option_=2, pt_hex=block8.hex())
    return ct[:8]

def solve(io):
    # Get E_k1(pad(flag)) via ultra secure v1
    enc_flag = do_query(io, option=2, option_=1)

    # Decrypt each block with v2 oracle
    pt_padded = b"".join(decrypt_block(io, enc_flag[i:i+8]) for i in range(0, len(enc_flag), 8))
    return pkcs7_unpad(pt_padded, 8)

if __name__ == "__main__":
    io = remote(HOST, PORT)
    flag = solve(io)
    print(flag.decode(errors="replace"))
    io.close()

Run

python3 solve_super_des.py