Athena Wiki
SteganographyText Steganography

Unicode Steganography

steganographyintermediate

Find and decode data hidden in Unicode homoglyphs, variation selectors, and lookalike characters.

steganographyunicodehomoglyphzero-widthvariation-selectortext

Unicode Steganography

Unicode steganography exploits the vast Unicode character space to hide data in text that appears visually identical to ASCII. The human eye cannot distinguish U+0041 (A) from U+0410 (А, Cyrillic A). By mixing lookalike characters, variation selectors, or zero-width joiners, data can be hidden invisibly in plain text.


Detection Workflow

Mermaid diagram

Detection Steps

Check for non-ASCII characters

with open('suspicious.txt', 'r', encoding='utf-8') as f:
    text = f.read()

for i, c in enumerate(text):
    if ord(c) > 127:
        print(f'Position {i}: U+{ord(c):04X} = {repr(c)}')

Identify the technique

  • U+FE00U+FE0F → variation selectors
  • U+200B, U+200C, U+200D → zero-width characters
  • Cyrillic/Greek letters mixed with Latin → homoglyphs

Decode based on technique

Use the appropriate decoder from the tabs below.


Common Techniques

Replacing ASCII letters with visually identical Unicode characters:

# Common homoglyphs (Cyrillic that look like Latin):
# A (U+0041) → А (U+0410)
# a (U+0061) → а (U+0430)
# e (U+0065) → е (U+0435)
# o (U+006F) → о (U+043E)
# p (U+0070) → р (U+0440)
# c (U+0063) → с (U+0441)

cyrillic_lookalikes = {
    'А','а','В','Е','е','К','М','Н','О','о','Р','р','С','с','Т','Х','х','У'
}

def decode_homoglyph_binary(text):
    binary = ''
    for char in text:
        if char in cyrillic_lookalikes:
            binary += '1'
        elif char.isalpha():
            binary += '0'
    chars = [chr(int(binary[i:i+8], 2)) for i in range(0, len(binary)-7, 8)]
    return ''.join(chars)

Comprehensive Detector

import unicodedata

def analyze_unicode(text):
    print(f"Total chars: {len(text)}")
    print(f"ASCII chars: {sum(1 for c in text if ord(c) < 128)}")
    print(f"Non-ASCII: {sum(1 for c in text if ord(c) >= 128)}")

    for i, c in enumerate(text):
        if ord(c) >= 128:
            try:
                name = unicodedata.name(c)
            except ValueError:
                name = "UNKNOWN"
            print(f"  [{i}] U+{ord(c):04X} = {name}")

with open('suspicious.txt', 'r', encoding='utf-8') as f:
    analyze_unicode(f.read())

CyberChef Approach

  1. Paste the text into CyberChef
  2. Apply "Strip Non-Printable Characters"
  3. Compare output length with original — if different, hidden chars present
  4. Also try "Encode text" → Unicode code points to see all character values

Checklist

  • Python: check ord(c) > 127 for every character
  • Look for Cyrillic, Greek, or other script lookalikes mixed with Latin
  • Check for variation selectors (U+FE00–FE0F)
  • Check for zero-width joiners/non-joiners (U+200B–200D)
  • CyberChef: "Strip Non-Printable" → compare length before/after
  • Try binary decode: ASCII=0, non-ASCII=1 (and vice versa)
  • hexdump the file → look for multi-byte UTF-8 sequences

Last updated on

On this page