
Encodeur binaire polymorphe pour payloads de sécurité offensive. Encode du shellcode avec une boucle de rétroaction basée sur LFSR, une injection d'instructions parasites et une obfuscation multi-itérations pour échapper à la détection statique.
SGN est un encodeur binaire polymorphe destiné à des fins offensives en sécurité, telles que la génération de charges utiles binaires statiquement indétectables. Il utilise une boucle de rétroaction additive pour encoder les instructions binaires fournies, de manière similaire au LFSR. Ce projet est la réimplémentation de l'original Shikata ga nai en golang, avec de nombreuses améliorations.
[!WARNING]
Le projet a récemment été porté vers Rust. Ce port conserve la conception et le comportement d'origine, mais remplace l'assembleur textuel keystone par l'assembleur purement Rust , donc — il se compile avec un simple . Consultez la branche pour l'ancienne version Go.
cargo buildPour la communauté de la sécurité offensive, l'implémentation originale de l'encodeur shikata ga nai est considérée comme le meilleur encodeur de shellcode (jusqu'à présent). Mais au fil des années, les chercheurs en sécurité ont découvert plusieurs failles permettant de détecter statiquement le stub de décodage (travail connexe article FireEye). La principale motivation de ce projet était de créer un meilleur encodeur, qui encode le binaire fourni au point de le rendre identique à des données totalement aléatoires et de rendre impossible la détection de la présence d'un décodeur.
Enfin des shellcodes x64 correctement encodés !Clé LFSR réduite à 1 octetLe stub du décodeur est également encodé avec un schéma pseudo-aléatoireLe stub se décode lui-même SANS utiliser de condition de boucle !!Générateur aléatoire d'instructions factices ajouté avec keystoneAucun des registres n'est écrasé (préambule optionnel, peut réduire le polymorphisme)Chaque passe d'encodage :
--plain, chiffre le stub lui-même avec un chiffrement de schéma aléatoire par exécution (XOR/ADD/SUB/ROL/ROR/NOT sur des DWORD) et préfixe un décodeur de schéma auto-localisant, de sorte que même le décodeur ressemble à des données aléatoires ;--enc fois avec de nouvelles graines ;cargo install sgn
Vous pouvez également obtenir les binaires pré-compilés ICI.
Utilisation
-h est assez explicite ; utilisez -v si vous voulez voir ce qui se passe en coulisses ( ͡° ͜ʖ ͡°)_/¯
__ _ __ __ _
___ / / (_) /_____ _/ /____ _ ___ ____ _ ___ ___ _(_)
(_-</ _ \/ / '_/ _ `/ __/ _ `/ / _ `/ _ `/ / _ \/ _ `/ /
/___/_//_/_/_/\_\\_,_/\__/\_,_/ \_, /\_,_/ /_//_/\_,_/_/
========[Author:-Ege-Balcı-]====/___/=======v2.0.2=========
┻━┻ ︵ヽ(`Д´)ノ︵ ┻━┻ (ノ ゜Д゜)ノ ︵ 仕方がない
sgn [OPTIONS]
Options:
-i, --input <INPUT> Input binary path
-o, --out <OUT> Encoded output binary name (default: <input>.sgn)
-a, --arch <ARCH> Binary architecture (32/64) [default: 64]
-c, --enc <ENC> Number of times to encode the binary [default: 1]
-M, --max <MAX> Maximum bytes per garbage block [default: 50]
--plain Do not encode the decoder stub
--ascii Generate a fully ASCII-printable payload (slow)
-S, --safe Preserve all register values (no clobber)
--badchars <BADCHARS> Avoid these bytes, hex format (e.g. \x00\x0a)
-v, --verbose Verbose mode
-h, --help Print help
-V, --version Print version
Exemple :
sgn -i shellcode.bin -o encoded.bin -a 64 --badchars '\x00\x0a\x0d'
L'image suivante est un schéma de flux de travail de base pour l'encodeur. Mais gardez à l'esprit que les tailles, les emplacements et les ordres changeront pour les instructions factices, les décodeurs et les décodeurs de schéma à chaque itération.
Le LFSR lui-même est assez puissant en termes d'espace de probabilité. Pour encore plus de polymorphisme, des instructions factices sont ajoutées au début de la charge utile brute non encodée. L'image ci-dessous montre la matrice compagnon du polynôme caractéristique du LFSR et, en désignant la graine comme un vecteur colonne, l'état du registre en configuration de Fibonacci après k étapes.
use sgn::Encoder;
let shellcode = std::fs::read("payload.bin")?;
let mut encoder = sgn::Encoder::new(64)?;
let encoded = encoder.encode(&shellcode)?;
println!("encoded {} bytes", encoded.len());
Voir examples/encode_binary.rs.
cargo test
La suite comprend des tests unitaires de chiffrement aller-retour ainsi que des tests d'exécution réels : les shellcodes x64 encodés sont mappés en mémoire exécutable et exécutés dans le processus, et les shellcodes x86 sont exécutés via un harnais d'assistance cc -m32 (ignoré automatiquement si aucune chaîne d'outils 32 bits n'est présente). Les deux architectures sont testées dans les modes plain, schema, multi-couche et safe-register, y compris une boucle de stress aléatoire.
iced-x86 plutôt que le texte d'assemblage de keystone ; les stubs de décodeur ont été reconstruits autour de l'adressage relatif à RIP (x64) et d'un schéma en deux passes call/pop (x86).random_byte couvre désormais toute la plage 0..=255, et le budget de taille des déchets (--max) est appliqué de manière cohérente comme une limite par bloc.