macOS .Net-Anwendungen Injection
Reading time: 5 minutes
tip
Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.
Dies ist eine Zusammenfassung des Beitrags https://blog.xpnsec.com/macos-injection-via-third-party-frameworks/. Überprüfen Sie ihn für weitere Details!
.NET Core Debugging
Einrichten einer Debugging-Sitzung
Die Handhabung der Kommunikation zwischen Debugger und Debuggee in .NET wird von dbgtransportsession.cpp verwaltet. Diese Komponente richtet zwei benannte Pipes pro .NET-Prozess ein, wie in dbgtransportsession.cpp#L127 zu sehen ist, die über twowaypipe.cpp#L27 initiiert werden. Diese Pipes sind mit -in
und -out
suffixiert.
Durch den Besuch des $TMPDIR
des Benutzers kann man Debugging-FIFOs finden, die für das Debuggen von .Net-Anwendungen verfügbar sind.
DbgTransportSession::TransportWorker ist verantwortlich für die Verwaltung der Kommunikation von einem Debugger. Um eine neue Debugging-Sitzung zu initiieren, muss ein Debugger eine Nachricht über die out
-Pipe senden, die mit einer MessageHeader
-Struktur beginnt, die im .NET-Quellcode detailliert beschrieben ist:
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];
}
Um eine neue Sitzung anzufordern, wird diese Struktur wie folgt ausgefüllt, wobei der Nachrichtentyp auf MT_SessionRequest
und die Protokollversion auf die aktuelle Version gesetzt wird:
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);
Dieser Header wird dann über den write
-Syscall an das Ziel gesendet, gefolgt von der sessionRequestData
-Struktur, die eine GUID für die Sitzung enthält:
write(wr, &sSendHeader, sizeof(MessageHeader));
memset(&sDataBlock.m_sSessionID, 9, sizeof(SessionRequestData));
write(wr, &sDataBlock, sizeof(SessionRequestData));
Ein Lesevorgang auf dem out
-Pipe bestätigt den Erfolg oder Misserfolg der Einrichtung der Debugging-Sitzung:
read(rd, &sReceiveHeader, sizeof(MessageHeader));
Lesen des Speichers
Sobald eine Debugging-Sitzung eingerichtet ist, kann der Speicher mit dem MT_ReadMemory
Nachrichtentyp gelesen werden. Die Funktion readMemory ist detailliert und führt die notwendigen Schritte aus, um eine Leseanforderung zu senden und die Antwort abzurufen:
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;
}
Der vollständige Proof of Concept (POC) ist hier verfügbar.
Schreiben in den Speicher
Ähnlich kann der Speicher mit der Funktion writeMemory
beschrieben werden. Der Prozess umfasst das Setzen des Nachrichtentyps auf MT_WriteMemory
, das Spezifizieren der Adresse und der Länge der Daten und das anschließende Senden der Daten:
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;
}
Der zugehörige POC ist hier verfügbar.
.NET Core Codeausführung
Um Code auszuführen, muss man einen Speicherbereich mit rwx-Berechtigungen identifizieren, was mit vmmap -pages: durchgeführt werden kann.
vmmap -pages [pid]
vmmap -pages 35829 | grep "rwx/rwx"
Einen Ort zu finden, um einen Funktionszeiger zu überschreiben, ist notwendig, und in .NET Core kann dies durch das Anvisieren der Dynamic Function Table (DFT) erfolgen. Diese Tabelle, die in jithelpers.h
detailliert beschrieben ist, wird vom Laufzeitumgebung für JIT-Kompilierungs-Hilfsfunktionen verwendet.
Für x64-Systeme kann die Signaturjagd verwendet werden, um einen Verweis auf das Symbol _hlpDynamicFuncTable
in libcorclr.dll
zu finden.
Die Debuggerfunktion MT_GetDCB
liefert nützliche Informationen, einschließlich der Adresse einer Hilfsfunktion, m_helperRemoteStartAddr
, die den Standort von libcorclr.dll
im Prozessspeicher angibt. Diese Adresse wird dann verwendet, um eine Suche nach der DFT zu starten und einen Funktionszeiger mit der Adresse des Shellcodes zu überschreiben.
Der vollständige POC-Code für die Injektion in PowerShell ist hier zugänglich.
References
tip
Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.