hacktricks/macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-.net-applications-injection.md

8.3 KiB

macOS .Net Applications Injection

{% hint style="success" %} Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks
{% endhint %}

Este é um resumo do post https://blog.xpnsec.com/macos-injection-via-third-party-frameworks/. Confira para mais detalhes!

.NET Core Debugging

Estabelecendo uma Sessão de Depuração

O manuseio da comunicação entre o depurador e o depurado no .NET é gerenciado por dbgtransportsession.cpp. Este componente configura dois pipes nomeados por processo .NET, como visto em dbgtransportsession.cpp#L127, que são iniciados via twowaypipe.cpp#L27. Esses pipes são sufixados com -in e -out.

Ao visitar o $TMPDIR do usuário, pode-se encontrar FIFOs de depuração disponíveis para depurar aplicações .Net.

DbgTransportSession::TransportWorker é responsável por gerenciar a comunicação de um depurador. Para iniciar uma nova sessão de depuração, um depurador deve enviar uma mensagem via o pipe out começando com uma estrutura MessageHeader, detalhada no código-fonte do .NET:

struct MessageHeader {
MessageType   m_eType;        // Message type
DWORD         m_cbDataBlock;  // Size of following data block (can be zero)
DWORD         m_dwId;         // Message ID from sender
DWORD         m_dwReplyId;    // Reply-to Message ID
DWORD         m_dwLastSeenId; // Last seen Message ID by sender
DWORD         m_dwReserved;   // Reserved for future (initialize to zero)
union {
struct {
DWORD         m_dwMajorVersion;   // Requested/accepted protocol version
DWORD         m_dwMinorVersion;
} VersionInfo;
...
} TypeSpecificData;
BYTE          m_sMustBeZero[8];
}

Para solicitar uma nova sessão, esta struct é preenchida da seguinte forma, definindo o tipo de mensagem como MT_SessionRequest e a versão do protocolo como a versão atual:

static const DWORD kCurrentMajorVersion = 2;
static const DWORD kCurrentMinorVersion = 0;

// Configure the message type and version
sSendHeader.m_eType = MT_SessionRequest;
sSendHeader.TypeSpecificData.VersionInfo.m_dwMajorVersion = kCurrentMajorVersion;
sSendHeader.TypeSpecificData.VersionInfo.m_dwMinorVersion = kCurrentMinorVersion;
sSendHeader.m_cbDataBlock = sizeof(SessionRequestData);

Este cabeçalho é então enviado para o alvo usando a chamada de sistema write, seguido pela estrutura sessionRequestData contendo um GUID para a sessão:

write(wr, &sSendHeader, sizeof(MessageHeader));
memset(&sDataBlock.m_sSessionID, 9, sizeof(SessionRequestData));
write(wr, &sDataBlock, sizeof(SessionRequestData));

Uma operação de leitura no pipe out confirma o sucesso ou falha do estabelecimento da sessão de depuração:

read(rd, &sReceiveHeader, sizeof(MessageHeader));

Leitura de Memória

Uma vez que uma sessão de depuração é estabelecida, a memória pode ser lida usando o tipo de mensagem MT_ReadMemory. A função readMemory é detalhada, realizando os passos necessários para enviar um pedido de leitura e recuperar a resposta:

bool readMemory(void *addr, int len, unsigned char **output) {
// Allocation and initialization
...
// Write header and read response
...
// Read the memory from the debuggee
...
return true;
}

A prova de conceito (POC) completa está disponível aqui.

Escrevendo na Memória

Da mesma forma, a memória pode ser escrita usando a função writeMemory. O processo envolve definir o tipo de mensagem como MT_WriteMemory, especificar o endereço e o comprimento dos dados e, em seguida, enviar os dados:

bool writeMemory(void *addr, int len, unsigned char *input) {
// Increment IDs, set message type, and specify memory location
...
// Write header and data, then read the response
...
// Confirm memory write was successful
...
return true;
}

O POC associado está disponível aqui.

Execução de Código .NET Core

Para executar código, é necessário identificar uma região de memória com permissões rwx, o que pode ser feito usando vmmap -pages:

vmmap -pages [pid]
vmmap -pages 35829 | grep "rwx/rwx"

Localizar um lugar para sobrescrever um ponteiro de função é necessário, e no .NET Core, isso pode ser feito direcionando-se para a Dynamic Function Table (DFT). Esta tabela, detalhada em jithelpers.h, é usada pelo runtime para funções auxiliares de compilação JIT.

Para sistemas x64, a busca por assinatura pode ser usada para encontrar uma referência ao símbolo _hlpDynamicFuncTable em libcorclr.dll.

A função de depuração MT_GetDCB fornece informações úteis, incluindo o endereço de uma função auxiliar, m_helperRemoteStartAddr, indicando a localização de libcorclr.dll na memória do processo. Este endereço é então usado para iniciar uma busca pela DFT e sobrescrever um ponteiro de função com o endereço do shellcode.

O código completo do POC para injeção no PowerShell está acessível aqui.

Referências

{% hint style="success" %} Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks
{% endhint %}