#! /usr/bin/env python3 # -*- coding: utf-8 -*- ''' Script to scan websites looking for web vulnerabilities Creation date: 05/03/2017 Date last updated: 19/03/2017 Nagios check_nikto plugin * * License: GPL * Copyright (c) 2017 DI-FCUL * * Description: * * This file contains the check_nikto plugin * * Use the nrpe program to check update information for wordpress in remote host. * * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. ''' import socket from datetime import datetime from collections import Counter import time import codecs import sys import urllib.request import re import urllib from optparse import OptionParser import os __author__ = "\nAuthor: Raimundo Henrique da Silva Chipongue\nE-mail: fc48807@alunos.fc.ul.pt, chipongue1@gmail.com\nInstitution: Faculty of Science of the University of Lisbon\n" __version__= "1.0.0" # define exit codes ExitOK = 0 ExitWarning = 1 ExitCritical = 2 ExitUnknown = 3 def check_connectivity(): ''' Check if the internet conection is up ''' try: urllib.request.urlopen('https://google.com', timeout=1) return True except urllib.request.URLError: return False def version(opts): if check_connectivity(): report = ("%s.html"%opts.report) time_to_check = 60 * 60 * 24 * opts.time domain = opts.host domain = domain.replace("https://", "") domain = domain.replace("http://", "") domain = domain.replace("www.", "") if domain: try: socket.gethostbyname_ex(domain) num = True except: num = False if not num: try: domain = ("www.%s"%domain) socket.gethostbyname_ex(domain) except: print('Unable to resolve "%s"'%domain) if os.path.exists("%s%s"%(opts.path,report)): file_ts = float(os.path.getatime("%s%s"%(opts.path,report))) current_ts = float(time.time()) dif = current_ts - file_ts if dif > time_to_check: if opts.tuning: tuning = opts.tuning.replace(",", "") os.popen("rm %s%s"%(opts.path,report)) os.popen('nikto -Tuning %s -h %s -output %s%s -Format html -port %s'%(tuning, domain, opts.path, report, opts.port)).read() else: os.popen("rm %s%s"%(opts.path,opts.report)) os.popen('nikto -h %s -output %s%s -Format html -port %s'%(domain, opts.path, report, opts.port)).read() else: if opts.tuning: tuning = opts.tuning.replace(",", "") os.popen('nikto -Tuning %s -h %s -output %s%s -Format html -port %s'%(tuning, domain, opts.path, report, opts.port)).read() else: os.popen('nikto -h %s -output %s%s -Format html -port %s'%(domain, opts.path, report, opts.port)).read() try: size = os.path.getsize("%s%s"%(opts.path,report)) except: print("Unable to read the file, verify that it exists and the nagios user has permissions of writing and reading in this path.") sys.exit(ExitUnknown) if size > 0: try: file = codecs.open("%s%s"%(opts.path,report)) except: print("report error") sys.exit(ExitUnknown) osvdb = [] end_scan = False for line in file: x = re.search('>OSVDB-[0-9]?\d', line) scan_summary = re.search('Scan Summary', line) if scan_summary: end_scan = True if x: osvdb.extend(re.findall(r'>(.*?)<', str(line))) if end_scan: num = (len(osvdb)) if num != len(set(osvdb)): class MyCounter(Counter): def __str__(self): return ("\n".join('{} {}'.format(k, v) for k, v in self.items())) vuln = MyCounter(Counter(osvdb)) vuln = str(vuln).replace("\n", ", ") else: vuln = (', '.join(osvdb)) if num: print("Were found the folowing %s vulnerabilities %s, in %s, please open the file %s%s for more details."%(num, vuln, domain, opts.path, report)) sys.exit(ExitCritical) else: print("The Nikto web scan didn't find any vulnerability in %s"%domain) sys.exit(ExitOK) else: print("Scan in progress") sys.exit(ExitUnknown) else: print("Can't read file") sys.exit(ExitUnknown) else: print('Error, check you internet connection') sys.exit(ExitUnknown) def main(): parser = OptionParser("usage: %prog [options] arg1 arg2 arg3 arg4. \nEx.: %prog -H ciencias.ulisboa.pt -p 80 -r ciencias -t 2 -T 9" ) parser.add_option("-H","--host", dest="host", help="Use this options to specify the target host") parser.add_option("-P", "--path", dest="path", default="/tmp/", type="string", help="Use this option to specify the path to save the nikto report." + " By default this is /tmp folder, however this doesn't secure, any malicious user can change the" + " your content, and generating false positives or false negatives." + " If the folder is different from /tmp, you must assign read and write permissions to nagios.") parser.add_option("-p", "--port", dest="port", default="443", help="Use this option to specify the port or ports", type="string") parser.add_option("-r", "--report", dest="report", default="report", help="Use this option to specify the report name", type="string") parser.add_option("-t", "--time", dest="time", default=1, help="Use this option to specify the life time for report file, life time is in day.", type=float) parser.add_option("-T", "--tuning", dest="tuning", default=False, help="Use this option to select the tuning mode, and specify the options you need to work."+ "Using Tuning mode, this implies the selection of tests, running in short time,"+ "that run in normal mode, takes a several minutes."+ " | 0 - File Upload"+ " | 1 - Interesting File // we will get in logs"+ " | 2 - Misconfiguration // Defoult File"+ " | 3 - Information Disclousure"+ " | 4 - Injection (XSS/Script/HTML)"+ " | 5 - Remote File Retrievel - Inside Web Root"+ " | 6 - Denial of Service // Scan for DDOS"+ " | 7 - Remote File Retrieval - Server Wide"+ " | 8 - Command Execution // Remote Shell"+ " | 9 - SQL Injection // Scan for mysql vulnerabilities"+ " | a - Authentication Bypass"+ " | b - Software Identification"+ " | c - Remote Source Inclusion"+ " | x - Reverse Tuning Options", type="string") parser.add_option("-V","--version", action="store_true", dest="version", help="This option show the current version number of the program and exit") parser.add_option("-A","--author", action="store_true", dest="author", help="This option show author information and exit") (opts, args) = parser.parse_args() if not os.path.exists(opts.path): parser.error("Please, this program requires to specify a valid and private folder to store temp file") if opts.author: print(__author__) sys.exit() if opts.version: print("check_nikto.py %s"%__version__) sys.exit() if not opts.host: parser.error("This script requires to specify host arguments.") sys.exit(ExitUnknown) version(opts) if __name__ == '__main__': main()