
Implementação de Beacon Object File para bypass de UAC por desserialização do Event Viewer
Esta é uma implementação em Beacon Object File do bypass de UAC por desserialização do Event Viewer, descoberto por @orange_8361 e com o POC montado por CsEnox.

Testado em x64 Win10/Win11
Este bypass de UAC realiza as seguintes ações que devem ser consideradas em relação ao OPSEC:
-1. Escreve um arquivo binário em %LOCALAPPDATA%\Microsoft\Event Viewer\RecentViews
-2. Chama ShellExecute() para iniciar mmc.exe / Event Viewer
--A. O Event Viewer será aberto na GUI!
--B. O Event Viewer chamará cmd.exe
---a. Cmd.exe chamará Taskkill para fechar mmc.exe
---b. Cmd.exe imprimirá uma mensagem de erro na janela do console que às vezes permanece aberta, com o intuito de enganar o usuário para que acredite que está tudo normal
---c. Cmd.exe executará quaisquer outros comandos fornecidos pelo usuário
-3. O arquivo binário é deletado de %LOCALAPPDATA%\Microsoft\Event Viewer\RecentViews
Seguindo a demonstração original e o POC do CsEnox, o ysoserial foi usado para criar um payload inicial:

ysoserial.exe -o raw -f BinaryFormatter -g TextFormattingRunProperties -c "taskkill /f /im mmc.exe >nul && echo Windows has Recovered from an Unexpected Error. You may Close this Window. &&" > cmd.bin
Este bypass de UAC funciona invocando mmc.exe, que abre na tela e precisa ser fechado. Infelizmente, não encontrei uma forma de iniciar mmc.exe sem abrir a GUI; pesquisei CreateProcess() e a flag CREATE_NO_WINDOW, porém CreateProcess() não funciona bem com UAC. Postagens no StackOverflow sobre este tópico me direcionaram para a API ShellExecuteA(), que possui a flag SW_HIDE (e outras), mas isso não se mostrou frutífero. O aparecimento do mmc.exe na tela parece inevitável.
Ysoserial chama cmd.exe /c, então isso é usado para chamar taskkill a fim de fechar mmc.exe. && é usado para encadear comandos, de modo que a ação desejada para ser executada como Administrador possa ser realizada. Durante os testes, descobri que em alguns casos essa ação mantinha o prompt cmd.exe gerado pelo mmc.exe aberto; isso ocorre ao chamar payloads do tipo Local Injector (normal), como o payload executável padrão do Cobalt Strike. Para lidar com isso, redirecionei a saída do comando taskkill (que antes era impressa no cmd.exe mantido aberto) para /dev/null, e exibi na tela uma mensagem sobre um erro que foi tratado, na tentativa de fazer com que esse comportamento suspeito não alarme um usuário desavisado.
Payloads que criam um processo, injetam nele e depois saem não mantêm a janela cmd.exe aberta e, portanto, a mensagem é desnecessária. Da mesma forma, comandos que executam e saem (ex.: net user Administrator newpassword) não mantêm a janela aberta.
Você pode executar comandos codificados em PowerShell através deste BOF:



O comando taskkill e a mensagem para o usuário estão incorporados no comando ysoserial. Se você quisesse alterar esse comportamento, precisaria executar seu próprio comando ysoserial e seguir a demonstração abaixo de como o código foi criado.
Usando uma ferramenta personalizada, o arquivo binário foi ingerido e impresso como hex:

Copiando isso para o bloco de notas e pesquisando por '&' em hex localiza os bytes desejados (note que existem vários conjuntos destes, precisamos do último):

O payload é dividido em duas partes imediatamente após esses bytes:

O comprimento de cada parte é calculado e anotado para uso posterior.
É aqui que as coisas ficam complicadas.
Para ser sincero, não sei muito sobre desserialização ou ysoserial, mas descobri que existem um conjunto de bytes no payload que representam o comprimento da parte "importante" do payload, incluindo o comando definido pelo usuário. Isso pode ser visto na captura de tela a seguir, de um dump hexadecimal lado a lado de dois payloads diferentes criados pelo ysoserial, cada um contendo um comando diferente a ser executado:

Por razões que não posso explicar, esses bytes refletem o comprimento do payload em base-128; o 0x05 é multiplicado por 128 para resultar em 640; isso é somado ao valor do outro byte (0xb0 ou 0xd4) menos 128; descobri que quando o segundo byte (0x05) ultrapassa (0x06), o primeiro byte começa novamente em 0x80, ao contrário do que se esperaria (0x00).
O payload usado nesta ferramenta se parece com isto:

Convertendo todos os valores para decimal, a matemática fica assim:
(6 x 128) + (180-128) = 820
O tamanho do arquivo é 1042; em testes, descobri que (com este gadget do ysoserial, pelo menos) há sempre uma diferença de 222 bytes entre o tamanho do arquivo e o comprimento refletido nesse conjunto de bytes. Esses 222 bytes parecem ser o "cabeçalho do payload".
Ambas as partes do payload são copiadas para main.c, onde o comprimento original de 820 também é definido como uma variável. O comprimento do comando do usuário é calculado, e esse valor é adicionado ao 820 original (além de um byte extra para um espaço que deve ser inserido) a fim de calcular o valor final que precisa ser representado nesse conjunto de bytes. O segundo byte é calculado dividindo LenBytes por 128, e o primeiro byte é calculado encontrando o resto de LenBytes e adicionando 128 (para acomodar a forma como ele começa em 0x80).

Uma vez que esses bytes são definidos, eles são copiados para o buffer Before no local apropriado para definir o comprimento do payload para o valor correto, dado o conteúdo. O buffer final é então montado:

O payload finalizado é então escrito no disco e mmc.exe é chamado para executá-lo. Depois disso, o payload é limpo.
x86_64-w64-mingw32-gcc -o EventViewerUAC.x64.o -Os -c main.c -DBOF
Ysoserial oferece uma opção --rawcmd onde você pode invocar um executável diferente do cmd.exe, porém você não pode fornecer argumentos para ele. Isso abre a porta para invocar diretamente seu payload sem chamar cmd.exe e, assim, não deixar a janela do console aberta; no entanto, neste caso, não haveria nada para fechar mmc.exe. Alguém poderia escrever seus payloads para determinar o processo pai do payload e, se for mmc.exe, matar mmc.exe para limpar após este bypass de UAC. Isso é muito possível, porém não tive o trabalho de fazer isso, pois este é um dos muitos bypasses de UAC existentes, e nem todo mundo está criando seus próprios payloads personalizados dessa forma. Posso incorporar isso em meus runners pessoais.