Reading view

There are new articles available, click to refresh the page.

Sifting through the spines: identifying (potential) Cactus ransomware victims

Authored by Willem Zeeman and Yun Zheng Hu

This blog is part of a series written by various Dutch cyber security firms that have collaborated on the Cactus ransomware group, which exploits Qlik Sense servers for initial access. To view all of them please check the central blog by Dutch special interest group Cyberveilig Nederland [1]

The effectiveness of the public-private partnership called Melissa [2] is increasingly evident. The Melissa partnership, which includes Fox-IT, has identified overlap in a specific ransomware tactic. Multiple partners, sharing information from incident response engagements for their clients, found that the Cactus ransomware group uses a particular method for initial access. Following that discovery, NCC Group’s Fox-IT developed a fingerprinting technique to identify which systems around the world are vulnerable to this method of initial access or, even more critically, are already compromised.

Qlik Sense vulnerabilities

Qlik Sense, a popular data visualisation and business intelligence tool, has recently become a focal point in cybersecurity discussions. This tool, designed to aid businesses in data analysis, has been identified as a key entry point for cyberattacks by the Cactus ransomware group.

The Cactus ransomware campaign

Since November 2023, the Cactus ransomware group has been actively targeting vulnerable Qlik Sense servers. These attacks are not just about exploiting software vulnerabilities; they also involve a psychological component where Cactus misleads its victims with fabricated stories about the breach. This likely is part of their strategy to obscure their actual method of entry, thus complicating mitigation and response efforts for the affected organizations.

For those looking for in-depth coverage of these exploits, the Arctic Wolf blog [3] provides detailed insights into the specific vulnerabilities being exploited, notably CVE-2023-41266, CVE-2023-41265 also known as ZeroQlik, and potentially CVE-2023-48365 also known as DoubleQlik.

Threat statistics and collaborative action

The scope of this threat is significant. In total, we identified 5205 Qlik Sense servers, 3143 servers seem to be vulnerable to the exploits used by the Cactus group. This is based on the initial scan on 17 April 2024. Closer to home in the Netherlands, we’ve identified 241 vulnerable systems, fortunately most don’t seem to have been compromised. However, 6 Dutch systems weren’t so lucky and have already fallen victim to the Cactus group. It’s crucial to understand that “already compromised” can mean that either the ransomware has been deployed and the initial access artifacts left behind were not removed, or the system remains compromised and is potentially poised for a future ransomware attack.

Since 17 April 2024, the DIVD (Dutch Institute for Vulnerability Disclosure) and the governmental bodies NCSC (Nationaal Cyber Security Centrum) and DTC (Digital Trust Center) have teamed up to globally inform (potential) victims of cyberattacks resembling those from the Cactus ransomware group. This collaborative effort has enabled them to reach out to affected organisations worldwide, sharing crucial information to help prevent further damage where possible.

Identifying vulnerable Qlik Sense servers

Expanding on Praetorian’s thorough vulnerability research on the ZeroQlik and DoubleQlik vulnerabilities [4,5], we found a method to identify the version of a Qlik Sense server by retrieving a file called product-info.json from the server. While we acknowledge the existence of Nuclei templates for the vulnerability checks, using the server version allows for a more reliable evaluation of potential vulnerability status, e.g. whether it’s patched or end of support.

This JSON file contains the release label and version numbers by which we can identify the exact version that this Qlik Sense server is running.

Figure 1: Qlik Sense product-info.json file containing version information

Keep in mind that although Qlik Sense servers are assigned version numbers, the vendor typically refers to advisories and updates by their release label, such as “February 2022 Patch 3”.

The following cURL command can be used to retrieve the product-info.json file from a Qlik server:

curl -H "Host: localhost" -vk 'https://<ip>/resources/autogenerated/product-info.json?.ttf'

Note that we specify ?.ttf at the end of the URL to let the Qlik proxy server think that we are requesting a .ttf file, as font files can be accessed unauthenticated. Also, we set the Host header to localhost or else the server will return 400 - Bad Request - Qlik Sense, with the message The http request header is incorrect.

Retrieving this file with the ?.ttf extension trick has been fixed in the patch that addresses CVE-2023-48365 and you will always get a 302 Authenticate at this location response:

