Enhancing Malicious IP Detection and Intelligence Using Abuse.ch API

Enhancing Malicious IP Detection and Intelligence Using Abuse.ch API

I recently finished a project aimed at improving the detection and threat intelligence of malicious IPs on an organization's network and assets.

Summary:


The project leveraged Wazuh as the SIEM tool (Other SIEM tools include Splunk, Microsoft Sentinel, and IBM QRadar), abuse.ch as the indicator-of-compromise source, and a Python script that pulls the malicious IP address from abuse.ch via an API into the Wazuh manager.


The Wazuh manager executes the script every 6 hours via a cron job and saves its output to a separate file. This file is referenced by another file that contains the detection rule that all agents connected to the Wazuh server/manager must obey.

The aim of the project is to enhance an organization's IP intelligence by detecting more malicious IP addresses, which are automatically fed into the detection rules, thereby blocking them from accessing our organization’s network.


The flow is as follows: Wazuh runs a Python script that pulls malicious IP addresses into a file. This file is checked by the Wazuh manager when an IP appears in the logs, and if a match is found, the active response is triggered.


Requirements

  • A Wazuh Server/Manager - This must run on a Linux machine. 

  • Wazuh Agents: Windows, Linux, macOS

  • Python 3.9 for writing and executing the Python script.

  • MalwareBazaar’s API


Set Up

Refer to this guide to set up Wazuh. 

On your Wazuh Server/Manager, install Python 3

sudo apt update -y && sudo apt upgrade -y
sudo apt install python3

Confirm your installation using:

python -version

Create your Python script. This script pulls the malicious IP address from MalwareBazaar using the abuse.ch feature API.

#!/usr/bin/env python3
import requests
from xml.etree.ElementTree import Element, SubElement, tostring, parse
from xml.dom import minidom
import os

# === CONFIG ===
API_KEY = 'redacted'  # Replace with your AbuseIPDB API key
OUTPUT_FILE = '/var/ossec/etc/lists/malicious_ips_list.xml'

# AbuseIPDB blacklist endpoint
URL = 'https://api.abuseipdb.com/api/v2/blacklist'
headers = {
    'Accept': 'application/json',
    'Key': API_KEY
}

try:
    response = requests.get(URL, headers=headers)
    response.raise_for_status()
    data = response.json()
    new_ips = [entry['ipAddress'] for entry in data['data']]

    # Load existing XML if it exists
    existing_ips = set()
    if os.path.exists(OUTPUT_FILE):
        tree = parse(OUTPUT_FILE)
        root = tree.getroot()
        for value in root.findall('value'):
            existing_ips.add(value.text)

    # Merge new IPs with existing ones
    all_ips = existing_ips.union(new_ips)

    # Create XML structure
    root = Element('list', name="malicious_ips_list", key="srcip")
    for ip in sorted(all_ips):
        SubElement(root, 'value').text = ip

    # Pretty print XML
    xml_str = minidom.parseString(tostring(root)).toprettyxml(indent="  ")

    # Write to file
    with open(OUTPUT_FILE, 'w') as f:
        f.write(xml_str)

    print(f"[+] Saved {len(all_ips)} malicious IPs to {OUTPUT_FILE}")

except Exception as e:
    print(f"[-] Error fetching or saving data: {e}")

Make the script executable using this command: 

chmod +x /usr/local/bin/abuse.py

Create a cron job to run the script every 6 hours. First, I need to enter the cron tab:

sudo crontab -e

Then paste the cron command at the end of the contents:

image.png

0 */6 * * * /usr/bin/python3 /usr/local/bin/abuse.py

Once you are done with this, navigate to the directory bearing the local_rules.xml

You may want to log in as root to access this file, as it is within the Wazuh configuration environment.

Run: sudo -i to login as root. Navigate to cd /var/ossec/etc

image.png

Then, navigate to the rules directory using cd rules, then open the local_rules.xml file using the nano local_rules.xml command

Paste this syscheck rule, or modify it to suit your organization. Ensure that the rule ID is above 10000

<group name="local,threat-intel,">
  <rule id="100002" level="10">
    <if_sid>100000</if_sid>
    <list lookup="match_key" field="srcip">malicious_ips_list</list>
    <description>Connection from known malicious IP detected</description>
  </rule>
</group>

image.png

After setting this rule, restart the Wazuh manager, then check for its status:

systemctl restart wazuh-manager
systemctl status wazuh-manager

image.png

Once you confirm that your manager is active and running, check that the script runs:

sudo /usr/bin/python3 /home/chisom/abuse.py

Running this command should execute the script, navigate back to your malicious_ips_list.txt folder, then open the file; you should see many IP addresses.

Active Response

The IP addresses in the malicious_ips_list.txt The file would be checked by Wazuh whenever an agent logs an IP address, and if a match is found, an alert is triggered on the dashboard.

However, to take this further, we can set up an active response for automated actions, especially in situations where our agents often face brute-force attacks (brute-force attack logs often include IP addresses).

For instance, if we want to drop all malicious IPs, we can configure our active response to read the local_rules.xml and execute accordingly.

Since I am leveraging the firewall-drop script, a default Active Response script on Wazuh, I simply need to navigate to the active response directory: cd /var/ossec/etc

Open the ossec.conf file, and locate the active response section; paste your rule, something like this, at the end of the section:

<disabled>no</disabled>
  <command>firewall-drop</command>
  <location>local</location>    <!-- runs on the agent that triggered it -->
  <rules_id>100002</rules_id> <!... Our rule id from above is 100002 ...>
  <timeout>6000</timeout>       <!-- unblock after 600s ...>

image.png

Restart your Wazuh manager using systemctl restart wazuh-manager and systemctl status wazuh-manager.

image.png

Once this is done, any IP address pulled by the abuse.py script that appears in any agent’s log will be dropped for 6000 seconds (100 minutes). You can increase the time limit as you wish.