8.5 KiB
Inyección de Aplicaciones .Net en macOS
Aprende hacking en AWS desde cero hasta experto con htARTE (HackTricks AWS Red Team Expert)!
Otras formas de apoyar a HackTricks:
- Si deseas ver tu empresa anunciada en HackTricks o descargar HackTricks en PDF ¡Consulta los PLANES DE SUSCRIPCIÓN!
- Obtén merchandising oficial de PEASS & HackTricks
- Descubre La Familia PEASS, nuestra colección exclusiva de NFTs
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @carlospolopm.
- Comparte tus trucos de hacking enviando PRs a los repositorios de HackTricks y HackTricks Cloud en GitHub.
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:
- Si deseas ver tu empresa anunciada en HackTricks o descargar HackTricks en PDF ¡Consulta los PLANES DE SUSCRIPCIÓN!
- Obtén el merchandising oficial de PEASS & HackTricks
- Descubre The PEASS Family, nuestra colección exclusiva de NFTs
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @carlospolopm.
- Comparte tus trucos de hacking enviando PRs a los repositorios de HackTricks y HackTricks Cloud en GitHub.