Skip to main content
Volver a writeups

Writeup técnico

Indian WAV (Esteganografía) – Taipanbyte CTF

---

Taipanbyte CTF

Datos del reto

Campo Valor
CTF Taipanbyte CTF
Reto Indian WAV
Categoría Esteganografía
Flag TB{H1dd3n_W@ve_Fl@g}

Descripción del reto

El archivo entregado, wav.tb, era un WAV válido. La parte interesante no estaba en la cabecera ni en chunks extraños, sino en los bits menos significativos del audio. La clave del reto era extraer esos bits en el orden correcto hasta reconstruir el texto escondido.


Reconocimiento

Lo primero fue confirmar qué tipo de archivo era:

file wav.tb

El resultado mostraba un RIFF WAVE PCM estéreo de 24 bits. Eso ya apuntaba a un caso clásico de esteganografía en audio. Al revisar el contenido en hex también se veía un patrón repetitivo al final, algo bastante común cuando hay información escondida en los LSB.

Con eso, la hipótesis razonable era tomar el bit menos significativo de cada byte del bloque data e intentar reconstruir bytes a partir de esos bits.


Análisis

El mensaje estaba escondido usando LSB, tomando un bit por cada byte de audio. Después había que agruparlos de 8 en 8 para formar caracteres.

La parte que realmente hacía diferencia era el orden de los bits. Si se empaquetaban como little-endian no salía nada legible. En cambio, al tomar el primer bit como el más significativo de cada byte, el texto empezaba a verse como ASCII válido y la flag aparecía al principio.

En otras palabras:

  • Se toma b & 1 para cada byte del audio.
  • Se agrupan 8 bits.
  • El primer bit del grupo pasa a ser el bit 7 del byte reconstruido.

Resolución

Con esa idea alcanzaba un script corto para saltar la cabecera WAV, recoger los LSB y reconstruir el mensaje con el primer bit como el más alto del byte:

with open("wav.tb", "rb") as f:
    data = f.read()

audio = data[44:]
bits = [byte & 1 for byte in audio]

msg = bytes(
    sum(bits[i + j] << (7 - j) for j in range(8))
    for i in range(0, len(bits), 8)
    if i + 8 <= len(bits)
)

print(msg[:60].decode())

La salida empezaba así:

TB{H1dd3n_W@ve_Fl@g}#############################################...

El resto era relleno, así que la flag quedaba clara desde el inicio del mensaje.


Flag

TB{H1dd3n_W@ve_Fl@g}