Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
ShellGhost — Uma técnica de evasão baseada em memória que torna o shellcode invisível do início ao fim do processo. | Kitploit
Ferramentas/GitHubGitHub/lem0nsec/shellghost
Ferramentas de Criptografia/DescriptografiaForensia de MemóriaExploraçãoShellcodeAnálise de BináriosRed TeamingDesenvolvimento de Payloads
GitHublem0nsec/shellghost

ShellGhost

Uma técnica de evasão baseada em memória que torna o shellcode invisível do início ao fim do processo.

Ver Repositório
1.2k140há 2 anosRevisado pelo Kitploit

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

ShellGhost

Uma técnica de evasão baseada em memória que torna o shellcode invisível do início ao fim do processo.


Motivação

Queria compartilhar este POC de auto-injeção de shellcode para mostrar alguns conceitos de evasão de AV/EDR que podem ser úteis para Red Teaming. Há apenas algumas semanas, criei uma técnica personalizada de evasão em memória que chamei de ShellGhost. Esta técnica surge da necessidade de ter um código que executa um shellcode 'invisível' do início ao fim do processo.


Gerenciamento do Fluxo de Execução da Thread

ShellGhost depende do Vectored Exception Handling em combinação com breakpoints de software para parar ciclicamente a execução da thread, substituir o breakpoint executado por uma instrução do shellcode criptografado com RC4, decriptar a instrução e retomar a execução após restaurar a proteção de memória para RX. Quando a próxima EXCEPTION_BREAKPOINT é levantada, o manipulador de exceções substitui a instrução anterior do shellcode por um novo breakpoint, de modo que a alocação nunca revelará o shellcode completo em estado não criptografado. Isso acontece dentro de uma página de memória privada que é inicialmente marcada como LEITURA/ESCRITA. Ter uma alocação PRV RW não será considerado um 'Indicador de Comprometimento' por scanners de memória como PE-Sieve e Moneta. Quando a alocação se torna RX e a página é escaneada, nada além de breakpoints será encontrado. Isso acontece enquanto o shellcode está realmente em execução. A imagem a seguir mostra que um reverse shell está em execução, mas nenhum IOC é encontrado pelo Moneta (além do binário não estar assinado).

Tentar escanear o processo com Pe-Sieve tem um resultado ainda melhor:


Mapeamento do Shellcode

O Mapeamento do Shellcode é a funcionalidade principal do ShellGhost. Esta tática permite que a thread execute instruções intermitentemente, sem nunca expor todo o shellcode na memória. Isso é possível porque a posição de cada instrução individual do shellcode que a thread executa corresponde à posição de um determinado breakpoint dentro da página de memória alocada. O ShellGhost resolve esta posição calculando o Endereço Virtual Relativo (RVA) do RIP da thread até o endereço base da página de memória alocada e adiciona-o ao endereço base do shellcode criptografado / instruções criptografadas. O número de breakpoints que serão substituídos nem sempre é o mesmo, mas varia dependendo do número de opcodes que cada instrução precisa para ser gerada e interpretada corretamente (QUOTA). Por exemplo, a instrução 'POP RBP' é igual a '5D', o que significa que apenas um breakpoint será substituído. Por outro lado, a instrução 'JMP RAX' requer os opcodes 'FF E0', então dois breakpoints serão substituídos. Por este motivo, criei a seguinte estrutura de dados em C.

root@kitploit:~
typedef struct CRYPT_BYTES_QUOTA {

	DWORD RVA;		// offset para a instrução criptografada 
	DWORD quota;	// número de opcodes que geram a instrução

} CRYPT_BYTES_QUOTA, * PCRYPT_BYTES_QUOTA;

Os breakpoints não são imediatamente substituídos por suas instruções correspondentes. Isso ocorre porque as instruções precisam passar por uma rotina de decriptação antes de serem executadas. É aí que entra o DWORD quota. O ShellGhost depende da agora popular 'SystemFunction032' para realizar a decriptação RC4. Diferente do XOR, o RC4 não é um esquema de criptografia de byte único. Isso significa que o shellcode não pode ser criptografado e decriptado de uma só vez. Esta é também outra razão pela qual cada instrução é tratada separadamente. Após os breakpoints serem substituídos, o tamanho do buffer que a SystemFunction032 precisa será igual à 'quota da instrução', que novamente representa o número de opcodes que compõem a instrução específica. Por exemplo, considere o seguinte trecho.

root@kitploit:~

CRYPT_BYTES_QUOTA instruction[200];
instruction[5].quota = 2

USTRING buf = { 0 }; 	// conterá o buffer a ser decriptado e seu comprimento
USTRING key = { 0 }; 	// conterá a chave RC4 e seu comprimento

buf.Length = 2 		// comprimento do buffer, ou comprimento da instrução a ser decriptada

Sabemos que a instrução número 5 do shellcode é composta por 2 opcodes, então um comprimento de buffer de 2 será passado para a SystemFunction032. Isso é importante porque tentar decriptar todo o shellcode com uma única chamada à SystemFunction032 irá corrompê-lo completamente.

