← Back to Writeups
CUSTOMMediumCrypto

Enigma

XESXOR9/10/20262 min read
#crypto#custom#medium#known-plaintext-attack-(kpa)#many-time-pad-attack#xor-key-recovery

Enigma

Platform: hackerlab | Category: Crypto | Type: Challenge | Difficulty: Medium | OS: NA | Author: XESXOR | Date: 2025-12-25 | Status: Solved Techniques: Known Plaintext Attack (KPA), Many-Time Pad Attack, XOR Key Recovery

Summary

Task: 100 XOR-encrypted messages with 64-bit key, known plaintext prefix "Hello Alice". Solution: Many-Time Pad attack - recovered key using known plaintext XOR, decrypted all messages to find real flag among 99 fakes.

Enigma — hackerlab

Description

Мы перехватили 100 шифровок, отправленных 28 июля 2025. Известно, что длина ключа 64 бит. Также известно, что открытый текст каждой шифровки начинается со слов "Hello Alice"

Analysis

Initial Data

  • 100 encrypted messages (hex-encoded)
  • Key length: 64 bits = 8 bytes
  • Known plaintext: each message starts with "Hello Alice"

Identified Vulnerability

Many-Time Pad (Key Reuse Attack)

Analysis of 100 ciphertexts revealed:

  1. All ciphertexts have identical first 8 bytes — this means the same key is used for all messages
  2. Known plaintext of first 8 characters: Hello Al (exactly 8 bytes = key length) ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Attack Mathematics

XOR encryption:

Ciphertext = Plaintext ⊕ Key

With known plaintext:

Key = Ciphertext ⊕ Plaintext

Knowing the first 8 bytes of plaintext (Hello Al) and the first 8 bytes of any ciphertext, we can recover the full 8-byte key.

Solution

Step 1: Key Recovery

python
import binascii

known_plaintext = b"Hello Al"  # 8 bytes = key length

# First ciphertext (first 8 bytes)
ciphertext_prefix = binascii.unhexlify("45c07661e012e844")

# XOR to get the key
key = bytes(c ^ p for c, p in zip(ciphertext_prefix, known_plaintext))
print(f"Key: {key.hex()}")  # 0D A0 16 05 88 77 89 25

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Step 2: Decrypting All Messages

python
#!/usr/bin/env python3
import binascii

def xor_bytes(b1, b2):
    return bytes(a ^ b for a, b in zip(b1, b2))

def decrypt(ciphertext_hex, key):
    ciphertext = binascii.unhexlify(ciphertext_hex)
    plaintext = bytearray()
    for i in range(len(ciphertext)):
        plaintext.append(ciphertext[i] ^ key[i % len(key)])
    return plaintext

# Recovered key
key = bytes([0x0D, 0xA0, 0x16, 0x05, 0x88, 0x77, 0x89, 0x25])

with open('ciphertexts.txt', 'r') as f:
    ciphertexts = [line.strip() for line in f if line.strip()]
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

for i, ct in enumerate(ciphertexts):
    pt = decrypt(ct, key)
    try:
        text = pt.decode('utf-8')
        if 'CODEBY' in text:
            print(f"[!] Message {i}: {text}")
        else:
            print(f"{i}: {text}")
    except:
        print(f"{i}: {pt}")

Result

  • 99 messages contained fake flags: Hello Alice, FAKE-FLAG-XXXX...
  • Message #11 contained the real flag

Defense

  1. Never use the same key twice in stream cipher / XOR
  2. Use a unique nonce/IV for each message
  3. Apply authenticated encryption (AES-GCM, ChaCha20-Poly1305)
  4. For OTP — the key must be longer than the message and used only once ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

References

  • Many-Time Pad Attack
  • Known-Plaintext Attack ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