XOR Key Recovery — Favourite byte & You either know XOR you don't
XOR Key Recovery — Favourite byte & You either know XOR you don't
Platform: cryptohack | Category: Crypto | Type: Challenge | Difficulty: Easy | OS: NA | Author: XESXOR | Date: 2026-06-21 | Status: Solved Techniques: crib_dragging, flag_format_crib, repeating_key_xor_known_plaintext, single_byte_xor_brute_force
Summary
Task: two CryptoHack XOR challenges — single-byte XOR and repeating-key XOR, each given a hex ciphertext. Solution: brute-force the single byte with the crypto{ crib, then XOR the known prefix against the ciphertext to leak the repeating key (myXORkey) and decrypt.
XOR Key Recovery — CryptoHack (General / XOR stage)
Description
Two consecutive CryptoHack General challenges on the XOR stage:
- Favourite byte (20 pts): "Hidden with XOR using a single secret byte."
- You either know, XOR you don't (30 pts): "Remember the flag format and how it might help you."
Each provides a hex-encoded ciphertext. The goal in both is to recover the plaintext flag. The common thread is exploiting the known CryptoHack flag format crypto{ as a crib (known-plaintext fragment).
Analysis
XOR is its own inverse: pt ^ key = ct implies ct ^ key = pt and ct ^ pt = key.
- Single-byte XOR: the whole message is XORed with one byte. The keyspace is only 256 values, so an exhaustive search plus a crib (the plaintext must begin with
crypto{) recovers the key instantly. - Repeating-key XOR (Vigenère-style): the message is XORed with a short key that repeats. XORing the ciphertext against a known plaintext fragment over its length reveals the corresponding bytes of the key. If the key is a short human-readable word, the leaked prefix gives away the whole key.
Solution
Challenge 1 — Favourite byte (single-byte XOR)
Brute force all 256 possible key bytes and keep the one whose plaintext starts with the flag format:
python read-only • copy allowed#!/usr/bin/env python3 ct = bytes.fromhex("73626960647f6b206821204f21254f7d694f7624662065622127234f726927756d") for k in range(256): pt = bytes(b ^ k for b in ct) if pt.startswith(b"crypto{"): print(hex(k), pt.decode()) # 0x10 crypto{0x10_15_my_f4v0ur173_by7e}
The key byte is 0x10, and the flag even spells out the answer.
Challenge 2 — You either know, XOR you don't (repeating-key XOR)
XOR the ciphertext against the 7-byte known prefix crypto{ to leak the start of the key:
python read-only • copy allowed#!/usr/bin/env python3 ct = bytes.fromhex("0e0b213f26041e480b26217f27342e175d0e070a3c5b103e2526217f27342e175d0e077e263451150104") known = b"crypto{" partial = bytes(ct[i] ^ known[i] for i in range(len(known))) print(partial) # b'myXORke' -> obviously the start of "myXORkey" key = b"myXORkey" pt = bytes(ct[i] ^ key[i % len(key)] for i in range(len(ct))) print(pt.decode()) # crypto{REDACTED}
The leaked prefix myXORke makes the full 8-byte key myXORkey obvious. Decrypting with the repeating key yields the flag.
Flags
crypto{0x10_15_my_f4v0ur173_by7e}
crypto{REDACTED}
Bonus — CryptoHack submission API (automation)
To script bulk flag submission on CryptoHack:
- Endpoint:
POST https://www.cryptohack.org/api/submit/{challenge_id}.json— note the/apiprefix. The page form action/submit/{id}.jsonissues a 308 redirect that drops the POST body, so always hit/api/...directly. - Body:
flag=<value>&_csrf_token=<token>. - Required: the authenticated session cookie and header
X-Requested-With: XMLHttpRequest. - Responses: success →
{"code":0,"message":"Success!","solves":N}; already solved →{"code":1001,...}.
Both flags were submitted and confirmed correct (code 0, Success!).