$ cat writeup.md…
$ cat writeup.md…
umasscybersec
Task: analyze a VM disk image where a home directory and Git repository were recursively obfuscated with an XOR utility. Solution: recover the repeating key from known plaintext in Git hook templates, decrypt the tree, and inspect the stash ref directly to extract the flag.
Original task text was not preserved in the local solve artifacts.
English summary: the challenge provides a VM image with an Alpine-like filesystem. The goal is to recover what was hidden inside an XOR-obfuscated /home tree and extract the flag.
After extracting the OVA, converting the VMDK to raw, and inspecting the ext4 filesystem, the first useful artifact was /root/.ash_history. It showed cargo install xor, git init ., later Git activity, and manual editing of .ash_history, which strongly suggested intentional filesystem tampering.
Reading the installed crate source at /root/.cargo/registry/.../xor-1.4.5 explained the layout under /home: in recursive mode, the tool XORs file contents and renames files and directories by XORing the original names and hex-encoding the result. That immediately explained the hex-looking names such as 5457501C.
Using the repeated encrypted filename for red-herring as known plaintext recovered part of the key and showed that 5457501C decrypted to .git. The full repeating 512-byte key was then recovered by XORing encrypted .git/hooks/* files against the default hook templates in /usr/share/git-core/templates/hooks/.
/root/.ash_history and identify xor plus Git usage.xor crate source to confirm recursive XOR of both file contents and names.red-herring filename to identify part of the key and confirm 5457501C -> .git..git/hooks/* files to the stock Git hook templates./home tree and restore the Git repository..git/refs/stash directly.#!/usr/bin/env python3 from pathlib import Path HOOK_NAME_MAP = { "1B4049040C060901505B445E11055B0514150A0F0C": "applypatch-msg.sample", "195F54051C02451840544740030F051A10": "commit-msg.sample", "0955570C101B091C5F1E1F520E0B1117011D54100802181606": "sendemail-validate.sample", "0A5F4A1C5803181152470C1D11031806191D": "post-update.sample", "0A425C45140618194A430847010A5B0514150A0F0C": "pre-applypatch.sample", "0A425C45161905185A474740030F051A10": "pre-commit.sample", "0A425C4518131A12561E0A5C0F0F1C025B0B1B0E19030D": "pre-merge-commit.sample", "0A425C4505031B1D1D40085E120E10": "pre-push.sample", "0A425C4507130A1440564740030F051A10": "pre-rebase.sample", "0A425C4507130B105A450C1D11031806191D": "pre-receive.sample", "0A425C1814040D58505C045E0B16581B061F54100802181606": "prepare-commit-msg.sample", "0A454A0058020758505B0C50090D00025B0B1B0E19030D": "push-to-checkout.sample", "0F405D0901134606525E195F07": "update.sample", } def xor_bytes(data: bytes, key: bytes) -> bytes: return bytes(b ^ key[i % len(key)] for i, b in enumerate(data)) def derive_key(base: Path) -> bytes: enc_hooks = base / "recovered_root/home/5457501C/125F560306" plain_hooks = base / "recovered_root/usr/share/git-core/templates/hooks" positions = {} for enc_name, plain_name in HOOK_NAME_MAP.items(): enc = (enc_hooks / enc_name).read_bytes() plain = (plain_hooks / plain_name).read_bytes() for i, (a, b) in enumerate(zip(enc, plain)): positions[i] = a ^ b derived = bytes(positions[i] for i in range(max(positions) + 1)) for period in range(1, len(derived) + 1): if all(derived[i] == derived[i % period] for i in range(len(derived))): return derived[:period] raise RuntimeError("no repeating key found") def decrypt_name(name: str, key: bytes) -> str: return xor_bytes(bytes.fromhex(name), key).decode() def decrypt_tree(src: Path, dst: Path, key: bytes) -> None: dst.mkdir(parents=True, exist_ok=True) for entry in src.iterdir(): out = dst / decrypt_name(entry.name, key) if entry.is_dir(): decrypt_tree(entry, out, key) else: out.parent.mkdir(parents=True, exist_ok=True) out.write_bytes(xor_bytes(entry.read_bytes(), key)) base = Path("tasks/umasscybersec/Lost and Found") key = derive_key(base) decrypt_tree(base / "recovered_root/home", base / "decrypted_home", key) stash_log = (base / "decrypted_home/.git/logs/refs/stash").read_text() print(stash_log.strip())
The stash log contained:
On master: You found me! UMASS{REDACTED}
$ cat /etc/motd
Liked this one?
Pro unlocks every complete writeup and expanded API access. $9/mo.
$ cat pricing.md$ grep --similar