Parsing postfix

This commit is contained in:
geoffrey 2023-09-10 18:08:48 +02:00
parent 3cd25dce85
commit 582b85bb07
14 changed files with 281 additions and 77 deletions

@ -1,6 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
def apaches_ssl() -> list: def apache() -> list:
ssl = list() ssl = list()
# Check if apaches has disabled the bad SSL/TLS version # Check if apaches has disabled the bad SSL/TLS version

@ -3,7 +3,15 @@
def postfix() -> list: def postfix() -> list:
postfix = list() postfix = list()
postfix.append({ postfix.append({
'directive': "inet_interfaces", 'from': 'cis',
'value': "loopback-only", 'id': '',
'description': 'Disable the listening from all interfaces',
'flag': "inet_interfaces",
'level': 'medium',
'value': [
"127.0.0.1",
"loopback-only",
"[::1]",
]
}) })
return postfix return postfix

@ -6,6 +6,8 @@ system:
postfix_file: "/etc/postfix/master.cf" postfix_file: "/etc/postfix/master.cf"
sysctl: sysctl:
sysctl_file: "/etc/sysctl.conf" sysctl_file: "/etc/sysctl.conf"
apache:
apache_directory: "/etc/apache2/"
# Audit application # Audit application
application: application:

@ -11,6 +11,7 @@ LOW = "low"
AUDIT_SYSTEM = [ AUDIT_SYSTEM = [
"sysctl", "sysctl",
"postfix", "postfix",
"apache",
] ]
AUDIT_APPLICATION = [ AUDIT_APPLICATION = [
@ -24,6 +25,8 @@ def generateConfig() -> dict:
config["system"] = dict() config["system"] = dict()
config["system"]["postfix"] = dict() config["system"]["postfix"] = dict()
config["system"]["postfix"]["postfix_file"] = "/etc/postfix/main.cf" config["system"]["postfix"]["postfix_file"] = "/etc/postfix/main.cf"
config["system"]["apache"] = dict()
config["system"]["apache"]["apache_directory"] = "/etc/apache2/"
config["system"]["sysctl"] = dict() config["system"]["sysctl"] = dict()
config["system"]["sysctl"]["sysctl_file"] = "/etc/sysctl.conf" config["system"]["sysctl"]["sysctl_file"] = "/etc/sysctl.conf"
config["system"]["exclude_plugins"] = list() config["system"]["exclude_plugins"] = list()
@ -80,24 +83,5 @@ def parsingConfigFile(filename, configs):
# except TypeError: # except TypeError:
# pass # pass
#fdata = f.read()
#lines = fdata.splitlines()
#for line in lines:
# line = line.decode('utf-8')
# try:
# sLine = line.split("=")
# flag = sLine[0].strip()
# value = sLine[1].strip()
# if flag in configs:
# if flag == "exclude_plugins":
# value = value.replace("\"", "")
# value = value.split(",")
# configs[flag] = value
# except IndexError:
# pass
except FileNotFoundError: except FileNotFoundError:
print(f"Config file {filename} not found. Bypass it") print(f"Config file {filename} not found. Bypass it")

@ -8,7 +8,7 @@ class Dispatcher:
def runPlugin(self, plugin, *args) -> dict: def runPlugin(self, plugin, *args) -> dict:
""" """
We run the puglin. The result of this function We run the plugin. The result of this function
is the report of the audit is the report of the audit
""" """
return self._plugins[plugin](self, *args) return self._plugins[plugin](self, *args)
@ -18,3 +18,5 @@ class Dispatcher:
cls._plugins[plugin.__name__] = plugin cls._plugins[plugin.__name__] = plugin
return plugin return plugin
def get_plugins(self):
return self._plugins

@ -1,14 +1,13 @@
#!/usr/bin/env python #!/usr/bin/env python
from argparse import ArgumentParser from argparse import ArgumentParser
from core.sysctl import Sysctl from core.plugins.sysctl import Sysctl
from core.postfix import Postfix from core.plugins.postfix import Postfix
from core.plugins.apache import Apache
from core.report import generateHtmlReport from core.report import generateHtmlReport
from core.config import AUDIT_SYSTEM, AUDIT_APPLICATION, generateConfig, parsingConfigFile from core.config import AUDIT_SYSTEM, AUDIT_APPLICATION, generateConfig, parsingConfigFile
from core.dispatcher import Dispatcher from core.dispatcher import Dispatcher
from utils import getHostname, getKernelVersion, identifySystem, getCodeName, getRelease from utils import getHostname, getKernelVersion, identifySystem, getCodeName, getRelease
from os import listdir
from os.path import isdir
def checkArguments(): def checkArguments():
@ -20,15 +19,14 @@ def checkArguments():
def getAllPlugins(audit): def getAllPlugins(audit):
print(f"List all plugins for {audit}") print(f"List all plugins for {audit}")
path = str()
if audit == "system":
path = "audit/system/plugins/"
else:
path = "audit/applications/"
for directory in listdir(path): if audit == "system":
if isdir(f"{path}/{directory}"): dis = Dispatcher()
print(directory) plugins = dis.get_plugins()
for plugin in plugins:
print(plugin)
elif audit == "application":
pass
def main(): def main():
args = checkArguments() args = checkArguments()
@ -91,5 +89,11 @@ def postfix(*args) -> dict:
postfix.runAudit() postfix.runAudit()
return postfix.getReports() return postfix.getReports()
@Dispatcher.register_plugins
def apache(*args) -> dict:
apache = Apache(args[1])
apache.runAudit()
return apache.getReports()
if __name__ == "__main__": if __name__ == "__main__":
main() main()

67
core/plugins/apache.py Normal file

@ -0,0 +1,67 @@
#!/usr/bin/env python3
import re
from os import listdir
from os.path import isdir
from audit.system.plugins.apache.apache import apache
class Apache:
def __init__(self, arguments):
self._objects = apache()
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")
self._runParsing()
def getReports(self) -> dict:
return self._reports
def _runParsing(self):
# Check if the file exist
path = f"{self._apache_directory}/sites-available"
if isdir(self._apache_directory):
for site in listdir(path):
with open(f"{path}/{site}", 'rb') as f:
self._parseFile(f)
else:
self._reports["apache"]["test"] = "No directory found"
def _parseFile(self, fdata):
data = fdata.read()
lines = data.splitlines()
for line in lines:
line = line.decode('utf-8')
# check if SSL is enable for the VirtualHost
grSSLEngine = re.search("SSLEngine on", line)
if grSSLEngine:
print(line)
def _check_value_exist(self, line, value) -> bool:
grValue = re.search(value, line)
if grValue:
return True
return False
def _constructReports(self):
"""
Construct dictionary for result of the tests
Each entry contains:
Key:
- filename: filename of the test
- line: line of the test
- parse: Display the line where the vulnerabilites has been found
- description: description of the vulnerability
- level: high, medium or low
"""
self._reports['apache'] = dict()

89
core/plugins/postfix.py Normal file

@ -0,0 +1,89 @@
#!/usr/bin/env python3
import re
from audit.system.plugins.postfix.postfix import postfix
class Postfix:
def __init__(self, arguments):
self._objects = postfix()
self._reports = dict()
self._postfix_file = arguments["postfix_file"]
# Create the report
self._constructReports()
# Report
self._reports["filename"] = self._postfix_file
def runAudit(self):
print("Running test for postfix")
self._runParsing()
def getReports(self) -> dict:
return self._reports
def _runParsing(self):
# Check if the file exist
try:
with open(self._postfix_file, 'rb') as fdata:
self._parseFile(fdata)
except FileNotFoundError:
print("No postfix file found. Add into the report")
pass
def _parseFile(self, fdata):
data = fdata.read()
lines = data.splitlines()
for line in lines:
line = line.decode('utf-8')
for obj in self._objects:
grDirective = re.search(
f"^({obj['flag']})",
line
)
if grDirective:
res = False
if not isinstance(obj['value'], list):
obj['value'] = [obj['value']]
for value in obj['value']:
res = self._check_value_exist(line, value)
if res:
break
if res:
self._reports["postfix"][obj['flag']] = dict()
self._reports["postfix"][obj['flag']]["result"] = "success"
self._reports["postfix"][obj['flag']]["description"] = obj['description']
self._reports["postfix"][obj['flag']]["flagFound"] = line
else:
self._reports["postfix"][obj['flag']] = dict()
self._reports["postfix"][obj['flag']]["result"] = "failed"
self._reports["postfix"][obj["flag"]]["recommand_value"] = obj["value"]
self._reports["postfix"][obj['flag']]["description"] = obj['description']
self._reports["postfix"][obj['flag']]["flag"] = obj['flag']
def _check_value_exist(self, line, value) -> bool:
if '[' in value:
value = value.replace('[', '\[')
if ']' in value:
value = value.replace(']', '\]')
grValue = re.search(value, line)
if grValue:
return True
return False
def _constructReports(self):
"""
Construct dictionary for result of the tests
Each entry contains:
Key:
- filename: filename of the test
- line: line of the test
- parse: Display the line where the vulnerabilites has been found
- description: description of the vulnerability
- level: high, medium or low
"""
self._reports['postfix'] = dict()

@ -1,29 +0,0 @@
#!/usr/bin/env python3
from audit.system.plugins.postfix.parsing import Parsing
from audit.system.plugins.postfix.postfix import postfix
class Postfix:
def __init__(self, arguments):
self._objects = dict()
self._reports = dict()
self._arguments = arguments
self._postfix()
self._parsing = Parsing(self._objects, arguments)
def _postfix(self):
"""
Store all data to analyze in the object variable
"""
self._objects = postfix()
def runAudit(self):
print("Running test for postfix")
self._parsing.runParsing()
self._reports = self._parsing.getResults()
def getReports(self) -> dict:
return self._reports

@ -24,8 +24,11 @@ def generateHtmlReport(data):
dataJinja2['plugins'].append(f"{plugin}.html.j2") dataJinja2['plugins'].append(f"{plugin}.html.j2")
if 'postfix' in data['system']: if 'postfix' in data['system']:
#print(data['system']['postfix']) dataJinja2['postfix'] = dict()
pass dataJinja2['postfix']['filename'] = data["system"]["postfix"]["filename"]
dataJinja2['postfix']['vulnerabilities'] = data['system']['postfix']['postfix']
_generateAccordion(dataJinja2['postfix']['vulnerabilities'])
if 'sysctl' in data['system']: if 'sysctl' in data['system']:
dataJinja2['sysctl'] = dict() dataJinja2['sysctl'] = dict()
@ -33,11 +36,10 @@ def generateHtmlReport(data):
dataJinja2['sysctl']['file']['filename'] = data['system']['sysctl']['file']['filename'] dataJinja2['sysctl']['file']['filename'] = data['system']['sysctl']['file']['filename']
dataJinja2['sysctl']['file']['sysctl'] = data['system']['sysctl']['file']['sysctl'] dataJinja2['sysctl']['file']['sysctl'] = data['system']['sysctl']['file']['sysctl']
index = 1 _generateAccordion(dataJinja2['sysctl']['file']['sysctl'])
for sysctl in dataJinja2['sysctl']['file']['sysctl']: if 'apache' in data['system']:
dataJinja2['sysctl']['file']['sysctl'][sysctl]['accordion-id'] = f"accordion-{index}" pass
index += 1
dataJinja2['year'] = '2023' dataJinja2['year'] = '2023'
dataJinja2['hostname'] = data['hostname'] dataJinja2['hostname'] = data['hostname']
@ -50,3 +52,9 @@ def generateHtmlReport(data):
print("The report is generated at this location: " \ print("The report is generated at this location: " \
f"reports/reports_{today}.html") f"reports/reports_{today}.html")
def _generateAccordion(obj):
index = 1
for entry in obj:
obj[entry]['accordion-id'] = f"accordion-{index}"
index += 1

@ -0,0 +1,43 @@
<h3 class="fs-3">Apache</h3>
{% for item in data['postfix']['vulnerabilities'] %}
<div class="accordion" id="accordionSysctl">
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#{{ data['postfix']['vulnerabilities'][item]['accordion-id'] }}" aria-expanded="true" aria-controls="{{ data['postfix']['vulnerabilities'][item]['accordion-id'] }}">
<strong>{{ item }}</strong>
{% if data['postfix']['vulnerabilities'][item]['result'] == 'failed' %}
<span class="text-bg-danger p-1" style="padding-left:10pt;padding-right:10pt;margin-left:15pt;">{{ data['postfix']['vulnerabilities'][item]['result'] }}</span>
{% elif data['postfix']['vulnerabilities'][item]['result'] == 'success' %}
<span class="text-bg-success p-1" style="padding-left:10pt;padding-right:10pt;margin-left:15pt;">{{ data['postfix']['vulnerabilities'][item]['result'] }}</span>
{% endif %}
</button>
</h2>
<div id="{{ data['postfix']['vulnerabilities'][item]['accordion-id'] }}" class="accordion-collapse collapse" data-bs-parent="#accordionPostfix">
<div class="accordion-body">
{{ data['postfix']['vulnerabilities'][item]['description'] }}. <br />
{% if data['postfix']['vulnerabilities'][item]['result'] == 'success' %}
<div class="bd-example-snippet bd-code-snippet">
<div class="highlight">
<pre tabindex="0" class="chroma"><code class="language-shell">
{{ data['postfix']['vulnerabilities'][item]['flagFound'] }}
</pre></code>
</div>
</div>
{% else %}
For resolving the issue, add this line in the <strong>{{ data['postfix']['filename'] }}</strong> vulnerabilities:
<div class="bd-example-snippet bd-code-snippet">
<div class="highlight">
<pre tabindex="0" class="chroma"><code class="language-shell">
{% for value in data['postfix']['vulnerabilities'][item]['recommand_value'] %}
{{ data['postfix']['vulnerabilities'][item]['flag'] }} = {{ value }}
{% endfor %}
</pre></code>
</div>
</div>
{% endif %}
</div>
</div>
</div>
</div>
{% endfor %}

@ -31,6 +31,7 @@
{% for plugin in data['plugins'] %} {% for plugin in data['plugins'] %}
{% include plugin %} {% include plugin %}
<div style="margin-bottom:15pt"></div>
{% endfor %} {% endfor %}

@ -1,16 +1,41 @@
<h3 class="fs-3">Postfix</h3> <h3 class="fs-3">Postfix</h3>
{% for item in data['postfix'] %} {% for item in data['postfix']['vulnerabilities'] %}
<div class="accordion" id="accordionPostfix"> <div class="accordion" id="accordionSysctl">
<div class="accordion-item"> <div class="accordion-item">
<h2 class="accordion-header"> <h2 class="accordion-header">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#{{ item['accordion-id'] }}" aria-expanded="true" aria-controls="collapseOne"> <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#{{ data['postfix']['vulnerabilities'][item]['accordion-id'] }}" aria-expanded="true" aria-controls="{{ data['postfix']['vulnerabilities'][item]['accordion-id'] }}">
Accordion Item #1 <strong>{{ item }}</strong>
{% if data['postfix']['vulnerabilities'][item]['result'] == 'failed' %}
<span class="text-bg-danger p-1" style="padding-left:10pt;padding-right:10pt;margin-left:15pt;">{{ data['postfix']['vulnerabilities'][item]['result'] }}</span>
{% elif data['postfix']['vulnerabilities'][item]['result'] == 'success' %}
<span class="text-bg-success p-1" style="padding-left:10pt;padding-right:10pt;margin-left:15pt;">{{ data['postfix']['vulnerabilities'][item]['result'] }}</span>
{% endif %}
</button> </button>
</h2> </h2>
<div id="{{ item['accordion-id'] }}" class="accordion-collapse collapse show" data-bs-parent="#accordionPostfix"> <div id="{{ data['postfix']['vulnerabilities'][item]['accordion-id'] }}" class="accordion-collapse collapse" data-bs-parent="#accordionPostfix">
<div class="accordion-body"> <div class="accordion-body">
<strong></strong> {{ data['postfix']['vulnerabilities'][item]['description'] }}. <br />
{% if data['postfix']['vulnerabilities'][item]['result'] == 'success' %}
<div class="bd-example-snippet bd-code-snippet">
<div class="highlight">
<pre tabindex="0" class="chroma"><code class="language-shell">
{{ data['postfix']['vulnerabilities'][item]['flagFound'] }}
</pre></code>
</div>
</div>
{% else %}
For resolving the issue, add this line in the <strong>{{ data['postfix']['filename'] }}</strong> vulnerabilities:
<div class="bd-example-snippet bd-code-snippet">
<div class="highlight">
<pre tabindex="0" class="chroma"><code class="language-shell">
{% for value in data['postfix']['vulnerabilities'][item]['recommand_value'] %}
{{ data['postfix']['vulnerabilities'][item]['flag'] }} = {{ value }}
{% endfor %}
</pre></code>
</div>
</div>
{% endif %}
</div> </div>
</div> </div>
</div> </div>