
Codificador binario polimórfico para payloads de seguridad ofensiva. Codifica shellcode con bucle de retroalimentación basado en LFSR, inyección de instrucciones basura y ofuscación multi-iteración para evadir la detección estática.
SGN es un codificador binario polimórfico con fines de seguridad ofensiva, como la generación de payloads binarios estáticamente indetectables. Utiliza un bucle de realimentación aditiva para codificar las instrucciones binarias proporcionadas, similar a LFSR. Este proyecto es la reimplementación de la Shikata ga nai original en golang con muchas mejoras.
[!WARNING]
El proyecto se ha portado recientemente a Rust. Este port mantiene el diseño y el comportamiento originales, pero sustituye el ensamblador de texto keystone por el ensambladoriced-x86escrito en Rust puro, por lo que no hay dependencias nativas de bibliotecas: se compila con un simplecargo build. Consulta la rama sgn-go para la versión heredada en Go.
Para la comunidad de seguridad ofensiva, la implementación original del codificador shikata ga nai está considerada como el mejor codificador de shellcode (hasta ahora). Pero con los años, los investigadores de seguridad han encontrado varias trampas a la hora de detectar estáticamente el stub del decodificador (trabajo relacionado artículo de FireEye). La principal motivación de este proyecto fue crear un codificador mejor que codifique el binario proporcionado hasta el punto de que sea idéntico a datos totalmente aleatorios y no sea posible detectar la presencia de un decodificador.
¡Por fin shellcodes x64 codificados correctamente!Clave LFSR reducida a 1 byteEl stub del decodificador también se codifica con un esquema pseudoaleatorio¡El stub se decodifica a sí mismo SIN usar ninguna condición de bucle!Generador de instrucciones basura aleatorias añadido con keystoneNinguno de los registros se sobrescribe (preámbulo opcional, puede reducir el polimorfismo)Cada pasada de codificación:
--plain, cifra el propio stub con un cifrado de esquema aleatorio por ejecución (XOR/ADD/SUB/ROL/ROR/NOT sobre DWORDs) y antepone un decodificador de esquema autoubicado, de modo que incluso el decodificador parece datos aleatorios;--enc veces con semillas nuevas;cargo install sgn
También puedes obtener los binarios precompilados AQUÍ.
Uso
-h se explica por sí solo; usa -v si quieres ver lo que ocurre entre bastidores ( ͡° ͜ʖ ͡°)_/¯
__ _ __ __ _
___ / / (_) /_____ _/ /____ _ ___ ____ _ ___ ___ _(_)
(_-</ _ \/ / '_/ _ `/ __/ _ `/ / _ `/ _ `/ / _ \/ _ `/ /
/___/_//_/_/_/\_\\_,_/\__/\_,_/ \_, /\_,_/ /_//_/\_,_/_/
========[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
Ejemplo:
sgn -i shellcode.bin -o encoded.bin -a 64 --badchars '\x00\x0a\x0d'
La siguiente imagen es un diagrama básico del flujo de trabajo del codificador. Pero ten en cuenta que los tamaños, ubicaciones y órdenes cambiarán para las instrucciones basura, los decodificadores y los decodificadores de esquema en cada iteración.
El propio LFSR es bastante potente en términos de espacio de probabilidad. Para aún más polimorfismo, se añaden instrucciones basura al principio del payload bruto sin codificar. La siguiente imagen muestra la matriz compañera del polinomio característico del LFSR y, denotando la semilla como un vector columna, el estado del registro en configuración de Fibonacci tras k pasos.
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());
Consulta examples/encode_binary.rs.
cargo test
La suite incluye pruebas unitarias de ida y vuelta del cifrado además de pruebas de ejecución reales: el shellcode x64 codificado se mapea como ejecutable y se ejecuta en el propio proceso, y el shellcode x86 se ejecuta mediante un harness auxiliar cc -m32 (se omite automáticamente si no hay cadena de herramientas de 32 bits). Ambas arquitecturas se prueban en los modos plain, schema, multicapa y safe-register, incluido un bucle de estrés aleatorizado.
iced-x86 en lugar del texto de ensamblador de keystone; los stubs del decodificador se reconstruyeron en torno al direccionamiento relativo a RIP (x64) y a un esquema de dos pasadas call/pop (x86).random_byte ahora abarca todo el rango 0..=255, y el presupuesto de tamaño de basura (--max) se aplica de forma consistente como límite por bloque.