xorxorxor
D3v0o0Nu119/2/20264 min read
#cryptography#htb#easy#key_recovery#known_plaintext_attack
xorxorxor
Platform: HackTheBox | Category: Cryptography | Type: Challenge | Difficulty: Easy | OS: NA | Author: D3v0o0Nu11 | Date: 2026-02-02 | Status: Solved Techniques: key_recovery, known_plaintext_attack
Summary
Task: XOR encryption with 4-byte repeating key and encrypted flag. Solution: Known plaintext attack using flag format HTB{ to recover the full key.
Recon
Port scan
bash read-only • copy allowednmap -p- -sV -sC <TARGET> --min-rate 1000 -Pn
| Port | Service | Version | Notes |
|---|---|---|---|
| <PORT> | <SVC> | <VER> | <notes> |
Enumeration highlights
- Event:
hackthebox| ID:20260202_hackthebox_xorxorxor - Tags: xor, known_plaintext_attack, repeating_key
- Indicators: XOR cipher, short repeating key, known flag format, key length <= known plaintext length
- Source:
20260202_hackthebox_xorxorxor.md
Foothold
Vulnerability / Misconfiguration
- key_recovery
- known_plaintext_attack
bash read-only • copy allowed<command>
Exploitation
- See original writeup content for detailed exploitation.
Privilege Escalation
Enumeration
bash read-only • copy allowedsudo -l find / -perm -4000 2>/dev/null getcap -r / 2>/dev/null cat /etc/crontab ps aux
Exploitation
- N/A for challenge-type writeup; see exploitation above.
- Flag obtained via challenge solve.
bash read-only • copy allowed<command>
Flags
| Flag | Location | Value |
|---|---|---|
| flag | REDACTED |
Key Takeaways / Lessons
- key_recovery
- known_plaintext_attack
- Tags: xor, known_plaintext_attack, repeating_key
Original Writeup
<details><summary>Click to expand original content</summary>Description
"Who needs AES when you have XOR?"
Given an encryption program in Python and an encrypted flag.
Analysis
Encryption Source Code
python read-only • copy allowed#!/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()
Encrypted Flag
Flag: 134af6e1297bc4a96f6a87fe046684e8047084ee046d84c5282dd7ef292dc9
Vulnerabilities
- Short key (4 bytes) — the key repeats cyclically
- Known flag format — the flag starts with
HTB{(exactly 4 bytes) - XOR property —
A ⊕ B ⊕ B = A(self-inverse)
Since the length of known plaintext (4 bytes) equals the key length, we can fully recover the key!
Solution
Attack Mathematics
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
Solution Script
python read-only • copy allowed#!/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()}")
Output
Recovered key: 5b1eb49a
Flag: HTB{REDACTED}
Theory
XOR Properties
| 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 |
Why Repeating-Key XOR is Insecure
- Known Plaintext Attack — if known plaintext >= key length, the key is fully recoverable
- Frequency Analysis — with long text, frequency analysis can be used
- Kasiski Examination — repeating patterns in ciphertext reveal key length
Alternative Approaches
If Key is Longer Than Known Plaintext
python read-only • copy allowed# 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)
If Key Length is Unknown
python read-only • copy allowed# 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
Lessons
- Repeating-key XOR is insecure with any known or predictable plaintext
- Flag format — this is always known plaintext in CTF
- Short key — critical vulnerability, especially when key_len <= known_plaintext_len
- XOR is not encryption without OTP (One-Time Pad) with key of equal length
signed by XESXOR