Como o Mapeamento do Shellcode é realizado?

O shellcode precisa ser mapeado com ShellGhost_mapping.py antes da compilação. O script extrai cada instrução individualmente e a trata como um pequeno e independente shellcode. As instruções são criptografadas uma a uma e impressas em formato C todas juntas como unsigned char. O resultado pode ser hardcoded dentro do código C. Abaixo está um exemplo de como ficam as instruções criptografadas de um shellcode do MSF para calc.exe.

Este shellcode tem 98 instruções, então 98 structs CRYPT_BYTES_QUOTA são declaradas. Quando o código executa, essas structs precisam ser populadas com os RVAs e QUOTAs adequados das instruções. O parâmetro '-1' instrui o script de mapeamento a imprimir o trecho de código que faz isso.

Ajustando Parâmetros de Winapi

Shellcodes x64 do Metasploit tipicamente têm parâmetros de string de winapi armazenados entre as instruções. Por assim dizer, um shellcode x64 do MSF que chama Winexec não empurra uma série de bytes com um byte nulo no final para ter a primeira string de parâmetro na pilha. Em vez disso, o registrador RCX (primeiro parâmetro) é um ponteiro dentro do próprio shellcode, como mostra a imagem a seguir.

Isso significa que os breakpoints cuja posição se relaciona com a string nunca serão resolvidos, porque o RIP nunca tocará nessa posição. Na verdade, este código resolve as instruções reais do shellcode pelas quais o RIP passa, não parâmetros que nunca serão executados como instruções. Para corrigir isso, notei que os shellcodes do MSF sempre armazenam um ponteiro para a winapi que estão chamando dentro do registrador RAX, e então fazem um salto para o próprio registrador. Então, quando o VEH do ShellGhost detecta que o breakpoint resolvido é 'JMP RAX' e o registrador RCX contém um ponteiro para uma posição dentro do shellcode, ele tenta também resolver o que é apontado por RCX. Subsequentemente, a execução não é retornada para a memória alocada. Em vez disso, RAX (endereço da winapi) é copiado para RIP e a execução da thread é retomada a partir da winapi, sobrescrevendo o 'JMP RAX' e mantendo a memória alocada como RW. Isso é necessário para reverse shells que chamam WaitForSingleObject, o que faria a thread dormir após o 'JMP RAX' enquanto deixa a memória como RX pelo tempo que o shell permanecer ativo. O trecho de código a seguir contém as duas condições que devem ser atendidas para que o ShellGhost ajuste o registrador RCX quando ele contém uma string de parâmetro de winapi e permita que o shellcode do MSF emita corretamente a chamada de função (WinExec no exemplo aqui).

root@kitploit:~
<snip>
	
if (*(PWORD)exceptionData->ContextRecord->Rip == 0xe0ff) // if RIP is 'JMP RAX'

<snip>

if ((contextRecord->Rcx >= (DWORD_PTR)allocation_base) && (contextRecord->Rcx <= ((DWORD_PTR)allocation_base + sizeof(sh)))) // if RCX is inside the allocation

<snip>

RDX, R8 e R9 (segundo, terceiro e quarto parâmetros) ainda não são cobertos.

Diferenças e Semelhanças com Outras Técnicas

ShellcodeFluctuation é um conceito de evasão em memória muito semelhante. Assim como ele, a memória alocada aqui 'flutua' de RW para RX. Em contraste, o ShellGhost introduz as seguintes melhorias:

  • Criptografia RC4 mais 'Mapeamento do Shellcode' em vez de XOR de byte único
  • Não há necessidade de hookear funções
  • Suporte para shellcodes do Metasploit

No entanto, o ShellGhost está longe de ser uma técnica perfeita. Ainda sofre da maior desvantagem que todas essas técnicas têm, ou seja, a necessidade de ter memória executável privada em algum ponto durante a execução. Técnicas mais avançadas como Foliage já encontraram uma maneira de contornar isso. Além disso, uma alocação de memória cheia de breakpoints de software pode ser detectada por uma regra YARA. A imagem a seguir mostra o Moneta detectando corretamente um IOC para a alocação PRV RX.

Quando se trata de evadir uma solução EDR, a varredura de memória é apenas parte de um quadro maior. A ausência completa de IOCs não significa necessariamente que um binário usando esta técnica será eficaz contra um determinado EDR. Pelo que posso dizer, experimentei situações em que a solução nem permite que você execute o binário da maneira que está fazendo. O outro lado da moeda é que os IOCs nem sempre são indicadores precisos, e alguns deles podem se revelar falsos positivos. Dito isso, esta é apenas uma técnica bruta e uma inspiração que espero que o leitor aprecie. O Red Teamer sabe que, assim como os componentes de um EDR, a evasão em memória é apenas um componente do motor.

Notas

A compilação requer a desativação da vinculação incremental. Este projeto do VS já tem todas as opções de compilador/linker configuradas.

Baixar ferramenta