Detecting and Removing Malware using VirusTotal Integration on Windows Endpoints with Wazuh
- Sean Nanty (z3tssu)
- Nov 8, 2024
- 4 min read
This is a continuation to my first post, on how to setup Wazuh.
In this guide, I will show you how to configure Wazuh to remove malware that a user may download on their machines with the use of VirusTotal API integration.
All instructions can be found here: https://documentation.wazuh.com/current/proof-of-concept-guide/detect-remove-malware-virustotal.html
Objectives
Configure Windows Endpoints
Create a Python Script and Convert it to Exe
Configure Wazuh Server
Integrate VirusTotal
Protect the Downloads folder
Seems Easy enough right?
1. Configure Windows Endpoint
The instruction requires you to manually configure each individual endpoint’s agent which is located in C:\Program Files (x86)\ossec-agent\ossec.conf
However I am not going to be doing that, rather I want the lazy and fast method, so I will configure the shared agent.conf file which is located on the wazuh-server, which will affect all Wazuh agent’s configurations.
On the Wazuh server access the agent.conf
cd /var/ossec/etc/shared/default/agent.conf
nano agent.conf
add the following:

This will check the Downloads folder of the particular user.
2. Create a Python Script and Convert it to EXE
This will be used to remove malicious downloaded files.
Download the Python executable installer from the official Python website.
Run the Python installer once downloaded. Make sure to check the following boxes:
Install launcher for all users
Add Python 3.X to PATH (This places the interpreter in the execution path)
3. Once Python completes the installation process, open an administrator PowerShell terminal and use pip it to install PyInstaller:
> pip install pyinstaller
> pyinstaller --version
4. The next step will be to create the active response script remove-threat.py to remove a file from the Windows endpoint
Python Script
#!/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)
You can create this with a simple notepad or the Python IDE (pycharm)
Store it somewhere that's easy to find
5. Convert the created Python script into an EXE
Run the following as an administrator
pyinstaller -F \path_to_remove-threat.py
Keep track of where it created the application.
6. Move the executable file remove-threat.exe to the C:\Program Files (x86)\ossec-agent\active-response\bin directory.
7. Now restart the Wazuh agent
Restart-Service -Name wazuh
Configuring the Wazuh Server
The following steps will involve adding your VirusTotal API key to the Wazuh server ossec.conf file.
Integrating VirusTotal
cd /var/ossec/etc
nano ossec.conf
Now access your VirusTotal and find your API key, then replace<YOUR_VIRUS_TOTAL_API_KEY> with your VirusTotal API key. This allows to trigger a VirusTotal query whenever any of the rules in the FIM syscheck the group is triggered:
<ossec_config>
<integration>
<name>virustotal</name>
<api_key><YOUR_VIRUS_TOTAL_API_KEY></api_key> <!-- Replace with your VirusTotal API key -->
<group>syscheck</group>
<alert_format>json</alert_format>
</integration>
</ossec_config>
2. Enabling Active Response
Within ossec.conf, add the following that will enable active response and will trigger the created remove-threat.exe when VirusTotal queries a positive malware match.
<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>
I added it to the bottom of my ossec.conf

3. Add the detection and removal rules
navigate to:
cd /var/ossec/etc/rulesnano local_rules.xml
<group name="virustotal,">
<rule id="100092" 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="100093" 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>
Add it to the local_rules.xml as below

4. Restart the wazuh-manager
sudo systemctl restart wazuh-manager
Attack emulation
Download an EICAR test file to the C:\Users\<USER_NAME>\Downloads directory on the Windows endpoint.
> Invoke-WebRequest -Uri https://secure.eicar.org/eicar.com.txt -OutFile eicar.txt > cp .\eicar.txt C:\Users\<USER_NAME>\Downloads
This triggers a VirusTotal query and generates an alert. In addition, the active response script automatically removes the file.
Visualize the alerts
You can visualize the alert data in the Wazuh dashboard. To do this, go to the Security Events module and add the filters in the search bar to query the alerts.
Windows — rule.id: is one of 554,100092,553,87105
Comments