Nmap (network mapper) is an open source security scanner used for network exploration and security auditing. It identifies endpoints and services within a network and provides a comprehensive network map. The network mapper is commonly referred to as the Swiss army knife of networking due to its many interesting capabilities to gather information from endpoints on a network.

ChatGPT is an artificial intelligence (AI) powered language model based on the GPT-4 (generative pre-trained transformer) architecture. It is a chatbot designed to generate human-like text based on the input it receives. This AI engine has been trained on diverse data and can provide constructive responses to questions. ChatGPT is a useful tool in various disciplines, including cybersecurity. The tool provides an API to obtain suggestions for effectively managing security audits, conducting threat hunting, or summarizing security issues.

Nmap and ChatGPT are resourceful tools that can improve the security posture of organizations when used correctly. In this blog post, we show how Wazuh utilizes the resources provided by these tools to improve your organization’s security posture.

Infrastructure

To demonstrate integrating Nmap and ChatGPT with Wazuh, we use the following infrastructure.

  • A ready-to-use Wazuh OVA 4.4.5. Follow this guide to download the virtual machine.
  • An Ubuntu 22.04 LTS endpoint with Wazuh agent 4.4.5 installed. To install the Wazuh agent, refer to the following installation guide.
  • A Windows 11 endpoint with Wazuh agent 4.4.5 installed. To install the Wazuh agent, refer to the following installation guide.

Configuration

We use the Wazuh command monitoring capability combined with Nmap to periodically query the endpoints’ open port services.

The Wazuh command monitoring module allows you to execute specified commands on monitored endpoints, providing a way to gather important information or perform scheduled tasks. The output generated by these commands is captured as log data. You can analyze this data to identify potential security threats or gain valuable insights into the behavior of your network.

Nmap integration

In this section, we run an Nmap scan using Python to provide information about open ports on a Windows and Ubuntu endpoint.

Nmap script

We created a Python script to perform network scans on an endpoint. The script extracts information such as hostnames, protocols, and open ports.

#!/var/ossec/framework/python/bin/python3
# Copyright (C) 2015-2023, Wazuh Inc.

import nmap
import time
import json
import platform

# The function to perform network scan on a host endpoint
def scan_subnet(subnet):
    nm = nmap.PortScanner()
    nm.scan(subnet)
    results = []

    for host in nm.all_hosts():
        for proto in nm[host].all_protocols():
            if proto not in ["tcp", "udp"]:
                continue

            lport = list(nm[host][proto].keys())
            lport.sort()
# Iterate over each port for the current host and protocol
            for port in lport:
                hostname = ""
                json_output = {
                    'nmap_host': host,
                    'nmap_protocol': proto,
                    'nmap_port': port,
                    'nmap_hostname': "",
                    'nmap_hostname_type': "",
                    'nmap_port_name': "",
                    'nmap_port_state': "",
                    'nmap_port_service': ""
                }
# Get the first hostname and it’s type
                if nm[host]["hostnames"]:
                    hostname = nm[host]["hostnames"][0]["name"]
                    hostname_type = nm[host]["hostnames"][0]["type"]
                    json_output['nmap_hostname'] = hostname
                    json_output['nmap_hostname_type'] = hostname_type
# Get the port name if available
                if 'name' in nm[host][proto][port]:
                    json_output['nmap_port_name'] = nm[host][proto][port]['name']
# Get the port state if available
                if 'state' in nm[host][proto][port]:
                    json_output['nmap_port_state'] = nm[host][proto][port]['state']
# Get the port service version if available
                if 'product' in nm[host][proto][port] and 'version' in nm[host][proto][port]:
                    service = nm[host][proto][port]['product'] + " " + nm[host][proto][port]['version']
                    json_output['nmap_port_service'] = service

                results.append(json_output)
    return results

# The function to append the scan results to the active response log file
def append_to_log(results, log_file):
    with open(log_file, "a") as active_response_log:
        for result in results:
            active_response_log.write(json.dumps(result))
            active_response_log.write("\n")
# Specify the address(es) to scan
subnets = ['127.0.0.1']
# path of the log file
if platform.system() == 'Windows':
	log_file = "C:\Program Files (x86)\ossec-agent\active-response\active-responses.log"
elif platform.system() == 'Linux':
	log_file = "/var/ossec/logs/active-responses.log"
else:
	log_file = "/Library/Ossec/logs/active-responses.log"

for subnet in subnets:
    results = scan_subnet(subnet)
    append_to_log(results, log_file)
    time.sleep(2)

Note: You can use the centralized configuration to distribute this setting across multiple monitored endpoints. Please note that remote commands are disabled by default for security reasons and must be explicitly enabled on each agent.

Ubuntu endpoint

Install the following packages to run an Nmap scan using Python on Ubuntu.

