Dissecting BlackByte Ransomware

Author

Uriel Kosayev
Cybersecurity researcher and red teamer who lives both on the offensive and defensive fronts. The author of the “Antivirus Bypass Techniques” book, expert in malware research, reverse engineering, penetration testing, digital forensics, and incident response

Dive into BlackByte ransomware! Learn how it exploits ProxyShell vulnerabilities, evades detection, and employs anti-debugging techniques. Packed with reverse engineering insights, this post is part of the free TrainSec Knowledge Library—your go-to resource for mastering malware analysis.

This post was written in collaboration of:

  • Naor Evgi (Linkedin) – Cybersecurity expert and malware researcher specializing in reverse engineering, advanced malware analysis, and organizational security testing.
  • Matan Bahar – @Bl4ckShad3 –  Security Audits & Penetration Tests Team Lead at White-Hat
  • Uriel Kosayev  – @MalFuzzer – Founder of TrainSec academy, Author of the Antivirus Bypass Techniques book

Introduction to BlackByte

BlackByte ransomware is malicious software that first appeared in July 2021. The ransomware operates as a service (RaaS), allowing cybercriminals to use it for their malicious purposes. The initial infections were the result of exploiting ProxyShell vulnerabilities, allowing the attackers to gain access to the victim’s environment.

BlackByte has similarities to other ransomware variants, such as Lockbit 2.0, in that it avoids systems that use Russian and several Eastern European languages, including those written with Cyrillic alphabets. Since its emergence, the operators behind BlackByte have been highly active and have targeted numerous U.S. and global organizations in various industries, such as energy, agriculture, financial services, and the public sector.

The number of attacks associated with the RaaS increased by more than 200% in the last quarter of 2021 (October-December) compared to the previous quarter (July-September). This highlights the pervasiveness and evolving tactics of the BlackByte ransomware group and the need for organizations to be vigilant in their cybersecurity efforts.

In the first part of our research on BlackByte, we will mainly talk about the first techniques that the malicious software performs to avoid debuggers and virtualized-based environments, some of which are not documented in Microsoft’s official documentation. And in addition to using basic obfuscation techniques which will help the malicious software avoid the detection of many security systems, BlackByte silently catalogs the steps it performs, and by using some patching steps we were able to find indicators of the many steps it is performing on victim machines. For instance, the a1 step/phase indicates the first step, a2 the second, and so on.

A typical BlackByte ransomware note looks like so:

image

Initial Analysis

Let’s start with some basics. First of all, the malware seems to be packed. There is a high entropy value which can indicate the presence of encrypted, compressed, encoded, and/or obfuscated data in the file. Entropy is a measure of the randomness or disorder of the data in a given file. You can see the high-value entropy of 7.832. A value higher than 7 is considered to be relatively high as can be seen in the following screenshot:

image 1

Furthermore, the PE sections are named UPX which is used as another indicator of the presence of a packer:

image 2

More evidence on the use of the UPX packer can be also noticed that the used permissions of these sections are Read, Write, and Execute which is abnormal and also used for self-modifying / self-injection capabilities to self-modify its content/code on runtime as can be seen in the following screenshot:

image 3

Such a technique is widely used by malware authors to evade detections of EPP/AV/EDR solutions.

Preparing the file for analysis

We will use the UPX command-line utility to decompress and unpack this UPX-packed executable. The command is simple as:

image 4

By getting into the strings window in IDA, we can see that this malware is written in Go by the name of the functions:

image 5

By using the AlphaGolang tool we can ease our reverse engineering efforts by resolving the Go-based functionalities. The following are the steps to apply to our analyzed IDA IDB file of the BlackByte ransomware:

  • Script1 — Recreate plan table.
  • Script2 — Discover functions by walking the plan table.
  • Script3 — Surface user-generated functions.
  • Script4 — Fix string references.
  • Script5 — Extract type information (by Ivan Kwiatkowski).

Reverse Engineering

In the beginning, the malware will hide and if we patched the API that responds to this action, we will see the malware categorize its action with the following prefixes A1, A2, A3, and so on.

Each section shows the stage the malware is in until full encryption.

Window Hiding

The malware hides itself using the ShowWindow function with the SW_HIDE parameter. This will hide the window from being shown on desktops, effectively making it invisible to the victim:

image 6
image 7

If we change the value of the EDX register to 5 (SW_SHOW) to show the window with the stages that the ransomware executes on victim machines (A2, A2, and A3).

Anti-VM

