Writeup
Writeup
Platform: hackerlab | Category: Steganography | Type: Challenge | Difficulty: Medium | OS: NA | Author: XESXOR | Date: 2026-04-08 | Status: Solved Techniques: hex_dump_invisible_character_detection, morse_code_binary_decoding, zero_width_unicode_extraction
Summary
Task: a text file with visible Russian text hides invisible zero-width Unicode characters (U+200B and U+2063) between lines. Solution: extract the two types of zero-width characters, interpret them as Morse code dashes and dots respectively, decode each line to get CODEBY{REDACTED}.
Writeup — Hackerlab
Description
Пытался сделать райтап на недавний таск с Киберколизея, но что-то пошло не так
English summary: we receive a ZIP archive containing task.txt (1619 bytes). The file contains visible Russian text that looks like a blog post about a CyberColiseum 2 CTF writeup. The goal is to find a hidden flag.
Analysis
The visible text is a short blog post (about 500 characters of visible content), but the file is 1619 bytes — suspiciously large. The text itself contains several hints:
- "CTRL + V" — the author mentions pasting, but "practically nothing was copied," suggesting invisible content was pasted alongside visible text.
- "Юни[код]" — the word "Unicode" is split with brackets, drawing attention to Unicode encoding specifically.
Running a hex dump reveals clusters of invisible bytes between lines of visible text. Only two types of invisible UTF-8 sequences appear:
E2 80 8B→ U+200B (Zero-Width Space, ZWS) — 35 occurrencesE2 81 A3→ U+2063 (Invisible Separator, IS) — 28 occurrences
Total: 63 invisible characters arranged in 17 groups (one group per line or between visible text lines).
Since exactly two distinct symbols are used, this is a binary encoding. The pattern matches Morse code: one symbol for dashes, one for dots.
Solution
Step 1: Extract invisible characters
Using hex analysis or a Python script, extract all zero-width characters from the file, preserving their grouping by line:
python read-only • copy allowed#!/usr/bin/env python3 with open("task.txt", "r", encoding="utf-8") as f: text = f.read() ZWS = '\u200b' # Zero-Width Space IS = '\u2063' # Invisible Separator # Extract only invisible characters, split by newlines lines = text.split('\n') for i, line in enumerate(lines, 1): hidden = ''.join(c for c in line if c in (ZWS, IS)) if hidden: symbols = hidden.replace(ZWS, '-').replace(IS, '.') print(f"Line {i:2d}: {symbols}")
Step 2: Decode Morse code
Each group of invisible characters represents one Morse code character:
| Line | Pattern | Morse | Char |
|---|---|---|---|
| 2 | -.-. | C | C |
| 4 | --- | O | O |
| 6 | -.. | D | D |
| 8 | . | E | E |
| 12 | -... | B | B |
| 14 | -.-- | Y | Y |
| 16 | -.--. | ( | { |
| 18 | --.. | Z | Z |
| 19 | ...-- | 3 | 3 |
| 20 | .-. | R | R |
| 21 | ----- | 0 | 0 |
| 22 | .-- | W | W |
| 23 | .---- | 1 | 1 |
| 24 | -.. | D | D |
| 25 | - | T | T |
| 26 | .... | H | H |
| 27 | -.--.- | ) | } |
| |
Step 3: Assemble the flag
The decoded characters spell: CODEBY(Z3R0W1DTH)
Morse code uses -.--. for ( and -.--.- for ). Replacing parentheses with curly braces per the flag format: CODEBY{REDACTED}
The flag content "z3r0w1dth" is leetspeak for "zero width" — a self-referencing hint to the zero-width character steganography technique used in the challenge.
Full solve script
python read-only • copy allowed#!/usr/bin/env python3 """Solve: Hackerlab - Writeup (Stego 350) Extract zero-width Unicode chars from task.txt, decode as Morse code.""" MORSE = { '.-': 'A', '-...': 'B', '-.-.': 'C', '-..': 'D', '.': 'E', '..-.': 'F', '--.': 'G', '....': 'H', '..': 'I', '.---': 'J', '-.-': 'K', '.-..': 'L', '--': 'M', '-.': 'N', '---': 'O', '.--.': 'P', '--.-': 'Q', '.-.': 'R', '...': 'S', '-': 'T', '..-': 'U', '...-': 'V', '.--': 'W', '-..-': 'X', '-.--': 'Y', '--..': 'Z', '.----': '1', '..---': '2', '...--': '3', '....-': '4', '.....': '5', '-....': '6', '--...': '7', '---..': '8', '----.': '9', '-----': '0', '-.--.': '(', '-.--.-': ')', } ZWS = '\u200b' # Zero-Width Space → dash IS = '\u2063' # Invisible Separator → dot with open("task.txt", "r", encoding="utf-8") as f: text = f.read() flag = "" for line in text.split('\n'): hidden = ''.join(c for c in line if c in (ZWS, IS)) if hidden: morse = hidden.replace(ZWS, '-').replace(IS, '.') char = MORSE.get(morse, '?') flag += char # Replace Morse parentheses with curly braces for flag format flag = flag.replace('(', '{').replace(')', '}') print(f"Flag: {flag}")
Output: Flag: CODEBY{REDACTED} → lowercase: CODEBY{REDACTED}