> GET /resources/autogenerated/product-info.json?.ttf HTTP/1.1
> Host: localhost
> Accept: */*
>
< HTTP/1.1 302 Authenticate at this location
< Cache-Control: no-cache, no-store, must-revalidate
< Location: https://localhost/internal_forms_authentication/?targetId=2aa7575d-3234-4980-956c-2c6929c57b71
< Content-Length: 0
<

Nevertheless, this is still a good way to determine the state of a Qlik instance, because if it redirects using 302 Authenticate at this location it is likely that the server is not vulnerable to CVE-2023-48365.

An example response from a vulnerable server would return the JSON file:

> GET /resources/autogenerated/product-info.json?.ttf HTTP/1.1
> Host: localhost
> Accept: */*
>
< HTTP/1.1 200 OK
< Set-Cookie: X-Qlik-Session=893de431-1177-46aa-88c7-b95e28c5f103; Path=/; HttpOnly; SameSite=Lax; Secure
< Cache-Control: public, max-age=3600
< Transfer-Encoding: chunked
< Content-Type: application/json;charset=utf-8
< Expires: Tue, 16 Apr 2024 08:14:56 GMT
< Last-Modified: Fri, 04 Nov 2022 23:28:24 GMT
< Accept-Ranges: bytes
< ETag: 638032013040000000
< Server: Microsoft-HTTPAPI/2.0
< Date: Tue, 16 Apr 2024 07:14:55 GMT
< Age: 136
<
{"composition":{"contentHash":"89c9087978b3f026fb100267523b5204","senseId":"qliksenseserver:14.54.21","releaseLabel":"February 2022 Patch 12","originalClassName":"Composition","deprecatedProductVersion":"4.0.X","productName":"Qlik Sense","version":"14.54.21","copyrightYearRange":"1993-2022","deploymentType":"QlikSenseServer"},
<snipped>

We utilised Censys and Google BigQuery [6] to compile a list of potential Qlik Sense servers accessible on the internet and conducted a version scan against them. Subsequently, we extracted the Qlik release label from the JSON response to assess vulnerability to CVE-2023-48365.

Our vulnerability assessment for DoubleQlik / CVE-2023-48365 operated on the following criteria:

  1. The release label corresponds to vulnerability statuses outlined in the original ZeroQlik and DoubleQlik vendor advisories [7,8].
  2. The release label is designated as End of Support (EOS) by the vendor [9], such as “February 2019 Patch 5”.

We consider a server non-vulnerable if:

  1. The release label date is post-November 2023, as the advisory states that “November 2023” is not affected.
  2. The server responded with HTTP/1.1 302 Authenticate at this location.

Any other responses were disregarded as invalid Qlik server instances.

As of 17 April 2024, and as stated in the introduction of this blog, we have detected 5205 Qlik Servers on the Internet. Among them, 3143 servers are still at risk of DoubleQlik, indicating that 60% of all Qlik Servers online remain vulnerable.

Figure 2: Qlik Sense patch status for DoubleQlik CVE-2023-48365

The majority of vulnerable Qlik servers reside in the United States (396), trailed by Italy (280), Brazil (244), the Netherlands (241), and Germany (175).

Figure 3: Top 20 countries with servers vulnerable to DoubleQlik CVE-2023-48365

Identifying compromised Qlik Sense servers

Based on insights gathered from the Arctic Wolf blog and our own incident response engagements where the Cactus ransomware was observed, it’s evident that the Cactus ransomware group continues to redirect the output of executed commands to a True Type font file named qle.ttf, likely abbreviated for “qlik exploit”.

Below are a few examples of executed commands and their output redirection by the Cactus ransomware group:

whoami /all > ../Client/qmc/fonts/qle.ttf
quser > ../Client/qmc/fonts/qle.ttf

In addition to the qle.ttf file, we have also observed instances where qle.woff was used:

Figure 4: Directory listing with exploitation artefacts left by Cactus ransomware group

It’s important to note that these font files are not part of a default Qlik Sense server installation.

We discovered that files with a font file extension such as .ttf and .woff can be accessed without any authentication, regardless of whether the server is patched. This likely explains why the Cactus ransomware group opted to store command output in font files within the fonts directory, which in turn, also serves as a useful indicator of compromise.

Our scan for both font files, found a total of 122 servers with the indicator of compromise. The United States ranked highest in exploited servers with 49 online instances carrying the indicator of compromise, followed by Spain (13), Italy (11), the United Kingdom (8), Germany (7), and then Ireland and the Netherlands (6).

Figure 5: Top 20 countries with known compromised Qlik Sense servers

Out of the 122 compromised servers, 46 were not vulnerable anymore.

When the indicator of compromise artefact is present on a remote Qlik Sense server, it can imply various scenarios. Firstly, it may suggest that remote code execution was carried out on the server, followed by subsequent patching to address the vulnerability (if the server is not vulnerable anymore). Alternatively, its presence could signify a leftover artefact from a previous security incident or unauthorised access.

While the root cause for the presence of these files is hard to determine from the outside it still is a reliable indicator of compromise.

Responsible disclosure by the DIVD
We shared our fingerprints and scan data with the Dutch Institute of Vulnerability Disclosure (DIVD), who then proceeded to issue responsible disclosure notifications to the administrators of the Qlik Sense servers.

Call to action

Ensure the security of your Qlik Sense installations by checking your current version. If your software is still supported, apply the latest patches immediately. For systems that are at the end of support, consider upgrading or replacing them to maintain robust security.

Additionally, to enhance your defences, it’s recommended to avoid exposing these services to the entire internet. Implement IP whitelisting if public access is necessary, or better yet, make them accessible only through secure remote working solutions.

If you discover you’ve been running a vulnerable version, it’s crucial to contact your (external) security experts for a thorough check-up to confirm that no breaches have occurred. Taking these steps will help safeguard your data and infrastructure from potential threats.

References

  1. https://cyberveilignederland.nl/actueel/persbericht-samenwerkingsverband-melissa-vindt-diverse-nederlandse-slachtoffers-van-ransomwaregroepering-cactus ↩
  2. https://www.ncsc.nl/actueel/nieuws/2023/oktober/3/melissa-samenwerkingsverband-ransomwarebestrijding ↩
  3. https://arcticwolf.com/resources/blog/qlik-sense-exploited-in-cactus-ransomware-campaign/ ↩
  4. https://www.praetorian.com/blog/qlik-sense-technical-exploit/ ↩
  5. https://www.praetorian.com/blog/doubleqlik-bypassing-the-original-fix-for-cve-2023-41265/ ↩
  6. https://support.censys.io/hc/en-us/articles/360038759991-Google-BigQuery-Introduction ↩
  7. https://community.qlik.com/t5/Official-Support-Articles/Critical-Security-fixes-for-Qlik-Sense-Enterprise-for-Windows/ta-p/2110801 ↩
  8. https://community.qlik.com/t5/Official-Support-Articles/Critical-Security-fixes-for-Qlik-Sense-Enterprise-for-Windows/ta-p/2120325 ↩
  9. https://community.qlik.com/t5/Product-Lifecycle/Qlik-Sense-Enterprise-on-Windows-Product-Lifecycle/ta-p/1826335 ↩

Android Malware Vultur Expands Its Wingspan

Authored by Joshua Kamp

Executive summary

The authors behind Android banking malware Vultur have been spotted adding new technical features, which allow the malware operator to further remotely interact with the victim’s mobile device. Vultur has also started masquerading more of its malicious activity by encrypting its C2 communication, using multiple encrypted payloads that are decrypted on the fly, and using the guise of legitimate applications to carry out its malicious actions.

Key takeaways

  • The authors behind Vultur, an Android banker that was first discovered in March 2021, have been spotted adding new technical features.
  • New technical features include the ability to:
    • Download, upload, delete, install, and find files;
    • Control the infected device using Android Accessibility Services (sending commands to perform scrolls, swipe gestures, clicks, mute/unmute audio, and more);
    • Prevent apps from running;
    • Display a custom notification in the status bar;
    • Disable Keyguard in order to bypass lock screen security measures.
  • While the new features are mostly related to remotely interact with the victim’s device in a more flexible way, Vultur still contains the remote access functionality using AlphaVNC and ngrok that it had back in 2021.
  • Vultur has improved upon its anti-analysis and detection evasion techniques by:
    • Modifying legitimate apps (use of McAfee Security and Android Accessibility Suite package name);
    • Using native code in order to decrypt payloads;
    • Spreading malicious code over multiple payloads;
    • Using AES encryption and Base64 encoding for its C2 communication.

Introduction

Vultur is one of the first Android banking malware families to include screen recording capabilities. It contains features such as keylogging and interacting with the victim’s device screen. Vultur mainly targets banking apps for keylogging and remote control. Vultur was first discovered by ThreatFabric in late March 2021. Back then, Vultur (ab)used the legitimate software products AlphaVNC and ngrok for remote access to the VNC server running on the victim’s device. Vultur was distributed through a dropper-framework called Brunhilda, responsible for hosting malicious applications on the Google Play Store [1]. The initial blog on Vultur uncovered that there is a notable connection between these two malware families, as they are both developed by the same threat actors [2].

In a recent campaign, the Brunhilda dropper is spread in a hybrid attack using both SMS and a phone call. The first SMS message guides the victim to a phone call. When the victim calls the number, the fraudster provides the victim with a second SMS that includes the link to the dropper: a modified version of the McAfee Security app.

The dropper deploys an updated version of Vultur banking malware through 3 payloads, where the final 2 Vultur payloads effectively work together by invoking each other’s functionality. The payloads are installed when the infected device has successfully registered with the Brunhilda Command-and-Control (C2) server. In the latest version of Vultur, the threat actors have added a total of 7 new C2 methods and 41 new Firebase Cloud Messaging (FCM) commands. Most of the added commands are related to remote access functionality using Android’s Accessibility Services, allowing the malware operator to remotely interact with the victim’s screen in a way that is more flexible compared to the use of AlphaVNC and ngrok.

In this blog we provide a comprehensive analysis of Vultur, beginning with an overview of its infection chain. We then delve into its new features, uncover its obfuscation techniques and evasion methods, and examine its execution flow. Following that, we dissect its C2 communication, discuss detection based on YARA, and draw conclusions. Let’s soar alongside Vultur’s smarter mobile malware strategies!

Infection chain

In order to deceive unsuspecting individuals into installing malware, the threat actors employ a hybrid attack using two SMS messages and a phone call. First, the victim receives an SMS message that instructs them to call a number if they did not authorise a transaction involving a large amount of money. In reality, this transaction never occurred, but it creates a false sense of urgency to trick the victim into acting quickly. A second SMS is sent during the phone call, where the victim is instructed into installing a trojanised version of the McAfee Security app from a link. This application is actually Brunhilda dropper, which looks benign to the victim as it contains functionality that the original McAfee Security app would have. As illustrated below, this dropper decrypts and executes a total of 3 Vultur-related payloads, giving the threat actors total control over the victim’s mobile device.

Figure 1: Visualisation of the complete infection chain. Note: communication with the C2 server occurs during every malware stage.

New features in Vultur

The latest updates to Vultur bring some interesting changes worth discussing. The most intriguing addition is the malware’s ability to remotely interact with the infected device through the use of Android’s Accessibility Services. The malware operator can now send commands in order to perform clicks, scrolls, swipe gestures, and more. Firebase Cloud Messaging (FCM), a messaging service provided by Google, is used for sending messages from the C2 server to the infected device. The message sent by the malware operator through FCM can contain a command, which, upon receipt, triggers the execution of corresponding functionality within the malware. This eliminates the need for an ongoing connection with the device, as can be seen from the code snippet below.

Figure 2: Decompiled code snippet showing Vultur’s ability to perform clicks and scrolls using Accessibility Services. Note for this (and upcoming) screenshot(s): some variables, classes and method names were renamed by the analyst. Pink strings indicate that they were decrypted.

While Vultur can still maintain an ongoing remote connection with the device through the use of AlphaVNC and ngrok, the new Accessibility Services related FCM commands provide the actor with more flexibility.

In addition to its more advanced remote control capabilities, Vultur introduced file manager functionality in the latest version. The file manager feature includes the ability to download, upload, delete, install, and find files. This effectively grants the actor(s) with even more control over the infected device.

Figure 3: Decompiled code snippet showing part of the file manager related functionality.

Another interesting new feature is the ability to block the victim from interacting with apps on the device. Regarding this functionality, the malware operator can specify a list of apps to press back on when detected as running on the device. The actor can include custom HTML code as a “template” for blocked apps. The list of apps to block and the corresponding HTML code to be displayed is retrieved through the vnc.blocked.packages C2 method. This is then stored in the app’s SharedPreferences. If available, the HTML code related to the blocked app will be displayed in a WebView after it presses back. If no HTML code is set for the app to block, it shows a default “Temporarily Unavailable” message after pressing back. For this feature, payload #3 interacts with code defined in payload #2.

Figure 4: Decompiled code snippet showing part of Vultur’s implementation for blocking apps.

The use of Android’s Accessibility Services to perform RAT related functionality (such as pressing back, performing clicks and swipe gestures) is something that is not new in Android malware. In fact, it is present in most Android bankers today. The latest features in Vultur show that its actors are catching up with this trend, and are even including functionality that is less common in Android RATs and bankers, such as controlling the device volume.

A full list of Vultur’s updated and new C2 methods / FCM commands can be found in the “C2 Communication” section of this blog.

Obfuscation techniques & detection evasion

Like a crafty bird camouflaging its nest, Vultur now employs a set of new obfuscation and detection evasion techniques when compared to its previous versions. Let’s look into some of the notable updates that set apart the latest variant from older editions of Vultur.

AES encrypted and Base64 encoded HTTPS traffic

In October 2022, ThreatFabric mentioned that Brunhilda started using string obfuscation using AES with a varying key in the malware samples themselves [3]. At this point in time, both Brunhilda and Vultur did not encrypt its HTTP requests. That has changed now, however, with the malware developer’s adoption of AES encryption and Base64 encoding requests in the latest variants.

Figure 5: Example AES encrypted and Base64 encoded request for bot registration.

By encrypting its communications, malware can evade detection of security solutions that rely on inspecting network traffic for known patterns of malicious activity. The decrypted content of the request can be seen below. Note that the list of installed apps is shown as Base64 encoded text, as this list is encoded before encryption.

{"id":"6500","method":"application.register","params":{"package":"com.wsandroid.suite","device":"Android/10","model":"samsung GT-I900","country":"sv-SE","apps":"cHQubm92b2JhbmNvLm5iYXBwO3B0LnNhbnRhbmRlcnRvdHRhLm1vYmlsZXBhcnRpY3VsYXJlcztzYS5hbHJhamhpYmFuay50YWh3ZWVsYXBwO3NhLmNvbS5zZS5hbGthaHJhYmE7c2EuY29tLnN0Y3BheTtzYW1zdW5nLnNldHRpbmdzLnBhc3M7c2Ftc3VuZy5zZXR0aW5ncy5waW47c29mdGF4LnBla2FvLnBvd2VycGF5O3RzYi5tb2JpbGViYW5raW5nO3VrLmNvLmhzYmMuaHNiY3VrbW9iaWxlYmFua2luZzt1ay5jby5tYm5hLmNhcmRzZXJ2aWNlcy5hbmRyb2lkO3VrLmNvLm1ldHJvYmFua29ubGluZS5tb2JpbGUuYW5kcm9pZC5wcm9kdWN0aW9uO3VrLmNvLnNhbnRhbmRlci5zYW50YW5kZXJVSzt1ay5jby50ZXNjb21vYmlsZS5hbmRyb2lkO3VrLmNvLnRzYi5uZXdtb2JpbGViYW5rO3VzLnpvb20udmlkZW9tZWV0aW5nczt3aXQuYW5kcm9pZC5iY3BCYW5raW5nQXBwLm1pbGxlbm5pdW07d2l0LmFuZHJvaWQuYmNwQmFua2luZ0FwcC5taWxsZW5uaXVtUEw7d3d3LmluZ2RpcmVjdC5uYXRpdmVmcmFtZTtzZS5zd2VkYmFuay5tb2JpbA==","tag":"dropper2"}

Utilisation of legitimate package names

The dropper is a modified version of the legitimate McAfee Security app. In order to masquerade malicious actions, it contains functionality that the official McAfee Security app would have. This has proven to be effective for the threat actors, as the dropper currently has a very low detection rate when analysed on VirusTotal.

Figure 6: Brunhilda dropper’s detection rate on VirusTotal.

Next to modding the legitimate McAfee Security app, Vultur uses the official Android Accessibility Suite package name for its Accessibility Service. This will be further discussed in the execution flow section of this blog.

Figure 7: Snippet of Vultur’s AndroidManifest.xml file, where its Accessibility Service is defined with the Android Accessibility Suite package name.

Leveraging native code for payload decryption

Native code is typically written in languages like C or C++, which are lower-level than Java or Kotlin, the most popular languages used for Android application development. This means that the code is closer to the machine language of the processor, thus requiring a deeper understanding of lower-level programming concepts. Brunhilda and Vultur have started using native code for decryption of payloads, likely in order to make the samples harder to reverse engineer.

Distributing malicious code across multiple payloads

In this blog post we show how Brunhilda drops a total of 3 Vultur-related payloads: two APK files and one DEX file. We also showcase how payload #2 and #3 can effectively work together. This fragmentation can complicate the analysis process, as multiple components must be assembled to reveal the malware’s complete functionality.

Execution flow: A three-headed… bird?

While previous versions of Brunhilda delivered Vultur through a single payload, the latest variant now drops Vultur in three layers. The Brunhilda dropper in this campaign is a modified version of the legitimate McAfee Security app, which makes it seem harmless to the victim upon execution as it includes functionality that the official McAfee Security app would have.

Figure 8: The modded version of the McAfee Security app is launched.

In the background, the infected device registers with its C2 server through the /ejr/ endpoint and the application.register method. In the related HTTP POST request, the C2 is provided with the following information:

  • Malware package name (as the dropper is a modified version of the McAfee Security app, it sends the official com.wsandroid.suite package name);
  • Android version;
  • Device model;
  • Language and country code (example: sv-SE);
  • Base64 encoded list of installed applications;
  • Tag (dropper campaign name, example: dropper2).

The server response is decrypted and stored in a SharedPreference key named 9bd25f13-c3f8-4503-ab34-4bbd63004b6e, where the value indicates whether the registration was successful or not. After successfully registering the bot with the dropper C2, the first Vultur payload is eventually decrypted and installed from an onClick() method.

Figure 9: Decryption and installation of the first Vultur payload.

In this sample, the encrypted data is hidden in a file named 78a01b34-2439-41c2-8ab7-d97f3ec158c6 that is stored within the app’s “assets” directory. When decrypted, this will reveal an APK file to be installed.

The decryption algorithm is implemented in native code, and reveals that it uses AES/ECB/PKCS5Padding to decrypt the first embedded file. The Lib.d() function grabs a substring from index 6 to 22 of the second argument (IPIjf4QWNMWkVQN21ucmNiUDZaVw==) to get the decryption key. The key used in this sample is: QWNMWkVQN21ucmNi (key varies across samples). With this information we can decrypt the 78a01b34-2439-41c2-8ab7-d97f3ec158c6 file, which brings us another APK file to examine: the first Vultur payload.

Layer 1: Vultur unveils itself

The first Vultur payload also contains the application.register method. The bot registers itself again with the C2 server as observed in the dropper sample. This time, it sends the package name of the current payload (se.accessibility.app in this example), which is not a modded application. The “tag” that was related to the dropper campaign is also removed in this second registration request. The server response contains an encrypted token for further communication with the C2 server and is stored in the SharedPreference key f9078181-3126-4ff5-906e-a38051505098.

Figure 10: Decompiled code snippet that shows the data to be sent to the C2 server during bot registration.

The main purpose of this first payload is to obtain Accessibility Service privileges and install the next Vultur APK file. Apps with Accessibility Service permissions can have full visibility over UI events, both from the system and from 3rd party apps. They can receive notifications, list UI elements, extract text, and more. While these services are meant to assist users, they can also be abused by malicious apps for activities, such as keylogging, automatically granting itself additional permissions, monitoring foreground apps and overlaying them with phishing windows.

In order to gain further control over the infected device, this payload displays custom HTML code that contains instructions to enable Accessibility Services permissions. The HTML code to be displayed in a WebView is retrieved from the installer.config C2 method, where the HTML code is stored in the SharedPreference key bbd1e64e-eba3-463c-95f3-c3bbb35b5907.

Figure 11: HTML code is loaded in a WebView, where the APP_NAME variable is replaced with the text “McAfee Master Protection”.

In addition to the HTML content, an extra warning message is displayed to further convince the victim into enabling Accessibility Service permissions for the app. This message contains the text “Your system not safe, service McAfee Master Protection turned off. For using full device protection turn it on.” When the warning is displayed, it also sets the value of the SharedPreference key 1590d3a3-1d8e-4ee9-afde-fcc174964db4 to true. This value is later checked in the onAccessibilityEvent() method and the onServiceConnected() method of the malicious app’s Accessibility Service.

ANALYST COMMENT
An important observation here, is that the malicious app is using the com.google.android.marvin.talkback package name for its Accessibility Service. This is the package name of the official Android Accessibility Suite, as can be seen from the following link: https://play.google.com/store/apps/details?id=com.google.android.marvin.talkback.
The implementation is of course different from the official Android Accessibility Suite and contains malicious code.

When the Accessibility Service privileges have been enabled for the payload, it automatically grants itself additional permissions to install apps from unknown sources, and installs the next payload through the UpdateActivity.

Figure 12: Decryption and installation of the second Vultur payload.

The second encrypted APK is hidden in a file named data that is stored within the app’s “assets” directory. The decryption algorithm is again implemented in native code, and is the same as in the dropper. This time, it uses a different decryption key that is derived from the DXMgKBY29QYnRPR1k1STRBNTZNUw== string. The substring reveals the actual key used in this sample: Y29QYnRPR1k1STRB (key varies across samples). After decrypting, we are presented with the next layer of Vultur.

Layer 2: Vultur descends

The second Vultur APK contains more important functionality, such as AlphaVNC and ngrok setup, displaying of custom HTML code in WebViews, screen recording, and more. Just like the previous versions of Vultur, the latest edition still includes the ability to remotely access the infected device through AlphaVNC and ngrok.

This second Vultur payload also uses the com.google.android.marvin.talkback (Android Accessibility Suite) package name for the malicious Accessibility Service. From here, there are multiple references to methods invoked from another file: the final Vultur payload. This time, the payload is not decrypted from native code. In this sample, an encrypted file named a.int is decrypted using AES/CFB/NoPadding with the decryption key SBhXcwoAiLTNIyLK (stored in SharedPreference key dffa98fe-8bf6-4ed7-8d80-bb1a83c91fbb). We have observed the same decryption key being used in multiple samples for decrypting payload #3.

Figure 13: Decryption of the third Vultur payload.

Furthermore, from payload #2 onwards, Vultur uses encrypted SharedPreferences for further hiding of malicious configuration related key-value pairs.

Layer 3: Vultur strikes

The final payload is a Dalvik Executable (DEX) file. This decrypted DEX file holds Vultur’s core functionality. It contains the references to all of the C2 methods (used in communication from bot to C2 server, in order to send or retrieve information) and FCM commands (used in communication from C2 server to bot, in order to perform actions on the infected device).

An important observation here, is that code defined in payload #3 can be invoked from payload #2 and vice versa. This means that these final two files effectively work together.

Figure 14: Decompiled code snippet showing some of the FCM commands implemented in Vultur payload #3.

The last Vultur payload does not contain its own Accessibility Service, but it can interact with the Accessibility Service that is implemented in payload #2.

C2 Communication: Vultur finds its voice

When Vultur infects a device, it initiates a series of communications with its designated C2 server. Communications related to C2 methods such as application.register and vnc.blocked.packages occur using JSON-RPC 2.0 over HTTPS. These requests are sent from the infected device to the C2 server to either provide or receive information.

Actual vultures lack a voice box; their vocalisations include rasping hisses and grunts [4]. While the communication in older variants of Vultur may have sounded somewhat similar to that, you could say that the threat actors have developed a voice box for the latest version of Vultur. The content of the aforementioned requests are now AES encrypted and Base64 encoded, just like the server response.

Next to encrypted communication over HTTPS, the bot can receive commands via Firebase Cloud Messaging (FCM). FCM is a cross-platform messaging solution provided by Google. The FCM related commands are sent from the C2 server to the infected device to perform actions on it.

During our investigation of the latest Vultur variant, we identified the C2 endpoints mentioned below.

EndpointDescription
/ejr/Endpoint for C2 communication using JSON-RPC 2.0.
Note: in older versions of Vultur the /rpc/ endpoint was used for similar communication.
/upload/Endpoint for uploading files (such as screen recording results).
/version/app/?filename=ngrok&arch={DEVICE_ARCH}Endpoint for downloading the relevant version of ngrok.
/version/app/?filename={FILENAME}Endpoint for downloading a file specified by the payload (related to the new file manager functionality).

C2 methods in Brunhilda dropper

The commands below are sent from the infected device to the C2 server to either provide or receive information.

MethodDescription
application.registerRegisters the bot by providing the malware package name and information about the device: model, country, installed apps, Android version. It also sends a tag that is used for identifying the dropper campaign name.
Note: this method is also used once in Vultur payload #1, but without sending a tag. This method then returns a token to be used in further communication with the C2 server.
application.stateSends a token value that was set as a response to the application.register command, together with a status code of “3”.

C2 methods in Vultur

The commands below are sent from the infected device to the C2 server to either provide or receive information.

MethodDescription
vnc.register (UPDATED)Registers the bot by providing the FCM token, malware package name and information about the device, model, country, Android version. This method has been updated in the latest version of Vultur to also include information on whether the infected device is rooted and if it is detected as an emulator.
vnc.status (UPDATED)Sends the following status information about the device: if the Accessibility Service is enabled, if the Device Admin permissions are enabled, if the screen is locked, what the VNC address is. This method has been updated in the latest version of Vultur to also send information related to: active fingerprints on the device, screen resolution, time, battery percentage, network operator, location.
vnc.appsSends the list of apps that are installed on the victim’s device.
vnc.keylogSends the keystrokes that were obtained via keylogging.
vnc.config (UPDATED)Obtains the config of the malware, such as the list of targeted applications by the keylogger and VNC. This method has been updated in the latest version of Vultur to also obtain values related to the following new keys: “packages2”, “rurl”, “recording”, “main_content”, “tvmq”.
vnc.overlayObtains the HTML code for overlay injections of a specified package name using the pkg parameter. It is still unclear whether support for overlay injections is fully implemented in Vultur.
vnc.overlay.logsSends the stolen credentials that were obtained via HTML overlay injections. It is still unclear whether support for overlay injections is fully implemented in Vultur.
vnc.pattern (NEW)Informs the C2 server whether a PIN pattern was successfully extracted and stored in the application’s Shared Preferences.
vnc.snapshot (NEW)Sends JSON data to the C2 server, which can contain:

1. Information about the accessibility event’s class, bounds, child nodes, UUID, event type, package name, text content, screen dimensions, time of the event, and if the screen is locked.
2. Recently copied text, and SharedPreferences values related to “overlay” and “keyboard”.
3. X and Y coordinates related to a click.
vnc.submit (NEW)Informs the C2 server whether the bot registration was successfully submitted or if it failed.
vnc.urls (NEW)Informs the C2 server about the URL bar related element IDs of either the Google Chrome or Firefox webbrowser (depending on which application triggered the accessibility event).
vnc.blocked.packages (NEW)Retrieves a list of “blocked packages” from the C2 server and stores them together with custom HTML code in the application’s Shared Preferences. When one of these package names is detected as running on the victim device, the malware will automatically press the back button and display custom HTML content if available. If unavailable, a default “Temporarily Unavailable” message is displayed.
vnc.fm (NEW)Sends file related information to the C2 server. File manager functionality includes downloading, uploading, installing, deleting, and finding of files.
vnc.syslogSends logs.
crash.logsSends logs of all content on the screen.
installer.config (NEW)Retrieves the HTML code that is displayed in a WebView of the first Vultur payload. This HTML code contains instructions to enable Accessibility Services permissions.

FCM commands in Vultur

The commands below are sent from the C2 server to the infected device via Firebase Cloud Messaging in order to perform actions on the infected device. The new commands use IDs instead of names that describe their functionality. These command IDs are the same in different samples.

CommandDescription
registeredReceived when the bot has been successfully registered.
startStarts the VNC connection using ngrok.
stopStops the VNC connection by killing the ngrok process and stopping the VNC service.
unlockUnlocks the screen.
deleteUninstalls the malware package.
patternProvides a gesture/stroke pattern to interact with the device’s screen.
109b0e16 (NEW)Presses the back button.
18cb31d4 (NEW)Presses the home button.
811c5170 (NEW)Shows the overview of recently opened apps.
d6f665bf (NEW)Starts an app specified by the payload.
1b05d6ee (NEW)Shows a black view.
1b05d6da (NEW)Shows a black view that is obtained from the layout resources in Vultur payload #2.
7f289af9 (NEW)Shows a WebView with HTML code loaded from SharedPreference key “946b7e8e”.
dc55afc8 (NEW)Removes the active black view / WebView that was added from previous commands (after sleeping for 15 seconds).
cbd534b9 (NEW)Removes the active black view / WebView that was added from previous commands (without sleeping).
4bacb3d6 (NEW)Deletes an app specified by the payload.
b9f92adb (NEW)Navigates to the settings of an app specified by the payload.
77b58a53 (NEW)Ensures that the device stays on by acquiring a wake lock, disables keyguard, sleeps for 0,1 second, and then swipes up to unlock the device without requiring a PIN.
ed346347 (NEW)Performs a click.
5c900684 (NEW)Scrolls forward.
d98179a8 (NEW)Scrolls backward.
7994ceca (NEW)Sets the text of a specified element ID to the payload text.
feba1943 (NEW)Swipes up.
d403ad43 (NEW)Swipes down.
4510a904 (NEW)Swipes left.
753c4fa0 (NEW)Swipes right.
b183a400 (NEW)Performs a stroke pattern on an element across a 3×3 grid.
81d9d725 (NEW)Performs a stroke pattern based on x+y coordinates and time duration.
b79c4b56 (NEW)Press-and-hold 3 times near bottom middle of the screen.
1a7493e7 (NEW)Starts capturing (recording) the screen.
6fa8a395 (NEW)Sets the “ShowMode” of the keyboard to 0. This allows the system to control when the soft keyboard is displayed.
9b22cbb1 (NEW)Sets the “ShowMode” of the keyboard to 1. This means the soft keyboard will never be displayed (until it is turned back on).
98c97da9 (NEW)Requests permissions for reading and writing external storage.
7b230a3b (NEW)Request permissions to install apps from unknown sources.
cc8397d4 (NEW)Opens the long-press power menu.
3263f7d4 (NEW)Sets a SharedPreference value for the key “c0ee5ba1-83dd-49c8-8212-4cfd79e479c0” to the specified payload. This value is later checked for in other to determine whether the long-press power menu should be displayed (SharedPref value 1), or whether the back button must be pressed (SharedPref value 2).
request_accessibility (UPDATED)Prompts the infected device with either a notification or a custom WebView that instructs the user to enable accessibility services for the malicious app. The related WebView component was not present in older versions of Vultur.
announcement (NEW)Updates the value for the C2 domain in the SharedPreferences.
5283d36d-e3aa-45ed-a6fb-2abacf43d29c (NEW)Sends a POST with the vnc.config C2 method and stores the malware config in SharedPreferences.
09defc05-701a-4aa3-bdd2-e74684a61624 (NEW)Hides / disables the keyboard, obtains a wake lock, disables keyguard (lock screen security), mutes the audio, stops the “TransparentActivity” from payload #2, and displays a black view.
fc7a0ee7-6604-495d-ba6c-f9c2b55de688 (NEW)Hides / disables the keyboard, obtains a wake lock, disables keyguard (lock screen security), mutes the audio, stops the “TransparentActivity” from payload #2, and displays a custom WebView with HTML code loaded from SharedPreference key “946b7e8e” (“tvmq” value from malware config).
8eac269d-2e7e-4f0d-b9ab-6559d401308d (NEW)Hides / disables the keyboard, obtains a wake lock, disables keyguard (lock screen security), mutes the audio, stops the “TransparentActivity” from payload #2.
e7289335-7b80-4d83-863a-5b881fd0543d (NEW)Enables the keyboard and unmutes audio. Then, sends the vnc.snapshot method with empty JSON data.
544a9f82-c267-44f8-bff5-0726068f349d (NEW)Retrieves the C2 command, payload and UUID, and executes the command in a thread.
a7bfcfaf-de77-4f88-8bc8-da634dfb1d5a (NEW)Creates a custom notification to be shown in the status bar.
444c0a8a-6041-4264-959b-1a97d6a92b86 (NEW)Retrieves the list of apps to block and corresponding HTML code through the vnc.blocked.packages C2 method and stores them in the blocked_package_template SharedPreference key.
a1f2e3c6-9cf8-4a7e-b1e0-2c5a342f92d6 (NEW)Executes a file manager related command. Commands are:

1. 91b4a535-1a78-4655-90d1-a3dcb0f6388a – Downloads a file
2. cf2f3a6e-31fc-4479-bb70-78ceeec0a9f8 – Uploads a file
3. 1ce26f13-fba4-48b6-be24-ddc683910da3 – Deletes a file
4. 952c83bd-5dfb-44f6-a034-167901990824 – Installs a file
5. 787e662d-cb6a-4e64-a76a-ccaf29b9d7ac – Finds files containing a specified pattern

Detection

Writing YARA rules to detect Android malware can be challenging, as APK files are ZIP archives. This means that extracting all of the information about the Android application would involve decompressing the ZIP, parsing the XML, and so on. Thus, most analysts build YARA rules for the DEX file. However, DEX files, such as Vultur payload #3, are less frequently submitted to VirusTotal as they are uncovered at a later stage in the infection chain. To maximise our sample pool, we decided to develop a YARA rule for the Brunhilda dropper. We discovered some unique hex patterns in the dropper APK, which allowed us to create the YARA rule below.

rule brunhilda_dropper
{
meta:
author = "Fox-IT, part of NCC Group"
description = "Detects unique hex patterns observed in Brunhilda dropper samples."
target_entity = "file"
strings:
$zip_head = "PK"
$manifest = "AndroidManifest.xml"
$hex1 = {63 59 5c 28 4b 5f}
$hex2 = {32 4a 66 48 66 76 64 6f 49 36}
$hex3 = {63 59 5c 28 4b 5f}
$hex4 = {30 34 7b 24 24 4b}
$hex5 = {22 69 4f 5a 6f 3a}
condition:
$zip_head at 0 and $manifest and #manifest >= 2 and 2 of ($hex*)
}

Wrap-up

Vultur’s recent developments have shown a shift in focus towards maximising remote control over infected devices. With the capability to issue commands for scrolling, swipe gestures, clicks, volume control, blocking apps from running, and even incorporating file manager functionality, it is clear that the primary objective is to gain total control over compromised devices.

Vultur has a strong correlation to Brunhilda, with its C2 communication and payload decryption having the same implementation in the latest variants. This indicates that both the dropper and Vultur are being developed by the same threat actors, as has also been uncovered in the past.

Furthermore, masquerading malicious activity through the modification of legitimate applications, encryption of traffic, and the distribution of functions across multiple payloads decrypted from native code, shows that the actors put more effort into evading detection and complicating analysis.

During our investigation of recently submitted Vultur samples, we observed the addition of new functionality occurring shortly after one another. This suggests ongoing and active development to enhance the malware’s capabilities. In light of these observations, we expect more functionality being added to Vultur in the near future.

Indicators of Compromise

Analysed samples

Package nameFile hash (SHA-256)Description
com.wsandroid.suiteedef007f1ca60fdf75a7d5c5ffe09f1fc3fb560153633ec18c5ddb46cc75ea21Brunhilda Dropper
com.medical.balance89625cf2caed9028b41121c4589d9e35fa7981a2381aa293d4979b36cf5c8ff2Vultur payload #1
com.medical.balance1fc81b03703d64339d1417a079720bf0480fece3d017c303d88d18c70c7aabc3Vultur payload #2
com.medical.balance4fed4a42aadea8b3e937856318f9fbd056e2f46c19a6316df0660921dd5ba6c5Vultur payload #3
com.wsandroid.suite001fd4af41df8883957c515703e9b6b08e36fde3fd1d127b283ee75a32d575fcBrunhilda Dropper
se.accessibility.appfc8c69bddd40a24d6d28fbf0c0d43a1a57067b19e6c3cc07e2664ef4879c221bVultur payload #1
se.accessibility.app7337a79d832a57531b20b09c2fc17b4257a6d4e93fcaeb961eb7c6a95b071a06Vultur payload #2
se.accessibility.app7f1a344d8141e75c69a3c5cf61197f1d4b5038053fd777a68589ecdb29168e0cVultur payload #3
com.wsandroid.suite26f9e19c2a82d2ed4d940c2ec535ff2aba8583ae3867502899a7790fe3628400Brunhilda Dropper
com.exvpn.fastvpn2a97ed20f1ae2ea5ef2b162d61279b2f9b68eba7cf27920e2a82a115fd68e31fVultur payload #1
com.exvpn.fastvpnc0f3cb3d837d39aa3abccada0b4ecdb840621a8539519c104b27e2a646d7d50dVultur payload #2
com.wsandroid.suite92af567452ecd02e48a2ebc762a318ce526ab28e192e89407cac9df3c317e78dBrunhilda Dropper
jk.powder.tendencefa6111216966a98561a2af9e4ac97db036bcd551635be5b230995faad40b7607Vultur payload #1
jk.powder.tendencedc4f24f07d99e4e34d1f50de0535f88ea52cc62bfb520452bdd730b94d6d8c0eVultur payload #2
jk.powder.tendence627529bb010b98511cfa1ad1aaa08760b158f4733e2bbccfd54050838c7b7fa3Vultur payload #3
com.wsandroid.suitef5ce27a49eaf59292f11af07851383e7d721a4d60019f3aceb8ca914259056afBrunhilda Dropper
se.talkback.app5d86c9afd1d33e4affa9ba61225aded26ecaeb01755eeb861bb4db9bbb39191cVultur payload #1
se.talkback.app5724589c46f3e469dc9f048e1e2601b8d7d1bafcc54e3d9460bc0adeeada022dVultur payload #2
se.talkback.app7f1a344d8141e75c69a3c5cf61197f1d4b5038053fd777a68589ecdb29168e0cVultur payload #3
com.wsandroid.suitefd3b36455e58ba3531e8cce0326cce782723cc5d1cc0998b775e07e6c2622160Brunhilda Dropper
com.adajio.storm819044d01e8726a47fc5970efc80ceddea0ac9bf7c1c5d08b293f0ae571369a9Vultur payload #1
com.adajio.storm0f2f8adce0f1e1971cba5851e383846b68e5504679d916d7dad10133cc965851Vultur payload #2
com.adajio.stormfb1e68ee3509993d0fe767b0372752d2fec8f5b0bf03d5c10a30b042a830ae1aVultur payload #3
com.protectionguard.appd3dc4e22611ed20d700b6dd292ffddbc595c42453f18879f2ae4693a4d4d925aBrunhilda Dropper (old variant)
com.appsmastersafeyf4d7e9ec4eda034c29b8d73d479084658858f56e67909c2ffedf9223d7ca9bd2Vultur (old variant)
com.datasafeaccountsanddata.club7ca6989ccfb0ad0571aef7b263125410a5037976f41e17ee7c022097f827bd74Vultur (old variant)
com.app.freeguarding.twofactorc646c8e6a632e23a9c2e60590f012c7b5cb40340194cb0a597161676961b4de0Vultur (old variant)

Note: Vultur payloads #1 and #2 related to Brunhilda dropper 26f9e19c2a82d2ed4d940c2ec535ff2aba8583ae3867502899a7790fe3628400 are the same as Vultur payloads #2 and #3 in the latest variants. The dropper in this case only drops two payloads, where the latest versions deploy a total of three payloads.

C2 servers

  • safetyfactor[.]online
  • cloudmiracle[.]store
  • flandria171[.]appspot[.]com (FCM)
  • newyan-1e09d[.]appspot[.]com (FCM)

Dropper distribution URLs

  • mcafee[.]960232[.]com
  • mcafee[.]353934[.]com
  • mcafee[.]908713[.]com
  • mcafee[.]784503[.]com
  • mcafee[.]053105[.]com
  • mcafee[.]092877[.]com
  • mcafee[.]582630[.]com
  • mcafee[.]581574[.]com
  • mcafee[.]582342[.]com
  • mcafee[.]593942[.]com
  • mcafee[.]930204[.]com

References

  1. https://resources.prodaft.com/brunhilda-daas-malware-report ↩
  2. https://www.threatfabric.com/blogs/vultur-v-for-vnc ↩
  3. https://www.threatfabric.com/blogs/the-attack-of-the-droppers ↩
  4. https://www.wildlifecenter.org/vulture-facts ↩

Memory Scanning for the Masses

Authors: Axel Boesenach and Erik Schamper

In this blog post we will go into a user-friendly memory scanning Python library that was created out of the necessity of having more control during memory scanning. We will give an overview of how this library works, share the thought process and the why’s. This blog post will not cover the inner workings of the memory management of the respective platforms.

Memory scanning

Memory scanning is the practice of iterating over the different processes running on a computer system and searching through their memory regions for a specific pattern. There can be a myriad of reasons to scan the memory of certain processes. The most common use cases are probably credential access (accessing the memory of the lsass.exe process for example), scanning for possible traces of malware and implants or recovery of interesting data, such as cryptographic material.

If time is as valuable to you as it is to us at Fox-IT, you probably noticed that performing a full memory scan looking for a pattern is a very time-consuming process, to say the least.

Why is scanning memory so time consuming when you know what you are looking for, and more importantly; how can this scanning process be sped up? While looking into different detection techniques to identify running Cobalt Strike beacons, we noticed something we could easily filter on, speeding up our scanning processes: memory attributes.

Speed up scanning with memory attributes

Memory attributes are comparable to the permission system we all know and love on our regular file and directory structures. The permission system dictates what kind of actions are allowed within a specific memory region and can be changed to different sets of attributes by their respective API calls.

The following memory attributes exist on both the Windows and UNIX platforms:

  • Read (R)
  • Write (W)
  • Execute (E)

The Windows platform has some extra permission attributes, plus quite an extensive list of allocation1 and protection2 attributes. These attributes can also be used to filter when looking for specific patterns within memory regions but are not important to go into right now.

So how do we leverage this information about attributes to speed up our scanning processes? It turns out that by filtering the regions to scan based on the memory attributes set for the regions, we can speed up our scanning process tremendously before even starting to look for our specified patterns.

Say for example we are looking for a specific byte pattern of an implant that is present in a certain memory region of a running process on the Windows platform. We already know what pattern we are looking for and we also know that the memory regions used by this specific implant are always set to:

TypeProtectionInitial
PRVERWERW
Table 1. Example of implant memory attributes that are set

Depending on what is running on the system, filtering on the above memory attributes already rules out a large portion of memory regions for most running processes on a Windows system.

If we take a notepad.exe process as an example, we can see that the different sections of the executable have their respective rights. The .text section of an executable contains executable code and is thus marked with the E permission as its protection:

If we were looking for just the sections and regions that are marked as being executable, we would only need to scan the .text section of the notepad.exe process. If we scan all the regions of every running process on the system, disregarding the memory attributes which are set, scanning for a pattern will take quite a bit longer.

Introducing Skrapa

We’ve incorporated the techniques described above into an easy to install Python package. The package is designed and tested to work on Linux and Microsoft Windows systems. Some of the notable features include:

  • Configurable scanning:
    • Scan all the process memory, specific processes by name or process identifier.
  • Regex and YARA support.
  • Support for user callback functions, define custom functions that execute routines when user specified conditions are met.
  • Easy to incorporate in bigger projects and scripts due to easy to use API.

The package was designed to be easily extensible by the end users, providing an API that can be leveraged to perform more.

Where to find Skrapa?

The Python library is available on our GitHub, together with some examples showing scenarios on how to use it.

GitHub: https://github.com/fox-it/skrapa

References

  1. https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc ↩
  2. https://learn.microsoft.com/en-us/windows/win32/Memory/memory-protection-constants ↩

Reverse, Reveal, Recover: Windows Defender Quarantine Forensics

Max Groot & Erik Schamper

TL;DR

  • Windows Defender (the antivirus shipped with standard installations of Windows) places malicious files into quarantine upon detection.
  • Reverse engineering mpengine.dll resulted in finding previously undocumented metadata in the Windows Defender quarantine folder that can be used for digital forensics and incident response.
  • Existing scripts that extract quarantined files do not process this metadata, even though it could be useful for analysis.
  • Fox-IT’s open-source digital forensics and incident response framework Dissect can now recover this metadata, in addition to recovering quarantined files from the Windows Defender quarantine folder.
  • dissect.cstruct allows us to use C-like structure definitions in Python, which enables easy continued research in other programming languages or reverse engineering in tools like IDA Pro.
    • Want to continue in IDA Pro? Just copy & paste the structure definitions!

Introduction

During incident response engagements we often encounter antivirus applications that have rightfully triggered on malicious software that was deployed by threat actors. Most commonly we encounter this for Windows Defender, the antivirus solution that is shipped by default with Microsoft Windows. Windows Defender places malicious files in quarantine upon detection, so that the end user may decide to recover the file or delete it permanently. Threat actors, when faced with the detection capabilities of Defender, either disable the antivirus in its entirety or attempt to evade its detection.

The Windows Defender quarantine folder is valuable from the perspective of digital forensics and incident response (DFIR). First of all, it can reveal information about timestamps, locations and signatures of files that were detected by Windows Defender. Especially in scenarios where the threat actor has deleted the Windows Event logs, but left the quarantine folder intact, the quarantine folder is of great forensic value. Moreover, as the entire file is quarantined (so that the end user may choose to restore it), it is possible to recover files from quarantine for further reverse engineering and analysis.

While scripts already exist to recover files from the Defender quarantine folder, the purpose of much of the contents of this folder were previously unknown. We don’t like big unknowns, so we performed further research into the previously unknown metadata to see if we could uncover additional forensic traces.

Rather than just presenting our results, we’ve structured this blog to also describe the process to how we got there. Skip to the end if you are interested in the results rather than the technical details of reverse engineering Windows Defender.

Diving into Windows Defender internals

Existing Research

We started by looking into existing research into the internals of Windows Defender. The most extensive documentation we could find on the structures of Windows Defender quarantine files was Florian Bauchs’ whitepaper analyzing antivirus software quarantine files, but we also looked at several scripts on GitHub.

  • In summary, whenever Defender puts a file into quarantine, it does three things:
    A bunch of metadata pertaining to when, why and how the file was quarantined is held in a QuarantineEntry. This QuarantineEntry is RC4-encrypted and saved to disk in the /ProgramData/Microsoft/Windows Defender/Quarantine/Entries folder.
  • The contents of the malicious file is stored in a QuarantineEntryResourceData file, which is also RC4-encrypted and saved to disk in the /ProgramData/Microsoft/Windows Defender/Quarantine/ResourceData folder.
  • Within the /ProgramData/Microsoft/Windows Defender/Quarantine/Resource folder, a Resource file is made. Both from previous research as well as from our own findings during reverse engineering, it appears this file contains no information that cannot be obtained from the QuarantineEntry and the QuarantineEntryResourceData files. Therefore, we ignore the Resource file for the remainder of this blog.

While previous scripts are able to recover some properties from the ResourceData and QuarantineEntry files, large segments of data were left unparsed, which gave us a hunch that additional forensic artefacts were yet to be discovered.

Windows Defender encrypts both the QuarantineEntry and the ResourceData files using a hardcoded RC4 key defined in mpengine.dll. This hardcoded key was initially published by Cuckoo and is paramount for the offline recovery of the quarantine folder.

Pivotting off of public scripts and Bauch’s whitepaper, we loaded mpengine.dll into IDA to further review how Windows Defender places a file into quarantine. Using the PDB available from the Microsoft symbol server, we get a head start with some functions and structures already defined.

Recovering metadata by investigating the QuarantineEntry file

Let us begin with the QuarantineEntry file. From this file, we would like to recover as much of the QuarantineEntry structure as possible, as this holds all kinds of valuable metadata. The QuarantineEntry file is not encrypted as one RC4 cipherstream, but consists of three chunks that are each individually encrypted using RC4.

These three chunks are what we have come to call QuarantineEntryFileHeader, QuarantineEntrySection1 and QuarantineEntrySection2.

  • QuarantineEntryFileHeader describes the size of QuarantineEntrySection1 and QuarantineEntrySection2, and contains CRC checksums for both sections.
  • QuarantineEntrySection1 contains valuable metadata that applies to all QuarantineEntryResource instances within this QuarantineEntry file, such as the DetectionName and the ScanId associated with the quarantine action.
  • QuarantineEntrySection2 denotes the length and offset of every QuarantineEntryResource instance within this QuarantineEntry file so that they can be correctly parsed individually.

A QuarantineEntry has one or more QuarantineEntryResource instances associated with it. This contains additional information such as the path of the quarantined artefact, and the type of artefact that has been quarantined (e.g. regkey or file).

An overview of the different structures within QuarantineEntry is provided in Figure 1:

upload_55de55932e8d2b42e392875c9982dfb5
Figure 1: An example overview of a QuarantineEntry. In this example, two files were simultaneously quarantined by Windows Defender. Hence, there are two QuarantineEntryResource structures contained within this single QuarantineEntry.

As QuarantineEntryFileHeader is mostly a structure that describes how QuarantineEntrySection1 and QuarantineEntrySection2 should be parsed, we will first look into what those two consist of.

QuarantineEntrySection1

When reviewing mpengine.dll within IDA, the contents of both QuarantineEntrySection1 and QuarantineEntrySection2 appear to be determined in the
QexQuarantine::CQexQuaEntry::Commit function.

The function receives an instance of the QexQuarantine::CQexQuaEntry class. Unfortunately, the PDB file that Microsoft provides for mpengine.dll does not contain contents for this structure. Most fields could, however, be derived using the function names in the PDB that are associated with the CQexQuaEntry class:

upload_5238619cbda300bf7ebb129b3a592985
Figure 2: Functions retrieving properties from QuarantineEntry

The Id, ScanId, ThreatId, ThreatName and Time fields are most important, as these will be written to the QuarantineEntry file.

At the start of the QexQuarantine::CQexQuaEntry::Commit function, the size of Section1 is determined.

upload_a6065a0a572b7fd2230d23c07ce25c02
Figure 3: Reviewing the decompiled output of CqExQuaEntry::Commit shows the size of QuarantineEntrySection1 being set to thre length of ThreatName plus 53.

This sets section1_size to a value of the length of the ThreatName variable plus 53. We can determine what these additional 53 bytes consist of by looking at what values are set in the QexQuarantine::CQexQuaEntry::Commit function for the Section1 buffer.

This took some experimentation and required trying different fields, offsets and sizes for the QuarantineEntrySection1 structure within IDA. After every change, we would review what these changes would do to the decompiled IDA view of the QexQuarantine::CQexQuaEntry::Commit function.

Some trial and error landed us the following structure definition:

struct QuarantineEntrySection1 {
CHAR Id[16];
CHAR ScanId[16];
QWORD Timestamp;
QWORD ThreatId;
DWORD One;
CHAR DetectionName[];
};
view raw defender-1.c hosted with ❤ by GitHub

While reviewing the final decompiled output (right) for the assembly code (left), we noticed a field always being set to 1:

upload_fd8ee20d24251cb21ef29720c1b2a3a8
Figure 4: A field of QuarantineEntrySection1 always being set to the value of 1.

Given that we do not know what this field is used for, we opted to name the field ‘One’ for now. Most likely, it’s a boolean value that is always true within the context of the QexQuarantine::CQexQuaEntry::Commit commit function.

QuarantineEntrySection2

Now that we have a structure definition for the first section of a QuarantineEntry, we now move on to the second part. QuarantineEntrySection2 holds the number of QuarantineEntryResource objects confined within a QuarantineEntry, as well as the offsets into the QuarantineEntry structure where they are located.

In most scenarios, one threat gets detected at a time, and one QuarantineEntry will be associated with one QuarantineEntryResource. This is not always the case: for example, if one unpacks a ZIP folder that contains multiple malicious files, Windows Defender might place them all into quarantine. Each individual malicious file of the ZIP would then be one QuarantineEntryResource, but they are all confined within one QuarantineEntry.

QuarantineEntryResource

To be able to parse QuarantineEntryResource instances, we look into the CQexQuaResource::ToBinary function. This function receives a QuarantineEntryResource object, as well as a pointer to a buffer to which it needs to write the binary output to. If we can reverse the logic within this function, we can convert the binary output back into a parsed instance during forensic recovery.

Looking into the CQexQuaResource::ToBinary function, we see two very similar loops as to what was observed before for serializing the ThreatName of QuarantineEntrySection1. By reviewing various decrypted QuarantineEntry files, it quickly became apparent that these loops are responsible for reserving space in the output buffer for DetectionPath and DetectionType, with DetectionPath being UTF-16 encoded:

upload_2673150155ac8022e30f0a5819615b59
Figure 5: Reservation of space for DetectionPath and DetectionType at the beginning of CQexQuaResource::ToBinary

Fields

When reviewing the QexQuarantine::CQexQuaEntry::Commit function, we observed an interesting loop that (after investigating function calls and renaming variables) explains the data that is stored between the DetectionType and DetectionPath:

upload_abea0cbdffa486b1db6ee9440caf85d5
Figure 6: Alignment logic for serializing Fields

It appears QuarantineEntryResource structures have one or more QuarantineResourceField instances associated with them, with the number of fields associated with a QuarantineEntryResource being stored in a single byte in between the DetectionPath and DetectionType. When saving the QuarantineEntry to disk, fields have an alignment of 4 bytes. We could not find mentions of QuarantineEntryResourceField structures in prior Windows Defender research, even though they can hold valuable information.

The CQExQuaResource class has several different implementations of AddField, accepting different kinds of parameters. Reviewing these functions showed that fields have an Identifier, Type, and a buffer Data with a size of Size, resulting in a simple TLV-like format:

struct QuarantineEntryResourceField {
WORD Size;
WORD Identifier:12;
FIELD_TYPE Type:4;
CHAR Data[Size];
};
view raw defender-2.c hosted with ❤ by GitHub

To understand what kinds of types and identifiers are possible, we delve further into the different versions of the AddField functions, which all accept a different data type:

upload_e3940f49457a6a21316e894c863cedf3
Figure 7: Finding different field types based on different implementations of the CqExQuaResource::AddField function

Visiting these functions, we reviewed the Type and Size variables to understand the different possible types of fields that can be set for QuarantineResource instances. This yields the following FIELD_TYPE enum:

enum FIELD_TYPE : WORD {
STRING = 0x1,
WSTRING = 0x2,
DWORD = 0x3,
RESOURCE_DATA = 0x4,
BYTES = 0x5,
QWORD = 0x6,
};
view raw defender-3.c hosted with ❤ by GitHub

As the AddField functions are part of a virtual function table (vtable) of the CQexQuaResource class, we cannot trivially find all places where the AddField function is called, as they are not directly called (which would yield an xref in IDA). Therefore, we have not exhausted all code paths leading to a call of AddField to identify all possible Identifier values and how they are used. Our research yielded the following field identifiers as the most commonly observed, and of the most forensic value:

enum FIELD_IDENTIFIER : WORD {
CQuaResDataID_File = 0x02,
CQuaResDataID_Registry = 0x03,
Flags = 0x0A,
PhysicalPath = 0x0C,
DetectionContext = 0x0D,
Unknown = 0x0E,
CreationTime = 0x0F,
LastAccessTime = 0x10,
LastWriteTime = 0x11,
};
view raw defender-4.c hosted with ❤ by GitHub

Especially CreationTime, LastAccessTime and LastWriteTime can provide crucial data points during an investigation.

Revisiting the QuarantineEntrySection2 and QuarantineEntryResource structures

Now that we have an understanding of how fields work and how they are stored within the QuarantineEntryResource, we can derive the following structure for it:

struct QuarantineEntryResource {
WCHAR DetectionPath[];
WORD FieldCount;
CHAR DetectionType[];
};
view raw defender-5.c hosted with ❤ by GitHub

Revisiting the QexQuarantine::CQexQuaEntry::Commit function, we can now understand how this function determines at which offset every QuarantineEntryResource is located within QuarantineEntry. Using these offsets, we will later be able to parse individual QuarantineEntryResource instances. Thus, the QuarantineEntrySection2 structure is fairly straightforward:

struct QuarantineEntrySection2 {
DWORD EntryCount;
DWORD EntryOffsets[EntryCount];
};
view raw defender-6.c hosted with ❤ by GitHub

The last step for recovery of QuarantineEntry: the QuarantineEntryFileHeader

Now that we have a proper understanding of the QuarantineEntry, we want to know how it ends up written to disk in encrypted form, so that we can properly parse the file upon forensic recovery. By inspecting the QexQuarantine::CQexQuaEntry::Commit function further, we can find how this ends up passing QuarantineSection1 and QuarantineSection2 to a function named CUserDatabase::Add.

We noted earlier that the QuarantineEntry contains three RC4-encrypted chunks. The first chunk of the file is created in the CUserDatabase::Add function, and is the QuarantineEntryHeader. The second chunk is QuarantineEntrySection1. The third chunk starts with QuarantineEntrySection2, followed by all QuarantineEntryResource structures and their 4-byte aligned QuarantineEntryResourceField structures.

We knew from Bauch’s work that the QuarantineEntryFileHeader has a static size of 60 bytes, and contains the size of QuarantineEntrySection1 and QuarantineEntrySection2. Thus, we need to decrypt the QuarantineEntryFileHeader first.

Based on Bauch’s work, we started with the following structure for QuarantineEntryFileHeader:

struct QuarantineEntryHeader {
char magic[16];
char unknown1[24];
uint32_t section1_size;
uint32_t section2_size;
char unknown[12];
};
view raw defender-7.c hosted with ❤ by GitHub

That leaves quite some bytes unknown though, so we went back to trusty IDA. Inspecting the CUserDatabase:Add function helps us further understand the QuarantineEntryHeader structure. For example, we can see the hardcoded magic header and footer:

upload_98b8e4a53c747b0e1a87190ef49328a3
Figure 8: Magic header and footer being set for the QuarantineEntryHeader

A CRC checksum calculation can be seen for both the buffer of QuarantineEntrySection1 and QuarantineSection2:

upload_1a2abe99ea699e4305c04f708fb8905d
Figure 9: CRC Checksum logic within CUserDatabase::Add

These checksums can be used upon recovery to verify the validity of the file. The CUserDatabase:Add function then writes the three chunks in RC4-encrypted form to the QuarantineEntry file buffer.

Based on these findings of the Magic header and footer and the CRC checksums, we can revise the structure definition for the QuarantineEntryFileHeader:

struct QuarantineEntryFileHeader {
CHAR MagicHeader[4];
CHAR Unknown[4];
CHAR _Padding[32];
DWORD Section1Size;
DWORD Section2Size;
DWORD Section1CRC;
DWORD Section2CRC;
CHAR MagicFooter[4];
};
view raw defender-8.c hosted with ❤ by GitHub

This was the last piece to be able to parse QuarantineEntry structures from their on-disk form. However, we do not want just the metadata: we want to recover the quarantined files as well.

Recovering files by investigating QuarantineEntryResourceData

We can now correctly parse QuarantineEntry files, so it is time to turn our attention to the QuarantineEntryResourceData file. This file contains the RC4-encrypted contents of the file that has been placed into quarantine.

Step one: eyeball hexdumps

Let’s start by letting Windows Defender quarantine a Mimikatz executable and reviewing its output files in the quarantine folder. One would think that merely RC4 decrypting the QuarantineEntryResourceData file would result in the contents of the original file. However, a quick hexdump of a decrypted QuarantineEntryResourceData file shows us that there is more information contained within:

max@dissect $ hexdump -C mimikatz_resourcedata_rc4_decrypted.bin | head -n 20
00000000 03 00 00 00 02 00 00 00 a4 00 00 00 00 00 00 00 |…………….|
00000010 00 00 00 00 01 00 04 80 14 00 00 00 30 00 00 00 |…………0…|
00000020 00 00 00 00 4c 00 00 00 01 05 00 00 00 00 00 05 |….L………..|
00000030 15 00 00 00 a4 14 d2 9b 1a 02 a7 4f 07 f6 37 b4 |………..O..7.|
00000040 e8 03 00 00 01 05 00 00 00 00 00 05 15 00 00 00 |…………….|
00000050 a4 14 d2 9b 1a 02 a7 4f 07 f6 37 b4 01 02 00 00 |…….O..7…..|
00000060 02 00 58 00 03 00 00 00 00 00 14 00 ff 01 1f 00 |..X………….|
00000070 01 01 00 00 00 00 00 05 12 00 00 00 00 00 18 00 |…………….|
00000080 ff 01 1f 00 01 02 00 00 00 00 00 05 20 00 00 00 |………… …|
00000090 20 02 00 00 00 00 24 00 ff 01 1f 00 01 05 00 00 | …..$………|
000000a0 00 00 00 05 15 00 00 00 a4 14 d2 9b 1a 02 a7 4f |……………O|
000000b0 07 f6 37 b4 e8 03 00 00 01 00 00 00 00 00 00 00 |..7………….|
000000c0 00 ae 14 00 00 00 00 00 00 00 00 00 4d 5a 90 00 |…………MZ..|
000000d0 03 00 00 00 04 00 00 00 ff ff 00 00 b8 00 00 00 |…………….|
000000e0 00 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 |….@………..|
000000f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |…………….|
00000100 00 00 00 00 00 00 00 00 20 01 00 00 0e 1f ba 0e |…….. …….|
00000110 00 b4 09 cd 21 b8 01 4c cd 21 54 68 69 73 20 70 |….!..L.!This p|
00000120 72 6f 67 72 61 6d 20 63 61 6e 6e 6f 74 20 62 65 |rogram cannot be|
00000130 20 72 75 6e 20 69 6e 20 44 4f 53 20 6d 6f 64 65 | run in DOS mode|
view raw defender-hex-1 hosted with ❤ by GitHub

As visible in the hexdump, the MZ value (which is located at the beginning of the buffer of the Mimikatz executable) only starts at offset 0xCC. This gives reason to believe there is potentially valuable information preceding it.

There is also additional information at the end of the ResourceData file:

max@dissect $ hexdump -C mimikatz_resourcedata_rc4_decrypted.bin | tail -n 10
0014aed0 00 00 00 00 52 00 00 00 00 00 00 00 2c 00 00 00 |….R…….,…|
0014aee0 3a 00 5a 00 6f 00 6e 00 65 00 2e 00 49 00 64 00 |:.Z.o.n.e…I.d.|
0014aef0 65 00 6e 00 74 00 69 00 66 00 69 00 65 00 72 00 |e.n.t.i.f.i.e.r.|
0014af00 3a 00 24 00 44 00 41 00 54 00 41 00 5b 5a 6f 6e |:.$.D.A.T.A.[Zon|
0014af10 65 54 72 61 6e 73 66 65 72 5d 0d 0a 5a 6f 6e 65 |eTransfer]..Zone|
0014af20 49 64 3d 33 0d 0a 52 65 66 65 72 72 65 72 55 72 |Id=3..ReferrerUr|
0014af30 6c 3d 43 3a 5c 55 73 65 72 73 5c 75 73 65 72 5c |l=C:\Users\user\|
0014af40 44 6f 77 6e 6c 6f 61 64 73 5c 6d 69 6d 69 6b 61 |Downloads\mimika|
0014af50 74 7a 5f 74 72 75 6e 6b 2e 7a 69 70 0d 0a |tz_trunk.zip..|
view raw defender-hex-2 hosted with ❤ by GitHub

At the end of the hexdump, we see an additional buffer, which some may recognize as the “Zone Identifier”, or the “Mark of the Web”. As this Zone Identifier may tell you something about where a file originally came from, it is valuable for forensic investigations.

Step two: open IDA

To understand where these additional buffers come from and how we can parse them, we again dive into the bowels of mpengine.dll. If we review the QuarantineFile function, we see that it receives a QuarantineEntryResource and QuarantineEntry as parameters. When following the code path, we see that the BackupRead function is called to write to a buffer of which we know that it will later be RC4-encrypted by Defender and written to the quarantine folder:

upload_a5cf05e6acd61d331963cd4c3ef9f95a
Figure 10: BackupRead being called withi nthe QuarantineFile function.

Step three: RTFM

A glance at the documentation of BackupRead reveals that this function returns a buffer seperated by Win32 stream IDs. The streams stored by BackupRead contain all data streams as well as security data about the owner and permissions of a file. On NTFS file systems, a file can have multiple data attributes or streams: the “main” unnamed data stream and optionally other named data streams, often referred to as “alternate data streams”. For example, the Zone Identifier is stored in a seperate Zone.Identifier data stream of a file. It makes sense that a function intended for backing up data preserves these alternate data streams as well.

The fact that BackupRead preserves these streams is also good news for forensic analysis. First of all, malicious payloads can be hidden in alternate data streams. Moreover, alternate datastreams such as the Zone Identifier and the security data can help to understand where a file has come from and what it contains. We just need to recover the streams as they have been saved by BackupRead!

Diving into IDA is not necessary, as the documentation tells us all that we need. For each data stream, the BackupRead function writes a WIN32_STREAM_ID to disk, which denotes (among other things) the size of the stream. Afterwards, it writes the data of the stream to the destination file and continues to the next stream. The WIN32_STREAM_ID structure definition is documented on the Microsoft Learn website:

typedef struct _WIN32_STREAM_ID {
STREAM_ID StreamId;
STREAM_ATTRIBUTES StreamAttributes;
QWORD Size;
DWORD StreamNameSize;
WCHAR StreamName[StreamNameSize / 2];
} WIN32_STREAM_ID;
view raw defender-9.c hosted with ❤ by GitHub

Who slipped this by the code review?

While reversing parts of mpengine.dll, we came across an interesting looking call in the HandleThreatDetection function. We appreciate that threats must be dealt with swiftly and with utmost discipline, but could not help but laugh at the curious choice of words when it came to naming this particular function.
upload_f98fac728a52164d573769c06a18b18f
Figure 11: A function call to SendThreatToCamp, a ‘call’ to action that seems pretty harsh.

Implementing our findings into Dissect

We now have all structure definitions that we need to recover all metadata and quarantined files from the quarantine folder. There is only one step left: writing an implementation.

During incident response, we do not want to rely on scripts scattered across home directories and git repositories. This is why we integrate our research into Dissect.

We can leave all the boring stuff of parsing disks, volumes and evidence containers to Dissect, and write our implementation as a plugin to the framework. Thus, the only thing we need to do is parse the artefacts and feed the results back into the framework.

The dive into Windows Defender of the previous sections resulted in a number of structure definitions that we need to recover data from the Windows Defender quarantine folder. When making an implementation, we want our code to reflect these structure definitions as closely as possible, to make our code both readable and verifiable. This is where dissect.cstruct comes in. It can parse structure definitions and make them available in your Python code. This removes a lot of boilerplate code for parsing structures and greatly enhances the readability of your parser. Let’s review how easily we can parse a QuarantineEntry file using dissect.cstruct :

from dissect.cstruct import cstruct
defender_def= """
struct QuarantineEntryFileHeader {
CHAR MagicHeader[4];
CHAR Unknown[4];
CHAR _Padding[32];
DWORD Section1Size;
DWORD Section2Size;
DWORD Section1CRC;
DWORD Section2CRC;
CHAR MagicFooter[4];
};
struct QuarantineEntrySection1 {
CHAR Id[16];
CHAR ScanId[16];
QWORD Timestamp;
QWORD ThreatId;
DWORD One;
CHAR DetectionName[];
};
struct QuarantineEntrySection2 {
DWORD EntryCount;
DWORD EntryOffsets[EntryCount];
};
struct QuarantineEntryResource {
WCHAR DetectionPath[];
WORD FieldCount;
CHAR DetectionType[];
};
struct QuarantineEntryResourceField {
WORD Size;
WORD Identifier:12;
FIELD_TYPE Type:4;
CHAR Data[Size];
};
"""
c_defender = cstruct()
c_defender.load(defender_def)
class QuarantineEntry:
def __init__(self, fh: BinaryIO):
# Decrypt & parse the header so that we know the section sizes
self.header = c_defender.QuarantineEntryFileHeader(rc4_crypt(fh.read(60)))
# Decrypt & parse Section 1. This will tell us some information about this quarantine entry.
# These properties are shared for all quarantine entry resources associated with this quarantine entry.
self.metadata = c_defender.QuarantineEntrySection1(rc4_crypt(fh.read(self.header.Section1Size)))
# […]
# The second section contains the number of quarantine entry resources contained in this quarantine entry,
# as well as their offsets. After that, the individal quarantine entry resources start.
resource_buf = BytesIO(rc4_crypt(fh.read(self.header.Section2Size)))
view raw defender.py hosted with ❤ by GitHub

As you can see, when the structure format is known, parsing it is trivial using dissect.cstruct. The only caveat is that the QuarantineEntryFileHeader, QuarantineEntrySection1 and QuarantineEntrySection2 structures are individually encrypted using the hardcoded RC4 key. Because only the size of QuarantineEntryFileHeader is static (60 bytes), we parse that first and use the information contained in it to decrypt the other sections.

To parse the individual fields contained within the QuarantineEntryResource, we have to do a bit more work. We cannot add the QuarantineEntryResourceField directly to the QuarantineEntryResource structure definition within dissect.cstruct, as it currently does not support the type of alignment used by Windows Defender. However, it does support the QuarantineEntryResourceField structure definition, so all we have to do is follow the alignment logic that we saw in IDA:

# As the fields are aligned, we need to parse them individually
offset = fh.tell()
for _ in range(field_count):
# Align
offset = (offset + 3) & 0xFFFFFFFC
fh.seek(offset)
# Parse
field = c_defender.QuarantineEntryResourceField(fh)
self._add_field(field)
# Move pointer
offset += 4 + field.Size

We can use dissect.cstruct‘s dumpstruct function to visualize our parsing to verify if we are correctly loading in all data:

upload_193c111d8639e63369484615023e25e8

And just like that, our parsing is done. Utilizing dissect.cstruct makes parsing structures much easier to understand and implement. This also facilitates rapid iteration: we have altered our structure definitions dozens of times during our research, which would have been pure pain without having the ability to blindly copy-paste structure definitions into our Python editor of choice.

Implementing the parser within the Dissect framework brings great advantages. We do not have to worry at all about the format in which the forensic evidence is provided. Implementing the Defender recovery as a Dissect plugin means it just works on standard forensic evidence formats such as E01 or ASDF, or against forensic packages the like of KAPE and Acquire, and even on a live virtual machine:

max@dissect $ target-query ~/Windows10.vmx -q -f defender.quarantine
<filesystem/windows/defender/quarantine/file hostname='DESKTOP-AR98HFK' domain=None ts=2022-11-22 09:37:16.536575+00:00 quarantine_id=b'\xe3\xc1\x03\x80\x00\x00\x00\x003\x12]]\x07\x9a\xd2\xc9' scan_id=b'\x88\x82\x89\xf5?\x9e J\xa5\xa8\x90\xd0\x80\x96\x80\x9b' threat_id=2147729891 detection_type='file' detection_name='HackTool:Win32/Mimikatz.D' detection_path='C:\\Users\\user\\Documents\\mimikatz.exe' creation_time=2022-11-22 09:37:00.115273+00:00 last_write_time=2022-11-22 09:37:00.240202+00:00 last_accessed_time=2022-11-22 09:37:08.081676+00:00 resource_id='9EC21BB792E253DBDC2E88B6B180C4E048847EF6'>
max@dissect $ target-query ~/Windows10.vmx -f defender.recover -o /tmp/ -v
2023-02-14T07:10:20.335202Z [info] <Target /home/max/Windows10.vmx>: Saving /tmp/9EC21BB792E253DBDC2E88B6B180C4E048847EF6.security_descriptor [dissect.target.target]
2023-02-14T07:10:20.335898Z [info <Target /home/max/Windows10.vmx>: Saving /tmp/9EC21BB792E253DBDC2E88B6B180C4E048847EF6 [dissect.target.target]
2023-02-14T07:10:20.337956Z [info] <Target /home/max/Windows10.vmx>: Saving /tmp/9EC21BB792E253DBDC2E88B6B180C4E048847EF6.ZoneIdentifierDATA [dissect.target.target]
view raw defender-query hosted with ❤ by GitHub

The full implementation of Windows Defender quarantine recovery can be observed on Github.

Conclusion

We hope to have shown that there can be great benefits to reverse engineering the internals of Microsoft Windows to discover forensic artifacts. By reverse engineering mpengine.dll, we were able to further understand how Windows Defender places detected files into quarantine. We could then use this knowledge to discover (meta)data that was previously not fully documented or understood. The main results of this are the recovery of more information about the original quarantined file, such as various timestamps and additional NTFS data streams, like the Zone.Identifier, which is information that can be useful in digital forensics or incident response investigations.

The documentation of QuarantineEntryResourceField was not available prior to this research and we hope others can use this to further investigate which fields are yet to be discovered. We have also documented how the BackupRead functionality is used by Defender to preserve the different data streams present in the NTFS file, including the Zone Identifier and Security Descriptor.

When writing our parser, using dissect.cstruct allowed us to tightly integrate our findings of reverse engineering in our parsing, enhancing the readability and verifiability of the code. This can in turn help others to pivot off of our research, just like we did when pivotting off of the research of others into the Windows Defender quarantine folder.

This research has been implemented as a plugin for the Dissect framework. This means that our parser can operate independently of the type of evidence it is being run against. This functionality has been added to dissect.target as of January 2nd 2023 and is installed with Dissect as of version 3.4.

The Spelling Police: Searching for Malicious HTTP Servers by Identifying Typos in HTTP Responses

Authored by Margit Hazenbroek

At Fox-IT (part of NCC Group) identifying servers that host nefarious activities is a critical aspect of our threat intelligence. One approach involves looking for anomalies in responses of HTTP servers. Sometimes cybercriminals that host malicious servers employ tactics that involve mimicking the responses of legitimate software to evade detection. However, a common pitfall of these malicious actors are typos, which we use as unique fingerprints to identify such servers. For example, we have used a simple extraneous whitespace in HTTP responses as a fingerprint to identify servers that were hosting Cobalt Strike with high confidence1. In fact, we have created numerous fingerprints based on textual slipups in HTTP responses of malicious servers, highlighting how fingerprinting these servers can be a matter of a simple mistake.

HTTP servers are expected to follow the established RFC guidelines of HTTP, producing consistent HTTP responses in accordance with standardized protocols. HTTP responses that are not set up properly can have an impact on the safety and security of websites and web services. With these considerations in mind, we decided to research the possibility of identifying unknown malicious servers by proactively searching for textual errors in HTTP responses.

In this blog post, we delve into this research, titled “The Spelling Police,” which aims to identify malevolent servers through the detection of typos in HTTP responses. Before we go into the methodology, we provide a brief overview of HTTP response headers and semantics. Then we explain how we spotted the spelling errors, focusing on the Levenshtein distance, a way to measure the differences between the expected and actual responses. Our preliminary research suggests that mistakes in HTTP responses are surprisingly common, even among legitimate servers. This finding suggests that typos alone are not enough to confirm malicious intent. However, we maintain that typos in HTTP response headers can be indicative of malicious servers, particularly when combined with other suspicious indicators.

HTTP response headers and semantics

HTTP is a protocol that governs communication between web servers and clients2. Typically, a client, such as a web browser, sends a request to a server to achieve specific goals, such as requesting to view a webpage. The server receives and processes these requests, then sends back corresponding responses. The client subsequently interprets the message semantics of these responses, for example by rendering the HTML in an HTTP response (see example 1).

Example 1: HTTP request and response

An HTTP response includes the status code and status line that provide information on how the server is responding, such as a ‘404 Page Not Found’ message. This status code is followed by response headers. Response headers are key:value pairs as described in the RFC that allow the server to give more information for context about the response and it can give information to the client on how to process the received data. Ensuring appropriate implementation of HTTP response headers plays a crucial role in preventing security vulnerabilities like Cross-Site Scripting, Clickjacking, Information disclosure, and many others34

Example 2: HTTP response

Methodology

The purpose of this research is to identify textual deviations in HTTP response headers and verify the servers behind them to detect new or unknown malicious servers. To accomplish this, we collected a large sample of HTTP responses and applied a spelling-checking model to flag any anomalous responses that contained deviations (see example 3 for an overview of the pipeline). These anomalous HTTP responses were further investigated to determine if they were originating from potentially malicious servers.

Example 3: Steps taken to get a list of anomalous HTTP responses

Data: Batch of HTTP responses
We sampled approximately 800,000 HTTP responses from public Censys scan data5. We also created a list of common HTTP response header fields, such as ‘Cache-Control’, ‘Expires’, ‘Content-Type’, and a list of typical server values, such as ‘Apache’, ‘Microsoft-IIS’, and ‘Nginx.’ We included a few common status codes like ‘200 OK,’ ensuring that the list contained commonly occurring words in HTTP responses to serve as our reference.

Metric: The Levenshtein distance
To measure typos, we used the Levenshtein distance, an intuitive spelling-checking model that measures the difference between two strings. The distance is calculated by counting the number of operations required to transform one string into the other. These operations can include insertions, deletions, and substitutions of characters. For example, when comparing the words ‘Cat’ and ‘Chat’ using the Levenshtein distance, we would observe that only one operation is needed to transform the word ‘Cat’ into ‘Chat’ (i.e., adding an ‘h’). Therefore, ‘Chat’ has a Levenshtein distance of one compared to ‘Cat’. However, comparing the words ‘Hats’ and ‘Cat’ would require two operations (i.e., changing ‘H’ to ‘C’ and adding an ‘s’ in the end), and therefore, ‘Hats’ would have a Levenshtein distance of two compared to ‘Cat.’

The Levenshtein distance can be made sensitive to capitalization and any character, allowing for the detection of unusual additional spaces or lowercase characters, for example. This measure can be useful for identifying small differences in text, such as those that may be introduced by typos or other anomalies in HTTP response headers. While HTTP header keys are case-insensitive by specification, our model has been adjusted to consider any character variation. Specifically, we have made the ‘Server’ header case-sensitive to catch all nuances of the server’s identity and possible anomalies.

Our model performs a comparative analysis between our predefined list (of commonly occurring HTTP response headers and server values) and the words in the HTTP responses. It is designed to return words that are nearly identical to those of the list but includes small deviations. For instance, it can detect slight deviations such as ‘Content-Tyle’ instead of the correct ‘Content-Type’.

Output: A list with anomalous HTTP responses
The model returned a list of two hundred anomalous HTTP responses from our batch of HTTP responses. We decided to check the frequency of these anomalies over the entire scan dataset, rather than the initial sample of 800.000 HTTP Responses. Our aim was to get more context regarding the prevalence of these spelling errors.

We found that some of these anomalies were relatively common among HTTP response headers. For example, we discovered more than eight thousand instances of the HTTP response header ‘Expired’ instead of ‘Expires.’ Additionally, we saw almost three thousand instances of server names that deviated from the typical naming convention of ‘Apache’ as can be seen in table 1.

DeviationCommon NameAmount
Server: Apache CoyoteServer: Apache-Coyote2941
Server: Apache \r\nServer: Apache2952
Server: Apache.Server: Apache3047
Server: CloudFlareServer: Cloudflare6615
Expired:Expires:8260
Table 1: Frequency of deviations in HTTP responses online 

Refining our research: Delving into the rarest anomalies
However, the rarest anomalies piqued our interest, as they could potentially indicate new or unknown malicious servers. We narrowed our investigation by only analyzing HTTP responses that appeared less than two hundred times in the wild and cross-referenced them with our own telemetry. By doing this, we could obtain more context from surrounding traffic to investigate potential nefarious activities. In the following section, we will focus on the most interesting typos that stood out and investigate them based on our telemetry.

Findings

Anomalous server values
During our investigation, we came across several HTTP responses that displayed deviations from the typical naming conventions of the values of the ‘Server’ header.

For instance, we encountered an HTTP response header where the ‘Server’ value was written differently than the typical ‘Microsoft-IIS’ servers. In this case, the header read ‘Microsoft -IIS’ instead of ‘Microsoft-IIS’ (again, note the space) as shown in example 3. We suspected that this deviation was an attempt to make it appear like a ‘Microsoft-IIS’ server response. However, our investigation revealed that a legitimate company was behind the server which did not immediately indicate any nefarious activity. Therefore, even though the typo in the server’s name was suspicious, it did not turn out to come from a malicious server.

Example 4: HTTP response with ‘Microsoft -IIS’ server value

The ‘ngengx’ server value appeared to intentionally mimic the common server name ‘nginx’ (see example 4). We found that it was linked to a cable setup account from an individual that subscribed to a big telecom and hosting provider in The Netherlands. This deviation from typical naming conventions was strange, but we could not find anything suspicious in this case.

Example 5: HTTP response with a ‘ngengx’ server value

Similarly, the ‘Apache64’ server value deviates from the standard ‘Apache’ server value (see example 5). We found that this HTTP response was associated with webservers of a game developer, and no apparent malevolent activities were detected.

Example 6: HTTP response with an ‘Apache64’ server value

While these deviations from standard naming conventions could potentially indicate an attempt to disguise a malicious server, it does not always indicate nefarious activity.

Anomalous response headers
Moreover, we encountered HTTP response headers that deviated from the standard naming conventions. The ‘Content-Tyle’ header deviated from the standard ‘Content-Type’ header, and we found both the correct and incorrect spellings within the HTTP response (see example 6). We discovered that these responses originated from ‘imgproxy,’ a service designed for image resizing. This service appears to be legitimate. Moreover, a review of the source code confirms that the ‘Content-Tyle’ header is indeed hardcoded in the landing page source code (see Example 7).

Example 7: HTTP response with a ‘Content-Tyle’ header
Example 8: Screenshot of the landing page source code of imgproxy

Similarly, the ‘CONTENT_LENGTH’ header deviated from the standard spelling of ‘Content-Length’ (see example 7). However, upon further investigation, we found that the server behind this response also belongs to a server associated with webservers of a game developer. Again, we did not detect any malicious activities associated with this deviation from typical naming conventions.

Example 9: HTTP response with a ‘CONTENT_LENGTH’ header

The findings of our research seem to reveal that even HTTP responses set up by legitimate companies include messy and incorrect response headers.

Concluding Insights

Our study was designed to uncover potentially malicious servers by proactively searching for spelling mistakes in HTTP response headers. HTTP servers are generally expected to adhere to the established RFC guidelines, producing consistent HTTP responses as dictated by the standard protocols. Sometimes cybercriminals hosting malicious servers attempt to evade detection by imitating standard responses of legitimate software. However, sometimes they slip up, leaving inadvertent typos, which can be used for fingerprinting purposes.

Our study reveals that typos in HTTP responses are not as rare as one might assume. Despite the crucial role that appropriate implementation of HTTP response headers plays in the security and safety of websites and web services, our research suggests that textual errors in HTTP responses are surprisingly widespread, even in the outputs of servers from legitimate organizations. Although these deviations from standard naming conventions could potentially indicate an attempt to disguise a malicious server, they do not always signify nefarious activity. The internet is simply too messy.

Our research concludes that typos alone are insufficient to identify malicious servers. Nevertheless, they retain potential as part of a broader detection framework. We propose advancing this research by combining the presence of typos with additional metrics. One approach involves establishing a baseline of common anomalous HTTP responses, and then flagging HTTP responses with new typos as they emerge.

Furthermore, more research could be conducted regarding the order of HTTP headers. If the header order in the output differs from what is expected from a particular software, in combination with (new) typos, it may signal an attempt to mimic that software.

Lastly, this strategy could be integrated with other modelling approaches, such as data science models in Security Operations Centers. For instance, monitoring servers that are not only new to the network but also exhibit spelling errors. By integrating these efforts, we strive to enhance our ability to detect emerging malicious servers.

References

  1. https://blog.fox-it.com/2019/02/26/identifying-cobalt-strike-team-servers-in-the-wild/ ↩
  2. https://www.rfc-editor.org/rfc/rfc7231 ↩
  3. https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html ↩
  4. https://owasp.org/www-project-secure-headers/ ↩
  5. https://search.censys.io/ ↩

Popping Blisters for research: An overview of past payloads and exploring recent developments

Authored by Mick Koomen

Summary

Blister is a piece of malware that loads a payload embedded inside it. We provide an overview of payloads dropped by the Blister loader based on 137 unpacked samples from the past one and a half years and take a look at recent activity of Blister. The overview shows that since its support for environmental keying, most samples have this feature enabled, indicating that attackers mostly use Blister in a targeted manner. Furthermore, there has been a shift in payload type from Cobalt Strike to Mythic agents, matching with previous reporting. Blister drops the same type of Mythic agent which we thus far cannot link to any public Mythic agents. Another development is that its developers started obfuscating the first stage of Blister, making it more evasive. We provide YARA rules and scripts1 to help analyze the Mythic agent and the packer we observed with it.

Recap of Blister

Blister is a loader that loads a payload embedded inside it and in the past was observed with activity linked to Evil Corp2,3. Matching with public reporting, we have also seen it as a follow-up in SocGholish infections. In the past, we observed Blister mostly dropping Cobalt Strike beacons, yet current developments show a shift to Mythic agents, another red teaming framework.

Elastic Security first documented Blister in December 2021 in a campaign that used malicious installers4. It used valid code signatures referencing the company Blist LLC to pose as a legitimate executable, likely leading to the name Blister. That campaign reportedly dropped Cobalt Strike and BitRat.

In 2022, Blister started solely using the x86-64 instruction set, versus including 32-bit as well. Furthermore, RedCanary wrote observing SocGholish dropping Blister5, which was later confirmed by other vendors as well6.

In August the same year, we observed a new version of Blister. This update included more configuration options, along with an optional domain hash for environmental keying, allowing attackers to deploy Blister in a targeted manner. Elastic Security recently wrote about this version7.

2023 initially did not bring new developments for Blister. However, similar to its previous update, we observed development activity in August. Notably, we saw samples with added obfuscation to the first stage of Blister, i.e. the loader component that is injected into a legitimate executable. Additionally, in July, Unit 428 observed SocGholish dropping Blister with a Mythic agent.

In summary, 2023 brought new developments for Blister, with added obfuscations to the first stage and a new type of payload. The next part of this blog is divided into two parts: firstly, we look back at previous Blister payloads and configurations, and in the second part, we discuss the recent developments.

Looking back at Blister

In early 2023, we observed a SocGholish infection at our security operations center (SOC). We notified the customer and were given a binary that was related to the infection. This turned out to be a Blister sample, with Cobalt Strike as its payload.

We wrote an extractor that worked on the sample encountered at the SOC, but for certain other Blister samples it did not. It turned out that the sample from the SOC investigation belonged to a version of Blister that was introduced in August, 2022, while older samples had a different configuration. After writing an extractor for these older versions, we made an overview of what Blister had been dropping in roughly the past two years.

The samples we analyzed are all available on VirusTotal, the platform we used to find samples. We focus on 64-bit Blister samples, newer samples are not using 32-bit anymore, as far as we know. In total, we found 137 samples we could unpack, 33 samples with the older version and 104 samples with the newer version from 2022.

In the Appendix, we list these samples, where version 1 and 2 refer to the old and new version respectively. The table is sorted on the first seen date of a sample in VirusTotal, where you clearly see the introduction of the update.

Because we want to keep the tables comprehensible, we have split up the data into four tables. For now, it is important to note that Table 2 provides information per Blister sample we unpacked, including the date it was first uploaded to VirusTotal, the version, the label of the payload it drops, the type of payload, and two configuration flags. Furthermore, to have a list of Blister and payload hashes in clear text in the blog, we included these in Table 6. We also included a more complete data set at https://github.com/fox-it/blister-research.

Discussing payloads

Looking at the dropped payloads, we see that it mostly conforms with what has already been reported. In Figure 1, we provide a timeline based on the first seen date of a sample in VirusTotal and the family of the payload. The observed payloads consist of Cobalt Strike, Mythic, Putty, and a test application. Initially, Blister dropped various flavors of Cobalt Strike and later dropped a Mythic agent, which we refer to as BlisterMythic. Recently, we also observed a packer that unpacks BlisterMythic, which we refer to as MythicPacker. Interestingly, we did not observe any samples drop BitRat.

Figure 1, Overview of Blister samples we were able to unpack, based on the first seen date reported in VirusTotal.

From the 137 samples, we were able to retrieve 74 unique payloads. This discrepancy in amount of unique Blister samples versus unique payloads is mainly caused by various Blister samples that drop the same Putty or test application, namely 18 and 22 samples, respectively. This summer has shown a particular increase in test payloads.

Cobalt Strike

Cobalt Strike was dropped through three different types of payloads, generic shellcode, DLL stagers, or obfuscated shellcode. In total, we retrieved 61 beacons, in Table 1 we list the Cobalt Strike watermarks we observed. Watermarks are a unique value linked to a license key. It should be noted that Cobalt Strike watermarks can be changed and hence are not a sound way to identify clusters of activity.

Watermark (decimal)Watermark (hexadecimal)Nr. of beacons
2065460020xc4fa4522
15801038240x5e2e789021
11019917750x41af0f5f38
Table 1, Counted Cobalt Strike watermarks observed in beacons dropped by Blister.

The watermark 206546002, though only used twice, shows up in other reports as well, e.g. a report on an Emotet intrusion9 and a report linking it to Royal, Quantum, and Play ransomware activity10,11. The watermark 1580103824 is mentioned in reports on Gootloader12, but also Cl0p13 and also is the 9th most common beacon watermark, based on our dataset of Cobalt Strike beacons14. Interestingly, 1101991775, the watermark that is most common, is not mentioned in public reporting as far as we can tell.

Cobalt Strike profile generators

In Table 3, we list information on the extracted beacons. In there, we also list the submission path. Most of the submission paths contain /safebrowsing/ and /rest/2/meetings, matching with paths found in SourcePoint15, a Cobalt Strike command-and-control (C2) profile generator. This is only, however, for the regular shellcode beacons, when we look at the obfuscated shellcode and the DLL stager beacons, it seems to use a different C2 profile. The C2 profiles for these payloads match with another public C2 profile generator16.

Domain fronting

Some of the beacons are configured to use “domain fronting”, which is a technique that allows malicious actors to hide the true destination of their network traffic and evade detection by security systems. It involves routing malicious traffic through a content delivery network (CDN) or other intermediary server, making it appear as if the traffic is going to a legitimate or benign domain, while in reality, it’s communicating with a malicious C2 server.

Certain beacons have subdomains of fastly[.]net as their C2 server, e.g. backend.int.global.prod.fastly[.]net or python.docs.global.prod.fastly[.]net. However, the domains they connect to are admin.reddit[.]com or admin.wikihow[.]com, which are legitimate domains hosted on a CDN.

Obfuscated shellcode

In five cases, we observed Blister drop Cobalt Strike by first loading obfuscated shellcode. We included a YARA rule for this particular shellcode in the Appendix.

Performing a retrohunt on VirusTotal yielded only 12 samples, with names indicating potential test files and at least one sample dropping Cobalt Strike. We are unsure whether this is an obfuscator solely used by Evil Corp or whether it is used by other threat actors as well.

Figure 2, Layout of particular shellcode, with denoted steps.

The shellcode is fairly simple, we provide an overview of it in Figure 2. The entrypoint is at the start of the buffer, which calls into the decoding stub. This call instruction automatically pushes the next instruction’s address on the stack, which the decoding stub uses as a starting point to start mutating memory. Figure 3 shows some of these instructions, which are quite distinctive.

Figure 3, Decoding instructions observed in particular shellcode.

At the end of the decoding stub, it either jumps or calls back and then invokes the decryption function. This decryption function uses RC4, but the S-Box is already initialized, thus no key-scheduling algorithm is implemented. Lastly, it jumps to the final payload.

BlisterMythic

Matching with what was already reported by Unit 428, Blister recently started using Mythic agents as its payload. Mythic is one of the many red teaming frameworks on GitHub18. You can use various agents, which are listed on GitHub as well19 and can roughly be compared to a Cobalt Strike beacon. It is possible to write your own Mythic agent, as long as you comply with a set of constraints. Thus far, we keep seeing the same Mythic agent, which we discuss in more detail later on. The first sample dropping Mythic agents was uploaded to VirusTotal on July 24th 2023, just days before initial reportings of SocGholish infections leading to Mythic. In Table 4, we provide the C2 information from the observed Mythic agents.

We observed Mythic either as a Portable Executable (PE) or as shellcode. The shellcode seems to be rare and unpacks a PE file which thus far always resulted in a Mythic agent, in our experience. We discuss this packer later on as well and provide scripts that help with retrieving the PE file it packs. We refer to this specific Mythic agent as BlisterMythic and to the packer as MythicPacker.

In Table 5, we list the BlisterMythic C2 servers we were able to find. Interestingly, the domains were all registered at DNSPod. We also observed this in the past with Cobalt Strike domains we linked to Evil Corp. Apart from this, we also see similarities in the domain names used, e.g. domains consisting of two or three words concatenated to each other and using com as top-level domain (TLD).

Test payloads

Besides red team tooling like Mythic and Cobalt Strike, we also observed Putty and a test application as payloads. Running Putty through Blister does not seem logical and is likely linked to testing. It would only result in Putty not touching the disk and running in memory, which in itself is not useful. Additionally, when we look at the domain hashes in the Blister samples, only the Putty and test application samples in some cases share their domain hash.

Blister configurations

We also looked at the configurations of Blister, from this we can to some extent derive how it is used by attackers. Note that the collection also contains “test samples” from the attacker. Except for the more obvious Putty and test application, some samples that dropped Mythic, for instance, could also be linked to testing. We chose to leave out samples that drop Putty or the test application, leaving 97 samples in total. This means that the samples paint a partly biased picture, though we think it is still valuable and provides a view into how Blister is used.

Environmental keying

Since its update in 2022, Blister includes an optional domain hash, that it computes over the DNS search domain of the machine (ComputerNameDnsDomain). It only continues executing if the hash matches with its configuration, enabling environmental keying.

By looking at the amount of samples that have domain hash verification enabled, we can say something about how Blister is deployed. From the 66 Blister samples, only 6 samples did not have domain hash verification enabled. This indicates it is mostly used in a targeted manner, corresponding with using SocGholish for initial access and reconnaissance and then deploying Blister, for example.

Persistence

Of the 97 samples, 70 have persistence enabled. For persistence, Blister still uses the same method as described by Elastic Security20. It mostly uses IFileOperation COM interface to copy rundll32.exe and itself to the Startup folder, this is significant for detection, as it means that these operations are done by the process DllHost.exe, not the rundll32.exe process that hosts Blister.

Blister trying new things

Blister’s previous update altered the core payload, however, the loader that is injected into the legitimate executable remained unchanged. In August this year, we observed experimental samples on VirusTotal with an obfuscated loader component, hinting at developer activity. Interestingly, we could link these samples to another sample on VirusTotal which solely contained the function body of the loader and another sample that contained a loader with a large set of INT 3 instructions added to it. Perhaps the developer was experimenting with different mutations to see how it influences the detection rate.

Obfuscating the first stage

Recent samples from September 2023 have the loader obfuscated in the same manner, with bogus instructions and excessive jump instructions. These changes make it harder to detect Blister using YARA, as the loader instructions are now intertwined with junk instructions and sometimes are followed by junk data due to the added jump instructions.

Figure 4, Comparison of two loader components from recent Blister samples, left is without obfuscation and right is with obfuscation.

In Figure 4, we compare the two function bodies of the loader, one body which is normally seen in Blister samples and one obfuscated function body, observed in the recent samples. The comparison shows that naive YARA rules are less likely to trigger on the obfuscated function body. In the Appendix, we provide a Blister rule that tries to detect these obfuscated samples. The added bogus instructions include instructions, such as btc, bts, lahf and cqo, bogus instructions we also observed in the Blister core before, see the core component of SHA256 4faf362b3fe403975938e27195959871523689d0bf7fba757ddfa7d00d437fd4, for example.

Dropping Mythic agents

Apart from an obfuscated loader, Mythic agents currently are the payload of choice. In September and October, we found obfuscated Blister samples only dropping Mythic. Certain samples have low or zero detections on VirusTotal21 at the time of writing, showing that obfuscation does pay off.

We now discuss one sample22 that drops a shellcode eventually executing a Mythic agent. The shellcode unpacks a PE file and executes it. We provide a YARA rule for this packer in the Appendix, which we refer to as MythicPacker. Based on this rule, we did not find other samples, suggesting it is a custom packer. Until now, we have only seen this packer unpacking Mythic agents.

The dropped Mythic agents are all similar and we cannot link them to any public agents thus far. This could mean that Blister developers created their own Mythic agent, though this is uncertain. We provided a YARA rule that matches on all agents we encountered, a VirusTotal retrohunt over the past year resulted in only four samples, all linked to Blister. We think this Mythic agent is likely custom-made.

Figure 5, BlisterMythic configuration decryption.

The agents all share a similar structure, namely an encrypted configuration in the .bss section of the executable. The agent has an encrypted configuration which is decrypted by XORing the size of the configuration with a constant that differs per sample, it seems. For PE files, we have a Python script that can decrypt a configuration. Figure 5 denotes this decryption loop, where the XOR constant is 0x48E12000.

Figure 6, Decrypted BlisterMythic configuration

Dumping the configuration results in a binary blob that contains various information, including the C2 server. Figure 6 shows a hexdump of a snippet from the decrypted configuration. We created a script to dump the decrypted configuration of the BlisterMythic agent in PE format and also a script that unpacks MythicPacker shellcode and outputs a reconstructed PE file, see https://github.com/fox-it/blister-research.

Conclusion

In this post, we provided an overview of observed Blister payloads from the past one and a half years on VirusTotal and also gave insight into recent developments. Furthermore, we provided scripts and YARA rules to help analyze Blister and the Mythic agent it drops.

From the analyzed payloads, we see that Cobalt Strike was the favored choice, but that lately this has been replaced by Mythic. Cobalt Strike was mostly dropped as shellcode and briefly run through obfuscated shellcode or a DLL stager. Apart from Cobalt Strike and Mythic, we saw that Blister test samples are uploaded to VirusTotal as well.

The custom Mythic agent together with the obfuscated loader, are new Blister developments that happened in the past months. It is likely that its developers were aware that the loader component was still a weak spot in terms of static detection. Additionally, throughout the years, Cobalt Strike has received a lot of attention from the security community, with available dumpers and C2 feeds readily available. Mythic is not as popular and allows you to write your own agent, making it an appropriate replacement for now.

References

  1. https://github.com/fox-it/blister-research ↩
  2. https://www.mandiant.com/resources/blog/unc2165-shifts-to-evade-sanctions ↩
  3. https://www.microsoft.com/en-us/security/blog/2022/05/09/ransomware-as-a-service-understanding-the-cybercrime-gig-economy-and-how-to-protect-yourself/ ↩
  4. https://www.elastic.co/security-labs/elastic-security-uncovers-blister-malware-campaign ↩
  5. https://redcanary.com/blog/intelligence-insights-january-2022/ ↩
  6. https://www.trendmicro.com/en_ie/research/22/d/Thwarting-Loaders-From-SocGholish-to-BLISTERs-LockBit-Payload.html ↩
  7. https://www.elastic.co/security-labs/revisiting-blister-new-developments-of-the-blister-loader ↩
  8. https://twitter.com/Unit42_Intel/status/1684583246032506880 ↩
  9. https://thedfirreport.com/2022/09/12/dead-or-alive-an-emotet-story/ ↩
  10. https://www.group-ib.com/blog/shadowsyndicate-raas/ ↩
  11. https://www.trendmicro.com/en_us/research/22/i/play-ransomware-s-attack-playbook-unmasks-it-as-another-hive-aff.html ↩
  12. https://thedfirreport.com/2022/05/09/seo-poisoning-a-gootloader-story/ ↩
  13. https://redcanary.com/wp-content/uploads/2022/05/Gootloader.pdf ↩
  14. https://research.nccgroup.com/2022/03/25/mining-data-from-cobalt-strike-beacons/ ↩
  15. https://github.com/Tylous/SourcePoint ↩
  16. https://github.com/threatexpress/random_c2_profile ↩
  17. https://twitter.com/Unit42_Intel/status/1684583246032506880 ↩
  18. https://github.com/its-a-feature/Mythic ↩
  19. https://mythicmeta.github.io/overview/ ↩
  20. https://www.elastic.co/security-labs/blister-loader ↩
  21. https://www.virustotal.com/gui/file/a5fc8d9f9f4098e2cecb3afc66d8158b032ce81e0be614d216c9deaf20e888ac ↩
  22. https://www.virustotal.com/gui/file/f58de1733e819ea38bce21b60bb7c867e06edb8d4fd987ab09ecdbf7f6a319b9 ↩

Appendix

YARA rules

rule shellcode_obfuscator
{
    meta:
        os = "Windows"
        arch = "x86-64"
        description = "Detects shellcode packed with unknown obfuscator observed in Blister samples."
        reference_sample = "178ffbdd0876b99ad1c2d2097d9cf776eca56b540a36c8826b400cd9d5514566"
    strings:
        $rol_ror = { 48 C1 ?? ?? ?? 48 C1 ?? ?? ?? 48 C1 ?? ?? ?? }
        $mov_rol_mov = { 4d ?? ?? ?? 49 c1 ?? ?? ?? 4d ?? ?? ?? }
        $jmp = { 49 81 ?? ?? ?? ?? ?? 41 ?? }
    condition:
        #rol_ror > 60 and $jmp and filesize < 2MB and #mov_rol_mov > 60
}

import "pe"
import "math"

rule blister_x64_windows_loader {
    meta:
        os = "Windows"
        arch = "x86-64"
        family = "Blister"
        description = "Detects Blister loader component injected into legitimate executables."
        reference_sample = "343728792ed1e40173f1e9c5f3af894feacd470a9cdc72e4f62c0dc9cbf63fc1, 8d53dc0857fa634414f84ad06d18092dedeb110689a08426f08cb1894c2212d4, a5fc8d9f9f4098e2cecb3afc66d8158b032ce81e0be614d216c9deaf20e888ac"
    strings:
        // 65 48 8B 04 25 60 00 00 00                          mov     rax, gs:60h
        $inst_1 = {65 48 8B 04 25 60 00 00 00}
        // 48 8D 87 44 6D 00 00                                lea     rax, [rdi+6D44h]
        $inst_2 = {48 8D 87 44 6D 00 00}
        // 44 69 C8 95 E9 D1 5B                                imul    r9d, eax, 5BD1E995h
        $inst_3 = {44 ?? ?? 95 E9 D1 5B}
        // 41 81 F9 94 85 09 64                                cmp     r9d, 64098594h
        $inst_4 = {41 ?? ?? 94 85 09 64}
        // B8 FF FF FF 7F                                      mov     eax, 7FFFFFFFh
        $inst_5 = {B8 FF FF FF 7F}
        // 48 8D 4D 48                                         lea     rcx, [rbp+48h]
        $inst_6 = {48 8D 4D 48}
    condition:
        uint16(0) == 0x5A4D and
        all of ($inst_*) and
        pe.number_of_resources > 0 and
        for any i in (0..pe.number_of_resources - 1):
            ( (math.entropy(pe.resources[i].offset, pe.resources[i].length) > 6) and
                pe.resources[i].length > 200000 
            )
}

rule blister_mythic_payload {
    meta:
        os = "Windows"
        arch = "x86-64"
        family = "BlisterMythic"
        description = "Detects specific Mythic agent dropped by Blister."
        reference_samples = "2fd38f6329b9b2c5e0379a445e81ece43fe0372dec260c1a17eefba6df9ffd55, 3d2499e5c9b46f1f144cfbbd4a2c8ca50a3c109496a936550cbb463edf08cd79, ab7cab5192f0bef148670338136b0d3affe8ae0845e0590228929aef70cb9b8b, f89cfbc1d984d01c57dd1c3e8c92c7debc2beb5a2a43c1df028269a843525a38"
    strings:
        $start_inst = { 48 83 EC 28 B? [4-8] E8 ?? ?? 00 00 }
        $for_inst = { 48 2B C8 0F 1F 00 C6 04 01 00 48 2D 00 10 00 00 }
    condition:
        all of them
}

rule mythic_packer
{
    meta:
        os = "Windows"
        arch = "x86-64"
        family = "MythicPacker"
        description = "Detects specific PE packer dropped by Blister."
        reference_samples = "9a08d2db7d0bd7d4251533551d4def0f5ee52e67dff13a2924191c8258573024, 759ac6e54801e7171de39e637b9bb525198057c51c1634b09450b64e8ef47255"
    strings:
        // 41 81 38 72 47 65 74        cmp     dword ptr [r8], 74654772h
        $a = { 41 ?? ?? 72 47 65 74 }
        // 41 81 38 72 4C 6F 61        cmp     dword ptr [r8], 616F4C72h
        $b = { 41 ?? ?? 72 4C 6F 61 }
        // B8 01 00 00 00              mov     eax, 1
        // C3                          retn
        $c = { B8 01 00 00 00 C3 }
    condition:
        all of them and uint8(0) == 0x48
}

Blister payloads listing

First seenVersionPayload familyPayload typeEnvironmental keyingPersistence
2021-12-031Cobalt StrikeshellcodeN/a0
2021-12-051Cobalt StrikeshellcodeN/a0
2021-12-141Cobalt StrikeshellcodeN/a0
2022-01-101Cobalt StrikeshellcodeN/a1
2022-01-111Cobalt StrikeshellcodeN/a1
2022-01-191Cobalt StrikeshellcodeN/a1
2022-01-191Cobalt StrikeshellcodeN/a1
2022-01-311Cobalt StrikeshellcodeN/a1
2022-02-141Cobalt StrikeshellcodeN/a1
2022-02-171Cobalt StrikeshellcodeN/a1
2022-02-221Cobalt StrikeshellcodeN/a1
2022-02-261Cobalt StrikeshellcodeN/a1
2022-03-101Cobalt StrikeshellcodeN/a1
2022-03-141Cobalt StrikeshellcodeN/a1
2022-03-151Cobalt StrikeshellcodeN/a0
2022-03-151Cobalt StrikeshellcodeN/a0
2022-03-181Cobalt StrikeshellcodeN/a0
2022-03-181Cobalt StrikeshellcodeN/a1
2022-03-241PuttyexeN/a0
2022-03-241PuttyexeN/a0
2022-03-301Cobalt StrikeshellcodeN/a1
2022-04-011Cobalt StrikeshellcodeN/a0
2022-04-111Cobalt StrikeshellcodeN/a1
2022-04-221Cobalt StrikeshellcodeN/a1
2022-04-251Cobalt StrikeshellcodeN/a0
2022-06-011Cobalt StrikeshellcodeN/a0
2022-06-021Cobalt StrikeshellcodeN/a1
2022-06-141Cobalt StrikeshellcodeN/a1
2022-07-041Cobalt StrikeshellcodeN/a1
2022-07-191Cobalt StrikeshellcodeN/a0
2022-07-211Cobalt StrikeshellcodeN/a0
2022-08-051Cobalt StrikeshellcodeN/a1
2022-08-292Cobalt Strikeshellcode01
2022-09-022Cobalt Strikeshellcode00
2022-09-292Cobalt Strikeshellcode10
2022-10-182Cobalt Strikeshellcode11
2022-10-182Cobalt Strikeshellcode11
2022-10-182Cobalt Strikeshellcode10
2022-10-182Cobalt Strikeshellcode11
2022-10-212Cobalt Strikeshellcode11
2022-10-212Cobalt Strikeshellcode10
2022-10-242Cobalt Strikeshellcode11
2022-10-262Cobalt Strikeshellcode11
2022-10-262Cobalt Strikeshellcode11
2022-10-282Cobalt Strikeshellcode10
2022-10-312Cobalt Strikeshellcode11
2022-11-022Cobalt Strikeshellcode11
2022-11-032Cobalt Strikeshellcode11
2022-11-072Cobalt Strikeshellcode11
2022-11-082Cobalt Strikeshellcode11
2022-11-172Cobalt Strikeshellcode11
2022-11-222Cobalt Strikeshellcode11
2022-11-302Cobalt Strikeshellcode11
2022-12-012Cobalt Strikeshellcode11
2022-12-012Cobalt Strikeshellcode10
2022-12-012Cobalt Strikeshellcode10
2022-12-022Cobalt Strikeshellcode11
2022-12-052Cobalt Strikeshellcode11
2022-12-122Cobalt Strikeshellcode11
2022-12-132Cobalt Strikeshellcode11
2022-12-232Cobalt Strikeshellcode11
2023-01-062Cobalt Strikeshellcode11
2023-01-162Cobalt Strike obfuscated shellcodeshellcode11
2023-01-162Cobalt Strike obfuscated shellcodeshellcode11
2023-01-162Cobalt Strike obfuscated shellcodeshellcode11
2023-01-172Cobalt Strikeshellcode01
2023-01-172Cobalt Strike obfuscated shellcodeshellcode11
2023-01-202Cobalt Strike obfuscated shellcodeshellcode11
2023-01-202Cobalt Strike obfuscated shellcodeshellcode11
2023-01-242Cobalt Strikeshellcode11
2023-01-262Cobalt Strikeshellcode11
2023-01-262Cobalt Strikeshellcode11
2023-02-022Cobalt Strikeshellcode11
2023-02-022Test applicationshellcode10
2023-02-022Test applicationshellcode10
2023-02-022Puttyexe10
2023-02-022Test applicationshellcode10
2023-02-152Puttyexe10
2023-02-152Test applicationshellcode10
2023-02-152Puttyexe10
2023-02-152Test applicationshellcode10
2023-02-172Cobalt Strike stagerexe11
2023-02-272Cobalt Strike stagerexe11
2023-02-282Cobalt Strike stagerexe11
2023-03-062Cobalt Strike stagerexe11
2023-03-062Cobalt Strike stagerexe11
2023-03-062Cobalt Strike stagerexe11
2023-03-152Cobalt Strike stagerexe10
2023-03-192Cobalt Strike stagerexe11
2023-03-231Cobalt StrikeshellcodeN/a1
2023-03-282Cobalt Strike stagerexe11
2023-03-282Cobalt Strike stagerexe10
2023-04-032Cobalt Strike stagerexe11
2023-05-252Cobalt Strike stagerexe01
2023-05-262Cobalt Strikeshellcode11
2023-06-112Test applicationshellcode10
2023-06-112Puttyexe10
2023-06-112Puttyexe10
2023-07-242BlisterMythicexe11
2023-07-272BlisterMythicexe11
2023-08-092Test applicationshellcode10
2023-08-092Test applicationshellcode10
2023-08-092Test applicationshellcode10
2023-08-092Test applicationshellcode10
2023-08-092Test applicationshellcode10
2023-08-092Test applicationshellcode10
2023-08-092Test applicationshellcode10
2023-08-092Test applicationshellcode10
2023-08-092Test applicationshellcode10
2023-08-092Test applicationshellcode10
2023-08-092Test applicationshellcode10
2023-08-102Puttyshellcode10
2023-08-102Puttyshellcode10
2023-08-102Puttyshellcode10
2023-08-102Puttyshellcode10
2023-08-102Puttyshellcode10
2023-08-102Puttyshellcode10
2023-08-102Puttyshellcode10
2023-08-102Puttyshellcode10
2023-08-102Puttyshellcode10
2023-08-112BlisterMythicexe10
2023-08-152Test applicationshellcode10
2023-08-172BlisterMythicexe11
2023-08-182MythicPackershellcode10
2023-09-052MythicPackershellcode00
2023-09-052MythicPackershellcode01
2023-09-082Test applicationshellcode10
2023-09-082Test applicationshellcode10
2023-09-082Test applicationshellcode10
2023-09-082Puttyshellcode10
2023-09-082Puttyshellcode10
2023-09-082Test applicationshellcode10
2023-09-192BlisterMythicexe11
2023-09-212MythicPackershellcode10
2023-09-212MythicPackershellcode10
2023-10-032MythicPackershellcode10
2023-10-102MythicPackershellcode10
Table 2, Information on unpacked Blister samples.

Cobalt Strike beacons

WatermarkDomainURI
1101991775albertonne[.]com/safebrowsing/d4alBmGBO/HafYg4QZaRhMBwuLAjVmSPc
1101991775astradamus[.]com/Collect/union/QXMY8BHNIPH7
1101991775backend.int.global.prod.fastly[.]net/Detect/devs/NJYO2MUY4V
1101991775cclastnews[.]com/safebrowsing/d4alBmGBO/UaIzXMVGvV3tS2OJiKxSzyzbh4u1
1101991775cdp-chebe6efcxhvd0an.z01.azurefd[.]net/Detect/devs/NJYO2MUY4V
1101991775deep-linking[.]com/safebrowsing/fDeBjO/2hmXORzLK7PkevU1TehrmzD5z9
1101991775deep-linking[.]com/safebrowsing/fDeBjO/dMfdNUdgjjii3Ccalh10Mh4qyAFw5mS
1101991775deep-linking[.]com/safebrowsing/fDeBjO/vnZNyQrwUjndCPsCUXSaI
1101991775diggin-fzbvcfcyagemchbq.z01.azurefd[.]net/restore/how/3RG4G5T87
1101991775edubosi[.]com/safebrowsing/bsaGbO6l/ybGoI3wmK2uF9w9aL5qKmnS8IZIWsJqhp
1101991775e-sistem[.]com/Detect/devs/NJYO2MUY4V
1101991775ewebsofts[.]com/safebrowsing/3Tqo/UMskN3Lh0LyLy8BfpG1Bsvp
1101991775expreshon[.]com/safebrowsing/fDeBjO/2hmXORzLK7PkevU1TehrmzD5z9
1101991775eymenelektronik[.]com/safebrowsing/dfKa/B58qAhJ0AEF7aNwauoqpAL8
1101991775gotoknysna.com.global.prod.fastly[.]net/safebrowsing/fDeBjO/2hmXORzLK7PkevU1TehrmzD5z9
1101991775henzy-h6hxfpfhcaguhyf5.z01.azurefd[.]net/Detect/devs/NJYO2MUY4V
1101991775lepont-edu[.]com/safebrowsing/dfKa/9T1BuXpqEDg9tx53mQRU6
1101991775lindecolas[.]com/safebrowsing/d4alBmGBO/UaIzXMVGvV3tS2OJiKxSzyzbh4u1
1101991775lodhaamarathane[.]com/safebrowsing/dfKa/9T1BuXpqEDg9tx53mQRU6
1101991775mail-adv[.]com/safebrowsing/bsaGbO6l/dl1sskHxt1uGDGUnLDB5gxn4vYZQK1kaG6
1101991775mainecottagebythesea[.]com/functionalStatus/cjdl-CLe4j-XHyiEaDqQx
1101991775onscenephotos[.]com/restore/how/3RG4G5T87
1101991775promedia-usa[.]com/safebrowsing/d4alBmGBO/HafYg4QZaRhMBwuLAjVmSPc
1101991775python.docs.global.prod.fastly[.]net/Collect/union/QXMY8BHNIPH7
1101991775realitygangnetwork[.]com/functionalStatus/qPprp9dtVhrGV3R3re5Xy4M2cfQo4wB
1101991775realitygangnetwork[.]com/functionalStatus/vFi8EPnc9zJTD0GgRPxggCQAaNb
1101991775sanfranciscowoodshop[.]com/safebrowsing/dfKa/GgVYon5zhYu5L7inFbl1MZEv7RGOnsS00b
1101991775sohopf[.]com/apply/admin_/99ZSSAHDH
1101991775spanish-home-sales[.]com/safebrowsing/d4alBmGBO/EB-9sfMPmsHmH-A7pmll9HbV0g
1101991775steveandzina[.]com/safebrowsing/d4alBmGBO/mr3lHbohEvZa0mKDWWdwTV5Flsxh
1101991775steveandzina[.]com/safebrowsing/d4alBmGBO/YwTM1CK0mBV1Y7UDagpjP
1101991775websterbarn[.]com/safebrowsing/fDeBjO/CGZcHKnX3arVCfFp98k8
158010382410.158.128[.]50
1580103824bimelectrical[.]com/safebrowsing/7IAMO/hxNTeZ8lBNYqjAsQ2tBRS
1580103824bimelectrical[.]com/safebrowsing/7IAMO/Jwee0NMJNKn9sDD8sUEem4g8jcB2v44UINpCIj
1580103824bookmark-tag[.]com/safebrowsing/eMUgI4Z/3RzgDBAvgg3DQUn8XtN8l
1580103824braprest[.]com/safebrowsing/d5pERENa/3tPCoNwoGwXAvV1w1JAS-OOPyVYxL1K2styHFtbXar7ME
1580103824change-land[.]com/safebrowsing/TKc3hA/DzwHHcc8y8O9kAS7cl4SDK0e6z0KHKIX9w7
1580103824change-land[.]com/safebrowsing/TKc3hA/nLTHCIhzOKpdFp0GFHYBK-0bRwdNDlZz6Qc
1580103824clippershipintl[.]com/safebrowsing/sj0IWAb/YhcZADXFB3NHbxFtKgpqBtK9BllJiGEL
1580103824couponbrothers[.]com/safebrowsing/Jwjy4/mzAoZyZk7qHIyw3QrEpXij5WFhIo1z8JDUVA0N0
1580103824electronic-infinity[.]com/safebrowsing/TKc3hA/t-nAkENGu9rpZ9ebRRXr79b
1580103824final-work[.]com/safebrowsing/AvuvAkxsR/8I6ikMUvdNd8HOgMeD0sPfGpwSZEMr
1580103824geotypico[.]com/safebrowsing/d5pERENa/f5oBhEk7xS3cXxstp6Kx1G7u3N546UStcg9nEnzJn2k
1580103824imsensors[.]com/safebrowsing/eMUgI4Z/BOhKRIMsJsuPnn3IQvgrEc3XLQUB3W
1580103824intradayinvestment[.]com/safebrowsing/dpNqi/nXeFgGufr9VqHjDdsIZbw-ZH0
1580103824medicare-cost[.]com/safebrowsing/dpNqi/F3QExtY65SvTVK1ewA26
1580103824optiontradingsignal[.]com/safebrowsing/dpNqi/7CtHhF-isMMQ6m7NmHYNb0N7E7Fe
1580103824setechnowork[.]com/safebrowsing/fBm1b/JbcKDYjMWcQNjn69LnGggFe6mpjn5xOQ
1580103824sikescomposites[.]com/safebrowsing/Jwjy4/cmr4tZ7IyFGbgCiof2tHMO
1580103824technicollit[.]com/safebrowsing/b0kKKIjr/AzX9ZHB37oJfPsUBUaxBJjzzi132cYRZhUZc81g
1580103824wasfatsahla[.]com/safebrowsing/IsXNCJJfH/5x0rUIrn–r85sLJIuEY7C9q
206546002smutlr[.]com/functionalStatus/qPprp9dtVhrGV3R3re5Xy4M2cfQo4wB
206546002spanish-home-sales[.]com/functionalStatus/fb8ClEdmm-WwYudk-zODoQYB7DX3wQYR
Table 3, Information on observed Cobalt Strike beacons dropped by Blister.

BlisterMythic payloads

DomainURI
139-177-202-78.ip.linodeusercontent[.]com/etc.clientlibs/sapdx/front-layer/dist/resources/sapcom/919.9853a7ee629d48b1ddbe.js
23-92-30-58.ip.linodeusercontent[.]com/etc.clientlibs/sapdx/front-layer/dist/resources/sapcom/919.9853a7ee629d48b1ddbe.js
aviditycellars[.]com/etc.clientlibs/sapdx/front-layer/dist/resources/sapcom/919.9853a7ee629d48b1ddbe.js
boxofficeseer[.]com/s/0.7.8/clarity.js
d1hp6ufzqrj3xv.cloudfront[.]net/organizations/oauth2/v2.0/authorize
makethumbmoney[.]com/s/0.7.8/clarity.js
rosevalleylimousine[.]com/login.sophos.com/B2C_1A_signup_signin/api/SelfAsserted/confirmed
Table 4, Information on observed Mythic agents dropped by Blister.

BlisterMythic C2 servers

IPDomain
37.1.215[.]57angelbusinessteam[.]com
92.118.112[.]100danagroupegypt[.]com
104.238.60[.]11shchiswear[.]com
172.233.238[.]215N/a
96.126.111[.]127N/a
23.239.11[.]145N/a
45.33.98[.]254N/a
45.79.199[.]4N/a
45.56.105[.]98N/a
149.154.158[.]243futuretechfarm[.]com
104.243.33[.]161sms-atc[.]com
104.243.33[.]129makethumbmoney[.]com
138.124.180[.]241vectorsandarrows[.]com
94.131.101[.]58pacatman[.]com
198.58.119[.]214N/a
185.174.101[.]53personmetal[.]com
185.45.195[.]30aviditycellars[.]com
185.250.151[.]145bureaudecreationalienor[.]com
23.227.194[.]115bitscoinc[.]com
88.119.175[.]140boxofficeseer[.]com
88.119.175[.]137thesheenterprise[.]com
37.1.214[.]162remontisto[.]com
45.66.248[.]99N/a
88.119.175[.]104visioquote[.]com
45.66.248[.]13cannabishang[.]com
92.118.112[.]8turanmetal[.]com
37.1.211[.]150lucasdoors[.]com
185.72.8[.]219displaymercials[.]com
172.232.172[.]128N/a
82.117.253[.]168digtupu[.]com
104.238.60[.]112avblokhutten[.]com
173.44.141[.]34hom4u[.]com
170.130.165[.]140rosevalleylimousine[.]com
172.232.172[.]110N/a
5.8.63[.]79boezgrt[.]com
172.232.172[.]125N/a
162.248.224[.]56hatchdesignsnh[.]com
185.174.101[.]13formulaautoparts[.]com
23.152.0[.]193ivermectinorder[.]com
192.169.6[.]200szdeas[.]com
194.87.32[.]85licencesolutions[.]com
185.45.195[.]205motorrungoli[.]com
Table 5, Detected BlisterMythic C2 servers

Blister samples

SHA256Payload familyPayload SHA256
0a73a9ee3650821352d9c4b46814de8f73fde659cae6b82a11168468becb68d1Cobalt Strike397c08f5cdc59085a48541c89d23a8880d41552031955c4ba38ff62e57cfd803
0bbf1a3a8dd436fda213bc126b1ad0b8704d47fd8f14c75754694fd47a99526cBlisterMythicab7cab5192f0bef148670338136b0d3affe8ae0845e0590228929aef70cb9b8b
0e8458223b28f24655caf37e5c9a1c01150ac7929e6cb1b11d078670da892a5bCobalt Strike4420bd041ae77fce2116e6bd98f4ed6945514fad8edfbeeeab0874c84054c80a
0f07c23f7fe5ff918ee596a7f1df320ed6e7783ff91b68c636531aba949a6f33Test application43308bde79e71b2ed14f318374a80fadf201cc3e34a887716708635294031b1b
a3cb53ddd4a5316cb02b7dc4ccd1f615755b46e86a88152a1f8fc59efe170497Cobalt Strikee85a2e8995ef37acf15ea79038fae70d4566bd912baac529bad74fbec5bb9c21
a403b82a14b392f8485a22f105c00455b82e7b8a3e7f90f460157811445a8776Cobalt Strikee0c0491e45dda838f4ac01b731dd39cc7064675a6e1b79b184fff99cdce52f54
a5fc8d9f9f4098e2cecb3afc66d8158b032ce81e0be614d216c9deaf20e888acTest application43308bde79e71b2ed14f318374a80fadf201cc3e34a887716708635294031b1b
a9ea85481e178cd35ae323410d619e97f49139dcdb2e7da72126775a89a8464fCobalt Strikec7accad7d8da9797788562a3de228186290b0f52b299944bec04a95863632dc0
ac232e7594ce8fbbe19fc74e34898c562fe9e8f46d4bfddc37aefeb26b85c02bCobalt Strike obfuscated shellcodecef1a88dfc436dab9ae104f0770a434891bbd609e64df43179b42b03a7e8f908
acdaac680e2194dd8fd06f937847440e7ab83ce1760eab028507ee8eba557291Cobalt Strikeb96d4400e9335d80dedee6f74ffaa4eca9ffce24c370790482c639df52cb3127
ae148315cec7140be397658210173da372790aa38e67e7aa51597e3e746f2cb2Cobalt Strikef245b2bc118c3c20ed96c8a9fd0a7b659364f9e8e2ee681f5683681e93c4d78b
aeecc65ac8f0f6e10e95a898b60b43bf6ba9e2c0f92161956b1725d68482721dCobalt Strike797abd3de3cb4c7a1ceb5de5a95717d84333bedcbc0d9e9776d34047203181bc
b062dd516cfa972993b6109e68a4a023ccc501c9613634468b2a5a508760873eCobalt Strike122b77fd4d020f99de66bba8346961b565e804a3c29d0757db807321e9910833
b10db109b64b798f36c717b7a050c017cf4380c3cb9cfeb9acd3822a68201b5bCobalt Strike902d29871d3716113ca2af5caa6745cb4ab9d0614595325c1107fb83c1494483
b1d1a972078d40777d88fb4cd6aef1a04f29c5dd916f30a6949b29f53a2d121cPutty0581160998be30f79bd9a0925a01b0ebc4cb94265dfa7f8da1e2839bf0f1e426
b1f3f1c06b1cc9a249403c2863afc132b2d6a07f137166bdd1e4863a0cece5b1Cobalt Strikee63807daa9be0228d90135ee707ddf03b0035313a88a78e50342807c27658ff2
b4c746e9a49c058ae3843799cdd6a3bb5fe14b413b9769e2b5a1f0f846cb9d37Cobalt Strike stager063191c49d49e6a8bdcd9d0ee2371fb1b90f1781623827b1e007e520ec925445
b4f37f13a7e9c56ea95fa3792e11404eb3bdb878734f1ca394ceed344d22858fTest application43308bde79e71b2ed14f318374a80fadf201cc3e34a887716708635294031b1b
b956c5e8ec6798582a68f24894c1e78b9b767aae4d5fb76b2cc71fc9c8befed8Cobalt Strike6fc283acfb7dda7bab02f5d23dc90b318f4c73a8e576f90e1cac235bf8d02470
b99ba2449a93ab298d2ec5cacd5099871bacf6a8376e0b080c7240c8055b1395Cobalt Strike96fab57ef06b433f14743da96a5b874e96d8c977b758abeeb0596f2e1222b182
b9e313e08b49d8d2ffe44cb6ec2192ee3a1c97b57c56f024c17d44db042fb9ebTest application43308bde79e71b2ed14f318374a80fadf201cc3e34a887716708635294031b1b
bc238b3b798552958009f3a4ce08e5ce96edff06795281f8b8de6f5df9e4f0feCobalt Strike stager191566d8cc119cd6631d353eab0b8c1b8ba267270aa88b5944841905fa740335
bcd64a8468762067d8a890b0aa7916289e68c9d8d8f419b94b78a19f5a74f378Putty0581160998be30f79bd9a0925a01b0ebc4cb94265dfa7f8da1e2839bf0f1e426
c113e8a1c433b4c67ce9bce5dea4b470da95e914de4dc3c3d5a4f98bce2b7d6cPutty0581160998be30f79bd9a0925a01b0ebc4cb94265dfa7f8da1e2839bf0f1e426
c1261f57a0481eb5d37176702903025c5b01a166ea6a6d42e1c1bdc0e5a0b04bCobalt Strike obfuscated shellcode189b7afdd280d75130e633ebe2fcf8f54f28116a929f5bb5c9320f11afb182d4
c149792a5e5ce4c15f8506041e2f234a9a9254dbda214ec79ceef7d0911a3095Putty0581160998be30f79bd9a0925a01b0ebc4cb94265dfa7f8da1e2839bf0f1e426
c2046d64bcfbab5afcb87a75bf3110e0fa89b3e0f7029ff81a335911cf52f00aCobalt Striked048001f09ad9eedde44f471702a2a0f453c573db9c8811735ec45d65801f1d0
c3509ba690a1fcb549b95ad4625f094963effc037df37bd96f9d8ed5c7136d94Cobalt Strikee0c0491e45dda838f4ac01b731dd39cc7064675a6e1b79b184fff99cdce52f54
c3cfbede0b561155062c2f44a9d44c79cdb78c05461ca50948892ff9a0678f3fCobalt Strikebcb32a0f782442467ea8c0bf919a28b58690c68209ae3d091be87ef45d4ef049
c79ab271d2abd3ee8c21a8f6ad90226e398df1108b4d42dc551af435a124043cCobalt Strike749d061acb0e584df337aaef26f3b555d5596a96bfffc9d6cd7421e22c0bacea
cab95dc6d08089dcd24c259f35b52bca682635713c058a74533501afb94ab91fPutty0581160998be30f79bd9a0925a01b0ebc4cb94265dfa7f8da1e2839bf0f1e426
cea5c060dd8abd109b478e0de481f0df5ba3f09840746a6a505374d526bd28dcMythicPacker759ac6e54801e7171de39e637b9bb525198057c51c1634b09450b64e8ef47255
cfa604765b9d7a93765d46af78383978251486d9399e21b8e3da4590649c53e4Cobalt Strike stager57acdb7a22f5f0c6d374be2341dbef97efbcc61f633f324beb0e1214614fef82
d1afca36f67b24eae7f2884c27c812cddc7e02f00f64bb2f62b40b21ef431084Cobalt Strikef570bd331a3d75e065d1825d97b922503c83a52fc54604d601d2e28f4a70902b
d1b6671fc0875678ecf39d737866d24aca03747a48f0c7e8855a5b09fc08712dTest application43308bde79e71b2ed14f318374a80fadf201cc3e34a887716708635294031b1b
d3d48aa32b062b6e767966a8bab354eded60e0a11be5bc5b7ad8329aa5718c76Cobalt Strike60905c92501ec55883afc3f6402a05bddfd335323fdc0144515f01e8da0acbda
d3eab2a134e7bd3f2e8767a6285b38d19cd3df421e8af336a7852b74f194802cBlisterMythic2fd38f6329b9b2c5e0379a445e81ece43fe0372dec260c1a17eefba6df9ffd55
d439f941b293e3ded35bf52fac7f20f6a2b7f2e4b189ad2ac7f50b8358110491Cobalt Strike18a9eafb936bf1d527bd4f0bfae623400d63671bafd0aad0f72bfb59beb44d5f
dac00ec780aabaffed1e89b3988905a7f6c5c330218b878679546a67d7e0eef2Cobalt Strikeadc73af758c136e5799e25b4d3d69e462e090c8204ec8b8f548e36aac0f64a66
db62152fe9185cbd095508a15d9008b349634901d37258bc3939fe3a563b4b3cMythicPacker7f71d316c197e4e0aa1fce9d40c6068ada4249009e5da51479318de039973ad8
db81e91fc05991f71bfd5654cd60b9093c81d247ccd8b3478ab0ebef61efd2adPutty0581160998be30f79bd9a0925a01b0ebc4cb94265dfa7f8da1e2839bf0f1e426
dd42c1521dbee54173be66a5f98a811e5b6ee54ad1878183c915b03b68b7c9bbCobalt Striked988a867a53c327099a4c9732a1e4ced6fe6eca5dd68f67e5f562ab822b8374b
e0888b80220f200e522e42ec2f15629caa5a11111b8d1babff509d0da2b948f4Cobalt Strike915503b4e985ab31bc1d284f60003240430b3bdabb398ae112c4bd1fe45f3cdd
e30503082d3257737bba788396d7798e27977edf68b9dba7712a605577649ffbCobalt Strikedf01b0a8112ca80daf6922405c3f4d1ff7a8ff05233fc0786e9d06e63c9804d6
e521cad48d47d4c67705841b9c8fa265b3b0dba7de1ba674db3a63708ab63201Cobalt Strike stager40cac28490cddfa613fd58d1ecc8e676d9263a46a0ac6ae43bcbdfedc525b8ee
e62f5fc4528e323cb17de1fa161ad55eb451996dec3b31914b00e102a9761a52Cobalt Strike19e7bb5fa5262987d9903f388c4875ff2a376581e4c28dbf5ae7d128676b7065
ebafb35fd9c7720718446a61a0a1a10d09bf148d26cdcd229c1d3d672835335cCobalt Strike5cb2683953b20f34ff26ddc0d3442d07b4cd863f29ec3a208cbed0dc760abd04
ebf40e12590fcc955b4df4ec3129cd379a6834013dae9bb18e0ec6f23f935bbaCobalt Striked99bac48e6e347fcfd56bbf723a73b0b6fb5272f92a6931d5f30da76976d1705
ef7ff2d2decd8e16977d819f122635fcd8066fc8f49b27a809b58039583768d2Cobalt Strikeadc73af758c136e5799e25b4d3d69e462e090c8204ec8b8f548e36aac0f64a66
efbffc6d81425ffb0d81e6771215c0a0e77d55d7f271ec685b38a1de7cc606a8Cobalt Strike47bd5fd96c350f5e48f5074ebee98e8b0f4efb8a0cd06db5af2bdc0f3ee6f44f
f08fdb0633d018c0245d071fa79cdc3915da75d3c6fc887a5ca6635c425f163aTest application43308bde79e71b2ed14f318374a80fadf201cc3e34a887716708635294031b1b
f3bfd8ab9e79645babf0cb0138d51368fd452db584989c4709f613c93caf2bdcCobalt Strikecd7135c94929f55e19e5d66359eab46422c3c59d794dde00c8b4726498e4e01a
f58de1733e819ea38bce21b60bb7c867e06edb8d4fd987ab09ecdbf7f6a319b9MythicPacker19eae7c0b7a1096a71b595befa655803c735006d75d5041c0e18307bd967dee6
f7fa532ad074db4a39fd0a545278ea85319d08d8a69c820b081457c317c0459eCobalt Strike902d29871d3716113ca2af5caa6745cb4ab9d0614595325c1107fb83c1494483
fce9de0a0acf2ba65e9e252a383d37b2984488b6a97d889ec43ab742160acce1Cobalt Strike stager40cac28490cddfa613fd58d1ecc8e676d9263a46a0ac6ae43bcbdfedc525b8ee
ffb255e7a2aa48b96dd3430a5177d6f7f24121cc0097301f2e91f7e02c37e6bfCobalt Strike5af6626a6bc7265c21adaffb23cc58bc52c4ebfe5bf816f77711d3bc7661c3d6
1a50c358fa4b725c6e0e26eee3646de26ba38e951f3fe414f4bf73532af62455Cobalt Strike8f1cc6ab8e95b9bfdf22a2bde77392e706b6fb7d3c1a3711dbc7ccd420400953
1be3397c2a85b4b9a5a111b9a4e53d382df47a0a09065639d9e66e0b55fe36fcCobalt Strike stager3f28a055d56f46559a21a2b0db918194324a135d7e9c44b90af5209a2d2fd549
1d058302d1e747714cac899d0150dcc35bea54cc6e995915284c3a64a76aacb1Putty0581160998be30f79bd9a0925a01b0ebc4cb94265dfa7f8da1e2839bf0f1e426
02b1bd89e9190ff5edfa998944fd6048d32a3bde3a72d413e8af538d9ad770b4Cobalt Strike obfuscated shellcode3760db55a6943f4216f14310ab10d404e5c0a53b966dd634b76dd669f59d2507
2cf125d6f21c657f8c3732be435af56ccbe24d3f6a773b15eccd3632ea509b1aPutty0581160998be30f79bd9a0925a01b0ebc4cb94265dfa7f8da1e2839bf0f1e426
2f2e62c9481ba738a5da7baadfc6d029ef57bf7a627c2ac0b3e615cab5b0cfa2Cobalt Strike39ed516d8f9d9253e590bad7c5daecce9df21f1341fb7df95d7caa31779ea40f
3bc8ce92409876526ad6f48df44de3bd1e24a756177a07d72368e2d8b223bb39Cobalt Strike20e43f60a29bab142f050fab8c5671a0709ee4ed90a6279a80dd850e6f071464
3dffb7f05788d981efb12013d7fadf74fdf8f39fa74f04f72be482847c470a53Cobalt Strike8e78ad0ef549f38147c6444910395b053c533ac8fac8cdaa00056ad60b2a0848
3f6e3e7747e0b1815eb2a46d79ebd8e3cb9ccdc7032d52274bc0e60642e9b31ePutty0581160998be30f79bd9a0925a01b0ebc4cb94265dfa7f8da1e2839bf0f1e426
3fff407bc45b879a1770643e09bb99f67cdcfe0e4f7f158a4e6df02299bac27eTest application43308bde79e71b2ed14f318374a80fadf201cc3e34a887716708635294031b1b
4b3cd3aa5b961791a443b89e281de1b05bc3a9346036ec0da99b856ae7dc53a8Putty0581160998be30f79bd9a0925a01b0ebc4cb94265dfa7f8da1e2839bf0f1e426
4faf362b3fe403975938e27195959871523689d0bf7fba757ddfa7d00d437fd4Cobalt Strike60905c92501ec55883afc3f6402a05bddfd335323fdc0144515f01e8da0acbda
5d72cc2e47d3fd781b3fc4e817b2d28911cd6f399d4780a5ff9c06c23069eae1MythicPacker9a08d2db7d0bd7d4251533551d4def0f5ee52e67dff13a2924191c8258573024
5ea74bca527f7f6ea8394d9d78e085bed065516eca0151a54474fffe91664198Cobalt Strikebe314279f817f9f000a191efb8bcc2962fcc614b1f93c73dda46755269de404f
5fc79a4499bafa3a881778ef51ce29ef015ee58a587e3614702e69da304395dbBlisterMythic3d2499e5c9b46f1f144cfbbd4a2c8ca50a3c109496a936550cbb463edf08cd79
06cd6391b5fcf529168dc851f27bf3626f20e038a9c0193a60b406ad1ece6958Test application43308bde79e71b2ed14f318374a80fadf201cc3e34a887716708635294031b1b
6a7ae217394047c17d56ec77b2243d9b55617a1ff591d2c2dfc01f2da335cbbfMythicPacker1e3b373f2438f1cc37e15fdede581bdf2f7fc22068534c89cb4e0c128d0e45dd
6e75a9266e6bbfd194693daf468dd86d106817706c57b1aad95d7720ac1e19e3Cobalt Strike4adf3875a3d8dd3ac4f8be9c83aaa7e3e35a8d664def68bc41fc539bfedfd33f
7e61498ec5f0780e0e37289c628001e76be88f647cad7a399759b6135be8210aTest application43308bde79e71b2ed14f318374a80fadf201cc3e34a887716708635294031b1b
7f7b9f40eea29cfefc7f02aa825a93c3c6f973442da68caf21a3caae92464127Putty0581160998be30f79bd9a0925a01b0ebc4cb94265dfa7f8da1e2839bf0f1e426
8b6eb2853ae9e5faff4afb08377525c9348571e01a0e50261c7557d662b158e1Test application43308bde79e71b2ed14f318374a80fadf201cc3e34a887716708635294031b1b
8d53dc0857fa634414f84ad06d18092dedeb110689a08426f08cb1894c2212d4Putty0581160998be30f79bd9a0925a01b0ebc4cb94265dfa7f8da1e2839bf0f1e426
8e6c0d338f201630b5c5ba4f1757e931bc065c49559c514658b4c2090a23e57bCobalt Strikef2329ae2eb28bba301f132e5923282b74aa7a98693f44425789b18a447a33bff
8f9289915b3c6f8bf9a71d0a2d5aeb79ff024c108c2a8152e3e375076f3599d5BlisterMythicf89cfbc1d984d01c57dd1c3e8c92c7debc2beb5a2a43c1df028269a843525a38
9c5c9d35b7c2c448a610a739ff7b85139ea1ef39ecd9f51412892cd06fde4b1bTest application43308bde79e71b2ed14f318374a80fadf201cc3e34a887716708635294031b1b
13c7f28044fdb1db2289036129b58326f294e76e011607ca8d4c5adc2ddddb16Cobalt Strike19e7bb5fa5262987d9903f388c4875ff2a376581e4c28dbf5ae7d128676b7065
19b0db9a9a08ee113d667d924992a29cd31c05f89582953eff5a52ad8f533f4bTest application43308bde79e71b2ed14f318374a80fadf201cc3e34a887716708635294031b1b
19d4a7d08176119721b9a302c6942718118acb38dc1b52a132d9cead63b11210Test application43308bde79e71b2ed14f318374a80fadf201cc3e34a887716708635294031b1b
22e65a613e4520a6f824a69b795c9f36af02247f644e50014320857e32383209Cobalt Strike18a9eafb936bf1d527bd4f0bfae623400d63671bafd0aad0f72bfb59beb44d5f
028da30664cb9f1baba47fdaf2d12d991dcf80514f5549fa51c38e62016c1710Cobalt Strike8e78ad0ef549f38147c6444910395b053c533ac8fac8cdaa00056ad60b2a0848
37b6fce45f6bb52041832eaf9c6d02cbc33a3ef2ca504adb88e19107d2a7aeaaCobalt Strike902d29871d3716113ca2af5caa6745cb4ab9d0614595325c1107fb83c1494483
42beac1265e0efc220ed63526f5b475c70621573920968a457e87625d66973afTest application43308bde79e71b2ed14f318374a80fadf201cc3e34a887716708635294031b1b
43c1ee0925ecd533e0b108c82b08a3819b371182e93910a0322617a8acf26646Cobalt Strike5cb2683953b20f34ff26ddc0d3442d07b4cd863f29ec3a208cbed0dc760abd04
44ce7403ca0c1299d67258161b1b700d3fa13dd68fbb6db7565104bba21e97aeMythicPackerf3b0357562e51311648684d381a23fa2c1d0900c32f5c4b03f4ad68f06e2adc1
49ba10b4264a68605d0b9ea7891b7078aeef4fa0a7b7831f2df6b600aae77776Cobalt Strike0603cf8f5343723892f08e990ae2de8649fcb4f2fd4ef3a456ef9519b545ed9e
54c7c153423250c8650efc0d610a12df683b2504e1a7a339dfd189eda25c98d4Test application43308bde79e71b2ed14f318374a80fadf201cc3e34a887716708635294031b1b
58fdee05cb962a13c5105476e8000c873061874aadbc5998887f0633c880296aTest application43308bde79e71b2ed14f318374a80fadf201cc3e34a887716708635294031b1b
73baa040cd6879d1d83c5afab29f61c3734136bffe03c72f520e025385f4e9a2Cobalt Strike17392d830935cfad96009107e8b034f952fb528f226a9428718669397bafd987
78d93b13efd0caa66f5d91455028928c3b1f44d0f2222d9701685080e30e317dPutty0581160998be30f79bd9a0925a01b0ebc4cb94265dfa7f8da1e2839bf0f1e426
83c121db96d99f0d99b9e7a2384386f3f6debcb01d977c4ddca5bcdf2c6a2daaCobalt Strike stager39323f9c0031250414cb4683662e1c533960dea8a54d7a700f77c6133a59c783
84b245fce9e936f1d0e15d9fca8a1e4df47c983111de66fcc0ad012a63478c8dCobalt Strike stagerd961e9db4a96c87226dbc973658a14082324e95a4b30d4aae456a8abe38f5233
84b2d16124b690d77c5c43c3a0d4ad78aaf10d38f88d9851de45d6073d8fcb65Cobalt Strike0091186459998ad5b699fdd54d57b1741af73838841c849c47f86601776b0b33
85d3f81a362a3df9ba2f0a00dd12cd654e55692feffc58782be44f4c531d9bb9Putty0581160998be30f79bd9a0925a01b0ebc4cb94265dfa7f8da1e2839bf0f1e426
96e8b44ec061c49661bd192f279f7b7ba394d03495a2b46d3b37dcae0f4892f1Cobalt Strike stager6f7d7da247cac20d5978f1257fdd420679d0ce18fd8738bde02246129f93841b
96ebacf48656b804aed9979c2c4b651bbb1bc19878b56bdf76954d6eff8ad7caCobalt Striked988a867a53c327099a4c9732a1e4ced6fe6eca5dd68f67e5f562ab822b8374b
113c9e7760da82261d77426d9c41bc108866c45947111dbae5cd3093d69e0f1dPutty0581160998be30f79bd9a0925a01b0ebc4cb94265dfa7f8da1e2839bf0f1e426
149c3d044abc3c3a15ba1bb55db7e05cbf87008bd3d23d7dd4a3e31fcfd7af10Cobalt Strikee63807daa9be0228d90135ee707ddf03b0035313a88a78e50342807c27658ff2
307fc7ebde82f660950101ea7b57782209545af593d2c1115c89f328de917dbbCobalt Strike stager40cac28490cddfa613fd58d1ecc8e676d9263a46a0ac6ae43bcbdfedc525b8ee
356efe6b10911d7daaffed64278ba713ab51f7130d1c15f3ca86d17d65849fa5Test application43308bde79e71b2ed14f318374a80fadf201cc3e34a887716708635294031b1b
394ce0385276acc6f6c173a3dde6694881130278bfb646be94234cc7798fd9a9Cobalt Strike60e2fe4eb433d3f6d590e75b2a767755146aca7a9ba6fd387f336ccb3c5391f8
396dce335b16111089a07ecb2d69827f258420685c2d9f3ea9e1deee4bff9561Test application43308bde79e71b2ed14f318374a80fadf201cc3e34a887716708635294031b1b
541eab9e348c40d510db914387068c6bfdf46a6ff84364fe63f6e114af8d79cfCobalt Strike stager4e2a011922e0060f995bfde375d75060bed00175dc291653445357b29d1afc38
745a3dcdda16b93fedac8d7eefd1df32a7255665b8e3ee71e1869dd5cd14d61cCobalt Strike obfuscated shellcodecef1a88dfc436dab9ae104f0770a434891bbd609e64df43179b42b03a7e8f908
753f77134578d4b941b8d832e93314a71594551931270570140805675c6e9ad3Putty0581160998be30f79bd9a0925a01b0ebc4cb94265dfa7f8da1e2839bf0f1e426
863de84a39c9f741d8103db83b076695d0d10a7384e4e3ba319c05a6018d9737Cobalt Strike3a1e65d7e9c3c23c41cb1b7d1117be4355bebf0531c7473a77f957d99e6ad1d4
902fa7049e255d5c40081f2aa168ac7b36b56041612150c3a5d2b6df707a3cffCobalt Strike397c08f5cdc59085a48541c89d23a8880d41552031955c4ba38ff62e57cfd803
927e04371fa8b8d8a1de58533053c305bb73a8df8765132a932efd579011c375Cobalt Strike2e0767958435dd4d218ba0bc99041cc9f12c9430a09bb1222ac9d1b7922c2632
2043d7f2e000502f69977b334e81f307e2fda742bbc5b38745f6c1841757fddcTest application43308bde79e71b2ed14f318374a80fadf201cc3e34a887716708635294031b1b
02239cac2ff37e7f822fd4ee57ac909c9f541a93c27709e9728fef2000453afeCobalt Strike18a9eafb936bf1d527bd4f0bfae623400d63671bafd0aad0f72bfb59beb44d5f
4257bf17d15358c2f22e664b6112437b0c2304332ff0808095f1f47cf29fc1a2Cobalt Strike3a1e65d7e9c3c23c41cb1b7d1117be4355bebf0531c7473a77f957d99e6ad1d4
6558ac814046ecf3da8c69affea28ce93524f93488518d847e4f03b9327acb44Test application43308bde79e71b2ed14f318374a80fadf201cc3e34a887716708635294031b1b
8450ed10b4bef6f906ff45c66d1a4a74358d3ae857d3647e139fdaf0e3648c10BlisterMythicab7cab5192f0bef148670338136b0d3affe8ae0845e0590228929aef70cb9b8b
9120f929938cd629471c7714c75d75d30daae1f2e9135239ea5619d77574c1feCobalt Strike647e992e24e18c14099b68083e9b04575164ed2b4f5069f33ff55f84ee97fff0
28561f309d208e885a325c974a90b86741484ba5e466d59f01f660bed1693689Cobalt Strike397c08f5cdc59085a48541c89d23a8880d41552031955c4ba38ff62e57cfd803
30628bcb1db7252bf710c1d37f9718ac37a8e2081a2980bead4f21336d2444bcCobalt Strike obfuscated shellcode13f23b5db4a3d0331c438ca7d516d565a08cac83ae515a51a7ab4e6e76b051b1
53121c9c5164d8680ae1b88d95018a553dff871d7b4d6e06bd69cbac047fe00fCobalt Strike902d29871d3716113ca2af5caa6745cb4ab9d0614595325c1107fb83c1494483
67136ab70c5e604c6817105b62b2ee8f8c5199a647242c0ddbf261064bb3ced3Cobalt Strike obfuscated shellcode0aecd621b386126459b39518f157ee240866c6db1885780470d30a0ebf298e16
79982f39ea0c13eeb93734b12f395090db2b65851968652cab5f6b0827b49005MythicPacker152455f9d970f900eb237e1fc2c29ac4c72616485b04e07c7e733b95b6afc4d8
87269a95b1c0e724a1bfe87ddcb181eac402591581ee2d9b0f56dedbaac04ff8Cobalt Strikef3d42e4c1a47f0e1d3812d5f912487d04662152c17c7aa63e836bef01a1a4866
89196b39a0edebdf2026053cb4e87d703b9942487196ff9054ef775fdcad1899Test application43308bde79e71b2ed14f318374a80fadf201cc3e34a887716708635294031b1b
91446c6d3c11074e6ff0ff42df825f9ffd5f852c2e6532d4b9d8de340fa32fb8Test application43308bde79e71b2ed14f318374a80fadf201cc3e34a887716708635294031b1b
96823bb6befe5899739bd69ab00a6b4ae1256fd586159968301a4a69d675a5ecCobalt Strike3b3bdd819f4ee8daa61f07fc9197b2b39d0434206be757679c993b11acc8d05f
315217b860ab46c6205b36e49dfaa927545b90037373279723c3dec165dfaf11Cobalt Strike96fab57ef06b433f14743da96a5b874e96d8c977b758abeeb0596f2e1222b182
427481ab85a0c4e03d1431a417ceab66919c3e704d7e017b355d8d64be2ccf41Putty0581160998be30f79bd9a0925a01b0ebc4cb94265dfa7f8da1e2839bf0f1e426
595153eb56030c0e466cda0becb1dc9560e38601c1e0803c46e7dfc53d1d2892Cobalt Strikef245b2bc118c3c20ed96c8a9fd0a7b659364f9e8e2ee681f5683681e93c4d78b
812263ea9c6c44ef6b4d3950c5a316f765b62404391ddb6482bdc9a23d6cc4a6Cobalt Strike18a9eafb936bf1d527bd4f0bfae623400d63671bafd0aad0f72bfb59beb44d5f
1358156c01b035f474ed12408a9e6a77fe01af8df70c08995393cbb7d1e1f8a6Cobalt Strikeb916749963bb08b15de7c302521fd0ffec1c6660ba616628997475ae944e86a3
73162738fb3b9cdd3414609d3fe930184cdd3223d9c0d7cb56e4635eb4b2ab67Cobalt Strike19e7bb5fa5262987d9903f388c4875ff2a376581e4c28dbf5ae7d128676b7065
343728792ed1e40173f1e9c5f3af894feacd470a9cdc72e4f62c0dc9cbf63fc1Putty0581160998be30f79bd9a0925a01b0ebc4cb94265dfa7f8da1e2839bf0f1e426
384408659efa1f87801aa494d912047c26259cd29b08de990058e6b45619d91aCobalt Strike stager824914bb34ca55a10f902d4ad2ec931980f5607efcb3ea1e86847689e2957210
49925637250438b05d3aebaac70bb180a0825ec4272fbe74c6fecb5e085bcf10Cobalt Strikee0c0491e45dda838f4ac01b731dd39cc7064675a6e1b79b184fff99cdce52f54
Table 6, Hashes of Blister samples and of the payload it drops, including the payload label.

Approximately 2000 Citrix NetScalers backdoored in mass-exploitation campaign

Fox-IT (part of NCC Group) has uncovered a large-scale exploitation campaign of Citrix NetScalers in a joint effort with the Dutch Institute of Vulnerability Disclosure (DIVD). An adversary appears to have exploited CVE-2023-3519 in an automated fashion, placing webshells on vulnerable NetScalers to gain persistent access. The adversary can execute arbitrary commands with this webshell, even when a NetScaler is patched and/or rebooted. At the time of writing, more than 1900 NetScalers remain backdoored. Using the data supplied by Fox-IT, the Dutch Institute of Vulnerability Disclosure has notified victims.

Figure 1: A global overview of known-compromised Netscalers located in each country, as of August 14th 2023

Main Takeaways

  • A set of vulnerabilities in NetScaler, one of which allows for remote code execution, were disclosed on July 18th. This disclosure was published after several security organisations saw limited exploitation of these vulnerabilities in the wild.
  • Fox-IT (in collaboration with the Dutch Institute of Vulnerability Disclosure) have scanned for these webshells to identify compromised systems. Responsible disclosure notifications have been sent by the DIVD.
  • At the time of this exploitation campaign, 31127 NetScalers were vulnerable to CVE-2023-3519.
  • As of August 14th, 1828 NetScalers remain backdoored.
  • Of the backdoored NetScalers, 1248 are patched for CVE-2023-3519.

Recommendations for NetScaler Administrators

  • A patched NetScaler can still contain a backdoor. It is recommended to perform an Indicator of Compromise check on your NetScalers, regardless of when the patch was applied.
    • Fox-IT has provided a Python script that utilizes Dissect to perform triage on forensic images of NetScalers.
    • Mandiant has provided a bash-script to check for Indicators of Compromise on live systems. Be aware that if this script is run twice, it will yield false positive results as certain searches get written into the NetScaler logs whenever the script is run.
  • If traces of compromise are discovered, secure forensic data; It is strongly recommended to make a forensic copy of both the disk and the memory of the appliance before any remediation or investigative actions are done. If the Citrix appliance is installed on a hypervisor, a snapshot can be made for follow-up investigation.
  • If a webshell is found, investigate whether it has been used to perform activities. Usage of the webshell should be visible in the NetScaler access logs. If there are indications that the webshell has been used to perform unauthorised activities, it is essential to perform a larger investigation, to identify whether the adversary has successfully taken steps to move laterally from the NetScaler, towards another system in your infrastructure.

Investigation and Disclosure Timeline

July 2023: Identifying & disclosing NetScalers vulnerable to CVE-2023-3519

Recently, three vulnerabilities were reported to be present in Citrix ADC and Citrix Gateway. Based on the information shared by Citrix, one of these vulnerabilities (CVE-2023-3519) gives an attacker the opportunity to perform unauthenticated remote code execution. Citrix, and various other organisations, also shared information regarding the fact that this vulnerability is actively being exploited in the wild.

At the time that Citrix disclosed information about CVE-2023-3519, details on how this vulnerability could be exploited were not publicly known. Using prior research on the identification of Citrix versions, we were able to quickly identify which Citrix servers on the web were vulnerable for CVE-2023-3519. This information was shared with the Dutch Institute of Vulnerability Disclosure (DIVD), who were able to notify administrators that they had vulnerable NetScalers exposed to the internet.

About the Dutch Institute of Vulnerability Disclosure (DIVD):

DIVD is a Dutch research institute that works with volunteers who aim to make the digital world safer by searching the internet for vulnerabilities and reporting the findings to those who can fix these vulnerabilities.

https://www.divd.nl/code/

In parallel with sharing the data with the DIVD, Fox-IT and NCC Group cross-referenced their scan data with their customer base to inform managed services customers shortly prior to the DIVD disclosure.

August 8th and 9th 2023: Identifying backdoored NetScalers

In July and August, the Fox-IT CERT (part of NCC Group) responded to several incidents related to CVE-2023-3519. Several webshells were found during these investigations. Based on both the findings of these IR engagements as well as Shadowserver’s Technical Summary of Observed Citrix CVE-2023-3519 Incidents, we were confident that the adversary had exploited at a large scale in an automated fashion.

While the discovered webshells return a 404 Not Found, the response still differs from how Citrix servers ordinarily respond to a request for a file that does not exist. Moreover, the webshell will not execute any commands on the target machine unless given proper parameters. These two factors combined allow us to scan the internet for webshells with high confidence, without impacting affected NetScalers.

In cooperation with the DIVD we decided to scan NetScalers accessible on the internet for known webshell paths. These scans may be recognized in Citrix HTTP Access logs by the User-Agent: DIVD-2023-00033. We initially only scanned systems that were not patched on July 21st, as the exploitation was believed to be between July 20th and July 21st. Later, we decided to also scan the systems that were already patched on July 21st. The results exceeded our expectations. Based on the internet wide scan, approximately 2000 unique IP addresses seem to have been backdoored with a webshell as of August 9th.

August 10th: Responsible Disclosure by the DIVD

Starting from August 10th, the DIVD has begun reaching out to organisations affected by the webshell. They used their already existing network and responsible disclosure methods to notify network owners and national CERTs. It however remains possible that this notification doesn’t reach the right people in time. We would therefore like to repeat the advice to manually perform an IOC check on your internet exposed NetScaler devices.

Findings

Most apparent from our scanning results is the percentage of patched NetScalers that still contain a backdoor. At the time of writing, approximately 69% of the NetScalers that contain a backdoor are not vulnerable anymore to CVE-2023-3519. This indicates that while most administrators were aware of the vulnerability and have since patched their NetScalers to a non-vulnerable version, they have not been (properly) checked for signs of successful exploitation.

Figure 2: The large majority of known compromised NetScalers are patched.

Thus, administrators may currently have a false sense of security even though an up to date Netscaler can still have been backdoored. The high percentage of patched NetScalers that have been backdoored is likely a result of the time at which mass exploitation took place. From incident response cases, we can confirm Shadowserver’s prior estimate that this specific exploitation campaign took place between late July 20th and early July 21st:

Figure 3: While patches were being applied, exploitation took place at a large scale between July 20th and July 21st.

We could not discern a pattern in the targeting of NetScalers. We have seen some systems that have been compromised with multiple webshells, but we also see large volumes of NetScalers that were vulnerable between July 20th and July 21st have not been compromised with a backdoor. In total we have found 2491 webshells across 1952 distinct NetScalers. Globally, there were 31127 NetScalers vulnerable to CVE-2023-3519 on July 21st, meaning that the exploitation campaign compromised 6.3% of all vulnerable NetScalers globally.

Figure 4: Amount of Compromised NetScalers per country as of August 14th 2023
Figure 5: Amount of vulnerable NetScalers per country as of July 21st 2023

It appears the majority of compromised NetScalers reside in Europe. Of the top 10 affected countries, only 2 are located outside of Europe. There are stark differences between countries in terms of what percentage of their NetScalers were compromised. For example, while Canada, Russia and the United States of America all had thousands of vulnerable NetScalers on July 21st, virtually none of these NetScalers were found to have a webshell on them. As of now, we have no clear explanation for these differences, nor do we have a confident hypothesis to explain which NetScalers were targeted by the adversary and which ones were not. Moreover, we do not see a particular targeting in terms of victim industry.

As of August 14th, 1828 NetScalers remain compromised. While we see a decline in the amount of compromised NetScalers following the disclosure on August 10th, we hope that this publication can raise further awareness that backdoors can persist even when Citrix servers are updated. Therefore, we again recommend any NetScaler administrator to perform basic triage on their NetScalers.

Conclusion

The monitoring and protection of edge devices such as NetScalers remains challenging. Sometimes, the window in which defenders must patch their systems is incredibly small. CVE-2023-3519 was exploited in targeted attacks before a patch was available and was later exploited on a large scale. System administrators need to be aware that adversaries can exploit edge devices to place backdoors that persist even after updates and / or reboots. As of now, it is strongly advised to check NetScalers, even if they have been patched and updated to the latest version. Resources are available at the Fox-IT GitHub.

References

CVE-2022-27510, CVE-2022-27518 – Measuring Citrix ADC & Gateway version adoption on the Internet

Authored by Yun Zheng Hu

Recently, two critical vulnerabilities were reported in Citrix ADC and Citrix Gateway; where one of them was being exploited in the wild by a threat actor. Due to these vulnerabilities being exploitable remotely and given the situation of past Citrix vulnerabilities, RIFT started to research on how to identify the exact version of Citrix ADC and Gateway servers on the internet so that we could inform customers if they hadn’t patched yet.

The exact version information is helpful to determine whether a server is still vulnerable to particular vulnerabilities. We also used version information to derive version statistics and version adoption over time.

In the first part of this blog, we go into how we acquired and analysed Citrix ADC disk images for version identification. Then we go into how we found a way to determine the version build date and how we used the build dates to download missing Citrix ADC images to aid our version identification research. The last part goes into the version statistics of Citrix ADC servers on the internet and how we used this to measure the version adoption on the internet.

Skip to the end if you are interested in the statistics rather than the technical details of Citrix ADC version identification.

CVE-2022-27510 – Unauthorized access to Gateway user capabilities

On November 8th 2022, Citrix published a security bulletin for CVE-2022-27510, a critical authentication bypass vulnerability affecting Citrix ADC (formerly known as NetScaler) and Citrix Gateway. For this to be exploitable, the server must be configured as a Gateway (SSL VPN, ICA Proxy, CVPN, RDP Proxy).

CVE-2022-27518 – Unauthenticated remote arbitrary code execution

Less than a month later, on December 13th 2022, the National Security Agency (NSA) released a Cybersecurity Advisory that APT5 is actively exploiting Citrix ADC servers. However this advisory does not mention a specific CVE that is being abused but most likely a new vulnerability as on the same day Citrix published a blog with guidance and a new security bulletin detailing CVE-2022-27518, which is a new vulnerability and not to be confused with CVE-2022-27510. For this to be exploitable, the Citrix ADC or Gateway server must be configured as a SAML Service Provider or SAML Identity Provider.

Finding Citrix ADC & Gateway Servers on the Internet

Citrix ADC and Gateway servers are commonly internet-facing due to the nature of the appliance. For example, services like Shodan and Censys regularly scan the internet and identify these servers. Using this information, we can build a list of Citrix ADC & Citrix Gateway servers with the SSL VPN / Gateway service exposed to the internet. We used this to create an initial list of servers, and we found around 28.000 servers on the internet as of November 11th 2022.

Version identification

Sadly, the exact version information is not available in the HTTP response of a Citrix ADC or Gateway server. However, we noticed that there is an MD5 hash-like value in the HTTP body when requesting the /vpn/index.html URL:

version hash appended to HTML resources

Here we see the parameter ?v=6e7b2de88609868eeda0b1baf1d34a7e appended to several resource URLs. We extracted these hashes from Censys scan data to create a list of the most common version hashes. We found around 100 unique version hashes.

To see if we can map the version hash to exact versions, we begin by first spinning up our own Citrix ADC server to start exploring wether this version hash in this HTML page is static or generated.

Cloud Marketplace

More server appliances become available in the cloud, and Citrix ADC is no exception. You can easily find it in your favourite cloud marketplace. So gone are the days of the time-consuming process of downloading images and spinning up a VM to install the application; we can do this directly in the cloud! We used the Google Cloud Marketplace, but it’s also available on AWS and Azure.

After deploying Citrix ADC with a single click from the Cloud Marketplace, we log in to the shell and explore the file system to see if we can find the index.html page and if it contains a hash value.

SSH into the VM and show the version, looks like we are on version 13.1 build 33.47:

yun@cloudshell:~ (rift-citrix-362712)$ gcloud ssh citrix-adc-vpx-instance
###############################################################################
#                                                                             #
#        WARNING: Access to this system is for authorized users only          #
#         Disconnect IMMEDIATELY if you are not an authorized user!           #
#                                                                             #
###############################################################################

 Done
> show version
        NetScaler NS13.1: Build 33.47.nc, Date: Sep 23 2022, 13:12:49   (64-bit)
 Done

Type shell to enter the shell, and let’s find all index.html files:

> shell
root@ns# uname -a
FreeBSD ns 11.4-NETSCALER-13.1 FreeBSD 11.4-NETSCALER-13.1 #0 e5f9d90507ab(heads/artesa_33_47)-dirty: Fri Sep 23 13:13:05 PDT 2022     root@sjc-bld-bsd114-228:/usr/obj/usr/home/build/adc/usr.src/sys/NS64  amd64

root@ns# find / -name 'index.html'
/netscaler/ns_gui/admin_ui/gui_v2/swagger_ui/index.html
/netscaler/ns_gui/vpn/index.html
/var/netscaler/gui/vpn/index.html
/var/netscaler/gui/admin_ui/gui_v2/swagger_ui/index.html
/var/netscaler/logon/LogonPoint/index.html
/var/python/lib/python3.7/site-packages/djangorestframework-3.11.0-py3.7.egg/rest_framework/templates/rest_framework/docs/index.html
/var/python/lib/python3.7/site-packages/Django-3.0.5-py3.7.egg/django/contrib/admin/templates/admin/index.html
/var/python/lib/python3.7/site-packages/Django-3.0.5-py3.7.egg/django/contrib/admindocs/templates/admin_doc/index.html

Looks like there are several directories with index.html, let’s check /vpn/index.html:

root@ns# head /netscaler/ns_gui/vpn/index.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XDEV_HTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Citrix Gateway</title>
<link rel="SHORTCUT ICON" href="/vpn/images/AccessGateway.ico" type="image/vnd.microsoft.icon">
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<META content=noindex,nofollow,noarchive name=robots>
<link href="/vpn/js/rdx/core/css/rdx.css?v=7afe87a42140b566a2115d1e232fdc07" rel="stylesheet" type="text/css"/>
<link href="/logon/themes/Default/css/base.css?v=7afe87a42140b566a2115d1e232fdc07" rel="stylesheet" type="text/css" media="screen" />

Bingo! The index.html file contains the hash 7afe87a42140b566a2115d1e232fdc07, and if we search for this value on Censys, we get multiple results which is promising. So we can assume that version 13.1-33.47 maps to this hash value.

Let’s check which other files contain this hash value for good measure:

root@ns# grep -r 7afe87a42140b566a2115d1e232fdc07 /var/netscaler | cut -d: -f1 | sort -u
/var/netscaler/gui/epa/epa.html
/var/netscaler/gui/epa/errorpage.html
/var/netscaler/gui/epa/posterrorpage.html
/var/netscaler/gui/vpn/index.html
/var/netscaler/gui/vpn/loading.html
/var/netscaler/gui/vpn/logout.html
/var/netscaler/gui/vpn/tmindex.html
/var/netscaler/gui/vpn/tmlogout.html
/var/netscaler/gui/vpns/choices.html
/var/netscaler/gui/vpns/f_ndisagent.html
/var/netscaler/gui/vpns/f_services1.html
/var/netscaler/gui/vpns/f_services_linux.html
/var/netscaler/gui/vpns/j_services.html
/var/netscaler/gui/vpns/m_services.html
/var/netscaler/gui/vpns/navui/refresh.html
/var/netscaler/gui/vpns/nohomepage.html
/var/netscaler/gui/vpns/postepa.html

When installing the application from the Google Cloud Marketplace, it does not give the option to choose a version to install. But of course, we want to install other versions to start building a list of known version hashes.

We noticed that a deployment.zip file can be downloaded from the Google Cloud Marketplace:

deployment.zip can be downloaded

After unpacking deployment.zip we see that this contains Terraform scripts to deploy the cloud application and references the disk image it uses for installation. Luckily, Citrix also left a list in a Jinja template of other disk image names referencing different Citrix ADC versions.

cloud image names of other Citrix ADC versions in a Jinja template

Can these disk images be downloaded? Yes, it turns out you can!

Acquiring Cloud images

We used the following gcloud commands to download Citrix ADC disk images:

  • gcloud compute images create, to download the image in our own Google Cloud project. This allows you to choose this image when you create a new VM. However, this does not make the image directly accessible for reading using other tools, for that we need to export it first.
  • gcloud compute images export, this exports the given image to a Google Cloud Storage (GCS) bucket using a different format such as qcow2 or vmdk.

The raw disk image is 20 GB but exporting it as qcow2 format we can reduce this to only 2 GB.

The following shell script does both steps in one script:

#!/bin/sh
#
# Export a Citrix ADC image to a Google Cloud Storage bucket
#
# Usage:
#   ./export-citrix-image.sh <image name> <bucket_path>
# Example:
#   ./export-citrix-image.sh citrix-adc-vpx-10-standard-13-0-85-19 gs://my-bucket
image="$1"
gcs_bucket="$2"

gcloud compute images create "$image" --source-image https://www.googleapis.com/compute/v1/projects/citrix-master-project/global/images/$image --verbosity debug
gcloud compute images export --destination-uri "$gcs_bucket/$image.qcow2"  --image "$image" --export-format qcow2

Now that we exported some known images from deployment.zip to a GCS bucket, we can process them in bulk. Of course, having them archived and preserved can also be helpful for future research, especially for vulnerable versions, as they tend to get deleted.

This technique can also be used for other appliances in the Google Cloud Marketplace.

Dissect to the rescue!

What better way to process disk images in bulk than dogfooding our own opensource dissect framework. While Citrix ADC runs on FreeBSD, dissect can read its UFS/FFS file systems just fine. After we fixed a few bugs that is.

We can also do everything in a Cloud Shell, without spinning up an extra VM.

Let’s mount our GCS bucket containing the qcow2 images first using gcsfuse:

yun@cloudshell:~ (rift-citrix-362712)$ mkdir bucket

yun@cloudshell:~ (rift-citrix-362712)$ gcsfuse my-citrix-adc-bucket bucket
2022/12/11 15:48:25.442402 Start gcsfuse/0.41.9 (Go version go1.18.4) for app "" using mount point: /home/yun/bucket
2022/12/11 15:48:25.462744 Opening GCS connection...
2022/12/11 15:48:25.557883 Mounting file system "my-citrix-adc-bucket"...
2022/12/11 15:48:25.563799 File system has been successfully mounted.

yun@cloudshell:~ (rift-citrix-362712)$ ls -1 bucket/citrix*.qcow2
citrix-adc-vpx-10-standard-13-0-83-27.qcow2
citrix-adc-vpx-10-standard-13-0-87-9.qcow2
citrix-adc-vpx-10-standard-13-0-88-14.qcow2
citrix-adc-vpx-10-standard-13-1-21-50.qcow2
citrix-adc-vpx-10-standard-13-1-33-52.qcow2
citrix-adc-vpx-byol-13-0-76-31.qcow2
citrix-adc-vpx-byol-13-0-79-64.qcow2
citrix-adc-vpx-byol-13-0-82-45.qcow2
citrix-adc-vpx-byol-13-0-88-16.qcow2
citrix-adc-vpx-byol-13-1-33-49.qcow2
citrix-adc-vpx-byol-13-1-33-52.qcow2
citrix-adc-vpx-byol-13-1-33-54.qcow2
citrix-adc-vpx-byol-13-1-37-38.qcow2
citrix-adc-vpx-express-13-0-83-29.qcow2
...

Now we install the latest version of dissect using pip3 in a virtualenv:

yun@cloudshell:~ (rift-citrix-362712)$ python3 -mvenv dissect

yun@cloudshell:~ (rift-citrix-362712)$ source dissect/bin/activate
(dissect) yun@cloudshell:~ (rift-citrix-362712)$ pip3 install --pre dissect

Dissect will install different command line tools. One of these tools is called target-shell, which can read disk images and file systems in various formats and gives you a shell-like interface to explore the file system.

Now let’s open a disk image using target-shell, we specify the -q flag to hide some warnings:

target-shell in a Cloud Shell


Dissect does not (yet!) support Citrix ADC, so instead it gives us access to the discovered filesystems. We see two partitions, and we found the first one is the /boot partition and second one is the /var partition. target-shell has a basic find command, but the output can be piped to an external tool such as grep:

citrix-adc-vpx-10-standard-13-0-83-27.qcow2 /> cd fs1

citrix-adc-vpx-10-standard-13-0-83-27.qcow2 /fs1> find . | grep index.html
/fs1/netscaler/gui/vpn/index.html
/fs1/netscaler/gui/vpn/tmindex.html
/fs1/netscaler/gui/admin_ui/gui_v2/swagger_ui/index.html
^C

Let’s cat the first index.html file (grep is used to limit the output for this example):

citrix-adc-vpx-10-standard-13-0-83-27.qcow2 /fs1> cat /fs1/netscaler/gui/vpn/index.html | grep ?v= | head -n1
<link href="/vpn/js/rdx/core/css/rdx.css?v=c9e95a96410b8f8d4bde6fa31278900f" rel="stylesheet" type="text/css"/>
citrix-adc-vpx-10-standard-13-0-83-27.qcow2 /fs1>

Nice, we find that for version 13.0-83-27 the version hash is c9e95a96410b8f8d4bde6fa31278900f.

Not everyone realizes this, but with just this one command, we loaded a FreeBSD image in qcow2 disk format containing multiple UFS/FFS2 partitions by using Python and dissect in a Cloud Shell. No hassle of installing additional tools and performing tedious steps to mount images. The future is now!

To even further automate this we can use the dissect Python API or we can make a simple shell oneliner using target-fs, which can execute some basic commands against a disk image:

target-fs oneliner

It’s good to mention that at this point, we also started to investigate if the version hash can be calculated by MD5 summing variants of the version string, but without any luck.

Identifying the build date

After processing our acquired cloud images, we found that we still had version hashes from the internet without a known version, so it seemed that not all versions were listed or available as a cloud image. We did manage to acquire some cloud images by guessing the image name, but this was not enough to fill in the gaps.

We finally resorted to the Citrix downloads page to look for other versions we didn’t have yet. A Citrix account is required but it’s open for registration. However, it seemed that not every version that was released is listed on the Citrix downloads page, especially older builds that were replaced by a new build. We found a GitHub project that scraped Citrix download links that were useful for our research, and we found that these links are still valid (after logging in).

While our known version list kept growing, we still missed certain hashes that were quite common in the dataset. We naturally wanted to know which version that was, and at this point, we found an interesting way to determine the approximate build date of the Citrix ADC server version.

In the disk image we found the gzip-compressed file named rdx_en.json.gz under the vpn subdirectory:

citrix-adc-vpx-10-standard-13-0-83-27.qcow2 /fs1> find . | grep rdx_en.json.gz
/fs1/netscaler/gui/vpn/js/rdx/core/lang/rdx_en.json.gz
/fs1/netscaler/gui/admin_ui/rdx/core/lang/rdx_en.json.gz
^C

citrix-adc-vpx-10-standard-13-0-83-27.qcow2 /> ls -la /fs1/netscaler/gui/vpn/js/rdx/core/lang | grep rdx_en
-rw-r--r-- 1001  513     35 2021-09-27T14:01:20 rdx_en.json.gz

Let’s run the file command on this gzip file:

citrix-adc-vpx-10-standard-13-0-83-27.qcow2 /fs1> file /fs1/netscaler/gui/vpn/js/rdx/core/lang/rdx_en.json.gz
/fs1/netscaler/gui/vpn/js/rdx/core/lang/rdx_en.json.gz: gzip compressed data, was "rdx_en.json", last modified: Mon Sep 27 14:01:20 2021, from Unix

Last modified Mon Sep 27 14:01:20 2021. Cool, we retrieve the timestamp of when this gzip file was created and we found that this timestamp is an accurate depiction of when the version was created/released, pretty neat! This JSON file seems to be used for translations purposes, but in newer versions this file is just an empty gzipped JSON dictionary.

Because this rdx_en.json.gz file resides in the vpn sub-directory, it can also be downloaded remotely by accessing the following URI /vpn/js/rdx/core/lang/rdx_en.json.gz

We now have a list of version hashes, known versions, and approximate build dates. Using the build dates we can now deduce the approximate version of a version hash that we don’t know the version of yet.

For example, the version hash 4434db1ec24dd90750ea176f8eab213c was still missing its version number, and we already processed all the cloud images and available download links from the Citrix download page. But armed with the knowledge of the build date 2022-06-29 13:46:08 of this version hash, we can deduce that this build date falls between the build dates of the known versions 12.1-65.15 and 12.1-65.25. Table for clarity:

build dateversion hashversion
2022-05-22 19:18:31fbdc5fbaed59f858aad0a870ac4a779c12.1-65.15
2022-06-29 13:46:084434db1ec24dd90750ea176f8eab213c??
2022-10-04 16:11:03f063b04477adc652c6dd502ac0c39a7512.1-65.25
Ordering version hashes by to their approximate build dates can help determine missing versions

Ok, the possible versions can be: 12.1-65.16 to 12.1-65.24.

These versions are not listed on the download page but does a version within this range exist and can it still be downloaded? By looking at the Citrix download links of known versions we see that the Download ID looks to be incremental, and that the files have a specific format. For example:

URL format example: https://downloads.citrix.com/[DOWNLOAD_ID]/build-[VERSION]_nc_64.tgz

https://downloads.citrix.com/20651/build-12.1-65.15_nc_64.tgz <-- known url
https://downloads.citrix.com/20???/build-12.1-65.??_nc_64.tgz <-- enumerate this url
https://downloads.citrix.com/21408/build-12.1-65.25_nc_64.tgz <-- known url

Can we enumerate the URL and then download the file? The answer is yes, by using a small Python script to enumerate the download link and version, we find that the following URL returns a 200 OK:

https://downloads.citrix.com/20929/build-12.1-65.17_nc_64.tgz

After downloading build-12.1-65.17_nc_64.tgz, we confirmed that version 12.1-65.17 maps to the version hash 4434db1ec24dd90750ea176f8eab213c. This technique proved to be very useful for filling in most of the gaps in versions in our dataset, with a few missing.

Our compiled list of version hashes, approximate build dates, and versions can be found in this gist: https://gist.github.com/fox-srt/c7eb3cbc6b4bf9bb5a874fa208277e86

List of approximate build date, version hash and version.

Version statistics

Now that we have mapped most of the known version hashes to a version, we can measure how many versions there are active on the internet, and if they are still vulnerable to CVE-2022-27510 or CVE-2022-27518.

The following graph shows the Top 20 active versions on the internet, and also shows if that version is vulnerable to any of the two recent CVEs:

Top 20 Citrix ADC / Gateway versions on the Internet

We see that the majority is on version 13.0-88.14, which is not vulnerable to either of the two CVEs. The runner up is version 12.1-65.21 which is not vulnerable to CVE-2022-27510, but it is to CVE-2022-27518. There are also many servers that do not return a version hash at all so for those servers we cannot identify the exact version.

Note that for CVE-2022-27518 a pre-condition of SAML is required for it to be exploitable, so knowing only the version does not fully indicate if the server can be exploited but it’s still a good indicator that it should be updated.

The following graph shows the Top 9 countries using Citrix ADC / Gateway and how many servers are still vulnerable to the two critical CVEs. For most countries we see an obvious drop of servers vulnerable to CVE-2022-27518 after the NSA and new Citrix advisory publication.

Top 9 countries using Citrix ADC with vulnerability statistics for latest two CVEs

This graph shows the Top 20 countries using Citrix ADC / Gateway and how many servers are properly updated so they are protected against both CVEs.

How many servers by country are fully updated against the latest two CVEs

Conclusion

In this blog, we’ve shown how we performed the version identification of Citrix ADC and Citrix Gateway servers by analysing disk images exported from Google Cloud Marketplace using dissect. We also demonstrated that gzip files can be helpful for timestamp information and how we utilised this to find and download missing Citrix ADC builds.

Finally, we used the version identification data to measure the versions of internet-facing Citrix ADC and Gateway servers over time and see that the NSA and Citrix advisory really helped with updates. However, some servers remain vulnerable to CVE-2022-27510 or CVE-2022-27518.

We hope this blog creates extra awareness for these two Citrix CVEs and that our research on version identification contributes to future studies.

One Year Since Log4Shell: Lessons Learned for the next ‘code red’

Authored by Edwin van Vliet and Max Groot

One year ago, Fox-IT and NCC Group released their blogpost detailing findings on detecting & responding to exploitation of CVE-2021-44228, better known as ‘Log4Shell’. Log4Shell was a textbook example of a code red scenario: exploitation was trivial, the software was widely used in all sorts of applications accessible from the internet, patches were not yet available and successful exploitation would result in remote code execution. To make matters worse, advisories heavily differed in the early hours of the incident, resulting in conflicting information about which products were affected.

Due to the high-profile nature of Log4Shell, the vulnerability quickly drew the attention of both attackers and defenders. This wasn’t the first time such a ‘perfect storm’ has taken place, and it will definitely not be the last one. This 1-year anniversary seems like an appropriate time to look back and reflect on what we did right and what we could do better next time.

Reflection firstly requires us to critically look at ourselves. Thus, in the first part of this blog we will look back at how our own Security Operations Center (SOC) tackled the problem in the initial days of the ‘code red’. What challenges did we face, what solutions worked, and what lessons will we apply for the next code red scenario?

The second part of this blog discusses the remainder of the year. Our CIRT has since been contacted by several organizations that were exploited using Log4Shell. Such cases ranged from mere coinminers to domain-wide ransomware, but there were several denominators between those cases that provide insight in how even a high-profile vulnerability such as Log4Shell can go by unnoticed and result in a compromise further down the line.

SOC perspective: From quick wins to long term solutions

Within our SOC we are tasked with detecting attacks and informing monitored customers with actionable information in a timely manner. We do not want to call them for generic internet noise, and they expect us to only call when a response on their end is necessary. We have a role that is restricted to monitoring: we do not segment, we do not block, and we do not patch. During the Log4Shell incident, our primary objective was to keep our customers secure by identifying compromised systems quickly and effectively.

The table below summarizes the most important actions we undertook in response to the emergency of the Log4Shell vulnerability. We will reflect further on these events to discuss why we took certain decisions, as well as consider what we could have done differently and what we did right.

Estimated Time (UTC) Event
2021-12-09 22:00 (+0H)Proof-of-Concept for Log4Shell exploitation was published on Github
2021-12-10 08:00 (+10H)Push experimental Suricata rule to detect exploitation attempts
2021-12-10 12:30 (+14,5H)Finish tool that harvests IOC’s out of detected exploitation attempts, start hunting across all platforms
2021-12-10 15:00 (+17H)Behavior-based detection picks up first successful hack using Log4Shell
2021-12-10 21:00 (+23H)Transition from improvised IOC hunting to emergency hunting shifts
2021-12-11 10:00 (+36H)Report first incidents based on hunting to customers
2021-12-12 16:00 (+42H)Send advisory, status update and IOCs to SOC customers
2021-12-12 17:00 (+43H)Add Suricata rule to testing that can distinguish between failed and successful exploitation in real-time
2021-12-13 08:00 (+58H)Determine that real-time detection works, move to production
2021-12-13 08:10 (+58H)Decide to publish all detection & IOC’s as soon as possible
2021-12-13 14:00 (+62H)Refactor IOC harvesting tool to keep up with exploitation volume
2021-12-13 14:30 (+62,5H)Another successful hack found using hunting procedure
2021-12-13 19:30 (+67,5H)Publish all detection and IOC’s in Log4Shell blog
2021-12-13 21:00 (+69h)End emergency hunting procedure
2021-12-14 06:30 (+80,5H)Successful hack detected using Suricata rule
Overview of most important event and actions for the Fox-IT SOC when responding to the emergence of Log4Shell

Friday (2021-12-10): Get visibility and grab the quick wins

On Thursday evening, a proof-of-concept was published on GitHub that made it trivial for attackers to exploit Log4Shell. As we became aware of this on Friday morning, our first point of attention was getting visibility. As we monitor networks with significant exposure to the internet, we anticipated that exploitation attempts would be coming quickly and in vast volumes. One of the first things we did was add detection for exploitation attempts. While we knew that we cannot manually investigate every exploitation attempt, detecting them in the first place would allow us to have a starting point for a threat hunt, add context to other alerts, and give us an idea how this vulnerability is being exploited in the wild. Of course, methods of gaining visibility differ for every SOC and even per threat scenario, but if you can deploy measures to increase visibility, these will often help you out for the remainder of your response process.

While we would have preferred to have full detection coverage immediately, that is often not realistic. We hoped that by detecting exploitation attempts, we would be pointed in the right direction for finding additional detection opportunities.

For the Log4Shell vulnerability, there was an additional benefit. Exploitation of Log4Shell is a multi-step process, where upon successful exploitation of the vulnerability the vulnerable Log4J package will reach out to an attacker-controlled server to retrieve the second-stage payload. This multi-step exploitation process is to the advantage of defenders: initial exploitation attempts will contain the location of the attacker-controlled server that hosts the second-stage payload. This made it possible to automatically retrieve and process the exploitation attempts that were detected. This could then be used to generate a list of high-confidence Indicators of Compromise (IOCs). After all, a connection to a server hosting a second-stage payload could be a strong indicator that a successful exploitation had occurred.

We started regularly mining these IOCs and using them as input for emergency threat hunting. Initially this hunting process was a bit freeform, but we quickly realized we would be doing multiple of such emergency threat hunts for the coming days. We initiated a procedure to perform emergency hunting shifts leveraging our SOC analysts on-duty to perform IOC checks and hunting the networks of customers where these IOCs were found.

We were aware that this ‘emergency threat hunting’ approach was not failproof, for a multitude of reasons:

  • We had to detect the exploitation attempt correctly to mine the corresponding IOC.
  • Hunting for these IOCs still requires manual investigation and threat hunting and is thus prone to human errors.
  • Lastly, searching for connections to IOC’s is a form of retroactive investigation: it does not allow defenders to identify a compromise in real-time.

It was clear that this approach wouldn’t last us all weekend. However, this procedure allowed us the much-needed time to dive deeper into the vulnerability and investigate how to detect it ‘on the wire.’ Mining IOCs was the ‘quick win’ solution that worked for us, but this might be different for others. The importance of quick wins should not be underestimated: quick wins help to buy you some time while you move to solutions that are suited for the long term.

Saturday (2021-12-11): Determine & work towards the short-term objective

Saturday was a day for experimentation: we knew that what we really wanted was to be able to distinguish unsuccessful exploitation attempts from successful ones in real time. At this point, the whole internet was regularly being scanned for Log4Shell, and the alerts were flooding in. It was impossible to manually investigate every exploitation attempt. Real time distinction between a failed and a successful exploitation attempt would allow us to respond quickly to incoming threats. Moreover, it would also allow us to phase out the emergency hunting shifts, that were placing a heavy burden on our SOC analysts.

While we were researching the vulnerability, we got an alert about a suspicious download occurring in a customer network. The alert that had triggered was based on a generic rule that monitors for suspicious downloads from rare domains. This download turned out to be post-exploitation activity following remote code execution that had been obtained using Log4Shell. The compromised system hadn’t come up yet in our threat hunting process, but the post-exploitation activity had been detected using our ruleset for generic malicious activity. While signatures for vulnerability exploitation are of great value, they work best in conjunction with detection for generic suspicious activity. In a code red scenario, many attackers will likely fall back on tooling and infrastructure they have used prior. Thus, for code reds, having generic detection in place is crucial to ‘fill the gaps’ while you are working on tightening your defenses.

Researching how to detect the vulnerability ‘over the wire’ took time and resources. We had reproduced the exploit in our local lab and combined with the PCAP from the observed ‘in the wild’ hack, we had what we needed to work on detection that could distinguish successful from failed exploitation attempts.

Halfway through Saturday, we pushed an experimental Suricata rule that appeared promising. This rule could detect the network traffic that Log4J generates when reaching out to attacker-controlled infrastructure. Therefore, the rule should only trigger on successful exploitation attempts, and not on failed ones. While this worked great in testing, it takes some time to know for sure whether this detection will hold up in production. At that point, the waiting game began. Will this detection yield false positives? Will it trigger when it needs to?

Something we should have done better at this stage of the ‘code red’ is inform our customers what we had been doing. When it comes to sending advisories to our customers, we often find ourselves conflicted. Our rule of thumb is that we only send an advisory when we feel that we have something meaningful to add that our customers do not know yet. Thus, we had not sent a ‘patch everything as soon as possible’ advisory to our customers, as this felt redundant. Having said that, sending something of an update at an earlier stage about what we had been doing would have been better for our customers.

On Saturday evening, more than 36 hours after we had started collecting IOC’s & threat hunting, we informed our customers about what we had been doing up until that point. We provided IOCs and explained what we had done in terms of detection. We referred to advisories & repositories made by others when it came to patching & mitigation. In hindsight, we should have an update earlier about where we would focus our efforts. Giving an update earlier would have allowed customers to focus their efforts elsewhere, knowing what part of the responsibility we would take up during the code red. After all, proactively informing those that depend on you for their own defense, greatly reduces the amount of redundant work that is being done across the line.

Sunday (2021-12-12): Transition to the long-term

On Sunday, we had our detection of real-time exploitation working. We knew that it worked as it had triggered several times on systems that had been targeted with Log4Shell exploitation attempts. These systems were segmented in a way where they could not set up connections to external servers, preventing them from successfully retrieving the second-stage payload. However, these systems were still highly vulnerable and exposed and thus we reported such instances as soon as we could.

On Sunday morning, the Dutch National Cyber Security Center (NCSC-NL) assembled all companies part of ‘Cyberveilig Nederland,’ an initiative that Fox-IT participates in. NCSC-NL had set up a repository where cybersecurity companies could document information on this vulnerability, including the identification of software that could be exploited.

This central repository made it a lot easier for us to share our work in a way where we knew others could easily find it within the larger context of the Log4Shell incident. Such a ‘central repository’ allows organizations such as ourselves to focus on the things they are good at. We are not specialized in patching but know a thing or two about writing detection and responding to identified compromise. Collaboration is key during a code red. Organizations should contribute based on their own specialties so that redundant work can be avoided.

At the start of the day, we had unanimously agreed to publish all our detection as soon as possible, preferably that same day. This was when we started writing our blog. One complication was that we were more than 60 hours underway, and fatigue was kicking in. This was also the time to ‘bring in the reinforcements’ and ask others to review our work. IOCs were double-checked, and tools that had been quickly scrapped together were refactored to keep up with the volume of alerts. We released the blogpost at about 8PM, a little later than we had aimed for. With the release of our blogpost, we could share all knowledge we had acquired over the weekend with the security community. We chose to focus on what we know best: network detection and post-exploitation detection. With real-time detection in place, we had our first response done. Over the following weeks, we would continue to work on detecting exploitation, as well as do additional research such as identifying varying payloads used in the wild and releasing the log4-jfinder.

As a summary, the below timeline highlights the key events in our first 72 hours responding to Log4Shell. All blue events are related to detection engineering, whereas green & red identify key moments in decision-making. You can click on the timeline for an enlarged view.

While the high-profile nature of the Log4Shell vulnerability alerted almost everyone that action had to be taken initially, Log4Shell turned out to be a vulnerability with a ‘long tail’. In the year that followed, off-the-shelf exploits would become available for several products, and some mitigations that were released initially turned out to be insufficient for the long run. The next section will therefore approach Log4Shell from the incident response perspective: what did we find when we were approached by organizations that had had trouble mitigating?

Incident Response Retrospective

In the past year, we responded to several incidents where the Log4Shell vulnerability was part of the root cause. In this retrospective we will compare those incidents to the expectations that we had in the security industry. We were expecting many ransomware incidents resulting from this initial attack vector. Was this expectation grounded?

The incident response cases we investigated can largely be divided into four categories. It should be noted that often times, multiple distinct threat actors had left their traces on the victim’s systems. It was not uncommon that we were able to identify activities in multiple of the following categories on the same system:

  • Ransomware. Either systems were already encrypted, or sufficient information was identified to conclude the actor was intending to deploy ransomware.
  • Coin miners. These were mostly fully automated attacks. The miners were often encountered as secondary compromises without any apparent connection to other compromises.
  • Espionage. Several cases were related to the Deep Panda actor, according to indicators mentioned by Fortinet. In several cases we identified activities what were concentrated on 5 February 2022. This indicates a short, widespread campaign by this actor. Most of the cases showed only partial signs of the chain though.
  • Initial stage only. Successful exploitation of the Log4Shell vulnerabilities, but no significant post-compromise activities. The attackers may have been hindered by network segmentation or by a big backlog of other victims. As a result, we cannot confidently put such an attacker in any of the other categories.

We compared the various recommendations that we provided in these incident response cases. All the incidents that led to an engagement of our incident response teams could have been prevented if:

  • The vulnerable systems had been updated in time.
    In every instance, a security advisory was released by the vendor of the product containing the Log4Shell vulnerability. And in every case, a security update was available at the time of compromise.
  • The system had been in a segmented network, blocking arbitrary outgoing connections.
    In most situations, the vulnerable system or appliance needed access only to specific services and therefore did not require access to any external resource.

As always, reality is a little more nuanced. We will elaborate a bit in the following two sections.

Unfortunate series of events

In follow-up conversations with organizations that contacted our CIRT, we noticed a pattern that many of them had heard of Log4Shell but believed they were safe. The most common reason for their false sense of safety is a very unfortunate series of events related to patch and lifecycle management.

For example, one vendor had released both a security update and a mitigation script, the latter being for organizations that were not able to immediately apply the security update. On several incident response engagements, we found administrators who had executed the script and thought they were safe, unaware that the vendor had later released new versions of the mitigation script. The first version of the mitigation script apparently did not completely resolve the problem. The newer mitigation scripts performed many more operations that were required to protect against Log4Shell, including for example modifying the Log4J jar-file. As a result of this, several victims were not aware they were still running vulnerable software.

We also encountered incidents where administrators kept delaying the security upgrades or performed the upgrade but had to roll back to a snapshot due to technical issues after the upgrade. The rollback restored the old situation in which the software was still vulnerable.

The lesson here is that clear communication from vendors about vulnerabilities and mitigation is key. For end users, a mitigation script or action is almost always less preferable than applying the security update. Mitigation scripts should be considered to be quick wins, not long-term solutions.

Destination unreachable

The other major mitigation that could have prevented several incidents was network segmentation. Exploitation of this type of vulnerability requires outbound network connections to download a secondary payload. Therefore, in a network segment where connections are heavily regulated, this attack simply would not work.

In server networks, a firewall should typically block all connections by default. Only some connections should be allowed, based on source and/or destination addresses and ports. Of course, in this modern world, cloud computing often requires larger blocks of IP space to be allowed. The usual solution is to configure proxy servers that allow downloading security updates or connecting to cloud services based on domain names.

In a few cases, the initial attempts by malicious actors were unsuccessful, because the outgoing connections were blocked or hindered by the customer’s network infrastructure. However, with later “improvements” some attackers managed to bypass some of the mechanisms (for example, hosting the payload on the often allow-listed port 443), or they found vulnerable services on non-standard ports. Those cases prove it was a race against the clock, because attackers were also improving their techniques.

Besides actively blocking outgoing connections, the firewalls offer insight due to the log entries they emit. Together with network monitoring they can immediately single out vulnerable devices. And possibly the biggest advantage of strict network segmentation: they counter zero-days of this entire category of vulnerabilities. A zero-day is not necessarily something that is only used by advanced attackers. Some software that is widely in use today will contain vulnerabilities that nobody knows about yet. If such a vulnerability is discovered by a malicious actor, an exploit may be available before a security update. That is one of the main reasons why we should value strict network segmentation: it provides an additional layer of defense.

Closing Remarks

Looking back, we have dealt with far fewer incidents than we had initially expected. Does this mean that we were overly pessimistic? Or does it mean that our “campaign” as a security industry was very effective? We like to believe the latter. After all, almost everybody who needed to know about the problem knew about it.

Having said that, one risk we are all taking is the risk of numbing our audience. We need to be conservative sending out ‘code red’ security advisories to prevent the “boy who cried wolf” syndrome. Nowadays it sometimes seems like the security industry does not take their end users seriously enough to believe that they will act on vulnerabilities that do not have a catchy name and logo.

In this case, we as a security community gave Log4Shell a lot of attention. We think this was justified. The vulnerability was easy to exploit with PoC code available, and the Log4J component was used in many software products. Moreover, we saw widespread exploitation in the wild, albeit mostly using off-the-shelf exploits for products that use Log4J.

Our role within the security industry comes with the responsibility to inform. With a component as widespread as Log4J, it is very difficult to provide specific advice. It’s the software vendors whose software uses these components who need to provide more specific security advisories. Their advisories need to be actionable. We think that for the general public, it is best to closely follow those specific advisories for products they might be using.

As security vendors, repeating information that is already available can only lead to confusion. Instead, we should contribute within our own areas of expertise. For the next code red, we know what we’ll do: focus our efforts where we have something to add, and stay out of areas where we do not. Bring it on!

❌