# An Old Bot’s Nasty New Tricks: Exploring Qbot’s Latest Attack Methods **research.checkpoint.com/2020/exploring-qbots-latest-attack-methods/** August 27, 2020 **Research By: Alex Ilgayev** ## Introduction August 27, 2020 **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: Stealing information from infected machines, including passwords, emails, credit card details and more. Installing other malware on infected machines, including ransomware Allowing the Bot controller to connect to the victim’s computer (even when the victim is logged in) to make banking transactions from the victim’s IP address Hijacking users’ legitimate email threads from their Outlook client and using those threads to try and infect other users’ PCs 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](https://blog.checkpoint.com/2020/08/07/julys-most-wanted-malware-emotet-strikes-again-after-five-month-absence/) 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](https://twitter.com/_CPResearch_/status/1289195232718880770) 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. B d i ibilit t f th tt k d i t US d E b d i ti i **Fi** **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 process – hijacked email threads and VBS downloaders. Its payload functionality and version break down. C&C communication protocol and module fetching. How a victim becomes a potential bot-proxy, and various methods that the Proxy module exposes. ## Infection Chain ----- QBot’s infection chain is described in the following flow-chart and will be discussed in the next sections: **Figure 3 – Infection chain diagram** ### Malicious Email The initial infection chain starts by sending specially crafted emails to the target organizations. The method is less sophisticated than spearphishing 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:](https://twitter.com/malware_traffic) **Figure 4 – Example of COVID-19 related email thread** ----- **Figure 5 – Example of email thread for tax payment reminder** ----- **gu e 6** a p e o e a t ead o job ec u t e t 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. ### VBS Infection 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. ## Qbot Payload ### Version Analysis 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** ### Decryption Schemes ----- e a a e p e e ts se e a e c ypt o sc e es to co cea ts u ct o a ty a d data o t e ct s, a d t us e do s o de 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 `KEY = ENCRYPTED[0:20]` Compression Javascript Updater File RC4 + Custom `KEY = ENCRYPTED[0:20]` Compression Bot List (Resource “311”) and Initial Configuration RC4 `KEY = ENCRYPTED[0:20]` (Resource “308”) Configuration “.dat” File, Web-Inject File, Hooking RC4 `KEY = SHA1(EXE_NAME)` Module Stolen Information “.dll” File RC4 `srand(CRC32(BOT_ID))` ``` KEY = RANDOM_STRING_32 ### Initial Payload ``` 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:** Latest versions are looking for VM-related artifacts on server-side. victim computer configuration is being enumerated and sent to the C2. Based on that information, the server decides whether is safe to “push” modules to the victim. Looking for “VMWare” port existence Looking for VM and analysis related processes. The latest versions also adds a long list of blacklisted analysis programs: ``` Fiddler.exe;samp1e.exe;sample.exe;runsample.exe;lordpe.exe;regshot.exe;Autoruns.exe;dsniff.exe;VBoxTray.exe;HashMyFiles CFF Explorer.exe;dumpcap.exe;Wireshark.exe;idaq.exe;idaq64.exe;TPAutoConnect.exe;ResourceHacker.exe;vmacthlp.exe;OLLYDBG.EXE vision-agent-nai.exe;bds-vision-apis.exe; bds-vision-agentapp.exe;MultiAnalysis_v1.0.294.exe;x32dbg.exe;VBoxTray.exe;VBoxService.exe;Tcpview.exe ``` Looking for VM related device drivers. Examples: **Figure 10 – Device driver Anti-VM technique** Looking for a VM through `CPUID instruction` Forcing exceptions to check if debugger is present Checking for sandbox signatures **P** **i t** A hi i i t th h i t l d t k h d l ----- e e e t e a a e dec des t s sa e to u o t e ta geted syste, t dec ypts ts esou ce 30 as e p a ed abo e, jects t to e y created process `explorer.exe, calls a loader procedure which loads the DLL and calls the` `DllEntryPoint of the main payload.` ### Main Payload The main payload has multiple roles: Creating and maintaining the configuration of the malware. Creating and maintaining the messaging mechanisms – Named pipes, events, and custom Windows messages. Installing and managing new modules – new feature. Creating and maintaining a proper communication channel with the C&C server. Executing commands through a custom thread queue mechanism. 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: “308” – Initial configuration data. “311” – List of 150 bots IP addresses and ports for building a communication tunnel. 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. ----- ## Communication Module 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://:/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. ### Keep-alive and Report 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 } ### Command Execution Session ``` ----- e a a e equest e co a ds pe od ca y a d e ecute t e acco d g to t e o o g co a d tab e e tab e co ta s t e 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 data } ### Module Fetching ``` 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** ----- **C** **ug** o s co t o g t e ct ac e t oug a e ote C co ect o at s, a e te a ope ato ca pe o ba 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. ## From a Victim to a Bot [McAfee has published a great article 3 years ago in which they covered important details regarding the bot proxy module. To understand the](https://www.mcafee.com/blogs/other-blogs/mcafee-labs/mcafee-discovers-pinkslipbot-exploiting-infected-machines-as-control-servers-releases-free-tool-to-detect-disable-trojan/) 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: Execute shell commands to allow incoming connections in the host firewall. Sending crafted UPnP commands to allow port forwarding. Whenever it creates the opened ports list, the program verifies whether the incoming connection is really allowed by sending the next message to a remote bot and waiting for a connection. URL – `https://:/bot_serv` Sample payload: ``` cmd=1&msg=J3zeJrBLh2sGU4ql0EIr9MncSBCnK&ports=443,995,993,465,990,22,2222,2078,2083,2087,1194,8443,20,21, ``` The remote bot tries to connect using the specified ports to the victim. If the victim receives the data they expected ( msg variable), then it’s a sign of a successful incoming connection. Remove the port listening. 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** ### Analyzing Proxy Module 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: ----- `bcu` o equests ``` 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: The service name, description, working folder, window name, and executable name has been changed. For example, the service name was changed from `hwmon to` `fsmon .` OpenSSL version has been upgraded from `1.0.2f to` `1.0.2r .` Updated Tier 2 Proxy servers. 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. ## Conclusion 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.](https://www.checkpoint.com/products/advanced-endpoint-protection/)** ## IOC ----- a y Qbot a d S sa p es e e a a y ed du g t e esea c e e attac g t e ece t sa p es a d odu es o /06/ 0 0 ### Hashes ``` 9001DF2C853B4BA118433DD83C17617E7AA368B1 – VBS Dropper 449F2B10320115E98B182204A4376DDC669E1369 – Qbot Sample SPX145 F85A63CB462B8FD60DA35807C63CD13226907901 – 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 Domains and IPs ``` **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 ``` **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 ``` **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 ``` **Bot List** ----- ``` 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 ``` ----- ``` 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 ``` ----- ### Other IOC **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---- ## Appendix ### Appendix A: YARA Rule for VBS Hunting 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 } Appendix B: VBS URL Extraction Script ``` ----- ``` 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) ``` ----- ``` 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 ``` ----- ``` 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 ``` ----- ``` 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_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__)} ") 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) ### Appendix C: JavaScript Updater URL Extraction Script ``` ----- ``` 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 = ['https://' + server + '/' + suffix for server in servers] return urls ``` -----