Integrating Mimecast with Wazuh

| by | Wazuh 4.7.4
Post icon

Mimecast is an email security and management platform that protects emails against cyber threats such as malware, phishing, and spam. Email remains one of the most prevalent attack vectors for cybercriminals seeking to infiltrate organizations and spread malware. Its widespread use makes it a top target for various malicious activities. Mimecast provides organizations with features like email filtering, data loss prevention (DLP), encryption, and archiving. 

Wazuh is a free and open source SIEM and XDR platform that offers protection against security threats for various platforms including virtualized, cloud, and containerized environments. 

In this blog post, we show the integration of Mimecast with Wazuh. This offers security analysts enhanced visibility into email-related threats by creating custom rules to alert on events of interest and show trends in certain malicious activities.

Requirements

We use the following infrastructure to demonstrate the integration of Mimecast with Wazuh:

  • Wazuh 4.7.4 central components (Wazuh server, Wazuh indexer, Wazuh dashboard) installed using the Quickstart guide on an Ubuntu server.
  • A Mimecast workspace and a user with administrator privileges to create API credentials.

Configuration

Follow the steps below to integrate Mimecast with Wazuh.

Mimecast web user interface

Enable logging

Follow the steps below to enable logging for your organization on your Mimecast account.

1. Login into the Mimecast administration console and navigate to Administration > Account > Account Settings to display the Account Settings page.

2. Select Enhanced Logging.

3. Select the types of logs you want to enable. The choices are:

  • Inbound – logs for messages from external senders to internal recipients.
  • Outbound – logs for messages from internal senders to external recipients.
  • Internal – logs for messages between internal domains.

4. Select Save to apply the changes.

Once these settings are saved, Mimecast will start logging email transactions in your organization.

Note: It might take up to 30 minutes before the logs become available.

Configuring authentication and authorization

Mimecast provides the /api/audit/get-siem-logs API endpoint to facilitate the download of logs. Follow the Mimecast documentation on Authentication (Scripts and Server Apps) to generate the following values required to authenticate to the /api/audit/get-siem-logs API endpoint:

  • APPLICATION_ID: The unique ID that identifies the application created in the Mimecast web user interface to pull logs from your account.
  • APPLICATION_KEY: A secret used to authenticate and authorize the created application.
  • ACCESS_KEY: The unique identifier that specifies the API user created in your mimecast account.
  • SECRET_KEY: This is a shared secret between the script and the Mimecast authorization server.

Wazuh server

The following steps describe how to create a script that retrieves logs from the Mimecast API endpoint. We also create custom decoders and rules to trigger alerts when events of interest are captured by Mimecast. 

Follow the steps below to configure the Wazuh server for log collection from Mimecast.

1. Create a Python script at /var/ossec/integrations/mimecast.py to connect to the Mimecast API and retrieve logs. The file /var/log/mimecast/mimecast.log is created upon execution of the script to store the retrieved Mimecast logs.

Replace the <APPLICATION_ID>, <APPLICATION_KEY>, <ACCESS_KEY>, <BASE_URL>, and <SECRET_KEY> variables with the appropriate values retrieved in configuring authentication and authorization above. 

The <BASE_URL> refers to your Mimecast region, for example, https://eu-api.mimecast.com for the EU region. Visit the Global Base URLs reference guide for a list of Mimecast URLs. 

Warning: We recommend you use a secret management solution to store the value of the variables instead of hardcoding them in the script.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#Mimecast SIEM logs download
import logging.handlers
import json
import os
import requests
import base64
import uuid
import datetime
import hashlib
import shutil
import hmac
import time
from zipfile import ZipFile
import io
from logging.handlers import RotatingFileHandler
 
# Set up variables
APP_ID = "<APPLICATION_ID>"
APP_KEY = "<APPLICATION_KEY>"
ACCESS_KEY = '<ACCESS_KEY>'
SECRET_KEY = '<SECRET_KEY>'
base_url = "<BASE_URL>"
LOG_FILE_PATH = "/var/log/mimecast/log"
CHK_POINT_DIR = '/var/log/mimecast'
URI = "/api/audit/get-siem-logs"
 
