
Uma técnica avançada de evasão em memória que alterna a proteção de memória do shellcode entre RW/NoAccess e RX e, em seguida, criptografa/descriptografa seu conteúdo.
Uma implementação de PoC para mais uma técnica de evasão em memória que criptografa e descriptografa ciclicamente o conteúdo do shellcode para então fazê-lo flutuar entre as proteções de memória RW (ou NoAccess) e RX.
Quando nosso shellcode reside em páginas de memória RW ou NoAccess, scanners como Moneta ou pe-sieve não conseguirão rastreá-lo e despejá-lo para análises adicionais.
Após lançar o ThreadStackSpoofer, recebi algumas perguntas sobre o seguinte ponto do README:
Altere a proteção das páginas de memória do seu Beacon para RW (de RX/RWX) e criptografe seu conteúdo antes de dormir (isso poderia evadir scanners como Moneta ou pe-sieve)
Antes, eu tinha quase certeza de que a comunidade já sabia como criptografar/descriptografar seus payloads e alterar suas proteções de memória para simplesmente evadir scanners de memória que procuram regiões executáveis anômalas. As perguntas provaram o contrário, então decidi lançar este PoC não armamentado para documentar mais uma estratégia de evasão e oferecer uma implementação de exemplo para a comunidade trabalhar.
Este PoC é uma demonstração de uma técnica bastante simples, já conhecida pela comunidade ofensiva (então não estou trazendo nada de novo aqui, na verdade), na esperança de revelar o segredo por trás da mágica mostrada por alguns frameworks comerciais que demonstram suas capacidades de evasão mirando ambos os scanners de memória mencionados.
Aqui está uma comparação ao flutuar para RW (outra opção é flutuar para PAGE_NOACCESS - descrito abaixo):

Esta implementação, juntamente com meu ThreadStackSpoofer, traz à comunidade de Segurança Ofensiva implementações de exemplo para acompanhar o que é oferecido pelos produtos C2 comerciais, para que possamos não fazer pior em nossas ferramentas de Red Team. 💪
Este programa realiza auto-injeção de shellcode (aproximadamente via clássico VirtualAlloc + memcpy + CreateThread).
Quando o shellcode é executado (esta implementação visa especificamente implantes Cobalt Strike Beacon), uma função do Windows será hookada interceptando o momento em que o Beacon adormece, kernel32!Sleep.
Sempre que a função hookada MySleep é invocada, ela localiza os limites de sua alocação de memória, altera a proteção para RW e aplica xor32 em todos os bytes armazenados ali.
Após aguardar o tempo esperado, quando o shellcode retorna ao nosso handler MySleep, descriptografamos os dados do shellcode e revertemos a proteção para RX.
PAGE_READWRITE funciona da seguinte formakernel32!Sleep apontando de volta para o nosso callback.VirtualAlloc + memcpy + CreateThread. Ao contrário do que tínhamos no ThreadStackSpoofer, aqui não estamos hookando nada na ntdll para executar nosso shellcode, mas sim saltando para ele a partir de nossa própria função. Isso tenta evitar deixar IOCs simples na memória apontando para memória modificada da ntdll.MySleep é invocado.RWkernel32!Sleep original para evitar deixar um IOC simples na memória indicando que Sleep foi trampolinada (hookada in-line).::Sleep original é feita para deixar o Beacon dormir enquanto aguarda novas comunicações.RX e então re-hookamos para garantir a interceptação dos sleeps seguintes.PAGE_NOACCESS funciona da seguinte formakernel32!Sleep apontando de volta para o nosso callback.VirtualAlloc + memcpy + CreateThread ...MySleep é invocado.PAGE_NOACCESSkernel32!Sleep original para evitar deixar um IOC simples na memória indicando que Sleep foi trampolinada (hookada in-line).::Sleep original é feita para deixar o Beacon dormir enquanto aguarda novas comunicações.kernel32!Sleep para garantir a interceptação dos sleeps seguintes.A técnica não é nova, nem algo que eu tenha criado. É apenas uma implementação mostrando o conceito e sua utilização prática para permitir que nossa comunidade de Segurança Ofensiva acompanhe o que é oferecido pelos frameworks C2 comerciais.
Na verdade, fui apresentado à ideia de alternar a proteção de memória do shellcode há alguns anos, através do trabalho de Josh Lospinoso em seu incrível Gargoyle.
Aqui está mais contexto:
Gargoyle leva o conceito de shellcode autoconsciente e autoflutuante um passo adiante, utilizando uma sequência de ROP que chama VirtualProtect.
No entanto, por mais impressionante que seja a técnica, é igualmente difícil utilizá-la com o Beacon do Cobalt Strike sem ter que matar sua thread e ficar reinicializando o Beacon na memória.
Isso está longe de ser perfeito; no entanto, como já operamos a partir da base do nosso próprio processo loader de auto-injeção, podemos fazer o que quisermos com o ambiente no qual o shellcode opera e ocultá-lo como preferirmos. Esta técnica (e a anterior, o ThreadStackSpoofer) mostra as vantagens de executar nossos shellcodes dessa forma.
A implementação da flutuação para PAGE_NOACCESS é inspirada no trabalho de ORCA666 apresentado em seu injetor https://github.com/ORCA666/0x41.
Ele mostrou que:
Esta implementação contém essa ideia implementada, disponível com a opção 2 em <fluctuate>.
Não deixe de conferir também os outros projetos dele.
A ferramenta ShellcodeFluctuation aceita três parâmetros: o primeiro é o caminho para o shellcode e o segundo é o modificador da nossa funcionalidade.```
Usage: ShellcodeFluctuation.exe
:
-1 - Read shellcode but dont inject it. Run in an infinite loop.
0 - Inject the shellcode but don't hook kernel32!Sleep and don't encrypt anything
1 - Inject shellcode and start fluctuating its memory with standard PAGE_READWRITE.
2 - Inject shellcode and start fluctuating its memory with ORCA666's PAGE_NOACCESS.
### Moneta (aparentemente) Falso Positivo```
C:\> ShellcodeFluctuation.exe beacon64.bin -1
Então, primeiro vamos ver o que o scanner Moneta64 pensa sobre o processo que não faz nada suspeito e simplesmente recorre a executar um loop infinito:

