commit 96342a2b2c95e5d288ec2770647d230841a752b2 Author: geoffrey Date: Sun Jun 4 21:26:27 2023 +0200 First commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bc171d5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +__pycache__/ +__pycache__/** diff --git a/main.py b/main.py new file mode 100644 index 0000000..954c955 --- /dev/null +++ b/main.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python + +from argparse import ArgumentParser +from sysctl import Sysctl + + +def checkArguments(): + args = ArgumentParser(description="Check Gitlab repositories") + args.add_argument('-a', '--audit', help="Kind of audit", choices=['system', 'application']) + return args.parse_args() + +def main(): + args = checkArguments() + + # If audit is not specified + if args.audit is None: + print("Please, you must specify the audit type") + exit(1) + + # Report + report = dict() + report['system'] = None + + # Audit application + if args.audit == "application": + pass + + # Audit system + if args.audit == "system": + sysctl = Sysctl() + sysctl.runAudit() + + report['sysctl'] = sysctl.getReports() + +if __name__ == "__main__": + main() diff --git a/old_main.py b/old_main.py new file mode 100755 index 0000000..f755304 --- /dev/null +++ b/old_main.py @@ -0,0 +1,118 @@ +# coding: utf-8 + +import re +from os import path +from subprocess import call, check_output, run + + +# 3 levels to tests: low, medium anh high +CHECKSLIST = {} + +# TIPS +# https://www.process.st/server-security/ + +def identifySystem(): + os = None + with open('/etc/issue', 'r') as f: + line = f.readline() + if re.search('Arch Linux', line): + os = 'ARCHLINUX' + elif re.search('Ubuntu', line): + os = 'UBUNTU' + elif re.search('Debian', line): + os = 'DEBIAN' + else: + os = 'UNKNOWN' + + return os + +def check_upgrade_packages(): + pass + +def check_telnet_is_open(): + # check port 23 is listening + r = run(['ss', '-atn'], capture_output=True) + r = r.stdout.decode() + print(r) + +def check_empty_local_passwords(): + pass + +def check_security_access(): + # Check in /etc/security/access + pass + +def check_hosts_allow(): + # Check in /etc/hosts.allow + pass + +def check_sshd_root(): + res = False + + if not path.exists("/etc/ssh/sshd_config"): + print("File sshd_config doesn't exist") + return False + + with open("/etc/ssh/sshd_config", "r") as f: + for l in f.readlines(): + l = l.replace('\n', '') + if re.search("PermitRootLogin.*root", l): + if not re.search("^#", l): + res = True + return res + +def generateChecksList(): + # LOW + CHECKSLIST['low'] = [] + CHECKSLIST['low'].append({ + 'callback': check_sshd_root, + 'name': check_sshd_root.__name__, + 'resolution': 'Please, remove root auth to your server', + 'score': 100 + }) + CHECKSLIST['low'].append({ + 'callback': check_upgrade_packages, + 'name': check_upgrade_packages.__name__, + 'resolution': 'Please, upgrade your packages', + 'score': 50 + }) + CHECKSLIST['low'].append({ + 'callback': check_telnet_is_open, + 'name': check_telnet_is_open.__name__, + 'resolution': 'Telnet is enabled. Please, disabled this program if you could.', + 'score': 50 + }) + # MEDIUM + CHECKSLIST['medium'] = {} + # HIGH + CHECKSLIST['high'] = {} + +def getTotalScore(): + score = 0 + for entry in CHECKSLIST['low']: + score += entry['score'] + + return score + +def main(): + # Generate our checklist + generateChecksList() + + # Get total score + totalScore = getTotalScore() + + # Identify system + identifySystem() + + score = totalScore + for entry in CHECKSLIST['low']: + print(f'Checking {entry["name"]}...') + res = entry['callback']() + if res: + print(entry['resolution']) + score -= entry['score'] + + print(f'Your total score: {score}') + +if __name__ == "__main__": + main() diff --git a/parsing.py b/parsing.py new file mode 100644 index 0000000..8bc18a4 --- /dev/null +++ b/parsing.py @@ -0,0 +1,41 @@ +import re +from json import dumps + +class Parsing: + def __init__(self, objects, audit): + self._parsing = dict() + self._results = dict() + self._objects = objects + self._audit = audit + + def runParsing(self): + for audit in self._audit: + if audit['audit'] == 'file': + with open(audit['value'], 'rb') as fdata: + self._parseFile(fdata) + + def _parseFile(self, fdata): + data = fdata.read() + lines = data.splitlines() + + for line in lines: + self._parsingFile(line, self._objects['sysctl']) + + def _parsingFile(self, line, item) -> dict: + """ + This function parse the line and try to find the item in it + """ + res = None + + line = line.decode("utf-8") + for entry in item: + groupLine = re.search(entry['item'], line) + if groupLine: + sLine = line.split('=') + + return res + + def getResults(self) -> dict: + result = dict() + + return result diff --git a/parsing/__init__.py b/parsing/__init__.py new file mode 100644 index 0000000..e5a0d9b --- /dev/null +++ b/parsing/__init__.py @@ -0,0 +1 @@ +#!/usr/bin/env python3 diff --git a/parsing/base.py b/parsing/base.py new file mode 100644 index 0000000..dc401bc --- /dev/null +++ b/parsing/base.py @@ -0,0 +1,21 @@ +import re +from json import dumps + +class ParsingBase: + def __init__(self, objects, audit): + pass + + def runParsing(self): + pass + + def _parseFile(self, fdata): + pass + + def _parsingFile(self, line, item) -> dict: + """ + This function parse the line and try to find the item in it + """ + pass + + def getResults(self) -> dict: + pass diff --git a/parsing/sysctl.py b/parsing/sysctl.py new file mode 100644 index 0000000..b5b8f76 --- /dev/null +++ b/parsing/sysctl.py @@ -0,0 +1,97 @@ +import re +from json import dumps +from parsing.base import ParsingBase + +class Parsing(ParsingBase): + def __init__(self, objects, audit): + self._parsing = dict() + self._results = dict() + self._objects = objects + self._audit = audit + + def runParsing(self): + for audit in self._audit: + if audit['audit'] == 'file': + with open(audit['value'], 'rb') as fdata: + self._parseFile(fdata) + if audit['audit'] == 'process': + pass + + def _parseFile(self, fdata): + data = fdata.read() + lines = data.splitlines() + numLines = 1 + + self._constructResults(filename='/etc/sysctl.conf') + + for line in lines: + line = line.decode("utf-8") + + for obj in self._objects['sysctl']: + result = self._parsingFile(line, obj) + if len(result) == 0: + pass + # If the flag is found + else: + # And if the current value is not setted corectly for the vulnerability + print(result) + + self._results[obj['flag']].append({ + 'lineNumber': numLines, + 'value': obj['value'], + 'audit': 'failed' # Or success + }) + + if result['value'] != result['current_value']: + print(f"You must change the value to {obj['value']} for fixing the vulnerabilities") + + numLines += 1 + print(self._results) + + def _parsingFile(self, line, obj) -> dict: + """ + This function parse the line and try to find the item in it + """ + result = dict() + + groupLine = re.search(obj['flag'], line) + if groupLine: + # Avoid the comment + if not line.startswith('#'): + sLine = line.split('=') + flag = sLine[0] + value = int(sLine[1].strip('')) + #print(sLine) + + result['found'] = flag + result['current_value'] = value + result['value'] = obj['value'] + + if value != obj['value']: + print("Need to change the value") + print(sLine) + + return result + + def _constructResults(self, filename): + """ + 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 vulnerabilities + - level: high, medium or low + """ + self._results['filename'] = filename + + for sysctl in self._objects['sysctl']: + self._results[sysctl['flag']] = list() + print(self._results) + print("") + + def getResults(self) -> dict: + result = dict() + + return result diff --git a/report.py b/report.py new file mode 100644 index 0000000..fb506e6 --- /dev/null +++ b/report.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 + +from datetime import datetime + +def generateHtmlReport(data): + today = datetime.now().isoformat()[0:10].replace("-", "_") + html = "" \ + "" \ + "" \ + "" \ + "" \ + f"

Reports of {today}

" + + body = str() + for project in data['projects']: + body += f"

{project['name']}

" + + # For python + body += f"

Python

" + for py in project['python']: + body += f"

{py['file']}

" + for vul in py['vulnerabilities']: + body += f"
{vul['name']}
" + body += f"

" + body += f"Results:
" + for result in vul['results']: + body += f"Line: {result['lineNumber']}
" + body += f"Line: {result['line']}
" + body += f"Level: {result['level']}
" + body += f"Description: {result['description']}

" + body += f"

" + + html += body + #print(body) + html += "" + with open(f"reports/reports_{today}.html", "w") as f: + f.write(html) + diff --git a/sysctl.py b/sysctl.py new file mode 100644 index 0000000..98923b9 --- /dev/null +++ b/sysctl.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 + +from parsing.sysctl import Parsing +from vulnerabilities.system import system +from vulnerabilities.sysctl import sysctl + + +class Sysctl: + def __init__(self): + self._objects = dict() + self._reports = list() + self._audit = list() + + self._audit.append({ + 'audit': 'file', + 'value': '/etc/sysctl.conf', + }) + self._audit.append({ + 'audit': 'process', + 'value': 'sysctl -a', + }) + + self._sysctl() + + self._parsing = Parsing(self._objects, self._audit) + + def _sysctl(self): + self._objects['sysctl'] = sysctl() + + def runAudit(self): + # Read /etc/sysctl.conf + self._parsing.runParsing() + self._reports.append(self._parsing.getResults()) + + # Run process sysctl + + def getReports(self) -> list: + return self._reports + diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..13d411d --- /dev/null +++ b/utils.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 + +import re +from subprocess import run + + +def identifySystem(): + os = None + with open('/etc/issue', 'r') as f: + line = f.readline() + if re.search('Arch Linux', line): + os = 'ARCHLINUX' + elif re.search('Ubuntu', line): + os = 'UBUNTU' + elif re.search('Debian', line): + os = 'DEBIAN' + else: + os = 'UNKNOWN' + + return os + +def getKernelVersion(): + """ + This function get the kernel version Linux + """ + kernelVers = run(['/usr/bin/uname', '-r']) + return kernelVers.stdout diff --git a/vulnerabilities/calls.py b/vulnerabilities/calls.py new file mode 100644 index 0000000..411a6d7 --- /dev/null +++ b/vulnerabilities/calls.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 + +def calls() -> list: + calls = list() + # https://cwe.mitre.org/data/definitions/78.html + # For commoand injections + calls.append({ + "call": "exec", + "description": "", + "level": "high" + }) + calls.append({ + "call": "chown", + "description": "", + "level": "high" + }) + calls.append({ + "call": "chmod", + "description": "", + "level": "high" + }) + calls.append({ + "call": "rsync", + "description": "", + "level": "high" + }) + calls.append({ + "call": "wrap_socket", + "description": "This functions is deprecated. You must use SSLContext.wrap_context", + "level": "high" + }) + return calls + diff --git a/vulnerabilities/sysctl.py b/vulnerabilities/sysctl.py new file mode 100644 index 0000000..b7e339c --- /dev/null +++ b/vulnerabilities/sysctl.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 + +def sysctl() -> list: + sysctl = list() + + # https://access.redhat.com/security/sysctl/sysctl-2023-0179 + sysctl.append({ + "cve": "cve-2023-0179", + "description": "", + "flag": "kernel.unprivileged_userns_clone", + "value": 0, + "level": "medium", + "affectedSystem": ({ + 'linux': "Debian", + 'release': 'buster', + 'kernel': '4.19.249-2' + }) + }) + + # Best practice from CIS + sysctl.append({ + "cve": "", + "description": "Disable IPv4 forwarding", + "flag": "net.ipv4.conf.all.forwarding", + "value": 0, + "level": "medium" + }) + + return sysctl diff --git a/vulnerabilities/system.py b/vulnerabilities/system.py new file mode 100644 index 0000000..780c8cf --- /dev/null +++ b/vulnerabilities/system.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python3 + +def system() -> list: + system = list() + + return system