# delete files after fetching
delete_files = False
# Set threshold in number of files in log file directory
log_file_threshold = 105

# Set up logging
log = logging.getLogger(__name__)
log.root.setLevel(logging.DEBUG)
log_formatter = logging.Formatter('%(levelname)s %(message)s')
log_handler = logging.StreamHandler()
log_handler.setFormatter(log_formatter)
log.addHandler(log_handler)

# Supporting methods
def rotate_file_if_needed(file_path, max_size_bytes):
    if os.path.exists(file_path):
        file_size = os.path.getsize(file_path)
        if file_size >= max_size_bytes:
            base_dir = os.path.dirname(file_path)
            base_name = os.path.basename(file_path)
            file_name, file_ext = os.path.splitext(base_name)
            new_file_name = f"{file_name}_1{file_ext}"
            new_file_path = os.path.join(base_dir, new_file_name)
            shutil.move(file_path, new_file_path)
            log.info(f"Rotated file: {file_path} -> {new_file_path}")
    else:
        log.warning(f"File not found: {file_path}")

def get_hdr_date():
    return datetime.datetime.utcnow().strftime("%a, %d %b %Y %H:%M:%S UTC")

def read_file(file_name):
    try:
        with open(file_name, 'r') as f:
            data = f.read()
        return data
    except Exception as e:
        log.error('Error reading file ' + file_name + '. Cannot continue. Exception: ' + str(e))
        quit()

def write_file(file_name, data_to_write):
    if '.zip' in file_name:
        try:
            byte_content = io.BytesIO(data_to_write)
            zip_file = ZipFile(byte_content)
            zip_file.extractall(LOG_FILE_PATH)
        except Exception as e:
            log.error('Error writing file ' + file_name + '. Cannot continue. Exception: ' + str(e))
            quit()
    else:
        try:
            with open(file_name, 'w') as f:
                f.write(data_to_write)
        except Exception as e:
            log.error('Error writing file ' + file_name + '. Cannot continue. Exception: ' + str(e))
            quit()

def post_request(base_url, uri, post_body, access_key, secret_key):
    request_id = str(uuid.uuid4())
    request_date = get_hdr_date()

    unsigned_auth_header = '{date}:{req_id}:{uri}:{app_key}'.format(
        date=request_date,
        req_id=request_id,
        uri=uri,
        app_key=APP_KEY
    )
    hmac_sha1 = hmac.new(
        base64.b64decode(secret_key),
        unsigned_auth_header.encode(),
        digestmod=hashlib.sha1).digest()
    sig = base64.encodebytes(hmac_sha1).rstrip()
    headers = {
        'Authorization': 'MC ' + access_key + ':' + sig.decode(),
        'x-mc-app-id': APP_ID,
        'x-mc-date': request_date,
        'x-mc-req-id': request_id,
        'Content-Type': 'application/json'
    }

    try:
        log.debug('Sending request to ' + base_url + uri + ' with request Id: ' + request_id)
        r = requests.post(url=base_url + uri, data=json.dumps(post_body), headers=headers)

        if r.status_code == 429:
            log.warning('Rate limit hit. Sleeping for ' + str(r.headers['X-RateLimit-Reset'] * 1000))
            time.sleep(r.headers['X-RateLimit-Reset'] * 1000)
            r = requests.post(url=base_url + uri, data=json.dumps(post_body), headers=headers)

    except Exception as e:
        log.error('Unexpected error connecting to API. Exception: ' + str(e))
        return 'error'

    if r.status_code != 200:
        log.error('Request to ' + uri + ' with , request id: ' + request_id + ' returned with status code: ' +
                  str(r.status_code) + ', response body: ' + r.text)
        return 'error'

    return r.content, r.headers

