it-security-2-deep-thought/scan_output_parser/lynis.py

109 lines
3.9 KiB
Python
Raw Normal View History

2021-01-09 06:00:27 +01:00
import re
LYNIS_REGEX = {
"green": r"\[1;32m", # more advanced "green": r"\[ .*\[1;32m([\w\d ]*).*",
"yellow": r"\[1;33m",
"red": r"\[1;31m",
"white": r"\[1;37m",
"heading": r".*\[1;33m([\w\d ,:-]*).*"
}
LYNIS_BLOCKS = {
"Boot and services",
"Kernel",
"Memory and Processes",
"Users, Groups and Authentication",
"Shells",
"File systems",
"USB Devices",
"Storage",
"NFS",
"Name services",
"Ports and packages",
"Networking",
"Printers and Spools",
"Software: e-mail and messaging",
"Software: firewalls",
"Software: webserver",
"SSH Support",
"Databases",
"PHP",
"Logging and files",
"Insecure services",
"Scheduled tasks",
"Accounting",
"Time and Synchronization",
"Cryptography",
"Security frameworks",
"Software: Malware",
"File Permissions",
"Home directories",
"Kernel Hardening",
"Hardening"
}
def lynis_get_base(path_to_log):
with open(path_to_log) as handle:
text = handle.read()
blocks = text.split("[+]")
interesting_blocks = {}
for block in blocks:
heading = re.findall(LYNIS_REGEX["heading"], block.splitlines()[0])
if heading and heading[0] in LYNIS_BLOCKS:
block_text = "".join(block.splitlines()[2:])
interesting_blocks[heading[0]] = {
"text": block_text,
"counts": {
"green": len(re.findall(LYNIS_REGEX['green'], block_text)),
"yellow": len(re.findall(LYNIS_REGEX['yellow'], block_text)),
"red": len(re.findall(LYNIS_REGEX['red'], block_text)),
"white": len(re.findall(LYNIS_REGEX['white'], block_text)),
}
}
interesting_blocks["GENERAL"] = {}
if warning_count := re.findall(r".*Warnings.* \((\d+)\)", text):
interesting_blocks["GENERAL"]["warningCount"] = int(warning_count[0])
if suggestion_count := re.findall(r".*Suggestions.* \((\d+)\)", text):
interesting_blocks["GENERAL"]["suggestionCount"] = int(suggestion_count[0])
if hardening_index := re.findall(r".*Hardening index.*m(\d+)", text):
interesting_blocks["GENERAL"]["hardeningIndex"] = int(hardening_index[0])
if tests_performed := re.findall(r".*Tests performed.*m(\d+)", text):
interesting_blocks["GENERAL"]["testsPerformed"] = int(tests_performed[0])
return interesting_blocks
def lynis_add_special_properties(lynis_base):
def find_prop(category, key, regex, as_int=False):
if category in lynis_base:
if match := re.findall(regex, lynis_base[category]["text"]):
lynis_base[category][key] = int(match[0]) if as_int else match[0]
find_prop("Boot and services", "runningServices", r"found (\d+) running services", as_int=True)
find_prop("Boot and services", "enabledServices", r"found (\d+) enabled services", as_int=True)
find_prop("Kernel", "defaultRunLevel", r"RUNLEVEL (\d+)", as_int=True)
find_prop("Kernel", "activeModules", r"Found (\d+) active modules", as_int=True)
find_prop("Kernel", "kernelUpdate", r"Checking for available kernel update.*? \[ .*?m([\w ]*).*? \]")
find_prop("Shells", "numShells", r"Result: found (\d*) shells", as_int=True)
find_prop("Networking", "ipv6Enabled", r"Checking IPv6 configuration.*? \[ .*?m([\w ]*).*? \]")
find_prop("Time and Synchronization", "lastSync", r"Last time synchronization.*? \[ .*?m([\w ]*).*? \]")
find_prop("Security frameworks", "unconfinedProcesses", r"Found (\d+) unconfined processes")
def lynis_remove_text_from_blocks(lynis_base):
for block in lynis_base:
try:
del lynis_base[block]["text"]
except KeyError:
pass
def lynis_parse(path):
lynis_base = lynis_get_base(path)
lynis_add_special_properties(lynis_base)
lynis_remove_text_from_blocks(lynis_base)
return lynis_base