From 63a9e55d4edfced4e3f0510a53f975161180fd9e Mon Sep 17 00:00:00 2001 From: Jokob-sk Date: Sun, 12 Mar 2023 19:31:59 +1100 Subject: [PATCH] Rougue DHCP plugin based on work of @leiweibau --- back/pialert.py | 9 +- front/css/pialert.css | 5 + front/js/pialert_common.js | 25 ++ front/plugins.php | 24 +- front/plugins/README.md | 4 +- front/plugins/dhcp_servers/README.md | 12 + front/plugins/dhcp_servers/config.json | 302 ++++++++++++++++++++++ front/plugins/dhcp_servers/script.py | 140 ++++++++++ front/plugins/website_monitor/config.json | 2 +- 9 files changed, 507 insertions(+), 16 deletions(-) create mode 100755 front/plugins/dhcp_servers/README.md create mode 100755 front/plugins/dhcp_servers/config.json create mode 100755 front/plugins/dhcp_servers/script.py diff --git a/back/pialert.py b/back/pialert.py index 2bbf4696..b8e6cb4e 100755 --- a/back/pialert.py +++ b/back/pialert.py @@ -3656,7 +3656,7 @@ def isNewVersion(): data = "" # make sure we received a valid response and not an API rate limit exceeded message - if len(data) > 0 and "published_at" in data[0]: + if data != "" and len(data) > 0 and isinstance(data, list) and "published_at" in data[0]: dateTimeStr = data[0]["published_at"] @@ -3850,7 +3850,7 @@ def execute_plugin(plugin): for row in arr: # There has to be always 9 columns - if len(row) == 9: + if len(row) == 9 and (row[0] in ['','null']) == False : sqlParams.append((plugin["unique_prefix"], row[0], row[1], 'null', row[2], row[3], row[4], row[5], row[6], 0, row[7], 'null', row[8])) else: mylog('none', [' [Plugins]: Skipped invalid sql result']) @@ -3896,7 +3896,7 @@ def process_plugin_events(plugin): existingPluginObjectsCount = len(pluginObjects) mylog('debug', [' [Plugins] Existing objects: ', existingPluginObjectsCount]) - mylog('debug', [' [Plugins] Events objects: ', len(plugEventsArr)]) + mylog('debug', [' [Plugins] Objects events : ', len(plugEventsArr)]) # set status as new - will be changed later if conditions are fulfilled, e.g. entry found for eve in plugEventsArr: @@ -4035,13 +4035,14 @@ def combine_plugin_objects(old, new): def resolve_wildcards(command, params): mylog('debug', [' [Plugins]: Pre-Resolved CMD: ', command]) + for param in params: mylog('debug', [' [Plugins]: key : {', param[0], '}']) mylog('debug', [' [Plugins]: resolved: ', param[1]]) command = command.replace('{' + param[0] + '}', param[1]) - mylog('debug', [' [Plugins]: Resolved CMD: ', command]) + mylog('debug', [' [Plugins]: Resolved CMD: ', command]) return command diff --git a/front/css/pialert.css b/front/css/pialert.css index 7484621d..26ba3796 100755 --- a/front/css/pialert.css +++ b/front/css/pialert.css @@ -827,5 +827,10 @@ height: 50px; padding-bottom: 8px; } +.plugins-description +{ + padding-top: 20px; +} + diff --git a/front/js/pialert_common.js b/front/js/pialert_common.js index d0318169..d78b6330 100755 --- a/front/js/pialert_common.js +++ b/front/js/pialert_common.js @@ -345,5 +345,30 @@ function openInNewTab (url) { } +// ----------------------------------------------------------------------------- +function navigateToDeviceWithIp (ip) { + + $.get('api/table_devices.json', function(res) { + + devices = res["data"]; + + mac = "" + + $.each(devices, function(index, obj) { + + if(obj.dev_LastIP.trim() == ip.trim()) + { + mac = obj.dev_MAC; + + window.open(window.location.origin +'/deviceDetails.php?mac=' + mac , "_blank"); + } + }); + + + + }); +} + + diff --git a/front/plugins.php b/front/plugins.php index 16b82b6a..55f71447 100755 --- a/front/plugins.php +++ b/front/plugins.php @@ -30,9 +30,7 @@ - + - + diff --git a/front/plugins/README.md b/front/plugins/README.md index 160e7e93..4dcccb57 100755 --- a/front/plugins/README.md +++ b/front/plugins/README.md @@ -313,8 +313,8 @@ The UI will adjust how columns are displayed in the UI based on the definition o ## Full Examples -- Script based plugin: Check the [website_monitor WEBMON) config.json](https://github.com/jokob-sk/Pi.Alert/blob/main/front/plugins/website_monitor/config.json) file for details. -- SQL query nased plugin: Check the [nmap_services NMAPSERV) config.json](https://github.com/jokob-sk/Pi.Alert/blob/main/front/plugins/nmap_services/config.json) file for details. +- Script based plugin: Check the [website_monitor (WEBMON) config.json](https://github.com/jokob-sk/Pi.Alert/blob/main/front/plugins/website_monitor/config.json) file for details. +- SQL query nased plugin: Check the [nmap_services (NMAPSERV) config.json](https://github.com/jokob-sk/Pi.Alert/blob/main/front/plugins/nmap_services/config.json) file for details. ### Screenshots diff --git a/front/plugins/dhcp_servers/README.md b/front/plugins/dhcp_servers/README.md new file mode 100755 index 00000000..c5474ad1 --- /dev/null +++ b/front/plugins/dhcp_servers/README.md @@ -0,0 +1,12 @@ +## Overview + +A simple sample plugin allowing for monitoring web services or urls. The status code corresponds to the commonly used [HTTP response status codes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status). + +### Usage + +- The user can specify which services (websites) to monitor via the `WEBMON_urls_to_check` setting. + +### Notes + +- Setting `(WEBMON_)SQL_internet_ip` is not used and specified for demonstration purposes only. +- Parameters `macs` and `internet_ip` in the `config.json` file are not used and specified for demonstration purposes only. \ No newline at end of file diff --git a/front/plugins/dhcp_servers/config.json b/front/plugins/dhcp_servers/config.json new file mode 100755 index 00000000..0d51badd --- /dev/null +++ b/front/plugins/dhcp_servers/config.json @@ -0,0 +1,302 @@ +{ + "code_name": "dhcp_servers", + "unique_prefix": "DHCPSRVS", + "enabled": true, + "data_source": "python-script", + "localized": ["display_name", "description", "icon"], + "display_name" : [{ + "language_code":"en_us", + "string" : "Rogue DHCP" + }], + "icon":[{ + "language_code":"en_us", + "string" : "" + }], + "description": [{ + "language_code":"en_us", + "string" : "This plugin is to use NMAP to monitor for rogue DHCP servers." + }], + "params" : [], + "database_column_definitions": + [ + { + "column": "Index", + "css_classes": "col-sm-2", + "show": false, + "type": "label", + "default_value":"", + "options": [], + "localized": ["name"], + "name":[{ + "language_code":"en_us", + "string" : "N/A" + }] + } , + { + "column": "Plugin", + "css_classes": "col-sm-2", + "show": false, + "type": "label", + "default_value":"", + "options": [], + "localized": ["name"], + "name":[{ + "language_code":"en_us", + "string" : "N/A" + }] + }, + { + "column": "Object_PrimaryID", + "css_classes": "col-sm-2", + "show": true, + "type": "deviceip", + "default_value":"", + "options": [], + "localized": ["name"], + "name":[{ + "language_code":"en_us", + "string" : "Server Identifier" + }] + }, + { + "column": "Object_SecondaryID", + "css_classes": "col-sm-2", + "show": true, + "type": "label", + "default_value":"", + "options": [], + "localized": ["name"], + "name":[{ + "language_code":"en_us", + "string" : "Domain Name" + }] + } , + { + "column": "DateTimeCreated", + "css_classes": "col-sm-2", + "show": true, + "type": "label", + "default_value":"", + "options": [], + "localized": ["name"], + "name":[{ + "language_code":"en_us", + "string" : "Created" + }] + }, + { + "column": "DateTimeChanged", + "css_classes": "col-sm-2", + "show": false, + "type": "label", + "default_value":"", + "options": [], + "localized": ["name"], + "name":[{ + "language_code":"en_us", + "string" : "Changed" + }] + }, + { + "column": "Watched_Value1", + "css_classes": "col-sm-2", + "show": false, + "type": "label", + "default_value":"", + "options": [], + "localized": ["name"], + "name":[{ + "language_code":"en_us", + "string" : "Domain Name Server" + }] + }, + { + "column": "Watched_Value2", + "css_classes": "col-sm-2", + "show": true, + "type": "label", + "default_value":"", + "options": [], + "localized": ["name"], + "name":[{ + "language_code":"en_us", + "string" : "IP Offered" + }] + }, + { + "column": "Watched_Value3", + "css_classes": "col-sm-2", + "show": false, + "type": "label", + "default_value":"", + "options": [], + "localized": ["name"], + "name":[{ + "language_code":"en_us", + "string" : "Interface" + }] + } , + { + "column": "Watched_Value4", + "css_classes": "col-sm-2", + "show": true, + "type": "label", + "default_value":"", + "options": [], + "localized": ["name"], + "name":[{ + "language_code":"en_us", + "string" : "Router" + }] + } , + { + "column": "UserData", + "css_classes": "col-sm-2", + "show": true, + "type": "textboxsave", + "default_value":"", + "options": [], + "localized": ["name"], + "name":[{ + "language_code":"en_us", + "string" : "Comments" + }] + }, + { + "column": "Status", + "css_classes": "col-sm-1", + "show": true, + "type": "replace", + "default_value":"", + "options": [ + { + "equals": "watched-not-changed", + "replacement": "
" + }, + { + "equals": "watched-changed", + "replacement": "
" + }, + { + "equals": "new", + "replacement": "
" + } + ], + "localized": ["name"], + "name":[{ + "language_code":"en_us", + "string" : "Status" + }] + }, + { + "column": "Extra", + "css_classes": "col-sm-3", + "show": true, + "type": "label", + "default_value":"", + "options": [], + "localized": ["name"], + "name":[{ + "language_code":"en_us", + "string" : "Extra info" + }] + } + ], + "settings":[ + { + "function": "RUN", + "type": "selecttext", + "default_value":"disabled", + "options": ["disabled", "once", "schedule", "always_after_scan", "on_new_device"], + "localized": ["name", "description"], + "name" :[{ + "language_code":"en_us", + "string" : "When to run" + }], + "description": [{ + "language_code":"en_us", + "string" : "Enable a regular scan of rogue DHCP servers. If you select schedule the scheduling settings from below are applied. If you select once the scan is run only once on start of the application (container) or after you update your settings." + }] + }, + { + "function": "CMD", + "type": "text", + "default_value":"python3 /home/pi/pialert/front/plugins/dhcp_servers/script.py", + "options": [], + "localized": ["name", "description"], + "name" : [{ + "language_code":"en_us", + "string" : "Command" + }], + "description": [{ + "language_code":"en_us", + "string" : "Command to run" + }] + }, + { + "function": "RUN_SCHD", + "type": "text", + "default_value":"0 2 * * *", + "options": [], + "localized": ["name", "description"], + "name" : [{ + "language_code":"en_us", + "string" : "Schedule" + }], + "description": [{ + "language_code":"en_us", + "string" : "Only enabled if you select schedule in the DHCPSRVS_RUN setting. Make sure you enter the schedule in the correct cron-like format (e.g. validate at crontab.guru). For example entering 0 4 * * * will run the scan after 4 am in the TIMEZONE you set above. Will be run NEXT time the time passes." + }] + }, + { + "function": "RUN_TIMEOUT", + "type": "integer", + "default_value":5, + "options": [], + "localized": ["name", "description"], + "name" : [{ + "language_code":"en_us", + "string" : "Run timeout" + }, + { + "language_code":"de_de", + "string" : "Wartezeit" + }], + "description": [{ + "language_code":"en_us", + "string" : "Maximum time in seconds to wait for the script to finish. If this time is exceeded the script is aborted." + }] + }, + { + "function": "WATCH", + "type": "multiselect", + "default_value":["Watched_Value1"], + "options": ["Watched_Value1","Watched_Value2","Watched_Value3","Watched_Value4"], + "localized": ["name", "description"], + "name" :[{ + "language_code":"en_us", + "string" : "Watched" + }] , + "description":[{ + "language_code":"en_us", + "string" : "Send a notification if selected values change. Use CTRL + Click to select/deselect. " + }] + }, + { + "function": "REPORT_ON", + "type": "multiselect", + "default_value":["new","watched-changed"], + "options": ["new","watched-changed","watched-not-changed"], + "localized": ["name", "description"], + "name" :[{ + "language_code":"en_us", + "string" : "Report on" + }] , + "description":[{ + "language_code":"en_us", + "string" : "Send a notification only on these statuses. new means a new unique (unique combination of PrimaryId and SecondaryId) object was discovered. watched-changed means that selected Watched_ValueN columns changed." + }] + } + ] +} + diff --git a/front/plugins/dhcp_servers/script.py b/front/plugins/dhcp_servers/script.py new file mode 100755 index 00000000..947fd1b5 --- /dev/null +++ b/front/plugins/dhcp_servers/script.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python +# Based on the work of https://github.com/leiweibau/Pi.Alert + +# /home/pi/pialert/front/plugins/website_monitor/script.py urls=http://google.com,http://bing.com +from __future__ import unicode_literals +from time import sleep, time, strftime +import requests +import pathlib +import threading +import subprocess +import socket + +import argparse +import io +#import smtplib +import sys +#from smtp_config import sender, password, receivers, host, port +from requests.packages.urllib3.exceptions import InsecureRequestWarning + +import pwd +import os + +curPath = str(pathlib.Path(__file__).parent.resolve()) +log_file = curPath + '/script.log' +last_run = curPath + '/last_result.log' + +print(last_run) + +# Workflow + +def main(): + + last_run_logfile = open(last_run, 'a') + + timeoutSec = 10 + + nmapArgs = ['sudo', 'nmap', '--script', 'broadcast-dhcp-discover'] + + # Execute N probes and insert in list + dhcp_probes = 1 # N probes + newLines = [] + newLines.append(strftime("%Y-%m-%d %H:%M:%S")) + #dhcp_server_list_time = [] + for _ in range(dhcp_probes): + output = subprocess.check_output (nmapArgs, universal_newlines=True, stderr=subprocess.STDOUT, timeout=(timeoutSec )) + # stream = os.popen('sudo nmap --script broadcast-dhcp-discover 2>/dev/null') + # output = stream.read() + # last_run_logfile.write(output) + + newLines = newLines + output.split("\n") + + # parse output + newEntries = [] + + duration = "" + for line in newLines: + + if newEntries is None: + index = 0 + else: + index = len(newEntries) - 1 + + if 'Response ' in line and ' of ' in line: + + newEntries.append(plugin_object_class()) + + elif 'Server Identifier' in line : + newEntries[index].primaryId = line.split(':')[1].strip() + + + elif 'Domain Name' in line : + newEntries[index].secondaryId = line.split(':')[1].strip() + elif 'Domain Name Server' in line : + newEntries[index].watched1 = line.split(':')[1].strip() + elif 'IP Offered' in line : + newEntries[index].watched2 = line.split(':')[1].strip() + elif 'Interface' in line : + newEntries[index].watched3 = line.split(':')[1].strip() + elif 'Router' in line : + newEntries[index].watched4 = line.split(':')[1].strip() + newEntries[index].foreignKey = line.split(':')[1].strip() + elif ('IP Address Lease Time' in line or 'Subnet Mask' in line or 'Broadcast Address' in line) : + newEntries[index].extra = newEntries[index].extra + ',' + line.split(':')[1].strip() + + + for e in newEntries: + # Insert list into the log + + service_monitoring_log(e.primaryId, e.secondaryId, e.created, e.watched1, e.watched2, e.watched3, e.watched4, e.extra, e.foreignKey ) + +# ----------------------------------------------------------------------------- +def service_monitoring_log(primaryId, secondaryId, created, watched1, watched2 = '', watched3 = '', watched4 = '', extra ='', foreignKey ='' ): + + if watched1 == '': + watched1 = 'null' + if watched2 == '': + watched2 = 'null' + if watched3 == '': + watched3 = 'null' + if watched4 == '': + watched4 = 'null' + + with open(last_run, 'a') as last_run_logfile: + # https://www.duckduckgo.com|192.168.0.1|2023-01-02 15:56:30|200|0.9898|null|null|Best search engine|null + last_run_logfile.write("{}|{}|{}|{}|{}|{}|{}|{}|{}\n".format( + primaryId, + secondaryId, + created, + watched1, + watched2, + watched3, + watched4, + extra, + foreignKey + ) + ) + +# ------------------------------------------------------------------- +class plugin_object_class: + def __init__(self, primaryId = '',secondaryId = '', watched1 = '',watched2 = '',watched3 = '',watched4 = '',extra = '',foreignKey = ''): + self.pluginPref = '' + self.primaryId = primaryId + self.secondaryId = secondaryId + self.created = strftime("%Y-%m-%d %H:%M:%S") + self.changed = '' + self.watched1 = watched1 + self.watched2 = watched2 + self.watched3 = watched3 + self.watched4 = watched4 + self.status = '' + self.extra = extra + self.userData = '' + self.foreignKey = foreignKey + +#=============================================================================== +# BEGIN +#=============================================================================== +if __name__ == '__main__': + sys.exit(main()) + diff --git a/front/plugins/website_monitor/config.json b/front/plugins/website_monitor/config.json index 52889a17..8e5ef3e2 100755 --- a/front/plugins/website_monitor/config.json +++ b/front/plugins/website_monitor/config.json @@ -73,7 +73,7 @@ }] }, { - "column": "Object_SecondaryD", + "column": "Object_SecondaryID", "css_classes": "col-sm-2", "show": false, "type": "label",