def get_mta_siem_logs(checkpoint_dir, base_url, access_key, secret_key, now):
    uri = "/api/audit/get-siem-logs"
    checkpoint_filename = os.path.join(checkpoint_dir, 'get_mta_siem_logs_checkpoint')
    post_body = dict()
    post_body['data'] = [{}]
    post_body['data'][0]['type'] = 'MTA'
    post_body['data'][0]['compress'] = True
    if os.path.exists(checkpoint_filename):
        post_body['data'][0]['token'] = read_file(checkpoint_filename)

    resp = post_request(base_url, uri, post_body, access_key, secret_key)

    if resp != 'error':
        resp_body = resp[0]
        resp_headers = resp[1]
        content_type = resp_headers['Content-Type']

        if content_type == 'application/json':
            log.info('No more logs available')
            return False
        elif content_type == 'application/octet-stream':
            file_name = resp_headers['Content-Disposition'].split('=\"')
            file_name = file_name[1][:-1]

            write_file(os.path.join(LOG_FILE_PATH, file_name), resp_body)
            write_file(checkpoint_filename, resp_headers['mc-siem-token'])

            return True
        else:
            log.error('Unexpected response')
            for header in resp_headers:
                log.error(header)
            return False

def run_script():
    try:
        log.info('Getting MTA log data')

        # Rotate merged log file if needed before downloading new logs
        rotate_file_if_needed(os.path.join(LOG_FILE_PATH, 'mimecast.log'), 5242880)

        get_mta_siem_logs(checkpoint_dir=CHK_POINT_DIR, base_url=base_url, access_key=ACCESS_KEY,
                          secret_key=SECRET_KEY, now=time.time())
    except Exception as e:
        log.error('Unexpected error getting MTA logs ' + str(e))

    files = [os.path.join(LOG_FILE_PATH, f) for f in os.listdir(LOG_FILE_PATH)]
    files.sort(key=os.path.getctime)

    if delete_files or len(files) >= log_file_threshold:
        num_files_to_delete = len(files) - log_file_threshold
        for i in range(num_files_to_delete):
            try:
                os.unlink(files[i])
            except Exception as e:
                log.error('Failed to delete file {}. Reason: {}'.format(files[i], e))
                continue

    merged_file_path = os.path.join(LOG_FILE_PATH, 'mimecast.log')
    with open(merged_file_path, 'a') as merged_file:
        for filename in os.listdir(LOG_FILE_PATH):
            if filename.endswith('.siem') and filename != 'mimecast.log':
                file_path = os.path.join(LOG_FILE_PATH, filename)
                try:
                    with open(file_path, 'r') as log_file:
                        for line in log_file:
                            merged_file.write(line)
                except Exception as e:
                    log.error('Failed to merge log file {}. Reason: {}'.format(file_path, e))

                os.remove(file_path)

run_script()

2. Set the ownership and permissions for the /var/ossec/integrations/mimecast.py file. This ensures the script is only available to the root user and wazuh group by granting them full permissions, read and execute access respectively:

# chown root:wazuh /var/ossec/integrations/mimecast.py
# chmod 750 /var/ossec/integrations/mimecast.py

3. Add the following configuration to the <ossec_config> block of the  /var/ossec/etc/ossec.conf local configuration file to automate the execution of the script. We also configure the Wazuh Logcollector module to monitor the /var/log/mimecast/log/mimecast.log file for new logs:

<wodle name="command">
  <disabled>no</disabled>
  <command>/var/ossec/framework/python/bin/python3 /var/ossec/integrations/mimecast.py</command>
  <interval>5m</interval>
  <ignore_output>yes</ignore_output>
  <run_on_start>yes</run_on_start>
  <timeout>120</timeout>
</wodle>

<localfile>
  <log_format>syslog</log_format>
  <location>/var/log/mimecast/log/mimecast.log</location>
</localfile>

Note: The <interval> tag specifies the interval between execution of the script.

If you already have logs enabled on your Mimecast account, the script will retrieve the earliest available logs on your account. Mimecast keeps your logs for up to 7 days.

Custom decoders

1. Create a decoder file /var/ossec/etc/decoders/mimecast_decoders.xml and add the following decoders:

<!--
  Mimecast SIEM Logs
