Geek On The Hill

PHP Script to Capture CSF BLOCK_REPORT Output and Report it to AbuseIPDB

This is an experimental script I’m working on to capture the output from CSF Firewall’s BLOCK_REPORT feature, save it to a database, and report it to AbuseIPDB. As written, it requires a MySQL database with one table and the following columns: “id” (primary key), “datetime”, “time”, “ip4”, “ports”, “categories”, “direction”, “domain”, “trg”, and “comment”.

This script depends on lfd being enabled in CSF and BLOCK_REPORT being set to the script’s path in /etc/csf/csf.conf (for example, BLOCK_REPORT="/root/this-script.php").You can read the documentation here. Note that you’ll have to restart lfd before the script takes effect.

If you’re coding on a Windows or Mac, make sure to save the file in UNIX format and make it executable. DO NOT place this script in any public-facing directory (for example, don’t place it anywhere in /public_html/).

As I mentioned, this script is experimental and rather crude at present. However, it has been working flawlessly since I installed it. Please feel free to improve upon it or notify me of any glaring errors. Thank you.

Rather than going back and forth to repeatedly correct WordPress’s butchery of PHP code, I annotated the script and am presenting it in full here.

#!/usr/local/bin/php
<?php // Set the time zone you wish to use date_default_timezone_set('America/New_York');
// Set some variables
$timeNow = time();
$fresh = time() - 900; // to check for reports on the same IP within the past 15 minutes
$currentDateTime = (date("M d, Y h:i:s a"));
$domain = "server.hostname"; // For database "domain" entry. Not otherwise important.

// Get arguments from CSF
$args = $argv; 
     // Use arguments to set PHP variables
     $ip = $args[1];
     $ports = $args[2];
     $direction = $args[4];
     $csf_message = $args[6];
     $csf_log = $args[7];
     $csf_trigger = $args[8];

// exclude your own server from reports due to dumb user errors
if ($ip == "xxx.xxx.xxx.xxx" || $ip == "xxx.xxx.xxx.xxx" || $ip == "xxx.xxx.xxx.xxx") { die; }
/*
Use CSF trigger statements to select AbuseIPDB categories and set $comments
AbuseIPDB categories can be found at https://www.abuseipdb.com/categories
Additional categories can be added here using the same format
*/
unset($categories); // Start fresh so we can ignore unwanted categories
if ($csf_trigger == "LF_SSHD") {
     $categories = "18,22";
     $comment = "Multiple failed SSH logins";
}
if ($csf_trigger == "LF_DISTATTACK") {
     $categories = "18,22";
     $comment = "Distributed SSH attack";
}
if ($csf_trigger == "LF_DISTFTP") {
     $categories = "5,18";
     $comment = "Distributed FTP attack";
}
if ($csf_trigger == "LF_FTPD") {
     $categories = "5,18";
     $comment = "Multiple failed FTP logins";
}
if ($csf_trigger == "LF_DISTSMTP") {
     $categories = "18";
     $comment = "Distributed SMTP attack";
}
if ($csf_trigger == "LF_CPANEL") {
     $categories = "21";
     $comment = "Multiple failed cPanel logins";
}
// If NOTA are true, then exit script
if (!isset($categories)) { die; }

// Check database for fresh reports made on the IP in the past 15 minutes
$con = mysqli_connect("localhost","prefix_abuse-reporter","Password","prefix_lfd-reports");
     if (!$con) { die('Could not connect: ' . mysqli_error($con)); }
// SELECT * is inefficient, but useful while this is testing phase
$result = mysqli_query($con, "SELECT * FROM reports WHERE (ip4 LIKE '$ip' AND time >= '$fresh')");
$row = mysqli_fetch_array($result);
$reportDate = $row['datetime'];
/*
If no recent reports for the IP were found, continue execution, starting with the database entry
Note that most of this information is not needed for the AbuseIPDB report. It's for my own records ans will be stored in the db.
Only the IP address, category, and API key are needed for the report
*/
if (empty($reportDate)) {
    // Sanitize
    $currentDateTime = mysqli_real_escape_string($con, $currentDateTime);
    $timeNow = mysqli_real_escape_string($con, $timeNow);
    $ip = mysqli_real_escape_string($con, $ip);
    $ports = mysqli_real_escape_string($con, $ports);
    $categories = mysqli_real_escape_string($con, $categories);
    $direction = mysqli_real_escape_string($con, $direction);
    $domain = mysqli_real_escape_string($con, $domain);
    $csf_trigger = mysqli_real_escape_string($con, $csf_trigger);
    $comment = mysqli_real_escape_string($con, $comment);
    // Insert to db
    mysqli_select_db($con, "rjmweb_lfd-reports");
    $sql = "INSERT INTO reports (datetime, time, ip4, ports, categories, direction, domain, trg, comment) VALUES ('$currentDateTime','$timeNow','$ip','$ports','$categories','$direction','$domain','$csf_trigger','$comment')";
    if (!mysqli_query($con,$sql)) {
        echo("Error description: " . mysqli_error($con));
    }
    /*
    Make AbuseIPDB report
    When testing, set the IP to 127.0.0.2 and CURLOPT_RETURNTRANSFER to 0 to return the response from AbuseIPDB
    A successful testing response when using 127.0.0.2 will be an error message
    stating that the same IP cannot be reported more than once every 15 minutes
    */
    // Using the array urlencodes the data
    $data = (array(
        "ip"  => $ip,
        "categories" => $categories,
        "comment" => $comment
    ));
    $headers =  array('Key: your-abuseipdb-api-key-goes-here', 'Accept: application/json');
    $ch = curl_init("https://api.abuseipdb.com/api/v2/report");
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1 ); // Set to 0 for testing to display response from AbuseIPDB
        curl_setopt($ch, CURLOPT_POST,           1 );
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    $output=curl_exec($ch);
    curl_close($ch);
}
die;
?>

Obviously, you’ll also need an account at AbuseIPDB and an APIv2 key to make this work.

Richard

Leave a Reply