Augmenting Wazuh with ChatGPT integration

Step-By-Step

ยท

5 min read

When it comes to cyber security, ChatGPT is a very productive tool that can perform various tasks in Blue Teaming (and Red Teaming) such as threat intelligence, report generation, and more. Using ChatGPT API, you can receive suggestions on handling security investigations, threat hunting, or summarizing security issues.

These suggestions are based on the vast knowledge and information that GPT has been trained on, making it a valuable asset for anyone dealing with security alerts. Whether you are a security analyst, engineer, or administrator, having the ability to quickly and effectively respond to security incidents can greatly improve your overall security posture. Incorporating ChatGPT into your security toolkit can simplify the process and provide you with a more comprehensive solution to address security alerts.

In this article, we will learn step-by-step how to configure Wazuh to communicate with the ChatGPT API.

Step 1

We need to create a rule that generates an alert when a non-private IP has attempted to log into our server. This allows us to distinguish malicious insiders and those attempting to gain access from outside the network.

Open the Wazuh manager local rules file /var/ossec/etc/rules/local_rules.xml and add the below block:

<!-- User Failed Authentication from Public IPv4 -->
<group name="local,syslog,sshd,">
 <rule id="100004" level="10">
    <if_sid>5760</if_sid>
    <match type="pcre2">\b(?!(10)|192\.168|172\.(2[0-9]|1[6-9]|3[0-1])|(25[6-9]|2[6-9][0-9]|[3-9][0-9][0-9]|99[1-9]))[0-9]{1,3}\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)</match>
    <description>sshd: Authentication failed from a public IP address > $(srcip).</description>
    <group>authentication_failed,pci_dss_10.2.4,pci_dss_10.2.5,</group>
  </rule>
</group>

The <match></match> block of the rule specifies that we want to perform a REGEX search to "detect" an IP address within the log.

Step 2

The below Python script takes the source IP that triggered our rule and sends it to the ChatGPT endpoint to get IPโ€™s information and insights.

#!/var/ossec/framework/python/bin/python3
# Copyright (C) 2015-2022, Wazuh Inc.
# ChatGPT Integration template by @WhatDoesKmean

import json
import sys
import time
import os
from socket import socket, AF_UNIX, SOCK_DGRAM

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

# Global vars
debug_enabled = False
pwd = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))

print(pwd)
#exit()

json_alert = {}
now = time.strftime("%a %b %d %H:%M:%S %Z %Y")
# Set paths
log_file = '{0}/logs/integrations.log'.format(pwd)
socket_addr = '{0}/queue/sockets/queue'.format(pwd)

def main(args):
    debug("# Starting")
    # Read args
    alert_file_location = args[1]
    apikey = args[2]
    debug("# API Key")
    debug(apikey)
    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)

    # Request chatgpt info
    msg = request_chatgpt_info(json_alert,apikey)
    # If positive match, send event to Wazuh Manager
    if msg:
        send_event(msg, json_alert["agent"])

def debug(msg):
    if debug_enabled:
        msg = "{0}: {1}\n".format(now, msg)
    print(msg)
    f = open(log_file,"a")
    f.write(str(msg))
    f.close()


def collect(data):
  srcip = data['srcip']
  choices = data['content']
  return srcip, choices


def in_database(data, srcip):
  result = data['srcip']
  if result == 0:
    return False
  return True


def query_api(srcip, apikey):
  # Calling ChatGPT API Endpoint
  headers = {
        'Authorization': 'Bearer ' + apikey,
        'Content-Type': 'application/json',
    }

  json_data = {
        'model': 'gpt-3.5-turbo',
        'messages': [
            {
                'role': 'user',
                'content': 'Give me more data about this IP: ' + srcip,
            },
        ],
    }

  response = requests.post('https://api.openai.com/v1/chat/completions', headers=headers, json=json_data)

  if response.status_code == 200:
      # Create new JSON to add the IP
      ip = {"srcip": srcip}
      new_json = {}
      new_json = response.json()["choices"][0]["message"]
      new_json.update(ip)
      json_response = new_json

      data = json_response
      return data
  else:
      alert_output = {}
      alert_output["chatgpt"] = {}
      alert_output["integration"] = "custom-chatgpt"
      json_response = response.json()
      debug("# Error: The chatgpt encountered an error")
      alert_output["chatgpt"]["error"] = response.status_code
      alert_output["chatgpt"]["description"] = json_response["errors"][0]["detail"]
      send_event(alert_output)
      exit(0)


