Detecting and removing WhisperGate malware

WhisperGate is a destructive file-wiper malware that is being used in a campaign targeting Ukrainian organizations. The malware targets Windows devices, corrupts the Master Boot Record (MBR), and the hard disk of the victim endpoint. It is designed to look like ransomware but doesn’t present a ransom recovery mechanism, which renders the device inoperable.
In this blog post, we explore the detection and removal of WhisperGate using several Wazuh features.
When WhisperGate malware infects an endpoint, its activities can be divided into 3 stages:
In stage 1, the primary objective of the malware is to overwrite the MBR with a malicious one. This malicious MBR takes effect once the endpoint is rebooted. The malware is often named stage1.exe
and is commonly found in directories such as C:\PerfLogs
, C:\ProgramData
, C:\
, and C:\temp
. When the endpoint is restarted, a ransomware note will be displayed to the user.
In stage 2, the file corrupter malware file is downloaded from the C2 server. It is usually named stage2.exe
and it retrieves the third stage malware from a Discord attachment.
In stage 3, the file corrupter downloaded by stage2.exe
is executed. After execution, it excludes the Windows C drive from being scanned by Microsoft Defender and stops the Microsoft Defender service. Finally, it locates and corrupts any file that matches a list of 191 file extensions in certain directories on the endpoint.
We detect WhisperGate using the following techniques:
Using the FIM module, we configure Wazuh to monitor changes to files or folders on an endpoint. The module looks for modifications by comparing the added files’ checksums to the old ones. This will allow us to create a rule when the checksum of the files matches any of the malware files of WhisperGate.
To simulate the malware behavior, we monitor one of the folders where the malware resides, by adding the following configuration to the “C:\Program Files (x86)\ossec-agent\ossec.conf
” file on the Windows endpoint.
<syscheck> <directories check_all="yes" realtime="yes">c:\perfLogs</directories> </syscheck>
To apply the changes, restart the agent by running the following PowerShell command as an administrator:
Restart-Service -Name wazuh
On the Wazuh manager, we add the following rules to the /var/ossec/etc/rules/local_rules.xml
file. This will generate an alert when WhisperGate malware files are found in the monitored folder.
<group name="syscheck,malware,"> <rule id="110001" level="10"> <if_group>syscheck</if_group> <field name="sha256">a196c6b8ffcb97ffb276d04f354696e2391311db3841ae16c8c9f56f36a38e92</field> <description>Malware Hash Match: WhisperGate Stage 1 IOC Detected</description> <mitre> <id>T1204.002</id> </mitre> </rule> <rule id="110002" level="10"> <if_group>syscheck</if_group> <field name="sha256">dcbbae5a1c61dbbbb7dcd6dc5dd1eb1169f5329958d38b58c3fd9384081c9b78</field> <description>Malware Hash Match: WhisperGate Stage 2 IOC Detected</description> <mitre> <id>T1204.002</id> </mitre> </rule> <rule id="110003" level="10"> <if_group>syscheck</if_group> <field name="sha256" type="pcre2">9ef7dbd3da51332a78eff19146d21c82957821e464e8133e9594a07d716d892d|923eb77b3c9e11d6c56052318c119c1a22d11ab71675e6b95d05eeb73d1accd6</field> <description>Malware Hash Match: WhisperGate Stage 3 IOC Detected</description> <mitre> <id>T1204.002</id> </mitre> </rule> </group>
We restart the wazuh manager service after adding the rules. This is done with the command below:
systemctl restart wazuh-manager
To simulate the malware behavior, we download the malware files to the monitored folder. It gets detected and we can see the alert on the Wazuh dashboard.
Using the VirusTotal integration, Wazuh automatically sends a request to the VirusTotal API with the hashes of files that are created or modified in the monitored folders. This is carried out using the following steps:
To scan these files with VirusTotal, we add the block below to the Wazuh manager /var/ossec/etc/ossec.conf
configuration file.
<ossec_config> <integration> <name>virustotal</name> <api_key>API_KEY</api_key> <!-- Replace with your VirusTotal API key --> <group>syscheck</group> <alert_format>json</alert_format> </integration> </ossec_config>
We restart the Wazuh manager service after adding the VirusTotal configuration. This is done with the command below:
systemctl restart wazuh-manager
After enabling the integration, we get alerts whenever samples of WhisperGate malware are added to our monitored folder.
Note
The VirusTotal Public API is limited to 500 requests per day at a rate of 4 requests per minute.
We need to modify the configuration of the Windows endpoint and the Wazuh manager to make use of our active response script. Once VirusTotal identifies a file as a threat, we can configure Wazuh to trigger an active response to remove the file from the endpoint.
The os.remove()
function in the active response script handles the removal of the malicious file.
os.remove(msg.alert["parameters"]["alert"]["data"]["virustotal"]["source"]["file"])
We make use of the active response script remove-threat.py
with the following content:
#!/usr/bin/python3 # Copyright (C) 2015-2022, Wazuh Inc. # All rights reserved. import os import sys import json import datetime if os.name == 'nt': LOG_FILE = "C:\\Program Files (x86)\\ossec-agent\\active-response\\active-responses.log" else: LOG_FILE = "/var/ossec/logs/active-responses.log" ADD_COMMAND = 0 DELETE_COMMAND = 1 CONTINUE_COMMAND = 2 ABORT_COMMAND = 3 OS_SUCCESS = 0 OS_INVALID = -1 class message: def __init__(self): self.alert = "" self.command = 0 def write_debug_file(ar_name, msg): with open(LOG_FILE, mode="a") as log_file: log_file.write(str(datetime.datetime.now().strftime('%Y/%m/%d %H:%M:%S')) + " " + ar_name + ": " + msg +"\n") def setup_and_check_message(argv): # get alert from stdin input_str = "" for line in sys.stdin: input_str = line break try: data = json.loads(input_str) except ValueError: write_debug_file(argv[0], 'Decoding JSON has failed, invalid input format') message.command = OS_INVALID return message message.alert = data command = data.get("command") if command == "add": message.command = ADD_COMMAND elif command == "delete": message.command = DELETE_COMMAND else: message.command = OS_INVALID write_debug_file(argv[0], 'Not valid command: ' + command) return message def send_keys_and_check_message(argv, keys): # build and send message with keys keys_msg = json.dumps({"version": 1,"origin":{"name": argv[0],"module":"active-response"},"command":"check_keys","parameters":{"keys":keys}}) write_debug_file(argv[0], keys_msg) print(keys_msg) sys.stdout.flush() # read the response of previous message input_str = "" while True: line = sys.stdin.readline() if line: input_str = line break # write_debug_file(argv[0], input_str) try: data = json.loads(input_str) except ValueError: write_debug_file(argv[0], 'Decoding JSON has failed, invalid input format') return message action = data.get("command") if "continue" == action: ret = CONTINUE_COMMAND elif "abort" == action: ret = ABORT_COMMAND else: ret = OS_INVALID write_debug_file(argv[0], "Invalid value of 'command'") return ret def main(argv): write_debug_file(argv[0], "Started") # validate json and get command msg = setup_and_check_message(argv) if msg.command < 0: sys.exit(OS_INVALID) if msg.command == ADD_COMMAND: alert = msg.alert["parameters"]["alert"] keys = [alert["rule"]["id"]] action = send_keys_and_check_message(argv, keys) # if necessary, abort execution if action != CONTINUE_COMMAND: if action == ABORT_COMMAND: write_debug_file(argv[0], "Aborted") sys.exit(OS_SUCCESS) else: write_debug_file(argv[0], "Invalid command") sys.exit(OS_INVALID) try: os.remove(msg.alert["parameters"]["alert"]["data"]["virustotal"]["source"]["file"]) write_debug_file(argv[0], json.dumps(msg.alert) + " Successfully removed threat") except OSError as error: write_debug_file(argv[0], json.dumps(msg.alert) + "Error removing threat") else: write_debug_file(argv[0], "Invalid command") write_debug_file(argv[0], "Ended") sys.exit(OS_SUCCESS) if __name__ == "__main__": main(sys.argv)
We proceed to convert the active response python script remove-threat.py
to an executable application, by using the following steps:
pip install wheel python setup.py install
remove-threat.py
file to the PyInstaller folderpyinstaller -F remove-threat.py
remove-threat.exe
from pyinstaller-5.2\dist
folder to C:\Program Files (x86)\ossec-agent\active-response\bin
To apply the changes, restart the agent by running the following PowerShell command as an administrator:
Restart-Service -Name wazuh
We proceed to configure active response on the Wazuh manager, this is triggered when malicious files are identified by VirusTotal. We append the following blocks to the Wazuh manager /var/ossec/etc/ossec.conf
file.
<ossec_config> <command> <name>remove-threat</name> <executable>remove-threat.exe</executable> <timeout_allowed>no</timeout_allowed> </command> <active-response> <disabled>no</disabled> <command>remove-threat</command> <location>local</location> <rules_id>87105</rules_id> </active-response> </ossec_config>
After the active response configuration is completed, we create rules that alerts us if the active response is successful or not. We achieve this by adding the following to our /var/ossec/etc/rules/local_rules.xml
file on the Wazuh manager.
<group name="virustotal,"> <rule id="110006" level="12"> <if_sid>657</if_sid> <match>Successfully removed threat</match> <description>$(parameters.program) removed threat located at $(parameters.alert.data.virustotal.source.file)</description> </rule> <rule id="110007" level="12"> <if_sid>657</if_sid> <match>Error removing threat</match> <description>Error removing threat located at $(parameters.alert.data.virustotal.source.file)</description> </rule> </group>
Finally, restart the Wazuh manager to apply configuration changes.
systemctl restart wazuh-manager
To simulate the malware behavior, we download the malware files to the folder monitored by the FIM module. The malicious files were removed by the active response module, and an alert was created upon successful deletion.
In this blog post, we use various components of Wazuh such as FIM, VirusTotal integration, and active response capability to detect and remove WhisperGate malware.
Destructive malware targeting Ukrainian organizations – Microsoft Security Blog
Indicators of compromise:
https://bazaar[.]abuse[.]ch/sample/a196c6b8ffcb97ffb276d04f354696e2391311db3841ae16c8c9f56f36a38e92
https://bazaar[.]abuse[.]ch/sample/dcbbae5a1c61dbbbb7dcd6dc5dd1eb1169f5329958d38b58c3fd9384081c9b78
https://bazaar[.]abuse[.]ch/sample/9ef7dbd3da51332a78eff19146d21c82957821e464e8133e9594a07d716d892d
https://bazaar[.]abuse[.]ch/sample/923eb77b3c9e11d6c56052318c119c1a22d11ab71675e6b95d05eeb73d1accd6