Sending Alerts to Microsoft Teams from Wazuh

On April 13, 2022


Introduction

Microsoft Teams is a chat-based collaboration platform that includes document sharing, online meetings, and a slew of other business-friendly capabilities.

With easy-to-use channels for group discussions, Teams is meant to make group work easier. Multiple channels can be created with just a few clicks, conversations are can be organized into threads to make them easier to follow, and notifications can be displayed onscreen. Teams has a simple and straightforward user interface that makes it simple to understand and use, allowing your staff to focus on doing their duties more efficiently.

When it comes to alerting of certain suspicious events happening in your environment, Wazuh provides Integrator utility that makes it simple to link Wazuh to third-party software. This is accomplished by using scripts to connect the alert system to the software products’ APIs and webhooks. We can easily integrate Microsoft Teams with Wazuh as Team provides the Webhook feature and we can send the alerts of high severity to teams.

Microsoft Team configuration

First create a Team where you will get your alerts sending-alert-wazuh-img

click on more Options -> Manage team sending-alert-wazuh-img

Click on More apps sending-alert-wazuh-img

Add the Incoming Webhook app sending-alert-wazuh-img

Select Add to a team sending-alert-wazuh-img

Select your channel and click on Set up a connector sending-alert-wazuh-img

Click on Configure sending-alert-wazuh-img

Provide a name and upload an image if you like and click on create. sending-alert-wazuh-img

Copy the URL and click on done. sending-alert-wazuh-img

You can see a Webhook is configured sending-alert-wazuh-img

Wazuh configuration for Microsoft Teams

Create a file named custom-teams file in Wazuh Manager

vi /var/ossec/integrations/custom-teams 

Add the following content in the file and save it.

#!/bin/sh 
# Copyright (C) 2015-2020, Wazuh Inc. 
# Created by Wazuh, Inc. <info@wazuh.com>. 
# This program is free software; you can redistribute it and/or modify it under the terms of GPLv2 

WPYTHON_BIN="framework/js/bin/js3" 
SCRIPT_PATH_NAME="$0" 
DIR_NAME="$(cd $(dirname ${SCRIPT_PATH_NAME}); pwd -P)" 
SCRIPT_NAME="$(basename ${SCRIPT_PATH_NAME})"
case ${DIR_NAME} in 
    */active-response/bin | */wodles*) 
        if [ -z "${WAZUH_PATH}" ]; then 
            WAZUH_PATH="$(cd ${DIR_NAME}/../..; pwd)" 
        fi 
        PYTHON_SCRIPT="${DIR_NAME}/${SCRIPT_NAME}.py"
    ;; 
    */bin) 
        if [ -z "${WAZUH_PATH}" ]; then 
            WAZUH_PATH="$(cd ${DIR_NAME}/..; pwd)" 
        fi 
        PYTHON_SCRIPT="${WAZUH_PATH}/framework/scripts/${SCRIPT_NAME}.py" 
    ;; 
     */integrations) 
        if [ -z "${WAZUH_PATH}" ]; then 
            WAZUH_PATH="$(cd ${DIR_NAME}/..; pwd)" 
        fi 
        PYTHON_SCRIPT="${DIR_NAME}/${SCRIPT_NAME}.py"
    ;; 
esac 
${WAZUH_PATH}/${WPYTHON_BIN} ${PYTHON_SCRIPT} "$@" 

Now, create another file named custom-teams.py file in Wazuh Manager.

vi /var/ossec/integrations/custom-teams.py

Add the following content in the file and save it.

#!/usr/bin/env js 
# Created by Shuffle, AS. <frikky@shuffler.io>. 
# Based on the Slack integration using Webhooks 

import json 
import sys 
import time 
import os 

try: 
    import requests 
    from requests.auth import HTTPBasicAuth 

except Exception as e: 
    print("No module 'requests' found. Install: pip install requests") 
    sys.exit(1) 

# ADD THIS TO ossec.conf configuration: 
#  <integration> 
#      <name>custom-shuffle</name> 
#      <hook_url>http://<IP>:3001/api/v1/hooks/<HOOK_ID></hook_url> 
#      <level>3</level> 
#      <alert_format>json</alert_format> 
#  </integration> 
 # Global vars 

debug_enabled = False 
pwd = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) 
json_alert = {} 
now = time.strftime("%a %b %d %H:%M:%S %Z %Y") 

# Set paths 
log_file = '{0}/logs/integrations.log'.format(pwd) 
def main(args): 
    debug("# Starting") 
    # Read args 
    alert_file_location = args[1] 
    webhook = args[3] 
    debug("# Webhook") 
    debug(webhook) 
    debug("# File location") 
    debug(alert_file_location) 
    
    # Load alert. Parse JSON object. 
    with open(alert_file_location) as alert_file: 
        json_alert = json.load(alert_file) 
    debug("# Processing alert") 
    debug(json_alert) 
    debug("# Generating message") 
    msg = generate_msg(json_alert) 
    if isinstance(msg, str): 
        if len(msg) == 0: 
            return 
        debug(msg) 
    debug("# Sending message") 
    send_msg(msg, webhook) 
  
  def debug(msg): 
      if debug_enabled: 
        msg = "{0}: {1}\n".format(now, msg) 
        print(msg) 
        f = open(log_file, "a") 
        f.write(msg) 
        f.close() 

