Highway to Conti: Analysis of Bazarloader

Eli Salem
15 min readFeb 16, 2022

As we look back to summarize the year 2021 we observe that the biggest threat in the cybersecurity landscape is still ransomware. A large number of ransomware incidents have occurred around the world, extorting hundreds of millions overall from victims across the globe.
As the sun went down on some past major players in the ransomware ecosystem (such as REvil), the sun definitely shone on others, specifically the most[1] prolific group in 2021: Conti.

The list of Conti’s victims is definitely long and vary, with some high profile names such as the recent incidents of the bank of Indonesia[2], and Delta Electronics[3].
Although each case has its own story to tell, it is reported that multiple incidents of attacks that ended up with Conti ransomware started or had involved BazarBackdoor or BazarLoader malware[4][5].
In this article, I will present an analysis of the BazarLoader malware, its defensive measures to hinder security researchers, and other important core functionalities.

Bazarloader Background

BazarLoader has been first observed and reported in April 2020[6] and was associated and believed to be developed by a group called ITG23 or TrickBot gang[7].
The loader itself is known to be distributed by phishing campaigns that use multiple LoLbins for deployment such as Powershell, Mshta[8], ISO files[9], and eventually the involvement of Rundll32 or Regsvr32.

The Dropper

SHA1 hash: 94114c925eff56b33aed465fce335906f31ae1b5

Bazarloader dropper in PEstudio[23]

Similar to many malware that comes from the e-crime scene, Bazarloader comes packed inside an initial dropper. The dropper itself is a 64-bit .dll file with a high entropy of over 6.8.

When we open the dropper in IDA, we immediately notice a large olive color in the navigation bar, in many cases (especially with packed malware) this can be a big clue for obfuscated content yet to be decrypted.
As we investigate the navigation bar, we see two interesting code blobs right at the beginning of the .rdata section, the first one is quite small, but the second is very big with a size of 156256 bytes. For convenience, we’ll convert them both to a byte array, to do so, do the following:

1) Right-click on the code blob name
2) Click Byte
3) Right-click on the code blob again
4) Click Array
5) For convenience, change the name of these blobs

Big & small chunks of data

Next, we’ll want to inspect where these bytes are being used by seeing their cross-reference. By tracing the usage of the big blob, we can see that it is entering to a function named “sub_1800F110” that also gets a value of 0x26260 which is 156256 in decimal, and as we know, this is the exact size of the big blob.
This function objective will be:

  1. Allocate a new buffer using VirtualAlloc
  2. Use the small blob
  3. Partially decrypt the buffer into the newly allocated memory
  4. Once the function is finished, it will return the allocated buffer to a higher function named “sub_18000FC10”.
First decryption

In terms of decrypted data, in runtime, it will look like this:

First decryption output

Next, the partially decrypted buffer will be sent to another function called “sub_1800015D0”. This function objective will be:

  1. Perform further decryption using XOR loop with a designated key
  2. Allocate new memory
  3. Perform another decryption which will result in the final bazarloader payload, and copy it into the new buffer
Second decryption

In the end, after these two iterations of data manipulation, the two phases will look like this

Final output

Note: If we wanted to avoid this way, the easy way to unpack this dropper manually will be:

  1. Set a breakpoint on VirtualAlloc
  2. On the second instance, set a hardware breakpoint on the allocated buffer
  3. Hit Run until you see the final clean payload


Unpacked Bazarloader

The unpacked file (Bazarloader payload) is a 64-bit DLL file with a much lower entropy of 3.96 compared to its dropper. In addition, the malware has 8 export functions, however, all of them are empty except the function “EproyAklW”.
Also, we notice the internal name of the malware called “l_dll_rndll_eaw_64_p2_g8_v221_11_01_22_logs_no.dll”.

Export functions in PE-bear[22]

Also, the malware’s import function table is empty, which indicates that the API calls will be resolved dynamically by some mechanism. In addition, in terms of size, Bazarloader is small\mid size malware.

Empty Import table

My investigation will be separated into two parts:

  1. Bazarloader defenses: Any method the malware used to slow down researchers and how to overcome them.
  2. Bazarloader operative mechanism: Basically how the malware works.

BazarLoader defenses 1: API Hashing

