
Um gerador de carregadores de shellcode com suporte para múltiplas técnicas de injeção, construído para engajamentos de red team.
hollow é um gerador de carregadores de shellcode. Você fornece um binário de shellcode bruto e um perfil, e ele gera um carregador PE do Windows compilado com seu shellcode criptografado internamente.
Binários estão disponíveis na página de releases, ou compile a partir do código-fonte:
go build -o hollow .
Requer x86_64-w64-mingw32-gcc para compilação cruzada.
No Arch Linux: pacman -S mingw-w64-gcc
No Debian/Ubuntu: apt install gcc-mingw-w64-x86-64
./hollow -shellcode payload.bin -profile profiles/new_process_injection_sc.json
| Flag | Descrição |
|---|---|
-shellcode | Caminho para o shellcode bruto (.bin) |
-profile | Caminho para um arquivo JSON de perfil |
-templates | Diretório de templates (padrão: ./templates) |
hollow segue um pipeline de três etapas: criptografar, substituir, compilar.
Seu shellcode é criptografado com AES-256-CBC usando uma chave e IV gerados aleatoriamente a cada execução. Ambos são incorporados no binário de saída. O template C escolhido então tem seus placeholders substituídos pelo shellcode criptografado, a chave e o IV, e o resultado é compilado em um PE estaticamente ligado e sem símbolos pelo MinGW.
Em tempo de execução, o carregador descriptografa o shellcode usando o BCrypt do Windows e o executa utilizando a técnica de injeção que o template implementa.
Templates são os arquivos fonte C que implementam a lógica real de injeção. Cada um reside em templates/ e contém tokens de substituição (${SHELLCODE}, ${KEY}, ${IV}, ${TARGET_PROCESS}) que o hollow preenche antes da compilação. Você seleciona um template através do seu perfil.
hollow vem com seis templates:
Você pode escrever seus próprios templates e colocá-los em templates/ — o hollow os detectará automaticamente desde que seu perfil aponte para eles.
Perfis são arquivos JSON que informam ao hollow qual template usar, qual processo alvo e como compilar a saída. Eles ficam em profiles/ e devem ser personalizados por engajamento.
{
"name": "New Process Injection via Direct Syscalls",
"author": "",
"template": "new_process_injection_sc",
"target_process": "C:\\Windows\\System32\\cmd.exe",
"arch": "x64",
"compile": {
"automatic": true,
"gcc": "x86_64-w64-mingw32-gcc",
"strip": true,
"output_type": "exe"
},
"output_dir": "./output"
}
output_type é exe ou dll. Defina automatic: false para despejar o código C substituído no disco em vez de compilá-lo, útil se você quiser modificar o código antes de compilar.
O arquivo de saída é gravado em output_dir, nomeado como {template}_loader.{exe|dll}.
Template: new_process_injection
Técnica: Injeção de Thread Remota em um processo recém-criado.
Cria o processo alvo com CREATE_BREAKAWAY_FROM_JOB | CREATE_NO_WINDOW, aguarda dois segundos para sua inicialização, então aloca memória em seu espaço de endereçamento, escreve o shellcode descriptografado, marca-o como executável e cria uma thread remota apontando para ele. A flag BREAKAWAY é necessária quando o carregador é lançado a partir do WinRM, que envolve todos os processos em um objeto de job. O alvo é um caminho completo do executável.
Chamadas Win32: CreateProcessA, VirtualAllocEx, WriteProcessMemory, VirtualProtectEx, CreateRemoteThread.
Template: new_process_injection_sc
Técnica: Injeção de Thread Remota em um processo recém-criado, via chamadas de sistema diretas (Hell's Gate).
Mesmo comportamento que new_process_injection, mas toda alocação e chamada de thread contorna a camada Win32 completamente. Os SSNs são resolvidos da ntdll em tempo de execução e executados via instrução syscall bruta. Veja a seção de benchmark.
Template: remote_thread_injection
Técnica: Injeção Clássica de Thread Remota em um processo existente.
Encontra um processo em execução pelo nome usando CreateToolhelp32Snapshot, abre um handle para ele, então aloca memória, escreve o shellcode e cria uma thread remota. Nenhum novo processo é criado. Melhor usado contra processos de longa duração como explorer.exe. O alvo é um nome de imagem do processo, não um caminho completo.
Chamadas Win32: OpenProcess, VirtualAllocEx, WriteProcessMemory, VirtualProtectEx, CreateRemoteThread.
Template: remote_thread_injection_sc
Técnica: Injeção Clássica de Thread Remota em um processo existente, via chamadas de sistema diretas (Hell's Gate).
Mesmo comportamento que remote_thread_injection, contornando a camada Win32. Veja a seção de benchmark.
Template: earlybird_apc
Técnica: Injeção APC Early Bird (CyberArk, 2018).
Cria o processo alvo em estado suspenso (CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB | CREATE_NO_WINDOW), escreve o shellcode descriptografado em seu espaço de endereçamento, então enfileira uma Chamada de Procedimento Assíncrona (APC) para a thread principal apontando para o shellcode via QueueUserAPC, e retoma com ResumeThread. Como a APC é disparada antes que o ponto de entrada do processo seja executado, o shellcode executa antes que qualquer ferramenta defensiva no processo tenha sido inicializada. Evita completamente a tríade VirtualAllocEx + WriteProcessMemory + CreateRemoteThread.
Template: dll_sideload
Técnica: DLL Sideloading / Execução de Shellcode In-Process.
Produz uma DLL em vez de um EXE. Em DLL_PROCESS_ATTACH, uma thread é criada que descriptografa e executa o shellcode no próprio processo: VirtualAlloc, memcpy, VirtualProtect, em seguida uma chamada de função direta para o shellcode. O processo hospedeiro deve permanecer ativo enquanto o payload inicializa (cerca de 10 segundos para um beacon Sliver). Implante colocando a DLL em um local onde um binário legítimo a carregará através de uma entrada de caminho de busca ausente.
Testado no Windows 10 Build 19041, proteção em tempo real do Windows Defender ativada, definições 1.453.354.0, com um beacon Sliver embrulhado com Donut de 17 MB como payload:
Trojan:Win64/AsyncRat.RPY!MTB é uma regra de comportamento de ameaça de máquina acionada pela sequência clássica de injeção remota: VirtualAllocEx + WriteProcessMemory + CreateRemoteThread chamados em um handle de processo remoto através da camada de API Win32. O Defender registra um callback de kernel que é acionado quando essas três chamadas aparecem em sequência.
Os templates _sc contornam isso nunca chamando essas funções Win32. Em vez disso, eles resolvem os números de serviço de chamada de sistema (SSN) do kernel diretamente da ntdll em tempo de execução usando Hell's Gate: cada stub ntdll não hookado começa com o prólogo de quatro bytes 4C 8B D1 B8, e o SSN está no offset 4. A chamada de sistema real é uma função nua do GCC contendo nada além de movq %rcx, %r10 / movl ssn(%rip), %eax / syscall / ret, que é exatamente a sequência que o próprio stub ntdll executaria. O callback do Defender nunca é acionado porque os wrappers Win32 monitorados nunca são invocados.
Em sistemas onde os stubs da ntdll são corrigidos por um EDR completo (prólogo substituído por um jump), a verificação de stub limpo falha e o carregador sai precocemente. O Halo's Gate (escaneamento de stubs vizinhos para inferir o SSN) não está implementado.
O código do carregador adiciona aproximadamente 19 KB de overhead. O tamanho da saída é essencialmente o tamanho do shellcode de entrada. Um beacon Sliver de 17 MB produz um carregador de 18 MB. Um shellcode típico do Metasploit (~200 KB) produziria um carregador de ~220 KB.
Contribuições são bem-vindas. Se você tiver um template que escreveu e quiser adicioná-lo, ou melhorias nos existentes, sinta-se à vontade para abrir um PR. Se encontrar um bug ou tiver uma sugestão, abra uma issue.
O objetivo desta ferramenta é facilitar o processo de desenvolvimento de carregadores, não ser um produto finalizado. Novos templates, melhores perfis e melhorias no núcleo são todos bem-vindos. hollow também foi construído com a intenção de ajudar as pessoas a entender os conceitos por trás de carregadores de shellcode e técnicas de injeção, portanto, um código de template claro e legível é tão valioso quanto a funcionalidade.
Em breve, detalharei os conceitos por trás de cada técnica e o uso completo do hollow no meu blog. Fique atento.
| Template | Técnica |
|---|
new_process_injection | Injeção de thread remota em um processo recém-criado |
new_process_injection_sc | Mesmo, via chamadas de sistema diretas (Hell's Gate) |
remote_thread_injection | Injeção clássica de thread remota em um processo existente |
remote_thread_injection_sc | Mesmo, via chamadas de sistema diretas (Hell's Gate) |
earlybird_apc | Injeção APC Early Bird |
dll_sideload | DLL sideloading, produz uma DLL em vez de um EXE |
| Template | Alerta comportamental do Defender | Sessão estabelecida |
|---|
| new_process_injection | Trojan:Win64/AsyncRat.RPY!MTB | sim |
| remote_thread_injection | Trojan:Win64/AsyncRat.RPY!MTB | sim |
| earlybird_apc | nenhum | sim |
| dll_sideload | nenhum | sim |
| new_process_injection_sc | nenhum | sim |
| remote_thread_injection_sc | nenhum | sim |