Example #1
0
    def __write_report(self):

        # Header
        print >>self.__fd, ""
        print >>self.__fd, "--= %s =--" % self.__colorize("Report", "cyan")
        print >>self.__fd, ""

        # Summary
        start_time, stop_time, run_time = parse_audit_times( *get_audit_times() )
        host_count  = Database.count(Data.TYPE_RESOURCE, Resource.RESOURCE_DOMAIN)
        host_count += Database.count(Data.TYPE_RESOURCE, Resource.RESOURCE_IP)
        vuln_count  = Database.count(Data.TYPE_VULNERABILITY)
        print >>self.__fd, "-# %s #- " % self.__colorize("Summary", "yellow")
        print >>self.__fd, ""
        print >>self.__fd, "Audit started:   %s" % self.__colorize(start_time, "yellow")
        print >>self.__fd, "Audit ended:     %s" % self.__colorize(stop_time, "yellow")
        print >>self.__fd, "Execution time:  %s" % self.__colorize(run_time, "yellow")
        print >>self.__fd, ""
        print >>self.__fd, "Scanned hosts:   %s" % self.__colorize(str(host_count), "yellow")
        print >>self.__fd, "Vulnerabilities: %s" % self.__colorize(str(vuln_count), "red" if vuln_count else "yellow")
        print >>self.__fd, ""

        # Audit scope
        if self.__show_data or not self.__console:
            table = Texttable()
            scope_domains = ["*." + r for r in Config.audit_scope.roots]
            scope_domains.extend(Config.audit_scope.domains)
            if Config.audit_scope.addresses:
                table.add_row(("IP addresses", "\n".join(Config.audit_scope.addresses)))
            if scope_domains:
                table.add_row(("Domains", "\n".join(scope_domains)))
            if Config.audit_scope.web_pages:
                table.add_row(("Web pages", "\n".join(Config.audit_scope.web_pages)))
            if table._rows:
                self.__fix_table_width(table)
                print >>self.__fd, "-# %s #- " % self.__colorize("Audit Scope", "yellow")
                print >>self.__fd, ""
                print >>self.__fd, table.draw()
                print >>self.__fd, ""

        # Discovered hosts
        if self.__show_data:
            need_header = True
            for domain in self.__iterate(Data.TYPE_RESOURCE, Resource.RESOURCE_DOMAIN):
                table = Texttable()
                self.__add_related(table, domain, Data.TYPE_RESOURCE, Resource.RESOURCE_IP, "IP Address")
                self.__add_related(table, domain, Data.TYPE_INFORMATION, Information.INFORMATION_GEOLOCATION, "Location")
                self.__add_related(table, domain, Data.TYPE_INFORMATION, Information.INFORMATION_WEB_SERVER_FINGERPRINT, "Web Server")
                self.__add_related(table, domain, Data.TYPE_INFORMATION, Information.INFORMATION_OS_FINGERPRINT, "OS Fingerprint")
                if table._rows:
                    if need_header:
                        need_header = False
                        print >>self.__fd, "-# %s #- " % self.__colorize("Hosts", "yellow")
                        print >>self.__fd, ""
                    table.header(("Domain Name", domain.hostname))
                    self.__fix_table_width(table)
                    text = table.draw()
                    if self.__color:
                        text = colorize_substring(text, domain.hostname, "red" if domain.get_links(Data.TYPE_VULNERABILITY) else "green")
                    print >>self.__fd, text
                    print >>self.__fd, ""
            for ip in self.__iterate(Data.TYPE_RESOURCE, Resource.RESOURCE_IP):
                table = Texttable()
                self.__add_related(table, ip, Data.TYPE_RESOURCE, Resource.RESOURCE_DOMAIN, "Domain Name")
                self.__add_related(table, ip, Data.TYPE_INFORMATION, Information.INFORMATION_GEOLOCATION, "Location")
                self.__add_related(table, ip, Data.TYPE_INFORMATION, Information.INFORMATION_WEB_SERVER_FINGERPRINT, "Web Server")
                self.__add_related(table, ip, Data.TYPE_INFORMATION, Information.INFORMATION_OS_FINGERPRINT, "OS Fingerprint")
                self.__add_related(table, ip, Data.TYPE_INFORMATION, Information.INFORMATION_PORTSCAN, "Port Scan")
                self.__add_related(table, ip, Data.TYPE_INFORMATION, Information.INFORMATION_TRACEROUTE, "Network Route")
                if table._rows:
                    if need_header:
                        need_header = False
                        print >>self.__fd, "-# %s #- " % self.__colorize("Hosts", "yellow")
                        print >>self.__fd, ""
                    table.header(("IP Address", ip.address))
                    self.__fix_table_width(table)
                    text = table.draw()
                    if self.__color:
                        text = colorize_substring(text, ip.address, "red" if ip.get_links(Data.TYPE_VULNERABILITY) else "green")
                    print >>self.__fd, text
                    print >>self.__fd, ""

        # Web servers
        if self.__show_data and Database.count(Data.TYPE_RESOURCE, Resource.RESOURCE_BASE_URL):
            print >>self.__fd, "-# %s #- " % self.__colorize("Web Servers", "yellow")
            print >>self.__fd, ""
            crawled = defaultdict(list)
            vulnerable = []
            for url in self.__iterate(Data.TYPE_RESOURCE, Resource.RESOURCE_URL):
                crawled[url.hostname].append(url.url)
                if self.__color and url.get_links(Data.TYPE_VULNERABILITY):
                    vulnerable.append(url)
            for url in self.__iterate(Data.TYPE_RESOURCE, Resource.RESOURCE_BASE_URL):
                table = Texttable()
                table.header(("Base URL", url.url))
                self.__add_related(table, url, Data.TYPE_INFORMATION, Information.INFORMATION_WEB_SERVER_FINGERPRINT, "Server")
                self.__add_related(table, url, Data.TYPE_INFORMATION, Information.INFORMATION_OS_FINGERPRINT, "Platform")
                urls = crawled[url.hostname]
                if urls:
                    urls.sort()
                    table.add_row(("Visited URLs", "\n".join(urls)))
                if table._rows:
                    self.__fix_table_width(table)
                    text = table.draw()
                    if self.__color:
                        p = text.find("\n")
                        p = text.find("\n", p + 1)
                        p = text.find("\n", p + 1)
                        if p > 0:
                            text = colorize_substring(text[:p], url.url, "red" if url.get_links(Data.TYPE_VULNERABILITY) else "green") + text[p:]
                        for u in vulnerable:
                            if u != url.url:
                                text = colorize_substring(text, u, "red")
                    print >>self.__fd, text
                    print >>self.__fd, ""

        # Emails
        if self.__show_data:
            emails = {
                e.address: "red" if e.get_links(Data.TYPE_VULNERABILITY) else "green"
                for e in self.__iterate(Data.TYPE_RESOURCE, Resource.RESOURCE_EMAIL)
            }
            if emails:
                print >>self.__fd, "-# %s #- " % self.__colorize("Email Addresses", "yellow")
                print >>self.__fd, ""
                for e in sorted(emails):
                    print >>self.__fd, "* " + self.__colorize(e, emails[e])
                print >>self.__fd, ""

        # Vulnerabilities
        print >>self.__fd, "-# %s #- " % self.__colorize("Vulnerabilities", "yellow")
        print >>self.__fd, ""
        count = Database.count(Data.TYPE_VULNERABILITY)
        if count:
            if self.__show_data:
                print >>self.__fd, self.__colorize("%d vulnerabilities found!" % count, "red")
                print >>self.__fd, ""
            vuln_types = { v.display_name: v.vulnerability_type for v in self.__iterate(Data.TYPE_VULNERABILITY) }
            titles = vuln_types.keys()
            titles.sort()
            if "Uncategorized Vulnerability" in titles:
                titles.remove("Uncategorized Vulnerability")
                titles.append("Uncategorized Vulnerability")
            for title in titles:
                data_subtype = vuln_types[title]
                print >>self.__fd, "-- %s (%s) -- " % (self.__colorize(title, "cyan"), data_subtype)
                print >>self.__fd, ""
                for vuln in self.__iterate(Data.TYPE_VULNERABILITY, data_subtype):
                    table = Texttable()
                    table.header(("Occurrence ID", vuln.identity))
                    w = len(table.draw())
                    table.add_row(("Title", vuln.title))
                    targets = [str(x) for x in vuln.associated_resources]
                    for info in vuln.associated_informations:
                        targets.extend(str(x) for x in info.associated_resources)
                    table.add_row(("Found By", get_plugin_name(vuln.plugin_id)))
                    p = len(table.draw())
                    table.add_row(("Level", vuln.level))
                    table.add_row(("Impact", vuln.impact))
                    table.add_row(("Severity", vuln.severity))
                    table.add_row(("Risk", vuln.risk))
                    q = len(table.draw())
                    if vuln.cvss_base:
                        table.add_row(("CVSS Base", vuln.cvss_base))
                    if vuln.cvss_base_vector:
                        table.add_row(("CVSS Base Vector", vuln.cvss_base_vector))
                    if len(targets) > 1:
                        targets.sort()
                        table.add_row(("Locations", "\n".join(targets)))
                    else:
                        table.add_row(("Location", targets[0]))
                    table.add_row(("Description", vuln.description))
                    table.add_row(("Solution", vuln.solution))
                    taxonomy = []
                    if vuln.bid:
                        taxonomy.extend(vuln.bid)
                    if vuln.cve:
                        taxonomy.extend(vuln.cve)
                    if vuln.cwe:
                        taxonomy.extend(vuln.cwe)
                    if vuln.osvdb:
                        taxonomy.extend(vuln.osvdb)
                    if vuln.sa:
                        taxonomy.extend(vuln.sa)
                    if vuln.sectrack:
                        taxonomy.extend(vuln.sectrack)
                    if vuln.xf:
                        taxonomy.extend(vuln.xf)
                    if taxonomy:
                        table.add_row(("Taxonomy", "\n".join(taxonomy)))
                    if vuln.references:
                        table.add_row(("References", "\n".join(sorted(vuln.references))))
                    details = vuln.display_properties["Details"]
                    if details:
                        props = details.keys()
                        props.sort()
                        table.add_row(("Additional details", "\n".join(("%s: %s" % (x, details[x])) for x in props)))
                    self.__fix_table_width(table)
                    text = table.draw()
                    if self.__color:
                        text_1 = text[:w]
                        text_3 = text[p:q]
                        text_1 = colorize_substring(text_1, vuln.identity, vuln.level.lower())
                        for lvl in Vulnerability.VULN_LEVELS:
                            if lvl in text_3:
                                text_3 = colorize_substring(text_3, lvl, lvl.lower())
                        text = text_1 + text[w:p] + text_3 + text[q:]
                    print >>self.__fd, text
                    print >>self.__fd, ""
        else:
            print >>self.__fd, self.__colorize("No vulnerabilities found.", "green")
            print >>self.__fd, ""