Como podemos ver, há algum falso positivo (pelo menos é assim que o considero) supostamente detetando Mismatching PEB module / Phantom image.
Os limites de memória apontam para o próprio módulo ShellcodeFluctuate.exe e podem indicar que este módulo, embora sendo do tipo MEM_IMAGE, não está ligado ao PEB do processo - o que é incomum e parece bastante estranho.
A razão para este IOC não me é conhecida e não tentei compreendê-la melhor, mas não é algo com que devamos realmente nos preocupar.
Se alguém souber qual é a razão para esta deteção, ficaria muito curioso em saber! Por favor, entre em contacto.
C:> ShellcodeFluctuation.exe beacon64.bin 0
O segundo caso de uso apresenta IOCs de Memória de um Beacon operando dentro do nosso processo, que não utiliza nenhum tipo de `Artifact Kits` personalizados, `User-Defined Reflective Loaders` (como o meu [`ElusiveMice`](https://github.com/mgeeky/ElusiveMice)), nem quaisquer ações iniciais que estragariam nossos resultados.

Podemos ver que o `Moneta64` reconhece corretamente `Abnormal private executable memory` apontando para o local onde nosso shellcode reside.
Esse é um IOC de Memória realmente forte, expondo nosso shellcode para ser despejado e analisado por scanners automatizados. Não é legal.
### Beacon criptografado com proteções RW```
C:\> ShellcodeFluctuation.exe beacon64.bin 1
Agora o terceiro caso de uso, o mais interessante do ponto de vista desta implementação, é o Beacon flutuante.

Além do primeiro IOC, considerado um tanto falso positivo, vemos um novo apontando que a memória de kernel32.dll foi modificada.
No entanto, nenhum IOC Abnormal private executable memory desta vez. Nossa flutuação (criptografia/descriptografia repetida e alternância de proteções de memória) está ativa.
E, para registro, o pe-sieve também detecta PE implantado quando usado com a opção /data 3 (a menos que essa opção seja fornecida, nenhuma detecção será feita):

Minha suposição atual é que o PE-Sieve está captando as mesmas características que o Moneta (descritas abaixo em Modified code in kernel32.dll) - o fato de o módulo PE mapeado ter um Working set não vazio, sendo uma evidência clara de injeção de código de algum tipo. Isso é rotulado como Implanted PE / Implanted. Se for esse o caso, a conclusão é semelhante à observação do Moneta. Não acho que devamos nos importar muito com esse IOC em termos de detecção.
Atualmente, não pensei em opção melhor para interceptar a execução do shellcode no meio (agora falando do Cobalt Strike), além de hook em kernel32!Sleep. Portanto, estamos fadados a deixar esses tipos de IOC.
Mas ei, ainda assim nenhum dos bytes difere em comparação com o que está lá no sistema de arquivos (C:\Windows\System32\kernel32.dll) e nenhuma função está com hook. Qual é a ideia? 😉
C:> ShellcodeFluctuation.exe beacon64.bin 2

Isso fará com que o shellcode flutue entre páginas `RX` e `NA` de forma eficaz.
No momento, não tenho certeza dos benefícios de alternar para `PAGE_NOACCESS` em vez de `PAGE_READWRITE`.
### Código modificado em kernel32.dll
Então, e sobre esse IOC de `kernel32` modificado?
Agora, vamos tentar chegar ao fundo desse IOC e ver o que está acontecendo aqui.
Primeiramente, vamos despejar a região de memória mencionada - a seção `.text` (código) de `kernel32.dll`. Vamos usar o `ProcessHacker` para esse propósito, a fim de utilizar ferramentas públicas e estáveis conhecidas:

Despejamos a seção de código do kernel32 supostamente modificado e depois fazemos o mesmo para o kernel32 em execução no processo que não modificou essa área.
De posse dos dois despejos, podemos então compará-los byte a byte (usando meu [expdevBadChars](https://github.com/mgeeky/expdevBadChars)) para procurar por quaisquer inconsistências:

Apenas para ver que eles são idênticos. Claramente não há um único byte modificado em `kernel32.dll`, e a razão para isso é que estamos removendo o hook de `kernel32!Sleep` antes de chamá-la:
`main.cpp:31:````
HookTrampolineBuffers buffers = { 0 };
buffers.originalBytes = g_hookedSleep.sleepStub;
buffers.originalBytesSize = sizeof(g_hookedSleep.sleepStub);
//
// Unhook kernel32!Sleep to evade hooked Sleep IOC.
// We leverage the fact that the return address left on the stack will make the thread
// get back to our handler anyway.
//
fastTrampoline(false, (BYTE*)::Sleep, &MySleep, &buffers);
// Perform sleep emulating originally hooked functionality.
::Sleep(dwMilliseconds);
Então, o que está causando a ativação do IOC? Vamos inspecionar o Moneta mais de perto:

Entrando no Ioc.cpp do Moneta, pouco antes da linha 104, onde ele reporta o IOC de MODIFIED_CODE, podemos modificar um pouco o código para expor melhor o momento exato em que ele analisa o pool da kernel32.
Agora:
a = truekernel32 tem b = 0x1000 bytes privados. Como assim? Deveria haver 0 deles.a && b), o IOC é reportadoQuando o Image Loader do Windows mapeia um módulo DLL para o espaço de memória do processo, as páginas de memória subjacentes serão rotuladas como MEM_MAPPED ou MEM_IMAGE, dependendo do cenário.
Sempre que modificamos até mesmo um único byte da alocação MEM_MAPPED/MEM_IMAGE, o sistema separará uma única página de memória (assumindo que modificamos menos de PAGE_SIZE bytes e não cruzamos o limite da página) para indicar um fragmento que não mapeia de volta para a imagem original.
Essa observação é então utilizada como IOC — uma imagem não deveria ter alocações MEM_PRIVATE dentro de sua região de memória (no seu interior), pois isso indicaria que alguns bytes foram modificados dentro daquela região. O Moneta está detectando corretamente a modificação de código, mesmo que os bytes fossem correspondentes aos bytes do módulo original no momento da comparação.
Para uma explicação abrangente de como o Moneta, a implementação de injeção de processo e os IOCs relacionados funcionam internamente, leia os seguintes artigos de altíssima qualidade de Forrest Orr:
Essa é uma pesquisa e documentação verdadeiramente excepcional feita por Forrest, ótimo trabalho, parceiro!
Especialmente o segundo artigo descreve a justificativa para essa detecção, conforme lemos o que Forrest nos ensina:
No caso de o módulo ter sido legitimamente carregado e adicionado ao PEB, o implante de shellcode ainda assim teria sido detectado devido aos 0x1000 bytes (1 página) de memória mapeada privadamente no espaço de endereçamento e recuperados pelo Moneta ao consultar seu working set — resultando em um IOC de código modificado, como visto acima.
Para resumir, estamos deixando um IOC para trás, mas deveríamos nos preocupar com isso? Mesmo que haja um IOC, não há bytes roubados visíveis, portanto não há referência imediata apontando para nosso shellcode ou distinguindo a técnica do nosso shellcode de outras.
Em resumo — não deveríamos nos preocupar realmente com esse IOC. :-)
Alguém pode dizer que esta implementação está longe de ser perfeita porque deixa algo, ainda existem IOCs e os produtos comerciais mostram que não têm características semelhantes.
Quando esse argumento vem à tona, preciso lembrar que os frameworks comerciais têm controle total sobre o código-fonte de seus implantes e carregadores de shellcode e, assim, podem integrar perfeitamente um ao outro para evitar a necessidade de fazer hook e de contornar o próprio shellcode. Aqui, precisamos fazer hook em kernel32!Sleep para interceptar a execução do Beacon do Cobalt Strike antes que ele adormeça, a fim de iniciar nossa manutenção. Se existisse um mecanismo melhor para entrarmos em ação sem precisar fazer hook no sleep — isso seria perfeito.
No entanto, existe o conceito de Sleep Mask introduzido no Cobalt Strike; as restrições de tamanho, limitadas a centenas de bytes, nos tornam totalmente incapazes de introduzir essa lógica na própria mask (caso contrário, poderíamos também não fazer hook no Sleep, sem deixar IOCs, assim como os produtos comerciais fazem).
Outro argumento poderia ser que os frameworks comerciais integram esse tipo de lógica em seus Reflective Loaders e aqui, em vez disso, deixamos isso no harness EXE. Isso é verdade, mas a razão para essa decisão é dupla:
Preciso ter muito cuidado ao divulgar esse tipo de tecnologia para evitar o risco de ajudar a armar criminosos do mundo real com uma implementação que voltará para nos assombrar como outro Petya. Dessa forma, decidi omitir alguns dos detalhes mais pesados que uso em minhas ferramentas profissionais utilizadas em exercícios comerciais contratados de Simulação de Adversário. Entregar a semente, espero, será recebido por profissionais da comunidade capazes de desenvolver o conceito em suas próprias ferramentas, assumindo que tenham as habilidades apropriadas.
Eu preferiria muito mover toda essa lógica para o User-Defined Reflective Loader do Cobalt Strike, facilitando para grupos de Red Team maiores chances em sua fase de entrega. Mas, primeiramente, veja o ponto (1); em segundo lugar, essa tecnologia está atualmente limitada a 5KBs de tamanho para seus RDLLs, o que me torna completamente incapaz de implementá-la também ali. Para aqueles de nós que constroem C2s e implantes personalizados para compromissos internos de Simulação de Adversário — agora eles receberam uma implementação de exemplo que certamente os ajudará a aprimorar suas ferramentas adequadamente.
Observe o código e sua implementação, entenda o conceito e reimplemente-o em seus próprios Carregadores de Shellcode que você utiliza para conduzir seus compromissos de Red Team. Esta é mais uma técnica de evasão avançada em memória que aumenta as chances de sua equipe não ser pega por Antivírus, EDRs e Analistas de Malware que examinam seus implantes.
Ao desenvolver seu carregador de shellcode avançado, você também pode querer implementar:
BeaconEyeMEM_PRIVATE referenciadas por essas threads)Caso de uso:``` Usage: ShellcodeFluctuation.exe : -1 - Read shellcode but dont inject it. Run in an infinite loop. 0 - Inject the shellcode but don't hook kernel32!Sleep and don't encrypt anything 1 - Inject shellcode and start fluctuating its memory with standard PAGE_READWRITE. 2 - Inject shellcode and start fluctuating its memory with ORCA666's PAGE_NOACCESS.
Onde:
- `<shellcode>` é um caminho para o arquivo de shellcode
- `<fluctuate>` como descrito acima, aceita `-1`, `0` ou `1`
Exemplo de execução que falsifica a pilha de chamadas da thread do beacon:```
C:\> ShellcodeFluctuation.exe ..\..\tests\beacon64.bin 1
[.] Reading shellcode bytes...
[.] Hooking kernel32!Sleep...
[.] Injecting shellcode...
[+] Shellcode is now running. PID = 9456
[+] Fluctuation initialized.
Shellcode resides at 0x000002210C091000 and occupies 176128 bytes. XOR32 key: 0x1e602f0d
[>] Flipped to RW. Encoding...
===> MySleep(5000)
[.] Decoding...
[>] Flipped to RX.
[>] Flipped to RW. Encoding...
===> MySleep(5000)
Se você planeja adicionar essa funcionalidade aos seus próprios loaders / ferramentas de shellcode, certifique-se de EVITAR remover os hooks de kernel32.dll.
Uma tentativa de remover os hooks de kernel32 restaurará a funcionalidade original do Sleep, impedindo que nosso callback seja chamado.
Se nosso callback não for chamado, a thread não conseguirá falsificar sua própria pilha de chamadas sozinha.
Se é isso que você deseja, então talvez seja necessário executar outra thread, uma thread de vigilância (watchdog), garantindo que a thread do Beacon seja falsificada sempre que dormir.
Se você estiver usando o Cobalt Strike e um BOF unhook-bof de Raphael's Mudge, confira meu Pull Request, que adiciona um parâmetro opcional ao BOF especificando as bibliotecas que não devem ter os hooks removidos.
Dessa forma, você pode manter seus hooks em kernel32:``` beacon> unhook kernel32 [*] Running unhook. Will skip these modules: wmp.dll, kernel32.dll [+] host called home, sent: 9475 bytes [+] received output: ntdll.dll <.text> Unhook is done.
[Modificado `unhook-bof` com opção de ignorar módulos especificados](https://github.com/mgeeky/unhook-bof)
---
## Considerações finais
Esta PoC foi projetada para funcionar com shellcodes do Beacon do Cobalt Strike. Sabe-se que o Beacon faz chamadas a `kernel32!Sleep` para aguardar novas instruções do seu C2.
Este loader aproveita esse fato ao interceptar `Sleep` para realizar sua manutenção interna.
Esta implementação pode não funcionar com outros shellcodes do mercado (como _Meterpreter_) se eles não usarem `Sleep` para pausar.
Como isso é meramente uma _Prova de Conceito_ demonstrando a técnica, não pretendo adicionar suporte a nenhum outro framework de C2.
Quando você entender o conceito, certamente será capaz de traduzi-lo para os requisitos do seu shellcode e adaptar a solução a seu favor.
Por favor, não abra issues no Github relacionadas a "este código não funciona com shellcode XYZ", elas serão fechadas imediatamente.
---
### ☕ Mostre Apoio ☕
Este e outros projetos são resultado de noites sem dormir e **muito trabalho duro**. Se você gosta do que faço e aprecia que eu sempre retribuo à comunidade,
[Considere me comprar um café](https://github.com/sponsors/mgeeky) _(ou melhor, uma cerveja)_ apenas para agradecer! 💪
---
## Autor```
Mariusz Banach / mgeeky, 21
<mb [at] binary-offensive.com>
(https://github.com/mgeeky)
kernel32!SleepRX, e o shellcode é retomado.