Crypto XPLOIT

Linear Guess

22 Oct 2026 • 2 min read

An LCG with unknown a, c and known modulus m = 2³² leaks consecutive outputs, then uses the same generator as a keystream to XOR the flag. Recover a and c from three outputs, then…

An LCG with unknown a, c and known modulus m = 2³² leaks consecutive outputs, then uses the same generator as a keystream to XOR the flag. Recover a and c from three outputs, then predict forward and decrypt.

Files

  • outputs.txt — six consecutive state outputs
  • public_info.txt, README.md
  • flag.enc
  • solve.py

Analysis

With three consecutive outputs X₀, X₁, X₂:

TEXT
X₁ = a·X₀ + c  (mod m)
X₂ = a·X₁ + c  (mod m)

Subtracting eliminates c:

TEXT
X₂ - X₁ = a·(X₁ - X₀)  (mod m)
a = (X₂ - X₁) · (X₁ - X₀)⁻¹ mod m
c = X₁ - a·X₀ mod m

The only trap: (X₁ - X₀) must be invertible mod 2³², i.e. odd. If it isn't, pick a different adjacent pair — the script notes this explicitly.

Decryption

The generator's next states are the keystream, packed little-endian in 4-byte words and XORed with flag.enc:

PYTHON
state = X[-1]
keystream = b""
for _ in range((len(ciphertext) + 3) // 4):
    state = (a * state + c) % m
    keystream += state.to_bytes(4, byteorder="little")

flag = bytes(c ^ k for c, k in zip(ciphertext, keystream))
print(flag.decode())

The recovered outputs were a = 2891336453, c = 1-style small constants in testing, but the value that matters is structural: the LCG is fully determined by three outputs, so the keystream is public data.

Takeaway

An LCG is not a stream cipher. Knowing any three consecutive outputs (and the modulus) reveals the whole parameter set, so predicting every future byte is trivial. Even a 32-bit state is recovered instantly — the effective key length here is zero.

Flag

Flag: not recovered in the writeup.

Start searching

Enter keywords to search articles.