Example #2
0
    def __write_report(self):

        # Header
        print >>self.__fd, ""
        print >>self.__fd, "--= %s =--" % self.__colorize("Report", "cyan")
        print >>self.__fd, ""

        # Summary
        start_time, stop_time, run_time = parse_audit_times( *get_audit_times() )
        host_count  = Database.count(Data.TYPE_RESOURCE, Domain.data_subtype)
        host_count += Database.count(Data.TYPE_RESOURCE, IP.data_subtype)
        vuln_count  = Database.count(Data.TYPE_VULNERABILITY)
        print >>self.__fd, "-# %s #- " % self.__colorize("Summary", "yellow")
        print >>self.__fd, ""
        print >>self.__fd, "Audit started:   %s" % self.__colorize(start_time, "yellow")
        print >>self.__fd, "Audit ended:     %s" % self.__colorize(stop_time, "yellow")
        print >>self.__fd, "Execution time:  %s" % self.__colorize(run_time, "yellow")
        print >>self.__fd, ""
        print >>self.__fd, "Scanned hosts:   %s" % self.__colorize(str(host_count), "yellow")
        print >>self.__fd, "Vulnerabilities: %s" % self.__colorize(str(vuln_count), "red" if vuln_count else "yellow")
        print >>self.__fd, ""

        # Audit scope
        if self.__show_data or not self.__console:
            table = Texttable()
            scope_domains = ["*." + r for r in Config.audit_scope.roots]
            scope_domains.extend(Config.audit_scope.domains)
            if Config.audit_scope.addresses:
                table.add_row(("IP addresses", "\n".join(Config.audit_scope.addresses)))
            if scope_domains:
                table.add_row(("Domains", "\n".join(scope_domains)))
            if Config.audit_scope.web_pages:
                table.add_row(("Web pages", "\n".join(Config.audit_scope.web_pages)))
            if table._rows:
                self.__fix_table_width(table)
                print >>self.__fd, "-# %s #- " % self.__colorize("Audit Scope", "yellow")
                print >>self.__fd, ""
                print >>self.__fd, table.draw()
                print >>self.__fd, ""

        # Discovered hosts
        if self.__show_data:
            need_header = True
            for domain in self.__iterate(Data.TYPE_RESOURCE, Domain.data_subtype):
                table = Texttable()
                self.__add_related(table, domain, Data.TYPE_RESOURCE, IP.data_subtype, "IP Address")
                self.__add_related(table, domain, Data.TYPE_INFORMATION, Geolocation.data_subtype, "Location")
                self.__add_related(table, domain, Data.TYPE_INFORMATION, WebServerFingerprint.data_subtype, "Web Server")
                self.__add_related(table, domain, Data.TYPE_INFORMATION, OSFingerprint.data_subtype, "OS Fingerprint")
                if table._rows:
                    if need_header:
                        need_header = False
                        print >>self.__fd, "-# %s #- " % self.__colorize("Hosts", "yellow")
                        print >>self.__fd, ""
                    table.header(("Domain Name", domain.hostname))
                    self.__fix_table_width(table)
                    text = table.draw()
                    if self.__color:
                        text = colorize_substring(text, domain.hostname, "red" if domain.get_links(Data.TYPE_VULNERABILITY) else "green")
                    print >>self.__fd, text
                    print >>self.__fd, ""
            for ip in self.__iterate(Data.TYPE_RESOURCE, IP.data_subtype):
                table = Texttable()
                self.__add_related(table, ip, Data.TYPE_RESOURCE, Domain.data_subtype, "Domain Name")
                self.__add_related(table, ip, Data.TYPE_RESOURCE, MAC.data_subtype, "MAC Address")
                self.__add_related(table, ip, Data.TYPE_RESOURCE, BSSID.data_subtype, "WiFi 802.11 BSSID")
                self.__add_related(table, ip, Data.TYPE_INFORMATION, Geolocation.data_subtype, "Location")
                self.__add_related(table, ip, Data.TYPE_INFORMATION, WebServerFingerprint.data_subtype, "Web Server")
                self.__add_related(table, ip, Data.TYPE_INFORMATION, OSFingerprint.data_subtype, "OS Fingerprint")
                self.__add_related(table, ip, Data.TYPE_INFORMATION, ServiceFingerprint.data_subtype, "Service")
                self.__add_related(table, ip, Data.TYPE_INFORMATION, Portscan.data_subtype, "Port Scan")
                self.__add_related(table, ip, Data.TYPE_INFORMATION, Traceroute.data_subtype, "Network Route")
                if table._rows:
                    if need_header:
                        need_header = False
                        print >>self.__fd, "-# %s #- " % self.__colorize("Hosts", "yellow")
                        print >>self.__fd, ""
                    table.header(("IP Address", ip.address))
                    self.__fix_table_width(table)
                    text = table.draw()
                    if self.__color:
                        text = colorize_substring(text, ip.address, "red" if ip.get_links(Data.TYPE_VULNERABILITY) else "green")
                    print >>self.__fd, text
                    print >>self.__fd, ""

        # Web servers
        if self.__show_data and Database.count(Data.TYPE_RESOURCE, BaseURL.data_subtype):
            print >>self.__fd, "-# %s #- " % self.__colorize("Web Servers", "yellow")
            print >>self.__fd, ""
            crawled = defaultdict(list)
            vulnerable = []
            for url in self.__iterate(Data.TYPE_RESOURCE, URL.data_subtype):
                crawled[url.hostname].append(url.url)
                if self.__color and url.get_links(Data.TYPE_VULNERABILITY):
                    vulnerable.append(url)
            for url in self.__iterate(Data.TYPE_RESOURCE, BaseURL.data_subtype):
                table = Texttable()
                table.header(("Base URL", url.url))
                self.__add_related(table, url, Data.TYPE_INFORMATION, WebServerFingerprint.data_subtype, "Server")
                self.__add_related(table, url, Data.TYPE_INFORMATION, OSFingerprint.data_subtype, "Platform")
                urls = crawled[url.hostname]
                if urls:
                    urls.sort()
                    table.add_row(("Visited URLs", "\n".join(urls)))
                if table._rows:
                    self.__fix_table_width(table)
                    text = table.draw()
                    if self.__color:
                        p = text.find("\n")
                        p = text.find("\n", p + 1)
                        p = text.find("\n", p + 1)
                        if p > 0:
                            text = colorize_substring(text[:p], url.url, "red" if url.get_links(Data.TYPE_VULNERABILITY) else "green") + text[p:]
                        for u in vulnerable:
                            if u != url.url:
                                text = colorize_substring(text, u, "red")
                    print >>self.__fd, text
                    print >>self.__fd, ""

        # Emails
        if self.__show_data:
            emails = {
                e.address: "red" if e.get_links(Data.TYPE_VULNERABILITY) else "green"
                for e in self.__iterate(Data.TYPE_RESOURCE, Email.data_subtype)
            }
            if emails:
                print >>self.__fd, "-# %s #- " % self.__colorize("Email Addresses", "yellow")
                print >>self.__fd, ""
                for e in sorted(emails):
                    print >>self.__fd, "* " + self.__colorize(e, emails[e])
                print >>self.__fd, ""

        # Vulnerabilities
        print >>self.__fd, "-# %s #- " % self.__colorize("Vulnerabilities", "yellow")
        print >>self.__fd, ""
        count = Database.count(Data.TYPE_VULNERABILITY)
        if count:
            if self.__show_data:
                print >>self.__fd, self.__colorize("%d vulnerabilities found!" % count, "red")
                print >>self.__fd, ""
            vuln_types = { v.display_name: v.vulnerability_type for v in self.__iterate(Data.TYPE_VULNERABILITY) }
            titles = vuln_types.keys()
            titles.sort()
            if "Uncategorized Vulnerability" in titles:
                titles.remove("Uncategorized Vulnerability")
                titles.append("Uncategorized Vulnerability")
            for title in titles:
                data_subtype = vuln_types[title]
                print >>self.__fd, "-- %s (%s) -- " % (self.__colorize(title, "cyan"), data_subtype)
                print >>self.__fd, ""
                for vuln in self.__iterate(Data.TYPE_VULNERABILITY, data_subtype):
                    table = Texttable()
                    table.header(("Occurrence ID", vuln.identity))
                    w = len(table.draw())
                    table.add_row(("Title", vuln.title))
                    ##targets = self.__gather_vulnerable_resources(vuln)
                    targets = [vuln.target]
                    table.add_row(("Found By", get_plugin_name(vuln.plugin_id)))
                    p = len(table.draw())
                    table.add_row(("Level", vuln.level))
                    #table.add_row(("Impact", vuln.impact))
                    #table.add_row(("Severity", vuln.severity))
                    #table.add_row(("Risk", vuln.risk))
                    q = len(table.draw())
                    if vuln.cvss_base:
                        table.add_row(("CVSS Base", vuln.cvss_base))
                    if vuln.cvss_score:
                        table.add_row(("CVSS Score", vuln.cvss_score))
                    if vuln.cvss_vector:
                        table.add_row(("CVSS Vector", vuln.cvss_vector))
                    if len(targets) > 1:
                        targets.sort()
                        table.add_row(("Locations", "\n".join(targets)))
                    elif targets:
                        table.add_row(("Location", targets[0]))
                    table.add_row(("Description", vuln.description))
                    table.add_row(("Solution", vuln.solution))
                    taxonomy = []
                    if vuln.bid:
                        taxonomy.extend(vuln.bid)
                    if vuln.cve:
                        taxonomy.extend(vuln.cve)
                    if vuln.cwe:
                        taxonomy.extend(vuln.cwe)
                    if vuln.osvdb:
                        taxonomy.extend(vuln.osvdb)
                    if vuln.sa:
                        taxonomy.extend(vuln.sa)
                    if vuln.sectrack:
                        taxonomy.extend(vuln.sectrack)
                    if vuln.xf:
                        taxonomy.extend(vuln.xf)
                    if taxonomy:
                        table.add_row(("Taxonomy", "\n".join(taxonomy)))
                    if vuln.references:
                        table.add_row(("References", "\n".join(sorted(vuln.references))))
                    details = vuln.display_properties.get("Details")
                    if details:
                        props = details.keys()
                        props.sort()
                        table.add_row(("Additional details", "\n".join(("%s: %s" % (x, details[x])) for x in props)))
                    self.__fix_table_width(table)
                    text = table.draw()
                    if self.__color:
                        text_1 = text[:w]
                        text_3 = text[p:q]
                        text_1 = colorize_substring(text_1, vuln.identity, vuln.level.lower())
                        for lvl in Vulnerability.VULN_LEVELS:
                            if lvl in text_3:
                                text_3 = colorize_substring(text_3, lvl, lvl.lower())
                        text = text_1 + text[w:p] + text_3 + text[q:]
                    print >>self.__fd, text
                    print >>self.__fd, ""
        else:
            print >>self.__fd, self.__colorize("No vulnerabilities found.", "green")
            print >>self.__fd, ""
