Skip to main content

💯 (unicode magic) – Nullcon CTF 2026

nullcon CTF 2026

Datos del reto

Campo Valor
CTF Nullcon CTF 2026
Reto 💯 (unicode magic)
Categoría Misc
Flag ENO{EM0J1S_UN1COD3_1S_MAG1C}

Descripción del reto

Recuperar la flag a partir del único artefacto entregado: un README.md que contiene aparentemente solo un emoji (💯). A simple vista no hay nada más; el truco está en que el archivo incluye caracteres invisibles Unicode (Variation Selectors) que codifican la flag.


Reconocimiento

Muchos retos "misc" esconden información usando Unicode Variation Selectors: caracteres "invisibles" que modifican cómo se renderiza un carácter previo. En texto plano quedan pegados al final del emoji y no se ven. Se suelen usar como canal encubierto para codificar bytes.

En este reto, el emoji 💯 funciona como portador, y después vienen una serie de Variation Selectors invisibles que codifican la flag.

Formas típicas de verlo:

  • Opción 1: Ver los codepoints con Python. Leyendo el archivo en UTF-8 y mostrando ord() (o hex(ord())) aparecen caracteres con rangos como U+FE00..U+FE0F o U+E0100..U+E01EF (Variation Selectors).
  • Opción 2: Herramientas CLI como xxd, hexdump -C o editores que muestran Unicode invisible; se ve que el archivo NO es solo el emoji.

Análisis de la vulnerabilidad

Los Variation Selectors se mapean a valores numéricos (0..255), que luego se interpretan como bytes ASCII:

  • Mapeo: U+FE00..U+FE0F → valores 0..15; U+E0100..U+E01EF → valores 16..255 (con valor = codepoint - 0xE0100 + 16). Cada valor se convierte a carácter con chr(valor) y se concatena.

Explotación

Script de solución (Python). Guardar como solve.py en la misma carpeta del README.md:

#!/usr/bin/env python3

def decode_variation_selectors(text: str) -> str:
    if not text:
        return ""

    payload = text[1:]  # El primer carácter es el emoji portador (💯). Lo ignoramos.

    vals = []
    for ch in payload:
        cp = ord(ch)
        if 0xFE00 <= cp <= 0xFE0F:
            vals.append(cp - 0xFE00)
        elif 0xE0100 <= cp <= 0xE01EF:
            vals.append(cp - 0xE0100 + 16)
        else:
            pass

    return "".join(chr(v) for v in vals)

def main():
    with open("README.md", "r", encoding="utf-8") as f:
        s = f.read().strip()
    decoded = decode_variation_selectors(s)
    print(decoded)

if __name__ == "__main__":
    main()

Ejecutar: python3 solve.py


Flag

ENO{EM0J1S_UN1COD3_1S_MAG1C}

Lecciones aprendidas

  • Los Variation Selectors Unicode son invisibles en la mayoría de editores pero siguen codificando información.
  • Retos misc de esteganografía pueden usar el mismo alfabeto (emoji + selectores) para codificar bytes.