
Thread Stack Spoofing - PoC para uma técnica avançada de evasão na memória que permite ocultar melhor a alocação de memória do shellcode injetado de scanners e analistas.
Uma implementação de PoC para uma técnica avançada de evasão em memória que falsifica a Pilha de Chamadas de Thread. Esta técnica permite contornar regras de exame de memória baseadas em thread e ocultar melhor shellcodes enquanto estão na memória do processo.
Esta é uma implementação de exemplo para a técnica Thread Stack Spoofing com o objetivo de evadir Analistas de Malware, AVs e EDRs que procuram referências aos frames do shellcode na pilha de chamadas de uma thread examinada. A ideia é esconder referências ao shellcode na pilha de chamadas da thread, mascarando assim alocações que contêm o código do malware.
A implementação, juntamente com o meu ShellcodeFluctuation, traz para a comunidade de Segurança Ofensiva implementações de exemplo para acompanhar o que é oferecido por produtos C2 comerciais, para que possamos fazer o mesmo em nossas ferramentas de Red Team. 💪
A implementação atual difere bastante do que foi originalmente publicado.
Isto porque percebi que existe uma abordagem muito mais simples para terminar o processamento da pilha de chamadas da thread e ocultar os frames relacionados ao shellcode, simplesmente escrevendo 0 no endereço de retorno do primeiro frame que controlamos:
void WINAPI MySleep(DWORD _dwMilliseconds)
{
[...]
auto overwrite = (PULONG_PTR)_AddressOfReturnAddress();
const auto origReturnAddress = *overwrite;
*overwrite = 0;
[...]
*overwrite = origReturnAddress;
}
A implementação anterior, que utilizava StackWalk64, pode ser acessada neste commit c250724.
Esta implementação é muito mais estável e funciona bem tanto em Debug como em Release nas duas arquiteturas – x64 e x86.
É assim que uma pilha de chamadas pode parecer quando NÃO está falsificada:

Isto, por sua vez, quando o spoofing da pilha de threads está ativado:

Acima podemos ver que o último frame na nossa pilha de chamadas é o nosso callback MySleep.
Alguém pode perguntar se isso imediatamente traz novas oportunidades de IOCs? Regras de caça podem procurar threads cujas pilhas de chamadas não se desenrolam para os seguintes pontos de entrada esperados da thread, localizados em bibliotecas do sistema:
kernel32!BaseThreadInitThunk+0x14
ntdll!RtlUserThreadStart+0x21
No entanto, a pilha de chamadas da thread falsificada pode parecer um pouco estranha à primeira vista; um breve exame do meu sistema mostrou que também existem outras threads que não se desenrolam para os pontos de entrada acima:

A captura de tela acima mostra uma thread do Total Commander x64 não modificado. Como podemos ver, a sua pilha de chamadas se assemelha bastante à nossa em termos de frames iniciais da pilha.
Por que deveríamos nos preocupar em falsificar cuidadosamente a nossa pilha de chamadas quando existem processos que exibem características que podemos simplesmente imitar?
O algoritmo geral é o seguinte:
dbghelp.dll, chamar SymInitializehook) kernel32!Sleep apontando de volta para o nosso callback.VirtualAlloc + memcpy + CreateThread. A thread deve começar a partir da nossa função runShellcode para evitar que o StartAddress da thread aponte para algo inesperado e anómalo (como ntdll!RtlUserThreadStart+0x21)MySleep é invocado.0, o que efetivamente deve terminar a pilha de chamadas.::SleepEx para permitir que o Beacon durma enquanto aguarda mais comunicação.Os endereços de retorno das funções estão espalhados por toda a área de memória da pilha da thread, apontados pelo registo RBP/EBP.
Para encontrá-los na pilha, precisamos primeiro recolher os ponteiros de frame e depois desreferenciá-los para sobrescrever:

(a imagem acima foi retirada do post de Eli Bendersky chamado Stack frame layout on x86-64)
*(PULONG_PTR)(frameAddr + sizeof(void*)) = Fake_Return_Address;
A implementação inicial do ThreadStackSpoofer fazia isso nas funções walkCallStack e spoofCallStack, no entanto a implementação atual mostra que esses esforços não são necessários para manter uma pilha de chamadas furtiva.
Caso de uso:
C:\> ThreadStackSpoofer.exe <shellcode> <spoof>
Onde:
<shellcode> é o caminho para o ficheiro do shellcode<spoof> quando 1 ou true ativa o spoofing da pilha de threads; qualquer outra coisa desativa-o.Exemplo de execução que falsifica a pilha de chamadas da thread do beacon:
PS D:\dev2\ThreadStackSpoofer> .\x64\Release\ThreadStackSpoofer.exe .\tests\beacon64.bin 1
[.] Reading shellcode bytes...
[.] Hooking kernel32!Sleep...
[.] Injecting shellcode...
[+] Shellcode is now running.
[>] Original return address: 0x1926747bd51. Finishing call stack...
===> MySleep(5000)
[<] Restoring original return address...
[>] Original return address: 0x1926747bd51. Finishing call stack...
===> MySleep(5000)
[<] Restoring original return address...
[>] Original return address: 0x1926747bd51. Finishing call stack...
Observe o código e a sua implementação, entenda o conceito e reimplemente-o nos seus próprios Carregadores de Shellcode que você utiliza nas suas atividades de Red Team. Esta é mais uma técnica de evasão avançada em memória que aumenta as chances da sua Equipe de não ser detectada por Antivírus, EDRs e Analistas de Malware que estão a analisar os seus implantes.
Ao desenvolver o seu carregador de shellcode avançado, você também pode querer implementar:
BeaconEyeRW (de RX/RWX) e criptografar o seu conteúdo – usando a técnica Shellcode Fluctuation – imediatamente antes de dormir (o que pode evadir scanners como o Moneta ou o pe-sieve)