Qbot or Qakbot is a sophisticated worm with banking capabilities. This malware family has been infecting computers since 2009, utilizing a number of techniques (some of them quite advanced) which make it difficult to detect. It has a packing layer, anti-VM techniques, anti-debug techniques, and anti-sandbox techniques which make the analysis of this threat difficult. Qakbot is capable of updating itself and this also makes this threat more complex to detect since it is constantly changing on disk.
Using Triage we analyzed the most recent variant of this malware, and we added a new module to support the detection and configuration extraction of Qakbot samples as shown in the image below. A tool to deobfuscate the Qakbot payload is also included qakbuscator.py.
Qakbot has a custom packer. There are probably other versions of Qakbot in the
wild with different packers, but this section is based on analysis of the packer
for the sample:
In summary, the unpacking process is as follows:
The packer allocates memory and then drops an encrypted buffer there (Step 1-2).
The dropped buffer is decrypted and the decrypted data contains a PE file (Step
3). This PE file is not at the beginning of the buffer but starts at offset
0x427. From the beginning to the PE file offset is filled with
This could be a trick to make analysts think that this function is “freeing memory” or that it’s a memset-like function.
The PE header is modified - this can also confuse analysts or memory dumping tools that look for PE file signatures since they can’t find the “MZ” magic number. This is shown in the image below.
The decrypted PE file image size is calculated to allocate memory for it. The PE file is copied (mapped as a windows loader would do) from the decrypted buffer to the newly allocated memory (Step 3-4).
Once the file is mapped to the newly allocated memory, the header is fixed as shown in the following image. Once the PE file is mapped its entry point is called. (Step 4)
This PE is going to read the rest of the previous decrypted buffer since there is still some encrypted data. Once the data is decrypted a new PE file can be found. (Step 4-5)
This time the header is also modified and is fixed before mapping it. (Step 5-6)
The decrypted PE is Qakbot itself. In this case the PE header doesn’t have the well-known string “This program cannot be run in DOS mode”, because the DOS-Stub was deleted.
This PE file is the final payload of Qakbot so finally the PE file is mapped to the ImageBaseAddress of the original file (Step 6).
The original image loaded at address
0x400000 is wiped.
The newly unpacked PE (Qakbot) is copied to the original image base address
So, after mapping the Qakbot binary the execution flow goes to the EntryPoint of this file. (Step 7).
The unpacked sample hash of the file we ran in Triage:
Once the sample is unpacked, Qakbot itself also implements an obfuscation layer in its code. This obfuscation makes the analysis a bit harder. The flow graph of the main function is the following:
The obfuscation basically consists of adding unused loops with an empty body. Like the following:
As is shown in the image above, it does a “XOR EAX, EAX” operation and then decides to loop or not depending on the Z flag which is set with the previous instruction (so the loop will never happen). The goal of these small loops is to make a less comprehensive flow graph and to make the analysis harder. There are more than 600 loops like this throughout the code.
At Hatching, we implemented a tool qakbuscator.py to deobfuscate the code and make the analysis much easier. This tool is provided with this analysis to allow all researchers to use it.
The DLLs that are in the Qakbot resources also have this obfuscation layer - you can use the script to deobfuscate them.
The sample used to perform the behavioral analysis is the deobfuscated sample using our deobfuscator tool explained in the previous section.
This is how a process tree of a Qakbot infection looks like:
Regardless of the input vector, the first time Qakbot runs it tries to install itself.
First of all, it checks if it is running in a virtualized environment or not.
Qakbot executes itself with the option
"/C". Qakbot admits parameters, in this
case the parameter
"/C" is to make anti-VM and anti-sandbox checks like the
Reading from the virtual port in order to detect VMWare
Check the CPUID
There are also other techniques used by Qakbot to know if it is running in an emulated environment like checking the sample name - in order to see if it is set to some default name like “sample.exe” or “malware.exe”; or checking running processes in order to detect any related to a virtual environments, anti-virus, debuggers etc.
Among the different options that Qakbot accepts we can find the following:
|/I [name]||Disable Windows SpyNey and delete scheduled task [name]|
|/P[file]||Decrypt [file] and load it|
|/Q||Set exit status to
|/T||Sync related stuff|
|/i [name]||Install itself and delete scheduled task [name]|
|/t||Send Window Message|
|/A  ||Unknown|
If a VM is detected it exits. Otherwise, it copies itself into
under a randomly generated folder with a randomly generated name. Those names
are unique for each infected machine since they are created using some
characteristics from infected host.
It also creates the following registry key in order to be run when the system reboots “HKCU\Software\Microsoft\Windows\CurrentVersion\Run”.
Also, it drops a
.dat file that has configuration information, like botnet name,
timestamp, etc. This file contains encrypted data which is decrypted in memory
during run time. Once this file is decrypted it looks like is shown in the image
The following table, from a blog post by the security researcher Vitaly Kremez (link) , shows the meanings of some of these config values:
|11 = 2 (number of hardcoded C2)|
|1 = date of qbot install in HH:MM:ss-dd/mm/yyyy|
|2 = victim qbot install|
|45 = C2 IP|
|46 = C2 Port|
|39 = victim external IP|
|38 = last victim call to C2 (time in Unix)|
|43 = time of record ((time in Unix)|
|5 = victim network shares|
Finally, the copied file is executed and the original file is overwritten with
calc.exe. Some malware deletes the file directly, but Qakbot has decided to
overwrite it with a legitimate binary. This way it doesn’t leave traces.
When Qakbot is installed, its behavior is different. In this case, it is going
to create an instance of the
explorer.exe process in order to inject itself
Once injected into explorer, the main .dll is loaded. At this point, different things could happen since the communication with the control panel begins. As shown in the process tree above, the explorer process executes an update of Qakbot directly downloaded from the C&C. Also, it can exfiltrate data, or infect browsers in order to get banking information from the victim system.
Qakbot update sample: https://tria.ge/reports/191104-athqk1tjxn/task2
In Triage we’ve just added support for this family, meaning you can detect Qakbot as well as get its configuration directly after the analysis.
The Triage report for the sample that was used for this blog can be found (here).
|Qakbot resource 1 (main.dll)||83273809a35ba26c2fb30cba58ba437004483ae754babad63c5d168113efa430|
|Deobfuscated Qakbot resource 1 (main.dll)||74f8907acfd070d2590895523433a8c85b5ef87f4e1a5ef7ccd356f5562b7a6b|
|Qakbot resource 2 (injects dll x86)||b7d9a462bd105193e998b6324f3343b84f11ceb21ab24e60e2580a26d95e4494|
|Qakbot resource 3 (injects dll x64)||8c7a43002ee6105fc37fcdfc00a192239639f7c08bf28e06ca1432551fe21b3f|
Here is a list of related samples and their corresponding Triage reports.
|f614a06748251107a34fa7e44c7652fd88 e61fd958df724455e14ec88040abf9||1.bin https://tria.ge/reports/191111-ypg95xvrwj_|
|7d4d207fb5258f504d3f9ef60d431332d1 e7320d5849c0b0acf624612b01c8f0||2.bin https://tria.ge/reports/191111-mgrgp545yx_|
|357b4979324e2065adc8e6bd11cd7161f8 30250cae30f50fb13edd70fd2b506b||3.bin https://tria.ge/reports/191111-sbsq7xbqea_|
|29754f0caa9576eba6b9c351d20549e7e1 9216c6e72c2963da33450719a51277||4.bin https://tria.ge/reports/191111-57yf3bdh4j_|
|304a01a339d86ccbba7b1f671839624d44 6e6ea86474912bf976837df779bad2||5.bin https://tria.ge/reports/191111-38qmrk62q2_|
|d2f8a61e8cfc9a6c983fc40d2b7ac33e2a 686872d0136dce2f66466c044f246c||6.bin https://tria.ge/reports/191111-p6cqne7cwn_|
|2b9ef4a9f47402d171eec28acadf3753cb b33c9bc6ec26d99aa060127a470e95||7.bin https://tria.ge/reports/191111-zl9l5y6lp2_|
|eb17935cf972d90be92c9b39fff8b3d760 ecda78a6f602cb2b8bbaf3d87e6b61||8.bin https://tria.ge/reports/191111-7tn19rbh9x_|
|6b88260f4c4da4651a82bb62761cd23ee9 ad6662a2a0abbec017e7193668397b||9.bin https://tria.ge/reports/191111-hb6qpeaars_|
|256967605423fea1e00368078eea1cdb52 d391aa0091e0798db797ab337d1567||10.bin https://tria.ge/reports/191111-m8tm8zqbrs_|
|13c2f4b6fb80500884a4ea9d2fe8077412 4f46ebfd80de3e1dfcfb9e167aee08||11.bin https://tria.ge/reports/191111-7cpggrpxts_|