
P³-Shellcode Loader ist ein Loader, der eine Code-Injektionstechnik implementiert, die die Process-Parameters-Struktur als Ausführungs- und Staging-Ort für die Shellcode-Injektion in entfernte Prozesse nutzt, ohne gängige Erkennungsmechanismen auszulösen.
Autoren: Max Hirschberger & Ogulcan Ugur
Der P³-Shellcode Loader ist ein Loader, der eine Codeinjektionstechnik implementiert, die die Process-Parameters-Struktur (Process Parameter Poisoning) als Ausführungs- und Staging-Ort für die Shellcode-Injektion in Remote-Prozesse nutzt, ohne gängige Erkennungsmechanismen auszulösen.
Ein ähnliches Konzept wurde vom Sicherheitsforscher modexp beschrieben, der zeigte, dass die an die CreateProcess-API übergebenen Argumente für diesen Zweck genutzt werden können [1].
Angreifer möchten ihre Aktivitäten weniger verdächtig erscheinen lassen. Mit Prozessinjektion sind Angreifer in der Lage, ihre Aktivitäten aus einem anderen Prozess heraus auszuführen, der vertrauenswürdiger ist oder von dem erwartet wird, die jeweilige Aktivität auszuführen, wodurch der Verdacht verringert wird.
Die folgenden typischen Schritte sind erforderlich, um Code in einen anderen Prozess zu injizieren:
OpenProcess / NtOpenProcess oder CreateProcess / NtCreateProcess).VirtualAllocEx oder NtAllocateVirtualMemory).WriteProcessMemory / NtWriteVirtualMemory).VirtualProtectEx / NtProtectVirtualMemory).CreateRemoteThread bzw. NtCreateThreadEx).Weitere Injektionstechniken umfassen unter anderem die folgenden:
NtSetContextThread)NtQueueApcThread)RtlCreateProcessReflection, die Prozess-Forking implementiert.
In unseren Tests haben wir beobachtet, dass die meisten EDRs sich auf bestimmte Telemetriedaten konzentrieren, um Prozessinjektion zu erkennen. EDRs überwachen hauptsächlich die Nutzung von WriteProcessMemory und VirtualAllocEx sowie deren zugrunde liegende Kernel-Systemaufrufe NtWriteVirtualMemory, NtAllocateVirtualMemory und NtAllocateVirtualMemoryEx.Windows stellt die API-Funktion CreateProcessW zum Erstellen neuer Prozesse bereit, wie in Listing 1 gezeigt. Die ersten drei Parameter lpCommandLine, lpEnvironment und lpStartupInfo sind für die beschriebene Injektionstechnik relevant, da sie verwendet werden, um Daten an den neuen Prozess zu übertragen.```c
BOOL CreateProcessW(
[in, optional] LPCWSTR lpApplicationName,
[in, out, optional] LPWSTR lpCommandLine,
[in, optional] LPSECURITY_ATTRIBUTES lpProcessAttributes,
[in, optional] LPSECURITY_ATTRIBUTES lpThreadAttributes,
[in] BOOL bInheritHandles,
[in] DWORD dwCreationFlags,
[in, optional] LPVOID lpEnvironment,
[in, optional] LPCWSTR lpCurrentDirectory,
[in] LPSTARTUPINFOW lpStartupInfo,
[out] LPPROCESS_INFORMATION lpProcessInformation
);
*Listing 1: Definition der Windows-API-Funktion CreateProcessW*
Der Parameter `lpCommandLine` legt die Befehlszeile für den neuen Prozess fest. Er ist auf maximal 32.767 Unicode-Zeichen begrenzt, einschließlich des Unicode-Nullterminators. Für die Unicode-Variante muss eine Zeichenfolge bereitgestellt werden, in die die Funktion schreiben kann. Wenn eine konstante Zeichenfolge übergeben wird, führen alle Schreibversuche der API-Funktion zu einer Speicherzugriffsverletzung. Wenn der Wert `NULL` ist, wird die Befehlszeile des Prozesses aus dem Parameter `lpApplicationName` übernommen. Wenn `lpApplicationName` `NULL` ist, muss sie im Feld `lpCommandLine` angegeben werden und ist auf `MAX_PATH` Zeichen begrenzt.
Der Parameter `lpEnvironment` stellt dem Prozess eine Liste von Umgebungsvariablen bereit. Wenn der Wert `NULL` ist, wird die Umgebung des erstellenden Prozesses verwendet. Die Liste der Umgebungsvariablen besteht aus aufeinanderfolgenden nullterminierten Zeichenfolgen im Format `NAME=VALUE` mit einem weiteren Nullterminator am Ende.
Der Parameter `lpStartupInfo` ist eine in Listing 2 gezeigte Struktur mit Feldern wie Window-Station, Desktop, Handles für Standardeingabe und -ausgabe sowie Feldern, die das Hauptfenster des neuen Prozesses konfigurieren. Laut Microsoft-Dokumentation ist das Feld `lpReserved` für die interne Verwendung reserviert und nicht weiter dokumentiert. Durch die Analyse mit dem WinDbg-Debugger war es möglich, diesen Parameter mit der Variable `ShellInfo` vom Typ `UNICODE_STRING` im neuen Prozess in Verbindung zu bringen.```c
typedef struct _STARTUPINFOW {
DWORD cb;
LPWSTR lpReserved; // Copied to ShellInfo (UNICODE_STRING)
LPWSTR lpDesktop;
LPWSTR lpTitle;
DWORD dwX;
DWORD dwY;
DWORD dwXSize;
// (...) additional fields
WORD wShowWindow;
WORD cbReserved2;
LPBYTE lpReserved2;
HANDLE hStdInput;
// (...) additional fields
} STARTUPINFOW, *LPSTARTUPINFOW;
Listing 2: Layout der STARTUPINFOW-Datenstruktur