{
	"id": "a9387298-9442-49a0-a9d6-4b0b48055280",
	"created_at": "2026-04-06T00:06:39.065926Z",
	"updated_at": "2026-04-10T13:11:47.770031Z",
	"deleted_at": null,
	"sha1_hash": "b98e38c2710425cb24df7bb7d15af7279b4b7071",
	"title": "An Old Bot’s Nasty New Tricks: Exploring Qbot's Latest Attack Methods - Check Point Research",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 225945,
	"plain_text": "An Old Bot’s Nasty New Tricks: Exploring Qbot's Latest Attack Methods\r\n- Check Point Research\r\nBy Alex Ilgayev\r\nPublished: 2020-08-27 · Archived: 2026-04-05 13:47:38 UTC\r\nResearch By: Alex Ilgayev\r\nIntroduction\r\nThe notorious banking trojan Qbot has been in business for more than a decade. The malware, which has also been\r\ndubbed Qakbot and Pinkslipbot, was discovered in 2008 and is known for collecting browsing data and stealing banking\r\ncredentials and other financial information from victims. It is highly structured, multi-layered, and is being continuously\r\ndeveloped with new features to extend its capabilities. .These new ‘tricks’ mean that despite its age, Qbot is still a dangerous\r\nand persistent threat to organizations.  It has become the malware equivalent of a Swiss Army knife, capable of:\r\nStealing information from infected machines, including passwords, emails, credit card details and more.\r\nInstalling other malware on infected machines, including ransomware \r\nAllowing the Bot controller to connect to the victim’s computer (even when the victim is logged in) to make banking\r\ntransactions from the victim’s IP address\r\nHijacking users’ legitimate email threads from their Outlook client and using those threads to try and infect other\r\nusers’ PCs\r\nA prominent campaign using QBot ran from March to the end of June this year.   We assumed that the campaign was\r\nstopped to allow those behind QBot to conduct further malware development, but we did not imagine that it would return so\r\nquickly.\r\nTowards the end of July, one of today’s most serious cyber threats, the Emotet Trojan, returned to full activity and launched\r\nmultiple malspam campaigns, impacting 5% of organizations globally. Some of these campaigns included installing an\r\nupdated version of Qbot on victims’ PCs. A few days later, we identified a newer Qbot sample dropped by latest Emotet\r\ncampaign, which led us discovering a renewed command and control infrastructure and brand new malware techniques\r\ndistributed through Emotet’s infection process.\r\nIf that wasn’t enough for us, Qbot’s malspam campaign resumed earlier in August, spreading globally and infecting new\r\ntargets. One of Qbot’s new tricks is particularly nasty, as once a machine is infected, it activates a special ‘email collector\r\nmodule’ which extracts all email threads from the victim’s Outlook client, and uploads it to a hardcoded remote server.\r\nThese stolen emails are then utilized for future malspam campaigns, making it easier for users to be tricked into clicking on\r\ninfected attachments because the spam email appears to continue an existing legitimate email conversation.  Check Point’s\r\nresearchers have seen examples of targeted, hijacked email threads with subjects related to Covid-19, tax payment\r\nreminders, and job recruitments.\r\nBased on our visibility, most of the attacks were made against US- and Europe-based organizations as we can see in Figure\r\n1.\r\nhttps://research.checkpoint.com/2020/exploring-qbots-latest-attack-methods/\r\nPage 1 of 32\n\nFigure 1 – Attacked organizations by country\r\nAmong these, the most targeted industries were in the government, military, and manufacturing sectors.\r\nFigure 2 – Attacked Organizations by industry\r\nAfter a thorough analysis of these new QBot samples, we will share our knowledge and insights about the following topics:\r\nQbot’s infection process – hijacked email threads and VBS downloaders.\r\nIts payload functionality and version break down.\r\nC\u0026C communication protocol and module fetching.\r\nHow a victim becomes a potential bot-proxy, and various methods that the Proxy module exposes.\r\nInfection Chain\r\nQBot’s infection chain is described in the following flow-chart and will be discussed in the next sections:\r\nhttps://research.checkpoint.com/2020/exploring-qbots-latest-attack-methods/\r\nPage 2 of 32\n\nFigure 3 – Infection chain diagram\r\nMalicious Email\r\nThe initial infection chain starts by sending specially crafted emails to the target organizations. The method is less\r\nsophisticated than spear-phishing techniques but has additional attributes which add to its credibility. One of these is called\r\n“Hijacked Email Threads” – capturing archived email conversations and replying to the sender with the malicious content.\r\nThose conversations could be captured using Qbot’s Email Collector module which we will describe later.\r\nWe can see in Figure 4, Figure 5, and Figure 6, examples of such methods from samples submitted by @malware_traffic in\r\ntheir blog:\r\nFigure 4 – Example of COVID-19 related email thread\r\nFigure 5 – Example of email thread for tax payment reminder\r\nhttps://research.checkpoint.com/2020/exploring-qbots-latest-attack-methods/\r\nPage 3 of 32\n\nFigure 6 – Example of email thread for job recruitment\r\nEach of these emails contains a URL to a ZIP with a malicious VBS – Visual Basic Script file.\r\nDuring our tracking of the malspam campaign, we have seen hundreds of different URLs for malicious ZIP dropping when\r\nmost of them were compromised WordPress sites.\r\nVBS Infection\r\nThe VBS based infection method is rather new for the malware, and is being used since April 2020. In previous campaigns,\r\nthe infection chain started with a Word document containing malicious macros.\r\nWhile the previous macros had simple obfuscation and string decoding, the VBS file contains several more advanced\r\nmethods:\r\nFile Size – The file size is larger than 35MB, padded with NULL bytes. Big files are usually dismissed by various\r\nsandboxes due to performance limitations.\r\nSleep Timer – The script delays its execution by calling the Sleep API. This is another method for avoiding sandboxes.\r\nObfuscation – The script contains multiple obfuscation methods such as those described in Figure 7.\r\nFigure 7 – VBS obfuscation methods\r\nEncryption – The VBS file downloads the Qbot payload from one of 6 possible hardcoded encrypted URLs. These URLs\r\nare encrypted by a custom XOR encryption 3 times with different keys that are built dynamically. We created an extraction\r\nscript that can be accessed in Appendix B.\r\nIn order to support detection and hunting for additional malicious VBS files, We wrote a YARA rule which can be observed\r\nin Appendix A.\r\nhttps://research.checkpoint.com/2020/exploring-qbots-latest-attack-methods/\r\nPage 4 of 32\n\nSimilar to the old infection method, the VBS file downloads and executes the Qbot payload.\r\nQbot Payload\r\nVersion Analysis\r\nIn the course of our analysis, Qbot’s operators frequently upgraded its versions and encouraged us to track and analyze the\r\nchanges in each and every version. The fact that the developers left a version tag marked in the samples, allowed us to\r\nperform this analysis easier.\r\nFor example, let’s have a look at the version tag as it shown in the unpacked sample below:\r\nFigure 8 – Sample’s major and minor version\r\nFrom that, we can deduct that the initial payload version is 325/5 , while the main payload version is 325/7 . (The major\r\nversion is read as a hex value)\r\nOver the last few months, we tracked the different versions of Qbot and identified some of the differences in each version, as\r\ncan be seen in the following table.\r\nMajor Minor\r\nPayload\r\nMinor\r\nVersion\r\nTimestamp\r\n Notes\r\n324 44 8 Jan 22, 2020 First version seen for major version 324.\r\n324 353 53 Mar 3, 2020  \r\n324 375 65 Mar 13, 2020  \r\n324 379 70 Mar 20, 2020 Added command 35 supporting hVNC module.\r\n324 383 74 Apr 1, 2020  \r\n324 385 75 Apr 1, 2020  \r\n324 388 79 Apr 8, 2020 Added command 10 – terminate process by name.\r\n324 390 127 Apr 10, 2020  \r\n324 393 136 Apr 29, 2020\r\nJS Updater resource is no longer included. JS\r\nUpdate commands has been respectively\r\nadjusted.\r\n324 399 141 May 7, 2020\r\nAdded long list of blacklisted analysis programs\r\npart of anti-VM method.\r\n324 401 142 May 28, 2020  \r\n325 5 7 July 29, 2020\r\nIntroduced new anti-analysis techniques.\r\nAdded anti-VM checks on server-side.\r\n325 7 13 July 31, 2020  \r\n325 8 14 August 3, 2020  \r\n325 35 42 August 7, 2020  \r\n325 37 43\r\nAugust 11,\r\n2020\r\nLast known version up to the writing of this\r\narticle.\r\nThe date mentioned for each sample is based on the executable compilation time attribute. That field can be changed via\r\ntimestomping, but we suspect that it wasn’t forged in these cases.\r\nhttps://research.checkpoint.com/2020/exploring-qbots-latest-attack-methods/\r\nPage 5 of 32\n\nWe also tracked the timestamps of the main payload, and seen that the compilation time was consistently minutes apart from\r\nthe initial payload’s:\r\nFigure 9 – Comparing sample-to-payload compilation time\r\nDecryption Schemes\r\nThe malware implements several encryption schemes to conceal its functionality and data from the victims, and Anti-Virus\r\nvendors. In order to successfully analyze the malware and its components, we had to automate the decryption process for all\r\nthe variants.\r\nThe following table shows the different decryption and decoding methods:\r\nEncrypted Data Algorithm Key Source\r\nNetwork Data Base64 + RC4\r\nKEY = SHA1(ENCRYPTED[0:16] +\r\n\"jHxastDcds)oMc=jvh7wdUhxcsdt2\")\r\nPayload (Resource “307”)\r\nRC4 + Custom\r\nCompression\r\nKEY = ENCRYPTED[0:20]\r\nJavascript Updater File\r\nRC4 + Custom\r\nCompression\r\nKEY = ENCRYPTED[0:20]\r\nBot List (Resource “311”) and\r\nInitial Configuration (Resource\r\n“308”)\r\nRC4 KEY = ENCRYPTED[0:20]\r\nConfiguration “.dat” File, Web-Inject File, Hooking Module\r\nRC4 KEY = SHA1(EXE_NAME)\r\nStolen Information “.dll” File RC4\r\nsrand(CRC32(BOT_ID))\r\nKEY = RANDOM_STRING_32\r\nInitial Payload\r\nQbot’s initial payload has been covered extensively by fellow malware researchers. The latest versions have implemented\r\nseveral typical malware components to reduce its visibility and toughen its analysis:\r\nPacker – The executable has been reconstructed using a packer.\r\nRandom Directory Name – Creating a working directory with randomized directory and file names to avoid file signatures.\r\nDirectory location is %APPDATA%\\Microsoft . Working directory example can be observed at Figure 11.\r\nString Encryption – Containing encrypted strings using XOR encryption (applies also to other modules).\r\nDynamic Import Table – Import table built dynamically based on encrypted strings (applies also to other modules).\r\nAnti-VM and Anti-Debug Techniques:\r\nLatest versions are looking for VM-related artifacts on server-side. victim computer configuration is being\r\nenumerated and sent to the C2. Based on that information, the server decides whether is safe to “push” modules to\r\nthe victim.\r\nLooking for “VMWare” port existence\r\nLooking for VM and analysis related processes. The latest versions also adds a long list of blacklisted analysis\r\nprograms:\r\nFiddler.exe;samp1e.exe;sample.exe;runsample.exe;lordpe.exe;regshot.exe;Autoruns.exe;dsniff.exe;VBoxTray.exe;HashMyFiles.exe;ProcessHacke\r\nCFF\r\nExplorer.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\r\nhttps://research.checkpoint.com/2020/exploring-qbots-latest-attack-methods/\r\nPage 6 of 32\n\nLooking for VM related device drivers. Examples:\r\nFigure 10 – Device driver Anti-VM technique\r\nLooking for a VM through CPUID instruction\r\nForcing exceptions to check if debugger is present\r\nChecking for sandbox signatures\r\nPersistence – Achieving persistence through registry values and task scheduler.\r\nWhenever the malware decides it is safe to run on the targeted system, it decrypts its resource “307” as explained above,\r\ninjects it into newly created process explorer.exe , calls a loader procedure which loads the DLL and calls the\r\nDllEntryPoint of the main payload.\r\nMain Payload\r\nThe main payload has multiple roles:\r\nCreating and maintaining the configuration of the malware.\r\nCreating and maintaining the messaging mechanisms – Named pipes, events, and custom Windows messages.\r\nInstalling and managing new modules – new feature.\r\nCreating and maintaining a proper communication channel with the C\u0026C server.\r\nExecuting commands through a custom thread queue mechanism.\r\nThe payload has several more internal modules that we won’t elaborate on in this article such as – lateral movement\r\ncapabilities, certificates harvesting, spam bot, and more.\r\nThe malware constructs its configuration out of several embedded resources that are unpacked and decrypted on runtime.\r\nThe resources are:\r\n“308” – Initial configuration data.\r\n“311” – List of 150 bots IP addresses and ports for building a communication tunnel.\r\nThe working directory, as we can see in Figure 11, is an important part of the Qbot’s functionality, and is also used as a\r\nsynchronization method between the modules.\r\nFigure 11 – Example for Qbot’s working directory\r\nQbot’s configuration files (end with .dat ) and stolen information files (end with .dll ) are the most crucial. These files\r\nare accessed and loaded by all of its modules.\r\nhttps://research.checkpoint.com/2020/exploring-qbots-latest-attack-methods/\r\nPage 7 of 32\n\nFigure 12 – Configuration file\r\nFigure 13 – Stolen information file\r\nOne of the questions we were asking ourselves at this point of the research was where can we find the real “banking” logic.\r\nOlder versions of Qbot contained multiple malicious modules as embedded resources, but recent versions were rather\r\n“clean”.\r\nTo understand that, we had to dive deeper into the communication protocol and find methods to fetch the malicious modules.\r\nCommunication Module\r\nResource “311”, as we stated previously, contains a list of 150 IP addresses of other bots for the victim to communicate\r\nwith. Each of these bots will forward the traffic to the real C\u0026C server or to a second-tier proxy as we will show later. This\r\nmethod is an efficient way of hiding the C\u0026C IP address.\r\nAll the following messages are sent via POST method to the next URL: https://\u003cBOT_IP\u003e:\u003cBOT_PORT\u003e/t3 and are\r\nencrypted with a random initialization value. To make it easier understanding the logic, we will show only the decrypted\r\nnetwork data.\r\nThe C\u0026C communication data is sent in JSON format, where each property is identified by unique numerical ID. As we can\r\nsee later in sample messages, most important JSON property is its message code which holds the key 8 . We were able to\r\nmap the next unique message codes:\r\nVictim → C\u0026C:\r\n1 – Request the next command from C\u0026C.\r\n2 – Ack for a command given by C\u0026C.\r\n4 – Computer configuration and process enumeration.\r\n7 – Report of stolen information.\r\n8 – Open ports message.\r\n9 – Keep-alive message.\r\nC\u0026C → Victim:\r\n5 – Server Ack.\r\n6 – Command to execute.\r\nThe program holds two parallel networking loops – Keep-alive and report session, and Command Execution Session.\r\nhttps://research.checkpoint.com/2020/exploring-qbots-latest-attack-methods/\r\nPage 8 of 32\n\nKeep-alive and Report Session\r\nThis session is pretty simple. The program alternates between keep-alive message to stolen information report message. For\r\neach such message, it will receive a server ack. These messages will look as follows:\r\nKeep-alive Message\r\n\"1\": 17, // Network protocol version\r\n\"2\": \"powqdc619830\" // Victim BOT ID\r\n// Victim -\u003e C\u0026C { \"8\": 9, // MSG code \"1\": 17, // Network protocol version \"2\": \"powqdc619830\" // Victim BOT ID }\r\n// Victim -\u003e C\u0026C\r\n{\r\n \"8\": 9, // MSG code\r\n \"1\": 17, // Network protocol version\r\n \"2\": \"powqdc619830\" // Victim BOT ID\r\n}\r\nReport Stolen Information Message\r\nTakes the encrypted .dll file of the stolen information, and sends it.\r\n\"1\": 17, // Network protocol version\r\n\"2\": \"powqdc619830\", // Victim BOT ID\r\n\"3\": \"spx145\", // Bot group\r\n\"36\": \"617c...icR67==\" // Base64 encoded and encrypted information\r\n// Victim -\u003e C\u0026C { \"8\": 7, // MSG code \"1\": 17, // Network protocol version \"2\": \"powqdc619830\", // Victim BOT ID \"3\":\r\n\"spx145\", // Bot group \"6\": 223, \"7\": 4763, \"36\": \"617c...icR67==\" // Base64 encoded and encrypted information }\r\n// Victim -\u003e C\u0026C\r\n{\r\n \"8\": 7, // MSG code\r\n \"1\": 17, // Network protocol version\r\n \"2\": \"powqdc619830\", // Victim BOT ID\r\n \"3\": \"spx145\", // Bot group\r\n \"6\": 223,\r\n \"7\": 4763,\r\n \"36\": \"617c...icR67==\" // Base64 encoded and encrypted information\r\n}\r\nKeep-alive and Report Response\r\n\"16\": 270544960, // Victim IP address\r\n\"39\": \"mzJzbJU\", // Random data\r\n// C\u0026C -\u003e Victim { \"8\": 5, // MSG code \"16\": 270544960, // Victim IP address \"39\": \"mzJzbJU\", // Random data \"38\": 1 }\r\n// C\u0026C -\u003e Victim\r\n{\r\n \"8\": 5, // MSG code\r\n \"16\": 270544960, // Victim IP address\r\n \"39\": \"mzJzbJU\", // Random data\r\n \"38\": 1\r\n}\r\nCommand Execution Session\r\nThe malware will request new commands periodically and execute them according to the following command table. The\r\ntable contains the appropriate command ID and its handler.\r\nhttps://research.checkpoint.com/2020/exploring-qbots-latest-attack-methods/\r\nPage 9 of 32\n\nFigure 14 – Qbot’s command table\r\nThe command request message will have the next structure:\r\n\"1\": 17, // Network protocol version\r\n\"2\": \"powqdc619830\", // Victim BOT ID\r\n\"4\": 804, // Payload major version\r\n\"5\": 141, // Payload minor vesion\r\n\"10\": \"1582872269\", // Timestamp\r\n\"14\": \"U3HphEKFiQcKFFe0LUVZNDO9vsJ9zdEf09\"\r\n{ \"8\": 1, // MSG code \"1\": 17, // Network protocol version \"2\": \"powqdc619830\", // Victim BOT ID \"3\": \"b\", // Bot group\r\n\"4\": 804, // Payload major version \"5\": 141, // Payload minor vesion \"10\": \"1582872269\", // Timestamp \"6\": 6210, \"7\":\r\n6278, \"14\": \"U3HphEKFiQcKFFe0LUVZNDO9vsJ9zdEf09\" }\r\n{\r\n \"8\": 1, // MSG code\r\n \"1\": 17, // Network protocol version\r\n \"2\": \"powqdc619830\", // Victim BOT ID\r\n \"3\": \"b\", // Bot group\r\n \"4\": 804, // Payload major version\r\n \"5\": 141, // Payload minor vesion\r\n \"10\": \"1582872269\", // Timestamp\r\n \"6\": 6210,\r\n \"7\": 6278,\r\n \"14\": \"U3HphEKFiQcKFFe0LUVZNDO9vsJ9zdEf09\"\r\n}\r\nA typical response would look like the following:\r\n\"16\": 270544960, // Victim IP address\r\n\"19\": 31, // command ID to execute\r\n\"20\": [\"TVqQAAM...=\"], // command payload\r\n\"39\": \"\u003cRANDOM_STRING\u003e\" // Random data\r\n{ \"8\": 6, // MSG code \"15\": \"...\", \"16\": 270544960, // Victim IP address \"18\": 252, \"19\": 31, // command ID to execute \"20\":\r\n[\"TVqQAAM...=\"], // command payload \"39\": \"\u003cRANDOM_STRING\u003e\" // Random data }\r\n{\r\n \"8\": 6, // MSG code\r\n \"15\": \"...\",\r\n \"16\": 270544960, // Victim IP address\r\nhttps://research.checkpoint.com/2020/exploring-qbots-latest-attack-methods/\r\nPage 10 of 32\n\n\"18\": 252,\r\n \"19\": 31, // command ID to execute\r\n \"20\": [\"TVqQAAM...=\"], // command payload\r\n \"39\": \"\u003cRANDOM_STRING\u003e\" // Random data\r\n}\r\nModule Fetching\r\nDuring the research we were able to map several downloaded modules, some of which were newly added as we could see in\r\nversion break down.\r\nWe noticed that whenever a new Bot ID is being “registered” by the C\u0026C server, on the next command request it will\r\nreceive the next modules to download and install:\r\nExecutable Update – Updates the current executable with a newer version or newer bot list. The C\u0026C periodically pushes\r\nupdates to all of its victims.\r\nEmail Collector Module – Extracts all e-mail threads from the victim’s Outlook client by using MAPI32.dll API, and\r\nuploads it to a hardcoded remote server. These stolen e-mails will be utilized for the malspam to come later.\r\nFigure 15 – Email collector module\r\nHooking Module – The module injects itself to all running processes, and hooks relevant API functions. Sample hooking\r\ntable:\r\nFigure 16 – Hooking module\r\nWeb-Inject File – The file provides the injector module with a list of websites and JavaScript code that will be injected if\r\nthe victim visits any of these websites.\r\nWe can see the results of visiting one of the actor’s targets – Chase Bank.\r\nFigure 17 – HTML source code example for an injected target\r\nPassword Grabber Module – a large module that downloads Mimikatz and tries to harvest passwords.\r\nhttps://research.checkpoint.com/2020/exploring-qbots-latest-attack-methods/\r\nPage 11 of 32\n\nFigure 18 – Password grabber module\r\nhVNC Plugin – Allows controlling the victim machine through a remote VNC connection. That is, an external operator can\r\nperform bank transactions without the user’s knowledge, even while he is logged into his computer. The module shares a\r\nhigh percentage of code with similar modules like TrickBot’s hVNC.\r\nFigure 19 – Hidden VNC plugin\r\nJS Updater Loader – Decrypts and writes a Javascript updater script. Until recently, the script came as an encrypted\r\nresource inside the payload. Because the script contains encrypted hardcoded URLs, the new method makes it easier for the\r\noperator to push updated domains to the victims.\r\nWe wrote a Python script for ease URL extraction from a given script which can be observed in Appendix C.\r\nFigure 20 – JS updater script example\r\nCookie Grabber Module – targets popular browsers: IE, Edge, Chrome, and Firefox.\r\nFigure 21 – Cookie grabber module\r\nWe can identify these modules through a traffic capture program:\r\nhttps://research.checkpoint.com/2020/exploring-qbots-latest-attack-methods/\r\nPage 12 of 32\n\nFigure 22 – Downloaded modules in Fiddler\r\nOnce the victim has been infected, their computer is compromised, and they are also a potential threat to other computers in\r\nthe local network because of Qbot’s lateral movement capabilities. The malware then checks whether the victim can also be\r\na potential bot as part of Qbot’s infrastructure.\r\nFrom a Victim to a Bot\r\nMcAfee has published a great article 3 years ago in which they covered important details regarding the bot proxy module.\r\nTo understand the complete infection chain process we felt there is more to discover regarding that module, and ways of\r\nfetching it.\r\nTo reach that goal, we started analyzing Qbot’s efforts of converting an innocent victim machine into an active bot, and\r\nbeing part of the C\u0026C infrastructure. To do so, the malware does the following:\r\nExecute shell commands to allow incoming connections in the host firewall.\r\nSending crafted UPnP commands to allow port forwarding.\r\nWhenever it creates the opened ports list, the program verifies whether the incoming connection is really allowed by\r\nsending the next message to a remote bot and waiting for a connection.\r\nURL – https://\u003cBOT_IP\u003e:\u003cBOT_PORT\u003e/bot_serv\r\nSample payload:\r\ncmd=1\u0026msg=J3zeJrBLh2sGU4ql0EIr9MncSBCnK\u0026ports=443,995,993,465,990,22,2222,2078,2083,2087,1194,8443,20,21,53,80,3389,6\r\nThe remote bot tries to connect using the specified ports to the victim. If the victim receives the data they expected\r\n( msg variable), then it’s a sign of a successful incoming connection.\r\nRemove the port listening.\r\nWhen the program finishes verifying its potential ports, it forms message code 8 and sends it to the C\u0026C server:\r\n\"1\": 17, // Network protocol version\r\n\"2\": \"jnugfv895664\", // Victim BOT ID\r\n\"55\": 270544960, // External IP of the potential bot\r\nhttps://research.checkpoint.com/2020/exploring-qbots-latest-attack-methods/\r\nPage 13 of 32\n\n\"56\": [443, 995, 993, 465, 990, 22, 2222, 2078, 2083, 2087, 1194, 8443, 20, 21, 53, 80, 3389, 6881, 6882, 6883, 32100,\r\n32101, 32102, 32103, 50000, 50001, 50002, 50003, 50010, 61200, 61201, 61202] // Potential ports\r\n{ \"8\": 8, // MSG code \"1\": 17, // Network protocol version \"2\": \"jnugfv895664\", // Victim BOT ID \"4\": 3, \"5\": 111, \"55\":\r\n270544960, // External IP of the potential bot \"56\": [443, 995, 993, 465, 990, 22, 2222, 2078, 2083, 2087, 1194, 8443, 20,\r\n21, 53, 80, 3389, 6881, 6882, 6883, 32100, 32101, 32102, 32103, 50000, 50001, 50002, 50003, 50010, 61200, 61201,\r\n61202] // Potential ports }\r\n{\r\n \"8\": 8, // MSG code\r\n \"1\": 17, // Network protocol version\r\n \"2\": \"jnugfv895664\", // Victim BOT ID\r\n \"4\": 3,\r\n \"5\": 111,\r\n \"55\": 270544960, // External IP of the potential bot\r\n \"56\": [443, 995, 993, 465, 990, 22, 2222, 2078, 2083, 2087, 1194, 8443, 20, 21, 53, 80, 3389, 6881, 6882,\r\n}\r\nWhen the program does this specific process, we could observe that on the next command execution request, we will receive\r\na proxy module installation with the relevant port to listen:\r\n\"20\": [\"TVqQAAM...=\", \"prt=443\", \"n=jnugfv895664\"], // command payload\r\n{ \"8\": 6, // MSG code ... \"19\": 25, // command ID \"20\": [\"TVqQAAM...=\", \"prt=443\", \"n=jnugfv895664\"], // command\r\npayload ... }\r\n{\r\n \"8\": 6, // MSG code\r\n...\r\n \"19\": 25, // command ID\r\n \"20\": [\"TVqQAAM...=\", \"prt=443\", \"n=jnugfv895664\"], // command payload\r\n...\r\n}\r\nWe can visualize the process with the next diagram, and observe it through a traffic capture program:\r\nFigure 23 – Network flow for proxy module download\r\nFigure 24 – Downloaded proxy module in Fiddler\r\nhttps://research.checkpoint.com/2020/exploring-qbots-latest-attack-methods/\r\nPage 14 of 32\n\nAnalyzing Proxy Module\r\nThe proxy module is loaded by rundll32.exe , and copied into its working folder –\r\nC:\\ProgramData\\FilesystemMonitor\\ . If it is given SYSTEM privileges, it creates a new service named fsmon , otherwise\r\nupdates CurrentVersion\\Run registry value.\r\nMost of the module’s codebase is taken from the following open-source libraries:\r\nlibcurl 7.47.1 for HTTP requests.\r\nOpenSSL 1.0.2r 26 Feb 2019 – Used for certificate creation, and signature validation.\r\nminiupnp – For port opening.\r\nIt also contains 3 hard-coded IP addresses of the second-tier proxy server.\r\nThe module hasn’t changed a lot since McAfee’s publication 3 years ago. The changes we could find were:\r\nThe service name, description, working folder, window name, and executable name has been changed. For example,\r\nthe service name was changed from hwmon to fsmon .\r\nOpenSSL version has been upgraded from 1.0.2f to 1.0.2r .\r\nUpdated Tier 2 Proxy servers.\r\nOne rather interesting feature of the proxy module is its control API. The threat group behind Qbot has developed a control\r\nAPI to the proxy which is independent of the malicious payload update mechanism. That API is also unique, mainly because\r\nit receives control messages by pushing and not by pulling, which could expose the bots to external actors’ control.\r\nThe protocol is rather simple and can be observed in the next diagram:\r\nFigure 25 – Network flow for proxy module control API\r\nThe signature is being verified against the hardcoded public key of the actor. Hence, unless we possess the private key, the\r\nprotocol is extremely hard to break.\r\nConclusion\r\nThis article analyzes two aspects of the threat – the campaign that leads to the infection of the victim, and the complex\r\nmulti-layered malware which is constantly evolving. The article also covers several miscellaneous topics regarding its\r\nversion history in the past year, decryption methods, communication samples, proxy server control API, and more.\r\nThese days Qbot is much more dangerous than it was previously – it has active malspam campaign which infects\r\norganizations, and it manages to use a “3rd party” infection infrastructure like Emotet’s to spread the threat even further. It\r\nseems like the threat group behind Qbot is evolving its techniques through the years, and Check Point Research hopes that\r\nthe information in this article will help the researchers around the globe to mitigate and potentially stop Qbot’s activity.\r\nCheck Point SandBlast Agent protects against such attacks, and is capable of preventing them from the very first step.\r\nIOC\r\nhttps://research.checkpoint.com/2020/exploring-qbots-latest-attack-methods/\r\nPage 15 of 32\n\nMany Qbot and VBS samples were analyzed during the research. We’re attaching the recent samples and modules from\r\n22/06/2020.\r\nHashes\r\n9001DF2C853B4BA118433DD83C17617E7AA368B1 – VBS Dropper\r\n449F2B10320115E98B182204A4376DDC669E1369 – Qbot Sample SPX145\r\nF85A63CB462B8FD60DA35807C63CD13226907901 – Mail Collector Module Loader [Decrypted]\r\nB4BC69FF502AECB4BBC2FB9A3DFC0CA8CF99BA9E – Javascript Updater Loader [Decrypted]\r\n1AAA14A50C3C3F65269265C30D8AA05AD8695B1B – Javascript Updater [Decrypted]\r\n577522512506487C63A372BBDA77BE966C23CBD1 – Hooking Module Loader [Decrypted]\r\n75107AEE398EED78532652B462B77AE6FB576198 – Cookie Grabber Module [Decrypted]\r\n674685F3EC24C72458EDC11CF4F135E445B4185B – Password Grabber Module [Decrpyted]\r\nBECD8F2D6289B51981F07D5FF52916104D764DD5 – hVNC Module [Decrpyted]\r\n18E8971B2DE8EA3F8BB7E1462E414DA936425D4E – Proxy Module Loader [Decrypted]\r\n4C96D2BCE0E12F8591999D4E00498BCDB8A116DE – Proxy Module\r\nDomains and IPs\r\nZIP File URL\r\nhxxps://factory-hot[.]com/bafmxby/CcdEhoQGHq.zip\r\nVBS Dropper URLs\r\nhxxp://kiesow-auto[.]de/foojapfsyds/5555555.png\r\nhxxp://test[.]africanamericangolfersdigest[.]com/kkmthjsvf/5555555.png\r\nhxxp://frankiptv[.]com/liehyidqtu/5555555.png\r\nhxxp://klubnika-malina[.]by/utgritefmjq/5555555.png\r\nhxxp://centr-toshiba[.]by/wogvynkombk/5555555.png\r\nhxxp://marokeconstruction[.]com[.]au/hhmzmlqct/5555555.png\r\nWeb-Inject URLs\r\nhxxps://fortinet-cloud[.]com/wbj/br/content/chase/tom/ajax.js\r\nhxxps://fortinet-cloud[.]com/wbj/br/content/key/tom/ajax.js\r\nhxxps://fortinet-cloud[.]com/wbj/br/content/schwab/tom/schw.js\r\nhxxps://fortinet-cloud[.]com/wbj/br/content/bbt/tom/bbt.js\r\nhxxps://fortinet-cloud[.]com/wbj/att/js/AMAZON.js\r\nhxxps://fortinet-cloud[.]com/wbj/crt/uadmin/inj_src/usa/amex2019/script.js\r\nhxxps://fortinet-cloud[.]com/wbj/crt/uadmin/inj_src/usa/costco/costco.min.js\r\nhxxps://fortinet-cloud[.]com/wbj/crt/uadmin/inj_src/usa/verizon/script.js\r\nhxxps://fortinet-cloud[.]com/wbj/crt/uadmin/gate.php\r\nhxxps://callunaconycatcher[.]com/bre/content/bmo/ins/bmo.js\r\nhxxps://callunaconycatcher[.]com/bre/content/desjardins/ins/desjardins.js\r\nhxxps://callunaconycatcher[.]com/bre/content/rbc/ins/rbc.js\r\nhxxps://requirejscdn[.]com/*\r\nhxxps://cersomab[.]com/lob.php\r\nMail Collector Remote Server\r\nhxxps://82.118.22[.]125/bgate\r\nMimikatz URL Download\r\nhxxps://onedrive.live[.]com/download.aspx?\r\ncid=CE32720D26AED2D5\u0026authKey=%21AHHhrhk9od5OCBU\u0026resid=CE32720D26AED2D5%21111\u0026ithint=%2Eps1\r\nTier 2 Proxy Servers\r\n46.228.199.235:443\r\n93.88.75.176:443\r\n207.244.112.112:443\r\nJavascript Updater URLs\r\nhxxp://backup.justthebooks[.]com/datacollectionservice.php3\r\nhxxp://asn.crs.com[.]pa/datacollectionservice.php3\r\nhxxp://chs.zarifbarbari[.]com/datacollectionservice.php3\r\nhttps://research.checkpoint.com/2020/exploring-qbots-latest-attack-methods/\r\nPage 16 of 32\n\nBot List\r\n79.115.207.120:443\r\n156.213.80.140:443\r\n189.160.203.110:443\r\n71.114.39.220:443\r\n189.236.166.167:443\r\n193.248.44.2:2222\r\n206.51.202.106:50003\r\n24.152.219.253:995\r\n2.50.47.97:2222\r\n108.49.221.180:443\r\n207.246.75.201:443\r\n80.240.26.178:443\r\n199.247.16.80:443\r\n207.255.161.8:2222\r\n69.92.54.95:995\r\n199.247.22.145:443\r\n2.50.171.142:443\r\n24.110.14.40:3389\r\n79.101.130.104:995\r\n94.52.160.116:443\r\n172.243.155.62:443\r\n188.192.75.8:443\r\n175.111.128.234:443\r\n74.129.18.56:443\r\n36.77.151.211:443\r\n203.45.104.33:443\r\n118.160.162.77:443\r\n86.126.97.183:2222\r\n185.246.9.69:995\r\n140.82.21.191:443\r\n66.208.105.6:443\r\n206.183.190.53:993\r\n5.12.111.213:443\r\n72.177.157.217:995\r\n98.210.41.34:443\r\n98.242.36.86:443\r\n199.116.241.147:443\r\n49.144.81.46:8443\r\n75.110.250.89:995\r\n219.76.148.142:443\r\n70.174.3.241:443\r\n71.205.158.156:443\r\n78.96.192.26:443\r\n108.190.151.108:2222\r\n81.133.234.36:2222\r\n12.5.37.3:995\r\n210.61.141.92:443\r\n173.70.165.101:995\r\n5.13.84.186:995\r\n68.46.142.48:443\r\n188.27.6.170:443\r\n188.173.70.18:443\r\n86.124.13.101:443\r\n5.13.74.26:443\r\n68.190.152.98:443\r\n96.56.237.174:990\r\n175.143.12.8:443\r\n79.113.224.85:443\r\n2.51.240.61:995\r\n95.76.27.89:443\r\n5.12.243.211:443\r\n24.183.39.93:443\r\nhttps://research.checkpoint.com/2020/exploring-qbots-latest-attack-methods/\r\nPage 17 of 32\n\n86.124.228.254:443\r\n5.193.178.241:2078\r\n2.88.186.229:443\r\n108.227.161.27:995\r\n188.192.75.8:995\r\n98.32.60.217:443\r\n176.223.35.19:2222\r\n24.42.14.241:443\r\n70.95.118.217:443\r\n68.225.56.31:443\r\n191.84.11.112:443\r\n72.204.242.138:50001\r\n173.22.120.11:2222\r\n64.121.114.87:443\r\n68.60.221.169:465\r\n92.17.167.87:2222\r\n47.138.200.85:443\r\n71.187.7.239:443\r\n151.205.102.42:443\r\n72.179.13.59:443\r\n172.113.74.96:443\r\n5.193.61.212:2222\r\n47.28.135.155:443\r\n188.26.243.186:443\r\n41.228.206.99:443\r\n117.218.208.239:443\r\n203.122.7.82:443\r\n39.36.61.58:995\r\n49.207.105.25:443\r\n59.124.10.133:443\r\n89.44.196.211:443\r\n79.117.129.171:21\r\n24.110.96.149:443\r\n184.90.139.176:2222\r\n82.79.67.68:443\r\n86.153.98.35:2222\r\n101.108.4.251:443\r\n209.182.122.217:443\r\n89.32.220.79:443\r\n104.50.141.139:995\r\n85.204.189.105:443\r\n94.10.81.239:443\r\n211.24.72.253:443\r\n110.142.205.182:443\r\n86.124.105.88:443\r\n72.90.243.117:0\r\n41.225.231.43:443\r\n87.65.204.240:995\r\n62.121.123.57:443\r\n47.153.115.154:990\r\n66.30.92.147:443\r\n49.191.4.245:443\r\n47.180.66.10:443\r\n97.93.211.17:443\r\n65.100.247.6:2083\r\n65.131.43.76:995\r\n45.45.51.182:2222\r\n98.219.77.197:443\r\n166.62.180.194:2078\r\n72.16.212.108:995\r\n73.217.4.42:443\r\n76.187.8.160:443\r\n67.182.188.217:443\r\n37.182.238.170:2222\r\nhttps://research.checkpoint.com/2020/exploring-qbots-latest-attack-methods/\r\nPage 18 of 32\n\n117.216.227.70:443\r\n74.222.204.82:443\r\n89.137.77.237:443\r\n82.77.169.118:2222\r\n188.27.36.190:443\r\n108.39.93.45:443\r\n72.181.9.163:443\r\n58.233.220.182:443\r\n73.137.187.150:443\r\n97.127.144.203:2222\r\n103.76.160.110:443\r\n37.156.243.67:995\r\n67.246.16.250:995\r\n182.185.7.220:995\r\n82.81.172.21:443\r\n117.199.6.105:443\r\n216.163.4.132:443\r\n199.102.55.87:53\r\n96.244.45.155:443\r\n122.147.204.4:443\r\n89.45.107.209:443\r\n35.142.12.163:2222\r\n73.94.229.115:443\r\n165.0.3.95:995\r\nOther IOC\r\nProxy Service Name\r\nfsmon\r\nProxy Service Display name\r\nFilesystem Monitor\r\nProxy File Paths\r\nC:\\ProgramData\\FilesystemMonitor\\fsmonitor.dll\r\nC:\\ProgramData\\FilesystemMonitor\\fsmonitor.ini\r\nProxy Executable Command Line\r\nC:\\Windows\\SysWOW64\\rundll32.exe \"C:\\ProgramData\\FilesystemMonitor\\fsmonitor.dll\",FsMonServerMainNT\r\nC:\\Windows\\SysWOW64\\rundll32.exe \"C:\\ProgramData\\FilesystemMonitor\\fsmonitor.dll\",#1\r\nProxy RSA Public Key\r\n-----BEGIN RSA PUBLIC KEY-----\r\nMIIBCgKCAQEA4zJC+AO8v7U9WGOdqeqMn9CPrdgoz//B+f/xxb4UnSNM1NJ1RwTG\r\nN2jf6JRRD2gZz9735DU4I9FlIDEiRDdNn4OxX76L5eKe2GF4/etZ23DfuomMNXVw\r\nqwYcO8A7zjzG0+ybQH35eNoYJMJDwPOBWb/nHBlPNWXoyv7u8EzScENMBpfKWuMW\r\nUgmV08dulHPPyi9fjSsY3DLo5zNE6A8UEk2e2R2UkmiDbENOARgsfwHosyqEcBGc\r\nPk/+EismU1rsabaQV/sHw1zQQ9vAH+27d/T13hCuIgq1B3vRYFIrPkJYAdaxOwto\r\nAHn0rjeAN4tEIdDQ10RCriEmnNEBfxA9BwIDAQAB\r\n-----END RSA PUBLIC KEY-----\r\nAppendix\r\nAppendix A: YARA Rule for VBS Hunting\r\ndescription = \"Catches QBot VBS files\"\r\n$s5 = \"if ms.readyState = 4 Then\"\r\n$s6 = \"if len(ms.responseBody) \u003c\u003e 0 then\"\r\n$s7 = /if left\\(ms.responseText, \\w*?\\) = \\\"MZ\\\" then/\r\nfilesize \u003e 20MB and $s3 and $s4 and $s5 and $s6 and $s7\r\nhttps://research.checkpoint.com/2020/exploring-qbots-latest-attack-methods/\r\nPage 19 of 32\n\nrule qbot_vbs { meta: description = \"Catches QBot VBS files\" author = \"Alex Ilgayev\" date = \"2020-06-07\" strings: $s3 =\r\n\"ms.Send\" $s4 = \"for i=1 to 6\" $s5 = \"if ms.readyState = 4 Then\" $s6 = \"if len(ms.responseBody) \u003c\u003e 0 then\" $s7 = /if left\\\r\n(ms.responseText, \\w*?\\) = \\\"MZ\\\" then/ condition: filesize \u003e 20MB and $s3 and $s4 and $s5 and $s6 and $s7 }\r\nrule qbot_vbs\r\n{\r\n meta:\r\n description = \"Catches QBot VBS files\"\r\n author = \"Alex Ilgayev\"\r\n date = \"2020-06-07\"\r\n strings:\r\n $s3 = \"ms.Send\"\r\n $s4 = \"for i=1 to 6\"\r\n $s5 = \"if ms.readyState = 4 Then\"\r\n $s6 = \"if len(ms.responseBody) \u003c\u003e 0 then\"\r\n $s7 = /if left\\(ms.responseText, \\w*?\\) = \\\"MZ\\\" then/\r\n condition:\r\n filesize \u003e 20MB and $s3 and $s4 and $s5 and $s6 and $s7\r\n}\r\nAppendix B: VBS URL Extraction Script\r\n\"\"\"Qbot VBS URL extractor and de-obfuscator.\r\nThis script is for research purposes, and far from production ready (missing exception handling and more).\r\ndef remove_additions(lines):\r\n\"\"\"Removes stub calculations.\r\nIZLmoJg = 277 + 15 + 23 + 468 - 345 - 18 - 471 - 15 + 617\r\nlines (list): List of lines.\r\nlist: List of modified lines.\r\npattern = r'(([0-9]{1,15} [\\+,\\-] )+[0-9]{1,15})'\r\nres = re.search(pattern, line)\r\nnew_line = re.sub(pattern, lambda x:str(eval(x.group(1))), line)\r\nnew_file.append(new_line)\r\n\"\"\"Replaces \"chr(*)\" with their respective characters.\r\nlines (list): List of lines.\r\nlist: List of modified lines.\r\npattern = r'[c,C]hr\\((\\d?\\d?\\d?)\\)'\r\nres = re.search(pattern, line)\r\nnew_line = re.sub(pattern, lambda match: '\\\"' + str(chr(int(match.group(1)))) + '\\\"', line)\r\nnew_file.append(new_line)\r\ndef remove_replace(lines):\r\n\"\"\"Replaces \"replace(*)\" with its respective string.\r\nlines (list): List of lines.\r\nlist: List of modified lines.\r\npattern = r'replace\\(\\\"(.*)\\\"\\, \\\"(.*)\\\"\\, \\\"(.*)\\\"\\)'\r\nres = re.search(pattern, line)\r\nnew_line = re.sub(pattern,\r\nlambda match: '\\\"' + match.group(1).replace(match.group(2), match.group(3)) + '\\\"'\r\nhttps://research.checkpoint.com/2020/exploring-qbots-latest-attack-methods/\r\nPage 20 of 32\n\nnew_file.append(new_line)\r\ndef remove_concat(lines):\r\n\"\"\"Replaces the VB concatenation sign \"\u0026\" with the result string.\r\nlines (list): List of lines.\r\nlist: List of modified lines.\r\npattern = r'\\\"(.*)\\\"\\\u0026\\\"(.*)\\\"'\r\nres = re.search(pattern, line)\r\nnew_line = re.sub(pattern,\r\nlambda match: '\\\"' + match.group(1) + match.group(2) + '\\\"'\r\nnew_file.append(new_line)\r\ndef remove_trailing_zeros(lines):\r\n\"\"\"Removes all trailing NULL bytes from a file.\r\nlines (list): List of lines.\r\nlist: List of modified lines.\r\nif len(line) \u003e 0 and line[0] == '\\x00':\r\ndef deobfuscate_file(fpath_in, fpath_out):\r\n\"\"\"Converts Qbot VBS script into it's deobfuscated form.\r\n- removing stub calculations\r\n- converting \"chr(*)\" into their respective characters.\r\n- converting \"replace(*)\" into its respective string.\r\n- converting VB concatenations (\"\u0026\") into the final string.\r\n- removing trailing NULL bytes.\r\nfpath_in (str): Input VBS file path.\r\nfpath_out (str): Output file path.\r\nwith open(fpath_in, 'r') as f_in:\r\nlines = in_data.split('\\n')\r\nlines = remove_additions(lines)\r\nlines = remove_chr(lines)\r\nlines = remove_replace(lines)\r\nlines = remove_concat(lines)\r\nlines = remove_trailing_zeros(lines)\r\nnew_file_joined = '\\n'.join(lines)\r\nwith open(fpath_out, 'w') as f_out:\r\nf_out.write(new_file_joined)\r\ndef decrypt_data(enc_str, keys):\r\n\"\"\"Decrypts long blob of text data.\r\ndecryption method is looking for patterns of decimal numbers,\r\nand xor them with the key.\r\ndo that with three different keys.\r\nhttps://research.checkpoint.com/2020/exploring-qbots-latest-attack-methods/\r\nPage 21 of 32\n\nenc_str {string} -- encrypted data\r\ndef _decrypt_data_inner(str_param, key_param):\r\n\"\"\"Helper method. actual decryption.\r\nfor i in range(len(str_param)):\r\nif '0' \u003c= str_param[i] \u003c= '9':\r\nnumbers = numbers + str_param[i]\r\ndec_ch = enc_ch ^ key_param\r\nret_decrypted = ret_decrypted + chr(dec_ch)\r\nenc_str = _decrypt_data_inner(enc_str, key1)\r\nenc_str = _decrypt_data_inner(enc_str, key2)\r\nreturn _decrypt_data_inner(enc_str, key3)\r\n\"\"\"Encapsulates qbot VBS artifacts.\r\nThese artifacts are used for extraction.\r\nkey_idxs = [None, None, None]\r\ndef __init__(self, data):\r\nself.lines = data.split('\\n')\r\ndef _extract_number_urls(self):\r\n\"\"\"Extracts number of encrypted urls.\r\n# number of urls: for i=1 to 6\r\npattern = r'[F,f]or i=1 to (\\d+)'\r\nres = re.search(pattern, '\\n'.join(self.lines))\r\nself.num_urls = int(res.group(1))\r\ndef _extract_enc_str(self):\r\n\"\"\"Extracts the string which has the encrypted data.\r\nshould be the biggest line in the script.\r\nfor i, line in enumerate(self.lines):\r\nif len(line) \u003e max_len and line[0] != '\\x00':\r\n# removing variable name.\r\nres = re.search(r'^\\w+ = \\\"(.*)\\\"$', self.lines[max_idx])\r\nself.enc_str = res.group(1)\r\ndef _extract_key_str(self):\r\n\"\"\"Extracts the string which the key is based upon.\r\nshould be called after `extract_enc_str()`.\r\nshould be the second biggest line after encrypted string.\r\nmax_len = len(self.enc_str)\r\nfor i, line in enumerate(self.lines):\r\nif len(line) \u003e second_max_len and len(line) \u003c max_len and line[0] != '\\x00':\r\nsecond_max_len = len(line)\r\n# removing variable name.\r\nhttps://research.checkpoint.com/2020/exploring-qbots-latest-attack-methods/\r\nPage 22 of 32\n\nres = re.search(r'^\\w+ = \\\"(.*)\\\"$', self.lines[second_max_idx])\r\nself.key_str = res.group(1)\r\ndef _extract_seed_and_key_indexes(self):\r\n\"\"\"Helper function for key extraction.\r\ndef _find_variables(var1_name, var2_name):\r\n\"\"\"Finds variables values for two vars.\r\npattern1 = fr'^{var1_name} = (\\d+)$'\r\npattern2 = fr'^{var2_name} = (\\d+)$'\r\nres = re.search(pattern1, line)\r\nvar1_value = res.group(1)\r\nres = re.search(pattern2, line)\r\nvar2_value = res.group(1)\r\nreturn var1_value, var2_value\r\ndef _extract_key_indexes(lines):\r\n# we have 6 'Mid' encounters:\r\n# yaGlYs = Mid(xHAaMv, 10, 2)\r\n# RLquKjB = Asc(Mid(HIbAriX, seSclZ, 1))\r\n# the second is not interesting for us.\r\nres = re.findall(r'Mid\\(\\w+\\, (\\w+)\\, \\w+\\)', text)\r\n# the key creation order is reversed to their using. (first key3 is set and so on)\r\nself.key_idxs[0] = int(res[4])\r\nself.key_idxs[1] = int(res[2])\r\nself.key_idxs[2] = int(res[0])\r\n# For uLRYNs = 0 To 2387414 Step 1\r\npattern1 = r' (238\\d\\d\\d\\d) '\r\n# YLTCm = YLTCm + DgZlWOk - jRryhge\r\npattern2 = r'^(\\w+) = (\\1) \\+ (\\w+) - (\\w+)$'\r\nfor i, line in enumerate(self.lines):\r\nres = re.search(pattern1, line)\r\n_extract_key_indexes(self.lines[i+1:])\r\nfor inner_line in self.lines[i+1:i+10]:\r\nres = re.search(pattern2, inner_line)\r\nfirst_param = res.group(3)\r\nsecond_param = res.group(4)\r\nfirst_value, second_value = _find_variables(first_param, second_param)\r\nnum *= (int(first_value) - int(second_value))\r\n\"\"\"Main extraction method.\r\nExtracts keys for the URL decryption.\r\nvbs._extract_number_urls()\r\nhttps://research.checkpoint.com/2020/exploring-qbots-latest-attack-methods/\r\nPage 23 of 32\n\nvbs._extract_seed_and_key_indexes()\r\nseed = self.seed * 999999\r\nidx = int(str_seed[self.key_idxs[2] - 1:self.key_idxs[2] + 1])\r\nkey3 = ord(self.key_str[idx - 1])\r\nidx = int(str_seed[self.key_idxs[1] - 1:self.key_idxs[1] + 1])\r\nkey2 = ord(self.key_str[idx - 1])\r\nidx = int(str_seed[self.key_idxs[0] - 1:self.key_idxs[0] + 1])\r\nkey1 = ord(self.key_str[idx - 1])\r\nif __name__ == \"__main__\":\r\nprint(f\"Usage: python {os.path.basename(__file__)} \u003cfpath_in\u003e\")\r\ndeobfuscate_file(sys.argv[1], fname_tmp)\r\nif not os.path.exists(fname_tmp):\r\nprint(\"Failed de-obfuscation script.\")\r\nwith open(fname_tmp) as f:\r\nkeys = vbs.extract_keys()\r\ndec = decrypt_data(vbs.enc_str, keys).strip('\\ufeff').split('_______')\r\nfor i in range(vbs.num_urls):\r\nurl = url.split('?')[0].strip()\r\n\"\"\"Qbot VBS URL extractor and de-obfuscator. This script is for research purposes, and far from production ready (missing\r\nexception handling and more). \"\"\" import re import os import sys def remove_additions(lines): \"\"\"Removes stub\r\ncalculations. Example: IZLmoJg = 277 + 15 + 23 + 468 - 345 - 18 - 471 - 15 + 617 Will be replaced with: IZLmoJg = 551\r\nArgs: lines (list): List of lines. Returns: list: List of modified lines. \"\"\" pattern = r'(([0-9]{1,15} [\\+,\\-] )+[0-9]{1,15})'\r\nnew_file = [] for line in lines: line = line.strip() res = re.search(pattern, line) if res: new_line = re.sub(pattern, lambda\r\nx:str(eval(x.group(1))), line) else: new_line = line new_file.append(new_line) return new_file def remove_chr(lines):\r\n\"\"\"Replaces \"chr(*)\" with their respective characters. Args: lines (list): List of lines. Returns: list: List of modified lines. \"\"\"\r\npattern = r'[c,C]hr\\((\\d?\\d?\\d?)\\)' new_file = [] for line in lines: line = line.strip() res = re.search(pattern, line) if res:\r\nnew_line = re.sub(pattern, lambda match: '\\\"' + str(chr(int(match.group(1)))) + '\\\"', line) else: new_line = line\r\nnew_file.append(new_line) return new_file def remove_replace(lines): \"\"\"Replaces \"replace(*)\" with its respective string.\r\nArgs: lines (list): List of lines. Returns: list: List of modified lines. \"\"\" pattern = r'replace\\(\\\"(.*)\\\"\\, \\\"(.*)\\\"\\, \\\"(.*)\\\"\\)'\r\nnew_file = [] for line in lines: line = line.strip() res = re.search(pattern, line) if res: new_line = re.sub(pattern, lambda match:\r\n'\\\"' + match.group(1).replace(match.group(2), match.group(3)) + '\\\"' ,line) else: new_line = line new_file.append(new_line)\r\nreturn new_file def remove_concat(lines): \"\"\"Replaces the VB concatenation sign \"\u0026\" with the result string. Args: lines\r\n(list): List of lines. Returns: list: List of modified lines. \"\"\" pattern = r'\\\"(.*)\\\"\\\u0026\\\"(.*)\\\"' new_file = [] for line in lines: line =\r\nline.strip() res = re.search(pattern, line) if res: new_line = re.sub(pattern, lambda match: '\\\"' + match.group(1) +\r\nmatch.group(2) + '\\\"' ,line) else: new_line = line new_file.append(new_line) return new_file def\r\nremove_trailing_zeros(lines): \"\"\"Removes all trailing NULL bytes from a file. Args: lines (list): List of lines. Returns: list:\r\nList of modified lines. \"\"\" new_file = [] for line in lines: if len(line) \u003e 0 and line[0] == '\\x00': continue new_file.append(line)\r\nreturn new_file def deobfuscate_file(fpath_in, fpath_out): \"\"\"Converts Qbot VBS script into it's deobfuscated form. Main\r\nchanges are: - removing stub calculations - converting \"chr(*)\" into their respective characters. - converting \"replace(*)\" into\r\nits respective string. - converting VB concatenations (\"\u0026\") into the final string. - removing trailing NULL bytes. Args:\r\nfpath_in (str): Input VBS file path. fpath_out (str): Output file path. \"\"\" try: with open(fpath_in, 'r') as f_in: in_data =\r\nf_in.read() except: return None lines = in_data.split('\\n') lines = remove_additions(lines) lines = remove_chr(lines) lines =\r\nremove_replace(lines) for _ in range(100): lines = remove_concat(lines) lines = remove_trailing_zeros(lines)\r\nnew_file_joined = '\\n'.join(lines) try: with open(fpath_out, 'w') as f_out: f_out.write(new_file_joined) except: return None\r\ndef decrypt_data(enc_str, keys): \"\"\"Decrypts long blob of text data. decryption method is looking for patterns of decimal\r\nnumbers, and xor them with the key. do that with three different keys. Arguments: enc_str {string} -- encrypted data key1\r\n{int} -- first key key2 {int} -- second key key3 {int} -- third key \"\"\" def _decrypt_data_inner(str_param, key_param):\r\n\"\"\"Helper method. actual decryption. \"\"\" numbers = \"\" ret_decrypted = \"\" f = True for i in range(len(str_param)): if '0' \u003c=\r\nstr_param[i] \u003c= '9': numbers = numbers + str_param[i] f = True else: if f: try: enc_ch = int(numbers) except: break dec_ch =\r\nenc_ch ^ key_param ret_decrypted = ret_decrypted + chr(dec_ch) numbers = \"\" f = False return ret_decrypted key1 =\r\nkeys[0] key2 = keys[1] key3 = keys[2] enc_str = _decrypt_data_inner(enc_str, key1) enc_str = _decrypt_data_inner(enc_str,\r\nkey2) return _decrypt_data_inner(enc_str, key3) class qbot_vbs(object): \"\"\"Encapsulates qbot VBS artifacts. These artifacts\r\nhttps://research.checkpoint.com/2020/exploring-qbots-latest-attack-methods/\r\nPage 24 of 32\n\nare used for extraction. \"\"\" num_urls = 0 enc_str = None key_str = None seed = 0 key_idxs = [None, None, None] def\r\n__init__(self, data): self.data = data self.lines = data.split('\\n') def _extract_number_urls(self): \"\"\"Extracts number of\r\nencrypted urls. \"\"\" # sample: # number of urls: for i=1 to 6 pattern = r'[F,f]or i=1 to (\\d+)' res = re.search(pattern,\r\n'\\n'.join(self.lines)) self.num_urls = int(res.group(1)) def _extract_enc_str(self): \"\"\"Extracts the string which has the\r\nencrypted data. should be the biggest line in the script. \"\"\" max_len = 0 max_idx = -1 for i, line in enumerate(self.lines): if\r\nlen(line) \u003e max_len and line[0] != '\\x00': max_len = len(line) max_idx = i # removing variable name. res = re.search(r'^\\w+\r\n= \\\"(.*)\\\"$', self.lines[max_idx]) self.enc_str = res.group(1) def _extract_key_str(self): \"\"\"Extracts the string which the key\r\nis based upon. should be called after `extract_enc_str()`. should be the second biggest line after encrypted string. \"\"\"\r\nsecond_max_len = 0 second_max_idx = -1 max_len = len(self.enc_str) for i, line in enumerate(self.lines): if len(line) \u003e\r\nsecond_max_len and len(line) \u003c max_len and line[0] != '\\x00': second_max_len = len(line) second_max_idx = i # removing\r\nvariable name. res = re.search(r'^\\w+ = \\\"(.*)\\\"$', self.lines[second_max_idx]) self.key_str = res.group(1) def\r\n_extract_seed_and_key_indexes(self): \"\"\"Helper function for key extraction. \"\"\" def _find_variables(var1_name,\r\nvar2_name): \"\"\"Finds variables values for two vars. for example: DgZlWOk = 8 jRryhge = 4 \"\"\" pattern1 = fr'^{var1_name}\r\n= (\\d+)$' pattern2 = fr'^{var2_name} = (\\d+)$' var1_value = None var2_value = None for line in self.lines: res =\r\nre.search(pattern1, line) if res: var1_value = res.group(1) res = re.search(pattern2, line) if res: var2_value = res.group(1)\r\nreturn var1_value, var2_value def _extract_key_indexes(lines): # we have 6 'Mid' encounters: # yaGlYs = Mid(xHAaMv,\r\n10, 2) # RLquKjB = Asc(Mid(HIbAriX, seSclZ, 1)) # three times. # the second is not interesting for us. text = '\\n'.join(lines)\r\nres = re.findall(r'Mid\\(\\w+\\, (\\w+)\\, \\w+\\)', text) # the key creation order is reversed to their using. (first key3 is set and so\r\non) 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 =\r\n0 To 2387414 Step 1 pattern1 = r' (238\\d\\d\\d\\d) ' # sample line: # YLTCm = YLTCm + DgZlWOk - jRryhge pattern2 =\r\nr'^(\\w+) = (\\1) \\+ (\\w+) - (\\w+)$' for i, line in enumerate(self.lines): res = re.search(pattern1, line) if res: num =\r\nint(res.group(1)) _extract_key_indexes(self.lines[i+1:]) for inner_line in self.lines[i+1:i+10]: res = re.search(pattern2,\r\ninner_line) if res: first_param = res.group(3) second_param = res.group(4) first_value, second_value =\r\n_find_variables(first_param, second_param) num += 1 num *= (int(first_value) - int(second_value)) self.seed = num return\r\ndef extract_keys(self): \"\"\"Main extraction method. Extracts keys for the URL decryption. Returns: list: List of 3 keys. \"\"\"\r\nvbs._extract_number_urls() vbs._extract_enc_str() vbs._extract_key_str() vbs._extract_seed_and_key_indexes() seed =\r\nself.seed * 999999 str_seed = str(seed) idx = int(str_seed[self.key_idxs[2] - 1:self.key_idxs[2] + 1]) key3 =\r\nord(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 =\r\nint(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__\r\n== \"__main__\": if len(sys.argv) != 2: print(f\"Usage: python {os.path.basename(__file__)} \u003cfpath_in\u003e\") exit(1) fname_tmp\r\n= 'tmp' deobfuscate_file(sys.argv[1], fname_tmp) if not os.path.exists(fname_tmp): print(\"Failed de-obfuscation script.\")\r\nexit(0) with open(fname_tmp) as f: data = f.read() os.remove(fname_tmp) vbs = qbot_vbs(data) keys = vbs.extract_keys()\r\ndec = decrypt_data(vbs.enc_str, keys).strip('\\ufeff').split('_______') for i in range(vbs.num_urls): url = dec[i] url =\r\nurl.split('?')[0].strip() print(url)\r\n\"\"\"Qbot VBS URL extractor and de-obfuscator.\r\nThis script is for research purposes, and far from production ready (missing exception handling and more).\r\n\"\"\"\r\nimport re\r\nimport os\r\nimport sys\r\ndef remove_additions(lines):\r\n \"\"\"Removes stub calculations.\r\n Example:\r\n IZLmoJg = 277 + 15 + 23 + 468 - 345 - 18 - 471 - 15 + 617\r\n Will be replaced with:\r\n IZLmoJg = 551\r\n Args:\r\n lines (list): List of lines.\r\n Returns:\r\n list: List of modified lines.\r\n \"\"\"\r\n pattern = r'(([0-9]{1,15} [\\+,\\-] )+[0-9]{1,15})'\r\n new_file = []\r\n for line in lines:\r\n line = line.strip()\r\n res = re.search(pattern, line)\r\n if res:\r\n new_line = re.sub(pattern, lambda x:str(eval(x.group(1))), line)\r\nhttps://research.checkpoint.com/2020/exploring-qbots-latest-attack-methods/\r\nPage 25 of 32\n\nelse:\r\n new_line = line\r\n new_file.append(new_line)\r\n return new_file\r\ndef remove_chr(lines):\r\n \"\"\"Replaces \"chr(*)\" with their respective characters.\r\n Args:\r\n lines (list): List of lines.\r\n Returns:\r\n list: List of modified lines.\r\n \"\"\"\r\n pattern = r'[c,C]hr\\((\\d?\\d?\\d?)\\)'\r\n new_file = []\r\n for line in lines:\r\n line = line.strip()\r\n res = re.search(pattern, line)\r\n if res:\r\n new_line = re.sub(pattern, lambda match: '\\\"' + str(chr(int(match.group(1)))) + '\\\"', line)\r\n else:\r\n new_line = line\r\n new_file.append(new_line)\r\n return new_file\r\ndef remove_replace(lines):\r\n \"\"\"Replaces \"replace(*)\" with its respective string.\r\n Args:\r\n lines (list): List of lines.\r\n Returns:\r\n list: List of modified lines.\r\n \"\"\"\r\n pattern = r'replace\\(\\\"(.*)\\\"\\, \\\"(.*)\\\"\\, \\\"(.*)\\\"\\)'\r\n new_file = []\r\n for line in lines:\r\n line = line.strip()\r\n res = re.search(pattern, line)\r\n if res:\r\n new_line = re.sub(pattern,\r\n lambda match: '\\\"' + match.group(1).replace(match.group(2), match.group(3)) + '\\\"'\r\n ,line)\r\n else:\r\n new_line = line\r\n new_file.append(new_line)\r\n return new_file\r\ndef remove_concat(lines):\r\n \"\"\"Replaces the VB concatenation sign \"\u0026\" with the result string.\r\n Args:\r\n lines (list): List of lines.\r\n Returns:\r\n list: List of modified lines.\r\n \"\"\"\r\n pattern = r'\\\"(.*)\\\"\\\u0026\\\"(.*)\\\"'\r\n new_file = []\r\n for line in lines:\r\n line = line.strip()\r\n res = re.search(pattern, line)\r\n if res:\r\n new_line = re.sub(pattern,\r\nhttps://research.checkpoint.com/2020/exploring-qbots-latest-attack-methods/\r\nPage 26 of 32\n\nlambda match: '\\\"' + match.group(1) + match.group(2) + '\\\"'\r\n ,line)\r\n else:\r\n new_line = line\r\n new_file.append(new_line)\r\n return new_file\r\ndef remove_trailing_zeros(lines):\r\n \"\"\"Removes all trailing NULL bytes from a file.\r\n Args:\r\n lines (list): List of lines.\r\n Returns:\r\n list: List of modified lines.\r\n \"\"\"\r\n new_file = []\r\n for line in lines:\r\n if len(line) \u003e 0 and line[0] == '\\x00':\r\n continue\r\n new_file.append(line)\r\n return new_file\r\ndef deobfuscate_file(fpath_in, fpath_out):\r\n \"\"\"Converts Qbot VBS script into it's deobfuscated form.\r\n Main changes are:\r\n - removing stub calculations\r\n - converting \"chr(*)\" into their respective characters.\r\n - converting \"replace(*)\" into its respective string.\r\n - converting VB concatenations (\"\u0026\") into the final string.\r\n - removing trailing NULL bytes.\r\n Args:\r\n fpath_in (str): Input VBS file path.\r\n fpath_out (str): Output file path.\r\n \"\"\"\r\n try:\r\n with open(fpath_in, 'r') as f_in:\r\n in_data = f_in.read()\r\n except:\r\n return None\r\n lines = in_data.split('\\n')\r\n lines = remove_additions(lines)\r\n lines = remove_chr(lines)\r\n lines = remove_replace(lines)\r\n for _ in range(100):\r\n lines = remove_concat(lines)\r\n lines = remove_trailing_zeros(lines)\r\n new_file_joined = '\\n'.join(lines)\r\n try:\r\n with open(fpath_out, 'w') as f_out:\r\n f_out.write(new_file_joined)\r\n except:\r\n return None\r\ndef decrypt_data(enc_str, keys):\r\n \"\"\"Decrypts long blob of text data.\r\n decryption method is looking for patterns of decimal numbers,\r\n and xor them with the key.\r\n do that with three different keys.\r\n Arguments:\r\n enc_str {string} -- encrypted data\r\n key1 {int} -- first key\r\n key2 {int} -- second key\r\n key3 {int} -- third key\r\nhttps://research.checkpoint.com/2020/exploring-qbots-latest-attack-methods/\r\nPage 27 of 32\n\n\"\"\"\r\n def _decrypt_data_inner(str_param, key_param):\r\n \"\"\"Helper method. actual decryption.\r\n \"\"\"\r\n numbers = \"\"\r\n ret_decrypted = \"\"\r\n f = True\r\n for i in range(len(str_param)):\r\n if '0' \u003c= str_param[i] \u003c= '9':\r\n numbers = numbers + str_param[i]\r\n f = True\r\n else:\r\n if f:\r\n try:\r\n enc_ch = int(numbers)\r\n except:\r\n break\r\n dec_ch = enc_ch ^ key_param\r\n ret_decrypted = ret_decrypted + chr(dec_ch)\r\n numbers = \"\"\r\n f = False\r\n return ret_decrypted\r\n key1 = keys[0]\r\n key2 = keys[1]\r\n key3 = keys[2]\r\n enc_str = _decrypt_data_inner(enc_str, key1)\r\n enc_str = _decrypt_data_inner(enc_str, key2)\r\n return _decrypt_data_inner(enc_str, key3)\r\nclass qbot_vbs(object):\r\n \"\"\"Encapsulates qbot VBS artifacts.\r\n These artifacts are used for extraction.\r\n \"\"\"\r\n num_urls = 0\r\n enc_str = None\r\n key_str = None\r\n seed = 0\r\n key_idxs = [None, None, None]\r\n def __init__(self, data):\r\n self.data = data\r\n self.lines = data.split('\\n')\r\n def _extract_number_urls(self):\r\n \"\"\"Extracts number of encrypted urls.\r\n \"\"\"\r\n # sample:\r\n # number of urls: for i=1 to 6\r\n pattern = r'[F,f]or i=1 to (\\d+)'\r\n res = re.search(pattern, '\\n'.join(self.lines))\r\n self.num_urls = int(res.group(1))\r\n def _extract_enc_str(self):\r\n \"\"\"Extracts the string which has the encrypted data.\r\n should be the biggest line in the script.\r\n \"\"\"\r\n max_len = 0\r\n max_idx = -1\r\n for i, line in enumerate(self.lines):\r\n if len(line) \u003e max_len and line[0] != '\\x00':\r\n max_len = len(line)\r\n max_idx = i\r\n # removing variable name.\r\n res = re.search(r'^\\w+ = \\\"(.*)\\\"$', self.lines[max_idx])\r\n self.enc_str = res.group(1)\r\n def _extract_key_str(self):\r\n \"\"\"Extracts the string which the key is based upon.\r\n should be called after `extract_enc_str()`.\r\nhttps://research.checkpoint.com/2020/exploring-qbots-latest-attack-methods/\r\nPage 28 of 32\n\nshould be the second biggest line after encrypted string.\r\n \"\"\"\r\n second_max_len = 0\r\n second_max_idx = -1\r\n max_len = len(self.enc_str)\r\n for i, line in enumerate(self.lines):\r\n if len(line) \u003e second_max_len and len(line) \u003c max_len and line[0] != '\\x00':\r\n second_max_len = len(line)\r\n second_max_idx = i\r\n # removing variable name.\r\n res = re.search(r'^\\w+ = \\\"(.*)\\\"$', self.lines[second_max_idx])\r\n self.key_str = res.group(1)\r\n def _extract_seed_and_key_indexes(self):\r\n \"\"\"Helper function for key extraction.\r\n \"\"\"\r\n def _find_variables(var1_name, var2_name):\r\n \"\"\"Finds variables values for two vars.\r\n for example:\r\n DgZlWOk = 8\r\n jRryhge = 4\r\n \"\"\"\r\n pattern1 = fr'^{var1_name} = (\\d+)$'\r\n pattern2 = fr'^{var2_name} = (\\d+)$'\r\n var1_value = None\r\n var2_value = None\r\n for line in self.lines:\r\n res = re.search(pattern1, line)\r\n if res:\r\n var1_value = res.group(1)\r\n res = re.search(pattern2, line)\r\n if res:\r\n var2_value = res.group(1)\r\n return var1_value, var2_value\r\n def _extract_key_indexes(lines):\r\n # we have 6 'Mid' encounters:\r\n # yaGlYs = Mid(xHAaMv, 10, 2)\r\n # RLquKjB = Asc(Mid(HIbAriX, seSclZ, 1))\r\n # three times.\r\n # the second is not interesting for us.\r\n text = '\\n'.join(lines)\r\n res = re.findall(r'Mid\\(\\w+\\, (\\w+)\\, \\w+\\)', text)\r\n # the key creation order is reversed to their using. (first key3 is set and so on)\r\n self.key_idxs[0] = int(res[4])\r\n self.key_idxs[1] = int(res[2])\r\n self.key_idxs[2] = int(res[0])\r\n # sample line:\r\n # For uLRYNs = 0 To 2387414 Step 1\r\n pattern1 = r' (238\\d\\d\\d\\d) '\r\n # sample line:\r\n # YLTCm = YLTCm + DgZlWOk - jRryhge\r\n pattern2 = r'^(\\w+) = (\\1) \\+ (\\w+) - (\\w+)$'\r\n for i, line in enumerate(self.lines):\r\n res = re.search(pattern1, line)\r\n if res:\r\n num = int(res.group(1))\r\n _extract_key_indexes(self.lines[i+1:])\r\n for inner_line in self.lines[i+1:i+10]:\r\n res = re.search(pattern2, inner_line)\r\n if res:\r\nhttps://research.checkpoint.com/2020/exploring-qbots-latest-attack-methods/\r\nPage 29 of 32\n\nfirst_param = res.group(3)\r\n second_param = res.group(4)\r\n first_value, second_value = _find_variables(first_param, second_param)\r\n num += 1\r\n num *= (int(first_value) - int(second_value))\r\n self.seed = num\r\n return\r\n def extract_keys(self):\r\n \"\"\"Main extraction method.\r\n Extracts keys for the URL decryption.\r\n Returns:\r\n list: List of 3 keys.\r\n \"\"\"\r\n vbs._extract_number_urls()\r\n vbs._extract_enc_str()\r\n vbs._extract_key_str()\r\n vbs._extract_seed_and_key_indexes()\r\n seed = self.seed * 999999\r\n str_seed = str(seed)\r\n idx = int(str_seed[self.key_idxs[2] - 1:self.key_idxs[2] + 1])\r\n key3 = ord(self.key_str[idx - 1])\r\n idx = int(str_seed[self.key_idxs[1] - 1:self.key_idxs[1] + 1])\r\n key2 = ord(self.key_str[idx - 1])\r\n idx = int(str_seed[self.key_idxs[0] - 1:self.key_idxs[0] + 1])\r\n key1 = ord(self.key_str[idx - 1])\r\n return key1, key2, key3\r\nif __name__ == \"__main__\":\r\n if len(sys.argv) != 2:\r\n print(f\"Usage: python {os.path.basename(__file__)} \u003cfpath_in\u003e\")\r\n exit(1)\r\n fname_tmp = 'tmp'\r\n deobfuscate_file(sys.argv[1], fname_tmp)\r\n if not os.path.exists(fname_tmp):\r\n print(\"Failed de-obfuscation script.\")\r\n exit(0)\r\n with open(fname_tmp) as f:\r\n data = f.read()\r\n os.remove(fname_tmp)\r\n vbs = qbot_vbs(data)\r\n keys = vbs.extract_keys()\r\n dec = decrypt_data(vbs.enc_str, keys).strip('\\ufeff').split('_______')\r\n for i in range(vbs.num_urls):\r\n url = dec[i]\r\n url = url.split('?')[0].strip()\r\n print(url)\r\nAppendix C: JavaScript Updater URL Extraction Script\r\ndef extract_urls_from_js_updater(js_data):\r\n\"\"\"Extracts update URLs out of given Qbot Javascript updater.\r\njs_data (str or bytes): Javascript code content.\r\nhttps://research.checkpoint.com/2020/exploring-qbots-latest-attack-methods/\r\nPage 30 of 32\n\nlist: Returns list of extracted URLs or None if falied.\r\nif isinstance(js_data, bytes):\r\njs_data = js_data.decode('ascii')\r\n# var WcrApaqyDNEBJYsFkiXPVzHCeKGmnxd = [30,209...19];\r\npattern = re.compile(r\"^\\s*var [a-zA-Z0-9]+\\s?=\\s?\\[(([0-9]+,)+)([0-9]+)\\];$\")\r\nfor line in js_data.splitlines():\r\nmatch = pattern.match(line)\r\narray = match.group(1) + match.group(3)\r\nsuffix = 'datacollectionservice.php3'\r\nbase_values = [int(c) for c in arrays[0].split(\",\")]\r\nxor_values = [int(c) for c in arrays[1].split(\",\")]\r\nfor i in range(len(base_values)):\r\nres += chr(base_values[i] ^ xor_values[i % len(xor_values)])\r\nurls = ['http://' + server + '/' + suffix for server in servers]\r\nimport re import os def extract_urls_from_js_updater(js_data): \"\"\"Extracts update URLs out of given Qbot Javascript\r\nupdater. Args: js_data (str or bytes): Javascript code content. Returns: list: Returns list of extracted URLs or None if falied.\r\n\"\"\" try: if isinstance(js_data, bytes): js_data = js_data.decode('ascii') except: return None arrays = [] # var\r\nWcrApaqyDNEBJYsFkiXPVzHCeKGmnxd = [30,209...19]; # encrypted urls pattern = re.compile(r\"^\\s*var [a-zA-Z0-\r\n9]+\\s?=\\s?\\[(([0-9]+,)+)([0-9]+)\\];$\") for line in js_data.splitlines(): match = pattern.match(line) if match: array =\r\nmatch.group(1) + match.group(3) arrays.append(array) if not len(arrays) == 2: return None suffix =\r\n'datacollectionservice.php3' # encrypted text base_values = [int(c) for c in arrays[0].split(\",\")] # key xor_values = [int(c) for\r\nc in arrays[1].split(\",\")] res = \"\" for i in range(len(base_values)): res += chr(base_values[i] ^ xor_values[i %\r\nlen(xor_values)]) servers = res.split(\";\") urls = ['http://' + server + '/' + suffix for server in servers] return urls\r\nimport re\r\nimport os\r\ndef extract_urls_from_js_updater(js_data):\r\n \"\"\"Extracts update URLs out of given Qbot Javascript updater.\r\n Args:\r\n js_data (str or bytes): Javascript code content.\r\n Returns:\r\n list: Returns list of extracted URLs or None if falied.\r\n \"\"\"\r\n try:\r\n if isinstance(js_data, bytes):\r\n js_data = js_data.decode('ascii')\r\n except:\r\n return None\r\n arrays = []\r\n # var WcrApaqyDNEBJYsFkiXPVzHCeKGmnxd = [30,209...19];\r\n # encrypted urls\r\n pattern = re.compile(r\"^\\s*var [a-zA-Z0-9]+\\s?=\\s?\\[(([0-9]+,)+)([0-9]+)\\];$\")\r\n for line in js_data.splitlines():\r\n match = pattern.match(line)\r\n if match:\r\n array = match.group(1) + match.group(3)\r\n arrays.append(array)\r\n if not len(arrays) == 2:\r\n return None\r\nhttps://research.checkpoint.com/2020/exploring-qbots-latest-attack-methods/\r\nPage 31 of 32\n\nsuffix = 'datacollectionservice.php3'\r\n # encrypted text\r\n base_values = [int(c) for c in arrays[0].split(\",\")]\r\n # key\r\n xor_values = [int(c) for c in arrays[1].split(\",\")]\r\n res = \"\"\r\n for i in range(len(base_values)):\r\n res += chr(base_values[i] ^ xor_values[i % len(xor_values)])\r\n servers = res.split(\";\")\r\n urls = ['http://' + server + '/' + suffix for server in servers]\r\n \r\n return urls\r\nSource: https://research.checkpoint.com/2020/exploring-qbots-latest-attack-methods/\r\nhttps://research.checkpoint.com/2020/exploring-qbots-latest-attack-methods/\r\nPage 32 of 32\n\n$s6 = \"if len(ms.responseBody) $s7 = /if left\\(ms.responseText, \u003c\u003e 0 then\" \\w*?\\) = \\\"MZ\\\" then/\nfilesize \u003e 20MB and $s3 and $s4 and $s5 and $s6 and $s7\n  Page 19 of 32",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"ETDA",
		"Malpedia"
	],
	"origins": [
		"web"
	],
	"references": [
		"https://research.checkpoint.com/2020/exploring-qbots-latest-attack-methods/"
	],
	"report_names": [
		"exploring-qbots-latest-attack-methods"
	],
	"threat_actors": [
		{
			"id": "cf7fc640-acfe-41c4-9f3d-5515d53a3ffb",
			"created_at": "2023-01-06T13:46:38.228042Z",
			"updated_at": "2026-04-10T02:00:02.883048Z",
			"deleted_at": null,
			"main_name": "APT1",
			"aliases": [
				"PLA Unit 61398",
				"Comment Crew",
				"Byzantine Candor",
				"Comment Group",
				"GIF89a",
				"Group 3",
				"TG-8223",
				"Brown Fox",
				"ShadyRAT",
				"G0006",
				"COMMENT PANDA"
			],
			"source_name": "MISPGALAXY:APT1",
			"tools": [],
			"source_id": "MISPGALAXY",
			"reports": null
		},
		{
			"id": "761d1fb2-60e3-46f0-9f1c-c8a9715967d4",
			"created_at": "2023-01-06T13:46:38.269054Z",
			"updated_at": "2026-04-10T02:00:02.90356Z",
			"deleted_at": null,
			"main_name": "APT3",
			"aliases": [
				"GOTHIC PANDA",
				"TG-0110",
				"Buckeye",
				"Group 6",
				"Boyusec",
				"BORON",
				"BRONZE MAYFAIR",
				"Red Sylvan",
				"Brocade Typhoon"
			],
			"source_name": "MISPGALAXY:APT3",
			"tools": [],
			"source_id": "MISPGALAXY",
			"reports": null
		},
		{
			"id": "4b066585-3591-4ddd-b3cc-f4e19e0e00ef",
			"created_at": "2022-10-25T16:07:24.086915Z",
			"updated_at": "2026-04-10T02:00:04.862463Z",
			"deleted_at": null,
			"main_name": "Putter Panda",
			"aliases": [
				"4HCrew",
				"APT 2",
				"G0024",
				"Group 36",
				"Putter Panda",
				"SearchFire",
				"TG-6952"
			],
			"source_name": "ETDA:Putter Panda",
			"tools": [
				"3PARA RAT",
				"4H RAT",
				"4h_rat",
				"MSUpdater",
				"httpclient",
				"pngdowner"
			],
			"source_id": "ETDA",
			"reports": null
		},
		{
			"id": "06f622cb-3a78-49cf-9a4c-a6007a69325f",
			"created_at": "2022-10-25T16:07:23.315239Z",
			"updated_at": "2026-04-10T02:00:04.537826Z",
			"deleted_at": null,
			"main_name": "APT 3",
			"aliases": [
				"APT 3",
				"Boron",
				"Brocade Typhoon",
				"Bronze Mayfair",
				"Buckeye",
				"G0022",
				"Gothic Panda",
				"Group 6",
				"Operation Clandestine Fox",
				"Operation Clandestine Fox, Part Deux",
				"Operation Clandestine Wolf",
				"Operation Double Tap",
				"Red Sylvan",
				"TG-0110",
				"UPS Team"
			],
			"source_name": "ETDA:APT 3",
			"tools": [
				"APT3 Keylogger",
				"Agent.dhwf",
				"BKDR_HUPIGON",
				"Backdoor.APT.CookieCutter",
				"Badey",
				"Bemstour",
				"CookieCutter",
				"Destroy RAT",
				"DestroyRAT",
				"DoublePulsar",
				"EXL",
				"EternalBlue",
				"HTran",
				"HUC Packet Transmit Tool",
				"Hupigon",
				"Hupigon RAT",
				"Kaba",
				"Korplug",
				"LaZagne",
				"MFC Huner",
				"OSInfo",
				"Pirpi",
				"PlugX",
				"RedDelta",
				"RemoteCMD",
				"SHOTPUT",
				"Sogu",
				"TIGERPLUG",
				"TTCalc",
				"TVT",
				"Thoper",
				"Xamtrav",
				"remotecmd",
				"shareip",
				"w32times"
			],
			"source_id": "ETDA",
			"reports": null
		},
		{
			"id": "3aaf0755-5c9b-4612-9f0e-e266ef1bdb4b",
			"created_at": "2022-10-25T16:07:23.480196Z",
			"updated_at": "2026-04-10T02:00:04.626125Z",
			"deleted_at": null,
			"main_name": "Comment Crew",
			"aliases": [
				"APT 1",
				"BrownFox",
				"Byzantine Candor",
				"Byzantine Hades",
				"Comment Crew",
				"Comment Panda",
				"G0006",
				"GIF89a",
				"Group 3",
				"Operation Oceansalt",
				"Operation Seasalt",
				"Operation Siesta",
				"Shanghai Group",
				"TG-8223"
			],
			"source_name": "ETDA:Comment Crew",
			"tools": [
				"Auriga",
				"Cachedump",
				"Chymine",
				"CookieBag",
				"Darkmoon",
				"GDOCUPLOAD",
				"GLOOXMAIL",
				"GREENCAT",
				"Gen:Trojan.Heur.PT",
				"GetMail",
				"Hackfase",
				"Hacksfase",
				"Helauto",
				"Kurton",
				"LETSGO",
				"LIGHTBOLT",
				"LIGHTDART",
				"LOLBAS",
				"LOLBins",
				"LONGRUN",
				"Living off the Land",
				"Lslsass",
				"MAPIget",
				"ManItsMe",
				"Mimikatz",
				"MiniASP",
				"Oceansalt",
				"Pass-The-Hash Toolkit",
				"Poison Ivy",
				"ProcDump",
				"Riodrv",
				"SPIVY",
				"Seasalt",
				"ShadyRAT",
				"StarsyPound",
				"TROJAN.COOKIES",
				"TROJAN.FOXY",
				"TabMsgSQL",
				"Tarsip",
				"Trojan.GTALK",
				"WebC2",
				"WebC2-AdSpace",
				"WebC2-Ausov",
				"WebC2-Bolid",
				"WebC2-Cson",
				"WebC2-DIV",
				"WebC2-GreenCat",
				"WebC2-Head",
				"WebC2-Kt3",
				"WebC2-Qbp",
				"WebC2-Rave",
				"WebC2-Table",
				"WebC2-UGX",
				"WebC2-Yahoo",
				"Wordpress Bruteforcer",
				"bangat",
				"gsecdump",
				"pivy",
				"poisonivy",
				"pwdump",
				"zxdosml"
			],
			"source_id": "ETDA",
			"reports": null
		}
	],
	"ts_created_at": 1775433999,
	"ts_updated_at": 1775826707,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/b98e38c2710425cb24df7bb7d15af7279b4b7071.pdf",
		"text": "https://archive.orkl.eu/b98e38c2710425cb24df7bb7d15af7279b4b7071.txt",
		"img": "https://archive.orkl.eu/b98e38c2710425cb24df7bb7d15af7279b4b7071.jpg"
	}
}