One of the used Anti-VM techniques in this variant of BlackByte ransomware is the use of the assembly CPUID instruction. When CPUID assembly instruction is executed with the passed parameter of 0 in the EAX register, it will return the CPU manufacturer-ID string, a 12-character ASCII string that will be stored in the EBX, EDX, and ECX registers. On machines running on a physical Intel or AMD CPU, this string will be “GenuineIntel” or “AuthenticAMD”, respectively. In cases of Microsoft’s Hyper-V or VMware, the returned string will be “Microsoft HV” or “VMware VMware”.

EBX = Genu

EDX = ineI

ECX = ntel

EBX + EDX + ECX = GenuineIntel

image 8
image 9

When the CPUID instruction is executed with the EAX register set to 1 as input will return [Processor Info and Feature Bits] (https://en.wikipedia.org/wiki/CPUID#EAX.3D1:_Processor_Info_and_Feature_Bits), essentially a rundown of various details about the CPU.

On a physical machine, the 31st bit of the returned ECX register will be 0, while on a virtual machine, it will be 1 as can be seen in the following screenshot:

image 10
image 11
image 12

To bypass this Anti-VM technique, we need to go past this instruction, changing the passed parameter values or just changing the respective return values which will hold the needed information for the malware to detect the presence of a virtualized environment.

Another way to prevent from malware detecting the presence of a virtualized environment is by editing the VMX file of the virtual machine (VMX for VMware-based virtual machines). We first locate the VMX file which holds within it the configuration of the virtual machine.

Open the file in a text editor of your choice, and add the following lines at the end of the file:

image 13

Adding this setting replaces the default VMware CPUID configuration which will return different values for malware or any other software that will use this same technique of CPUID.

Windows Security Researcher Badge

$1408

$1128 or $113 X 10 payments

Windows Security Researcher

Provides the necessary knowledge, understanding, and tools to be a successful Windows OS researcher.

Anti-Debugging

The first Anti-Debugging technique employed in this variant is the CheckRemoteDebuggerPresent which is a Windows API function that can be used to determine whether a debugger is currently attached to a process. This function can be used as an anti-debugging technique to detect if a process is being debugged and take evasive action if and when necessary. The function takes a handle to the process and a pointer to a BOOL-type variable as the input and returns a non-zero value if a debugger is attached to the process.

image 16

The second Anti-Debugging technique employed in this variant is the GetTickCuont64 Windows API function followed by the NtDelayExecution Windows native API function imported from the NTDLL.dll library as can be seen in the following screenshot:

image 15

From the above code we can conclude that the following logic is being applied:

1. It calls GetTickCount64 to get the current system tick count.

2. It then loads the address of the “ntdll” string into the rcx register and uses the GetModuleHandleA function to get a handle on the ntdll library.

3. It then loads the address of the “NtDelayExecution” string into the RDX register and uses the GetProcAddress function to get the address of the NtDelayExecution function from the ntdll library.

4. It then loads a specific value into the [rsp+5A8h+var_566+6] memory location, xors the ecx register with itself, and calls the NtDelayExecution function.

5. It then calls GetTickCount64 again, subtracts the first tick count value from the second one, and compares the result to 0x270F.

6. If the result is less than or equal to 0x270F, it jumps to the loc_6023F0 location.

It appears that the code is attempting to use the NtDelayExecution function to introduce a delay in the execution of the program, and then checks the amount of time that has passed to see if the delay was skipped or took less time than expected. This may be an indication that a debugger is attached to the process and the code is taking evasive action to evade debugging.

The third Anti-Debugging technique employed in this variant is the NtSetInformationThread Windows native API function. This technique is used to hide a thread from a debugger with a help of the undocumented value THREAD_INFORMATION_CLASS::ThreadHideFromDebugger (0x11). After the thread is hidden from the debugger, it will continue running but the debugger won’t receive events related to this thread.

If there is a breakpoint in the hidden thread or if it hides the main thread from the debugger, the malware will crash and the debugger will be stuck.

image 14

The fourth Anti-Debugging technique employed in this variant is the GetThreadContext Windows API function. The Malware checks the values of the Debug Registers (DR0-DR3) in the thread context. Debug Registers are used by debuggers to set hardware breakpoints in a process’s memory.

By checking the values of the Debug Registers in the context of a thread, you can determine whether a debugger has set any hardware breakpoints. If the values are not set to their default values, this indicates that a debugger is attached to the process and is using the Debug Registers for breakpoints.

List of the x86 debug registers

  • DR0 — Linear breakpoint address 0
  • DR1 — Linear breakpoint address 1
  • DR2 — Linear breakpoint address 2
  • DR3 — Linear breakpoint address 3
  • DR4 — Reserved. Not defined by Intel
  • DR5 — Reserved. Not defined by Intel
  • DR6 — Breakpoint Status
  • DR7 — Breakpoint control

DR0-DR3 store a linear address of a breakpoint. The stored address can be the same as the physical address or it needs to be translated to the physical address. _DR6_ indicates which breakpoint is activated. _DR7_ defines the breakpoint activation mode by the access modes: _read_, _write_, or _execute_.

image 17

Defense Evasion Techniques

Mutex testing with the CreateEvent Windows API Function

The employment of the CreateEventA Windows API function can be used as a form of a “mutex” technique to check the presence of an already running instance of processes/malware. A mutex is a synchronization object that can be used to protect shared resources from being accessed by multiple threads or processes at the same time. A process can use the CreateEventA function to create a named event object and check if the event already exists by passing the CREATE_NEW flag. If the event already exists, it means that another instance of the process is already running and the process exits, however, if the event does not exist, it means that the process is the first instance, and it continues to execute.

A debugger can manipulate the memory to make the event always exist, or the process will exit when it sees the event exists as can be seen in the following screenshot:

image 18

This way the process can detect whether it’s being debugged and take evasive action.

Gain Insider Knowledge

Subscribe to updates from the TrainSec trainers

Direct Syscall

Using direct syscalls is taking little extra work for skilled attackers/malware developers but allows the malware to bypass API hooks employed by endpoint security solutions such as AV and EDR systems for detection purposes. The following example shows the use of a Direct syscall implementation of NtCreateFile10 which is NtCreateFile:

image 19

Interestingly enough, we found this syscall implementation with the same naming of NtCreateFile10 (which probably corresponds to the NtCreateFile syscall id of 55h of Windows 10 OS builds).

image 20

The above code snippet can be found in the very well-known LSASS dumping tool of Outflank-Dumpert: https://github.com/outflanknl/Dumpert/blob/master/Dumpert-DLL/Outflank-Dumpert-DLL/Syscalls.asm

The same direct syscall technique is used for the ZwProtectVirtualMemory function to change proves protection bits:

image 21
image 22

Code Obfuscation

Obfuscation involves making code or data intentionally complex, making it difficult to comprehend or decipher. Malware authors, frequently utilize obfuscation to conceal the true nature and actions of their malicious software. Also, by implementing obfuscation techniques, they can evade detection by security systems and deceive victims into believing the software is benign. The following example shows the use of sample manipulation on a string to bypass security systems:

image 23

The above code explains the data decoding process. It is important to note that the lookup table used for the encoding is loaded in the RDX register: h?a|nsit|xax~xyjr87asyiqq3iqq.

  • The first instruction, `sub edx, 5`, subtracts 5 from the value stored in the `edx` register.
  • The second instruction, `add rax, 1`, adds 1 to the value stored in the `rax` register.
  • The third instruction, `mov [rax-1], dl`, stores the least significant byte of the value stored in `edx` (`dl`) into the memory location pointed to by `rax-1`.
  • The fourth instruction, `cmp r8, rax`, compares the value stored in `r8` with the value stored in `rax`.
  • The fifth instruction, `movzx edx, byte ptr [rax]`, loads a byte from memory at the address stored in `rax` into the `edx` register, zero-extending it to a 32-bit value.
  • The final instruction, `test dl, dl`, performs a bitwise AND between the least significant byte of `edx` (`dl`) and itself.

A small Python script we wrote that performs the reverse operation to return the decoded string blobs.

image 24

Attached is the code snippet here for your convenience:

Python
Encoding_String = "h?a|nsit|xax~xyjr87asyiqq3iqq"
Decode_String = ""
for I in Encoding_String:
  value = ord(i)
  val = value - 5
  new_val = chr(val)
  Decode_String += new_val
print(Decode_String)

Encoding_String = “h?a|nsit|xax~xyjr87asyiqq3iqq”
 Decode_String = “”
 for I in Encoding_String:
 value = ord(i)
 val = value — 5
 new_val = chr(val)
 Decode_String += new_val
 print(Decode_String)

Indicators of Compromise

Packed BlackByte 

3de8fe5cee8180e93697e4ddca87e721910b9dd922de849cab7b1b3a50e54a00

Unpacked BlackByte 

4ea84c6cc76b18435d74a0f7968a2359a3c2753bcaa940c6665db03c780a5537

Gain Insider Knowledge

Subscribe to updates from the TrainSec trainers
blue depth

About the author

Uriel Kosayev
Cybersecurity researcher and red teamer who lives both on the offensive and defensive fronts. The author of the “Antivirus Bypass Techniques” book, expert in malware research, reverse engineering, penetration testing, digital forensics, and incident response