macOS .Net Applications Injection

Aprende hacking en AWS desde cero hasta experto con htARTE (HackTricks AWS Red Team Expert)!

Otras formas de apoyar a HackTricks:

Este es un resumen del post https://blog.xpnsec.com/macos-injection-via-third-party-frameworks/. ¡Consultalo para más detalles!

Depuración de .NET Core

Estableciendo una Sesión de Depuración

El manejo de la comunicación entre el depurador y el depurado en .NET es gestionado por dbgtransportsession.cpp. Este componente establece dos named pipes por proceso .NET como se ve en dbgtransportsession.cpp#L127, que son iniciados a través de twowaypipe.cpp#L27. Estos pipes tienen los sufijos -in y -out.

Al visitar el directorio $TMPDIR del usuario, se pueden encontrar FIFOs de depuración disponibles para depurar aplicaciones .Net.

DbgTransportSession::TransportWorker es responsable de gestionar la comunicación desde un depurador. Para iniciar una nueva sesión de depuración, un depurador debe enviar un mensaje a través del pipe out comenzando con una estructura MessageHeader, detallada en el código fuente de .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 una nueva sesión, esta estructura se completa de la siguiente manera, estableciendo el tipo de mensaje en MT_SessionRequest y la versión del protocolo en la versión actual:

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 encabezado se envía al objetivo utilizando la llamada al sistema write, seguido por la estructura sessionRequestData que contiene un GUID para la sesión:

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

Una operación de lectura en la tubería out confirma el éxito o fracaso del establecimiento de la sesión de depuración:

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

Leyendo la memoria

Una vez que se establece una sesión de depuración, la memoria se puede leer utilizando el tipo de mensaje MT_ReadMemory. La función readMemory está detallada, realizando los pasos necesarios para enviar una solicitud de lectura y recuperar la respuesta:

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;
}

El concepto de prueba completo (POC) está disponible aquí.

Escribiendo en la memoria

De manera similar, la memoria puede ser escrita utilizando la función writeMemory. El proceso implica establecer el tipo de mensaje en MT_WriteMemory, especificar la dirección y longitud de los datos, y luego enviar los datos:

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;
}

El POC asociado está disponible aquí.

Ejecución de código en .NET Core

Para ejecutar código, es necesario identificar una región de memoria con permisos rwx, lo cual se puede hacer utilizando vmmap -pages:

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

Encontrar un lugar para sobrescribir un puntero de función es necesario, y en .NET Core, esto se puede hacer apuntando a la Tabla de Funciones Dinámicas (DFT). Esta tabla, detallada en jithelpers.h, es utilizada por el tiempo de ejecución para funciones auxiliares de compilación JIT.

Para sistemas x64, la caza de firmas se puede utilizar para encontrar una referencia al símbolo _hlpDynamicFuncTable en libcorclr.dll.

La función de depuración MT_GetDCB proporciona información útil, incluida la dirección de una función auxiliar, m_helperRemoteStartAddr, que indica la ubicación de libcorclr.dll en la memoria del proceso. Esta dirección se utiliza luego para iniciar la búsqueda de la DFT y sobrescribir un puntero de función con la dirección del shellcode.

El código POC completo para la inyección en PowerShell es accesible aquí.

Referencias

Aprende hacking en AWS desde cero hasta experto con htARTE (HackTricks AWS Red Team Expert)!

Otras formas de apoyar a HackTricks:

Última actualización