-->

<decoder name="mimecast">
  <prematch type="pcre2">datetime=.*\|aCode=</prematch>
</decoder>

<decoder name="mimecast">
  <prematch type="pcre2">datetime=.*\|acc=</prematch>
</decoder>

<decoder name="mimecast">
  <prematch type="pcre2">datetime=.*Mimecast</prematch>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)datetime=(?:\\)?([^|]+)</regex>
  <order>mimecast.datetime</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)aCode=(?:\\)?([^|]+)</regex>
  <order>mimecast.aCode</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)acc=(?:\\)?([^|]+)</regex>
  <order>mimecast.acc</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)IP=(?:\\)?([^|]+)</regex>
  <order>mimecast.IP</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)RejType=(?:\\)?([^|]+)</regex>
  <order>mimecast.RejType</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)Error=(?:\\)?([^|]+)</regex>
  <order>mimecast.Error</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)RejCode=(?:\\)?([^|]+)</regex>
  <order>mimecast.RejCode</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)Dir=(?:\\)?([^|]+)</regex>
  <order>mimecast.Dir</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)headerFrom=(?:\\)?([^|]+)</regex>
  <order>mimecast.headerFrom</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)Sender=(?:\\)?([^|]+)</regex>
  <order>mimecast.Sender</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)Rcpt=(?:\\)?([^|]+)</regex>
  <order>mimecast.Rcpt</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)Act=(?:\\)?([^|]+)</regex>
  <order>mimecast.Act</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)RejInfo=(?:\\)?([^|]+)</regex>
  <order>mimecast.RejInfo</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)TlsVer=(?:\\)?([^|]+)</regex>
  <order>mimecast.TlsVer</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)Cphr=(?:\\)?([^|]+)</regex>
  <order>mimecast.Cphr</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)Hld=(?:\\)?([^|]+)</regex>
  <order>mimecast.Hld</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)AttSize=(?:\\)?([^|]+)</regex>
  <order>mimecast.AttSize</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)IPNewDomain=(?:\\)?([^|]+)</regex>
  <order>mimecast.IPNewDomain</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)IPReplyMismatch=(?:\\)?([^|]+)</regex>
  <order>mimecast.IPReplyMismatch</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)AttCnt=(?:\\)?([^|]+)</regex>
  <order>mimecast.AttCnt</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)IPInternalName=(?:\\)?([^|]+)</regex>
  <order>mimecast.IPInternalName</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)AttNames=(?:\\)?([^|]+)</regex>
  <order>mimecast.AttNames</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)MsgSize=(?:\\)?([^|]+)</regex>
  <order>mimecast.MsgSize</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)MsgId=(?:\\)?([^|]+)</regex>
  <order>mimecast.MsgId</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)IPThreadDict=(?:\\)?([^|]+)</regex>
  <order>mimecast.IPThreadDict</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)IPSimilarDomain=(?:\\)?([^|]+)</regex>
  <order>mimecast.IPSimilarDomain</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)Delivered=(?:\\)?([^|]+)</regex>
  <order>mimecast.Delivered</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)Err=(?:\\)?([^|]+)</regex>
  <order>mimecast.Err</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)ReceiptAck=(?:\\)?([^|]+)</regex>
  <order>mimecast.ReceiptAck</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)Subject=(?:\\)?([^|]+)</regex>
  <order>mimecast.Subject</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)Attempt=(?:\\)?([^|]+)</regex>
  <order>mimecast.Attempt</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)Snt=(?:\\)?([^|]+)</regex>
  <order>mimecast.Snt</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)UseTls=(?:\\)?([^|]+)</regex>
  <order>mimecast.UseTls</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)MimecastIP=(?:\\)?([^|]+)</regex>
  <order>mimecast.MimecastIP</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)fileName=(?:\\)?([^|]+)</regex>
  <order>mimecast.fileName</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)sha256=(?:\\)?([^|]+)</regex>
  <order>mimecast.sha256</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)Size=(?:\\)?([^|]+)</regex>
  <order>mimecast.Size</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)Recipient=(?:\\)?([^|]+)</regex>
  <order>mimecast.Recipient</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)SenderDomain=(?:\\)?([^|]+)</regex>
  <order>mimecast.SenderDomain</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)fileExt=(?:\\)?([^|]+)</regex>
  <order>mimecast.fileExt</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)Virus=(?:\\)?([^|]+)</regex>
  <order>mimecast.Virus</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)sha1=(?:\\)?([^|]+)</regex>
  <order>mimecast.sha1</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)SenderDomainInternal=(?:\\)?([^|]+)</regex>
  <order>mimecast.SenderDomainInternal</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)fileMime=(?:\\)?([^|]+)</regex>
  <order>mimecast.fileMime</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)CustomerIP=(?:\\)?([^|]+)</regex>
  <order>mimecast.CustomerIP</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)Route=(?:\\)?([^|]+)</regex>
  <order>mimecast.Route</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)md5=(?:\\)?([^|]+)</regex>
  <order>mimecast.md5</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)SourceIP=(?:\\)?([^|]+)</regex>
  <order>mimecast.SourceIP</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)UrlCategory=(?:\\)?([^|]+)</regex>
  <order>mimecast.UrlCategory</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)ScanResultInfo=(?:\\)?([^|]+)</regex>
  <order>mimecast.ScanResultInfo</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)URL=(?:\\)?([^|]+)</regex>
  <order>mimecast.URL</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)Receipient=(?:\\)?([^|]+)</regex>
  <order>mimecast.Receipient</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)Definition=(?:\\)?([^|]+)</regex>
  <order>mimecast.Definition</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)Hits=(?:\\)?([^|]+)</regex>
  <order>mimecast.Hits</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)Action=(?:\\)?([^|]+)</regex>
  <order>mimecast.Action</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)TaggedExternal=(?:\\)?([^|]+)</regex>
  <order>mimecast.TaggedExternal</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)TaggedMalicious=(?:\\)?([^|]+)</regex>
  <order>mimecast.TaggedMalicious</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)InternalName=(?:\\)?([^|]+)</regex>
  <order>mimecast.InternalName</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)CustomName=(?:\\)?([^|]+)</regex>
  <order>mimecast.CustomName</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)NewDomain=(?:\\)?([^|]+)</regex>
  <order>mimecast.NewDomain</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)SimilarInternalDomain=(?:\\)?([^|]+)</regex>
  <order>mimecast.SimilarInternalDomain</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)SimilarCustomExternalDomain=(?:\\)?([^|]+)</regex>
  <order>mimecast.SimilarCustomExternalDomain</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)SimilarMimecastExternalDomain=(?:\\)?([^|]+)</regex>
  <order>mimecast.SimilarMimecastExternalDomain</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)ReplyMismatch=(?:\\)?([^|]+)</regex>
  <order>mimecast.ReplyMismatch</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)ThreatDictionary=(?:\\)?([^|]+)</regex>
  <order>mimecast.ThreatDictionary</order>