Example #3
0
    def generate_report(self, output_file):

        Logger.log_more_verbose("Generating JSON database...")

        # Warn about --full not being supported by this plugin.
        if not Config.audit_config.only_vulns:
            Config.audit_config.only_vulns = True
            Logger.log_more_verbose(
                "Full report mode not supported, switching to brief mode.")

        # Hardcode the arguments for the JSON plugin.
        Config.plugin_args["mode"] = "dump"
        Config.plugin_args["command"] = ""

        # Get the report data.
        report_data = self.get_report_data()

        Logger.log_more_verbose("Postprocessing JSON database...")

        # Remove the false positives, if any.
        del report_data["false_positives"]

        # Gather all taxonomies into a single property.
        for vuln in report_data["vulnerabilities"].itervalues():
            taxonomy = []
            for prop in TAXONOMY_NAMES:
                taxonomy.extend(vuln.get(prop, []))
            if taxonomy:
                taxonomy.sort()
                vuln["taxonomy"] = taxonomy

        # It's easier for the JavaScript code in the report to access the
        # vulnerabilities as an array instead of a map, so let's fix that.
        # Also, delete all properties we know aren't being used in the report.
        vulnerabilities = report_data["vulnerabilities"]
        sort_keys = [
            (data["display_name"],
             data["plugin_id"],
             data["target_id"],
             data["identity"])
            for data in vulnerabilities.itervalues()
        ]
        sort_keys.sort()
        report_data["vulnerabilities"] = [
            {
                propname: propvalue
                for propname, propvalue
                in vulnerabilities[identity].iteritems()
                if propname in (
                    "display_name",
                    "plugin_id",
                    "target_id",
                    "identity",
                    "links",
                    "data_type",
                    "data_subtype",
                    "title",
                    "description",
                    "solution",
                    "taxonomy",
                    "references",
                    "level",
                    "impact",
                    "severity",
                    "risk",
                )
            }
            for _, _, _, identity in sort_keys
        ]
        vulnerabilities.clear()
        sort_keys = []

        # Remove a bunch of data that won't be shown in the report anyway.
        for identity, data in report_data["informations"].items():
            if data["information_category"] not in (
                Information.CATEGORY_ASSET,
                Information.CATEGORY_FINGERPRINT,
            ):
                del report_data["informations"][identity]

        # Remove any dangling links we may have.
        links = set()
        for iterator in (
            report_data["resources"].itervalues(),
            report_data["informations"].itervalues(),
            report_data["vulnerabilities"]
        ):
            links.update(data["identity"] for data in iterator)
        for iterator in (
            report_data["resources"].itervalues(),
            report_data["informations"].itervalues(),
            report_data["vulnerabilities"]
        ):
            for data in iterator:
                tmp = set(data["links"])
                tmp.intersection_update(links)
                data["links"] = sorted(tmp)
                tmp.clear()
        links.clear()

        # Now, let's go through all Data objects and try to resolve the
        # plugin IDs to user-friendly plugin names.
        plugin_map = dict()
        for iterator in (
            report_data["resources"].itervalues(),
            report_data["informations"].itervalues(),
            report_data["vulnerabilities"]
        ):
            for data in iterator:
                if "plugin_id" in data:
                    plugin_id = data["plugin_id"]
                    if plugin_id not in plugin_map:
                        plugin_map[plugin_id] = get_plugin_name(plugin_id)
                    data["plugin_name"] = plugin_map[plugin_id]
        plugin_map.clear()

        # Calculate some statistics, so the JavaScript code doesn't have to.
        vulns_by_level = Counter()
        for level in Vulnerability.VULN_LEVELS:
            vulns_by_level[level] = 0
        vulns_by_level.update(
            v["level"] for v in report_data["vulnerabilities"])
        vulns_by_level = {k.title(): v for k, v in vulns_by_level.iteritems()}
        vulns_by_type = dict(Counter(
                v["display_name"] for v in report_data["vulnerabilities"]
            ))
        report_data["stats"] = {
            "resources":       len(report_data["resources"]),
            "informations":    len(report_data["informations"]),
            "vulnerabilities": len(report_data["vulnerabilities"]),
            "vulns_by_level":  vulns_by_level,
            "vulns_by_type":   vulns_by_type,
        }

        # It's better to show vulnerability levels as integers instead
        # of strings, so they can be sorted in the proper order. The
        # actual report shows them to the user as strings, but sorts
        # using the integer values.
        for vuln in report_data["vulnerabilities"]:
            vuln["level"] = Vulnerability.VULN_LEVELS.index(vuln["level"])

        # Generate the ZIP file comment.
        comment = "Report generated with GoLismero %s at %s UTC\n"\
                  % (VERSION, report_data["summary"]["report_time"])

        # Serialize the data and cleanup the unserialized version.
        if output_file.endswith(".zip"):
            serialized_data = json.dumps(report_data,
                                         sort_keys=True, indent=4)
        else:
            serialized_data = json.dumps(report_data)
        del report_data

        # Escape all HTML entities from the serialized data,
        # since the JSON library doesn't seem to do it.
        serialized_data = cgi.escape(serialized_data)

        # Get the directory where we can find our template.
        html_report = os.path.dirname(__file__)
        html_report = os.path.join(html_report, "html_report")
        html_report = os.path.abspath(html_report)
        if not html_report.endswith(os.path.sep):
            html_report += os.path.sep

        # Save the report data to disk.
        Logger.log_more_verbose("Writing report to disk...")

        # Save it as a zip file.
        if output_file.endswith(".zip"):
            with ZipFile(output_file, mode="w", compression=ZIP_DEFLATED,
                         allowZip64=True) as zip:

                # Save the zip file comment.
                zip.comment = comment

                # Save the JSON data.
                arcname = os.path.join("js", "database.js")
                serialized_data = "data = " + serialized_data
                zip.writestr(arcname, serialized_data)
                del serialized_data

                # Copy the template dependencies into the zip file.
                for root, directories, files in os.walk(html_report):
                    if root.endswith(os.path.sep + "backup"):
                        continue
                    for basename in files:
                        if basename.endswith(".less"):
                            continue
                        if basename in ("index.html", "database.js"):
                            continue
                        filename = os.path.join(root, basename)
                        arcname = filename[len(html_report):]
                        if arcname == "index-orig.html":
                            arcname = "index.html"
                        zip.write(filename, arcname)

        # Save it as an HTML file with no dependencies.
        else:
            with open(os.path.join(html_report, "index.html"), "rb") as fd:
                template = fd.read()
            assert "%DATA%" in template
            serialized_data = template.replace("%DATA%", serialized_data)
            del template
            with open(output_file, "wb") as fd:
                fd.write(serialized_data)
