Detecting and removing malware using VirusTotal integration

Wazuh uses the integrator module to connect to external APIs and alerting tools such as VirusTotal.

In this use case, you use the Wazuh File Integrity Monitoring (FIM) module to monitor a directory for changes and the VirusTotal API to scan the files in the directory. Then, configure Wazuh to trigger an active response script and remove files that VirusTotal detects as malicious. We test this use case on Ubuntu and Windows endpoints.

You need a VirusTotal API key in this use case to authenticate Wazuh to the VirusTotal API.

For more information on this integration, check the VirusTotal integration section of the documentation.

Infrastructure

Endpoint

Description

Ubuntu 22.04

This is the Linux endpoint where you download a malicious file. Wazuh triggers an active response script to remove the file once VirusTotal flags it as malicious.

Windows 11

This is the Windows endpoint where you download a malicious file. Wazuh triggers an active response script to remove the file once VirusTotal flags it as malicious.

Configuration for the Ubuntu endpoint

Configure your environment as follows to test the use case for the Ubuntu endpoint. These steps work for other Linux distributions as well.

Ubuntu endpoint

Perform the following steps to configure Wazuh to monitor near real-time changes in the /root directory of the Ubuntu endpoint. These steps also install the necessary packages and create the active response script that removes malicious files.

  1. Search for the <syscheck> block in the Wazuh agent configuration file /var/ossec/etc/ossec.conf. Make sure that <disabled> is set to no. This enables the Wazuh FIM to monitor for directory changes.

  2. Add an entry within the <syscheck> block to configure a directory to be monitored in near real-time. In this case, you are monitoring the /root directory:

    <directories realtime="yes">/root</directories>
    
  3. Install jq, a utility that processes JSON input from the active response script.

    $ sudo apt update
    $ sudo apt -y install jq
    
  4. Create the /var/ossec/active-response/bin/remove-threat.sh active response script to remove malicious files from the endpoint:

    #!/bin/bash
    
    LOCAL=`dirname $0`;
    cd $LOCAL
    cd ../
    
    PWD=`pwd`
    
    read INPUT_JSON
    FILENAME=$(echo $INPUT_JSON | jq -r .parameters.alert.data.virustotal.source.file)
    COMMAND=$(echo $INPUT_JSON | jq -r .command)
    LOG_FILE="${PWD}/../logs/active-responses.log"
    
    #------------------------ Analyze command -------------------------#
    if [ ${COMMAND} = "add" ]
    then
     # Send control message to execd
     printf '{"version":1,"origin":{"name":"remove-threat","module":"active-response"},"command":"check_keys", "parameters":{"keys":[]}}\n'
    
     read RESPONSE
     COMMAND2=$(echo $RESPONSE | jq -r .command)
     if [ ${COMMAND2} != "continue" ]
     then
      echo "`date '+%Y/%m/%d %H:%M:%S'` $0: $INPUT_JSON Remove threat active response aborted" >> ${LOG_FILE}
      exit 0;
     fi
    fi
    
    # Removing file
    rm -f $FILENAME
    if [ $? -eq 0 ]; then
     echo "`date '+%Y/%m/%d %H:%M:%S'` $0: $INPUT_JSON Successfully removed threat" >> ${LOG_FILE}
    else
     echo "`date '+%Y/%m/%d %H:%M:%S'` $0: $INPUT_JSON Error removing threat" >> ${LOG_FILE}
    fi
    
    exit 0;
    
  5. Change the /var/ossec/active-response/bin/remove-threat.sh file ownership, and permissions:

    $ sudo chmod 750 /var/ossec/active-response/bin/remove-threat.sh
    $ sudo chown root:wazuh /var/ossec/active-response/bin/remove-threat.sh
    
  6. Restart the Wazuh agent to apply the changes:

    $ sudo systemctl restart wazuh-agent
    

Wazuh server

Perform the following steps on the Wazuh server to alert for changes in the endpoint directory and enable the VirusTotal integration. These steps also enable and trigger the active response script whenever a suspicious file is detected.

  1. Add the following rules to the /var/ossec/etc/rules/local_rules.xml file on the Wazuh server. These rules alert about changes in the /root directory that are detected by FIM scans:

    <group name="syscheck,pci_dss_11.5,nist_800_53_SI.7,">
        <!-- Rules for Linux systems -->
        <rule id="100200" level="7">
            <if_sid>550</if_sid>
            <field name="file">/root</field>
            <description>File modified in /root directory.</description>
        </rule>
        <rule id="100201" level="7">
            <if_sid>554</if_sid>
            <field name="file">/root</field>
            <description>File added to /root directory.</description>
        </rule>
    </group>
    
  2. Add the following configuration to the Wazuh server /var/ossec/etc/ossec.conf file to enable the Virustotal integration. Replace <YOUR_VIRUS_TOTAL_API_KEY> with your VirusTotal API key. This allows to trigger a VirusTotal query whenever any of the rules 100200 and 100201 are triggered:

    <ossec_config>
      <integration>
        <name>virustotal</name>
        <api_key><YOUR_VIRUS_TOTAL_API_KEY></api_key> <!-- Replace with your VirusTotal API key -->
        <rule_id>100200,100201</rule_id>
        <alert_format>json</alert_format>
      </integration>
    </ossec_config>
    

    Note

    The free VirusTotal API rate limits requests to four per minute. If you have a premium VirusTotal API key, with a high frequency of queries allowed, you can add more rules besides these two. You can also configure Wazuh to monitor more directories.

  3. Append the following blocks to the Wazuh server /var/ossec/etc/ossec.conf file. This enables active response and triggers the remove-threat.sh script when VirusTotal flags a file as malicious:

    <ossec_config>
      <command>
        <name>remove-threat</name>
        <executable>remove-threat.sh</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>
    
  4. Add the following rules to the Wazuh server /var/ossec/etc/rules/local_rules.xml file to alert about the active response results:

    <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>
    
  5. Restart the Wazuh manager to apply the configuration changes:

    $ sudo systemctl restart wazuh-manager
    

