macOS .Net Applications Injection

Lernen Sie AWS-Hacking von Grund auf mit htARTE (HackTricks AWS Red Team Expert)!

Andere Möglichkeiten, HackTricks zu unterstützen:

  • Wenn Sie Ihr Unternehmen in HackTricks bewerben möchten oder HackTricks als PDF herunterladen möchten, überprüfen Sie die ABONNEMENTPLÄNE!

  • Holen Sie sich das offizielle PEASS & HackTricks-Merchandise

  • Entdecken Sie die PEASS-Familie, unsere Sammlung exklusiver NFTs

  • Treten Sie der Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter @carlospolopm.

  • Teilen Sie Ihre Hacking-Tricks, indem Sie PRs an die HackTricks- und HackTricks Cloud-GitHub-Repositories 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 Kommunikation zwischen Debugger und Debuggee in .NET wird von dbgtransportsession.cpp verwaltet. Dieses Komponente richtet pro .NET-Prozess zwei benannte Pipes ein, wie in dbgtransportsession.cpp#L127 zu sehen ist, die über twowaypipe.cpp#L27 initiiert werden. Diese Pipes sind mit -in und -out suffixiert.

Wenn man das Verzeichnis $TMPDIR des Benutzers besucht, findet man Debugging-FIFOs, die für das Debuggen von .Net-Anwendungen verfügbar sind.

DbgTransportSession::TransportWorker ist für die Verwaltung der Kommunikation von einem Debugger verantwortlich. Um eine neue Debugging-Sitzung zu starten, 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 das 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));

Eine Leseoperation auf der out-Pipe bestätigt den Erfolg oder Misserfolg des Debugging-Sitzungsaufbaus:

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

Lesen des Speichers

Sobald eine Debugging-Sitzung hergestellt ist, kann der Speicher mithilfe des MT_ReadMemory-Nachrichtentyps gelesen werden. Die Funktion readMemory ist detailliert beschrieben und führt die erforderlichen Schritte aus, um eine Leseanfrage 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 von Speicher

Ebenso kann Speicher mithilfe der Funktion writeMemory geschrieben werden. Der Prozess besteht darin, den Nachrichtentyp auf MT_WriteMemory festzulegen, die Adresse und Länge der Daten anzugeben und dann die Daten zu senden:

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 dazugehörige POC ist hier verfügbar.

Ausführung von .NET Core-Code

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"

Um eine Stelle zum Überschreiben eines Funktionszeigers zu finden, ist es notwendig, das Dynamic Function Table (DFT) in .NET Core anzuzielen. Diese Tabelle, die in jithelpers.h detailliert beschrieben ist, wird vom Laufzeitsystem für JIT-Kompilierungshilfsfunktionen verwendet.

Für x64-Systeme kann die Signatursuche verwendet werden, um eine Referenz auf das Symbol _hlpDynamicFuncTable in libcorclr.dll zu finden.

Die Debugger-Funktion MT_GetDCB liefert nützliche Informationen, einschließlich der Adresse einer Hilfsfunktion, m_helperRemoteStartAddr, die den Speicherort von libcorclr.dll im Prozessspeicher angibt. Diese Adresse wird dann verwendet, um nach dem DFT zu suchen und einen Funktionszeiger mit der Adresse des Shellcodes zu überschreiben.

Der vollständige POC-Code für die Injektion in PowerShell ist hier verfügbar.

Referenzen

Lernen Sie AWS-Hacking von Grund auf mit htARTE (HackTricks AWS Red Team Expert)!

Andere Möglichkeiten, HackTricks zu unterstützen:

Last updated