Research By: Alex Ilgayev
The notorious banking trojan Qbot has been in business for more than a decade. The malware, which has also been dubbed Qakbot and Pinkslipbot, was discovered in 2008 and is known for collecting browsing data and stealing banking credentials and other financial information from victims. It is highly structured, multi-layered, and is being continuously developed with new features to extend its capabilities. .These new ‘tricks’ mean that despite its age, Qbot is still a dangerous and persistent threat to organizations. It has become the malware equivalent of a Swiss Army knife, capable of:
A prominent campaign using QBot ran from March to the end of June this year. We assumed that the campaign was stopped to allow those behind QBot to conduct further malware development, but we did not imagine that it would return so quickly.
Towards the end of July, one of today’s most serious cyber threats, the Emotet Trojan, returned to full activity and launched multiple malspam campaigns, impacting 5% of organizations globally. Some of these campaigns included installing an updated version of Qbot on victims’ PCs. A few days later, we identified a newer Qbot sample dropped by latest Emotet campaign, which led us discovering a renewed command and control infrastructure and brand new malware techniques distributed through Emotet’s infection process.
If that wasn’t enough for us, Qbot’s malspam campaign resumed earlier in August, spreading globally and infecting new targets. One of Qbot’s new tricks is particularly nasty, as once a machine is infected, it activates a special ‘email collector module’ which extracts all email threads from the victim’s Outlook client, and uploads it to a hardcoded remote server. These stolen emails are then utilized for future malspam campaigns, making it easier for users to be tricked into clicking on infected attachments because the spam email appears to continue an existing legitimate email conversation. Check Point’s researchers have seen examples of targeted, hijacked email threads with subjects related to Covid-19, tax payment reminders, and job recruitments.
Based on our visibility, most of the attacks were made against US- and Europe-based organizations as we can see in Figure 1.
Figure 1 – Attacked organizations by country
Among these, the most targeted industries were in the government, military, and manufacturing sectors.
Figure 2 – Attacked Organizations by industry
After a thorough analysis of these new QBot samples, we will share our knowledge and insights about the following topics:
QBot’s infection chain is described in the following flow-chart and will be discussed in the next sections:
Figure 3 – Infection chain diagram
The initial infection chain starts by sending specially crafted emails to the target organizations. The method is less sophisticated than spear-phishing techniques but has additional attributes which add to its credibility. One of these is called “Hijacked Email Threads” – capturing archived email conversations and replying to the sender with the malicious content. Those conversations could be captured using Qbot’s Email Collector module which we will describe later.
We can see in Figure 4, Figure 5, and Figure 6, examples of such methods from samples submitted by @malware_traffic in their blog:
Figure 4 – Example of COVID-19 related email thread
Figure 5 – Example of email thread for tax payment reminder
Figure 6 – Example of email thread for job recruitment
Each of these emails contains a URL to a ZIP with a malicious VBS – Visual Basic Script file.
During our tracking of the malspam campaign, we have seen hundreds of different URLs for malicious ZIP dropping when most of them were compromised WordPress sites.
The VBS based infection method is rather new for the malware, and is being used since April 2020. In previous campaigns, the infection chain started with a Word document containing malicious macros.
While the previous macros had simple obfuscation and string decoding, the VBS file contains several more advanced methods:
File Size – The file size is larger than 35MB, padded with NULL
bytes. Big files are usually dismissed by various sandboxes due to performance limitations.
Sleep Timer – The script delays its execution by calling the Sleep API. This is another method for avoiding sandboxes.
Obfuscation – The script contains multiple obfuscation methods such as those described in Figure 7.
Figure 7 – VBS obfuscation methods
Encryption – The VBS file downloads the Qbot payload from one of 6 possible hardcoded encrypted URLs. These URLs are encrypted by a custom XOR encryption 3 times with different keys that are built dynamically. We created an extraction script that can be accessed in Appendix B.
In order to support detection and hunting for additional malicious VBS files, We wrote a YARA rule which can be observed in Appendix A.
Similar to the old infection method, the VBS file downloads and executes the Qbot payload.
In the course of our analysis, Qbot’s operators frequently upgraded its versions and encouraged us to track and analyze the changes in each and every version. The fact that the developers left a version tag marked in the samples, allowed us to perform this analysis easier.
For example, let’s have a look at the version tag as it shown in the unpacked sample below:
Figure 8 – Sample’s major and minor version
From that, we can deduct that the initial payload version is 325/5
, while the main payload version is 325/7
. (The major version is read as a hex value)
Over the last few months, we tracked the different versions of Qbot and identified some of the differences in each version, as can be seen in the following table.
Major | Minor | Payload Minor | Version Timestamp | Notes |
324 | 44 | 8 | Jan 22, 2020 | First version seen for major version 324. |
324 | 353 | 53 | Mar 3, 2020 | |
324 | 375 | 65 | Mar 13, 2020 | |
324 | 379 | 70 | Mar 20, 2020 | Added command 35 supporting hVNC module. |
324 | 383 | 74 | Apr 1, 2020 | |
324 | 385 | 75 | Apr 1, 2020 | |
324 | 388 | 79 | Apr 8, 2020 | Added command 10 – terminate process by name. |
324 | 390 | 127 | Apr 10, 2020 | |
324 | 393 | 136 | Apr 29, 2020 | JS Updater resource is no longer included. JS Update commands has been respectively adjusted. |
324 | 399 | 141 | May 7, 2020 | Added long list of blacklisted analysis programs part of anti-VM method. |
324 | 401 | 142 | May 28, 2020 | |
325 | 5 | 7 | July 29, 2020 | Introduced new anti-analysis techniques. Added anti-VM checks on server-side. |
325 | 7 | 13 | July 31, 2020 | |
325 | 8 | 14 | August 3, 2020 | |
325 | 35 | 42 | August 7, 2020 | |
325 | 37 | 43 | August 11, 2020 | Last known version up to the writing of this article. |
The date mentioned for each sample is based on the executable compilation time attribute. That field can be changed via timestomping, but we suspect that it wasn’t forged in these cases.
We also tracked the timestamps of the main payload, and seen that the compilation time was consistently minutes apart from the initial payload’s:
Figure 9 – Comparing sample-to-payload compilation time
The malware implements several encryption schemes to conceal its functionality and data from the victims, and Anti-Virus vendors. In order to successfully analyze the malware and its components, we had to automate the decryption process for all the variants.
The following table shows the different decryption and decoding methods:
Encrypted Data | Algorithm | Key Source |
Network Data | Base64 + RC4 | KEY = SHA1(ENCRYPTED[0:16] + "jHxastDcds)oMc=jvh7wdUhxcsdt2") |
Payload (Resource “307”) | RC4 + Custom Compression | KEY = ENCRYPTED[0:20] |
Javascript Updater File | RC4 + Custom Compression | KEY = ENCRYPTED[0:20] |
Bot List (Resource “311”) and Initial Configuration (Resource “308”) | RC4 | KEY = ENCRYPTED[0:20] |
Configuration “.dat” File, Web-Inject File, Hooking Module | RC4 | KEY = SHA1(EXE_NAME) |
Stolen Information “.dll” File | RC4 | srand(CRC32(BOT_ID)) KEY = RANDOM_STRING_32 |
Qbot’s initial payload has been covered extensively by fellow malware researchers. The latest versions have implemented several typical malware components to reduce its visibility and toughen its analysis:
Packer – The executable has been reconstructed using a packer.
Random Directory Name – Creating a working directory with randomized directory and file names to avoid file signatures. Directory location is %APPDATA%\Microsoft
. Working directory example can be observed at Figure 11.
String Encryption – Containing encrypted strings using XOR encryption (applies also to other modules).
Dynamic Import Table – Import table built dynamically based on encrypted strings (applies also to other modules).
Anti-VM and Anti-Debug Techniques:
Fiddler.exe;samp1e.exe;sample.exe;runsample.exe;lordpe.exe;regshot.exe;Autoruns.exe;dsniff.exe;VBoxTray.exe;HashMyFiles.exe;ProcessHacker.exe;Procmon.exe;Procmon64.exe;netmon.exe;vmtoolsd.exe;vm3dservice.exe;VGAuthService.exe;pr0c3xp.exe;ProcessHacker.exe;
CFF Explorer.exe;dumpcap.exe;Wireshark.exe;idaq.exe;idaq64.exe;TPAutoConnect.exe;ResourceHacker.exe;vmacthlp.exe;OLLYDBG.EXE;windbg.exe;bds-vision-agent-nai.exe;bds-vision-apis.exe;
bds-vision-agent-app.exe;MultiAnalysis_v1.0.294.exe;x32dbg.exe;VBoxTray.exe;VBoxService.exe;Tcpview.exe
Figure 10 – Device driver Anti-VM technique
CPUID
instructionPersistence – Achieving persistence through registry values and task scheduler.
Whenever the malware decides it is safe to run on the targeted system, it decrypts its resource “307” as explained above, injects it into newly created process explorer.exe
, calls a loader procedure which loads the DLL and calls the DllEntryPoint
of the main payload.
The main payload has multiple roles:
The payload has several more internal modules that we won’t elaborate on in this article such as – lateral movement capabilities, certificates harvesting, spam bot, and more.
The malware constructs its configuration out of several embedded resources that are unpacked and decrypted on runtime. The resources are:
The working directory, as we can see in Figure 11, is an important part of the Qbot’s functionality, and is also used as a synchronization method between the modules.
Figure 11 – Example for Qbot’s working directory
Qbot’s configuration files (end with .dat
) and stolen information files (end with .dll
) are the most crucial. These files are accessed and loaded by all of its modules.
Figure 12 – Configuration file
Figure 13 – Stolen information file
One of the questions we were asking ourselves at this point of the research was where can we find the real “banking” logic. Older versions of Qbot contained multiple malicious modules as embedded resources, but recent versions were rather “clean”.
To understand that, we had to dive deeper into the communication protocol and find methods to fetch the malicious modules.
Resource “311”, as we stated previously, contains a list of 150 IP addresses of other bots for the victim to communicate with. Each of these bots will forward the traffic to the real C&C server or to a second-tier proxy as we will show later. This method is an efficient way of hiding the C&C IP address.
All the following messages are sent via POST
method to the next URL: https://<BOT_IP>:<BOT_PORT>/t3
and are encrypted with a random initialization value. To make it easier understanding the logic, we will show only the decrypted network data.
The C&C communication data is sent in JSON format, where each property is identified by unique numerical ID. As we can see later in sample messages, most important JSON property is its message code which holds the key 8
. We were able to map the next unique message codes:
Victim → C&C:
1
– Request the next command from C&C.
2
– Ack for a command given by C&C.
4
– Computer configuration and process enumeration.
7
– Report of stolen information.
8
– Open ports message.
9
– Keep-alive message.
C&C → Victim:
5
– Server Ack.
6
– Command to execute.
The program holds two parallel networking loops – Keep-alive and report session, and Command Execution Session.
This session is pretty simple. The program alternates between keep-alive message to stolen information report message. For each such message, it will receive a server ack. These messages will look as follows:
Keep-alive Message
// Victim -> C&C { "8": 9, // MSG code "1": 17, // Network protocol version "2": "powqdc619830" // Victim BOT ID }
Report Stolen Information Message
Takes the encrypted .dll
file of the stolen information, and sends it.
// Victim -> C&C { "8": 7, // MSG code "1": 17, // Network protocol version "2": "powqdc619830", // Victim BOT ID "3": "spx145", // Bot group "6": 223, "7": 4763, "36": "617c...icR67==" // Base64 encoded and encrypted information }
Keep-alive and Report Response
// C&C -> Victim { "8": 5, // MSG code "16": 270544960, // Victim IP address "39": "mzJzbJU", // Random data "38": 1 }
The malware will request new commands periodically and execute them according to the following command table. The table contains the appropriate command ID and its handler.
Figure 14 – Qbot’s command table
The command request message will have the next structure:
{ "8": 1, // MSG code "1": 17, // Network protocol version "2": "powqdc619830", // Victim BOT ID "3": "b", // Bot group "4": 804, // Payload major version "5": 141, // Payload minor vesion "10": "1582872269", // Timestamp "6": 6210, "7": 6278, "14": "U3HphEKFiQcKFFe0LUVZNDO9vsJ9zdEf09" }
A typical response would look like the following:
{ "8": 6, // MSG code "15": "...", "16": 270544960, // Victim IP address "18": 252, "19": 31, // command ID to execute "20": ["TVqQAAM...="], // command payload "39": "<RANDOM_STRING>" // Random data }
During the research we were able to map several downloaded modules, some of which were newly added as we could see in version break down.
We noticed that whenever a new Bot ID is being “registered” by the C&C server, on the next command request it will receive the next modules to download and install:
Executable Update – Updates the current executable with a newer version or newer bot list. The C&C periodically pushes updates to all of its victims.
Email Collector Module – Extracts all e-mail threads from the victim’s Outlook client by using MAPI32.dll
API, and uploads it to a hardcoded remote server. These stolen e-mails will be utilized for the malspam to come later.
Figure 15 – Email collector module
Hooking Module – The module injects itself to all running processes, and hooks relevant API functions. Sample hooking table:
Figure 16 – Hooking module
Web-Inject File – The file provides the injector module with a list of websites and JavaScript code that will be injected if the victim visits any of these websites.
We can see the results of visiting one of the actor’s targets – Chase Bank.
Figure 17 – HTML source code example for an injected target
Password Grabber Module – a large module that downloads Mimikatz and tries to harvest passwords.
Figure 18 – Password grabber module
hVNC Plugin – Allows controlling the victim machine through a remote VNC connection. That is, an external operator can perform bank transactions without the user’s knowledge, even while he is logged into his computer. The module shares a high percentage of code with similar modules like TrickBot’s hVNC.
Figure 19 – Hidden VNC plugin
JS Updater Loader – Decrypts and writes a Javascript updater script. Until recently, the script came as an encrypted resource inside the payload. Because the script contains encrypted hardcoded URLs, the new method makes it easier for the operator to push updated domains to the victims.
We wrote a Python script for ease URL extraction from a given script which can be observed in Appendix C.
Figure 20 – JS updater script example
Cookie Grabber Module – targets popular browsers: IE, Edge, Chrome, and Firefox.
Figure 21 – Cookie grabber module
We can identify these modules through a traffic capture program:
Figure 22 – Downloaded modules in Fiddler
Once the victim has been infected, their computer is compromised, and they are also a potential threat to other computers in the local network because of Qbot’s lateral movement capabilities. The malware then checks whether the victim can also be a potential bot as part of Qbot’s infrastructure.
McAfee has published a great article 3 years ago in which they covered important details regarding the bot proxy module. To understand the complete infection chain process we felt there is more to discover regarding that module, and ways of fetching it.
To reach that goal, we started analyzing Qbot’s efforts of converting an innocent victim machine into an active bot, and being part of the C&C infrastructure. To do so, the malware does the following:
https://<BOT_IP>:<BOT_PORT>/bot_serv
cmd=1&msg=J3zeJrBLh2sGU4ql0EIr9MncSBCnK&ports=443,995,993,465,990,22,2222,2078,2083,2087,1194,8443,20,21,53,80,3389,6881,6882,6883,32100,32101,32102,32103,50000,50001,50002,50003,50010,61200,61201,61202
msg
variable), then it’s a sign of a successful incoming connection.When the program finishes verifying its potential ports, it forms message code 8
and sends it to the C&C server:
{ "8": 8, // MSG code "1": 17, // Network protocol version "2": "jnugfv895664", // Victim BOT ID "4": 3, "5": 111, "55": 270544960, // External IP of the potential bot "56": [443, 995, 993, 465, 990, 22, 2222, 2078, 2083, 2087, 1194, 8443, 20, 21, 53, 80, 3389, 6881, 6882, 6883, 32100, 32101, 32102, 32103, 50000, 50001, 50002, 50003, 50010, 61200, 61201, 61202] // Potential ports }
When the program does this specific process, we could observe that on the next command execution request, we will receive a proxy module installation with the relevant port to listen:
{ "8": 6, // MSG code ... "19": 25, // command ID "20": ["TVqQAAM...=", "prt=443", "n=jnugfv895664"], // command payload ... }
We can visualize the process with the next diagram, and observe it through a traffic capture program:
Figure 23 – Network flow for proxy module download
Figure 24 – Downloaded proxy module in Fiddler
The proxy module is loaded by rundll32.exe
, and copied into its working folder – C:\ProgramData\FilesystemMonitor\
. If it is given SYSTEM
privileges, it creates a new service named fsmon
, otherwise updates CurrentVersion\Run
registry value.
Most of the module’s codebase is taken from the following open-source libraries:
libcurl 7.47.1
for HTTP requests.OpenSSL 1.0.2r 26 Feb 2019
– Used for certificate creation, and signature validation.miniupnp
– For port opening.It also contains 3 hard-coded IP addresses of the second-tier proxy server.
The module hasn’t changed a lot since McAfee’s publication 3 years ago. The changes we could find were:
hwmon
to fsmon
.1.0.2f
to 1.0.2r
.One rather interesting feature of the proxy module is its control API. The threat group behind Qbot has developed a control API to the proxy which is independent of the malicious payload update mechanism. That API is also unique, mainly because it receives control messages by pushing and not by pulling, which could expose the bots to external actors’ control.
The protocol is rather simple and can be observed in the next diagram:
Figure 25 – Network flow for proxy module control API
The signature is being verified against the hardcoded public key of the actor. Hence, unless we possess the private key, the protocol is extremely hard to break.
This article analyzes two aspects of the threat – the campaign that leads to the infection of the victim, and the complex multi-layered malware which is constantly evolving. The article also covers several miscellaneous topics regarding its version history in the past year, decryption methods, communication samples, proxy server control API, and more.
These days Qbot is much more dangerous than it was previously – it has active malspam campaign which infects organizations, and it manages to use a “3rd party” infection infrastructure like Emotet’s to spread the threat even further. It seems like the threat group behind Qbot is evolving its techniques through the years, and Check Point Research hopes that the information in this article will help the researchers around the globe to mitigate and potentially stop Qbot’s activity.
Check Point SandBlast Agent protects against such attacks, and is capable of preventing them from the very first step.
Many Qbot and VBS samples were analyzed during the research. We’re attaching the recent samples and modules from 22/06/2020.
9001DF2C853B4BA118433DD83C17617E7AA368B1
– VBS Dropper449F2B10320115E98B182204A4376DDC669E1369
– Qbot Sample SPX145F85A63CB462B8FD60DA35807C63CD13226907901
– Mail Collector Module Loader [Decrypted]B4BC69FF502AECB4BBC2FB9A3DFC0CA8CF99BA9E
– Javascript Updater Loader [Decrypted]1AAA14A50C3C3F65269265C30D8AA05AD8695B1B
– Javascript Updater [Decrypted]577522512506487C63A372BBDA77BE966C23CBD1
– Hooking Module Loader [Decrypted]75107AEE398EED78532652B462B77AE6FB576198
– Cookie Grabber Module [Decrypted]674685F3EC24C72458EDC11CF4F135E445B4185B
– Password Grabber Module [Decrpyted]BECD8F2D6289B51981F07D5FF52916104D764DD5
– hVNC Module [Decrpyted]18E8971B2DE8EA3F8BB7E1462E414DA936425D4E
– Proxy Module Loader [Decrypted]4C96D2BCE0E12F8591999D4E00498BCDB8A116DE
– Proxy Module
ZIP File URL
hxxps://factory-hot[.]com/bafmxby/CcdEhoQGHq.zip
VBS Dropper URLs
hxxp://kiesow-auto[.]de/foojapfsyds/5555555.png
hxxp://test[.]africanamericangolfersdigest[.]com/kkmthjsvf/5555555.png
hxxp://frankiptv[.]com/liehyidqtu/5555555.png
hxxp://klubnika-malina[.]by/utgritefmjq/5555555.png
hxxp://centr-toshiba[.]by/wogvynkombk/5555555.png
hxxp://marokeconstruction[.]com[.]au/hhmzmlqct/5555555.png
Web-Inject URLs
hxxps://fortinet-cloud[.]com/wbj/br/content/chase/tom/ajax.js
hxxps://fortinet-cloud[.]com/wbj/br/content/key/tom/ajax.js
hxxps://fortinet-cloud[.]com/wbj/br/content/schwab/tom/schw.js
hxxps://fortinet-cloud[.]com/wbj/br/content/bbt/tom/bbt.js
hxxps://fortinet-cloud[.]com/wbj/att/js/AMAZON.js
hxxps://fortinet-cloud[.]com/wbj/crt/uadmin/inj_src/usa/amex2019/script.js
hxxps://fortinet-cloud[.]com/wbj/crt/uadmin/inj_src/usa/costco/costco.min.js
hxxps://fortinet-cloud[.]com/wbj/crt/uadmin/inj_src/usa/verizon/script.js
hxxps://fortinet-cloud[.]com/wbj/crt/uadmin/gate.php
hxxps://callunaconycatcher[.]com/bre/content/bmo/ins/bmo.js
hxxps://callunaconycatcher[.]com/bre/content/desjardins/ins/desjardins.js
hxxps://callunaconycatcher[.]com/bre/content/rbc/ins/rbc.js
hxxps://requirejscdn[.]com/*
hxxps://cersomab[.]com/lob.php
Mail Collector Remote Server
hxxps://82.118.22[.]125/bgate
Mimikatz URL Download
hxxps://onedrive.live[.]com/download.aspx?cid=CE32720D26AED2D5&authKey=%21AHHhrhk9od5OCBU&resid=CE32720D26AED2D5%21111&ithint=%2Eps1
Tier 2 Proxy Servers
46.228.199.235:443
93.88.75.176:443
207.244.112.112:443
Javascript Updater URLs
hxxp://backup.justthebooks[.]com/datacollectionservice.php3
hxxp://asn.crs.com[.]pa/datacollectionservice.php3
hxxp://chs.zarifbarbari[.]com/datacollectionservice.php3
Bot List
79.115.207.120:443
156.213.80.140:443
189.160.203.110:443
71.114.39.220:443
189.236.166.167:443
193.248.44.2:2222
206.51.202.106:50003
24.152.219.253:995
2.50.47.97:2222
108.49.221.180:443
207.246.75.201:443
80.240.26.178:443
199.247.16.80:443
207.255.161.8:2222
69.92.54.95:995
199.247.22.145:443
2.50.171.142:443
24.110.14.40:3389
79.101.130.104:995
94.52.160.116:443
172.243.155.62:443
188.192.75.8:443
175.111.128.234:443
74.129.18.56:443
36.77.151.211:443
203.45.104.33:443
118.160.162.77:443
86.126.97.183:2222
185.246.9.69:995
140.82.21.191:443
66.208.105.6:443
206.183.190.53:993
5.12.111.213:443
72.177.157.217:995
98.210.41.34:443
98.242.36.86:443
199.116.241.147:443
49.144.81.46:8443
75.110.250.89:995
219.76.148.142:443
70.174.3.241:443
71.205.158.156:443
78.96.192.26:443
108.190.151.108:2222
81.133.234.36:2222
12.5.37.3:995
210.61.141.92:443
173.70.165.101:995
5.13.84.186:995
68.46.142.48:443
188.27.6.170:443
188.173.70.18:443
86.124.13.101:443
5.13.74.26:443
68.190.152.98:443
96.56.237.174:990
175.143.12.8:443
79.113.224.85:443
2.51.240.61:995
95.76.27.89:443
5.12.243.211:443
24.183.39.93:443
86.124.228.254:443
5.193.178.241:2078
2.88.186.229:443
108.227.161.27:995
188.192.75.8:995
98.32.60.217:443
176.223.35.19:2222
24.42.14.241:443
70.95.118.217:443
68.225.56.31:443
191.84.11.112:443
72.204.242.138:50001
173.22.120.11:2222
64.121.114.87:443
68.60.221.169:465
92.17.167.87:2222
47.138.200.85:443
71.187.7.239:443
151.205.102.42:443
72.179.13.59:443
172.113.74.96:443
5.193.61.212:2222
47.28.135.155:443
188.26.243.186:443
41.228.206.99:443
117.218.208.239:443
203.122.7.82:443
39.36.61.58:995
49.207.105.25:443
59.124.10.133:443
89.44.196.211:443
79.117.129.171:21
24.110.96.149:443
184.90.139.176:2222
82.79.67.68:443
86.153.98.35:2222
101.108.4.251:443
209.182.122.217:443
89.32.220.79:443
104.50.141.139:995
85.204.189.105:443
94.10.81.239:443
211.24.72.253:443
110.142.205.182:443
86.124.105.88:443
72.90.243.117:0
41.225.231.43:443
87.65.204.240:995
62.121.123.57:443
47.153.115.154:990
66.30.92.147:443
49.191.4.245:443
47.180.66.10:443
97.93.211.17:443
65.100.247.6:2083
65.131.43.76:995
45.45.51.182:2222
98.219.77.197:443
166.62.180.194:2078
72.16.212.108:995
73.217.4.42:443
76.187.8.160:443
67.182.188.217:443
37.182.238.170:2222
117.216.227.70:443
74.222.204.82:443
89.137.77.237:443
82.77.169.118:2222
188.27.36.190:443
108.39.93.45:443
72.181.9.163:443
58.233.220.182:443
73.137.187.150:443
97.127.144.203:2222
103.76.160.110:443
37.156.243.67:995
67.246.16.250:995
182.185.7.220:995
82.81.172.21:443
117.199.6.105:443
216.163.4.132:443
199.102.55.87:53
96.244.45.155:443
122.147.204.4:443
89.45.107.209:443
35.142.12.163:2222
73.94.229.115:443
165.0.3.95:995
Proxy Service Name
fsmon
Proxy Service Display name
Filesystem Monitor
Proxy File Paths
C:\ProgramData\FilesystemMonitor\fsmonitor.dll
C:\ProgramData\FilesystemMonitor\fsmonitor.ini
Proxy Executable Command Line
C:\Windows\SysWOW64\rundll32.exe "C:\ProgramData\FilesystemMonitor\fsmonitor.dll",FsMonServerMainNT
C:\Windows\SysWOW64\rundll32.exe "C:\ProgramData\FilesystemMonitor\fsmonitor.dll",#1
Proxy RSA Public Key
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEA4zJC+AO8v7U9WGOdqeqMn9CPrdgoz//B+f/xxb4UnSNM1NJ1RwTG
N2jf6JRRD2gZz9735DU4I9FlIDEiRDdNn4OxX76L5eKe2GF4/etZ23DfuomMNXVw
qwYcO8A7zjzG0+ybQH35eNoYJMJDwPOBWb/nHBlPNWXoyv7u8EzScENMBpfKWuMW
UgmV08dulHPPyi9fjSsY3DLo5zNE6A8UEk2e2R2UkmiDbENOARgsfwHosyqEcBGc
Pk/+EismU1rsabaQV/sHw1zQQ9vAH+27d/T13hCuIgq1B3vRYFIrPkJYAdaxOwto
AHn0rjeAN4tEIdDQ10RCriEmnNEBfxA9BwIDAQAB
-----END RSA PUBLIC KEY-----
rule qbot_vbs { meta: description = "Catches QBot VBS files" author = "Alex Ilgayev" date = "2020-06-07" strings: $s3 = "ms.Send" $s4 = "for i=1 to 6" $s5 = "if ms.readyState = 4 Then" $s6 = "if len(ms.responseBody) <> 0 then" $s7 = /if left\(ms.responseText, \w*?\) = \"MZ\" then/ condition: filesize > 20MB and $s3 and $s4 and $s5 and $s6 and $s7 }
"""Qbot VBS URL extractor and de-obfuscator. This script is for research purposes, and far from production ready (missing exception handling and more). """ import re import os import sys def remove_additions(lines): """Removes stub calculations. Example: IZLmoJg = 277 + 15 + 23 + 468 - 345 - 18 - 471 - 15 + 617 Will be replaced with: IZLmoJg = 551 Args: lines (list): List of lines. Returns: list: List of modified lines. """ pattern = r'(([0-9]{1,15} [\+,\-] )+[0-9]{1,15})' new_file = [] for line in lines: line = line.strip() res = re.search(pattern, line) if res: new_line = re.sub(pattern, lambda x:str(eval(x.group(1))), line) else: new_line = line new_file.append(new_line) return new_file def remove_chr(lines): """Replaces "chr(*)" with their respective characters. Args: lines (list): List of lines. Returns: list: List of modified lines. """ pattern = r'[c,C]hr\((\d?\d?\d?)\)' new_file = [] for line in lines: line = line.strip() res = re.search(pattern, line) if res: new_line = re.sub(pattern, lambda match: '\"' + str(chr(int(match.group(1)))) + '\"', line) else: new_line = line new_file.append(new_line) return new_file def remove_replace(lines): """Replaces "replace(*)" with its respective string. Args: lines (list): List of lines. Returns: list: List of modified lines. """ pattern = r'replace\(\"(.*)\"\, \"(.*)\"\, \"(.*)\"\)' new_file = [] for line in lines: line = line.strip() res = re.search(pattern, line) if res: new_line = re.sub(pattern, lambda match: '\"' + match.group(1).replace(match.group(2), match.group(3)) + '\"' ,line) else: new_line = line new_file.append(new_line) return new_file def remove_concat(lines): """Replaces the VB concatenation sign "&" with the result string. Args: lines (list): List of lines. Returns: list: List of modified lines. """ pattern = r'\"(.*)\"\&\"(.*)\"' new_file = [] for line in lines: line = line.strip() res = re.search(pattern, line) if res: new_line = re.sub(pattern, lambda match: '\"' + match.group(1) + match.group(2) + '\"' ,line) else: new_line = line new_file.append(new_line) return new_file def remove_trailing_zeros(lines): """Removes all trailing NULL bytes from a file. Args: lines (list): List of lines. Returns: list: List of modified lines. """ new_file = [] for line in lines: if len(line) > 0 and line[0] == '\x00': continue new_file.append(line) return new_file def deobfuscate_file(fpath_in, fpath_out): """Converts Qbot VBS script into it's deobfuscated form. Main changes are: - removing stub calculations - converting "chr(*)" into their respective characters. - converting "replace(*)" into its respective string. - converting VB concatenations ("&") into the final string. - removing trailing NULL bytes. Args: fpath_in (str): Input VBS file path. fpath_out (str): Output file path. """ try: with open(fpath_in, 'r') as f_in: in_data = f_in.read() except: return None lines = in_data.split('\n') lines = remove_additions(lines) lines = remove_chr(lines) lines = remove_replace(lines) for _ in range(100): lines = remove_concat(lines) lines = remove_trailing_zeros(lines) new_file_joined = '\n'.join(lines) try: with open(fpath_out, 'w') as f_out: f_out.write(new_file_joined) except: return None def decrypt_data(enc_str, keys): """Decrypts long blob of text data. decryption method is looking for patterns of decimal numbers, and xor them with the key. do that with three different keys. Arguments: enc_str {string} -- encrypted data key1 {int} -- first key key2 {int} -- second key key3 {int} -- third key """ def _decrypt_data_inner(str_param, key_param): """Helper method. actual decryption. """ numbers = "" ret_decrypted = "" f = True for i in range(len(str_param)): if '0' <= str_param[i] <= '9': numbers = numbers + str_param[i] f = True else: if f: try: enc_ch = int(numbers) except: break dec_ch = enc_ch ^ key_param ret_decrypted = ret_decrypted + chr(dec_ch) numbers = "" f = False return ret_decrypted key1 = keys[0] key2 = keys[1] key3 = keys[2] enc_str = _decrypt_data_inner(enc_str, key1) enc_str = _decrypt_data_inner(enc_str, key2) return _decrypt_data_inner(enc_str, key3) class qbot_vbs(object): """Encapsulates qbot VBS artifacts. These artifacts are used for extraction. """ num_urls = 0 enc_str = None key_str = None seed = 0 key_idxs = [None, None, None] def __init__(self, data): self.data = data self.lines = data.split('\n') def _extract_number_urls(self): """Extracts number of encrypted urls. """ # sample: # number of urls: for i=1 to 6 pattern = r'[F,f]or i=1 to (\d+)' res = re.search(pattern, '\n'.join(self.lines)) self.num_urls = int(res.group(1)) def _extract_enc_str(self): """Extracts the string which has the encrypted data. should be the biggest line in the script. """ max_len = 0 max_idx = -1 for i, line in enumerate(self.lines): if len(line) > max_len and line[0] != '\x00': max_len = len(line) max_idx = i # removing variable name. res = re.search(r'^\w+ = \"(.*)\"$', self.lines[max_idx]) self.enc_str = res.group(1) def _extract_key_str(self): """Extracts the string which the key is based upon. should be called after `extract_enc_str()`. should be the second biggest line after encrypted string. """ second_max_len = 0 second_max_idx = -1 max_len = len(self.enc_str) for i, line in enumerate(self.lines): if len(line) > second_max_len and len(line) < max_len and line[0] != '\x00': second_max_len = len(line) second_max_idx = i # removing variable name. res = re.search(r'^\w+ = \"(.*)\"$', self.lines[second_max_idx]) self.key_str = res.group(1) def _extract_seed_and_key_indexes(self): """Helper function for key extraction. """ def _find_variables(var1_name, var2_name): """Finds variables values for two vars. for example: DgZlWOk = 8 jRryhge = 4 """ pattern1 = fr'^{var1_name} = (\d+)$' pattern2 = fr'^{var2_name} = (\d+)$' var1_value = None var2_value = None for line in self.lines: res = re.search(pattern1, line) if res: var1_value = res.group(1) res = re.search(pattern2, line) if res: var2_value = res.group(1) return var1_value, var2_value def _extract_key_indexes(lines): # we have 6 'Mid' encounters: # yaGlYs = Mid(xHAaMv, 10, 2) # RLquKjB = Asc(Mid(HIbAriX, seSclZ, 1)) # three times. # the second is not interesting for us. text = '\n'.join(lines) res = re.findall(r'Mid\(\w+\, (\w+)\, \w+\)', text) # the key creation order is reversed to their using. (first key3 is set and so on) self.key_idxs[0] = int(res[4]) self.key_idxs[1] = int(res[2]) self.key_idxs[2] = int(res[0]) # sample line: # For uLRYNs = 0 To 2387414 Step 1 pattern1 = r' (238\d\d\d\d) ' # sample line: # YLTCm = YLTCm + DgZlWOk - jRryhge pattern2 = r'^(\w+) = (\1) \+ (\w+) - (\w+)$' for i, line in enumerate(self.lines): res = re.search(pattern1, line) if res: num = int(res.group(1)) _extract_key_indexes(self.lines[i+1:]) for inner_line in self.lines[i+1:i+10]: res = re.search(pattern2, inner_line) if res: first_param = res.group(3) second_param = res.group(4) first_value, second_value = _find_variables(first_param, second_param) num += 1 num *= (int(first_value) - int(second_value)) self.seed = num return def extract_keys(self): """Main extraction method. Extracts keys for the URL decryption. Returns: list: List of 3 keys. """ vbs._extract_number_urls() vbs._extract_enc_str() vbs._extract_key_str() vbs._extract_seed_and_key_indexes() seed = self.seed * 999999 str_seed = str(seed) idx = int(str_seed[self.key_idxs[2] - 1:self.key_idxs[2] + 1]) key3 = ord(self.key_str[idx - 1]) idx = int(str_seed[self.key_idxs[1] - 1:self.key_idxs[1] + 1]) key2 = ord(self.key_str[idx - 1]) idx = int(str_seed[self.key_idxs[0] - 1:self.key_idxs[0] + 1]) key1 = ord(self.key_str[idx - 1]) return key1, key2, key3 if __name__ == "__main__": if len(sys.argv) != 2: print(f"Usage: python {os.path.basename(__file__)} <fpath_in>") exit(1) fname_tmp = 'tmp' deobfuscate_file(sys.argv[1], fname_tmp) if not os.path.exists(fname_tmp): print("Failed de-obfuscation script.") exit(0) with open(fname_tmp) as f: data = f.read() os.remove(fname_tmp) vbs = qbot_vbs(data) keys = vbs.extract_keys() dec = decrypt_data(vbs.enc_str, keys).strip('\ufeff').split('_______') for i in range(vbs.num_urls): url = dec[i] url = url.split('?')[0].strip() print(url)
import re import os def extract_urls_from_js_updater(js_data): """Extracts update URLs out of given Qbot Javascript updater. Args: js_data (str or bytes): Javascript code content. Returns: list: Returns list of extracted URLs or None if falied. """ try: if isinstance(js_data, bytes): js_data = js_data.decode('ascii') except: return None arrays = [] # var WcrApaqyDNEBJYsFkiXPVzHCeKGmnxd = [30,209...19]; # encrypted urls pattern = re.compile(r"^\s*var [a-zA-Z0-9]+\s?=\s?\[(([0-9]+,)+)([0-9]+)\];$") for line in js_data.splitlines(): match = pattern.match(line) if match: array = match.group(1) + match.group(3) arrays.append(array) if not len(arrays) == 2: return None suffix = 'datacollectionservice.php3' # encrypted text base_values = [int(c) for c in arrays[0].split(",")] # key xor_values = [int(c) for c in arrays[1].split(",")] res = "" for i in range(len(base_values)): res += chr(base_values[i] ^ xor_values[i % len(xor_values)]) servers = res.split(";") urls = ['http://' + server + '/' + suffix for server in servers] return urls