Attack emulation

  1. Download an EICAR test file to the /root directory on the Ubuntu endpoint:

    $ sudo curl -Lo /root/eicar.com https://secure.eicar.org/eicar.com && sudo ls -lah /root/eicar.com
    

Visualize the alerts

You can visualize the alert data in the Wazuh dashboard. To do this, go to the Threat Hunting module and add the filters in the search bar to query the alerts.

  • Linux - rule.id: is one of 553,100092,87105,100201

    Remove malware on Linux alert

Configuration for the Windows endpoint

Windows endpoint

Perform the following steps to configure Wazuh to monitor near real-time changes in the /Downloads directory. These steps also install the necessary packages and create the active response script to remove malicious files.

  1. Search for the <syscheck> block in the Wazuh agent C:\Program Files (x86)\ossec-agent\ossec.conf file. Make sure that <disabled> is set to no. This enables the Wazuh FIM module to monitor for directory changes.

  2. Add an entry within the <syscheck> block to configure a directory to be monitored in near real-time. In this use case, you configure Wazuh to monitor the C:\Users\<USER_NAME>\Downloads directory. Replace the <USER_NAME> variable with the appropriate user name:

    <directories realtime="yes">C:\Users\<USER_NAME>\Downloads</directories>
    
  3. Download the Python executable installer from the official Python website.

  4. 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)

  5. Once Python completes the installation process, open an administrator PowerShell terminal and use pip to install PyInstaller:

    > pip install pyinstaller
    > pyinstaller --version
    

    You use Pyinstaller here to convert the active response Python script into an executable application that can run on a Windows endpoint.

  6. Create an active response script remove-threat.py to remove a file from the Windows endpoint:

    #!/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:
                file_path = msg.alert["parameters"]["alert"]["data"]["virustotal"]["source"]["file"]
                if os.path.exists(file_path):
                    os.remove(file_path)
                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)
    
  7. Convert the active response Python script remove-threat.py to a Windows executable application. Run the following PowerShell command as an administrator to create the executable:

    > pyinstaller -F \path_to_remove-threat.py
    

    Take note of the path where pyinstaller created remove-threat.exe.

  8. Move the executable file remove-threat.exe to the C:\Program Files (x86)\ossec-agent\active-response\bin directory.

  9. Restart the Wazuh agent to apply the changes. Run the following PowerShell command as an administrator:

    > Restart-Service -Name wazuh
    

Wazuh server

Perform the following steps on the Wazuh server to configure the VirusTotal integration. These steps also enable and trigger the active response script whenever a suspicious file is detected.

  1. Add the following configuration to the /var/ossec/etc/ossec.conf file on the Wazuh server to enable the VirusTotal integration. 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 group are 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>
    

    Note

    The free VirusTotal API rate limits requests to four per minute. If you have a premium VirusTotal API key, with a high frequency of queries allowed, you can add more rules besides these two. You can configure Wazuh to monitor more directories besides C:\Users\<USER_NAME>\Downloads.

  2. Append the following blocks to the Wazuh server /var/ossec/etc/ossec.conf file. This enables active response and trigger the remove-threat.exe executable when the VirusTotal query returns positive matches for threats:

    <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>
    
  3. Add the following rules to the Wazuh server /var/ossec/etc/rules/local_rules.xml file to alert about the active response results.

    <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>
    
  4. Restart the Wazuh manager to apply the configuration changes:

    $ sudo systemctl restart wazuh-manager
    

Attack emulation

  1. Follow the next steps to temporarily turn off real-time Microsoft Defender antivirus protection in Windows Security:

    1. Click on the Start menu and type Windows Security to search for that app.

    2. Select the Windows Security app from results, go to Virus & threat protection, and under Virus & threat protection settings select Manage settings.

    3. Switch Real-time protection to Off.

  2. 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 Threat Hunting module and add the filters in the search bar to query the alerts.

  • Windows - rule.id: is one of 554,100092,553,87105

    Remove malware from Windows alert