
Fritter v1.3
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.
Fritter
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.
O que é diferente
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.
!! É altamente recomendável compilar a partir do código-fonte !!
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.
Uso
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)
Exemplos
fritter -i payload.exe
fritter -i implant.dll -m RunMain -p "arg1 arg2"
fritter -i payload.exe -g 0 -k 2 -o out.bin
Arquitetura (muitas implementações não estão listadas aqui)
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.
Créditos
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.
Licença
BSD 3-Clause. Veja LICENSE.