1. Install python3 and python3-pip from the APT repository by running the command below:

$ sudo apt-get update && sudo apt-get install python3
$ sudo apt-get install python3-pip

2. Install Nmap and the python-nmap library. The python-nmap library provides many options for customizing Nmap scans:

$ sudo apt-get install nmap
$ sudo pip3 install python-nmap

Take the following steps to configure the Wazuh command monitoring module. 

1. Create a ~/Documents/nmapscan.py file and copy the content of Nmap script to it.

2. Edit the Wazuh agent /var/ossec/etc/ossec.conf file and add the following command monitoring configuration within the <ossec_config> block:

<!-- Run nmap python script -->
  <localfile>
    <log_format>full_command</log_format>
    <command>python3 /home/<USERNAME>/Documents/nmapscan.py</command>
    <frequency>604800</frequency>
  </localfile>

Replace <USERNAME> placeholder with the name of the user account on the endpoint.

3. Restart the Wazuh agent to apply this change:

$ sudo systemctl restart wazuh-agent

Windows endpoint

Install the following packages to run an Nmap scan using Python on Windows.

Note: Administrator privileges are required to perform the installation

1. Python v 3.8.7 or later (with pip pre-installed). Check the following boxes when prompted:

  • Install launcher for all users (recommended).
  • Add Python to PATH.

2. Microsoft Visual C++ 2015 Redistributable.

3. Nmap v7.94 or later. Ensure to add Nmap to PATH.

4. Run the command below to install the python-nmap library and all its dependencies using Powershell:

> pip3 install python-nmap

Follow the steps below to configure the Nmap port scanner using Python after installing the above packages:

1. Create a C:\Users\<USERNAME>\Documents\nmapscan.py file and copy the content of Nmap script to it.

2. Convert the Nmap script to an executable application. Open an administrator PowerShell terminal and use pip to install pyinstaller:

> pip install pyinstaller
> pyinstaller --version

3. Create the executable file using pyinstaller:

> pyinstaller -F <PATH_TO_NMAPSCAN.PY>

You can find the created nmapscan.exe executable in the C:\Users\<USERNAME>\dist\ directory.

4. Copy the nmapscan.exe executable file to C:\Users\<USERNAME>\Documents\nmapscan.exe directory.

5. Edit the Wazuh agent C:\Program Files (x86)\ossec-agent\ossec.conf file and add the following command monitoring configuration within the <ossec_config> block:

<!-- Run nmap python script -->
  <localfile>
    <log_format>full_command</log_format>
    <command>C:\Users\<USERNAME>\Documents\nmapscan.exe</command>
    <frequency>604800</frequency>
  </localfile>

Replace <USERNAME> placeholder with the name of the user account on the endpoint.

6. Restart the Wazuh agent using PowerShell for the changes to take effect:

> Restart-Service -Name wazuh

Wazuh server

In this section, we create a rule to capture the result of the Nmap scan on the monitored endpoint. 

1. Add the rule below to the /var/ossec/etc/rules/local_rules.xml file:

<group name="linux,nmap,">
  <rule id="100100" level="3">
    <decoded_as>json</decoded_as>
    <field name="nmap_port">\.+</field>
    <field name="nmap_port_service">\.+</field>
      <description>NMAP: Host scan. Port $(nmap_port) is open and hosting the $(nmap_port_service) service.</description>
    <options>no_full_log</options>
  </rule>
</group>

Where:

  • Rule ID 100100 is triggered after a successful Nmap scan on the monitored endpoint.

2. Restart the Wazuh manager to apply the configuration changes:

$  sudo systemctl restart wazuh-manager

Scan results

The images below show the alerts generated on the Wazuh dashboard when we perform Nmap scans on the Ubuntu and Windows endpoint.

Navigate to the Security events tab to view the generated alerts.

Ubuntu results

Ubuntu results

Windows results

Windows results

ChatGPT integration

The Wazuh ChatGPT integration is a configuration that allows Wazuh to communicate with the ChatGPT AI engine. Users can enhance security monitoring and incident response capabilities by using the power of natural language processing. By integrating Wazuh with ChatGPT, we can create a chatbot interface that interacts with the Wazuh platform to perform security-related tasks or provide more information.

In this section, we use ChatGPT to provide information about open ports scanned by Nmap.

Wazuh server

1. Add the below custom rule to the /var/ossec/etc/rules/local_rules.xml file.

<group name="linux,chat_gpt">
  <rule id="100101" level="5">
    <if_sid>100100</if_sid>
    <field name="nmap_port">\d+</field>
      <description>NMAP: Host scan. Port $(nmap_port) is open.</description>
    </rule>

  <rule id="100103" level="5">
    <if_sid>100100</if_sid>
    <field name="nmap_port_service">^\s$</field>
      <description>NMAP: Port $(nmap_port) is open but no service is found.</description>
    </rule>
