From 5691458f729446646e4c56b9b09ca0a34c18566d Mon Sep 17 00:00:00 2001 From: Alexander Gnatyuk Date: Mon, 18 Apr 2022 14:14:59 +0300 Subject: [PATCH 1/3] Parser,JsonBuilder,Template: fix issues with overlapping cpes --- contrib/internal_types/flan_types.py | 2 +- contrib/parsers/flan_xml_parser.py | 28 ++++++++---- .../report_builders/json_report_builder.py | 14 +++--- .../templates/jinja2_report.html | 43 ++++++++++++++----- 4 files changed, 61 insertions(+), 26 deletions(-) diff --git a/contrib/internal_types/flan_types.py b/contrib/internal_types/flan_types.py index fb63476..d6e4809 100644 --- a/contrib/internal_types/flan_types.py +++ b/contrib/internal_types/flan_types.py @@ -55,4 +55,4 @@ class ScanResult: """ def __init__(self): self.locations = defaultdict(list) # type: Dict[str, List[str]] - self.vulns = [] # type: List[Vuln] + self.vulns = defaultdict(list) # type: Dict[str, List[Vuln]] diff --git a/contrib/parsers/flan_xml_parser.py b/contrib/parsers/flan_xml_parser.py index e0e0b43..554bc6c 100644 --- a/contrib/parsers/flan_xml_parser.py +++ b/contrib/parsers/flan_xml_parser.py @@ -1,5 +1,5 @@ from collections import defaultdict -from typing import Dict, Any, List, Set +from typing import Dict, OrderedDict, Any, List, Set import xmltodict @@ -54,7 +54,7 @@ def parse(self, data: Dict[str, Any]): else: self.parse_host(hosts) - def parse_vuln(self, app_name: str, vuln: List[Dict[str, Any]]): + def parse_vuln(self, app_name: str, cpe: str, vuln: List[Dict[str, Any]]): vuln_name = '' severity = '' vuln_type = '' @@ -66,7 +66,10 @@ def parse_vuln(self, app_name: str, vuln: List[Dict[str, Any]]): elif field['@key'] == 'type': vuln_type = field['#text'] - self.results[app_name].vulns.append(Vuln(vuln_name, vuln_type, severity)) + if cpe: + self.results[app_name].vulns[cpe].append(Vuln(vuln_name, vuln_type, severity)) + else: + self.results[app_name].vulns[app_name].append(Vuln(vuln_name, vuln_type, severity)) def parse_script(self, ip_addr: str, port: str, app_name: str, script: Dict[str, Any]): if 'table' not in script: @@ -74,12 +77,21 @@ def parse_script(self, ip_addr: str, port: str, app_name: str, script: Dict[str, app_name) return self.vulnerable_services.append(app_name) - script_table = script['table']['table'] + script_table = script['table'] if isinstance(script_table, list): - for vuln in script_table: - self.parse_vuln(app_name, vuln['elem']) - else: - self.parse_vuln(app_name, script_table['elem']) + for table in script_table: + cpe = table.get("@key") + for vuln in table['table']: + self.parse_vuln(app_name, cpe, vuln['elem']) + elif (isinstance(script_table, OrderedDict) + and isinstance(script_table['table'], list)): + cpe = script_table.get("@key") + for vuln in script_table['table']: + self.parse_vuln(app_name, cpe, vuln['elem']) + elif (isinstance(script_table, OrderedDict) + and isinstance(script_table['table'], OrderedDict)): + cpe = script_table.get("@key") + self.parse_vuln(app_name, cpe, script_table['table']['elem']) def parse_port(self, ip_addr: str, port: Dict[str, Any]): if port['state']['@state'] == 'closed': diff --git a/contrib/report_builders/json_report_builder.py b/contrib/report_builders/json_report_builder.py index 2d0ede8..5c4735d 100644 --- a/contrib/report_builders/json_report_builder.py +++ b/contrib/report_builders/json_report_builder.py @@ -1,4 +1,5 @@ import json +from collections import defaultdict from typing import Any, Dict, List from contrib.descriptions import VulnDescriptionProvider @@ -21,15 +22,16 @@ def build(self) -> Any: def add_vulnerable_services(self, scan_results: Dict[str, ScanResult]): for app_name, result in scan_results.items(): self._buffer['vulnerable'][app_name] = { - 'vulnerabilities': [], + 'vulnerabilities': defaultdict(list), 'locations': self._serialize_locations(result.locations) } - for v in result.vulns: - data = v.to_dict() - description = self.description_provider.get_description(v.name, v.vuln_type) - data['description'], data['url'] = description.text, description.url - self._buffer['vulnerable'][app_name]['vulnerabilities'].append(data) + for vuln_cpe, vuln in result.vulns.items(): + for v in vuln: + data = v.to_dict() + description = self.description_provider.get_description(v.name, v.vuln_type) + data['description'], data['url'] = description.text, description.url + self._buffer['vulnerable'][app_name]['vulnerabilities'][vuln_cpe].append(data) def add_non_vulnerable_services(self, scan_results: Dict[str, ScanResult]): for app_name, result in scan_results.items(): diff --git a/contrib/report_builders/templates/jinja2_report.html b/contrib/report_builders/templates/jinja2_report.html index 5775bb3..a87af3d 100644 --- a/contrib/report_builders/templates/jinja2_report.html +++ b/contrib/report_builders/templates/jinja2_report.html @@ -71,6 +71,19 @@ border: 1px solid black; } + .sub_vuln { + font-size: 20px; + padding: 20px 0 15px 0; + font-family: 'helvetica', serif; + font-weight: bold; + position: relative; + left: -100px; + } + + .sub_vuln:before { + content: "\2014 "; + } + .vuln_desc { padding-top: 7px; } @@ -135,24 +148,32 @@

Summary

Services with vulnerabilities:

    + {% set ns = namespace(vulns_count=0) %} {% for service, report in data.vulnerable.items() %} + {% set ns.vulns_count = 0 %}
  1. {{ service }}
      - {% for vuln in report.vulnerabilities %} -
    • -
      - {{ vuln.name }} {{ vuln.severity_str }} - ({{ vuln.severity }}) -
      -
      {{ vuln.description }}
      -
    • + {% for vuln_cpe, vuln in report.vulnerabilities.items() %} + {% set ns.vulns_count = ns.vulns_count + vuln|length %} +
        +
        {{vuln_cpe}}
        + {% for v in vuln %} +
      • +
        + {{ v.name }} {{ v.severity_str }} + ({{ v.severity }}) +
        +
        {{ v.description }}
        +
      • + {% endfor %} +
      {% endfor %}
    -
    The above {{ report.vulnerabilities|length }} vulnerabilities apply +
    The above {{ ns.vulns_count }} vulnerabilities apply to these network locations:
      @@ -191,4 +212,4 @@

      Services with no known vulnerabilities:

- \ No newline at end of file + From 1a4f3cc54686df73805b9c0fe259b6fc67ea767b Mon Sep 17 00:00:00 2001 From: Alexander Gnatyuk Date: Mon, 18 Apr 2022 15:04:59 +0300 Subject: [PATCH 2/3] mdBuilder: add subsections for multiple cpe in one section --- .../markdown_report_builder.py | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/contrib/report_builders/markdown_report_builder.py b/contrib/report_builders/markdown_report_builder.py index f51b346..170d7ed 100644 --- a/contrib/report_builders/markdown_report_builder.py +++ b/contrib/report_builders/markdown_report_builder.py @@ -34,16 +34,18 @@ def add_vulnerable_services(self, scan_results: Dict[str, ScanResult]): for i, pair in enumerate(scan_results.items(), start=1): app_name, report = pair # type: str, ScanResult self._append_service(i, app_name) - num_vulns = len(report.vulns) - - for v in report.vulns: - description = self.description_provider.get_description(v.name, v.vuln_type) - self._append_line('- [**{name}** {severity} ({severity_num})]({link} "{title}")' - .format(name=v.name, severity=v.severity_str, severity_num=v.severity, - link=description.url, title=v.name), spaces=4) - self._append_line('```text', separators=1, spaces=6) - self._append_line(description.text, separators=1, spaces=6) - self._append_line('```', spaces=6) + num_vulns = sum([len(report.vulns[v]) for v in report.vulns]) + + for cpe, vuln in report.vulns.items(): + self._append_line('- **{0}**'.format(cpe)) + for v in vuln: + description = self.description_provider.get_description(v.name, v.vuln_type) + self._append_line('- [**{name}** {severity} ({severity_num})]({link} "{title}")' + .format(name=v.name, severity=v.severity_str, severity_num=v.severity, + link=description.url, title=v.name), spaces=4) + self._append_line('```text', separators=1, spaces=6) + self._append_line(description.text, separators=1, spaces=6) + self._append_line('```', spaces=6) self._append_line('The above {num} vulnerabilities apply to these network locations'.format(num=num_vulns), spaces=4) From 795e80cda0225112fbfc6405698dc306442fac28 Mon Sep 17 00:00:00 2001 From: Alexander Gnatyuk Date: Mon, 18 Apr 2022 18:19:16 +0300 Subject: [PATCH 3/3] texBuilder: add subsections for multiple cpe in one section --- .../report_builders/latex_report_builder.py | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/contrib/report_builders/latex_report_builder.py b/contrib/report_builders/latex_report_builder.py index f7ef197..d812718 100644 --- a/contrib/report_builders/latex_report_builder.py +++ b/contrib/report_builders/latex_report_builder.py @@ -38,25 +38,27 @@ def header(self) -> str: def add_vulnerable_services(self, scan_results: Dict[str, ScanResult]): for s, report in scan_results.items(): self._append('\\item \\textbf{\\large ' + s + ' \\large}') - vulns = report.vulns + vulns = report.vulns.items() locations = report.locations - num_vulns = len(vulns) - - for v in vulns: - description = self.description_provider.get_description(v.name, v.vuln_type) - severity_name = v.severity_str - self._append('\\begin{figure}[h!]\n') - self._append('\\begin{tabular}{|p{16cm}|}\\rowcolor[HTML]{' - + self.colors[severity_name] - + '} \\begin{tabular}{@{}p{15cm}>{\\raggedleft\\arraybackslash} p{0.5cm}@{}}\\textbf{' - + v.name + ' ' + severity_name + ' (' - + str(v.severity) - + ')} & \\href{' + description.url - + '}{\\large \\faicon{link}}' - + '\\end{tabular}\\\\\n Summary:' - + description.text - + '\\\\ \\hline \\end{tabular} ') - self._append('\\end{figure}\n') + num_vulns = sum([len(report.vulns[v]) for v in report.vulns]) + + for cpe, vuln in vulns: + self._append('\\item \\textbf{\\large ' + cpe + ' \\large}') + for v in vuln: + description = self.description_provider.get_description(v.name, v.vuln_type) + severity_name = v.severity_str + self._append('\\begin{figure}[h!]\n') + self._append('\\begin{tabular}{|p{16cm}|}\\rowcolor[HTML]{' + + self.colors[severity_name] + + '} \\begin{tabular}{@{}p{15cm}>{\\raggedleft\\arraybackslash} p{0.5cm}@{}}\\textbf{' + + v.name + ' ' + severity_name + ' (' + + str(v.severity) + + ')} & \\href{' + description.url + + '}{\\large \\faicon{link}}' + + '\\end{tabular}\\\\\n Summary:' + + description.text + + '\\\\ \\hline \\end{tabular} ') + self._append('\\end{figure}\n') self._append('\\FloatBarrier\n\\textbf{The above ' + str(num_vulns)