def request_chatgpt_info(alert, apikey):
    alert_output = {}
    # If there is no source ip address present in the alert. Exit.
    if not "srcip" in alert["data"]:
        return(0)

    # Request info using chatgpt API
    data = query_api(alert["data"]["srcip"], apikey)
    # Create alert
    alert_output["chatgpt"] = {}
    alert_output["integration"] = "custom-chatgpt"
    alert_output["chatgpt"]["found"] = 0
    alert_output["chatgpt"]["source"] = {}
    alert_output["chatgpt"]["source"]["alert_id"] = alert["id"]
    alert_output["chatgpt"]["source"]["rule"] = alert["rule"]["id"]
    alert_output["chatgpt"]["source"]["description"] = alert["rule"]["description"]
    alert_output["chatgpt"]["source"]["full_log"] = alert["full_log"]
    alert_output["chatgpt"]["source"]["srcip"] = alert["data"]["srcip"]
    srcip = alert["data"]["srcip"]

    # Check if chatgpt has any info about the srcip
    if in_database(data, srcip):
      alert_output["chatgpt"]["found"] = 1
    # Info about the IP found in chatgpt
    if alert_output["chatgpt"]["found"] == 1:
        srcip, choices = collect(data)

        # Populate JSON Output object with chatgpt request
        alert_output["chatgpt"]["srcip"] = srcip
        alert_output["chatgpt"]["choices"] = choices

        debug(alert_output)

    return(alert_output)


def send_event(msg, agent = None):
    if not agent or agent["id"] == "000":
        string = '1:chatgpt:{0}'.format(json.dumps(msg))
    else:
        string = '1:[{0}] ({1}) {2}->chatgpt:{3}'.format(agent["id"], agent["name"], agent["ip"] if "ip" in agent else "any", json.dumps(msg))

    debug(string)
    sock = socket(AF_UNIX, SOCK_DGRAM)
    sock.connect(socket_addr)
    sock.send(string.encode())
    sock.close()


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(str(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

This script will be saved in the /var/ossec/integrations/ path of the Wazuh Manager as custom-chatgpt.py

The file execution permissions can be changed by the chmod command. Also, don't forget to use the chown command to change the file ownership as well.

In my case:
chmod 750 /var/ossec/integrations/custom-chatgpt.py
chown root:wazuh /var/ossec/integrations/custom-chatgpt.py

Step 3

Now it's time to update the Wazuh manager configuration file /var/ossec/etc/ossec.conf using the integration block below:

<!-- ChatGPT Integration -->
  <integration>
    <name>custom-chatgpt.py</name>
    <hook_url>https://api.openai.com/v1/chat/completions</hook_url>
    <api_key>YOUR-OWN-API-KEY</api_key>
    <level>10</level>
    <rule_id>100004</rule_id>
    <alert_format>json</alert_format>
  </integration>

This instructs the Wazuh Manager to call the ChatGPT API endpoint anytime our rule id (100004), is triggered. You need to replace the <api_key> block with your own.
Register for a free API key at https://platform.openai.com/signup.

Step 4

Now, we need to capture the response sent back to the Wazuh Manager so we can observe the information gathered by our ChatGPT integration.

Open the Wazuh Manager local rules file at /var/ossec/etc/rules/local_rules.xml and add the block below:

<group name="local,syslog,sshd,">
  <rule id="100007" level="10">
    <field name="chatgpt.srcip">\.+</field>
    <description>IP address $(chatgpt.srcip) trying to connect to the network.</description>
    <group>authentication_failed,pci_dss_10.2.4,pci_dss_10.2.5,</group>
  </rule>
</group>

Lastly, restart the Wazuh Manager.

Now, you know! ๐Ÿ˜‰

ย