</group>

Where:

  • Rule ID 100101 is triggered after a successful Nmap scan on the monitored endpoint with the condition that there are one or more open ports with a found service.
  • Rule ID 100103 is triggered after a successful Nmap scan on the monitored endpoint with the condition that there are one or more open ports without a found service.

2. Install the Python module requests. This HTTP library is necessary for the ChatGPT integration script to work with HTTP requests.

# pip install requests

3. Create an integration script called /var/ossec/integrations/custom-chatgpt.py and copy the Python script below to custom-chatgpt.py. The Python script below takes note of open ports on an endpoint and sends it to ChatGPT to get information about the open services and past vulnerabilities:

#!/var/ossec/framework/python/bin/python3
# Copyright (C) 2015-2023, 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):
  nmap_port_service = data['nmap_port_service']
  choices = data['content']
  return nmap_port_service, choices


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


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

  json_data = {
        'model': 'gpt-3.5-turbo',
        'messages': [
            {
                'role': 'user',
                'content': 'In 4 or 5 sentences, tell me about this service and if there are past vulnerabilities: ' + nmap_port_service,
            },
        ],
    }

  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 port service
      ip = {"nmap_port_service": nmap_port_service}
      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 port service present in the alert. Exit.
    if not "nmap_port_service" in alert["data"]:
        return(0)

    # Request info using chatgpt API
    data = query_api(alert["data"]["nmap_port_service"], 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"]["nmap_port_service"] = alert["data"]["nmap_port_service"]
    nmap_port_service = alert["data"]["nmap_port_service"]

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

        # Populate JSON Output object with chatgpt request
        alert_output["chatgpt"]["nmap_port_service"] = nmap_port_service
        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

4. Grant executable permissions and modify the owner and group of the newly created Python script to belong to Wazuh:

# chmod 750 /var/ossec/integrations/custom-chatgpt.py
# chown root:wazuh /var/ossec/integrations/custom-chatgpt.py

5. Edit the /var/ossec/etc/ossec.conf file and add the integration block with the content below within the <ossec_config> block:

<!-- ChatGPT Integration -->
<integration>
  <name>custom-chatgpt.py</name>
  <hook_url>https://api.openai.com/v1/chat/completions</hook_url>
  <api_key><YOUR_CHATGPT_API_KEY></api_key>
  <level>5</level>
  <rule_id>100101</rule_id>
  <alert_format>json</alert_format>
</integration>

The parameters used in the integration block are as follows:

  • <name> is the name of the custom script that performs the integration. All custom script names must start with custom-.
  • <hook_url> is the API URL provided by ChatGPT.
  • <api_key> is the API key. Replace <YOUR_CHATGPT_API_KEY> with your API key.
  • <level> sets a level filter so that the script does not act upon alerts below a certain level.
  • <rule_id> sets the rules that will trigger this integration. In this article, we use the rule ID 100101  to trigger the ChatGPT integration script when Nmap discovers a service with an open port.
  • <alert_format> indicates the format of the alerts. We recommend the JSON format. The script will receive the alerts in full_log format if you do not set it to JSON.

Note: The OpenAI API is not free. However, when creating a new account, OpenAI provides you with a free trial usage for the API. You can register for a free API key at https://platform.openai.com/signup. Once your account is created:

  • Click on the upper-right upper user icon.
  • Click on View API Keys.
  • Click Create new secret key.
  • Confirm that you have free trial usage for the API at https://platform.openai.com/account/usage.
  • Copy the new key and save it someplace safe as you won’t be able to view the key again.

6. Add the custom rule below to the /var/ossec/etc/rules/local_rules.xml file. This rule will trigger when the port service is known and will also capture the response collected by the ChatGPT integration:

<group name="local,linux,">
  <rule id="100102" level="6">
    <field name="chatgpt.nmap_port_service">\w+</field>
      <description>The service $(chatgpt.nmap_port_service) is on an open port.</description>
  </rule>
</group>

7. Restart the Wazuh manager to apply the configuration changes:

# systemctl restart wazuh-manager

Scan results

Below is the image of the alerts generated on the Wazuh dashboard when Nmap scans an Ubuntu endpoint.

Navigate to the Security events tab to view the generated alerts.

Ubuntu results

Linux results

Windows results

Conclusion

In this blog post, we have demonstrated how to integrate Nmap and ChatGPT with Wazuh to perform network endpoint scans and enhance security audits. The integration enables ChatGPT to interact with Nmap and Wazuh, leveraging natural language processing to provide intelligent assistance. This functionality will allow your organization to gain better security insights by leveraging the combined strength of Nmap, ChatGPT, and Wazuh.

References

If you have any questions about this, join our Slack community channel! Our team and other contributors will assist you.