#!/usr/bin/env python3 import re from os import listdir from os.path import isdir, isfile from audit.system.plugins.apache import apache_protocols, apache_signature, apache_indexes class Apache: def __init__(self, arguments): self._ssl_versions = apache_protocols() self._signatures = apache_signature() self._indexes = apache_indexes() self._reports = dict() self._apache_directory = arguments["apache_directory"] # Create the report self._constructReports() # Report self._reports["directory"] = self._apache_directory def runAudit(self): print("Running test for Apache") # Check if the directory exist path = f"{self._apache_directory}" if isdir(path): self._reports['audit'] = True self._analyzingSslVersion() self._analyzingSignature() self._analyzingIndexes() else: self._reports['audit'] = False self._reports["msg"] = "No directory found" def getReports(self) -> dict: return self._reports ################################################ # Analyzing SSL ################################################ def _analyzingSslVersion(self): # Check if the file exist path = f"{self._apache_directory}/sites-available" if isdir(path): self._reports['ssl']['audit'] = True count = 0 self._reports["ssl"]["virtualhost"] = dict() for site in listdir(path): vh = f"{path}/{site}" with open(vh, 'rb') as f: self._parseFileFindingSsl(f, site) count += 1 if count == 0: self._reports['ssl']['audit'] = False self._reports['ssl']['msg'] = \ f'No virtual host found in the directory {path}' else: self._reports['ssl']['audit'] = False self._reports['ssl']["msg"] = f"No directory {path} found" def _parseFileFindingSsl(self, fdata, virtualhost): data = fdata.read() lines = data.splitlines() for line in lines: line = line.decode('utf-8') # check if SSL is enable for the VirtualHost # (?#) -> remove commentary # (\s) -> remove space and tabulation grSSLEngine = re.search( "(?#)(\s)SSLEngine on", line, re.IGNORECASE ) if grSSLEngine: report = self._check_ssl_version(lines) self._reports["ssl"]["virtualhost"][virtualhost] = report def _check_ssl_version(self, lines) -> dict: report = dict() findProtocol = False protocolsFound = list() for line in lines: line = line.decode("utf-8") grSSLProtocol = re.search( "(?#)(\s)SSLProtocol", line, re.IGNORECASE ) if grSSLProtocol: for protocol in self._ssl_versions["protocols"]: grProtocol = re.search(f"-{protocol}", line) if grProtocol: protocolsFound.append(protocol) if len(self._ssl_versions["protocols"]) == len(protocolsFound): report["result"] = "success" else: report["msg"] = list() report["result"] = "failed" for proto in self._ssl_versions["protocols"]: if proto not in protocolsFound: report["msg"].append( f"{proto} need to be disabled" ) report["description"] = \ self._ssl_versions["description"] report["level"] = self._ssl_versions["level"] report["recommand_value"] = \ self._ssl_versions["recommand_value"] return report ################################################ # Analyzing Signature ################################################ def _analyzingSignature(self): # Check if the apache2.conf exist path = f"{self._apache_directory}/apache2.conf" if isfile(path): self._reports["signature"]["audit"] = True with open(path, 'rb') as fdata: self._reports["signature"]["signature"] = \ self._parsingApacheConfigForSignature(fdata) else: self._reports["signature"]["audit"] = False self._reports["signature"]["audit"] = \ f"The file {path} do not exist" def _parsingApacheConfigForSignature(self, fdata) -> dict: report = dict() data = fdata.read() lines = data.splitlines() for signature in self._signatures["signature"]: report[signature] = dict() for line in lines: line = line.decode('utf-8') for signature in self._signatures["signature"]: grSignature = re.search( f"(?#){signature}", line, re.IGNORECASE ) if grSignature: # It's not a comment if '#' not in line: report[signature]["audit"] = True report[signature]["result"] = "success" report[signature]["description"] = self._signatures["description"] report[signature]["level"] = self._signatures["level"] for signature in report: if len(report[signature]) == 0: report[signature]["audit"] = True report[signature]["result"] = "failed" report[signature]["recommand_value"] = signature report[signature]["description"] = self._signatures["description"] report[signature]["level"] = self._signatures["level"] return report ################################################ # Analyzing Indexes ################################################ def _analyzingIndexes(self): # Check if the apache2.conf exist path = f"{self._apache_directory}/apache2.conf" if isfile(path): self._reports["indexes"]["audit"] = True with open(path, 'rb') as fdata: self._reports["indexes"]["indexes"] = \ self._parsingApacheConfig(fdata) else: self._reports["indexes"]["audit"] = False self._reports["indexes"]["audit"] = \ f"The file {path} do not exist" def _parsingApacheConfig(self, fdata) -> dict: report = dict() report["directories"] = dict() data = fdata.read() lines = data.splitlines() # Each entry in the variable directories contains a list # of with all data in it # We create another entry when a found directories = list() directoryFound = False index = 0 optsFound = list() for line in lines: line = line.decode('utf-8') # Find a ", line, re.IGNORECASE) if grDirectory: directoryFound = False index += 1 # We will find if we find an indexes option for d in directories: for entry in d: # We get the directory path path = self._getDirectoryPath(entry) report["directories"][path] = dict() # Try to find the Option flag grFlag = re.search( f"{self._indexes['flag']}", entry, re.IGNORECASE ) if grFlag: for opt in self._indexes['options']: grOption = re.search( f"-{opt}", entry, re.IGNORECASE ) if grOption: optsFound.append(opt) report["audit"] = True report["options"] = dict() if len(optsFound) == len(self._indexes['options']): report["result"] = "success" else: report["result"] = "failed" for opt in self._indexes["options"]: if opt not in optsFound: report["options"][opt] = f"{opt} is not removed. You should disable it" report["description"] = self._indexes["description"] report["level"] = self._indexes["level"] report["recommand_value"] = self._indexes["recommand_value"] print(report) return report def _getDirectoryPath(self, line) -> str: """ This function return the directory path """ path = None grDirectory = re.search( f"", "") path = path.replace("\"", "") return path def _constructReports(self): """ Construct dictionary for result of the tests Each entry contains: Key: - description: description of the vulnerability - level: high, medium or low """ self._reports['ssl'] = dict() self._reports['signature'] = dict() self._reports['indexes'] = dict()