Composition
Composition
Platform: hackerlab.pro | Category: Steganography | Type: Challenge | Difficulty: Medium | OS: NA | Author: XESXOR | Date: 2026-04-05 | Status: Solved Techniques: visible_formatting_mark_analysis, paragraph_break_binary_decoding, binary_to_ascii_decoding
Summary
Task: a DOCX file contains an ordinary essay and asks for a wrapped value in CODEBY{...}. Solution: enable visible formatting marks, read one paragraph break as 0 and two breaks as 1, group the resulting bits into bytes, and decode the binary text to recover the flag body.
Composition — hackerlab.pro
Description
Ученик скинул мне очень странное сочинение о том, как он провел лето. Надеюсь это не вирус... P.S. Полученное значение оберните в CODEBY{}
English summary: the challenge provides a Word document, composition.docx, containing an essay about summer. The visible text looks harmless, but the document hides a value that must be wrapped as CODEBY{...}.
Analysis
The intended channel is not a hidden OOXML string and not ordinary metadata. The official author writeup confirms that the signal is visible only when the document is opened in a word processor and formatting marks are enabled.
With paragraph marks shown, the beginning of each line reveals a binary pattern:
- one break =
0 - two breaks =
1
Reading those breaks in order produces a long binary string. Splitting it into 8-bit groups and decoding each byte as ASCII gives the hidden text:
text read-only • copy allowedREDACTED
An earlier false lead came from reconstructing the signal from OOXML structure alone. That approach can diverge from what Word actually renders, because the visible layout is affected by rendered line/paragraph breaks rather than only by counting empty XML paragraphs. Since the author PDF explicitly demonstrates the formatting-mark method, that rendered view is the authoritative solution path.
Solution
Step 1: Open the DOCX in Word and enable formatting marks
Open composition.docx in Microsoft Word (or another editor that accurately shows paragraph marks) and enable Show/Hide formatting marks.
Now inspect the start of each visible line. Some lines begin after a single break, others after a double break.
Step 2: Convert break counts to bits
Record the sequence in order using the challenge rule:
- one break →
0 - two breaks →
1
This yields the bitstream:
text read-only • copy allowed011100110111000001100001011000110011001101110011011000100011001101101100001100010110101100110011
Step 3: Split into 8-bit groups
text read-only • copy allowed01110011 01110000 01100001 01100011 00110011 01110011 01100010 00110011 01101100 00110001 01101011 00110011
Step 4: Decode binary to ASCII
Converting each byte to text gives:
text read-only • copy allowedREDACTED
Step 5: Wrap in the required flag format
text read-only • copy allowedCODEBY{REDACTED}
Helper script
The official solve is visual, but this helper script decodes the collected bitstring once the one-break/two-break sequence has been read from the rendered document:
python read-only • copy allowed#!/usr/bin/env python3 bitstream = "011100110111000001100001011000110011001101110011011000100011001101101100001100010110101100110011" decoded = ''.join( chr(int(bitstream[i:i+8], 2)) for i in range(0, len(bitstream), 8) ) print(decoded) print(f"CODEBY{{{decoded}}}")
Output:
text read-only • copy allowedREDACTED CODEBY{REDACTED}