
Gerador de shellcode polimórfico para execução em memória de EXE, DLL, .NET, VBScript e JScript com randomização por saída e por build para evasão e resistência a assinaturas.
O primo evasivo do Donut.
Fritter é um fork fortemente modificado do gerador de shellcode Donut, de TheWover e Odzhan. Ele gera shellcode independente de posição para execução em memória de VBScript, JScript, EXE, DLL e assemblies .NET, com foco em evasão e resistência a assinaturas. O código é exclusivamente x64.
Muita coisa. O Fritter remove recursos que não são comumente necessários e substitui partes internas que se tornaram bem assinadas ao longo dos anos. As camadas de criptografia, compressão, hashing e resolução de API foram todas reformuladas, entre muitas outras áreas.
Polimorfismo e evasão são o objetivo do projeto. Cada saída é única, e cada build da ferramenta é, por si só, único. Há duas camadas distintas:
Randomização por saída: aplicada a cada invocação do fritter. O stub de entrada, o decodificador polimórfico, as chaves de criptografia e muitos elementos estruturais dentro do shellcode gerado são regenerados a partir de nova entropia a cada build de PIC.
Randomização por build: aplicada a cada vez que o próprio fritter é compilado. Constantes de rotação de cifra e hash, layout da tabela de resolução de API, embaralhamento de strings no lado do shim, direções da varredura do PEB, padrões de limpeza pós-execução e vários eixos estruturais dentro do loader e do shim são gravados em tempo de compilação.
Em tempo de execução, o Fritter minimiza a pegada executável do loader. O loader é particionado em funções individualmente criptografadas, cada uma com sua própria seção PE, chave XOR e dispatcher. Apenas os bytes de uma função estão em texto claro a qualquer momento. O modelo anterior de janela deslizante baseado em VEH foi aposentado em favor deste modelo de despacho.
Isso importa. Os binários pré-compilados disponíveis para teste em releases compartilham suas constantes de build entre todos os usuários do binário.
Compile sua própria cópia. Os eixos por build são re-randomizados a cada invocação do make:
# Linux, static-musl ELF, no runtime libc dependency
# Requires: build-essential, mingw-w64, musl-tools
make -f Makefile.linux release
# Windows (MSVC), recommended on Windows
nmake -f Makefile.msvc
No Windows, o MSVC é o toolchain recomendado. Ele aloca cada função do loader em sua própria seção PE alinhada por página, que é o que o modelo de despacho por função N>1 precisa. O mingw atualmente emite tudo em um único .text e, portanto, executa uma única entrada cobrindo todo o loader com uma única chave XOR (funcionalmente idêntico à saída do MSVC, mas com apenas um dispatcher de polimorfismo em vez de vários). Se você não tem o Visual Studio, compile no WSL com Makefile.linux; ele faz cross-compilação do loader Windows via mingw-w64.
Cada make executa tools/gen_poly para gerar novas constantes por build e tools/gen_api_shuffle para permutar a tabela de resolução de API. O binário fritter resultante é, por si só, único, com constantes de cifra diferentes, constantes de hash diferentes, um layout de tabela de API diferente, embaralhamento de strings no shim diferente, e assim por diante. Cada shellcode gerado por esse binário compartilhará essas constantes por build, mas variará nos eixos por saída.
Uma pasta /test é incluída com calc.exe e inject_local64.exe para testar o Fritter. Para recompilar os hosts de teste você mesmo: nmake -f Makefile.msvc harness, ou make -f Makefile.linux harness no WSL.
fritter [options] -i <EXE/DLL/VBS/JS>
INPUT
-i, --input <path> Input file to execute in-memory
-p, --args <args> Parameters / command line for target
-c, --class <name> Class name (required for .NET DLL)
-m, --method <name> Method or function for DLL
-r, --runtime <ver> CLR runtime version
-w, --unicode Pass command line as UNICODE
-t, --thread Run unmanaged EXE entrypoint as thread
OUTPUT
-o, --output <path> Output file (default: loader.bin)
-f, --format <1-8> 1=Bin 2=B64 3=C 4=Ruby 5=Py 6=PS 7=C# 8=Hex
-x, --exit <1-3> 1=Thread (default) 2=Process 3=Block
-y, --fork <offset> Fork thread, continue at RVA offset
LOADER
-e, --entropy <1-3> 1=None 2=Random names 3=Names+Crypto (default)
-k, --headers <1-2> 1=Overwrite (default) 2=Keep all
-g, --chunked <0-1> (deprecated; dispatch shim is always used)
-d, --domain <name> AppDomain name for .NET
-j, --decoy <path> Decoy module for Module Overloading
STAGING
-n, --modname <name> Module name for HTTP staging
-s, --server <url> Server URL (supports basic auth)
fritter -i payload.exe
fritter -i implant.dll -m RunMain -p "arg1 arg2"
fritter -i payload.exe -g 0 -k 2 -o out.bin
Um payload de shellcode do Fritter é estruturado em camadas aninhadas, cada uma descriptografando ou preparando a próxima:
Stub de entrada. Um prefixo de lixo aleatório de comprimento variável, uma rotina de alinhamento de RSP gerada por saída e um trampolim generativo. Baseado na disciplina do Shikata Ga Nai.
Decodificador XOR polimórfico. Montado em duas passagens. Alocação de registradores sorteada de um pool por embaralhamento de Fisher-Yates. Comprimento da chave escolhido por saída. Lixo inserido entre cada instrução real. Os grupos de instruções móveis do loop principal são reordenados dentro de restrições de corretude.
Shim de despacho. Substitui o shim anterior de janela deslizante baseado em VEH. Alterna a região do loader de RW->RWX e então passa o controle para a entrada do loader. No despacho N>1, cada chamada é roteada por um thunk por função até um dispatcher por função que descriptografa, executa e limpa ao retornar. O layout de opcodes de cada dispatcher varia por build (veja CHANGELOG v1.3).
Loader. Mapeador de PE em memória. Resolve APIs por hash, mapeia o PE embutido por meio das APIs de seção, aplica imports / realocações / callbacks de TLS, invoca o ponto de entrada e então limpa. A direção da varredura do PEB, o byte de limpeza pós-execução e os locais de salt estrutural no MainProc são todos randomizados por build.
Limpeza. Limpa as páginas do loader com um padrão de bytes por build, apaga a instância e sai via término de thread ou processo conforme -x. Nenhum handler de VEH ou struct de contexto para remover.
A pegada residual após a execução é uma pequena página RWX onde o shim de despacho foi executado. No modo thread, a seção PE mapeada é intencionalmente deixada intacta para que os callbacks da CRT tenham continuações.
Fritter é construído sobre o trabalho de TheWover e Odzhan, cujo projeto original Donut tornou a geração de shellcode independente de posição acessível e prática. A arquitetura, o design do loader e o framework PIC deles são a base sobre a qual tudo aqui é construído. O mapeamento de PE, a hospedagem .NET e os caminhos de execução de script são em grande parte o trabalho deles, mantidos e respeitados.
BSD 3-Clause. Veja LICENSE.