$ cat writeup.md…
$ cat writeup.md…
hackthebox
Task: XOR encryption with 4-byte repeating key and encrypted flag. Solution: Known plaintext attack using flag format HTB{ to recover the full key.
"Who needs AES when you have XOR?"
Given an encryption program in Python and an encrypted flag.
#!/usr/bin/python3 import os flag = open('flag.txt', 'r').read().strip().encode() class XOR: def __init__(self): self.key = os.urandom(4) def encrypt(self, data: bytes) -> bytes: xored = b'' for i in range(len(data)): xored += bytes([data[i] ^ self.key[i % len(self.key)]]) return xored def decrypt(self, data: bytes) -> bytes: return self.encrypt(data) def main(): global flag crypto = XOR() print ('Flag:', crypto.encrypt(flag).hex()) if __name__ == '__main__': main()
Flag: 134af6e1297bc4a96f6a87fe046684e8047084ee046d84c5282dd7ef292dc9
HTB{ (exactly 4 bytes)A ⊕ B ⊕ B = A (self-inverse)Since the length of known plaintext (4 bytes) equals the key length, we can fully recover the key!
ciphertext = plaintext ⊕ key
key = ciphertext ⊕ plaintext
Knowing the first 4 bytes of plaintext (HTB{) and the first 4 bytes of ciphertext, we compute the key:
Ciphertext (hex): 13 4a f6 e1
Plaintext (hex): 48 54 42 7b (HTB{)
Key (hex): 5b 1e b4 9a
#!/usr/bin/env python3 """ HackTheBox: xorxorxor Known Plaintext Attack on repeating-key XOR """ encrypted_hex = "134af6e1297bc4a96f6a87fe046684e8047084ee046d84c5282dd7ef292dc9" encrypted = bytes.fromhex(encrypted_hex) # Known plaintext: HTB{ (first 4 bytes = key length) known_plaintext = b"HTB{" # Recover key: key = encrypted XOR plaintext key = bytes([encrypted[i] ^ known_plaintext[i] for i in range(4)]) print(f"Recovered key: {key.hex()}") # Decrypt the entire flag decrypted = b'' for i in range(len(encrypted)): decrypted += bytes([encrypted[i] ^ key[i % 4]]) print(f"Flag: {decrypted.decode()}")
Recovered key: 5b1eb49a
Flag: HTB{REDACTED}
| Property | Formula |
|---|---|
| Commutativity | A ⊕ B = B ⊕ A |
| Associativity | (A ⊕ B) ⊕ C = A ⊕ (B ⊕ C) |
| Self-inverse | A ⊕ A = 0 |
| Identity element | A ⊕ 0 = A |
| Reversibility | A ⊕ B ⊕ B = A |
# Partial key recovery partial_key = bytes([encrypted[i] ^ known_plaintext[i] for i in range(len(known_plaintext))]) # Bruteforce remaining bytes import itertools for remaining in itertools.product(range(256), repeat=key_len - len(partial_key)): full_key = partial_key + bytes(remaining) decrypted = xor_decrypt(encrypted, full_key) if is_printable(decrypted): print(decrypted)
# Kasiski examination or Index of Coincidence from collections import Counter def find_key_length(ciphertext, max_len=20): """Finds probable key length via IoC""" for key_len in range(1, max_len + 1): # Split into groups by position in key groups = [ciphertext[i::key_len] for i in range(key_len)] # Calculate average IoC avg_ioc = sum(index_of_coincidence(g) for g in groups) / key_len if avg_ioc > 0.06: # Close to English text return key_len
$ cat /etc/motd
Liked this one?
Pro unlocks every writeup, every flag, and API access. $9/mo.
$ cat pricing.md$ grep --similar