8.5 KiB
Iniezione di applicazioni .Net su macOS
Impara l'hacking di AWS da zero a esperto con htARTE (HackTricks AWS Red Team Expert)!
Altri modi per supportare HackTricks:
- Se vuoi vedere la tua azienda pubblicizzata su HackTricks o scaricare HackTricks in PDF Controlla i PACCHETTI DI ABBONAMENTO!
- Ottieni il merchandising ufficiale di PEASS & HackTricks
- Scopri The PEASS Family, la nostra collezione di NFT esclusivi
- Unisciti al 💬 gruppo Discord o al gruppo Telegram o seguici su Twitter 🐦 @carlospolopm.
- Condividi i tuoi trucchi di hacking inviando PR a HackTricks e HackTricks Cloud github repos.
Questo è un riassunto del post https://blog.xpnsec.com/macos-injection-via-third-party-frameworks/. Consultalo per ulteriori dettagli!
Debugging di .NET Core
Avvio di una sessione di debug
La gestione della comunicazione tra il debugger e il debuggee in .NET è gestita da dbgtransportsession.cpp. Questo componente configura due named pipe per ogni processo .NET, come si può vedere in dbgtransportsession.cpp#L127, che vengono iniziate tramite twowaypipe.cpp#L27. Queste pipe sono suffisse con -in
e -out
.
Visitando la directory $TMPDIR
dell'utente, è possibile trovare FIFO di debug disponibili per le applicazioni .Net.
DbgTransportSession::TransportWorker è responsabile della gestione della comunicazione da parte di un debugger. Per avviare una nuova sessione di debug, un debugger deve inviare un messaggio tramite la pipe out
che inizia con una struttura 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 struttura viene popolata nel seguente modo, impostando il tipo di messaggio su MT_SessionRequest
e la versione del protocollo alla versione corrente:
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);
Quest'intestazione viene quindi inviata al target utilizzando la chiamata di sistema write
, seguita dalla struttura sessionRequestData
che contiene 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 tubo 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 (POC) completa è disponibile qui.
Scrittura in 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 quindi 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 del codice .NET Core
Per eseguire il codice, è necessario identificare una regione di memoria con le autorizzazioni rwx, che può essere fatto utilizzando vmmap -pages:
vmmap -pages [pid]
vmmap -pages 35829 | grep "rwx/rwx"
È necessario individuare un punto in cui sovrascrivere un puntatore a una funzione e, in .NET Core, ciò può essere fatto mirando alla Dynamic Function Table (DFT). Questa tabella, descritta in jithelpers.h
, viene utilizzata dal runtime per le funzioni helper di compilazione JIT.
Per i sistemi x64, è possibile utilizzare la ricerca della firma per trovare un riferimento al simbolo _hlpDynamicFuncTable
in libcorclr.dll
.
La funzione di debug MT_GetDCB
fornisce informazioni utili, tra cui l'indirizzo di una funzione helper, m_helperRemoteStartAddr
, che indica la posizione di libcorclr.dll
nella memoria del processo. Questo indirizzo viene quindi utilizzato per avviare una ricerca della DFT e sovrascrivere un puntatore a una funzione con l'indirizzo del codice shell.
Il codice POC completo per l'iniezione in PowerShell è accessibile qui.
Riferimenti
Impara l'hacking di AWS da zero a esperto con htARTE (HackTricks AWS Red Team Expert)!
Altri modi per supportare HackTricks:
- Se vuoi vedere la tua azienda pubblicizzata in HackTricks o scaricare HackTricks in PDF controlla i PACCHETTI DI ABBONAMENTO!
- Ottieni il merchandising ufficiale di PEASS & HackTricks
- Scopri The PEASS Family, la nostra collezione di esclusive NFT
- Unisciti al 💬 gruppo Discord o al gruppo Telegram o seguici su Twitter 🐦 @carlospolopm.
- Condividi i tuoi trucchi di hacking inviando PR ai repository github di HackTricks e HackTricks Cloud.