Right as we enter the export function “EproyAklW” we observe the first defense mechanism of Bazarloader, its dynamic API hashing resolving function (which in our case is called sub_1800AC7C).
For those who are not familiar with the term API hashing:
“API hashing is simply an arbitrary function/algorithm, that calculates a hash value for a given text string[10].”
In simple words, the function gets as input some hash to be computed and eventually output a pointer to an API call. Next, usually, we’ll see this pointer being used in form of a function.

API hashing function

To confirm our hypothesis, we can always debug the function dynamically and step over it. once we do it we’ll notice two things:

  1. The register EAX will hold the address of the resolved API call (in this case it is RtlExitUserProcess (which is the kernel-mode equivalent to ExitProcess).
  2. Three instructions later the register EAX will be executed via call, which means RtlExitUserProcess will be executed.
Resolving API dynamically

As can be assumed, the main advantages of this technique are:

  1. The malware is more stealthy because as we said, its import table is empty, thus making the analysis more challenging and slow.
  2. This also creates some challenges for automated security products that rely on these API calls to be present in order to determine the file’s nature.

This technique is very common in the malware world and can be found in other malware such as Emotet, Qbot, Trickbot, Conti ransomware, Lockbit, and so on.

Small Tip: In many cases, the API hashing function will result in the address of the requested API call, therefore, in many cases, they will use the Process environment block (PEB) for the part of actually resolving. Searching for the usage of the PEB in the code is a good way to smell for these resolving functions.

As security researchers, the major issue with API hashing functions is that they are being executed many times, basically each time the malware wants to use a specific function. In Bazarloader’s case, we can see “sub_18000AC7C” being used 232 times. Obviously going to each function and resolving it dynamically is time-consuming and this process needs to be scaled.

Multiple times of resolving API

In order to speed things up, we’ll use one of my favorite tools, and a GO-TO when it comes to API hashing: HashDB[11][17][18].
The HashDB plugin is a community-sourced library of hashing algorithms used in malware. The plugin allows reverse engineers to test specific hashes against the algorithms that HasDB has.

Once having HashDB, do the following:

  1. Right-click on the hash
  2. Click on HashDB Hunt Algorithm
HashDB Hung Algorithm

After a couple of seconds, we got a popup that tells us that the algorithm found is “rol7_xor”, then, click ok.

HashDB found algorithm

Now, do the following:

  1. Right-click again on the hash
  2. Choose HashDB Lookup

Then, we’ll get another popup that will tell us that the hash is translated to the API call ExitProcess, similarly to what we saw during our dynamic analysis.

HashDB resolving API

Once the function has been decrypted, an Enum will be created, this Enum should be implied to all of the hashes. to do so, do the following:

  1. Right-click on the function name
  2. Click Set call type
  3. Change the type of the third argument to be the Enum name
HashDB enum

BazarLoader defenses 2: Stack strings (sort of)

Usually, malware authors like to hide indicative or important strings in embedded obfuscated code blobs inside the PE itself, a good example will be Qbot[12] which stores strings related to commands, process names, network activity, inside a code blob.

However, when we inspect this Bazarloader sample, we do not find any suspicious code blobs that could indicate hidden obfuscated data.
The reason for that is that Bazarloder store those strings in multiple small hashes that are combined during runtime and xored with a different key.

Stack strings (stacked hashes)

This behavior happened hundred of times during the malware operation, a good way to track them will be to use the plugin FLARE CAPA[13].

In order to decrypt them statically, all you need to do is the following:

  1. Click on the hash
  2. shift + E
  3. Copy the first 4 bytes to Cyberchef
  4. Do it for each hash and merge them
Decrypting hashes

5. Add from hex to the recipe

6. Add XOR to the recipe

7. In the xor key take the last 4 bytes (basically similar to the hashes)

Decrypting hashes

BazarLoader operative mechanisms

As mentioned, this section will be about anything related to the malware activity itself and commands.

Network Activity

First, like much other malware, Bazarloader will check the connectivity and try to access multiple legitimate domains.
Some of these domains are traditional for malware connectivity checks like google.com, however, some of them are more interesting such as the white house website.

Legitimate domains

Next, we observe indicative a command that instructs the malware to “download and run backdoor”, which could potentially be the BazarBackdoor.

download and run backdoor command

As for network capabilities, the malware will have two ways to operate and it will depend on:

  1. Use hardcoded IP \ Emercoin
  2. Use a generated Emercoin
Hardcoded vs generate

First method

If the malware will choose to use the hardcoded way, it will first use the following hardcoded IP addresses and Emercoin domains


Next, it will go to a function that will use WinINet functions to communicate externally

WinINet network function

This network function will return the status code of the network operation as an output using the API HttpQueryInfo. In other words, if the function is successful and works properly, it will return 200.

WinINet function returned value

Next, the caller function will check whether the status code is indeed 200, if yes, it will call a function that contains the code injection core function.

Checking status code

Second method: DGA

As told, bazarloader has an option to generate an Emercoin. In this option, Bazarloader will use its domain name generator (DGA) capabilities to generate a random .bazar (which is related to Emercoin) domain.
After generating the name, the malware will add the string “.bazar” to it as a suffix.

Bazarloader DGA

Then, the malware will have the ability to communicate externally using the WINSOCK functions. Unlike the WinINet functions where the majority of the functions are resolved directly by the API hashing function, in the case of the WINSOCK function the malware will:

  1. Decrypt their name which
  2. Use the API hashing function to resolve GetProcAddress
  3. Resolve the requested function using GetProcAddress
WINSOCK functions resolved

Then, the malware will use these functions to communicate

sendto & recvfrom

Code injection

The malware attempt to inject itself into one of the following processes:

  1. Svchost.exe
  2. cmd.exe
  3. explorer.exe

Then, it will go to a function that iterates through the running processes using the aforementioned API calls of CreateToolhelp32Snapshot[14] and ProcessFind32First\Next[15]. One found it will retrieve the process ID.

Processes to be injected
Processes to be injected

Eventually, the process ID of the chosen process will be sent to another function that will deal with the code injection itself.

Code injection process gets processID as an argument

In the code injection function, we can see the injection technique itself which appears to be Process Hollowing.

First, a process is created with the creation flag of 0x8000014. This number is actually masking the following flags:

  1. 0x08000000: CREATE_NO_WINDOW
  2. 0x00000010: CREATE_NEW_CONSOLE
  3. 0x00000004: CREATE_SUSPENDED
Process created suspended

Next, a new virtual memory will be allocated in the remote process followed by the traditional API calls we would expect to see in the Process Hollowing techniques.

Process hollowing

Looking for security products

More activities from Bazarloader are related to security products. Bazarloader will use the API calls CreateToolHelp32Snapshot and Process32First to create a snapshot of the running processes and iterate on them to search AV products-related processes.

Searching for security products

Also, the malware will use the traditional stack strings to search for the following processes:

  1. Norton Security
  2. nsWscSvc- Windows Security service
  3. ISSRV- Microsoft network real-time inspection service
Searching for security products

In addition, as already seen from the first image, Bazarloader will search for the names of the following security products:

  1. Avast
  2. BitDefender
  3. NortonSecurity
  4. WindowsDefender
Searching for security products

Registry operations

Bazarloader has a designated function that will create a process in order to perform specific commands, in most cases, this function occurs when a cmd command that manipulates the registry happens.


Bazarloader will use the cmd process in order to set a persistence into the traditional Microsoft\Windows\CurrentVersion\Run path.

Creating persistence

Additional commands are also include

cmd /c
cmd /c choice /c /y /d y /t 10
cmd /c choice /c /y /d y /t 10 & start
cmd /c echo
cmd.exe /c reg.exe query HKCU\Software\
cmd.exe /c reg.exe query HKCU\Software\ /t REG_BINARY /d
cmd.exe /c reg.exe query HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths


Right after the WinINet network function ends, another function that is related to the malware’s cryptography starts. The function also shares an argument with the network function.

Cryptography function intro

The cryptography function consists of multiple functions that each does several tasks. In order to not get into each one in detail, I will only demonstrate their important activities.

One of the functions is responsible to resolve the Crypt32.dll and Bcrypt.dll modules in the following way:

  1. Decrypt the names of the modules
  2. Use the dynamic API resolving function to resolve LoadLibrary
  3. Execute LoadLibrary with the decrypted module name as its argument
  4. Assign the handle for the DLL to an IDA variable for later usage
Resolving Bcrypt.dll

Then, it will do the same for the functions themselves, but with GetProcAddress. When it comes solely to Bcrypt, 14 different functions will be resolved and assigned to variables.

Bcrypt variables

After the resolving part ends, Bazarloader will use the functions to ignite its cryptography session. The algorithm that will be used will be RSA, this can be seen as plain text in the ALGID parameter of the function BCryptOpenAlgorithmProvider[19].

BCryptOpenAlgorithmProvider RSA

The malware then continues with creating the session with the usage of the rest of the functions, including generating the key to decrypt data from the BcryptImportKeyPair. Eventually, it will return to the base caller function (dubbed above as “start_crypt”).

in the end, the malware will use the function BcryptDecrypt[20] to decrypt requested data.

Final Bcrypt decryption

Designated strings and MD5 activity

One of the interesting activities of Bazarloader is the usage of specifically designated strings and using them as arguments in other parts of its activity.
These strings are manipulated several times before being used, thus making the process of observing their usage a little bit tricky. In order to show the general idea, I will demonstrate only one case.

Small Tip: In order to track the activity dynamically and align it with static analysis addresses, disabling the ASLR (with tools such as CFF Explorer) can be handy.

The entire activity will occur in one function that will deal with decrypting hardcoded strings using the aforementioned stack-strings and xor decryption method. However, as can be seen, before starting decrypting the strings, a different function named “sub_180005600” occurs.

Designated strings function

The objective of this function is relatively simple, creating an additional key for further decryption activities. In general, this will happen in the following way:

  1. First, with the use of two API calls SHGetSpecialFolderPathA, GetFileAttributesExA, and additional functions, the malware will generate some sort of digits array.
Generating digits

2. The digit array will go to another function that will deal with MD5 hashing as one of its arguments.

MD5 activity

If we inspect dynamically, we could see that after passing the MD5Final function, the digits will disappear and an MD5 hash will be produced.

Before MD5Final
After MD5Final

After the MD5 hash is created, the function will return to the caller function and the events will continue as the following:

  1. The hardcoded stack strings will be decrypted and combined into one string
  2. The combined string and the md5 hash key will be sent to another function named “sub_180004410” that will deal with further manipulation on the string.
Designated strings function workflow

Inside sub_180004410, the string will go through more manipulations, one of them is a loop that will XOR between the key (the MD5 hash) and the combined string.

XOR loop between the key and combined string

Next, several other manipulations will occur on the xored output until eventually a new string is generated.

Final string

Eventually, this process will happen to every string in the list of these designated strings, and as said, they will be used as an argument in further malware activity.
For example, the string from the image above will be used as the Mutex name when the malware executed CreateMutexA[21].

Final string being used as Mutex name


[1] https://twitter.com/ido_cohen2/status/1477620045794758658
[2] https://www.bitdefender.com/blog/hotforsecurity/bank-indonesia-confirms-conti-ransomware-attack-stolen-files-leaked/
[3] https://www.bleepingcomputer.com/news/security/taiwanese-apple-and-tesla-contractor-hit-by-conti-ransomware/
[4] https://www.cybereason.com/blog/threat-analysis-report-from-shatak-emails-to-the-conti-ransomware
[5] https://thedfirreport.com/2021/10/04/bazarloader-and-the-conti-leaks/
[6] https://www.bleepingcomputer.com/news/security/bazarbackdoor-trickbot-gang-s-new-stealthy-network-hacking-malware/
[7] https://www.cybereason.com/blog/threat-analysis-report-from-shatak-emails-to-the-conti-ransomware
[8] https://twitter.com/executemalware/status/1485799287615279109
[9] https://twitter.com/Max_Mal_/status/1483571535491260417
[10] https://www.ired.team/offensive-security/defense-evasion/windows-api-hashing-in-malware#:~:text=API%20hashing%20is%20simply%20an,for%20a%20given%20text%20string.&text=Set%20a%20variable%20%24hash%20to%20any%20initial%20value.
[11] https://github.com/OALabs/hashdb
[12] https://blog.vincss.net/2021/03/re021-qakbot-dangerous-malware-has-been-around-for-more-than-a-decade.html
[13] https://github.com/mandiant/capa
[14] https://docs.microsoft.com/en-us/windows/win32/api/tlhelp32/nf-tlhelp32-createtoolhelp32snapshot
[15] https://docs.microsoft.com/en-us/windows/win32/api/tlhelp32/nf-tlhelp32-process32first
[16] https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-gettickcount
[17] https://youtu.be/3FPY4cLaELU
[18] https://aaqeel01.wordpress.com/2021/10/18/zloader-reversing/
[19] https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptopenalgorithmprovider
[20] https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptdecrypt
[21] https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createmutexa
[22] https://hshrzd.wordpress.com/pe-bear/#:~:text=PE%2Dbear%20is%20a%20freeware,works%20for%20windows%20and%20Linux).
[23] https://www.winitor.com/