macOS .Net Applications Injection
Reading time: 5 minutes
tip
Impara e pratica l'Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica l'Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos di github.
Questo è un riepilogo del post https://blog.xpnsec.com/macos-injection-via-third-party-frameworks/. Controllalo per ulteriori dettagli!
.NET Core Debugging
Stabilire una sessione di debug
La gestione della comunicazione tra debugger e debuggee in .NET è gestita da dbgtransportsession.cpp. Questo componente imposta due pipe nominate per ogni processo .NET come visto in dbgtransportsession.cpp#L127, che sono iniziate tramite twowaypipe.cpp#L27. Queste pipe sono suffisse con -in
e -out
.
Visitando il $TMPDIR
dell'utente, si possono trovare FIFO di debug disponibili per il debug delle applicazioni .Net.
DbgTransportSession::TransportWorker è responsabile della gestione della comunicazione da un debugger. Per avviare una nuova sessione di debug, un debugger deve inviare un messaggio tramite la pipe out
che inizia con una struct MessageHeader
, dettagliata nel codice sorgente di .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];
}
Per richiedere una nuova sessione, questa struct viene popolata come segue, impostando il tipo di messaggio su MT_SessionRequest
e la versione del protocollo sulla versione attuale:
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);
Questo header viene quindi inviato al target utilizzando la syscall write
, seguito dalla struct sessionRequestData
contenente un GUID per la sessione:
write(wr, &sSendHeader, sizeof(MessageHeader));
memset(&sDataBlock.m_sSessionID, 9, sizeof(SessionRequestData));
write(wr, &sDataBlock, sizeof(SessionRequestData));
Un'operazione di lettura sul pipe out
conferma il successo o il fallimento dell'instaurazione della sessione di debug:
read(rd, &sReceiveHeader, sizeof(MessageHeader));
Lettura della memoria
Una volta stabilita una sessione di debug, la memoria può essere letta utilizzando il tipo di messaggio MT_ReadMemory
. La funzione readMemory è dettagliata, eseguendo i passaggi necessari per inviare una richiesta di lettura e recuperare la risposta:
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;
}
La prova di concetto completa (POC) è disponibile qui.
Scrittura della Memoria
Allo stesso modo, la memoria può essere scritta utilizzando la funzione writeMemory
. Il processo prevede di impostare il tipo di messaggio su MT_WriteMemory
, specificare l'indirizzo e la lunghezza dei dati, e poi inviare i dati:
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;
}
Il POC associato è disponibile qui.
Esecuzione di Codice .NET Core
Per eseguire codice, è necessario identificare una regione di memoria con permessi rwx, cosa che può essere fatta usando vmmap -pages:
vmmap -pages [pid]
vmmap -pages 35829 | grep "rwx/rwx"
Trovare un luogo per sovrascrivere un puntatore di funzione è necessario, e in .NET Core, questo può essere fatto mirato alla Dynamic Function Table (DFT). Questa tabella, dettagliata in jithelpers.h
, è utilizzata dal runtime per le funzioni helper di compilazione JIT.
Per i sistemi x64, la ricerca di firme può essere utilizzata per trovare un riferimento al simbolo _hlpDynamicFuncTable
in libcorclr.dll
.
La funzione del debugger MT_GetDCB
fornisce informazioni utili, incluso l'indirizzo di una funzione helper, m_helperRemoteStartAddr
, che indica la posizione di libcorclr.dll
nella memoria del processo. Questo indirizzo viene poi utilizzato per avviare una ricerca per la DFT e sovrascrivere un puntatore di funzione con l'indirizzo del shellcode.
Il codice POC completo per l'iniezione in PowerShell è accessibile qui.
Riferimenti
tip
Impara e pratica l'Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica l'Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos di github.