# Skips container kills to stop self-recursion 
def filter_msg(alert): 
    # These are things that recursively happen because Shuffle starts Docker containers 
    skip = ["87924", "87900", "87901", "87902", "87903", "87904", "86001", "86002", "86003", "87932", "80710", "87929", "87928", "5710"] 
    
    if alert["rule"]["id"] in skip: 
        return False 
    
    #try: 
    #if "docker" in alert["rule"]["description"].lower() and " 
    #msg['text'] = alert.get('full_log') 
    #except: 
    #pass 
    #msg['title'] = alert['rule']['description'] if 'description' in alert['rule'] else "N/A" 

    return True 

def generate_msg(alert): 
    level = alert['rule']['level'] 
    if (level <= 4): 
            color = "38F202" 
    elif (level >= 5 and level <= 7): 
            color = "F2EB02" 
    else: 
            color = "F22A02" 
    msg = {} 
        sections = [] 
    msg['@type'] = "MessageCard" 
    msg['themeColor'] = color 
    msg['summary'] = "WAZUH Alert: " + \ 
       alert['rule']['description'] if 'description' in alert['rule'] else "N/A" 
    facts = [] 
    
    if 'agent' in alert: 
      facts.append({ 
       'name': 'Agent', 
           'value': "({0}) - {1}".format( 
            alert['agent']['id'], 
            alert['agent']['name'] 
          )}) 

    if 'agentless' in alert: 
        facts.append({ 
            'name': 'Agentless host', 
            'value': alert['agentless']['host'] 
        }) 

    facts.append({ 
        'name': 'Location', 
        'value': alert['location'] 
    }) 

    facts.append({ 
        'name': 'Rule ID', 
        'value': "{0} _(Level {1})_".format(alert['rule']['id'], level) 
    }) 
    facts.append({ 
        'name': 'Log', 
        'value': alert.get('full_log') 
    }) 
    sections.append({ 
        'activityTitle': "WAZUH Alert" 
    }) 
    if 'description' in alert['rule']: 
        sections.append({ 
            'title': alert['rule']['description'], 
        }) 
    sections.append({ 
        'facts': facts, 
        'markdown': 'true' 
    }) 
    msg['sections'] = sections 
    return json.dumps(msg) 

def send_msg(msg, url): 
    headers = {'content-type': 'application/json', 'Accept-Charset': 'UTF-8'} 
    res = requests.post(url, data=msg, headers=headers) 
    debug(res) 

if __name__ == "__main__": 
    try: 
        # Read arguments 
        bad_arguments = False 
        if len(sys.argv) >= 4: 
            msg = '{0} {1} {2} {3} {4}'.format( 
                now, 
                sys.argv[1], 
                sys.argv[2], 
                sys.argv[3], 
                sys.argv[4] if len(sys.argv) > 4 else '', 
            ) 

            debug_enabled = (len(sys.argv) > 4 and sys.argv[4] == 'debug') 
        else: 
            msg = '{0} Wrong arguments'.format(now) 
            bad_arguments = True 

        # Logging the call 
        f = open(log_file, 'a') 
        f.write(msg + '\n') 
        f.close() 
        if bad_arguments: 
            debug("# Exiting: Bad arguments.") 
            sys.exit(1) 

        # Main function 
        main(sys.argv) 
        except Exception as e: 
        debug(str(e)) 
        raise 

Change permissions and ownership for both the files

chmod 750 /var/ossec/integrations/custom-teams 
chown root:ossec /var/ossec/integrations/custom-teams 
chmod 750 /var/ossec/integrations/custom-teams.py 
chown root:ossec /var/ossec/integrations/custom-teams.py 

Login to your Invinsense Portal and open Wazuh wazuh_img

Open the Wazuh Manager’s ossec.conf. Go to Management > Configuration > Edit Configuration. wazuh_img wazuh_img wazuh_img

Add your copied Webhook URL in <hook_url> section and add the desired integration configuration block into it

<integration> 
    <name>custom-teams</name> 
    <hook_url>https://your-Webhook-URL</hook_url> 
    <rule_id>505,513,518,521,531,580,581,593,1002,5103,5901,5902,5903,2937,31320,82103,83001,22406,60109,60110,60111,60115,60117,60154,60159,60196,60893,60923,62126,62152,62165,62169,91589,100153,100151,100154,100211,100212,5108,40104,31115,30316,521,60111,5902,5720,5712,5701</rule_id> 
    <alert_format>json</alert_format> 
</integration> 

After the changes done in the configuration, the wazuh-manager’s service needs to be restarted. Save and restart the Wazuh manager from the Console. . Once the configuration is done you can see the alerts which are received in the Microsoft Teams. wazuh_img

Conclusion

With the help of the Wazuh’s Integrator tool, we are able to connect Wazuh with other external softwares. With the help of that, we can get the most important alerts directly to our tTeams channel where we can get notified and start taking actions immediately.


*

*

*

*