</decoder>

<decoder name="mimecast-siem-logs">
  <parent>mimecast</parent>
  <regex type="pcre2">(?i)reason=(?:\\)?([^|]+)</regex>
  <order>mimecast.reason</order>
</decoder>

Custom rules

1. Create a rule file /var/ossec/etc/rules/mimecast_rules.xml and add the following rules:

<!-- Rules for Mimecast via API -->

<!-- Mimecast SIEM Logs -->

<!-- Mimecast logs grouped -->
<group name="mimecast">

  <rule id="106000" level="2">
    <field name="mimecast.datetime">\.</field>
    <description>Logs from Mimecast.</description>
  </rule>
  
  <!-- Rule to detect mail held for admin review -->
  <rule id="106001" level="5">
    <if_sid>106000</if_sid>
    <field name="mimecast.Act">Hld</field>
    <description>Message $(mimecast.MsgId) held for review.</description>
    <mitre>
     <id>T1566</id>
    </mitre>
  </rule>
  
  <!-- Rule to detect AV logs -->
  <rule id="106002" level="5">
    <if_sid>106000</if_sid>
    <field name="mimecast.Virus">\.</field>
    <field name="mimecast.Subject">\.</field>
    <description>Malicious email with category "$(mimecast.Virus)" sent to $(mimecast.Recipient).</description>
    <mitre>
     <id>T1598.003</id>
	 <id>T1114</id>
    </mitre>
  </rule>
  
  <rule id="106003" level="10" frequency="3" timeframe="300">
    <if_matched_sid>106002</if_matched_sid>
    <same_field>mimecast.Sender</same_field>
    <description>Multiple malicious emails received from same sender $(mimecast.Sender).</description>
    <mitre>
     <id>T1598.003</id>
	 <id>T1114</id>
    </mitre>
  </rule>
  
  <rule id="106004" level="10" frequency="5" timeframe="300">
    <if_matched_sid>106002</if_matched_sid>
    <same_field>mimecast.Sender</same_field>
    <different_field>mimecast.Recipient</different_field>
    <description>Malicious emails received by multiple users from same sender $(mimecast.Sender).</description>
    <mitre>
     <id>T1598.003</id>
	 <id>T1114</id>
    </mitre>
  </rule>
  
  <!-- Rules to detect Target Threat Protection - Internal Email Protect logs -->
  <rule id="106005" level="3">
    <if_sid>106000</if_sid>
    <field name="mimecast.ScanResultInfo">Blocked</field>
    <description>User $(mimecast.Recipient) blocked from clicking a malicious/blocked link $(URL).</description>
    <mitre>
     <id>T1566.002</id>
	 <id>T1114</id>
    </mitre>
  </rule>
  
  <rule id="106006" level="10" frequency="3" timeframe="300">
    <if_matched_sid>106005</if_matched_sid>
    <different_field>mimecast.Recipient</different_field>
    <same_field>mimecast.URL</same_field>
    <description>Multiple users trying to access same malicious/blocked link $(URL).</description>
    <mitre>
     <id>T1566.002</id>
	 <id>T1114</id>
    </mitre>
  </rule>
  
  <!-- Rules to detect Targeted Threat Protection - Impersonation Protect logs -->
  <rule id="106007" level="5">
    <if_sid>106000</if_sid>
    <field name="mimecast.Definition">Impersonation</field>
    <description>Internal user $(mimecast.Sender) has been impersonated.</description>
    <mitre>
     <id>T1036</id>
	 <id>T1586.002</id>
	 <id>T1566</id>
	 <id>T1114</id>
    </mitre>
  </rule>
  
  <rule id="106008" level="5">
    <if_sid>106007</if_sid>
    <field name="mimecast.ReplyMismatch">true</field>
    <description>Reply address mismatch: Reply address does not correspond to the senders address.</description>
    <mitre>
     <id>T1036</id>
	 <id>T1586.002</id>
	 <id>T1566</id>
	 <id>T1114</id>
    </mitre>
  </rule>
 
  <!-- Rules to detect Targeted Threat Protection - URL Protect logs --> 
  <rule id="106009" level="4">
    <if_sid>106000</if_sid>
    <field name="mimecast.reason">malicious</field>
    <field name="mimecast.URL">\.</field>
    <field name="mimecast.urlCategory">Blocked</field>
    <description>User $(mimecast.Recipient) blocked from clicking a malicious link $(URL).</description>
    <mitre>
     <id>T1566.002</id>
	 <id>T1114</id>
    </mitre>
  </rule>
  
  <rule id="106010" level="10" frequency="3" timeframe="300">
    <if_matched_sid>106009</if_matched_sid>
    <different_field>mimecast.Recipient</different_field>
    <description>Multiple users trying to access same malicious/blocked link $(URL).</description>
    <mitre>
     <id>T1566.002</id>
	 <id>T1114</id>
    </mitre>
  </rule>
  
  <!-- Rule to detect Targeted Threat Protection - Attachment Protect logs --> 
  <rule id="106011" level="10">
    <if_sid>106000</if_sid>
    <field name="mimecast.fileName">\.</field>
    <field name="mimecast.fileMime">\.</field>
    <field name="mimecast.sha256">\.</field>
    <match negate="yes">Subject</match>
    <description>Mimecast sandbox has detected a potentially malicious file $(mimecast.fileName).$(mimecast.fileExt) on $(mimecast.Recipient).</description>
    <mitre>
     <id>T1204.002</id>
	 <id>T1598.002</id>
	 <id>T1566.001</id>
	 <id>T1114</id>
    </mitre>
  </rule>
  
  <rule id="106012" level="10" frequency="3" timeframe="300">
    <if_matched_sid>106011</if_matched_sid>
    <same_field>mimecast.Sender</same_field>
    <description>Multiple potentially malicious files received from same sender $(mimecast.Sender).</description>
    <mitre>
     <id>T1204.002</id>
	 <id>T1598.002</id>
	 <id>T1566.001</id>
	 <id>T1114</id>
    </mitre>
  </rule>
  
  <rule id="106013" level="10" frequency="3" timeframe="300">
    <if_matched_sid>106011</if_matched_sid>
    <same_field>mimecast.IP</same_field>
    <description>Multiple potentially malicious files received from same source $(mimecast.IP).</description>
    <mitre>
     <id>T1204.002</id>
	 <id>T1598.002</id>
	 <id>T1566.001</id>
	 <id>T1114</id>
    </mitre>
  </rule>

  <rule id="106014" level="10" frequency="4" timeframe="300">
    <if_matched_sid>106011</if_matched_sid>
    <same_field>mimecast.sha256</same_field>
    <description>Same potentially malicious file $(mimecast.fileName) detected on multiple recipients.</description>
    <mitre>
     <id>T1204.002</id>
	 <id>T1598.002</id>
	 <id>T1566.001</id>
	 <id>T1114</id>
    </mitre>
  </rule>
  
  <rule id="106015" level="10" frequency="3" timeframe="300">
    <if_matched_sid>106011</if_matched_sid>
    <same_field>mimecast.SenderDomain</same_field>
    <description>Multiple potentially malicious files received from same domain.</description>
    <mitre>
     <id>T1204.002</id>
	 <id>T1598.002</id>
	 <id>T1566.001</id>
	 <id>T1114</id>
    </mitre>
  </rule>

