Hell is haard
Hell is haard
Platform: CUSTOM | Category: Crypto | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: blockwise_byte_shift_recovery, inverse_haar_transform, recursive_signal_reconstruction
Summary
Walkthrough of the CUSTOM Crypto Hell is haard. Task: a JSON array of 2048 integers hides a message behind a Haar-based encoding and extra byte scrambling. Solution: recursively apply the inverse unscaled Haar transform, convert the recovered sampl
Description
My good friend dante is trying to send me a message from the deepest level of hell but it its all convoluted!
We are given message.txt, which looks like raw numeric data instead of ciphertext or natural language. The goal is to recover the hidden plaintext and extract the embedded flag.
Challenge Summary
The file contains a JSON list of 2048 integers. The challenge title is the real hint: haard points to the Haar transform. Reversing the unscaled Haar encoding reconstructs 2048 plaintext bytes, and three of the four 512-byte blocks then need an additional modulo-256 byte rotation to become readable English.
Recon / Observations
The first useful step is simply inspecting message.txt. It is valid JSON containing 2048 signed integers, which strongly suggests transformed signal data rather than a standard text cipher.
Two details stand out:
- The challenge name uses
haard, which is very likely a pun on the Haar wavelet transform. - The data length is a power of two, which fits recursive transforms perfectly.
That makes an inverse Haar reconstruction the natural thing to try.
Why the Haar Hint Matters
In an unscaled Haar transform, each stage stores averages and details. If a block contains average a and detail d, the original pair is recovered as:
text read-only • copy allowedleft = (a + d) / 2 right = (a - d) / 2
The encoded data stores these coefficients recursively. Starting from the smallest prefix and repeatedly expanding it with that rule reconstructs the full 2048-sample sequence.
Without noticing the Haar hint, the integer list just looks arbitrary. Once the title is interpreted correctly, the recovery path becomes straightforward.
Analysis
Applying the inverse unscaled Haar transform recursively yields floating-point values very close to byte values. Rounding them and reducing modulo 256 produces a 2048-byte plaintext candidate.
That plaintext splits naturally into four 512-byte blocks:
- block 0: already readable English
- block 1: readable after byte shift
+90 mod 256 - block 2: readable after byte shift
+45 mod 256 - block 3: readable after byte shift
+138 mod 256
These shifts act like an 8-bit Caesar cipher applied independently to each block. After correcting them, the text becomes a Dante excerpt containing the flag string:
text read-only • copy allowedudctf{REDACTED}
Solution
- Parse
message.txtas JSON. - Treat the array as recursively Haar-encoded coefficients.
- Rebuild the plaintext samples by repeatedly expanding the recovered prefix:
- take averages from the first half of the current prefix
- take details from the second half
- recover each original pair with
(a + d) / 2and(a - d) / 2
- After all 2048 samples are restored, round each value and reduce modulo 256.
- Split the byte string into four 512-byte blocks.
- Apply per-block rotations of
0,90,45, and138modulo 256. - Read the recovered Dante passage and extract the embedded flag.
python read-only • copy allowed#!/usr/bin/env python3 import json from pathlib import Path coeffs = json.loads(Path("tasks/BlueHens CTF 2026/Hell is haard/message.txt").read_text()) prefix = [coeffs[0]] size = 1 while size < len(coeffs): details = coeffs[size:2 * size] rebuilt = [] for a, d in zip(prefix, details): rebuilt.append((a + d) / 2) rebuilt.append((a - d) / 2) prefix = rebuilt size *= 2 data = bytes(round(x) % 256 for x in prefix) shifts = [0, 90, 45, 138] parts = [] for i, shift in enumerate(shifts): block = data[i * 512:(i + 1) * 512] parts.append(bytes((b + shift) % 256 for b in block)) plaintext = b"".join(parts).decode("latin1") print(plaintext)
The decoded text is a long English passage from Dante, and one sentence contains the flag verbatim.
Short Takeaways
- A challenge title that sounds slightly wrong is often a direct hint.
- Power-of-two numeric arrays are a strong sign of recursive transforms.
- If recovered text is nearly readable, test simple post-processing like blockwise byte rotations.
Key Takeaways / Lessons
- Mastering blockwise_byte_shift_recovery, inverse_haar_transform, recursive_signal_reconstruction was key to this challenge.
- Always check for common misconfigurations and verify assumptions.
Crafted by XESXOR Research • 2026-04-22 • CUSTOM • Hell is haard • Difficulty: Medium