Challenge
Category: Crypto · Points: 200 · CTF: picoCTF 2026
Analysis
Reading the source, the key generation is immediately suspicious:
key = os.urandom(126) # 126 random bytes...
lfsr = key & 0xFF # ...but masked to just 8 bits
Despite key being 126 bytes, it is immediately masked to 0xFF — only the last byte matters. This means only 256 possible initial LFSR states exist, regardless of key size.
The LFSR steps with feedback from bits 7, 5, 4, and 3:
feedback = b7 ^ b5 ^ b4 ^ b3
lfsr = (feedback << 7) | (lfsr >> 1)
Each plaintext byte is XORed with the current LFSR state to produce ciphertext.
Exploit
Since the keyspace is just 8 bits, brute force all 256 possible initial states:
def decrypt_lfsr(ct, key):
lfsr = key & 0xFF
out = []
for byte in ct:
out.append(byte ^ lfsr)
b7 = (lfsr >> 7) & 1
b5 = (lfsr >> 5) & 1
b4 = (lfsr >> 4) & 1
b3 = (lfsr >> 3) & 1
feedback = b7 ^ b5 ^ b4 ^ b3
lfsr = ((feedback << 7) | (lfsr >> 1)) & 0xFF
return bytes(out)
for key in range(256):
pt = decrypt_lfsr(ct_bytes, key)
try:
s = pt.decode('ascii')
if 'picoCTF' in s or '{' in s:
print(f"Key={key}: {s}")
except:
pass
Keys 162 and 163 both produce the correct flag — consecutive keys can yield the same output because the LFSR’s first step may produce the same keystream byte from adjacent initial states.
Key Takeaway
The 126-byte key is a complete red herring. Truncating with & 0xFF throws away all but 8 bits of entropy. Secure LFSR-based stream ciphers need large state (hundreds of bits) and nonlinear combination to resist brute force and algebraic attacks.
Flag
picoCTF{l1n3ar_f33dback_sh1ft_r3g}