Example #4
0
    def generate_report(self, output_file):

        Logger.log_more_verbose("Generating JSON database...")

        # Warn about --full not being supported by this plugin.
        if not Config.audit_config.only_vulns:
            Config.audit_config.only_vulns = True
            Logger.log_more_verbose(
                "Full report mode not supported, switching to brief mode.")

        # Hardcode the arguments for the JSON plugin.
        Config.plugin_args["mode"] = "dump"
        Config.plugin_args["command"] = ""

        # Get the report data.
        report_data = self.get_report_data()

        Logger.log_more_verbose("Postprocessing JSON database...")

        # Remove the false positives, if any.
        del report_data["false_positives"]

        # Gather all taxonomies into a single property.
        for vuln in report_data["vulnerabilities"].itervalues():
            taxonomy = []
            for prop in TAXONOMY_NAMES:
                taxonomy.extend(vuln.get(prop, []))
            if taxonomy:
                taxonomy.sort()
                vuln["taxonomy"] = taxonomy

        # It's easier for the JavaScript code in the report to access the
        # vulnerabilities as an array instead of a map, so let's fix that.
        # Also, delete all properties we know aren't being used in the report.
        vulnerabilities = report_data["vulnerabilities"]
        sort_keys = [(data["display_name"], data["plugin_id"],
                      data["target_id"], data["identity"])
                     for data in vulnerabilities.itervalues()]
        sort_keys.sort()
        report_data["vulnerabilities"] = [{
            propname: propvalue
            for propname, propvalue in vulnerabilities[identity].iteritems()
            if propname in (
                "display_name",
                "plugin_id",
                "target_id",
                "identity",
                "links",
                "data_type",
                "data_subtype",
                "title",
                "description",
                "solution",
                "taxonomy",
                "references",
                "level",
                "impact",
                "severity",
                "risk",
            )
        } for _, _, _, identity in sort_keys]
        vulnerabilities.clear()
        sort_keys = []

        # Remove a bunch of data that won't be shown in the report anyway.
        for identity, data in report_data["informations"].items():
            if data["information_category"] not in (
                    Information.CATEGORY_ASSET,
                    Information.CATEGORY_FINGERPRINT,
            ):
                del report_data["informations"][identity]

        # Remove any dangling links we may have.
        links = set()
        for iterator in (report_data["resources"].itervalues(),
                         report_data["informations"].itervalues(),
                         report_data["vulnerabilities"]):
            links.update(data["identity"] for data in iterator)
        for iterator in (report_data["resources"].itervalues(),
                         report_data["informations"].itervalues(),
                         report_data["vulnerabilities"]):
            for data in iterator:
                tmp = set(data["links"])
                tmp.intersection_update(links)
                data["links"] = sorted(tmp)
                tmp.clear()
        links.clear()

        # Now, let's go through all Data objects and try to resolve the
        # plugin IDs to user-friendly plugin names.
        plugin_map = dict()
        for iterator in (report_data["resources"].itervalues(),
                         report_data["informations"].itervalues(),
                         report_data["vulnerabilities"]):
            for data in iterator:
                if "plugin_id" in data:
                    plugin_id = data["plugin_id"]
                    if plugin_id not in plugin_map:
                        plugin_map[plugin_id] = get_plugin_name(plugin_id)
                    data["plugin_name"] = plugin_map[plugin_id]
        plugin_map.clear()

        # Calculate some statistics, so the JavaScript code doesn't have to.
        vulns_by_level = Counter()
        for level in Vulnerability.VULN_LEVELS:
            vulns_by_level[level] = 0
        vulns_by_level.update(v["level"]
                              for v in report_data["vulnerabilities"])
        vulns_by_level = {k.title(): v for k, v in vulns_by_level.iteritems()}
        vulns_by_type = dict(
            Counter(v["display_name"] for v in report_data["vulnerabilities"]))
        report_data["stats"] = {
            "resources": len(report_data["resources"]),
            "informations": len(report_data["informations"]),
            "vulnerabilities": len(report_data["vulnerabilities"]),
            "vulns_by_level": vulns_by_level,
            "vulns_by_type": vulns_by_type,
        }

        # It's better to show vulnerability levels as integers instead
        # of strings, so they can be sorted in the proper order. The
        # actual report shows them to the user as strings, but sorts
        # using the integer values.
        for vuln in report_data["vulnerabilities"]:
            vuln["level"] = Vulnerability.VULN_LEVELS.index(vuln["level"])

        # Generate the ZIP file comment.
        comment = "Report generated with GoLismero %s at %s UTC\n"\
                  % (VERSION, report_data["summary"]["report_time"])

        # Serialize the data and cleanup the unserialized version.
        if output_file.endswith(".zip"):
            serialized_data = json.dumps(report_data, sort_keys=True, indent=4)
        else:
            serialized_data = json.dumps(report_data)
        del report_data

        # Escape all HTML entities from the serialized data,
        # since the JSON library doesn't seem to do it.
        serialized_data = cgi.escape(serialized_data)

        # Get the directory where we can find our template.
        html_report = os.path.dirname(__file__)
        html_report = os.path.join(html_report, "html_report")
        html_report = os.path.abspath(html_report)
        if not html_report.endswith(os.path.sep):
            html_report += os.path.sep

        # Save the report data to disk.
        Logger.log_more_verbose("Writing report to disk...")

        # Save it as a zip file.
        if output_file.endswith(".zip"):
            with ZipFile(output_file,
                         mode="w",
                         compression=ZIP_DEFLATED,
                         allowZip64=True) as zip:

                # Save the zip file comment.
                zip.comment = comment

                # Save the JSON data.
                arcname = os.path.join("js", "database.js")
                serialized_data = "data = " + serialized_data
                zip.writestr(arcname, serialized_data)
                del serialized_data

                # Copy the template dependencies into the zip file.
                for root, directories, files in os.walk(html_report):
                    if root.endswith(os.path.sep + "backup"):
                        continue
                    for basename in files:
                        if basename.endswith(".less"):
                            continue
                        if basename in ("index.html", "database.js"):
                            continue
                        filename = os.path.join(root, basename)
                        arcname = filename[len(html_report):]
                        if arcname == "index-orig.html":
                            arcname = "index.html"
                        zip.write(filename, arcname)

        # Save it as an HTML file with no dependencies.
        else:
            with open(os.path.join(html_report, "index.html"), "rb") as fd:
                template = fd.read()
            assert "%DATA%" in template
            serialized_data = template.replace("%DATA%", serialized_data)
            del template
            with open(output_file, "wb") as fd:
                fd.write(serialized_data)