← Back to Writeups
HTBEasyCryptography

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
nmap -p- -sV -sC <TARGET> --min-rate 1000 -Pn
PortServiceVersionNotes
<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

  1. key_recovery
  2. known_plaintext_attack
bash
<command>

Exploitation

  • See original writeup content for detailed exploitation.

Privilege Escalation

Enumeration

bash
sudo -l
find / -perm -4000 2>/dev/null
getcap -r / 2>/dev/null
cat /etc/crontab
ps aux

Exploitation

  1. N/A for challenge-type writeup; see exploitation above.
  2. Flag obtained via challenge solve.
bash
<command>

Flags

FlagLocationValue
flagREDACTED

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
#!/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

  1. Short key (4 bytes) — the key repeats cyclically
  2. Known flag format — the flag starts with HTB{ (exactly 4 bytes)
  3. 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
#!/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

PropertyFormula
CommutativityA ⊕ B = B ⊕ A
Associativity(A ⊕ B) ⊕ C = A ⊕ (B ⊕ C)
Self-inverseA ⊕ A = 0
Identity elementA ⊕ 0 = A
ReversibilityA ⊕ B ⊕ B = A

Why Repeating-Key XOR is Insecure

  1. Known Plaintext Attack — if known plaintext >= key length, the key is fully recoverable
  2. Frequency Analysis — with long text, frequency analysis can be used
  3. Kasiski Examination — repeating patterns in ciphertext reveal key length

Alternative Approaches

If Key is Longer Than Known Plaintext

python
# 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
# 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

  1. Repeating-key XOR is insecure with any known or predictable plaintext
  2. Flag format — this is always known plaintext in CTF
  3. Short key — critical vulnerability, especially when key_len <= known_plaintext_len
  4. XOR is not encryption without OTP (One-Time Pad) with key of equal length
</details>

signed by XESXOR