← Back to Writeups
CUSTOMMediumSteganography

Hidden

XESXOR9/2/20265 min read
#steganography#custom#medium#3-bit-(octal)-encoding-via-8-u#brute-force-mapping-using-know#hex-analysis-of-multi-byte-utf#unicode-whitespace-steganograp#linux

Hidden

Platform: TaipanByte CTF | Category: Steganography | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-02-14 | Status: Solved Techniques: 3-bit (octal) encoding via 8 unique character types, Brute-force mapping using known plaintext prefix, Hex analysis of multi-byte UTF-8 sequences, Unicode whitespace steganography

Summary

Given a file hide.tb for download. The task name "Hidden" hints at hidden data.

Hidden — TaipanByte CTF

Description

Given a file hide.tb for download. The task name "Hidden" hints at hidden data.

Analysis

  1. Initial file inspection:

    bash
    file hide.tb
    # hide.tb: Unicode text, UTF-8 text

    The file is only 198 bytes but is identified as Unicode text.

  2. Visual inspection: The file contains two lines:

    • Line 1: Some message is here — visible text, but with two hidden Unicode whitespace characters inserted between visible characters
    • Line 2: Appears completely empty in text editors, but is actually filled with invisible Unicode whitespace characters ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍
  3. Hex analysis (xxd):

    bash
    xxd hide.tb

    Line 2 contains various multi-byte UTF-8 sequences — different types of Unicode whitespace. These are not regular ASCII spaces (0x20), but special Unicode characters that are visually indistinguishable from each other.

  4. Identifying unique characters: Extracting all whitespace characters revealed 8 unique types of Unicode whitespace, used a total of 70 times (including 2 hidden in line 1):

    SymbolUnicodeName
    U+0020SPACE
    U+00A0NO-BREAK SPACE
    U+2000EN QUAD
    U+2002EN SPACE
    U+2003EM SPACE
    U+2004THREE-PER-EM SPACE
    U+2005FOUR-PER-EM SPACE
    U+2008PUNCTUATION SPACE

‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

  1. Key observation:
    • 8 unique types = 2^3 = 3 bits per character (octal/base-8 encoding)
    • 70 characters x 3 bits = 210 bits = 26 bytes + 2 extra bits
    • 26 bytes — perfectly fits the flag length in TB{...} format

Solution

Step 1: Extracting whitespace characters

python
#!/usr/bin/env python3
"""Extract hidden Unicode whitespace from hide.tb file."""

with open('hide.tb', 'r', encoding='utf-8') as f:
    content = f.read()

# Target Unicode whitespace characters
WHITESPACE_CHARS = {
    '\u0020', '\u00A0', '\u2000', '\u2002',
    '\u2003', '\u2004', '\u2005', '\u2008'
}
‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

# Extract all whitespace characters (except \n)
hidden = [ch for ch in content if ch in WHITESPACE_CHARS]
print(f"Found {len(hidden)} hidden characters")
print(f"Unique types: {len(set(hidden))}")

Step 2: Brute-force mapping

Since the flag format TB{...} is known, we iterate through all 8! = 40320 possible mappings of 8 whitespace types to values 0-7:

python
#!/usr/bin/env python3
"""
Brute-force mapping of Unicode whitespace to 3-bit values.
Using known prefix TB{ for filtering.
"""
from itertools import permutations
‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

with open('hide.tb', 'r', encoding='utf-8') as f:
    content = f.read()

WHITESPACE_CHARS = {
    '\u0020', '\u00A0', '\u2000', '\u2002',
    '\u2003', '\u2004', '\u2005', '\u2008'
}

# Extract hidden characters
hidden = [ch for ch in content if ch in WHITESPACE_CHARS]
unique_chars = sorted(set(hidden), key=lambda c: ord(c))

# Iterate through all permutations of values 0-7
for perm in permutations(range(8)):
    mapping = dict(zip(unique_chars, perm))
    
    # Convert to bits
    bits = ''
    for ch in hidden:
        bits += format(mapping[ch], '03b')
    
    # Decode to ASCII
    try:
        decoded = ''
        for i in range(0, len(bits) - 7, 8):
            byte = int(bits[i:i+8], 2)
            if byte == 0:
                continue
            decoded += chr(byte)
        
        # Check prefix
        if decoded.startswith('TB{') and decoded.endswith('}'):
            print(f"FOUND: {decoded}")
            print(f"Mapping: {mapping}")
            break
    except (ValueError, UnicodeDecodeError):
        continue

‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

Step 3: Brute-force result

Out of 40320 permutations, only one produced a correct result with the TB{ prefix:

SymbolUnicodeValue (3 bits)
SPACEU+00200 (000)
NO-BREAK SPACEU+00A01 (001)
EM SPACEU+20032 (010)
EN SPACEU+20023 (011)
EN QUADU+20004 (100)
THREE-PER-EM SPACEU+20045 (101)
FOUR-PER-EM SPACEU+20056 (110)
PUNCTUATION SPACEU+20087 (111)

Step 4: Decoding

‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

Each whitespace character is converted to a 3-bit value, all bits are concatenated, then read as 8-bit ASCII bytes:

[space1][space2][space3]... → 010 101 000 ... → 01010100 01000010 01111011 ... → T B { ...

Result: TB{REDACTED}

The flag in leetspeak decodes as "DMD is hard to compile" — a reference to the D programming language compiler.

Lessons

Unicode contains dozens of visually indistinguishable whitespace characters, making them an ideal channel for steganography. Key recognition algorithm: ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

  1. file shows "Unicode text" → suspect Unicode steganography
  2. xxd / hex analysis → identify multi-byte sequences in "empty" areas
  3. Count unique types → if power of two, it's N-bit encoding
  4. Known flag format → brute-force mapping (N! permutations)

Typical Unicode whitespace for steganography:

RangeCharacters
U+0020Regular space
U+00A0Non-breaking space
U+2000-U+200ATypographic spaces (en quad, em space, thin space, etc.)
U+2028-U+2029Line/paragraph separator
U+202FNarrow no-break space
U+205FMedium mathematical space
U+3000Ideographic space
U+200B-U+200DZero-width spaces (invisible, 0 width)
U+FEFFZero-width no-break space (BOM)
‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