</group>

Where:

  • Rule ID 100600 groups all Mimecast events.
  • Rule ID 106001 detects emails held for admin review.
  • Rule IDs 106002, 106003, and 106004 detect email antivirus events.
  • Rule IDs 106005 and 106006 detect internal email protection events.
  • Rule IDs 106007 and 106008 detect impersonation protection events.
  • Rule IDs 106009 and 106010 detect URL protection events.
  • Rule IDs 106011, 106012, 106013, 106014, and 106015  detect email attachment protection events.

2. Restart the Wazuh manager to apply the changes:

# systemctl restart wazuh-manager

Visualizing alerts

When a Mimecast event that matches any of our custom rules occurs, we see alerts on the Wazuh dashboard. Navigate to Modules > Security events to view the alerts.

Mimecast alert detecting potentially malicious email
Figure 1: Alert detecting a potentially malicious email attachment.
Alert detecting email pending
Figure 2: Alert detecting an email pending an admin’s review.
Alert detecting email user being impersonated
Figure 3: Alert detecting an email user being impersonated.
Mimecast alert detecting multiple users trying to access the same malicious URL
Figure 4: Alert detecting multiple users trying to access the same malicious URL.
Mimecast alert detecting malicious file
Figure 5: Alert detecting the same malicious file found on multiple recipients.

Conclusion

In this blog post, we demonstrated how to integrate Mimecast with Wazuh. This integration helps to streamline your security monitoring activities by offering a unified view of your email security events. It also gives administrators and security analysts deeper insights into email security events.

Wazuh is a free and open source SIEM and XDR solution. Wazuh can be deployed and managed on-premises or on the Wazuh cloud. Check out our community for support and updates.

References