Codebase list faraday-plugins / d3eb8e2
Import upstream version 1.5.9 Kali Janitor 2 years ago
47 changed file(s) with 776 addition(s) and 293 deletion(s). Raw diff Collapse all Expand all
0 Nov 19th, 2021
0 FIX extrainfo of netsparker plugin
0 Add nuclei_legacy plugin
0 Add CVE to plugins
1 - acunetix
2 - appscan
3 - burp
4 - metasploit
5 - nessus
6 - netsparker
7 - nexpose
8 - nikto
9 - nipper
10 - nmap
11 - openscap
12 - qualysguard
13 - retina
14 - shodan
0 Add support for Sslyze 5.0 resports
0 Fix errors while creating hosts with wrong regex
0 ADD masscan support to nmap plugin
0 Dec 13th, 2021
0 Fix bug in openvas plugin
0 Add cve in faraday_csv plugin
0 ADD Grype plugin
0 Dec 27th, 2021
154154 ...
155155 ```
156156
157 More documentation here https://github.com/infobyte/faraday/wiki/Basic-plugin-development
157 More documentation here https://docs.faradaysec.com/Basic-plugin-development/
0 1.5.9 [Dec 27th, 2021]:
1 ---
2 * ADD cve in faraday_csv plugin
3 * ADD Grype plugin
4
5 1.5.8 [Dec 13th, 2021]:
6 ---
7 * Add CVE to plugins
8 - acunetix
9 - appscan
10 - burp
11 - metasploit
12 - nessus
13 - netsparker
14 - nexpose
15 - nikto
16 - nipper
17 - nmap
18 - openscap
19 - qualysguard
20 - retina
21 - shodan
22 * Add support for Sslyze 5.0 resports
23 * Fix errors while creating hosts with wrong regex
24 * ADD masscan support to nmap plugin
25 * Fix bug in openvas plugin
26
27 1.5.7 [Nov 19th, 2021]:
28 ---
29 * FIX extrainfo of netsparker plugin
30 * Add nuclei_legacy plugin
31
032 1.5.6 [Nov 10th, 2021]:
133 ---
234 * FIX issue with acunetix plugin
35
336 * FIX typo in nikto plugin
437
538 1.5.5 [Oct 21st, 2021]:
0 __version__ = '1.5.6'
0 __version__ = '1.5.9'
33 See the file 'doc/LICENSE' for the license information
44
55 """
6 # Standard library imports
67 import hashlib
78 import logging
89 import os
1516 from datetime import datetime
1617 from pathlib import Path
1718
19 # Related third party imports
1820 import pytz
1921 import simplejson as json
22
23 # Local application imports
24 from faraday_plugins.plugins.plugins_utils import its_cve
2025
2126 logger = logging.getLogger("faraday").getChild(__name__)
2227
3035
3136 def __init__(self, ignore_info=False):
3237 # Must be unique. Check that there is not
33 # an existant plugin with the same id.
38 # an existent plugin with the same id.
3439 # TODO: Make script that list current ids.
3540 self.ignore_info = ignore_info
3641 self.id = None
8489 utc_date = date.astimezone(pytz.UTC)
8590 return utc_date.timestamp()
8691 except Exception as e:
87 logger.error("Error generating timestamp: %s", e)
92 logger.error(f"Error generating timestamp: {e}")
8893 return None
8994 else:
9095 return date
252257
253258 def canParseCommandString(self, current_input):
254259 """
255 This method can be overriden in the plugin implementation
260 This method can be overridden in the plugin implementation
256261 if a different kind of check is needed
257262 """
258263 if (self._command_regex is not None and
287292
288293 def getCompletitionSuggestionsList(self, current_input):
289294 """
290 This method can be overriden in the plugin implementation
295 This method can be overridden in the plugin implementation
291296 if a different kind of check is needed
292297 """
293298 words = current_input.split(" ")
314319 elif filename.is_dir():
315320 shutil.rmtree(filename)
316321 except Exception as e:
317 self.logger.error("Error on delete file: (%s) [%s]", filename, e)
322 self.logger.error(f"Error on delete file: ({filename}) [{e}]")
318323
319324 def processReport(self, filepath: Path, user="faraday"):
320325 if type(filepath) == str: # TODO workaround for compatibility, remove in the future
383388 def createAndAddVulnToHost(self, host_id, name, desc="", ref=None,
384389 severity="", resolution="", data="", external_id=None, run_date=None,
385390 impact=None, custom_fields=None, status="", policyviolations=None,
386 easeofresolution=None, confirmed=False, tags=None):
391 easeofresolution=None, confirmed=False, tags=None, cve=None):
387392 if ref is None:
388393 ref = []
389394 if status == "":
398403 tags = []
399404 if isinstance(tags, str):
400405 tags = [tags]
406 if cve is None:
407 cve = []
408 elif type(cve) is str:
409 cve = [cve]
410 cve = its_cve(cve)
401411 vulnerability = {"name": name, "desc": desc, "severity": self.normalize_severity(severity), "refs": ref,
402412 "external_id": external_id, "type": "Vulnerability", "resolution": resolution, "data": data,
403413 "custom_fields": custom_fields, "status": status, "impact": impact,
404 "policyviolations": policyviolations,
414 "policyviolations": policyviolations, "cve": cve,
405415 "confirmed": confirmed, "easeofresolution": easeofresolution, "tags": tags
406416 }
407417 if run_date:
412422 def createAndAddVulnToService(self, host_id, service_id, name, desc="",
413423 ref=None, severity="", resolution="", data="", external_id=None, run_date=None,
414424 custom_fields=None, policyviolations=None, impact=None, status="",
415 confirmed=False, easeofresolution=None, tags=None):
425 confirmed=False, easeofresolution=None, tags=None, cve=None):
416426 if ref is None:
417427 ref = []
418428 if status == "":
427437 tags = []
428438 if isinstance(tags, str):
429439 tags = [tags]
440 if cve is None:
441 cve = []
442 elif type(cve) is str:
443 cve = [cve]
444 cve = its_cve(cve)
430445 vulnerability = {"name": name, "desc": desc, "severity": self.normalize_severity(severity), "refs": ref,
431446 "external_id": external_id, "type": "Vulnerability", "resolution": resolution, "data": data,
432447 "custom_fields": custom_fields, "status": status, "impact": impact,
433 "policyviolations": policyviolations,
448 "policyviolations": policyviolations, "cve": cve,
434449 "easeofresolution": easeofresolution, "confirmed": confirmed, "tags": tags
435450 }
436451 if run_date:
445460 params="", query="", category="", data="", external_id=None,
446461 confirmed=False, status="", easeofresolution=None, impact=None,
447462 policyviolations=None, status_code=None, custom_fields=None, run_date=None,
448 tags=None):
463 tags=None, cve=None):
449464 if params is None:
450465 params = ""
451466 if response is None:
480495 tags = []
481496 if isinstance(tags, str):
482497 tags = [tags]
498 if cve is None:
499 cve = []
500 elif type(cve) is str:
501 cve = [cve]
502 cve = its_cve(cve)
483503 vulnerability = {"name": name, "desc": desc, "severity": self.normalize_severity(severity), "refs": ref,
484504 "external_id": external_id, "type": "VulnerabilityWeb", "resolution": resolution,
485505 "data": data, "website": website, "path": path, "request": request, "response": response,
486506 "method": method, "pname": pname, "params": params, "query": query, "category": category,
487507 "confirmed": confirmed, "status": status, "easeofresolution": easeofresolution,
488 "impact": impact, "policyviolations": policyviolations,
508 "impact": impact, "policyviolations": policyviolations, "cve": cve,
489509 "status_code": status_code, "custom_fields": custom_fields, "tags": tags}
490510 if run_date:
491511 vulnerability["run_date"] = self.get_utctimestamp(run_date)
582602 match = (self.extension == extension)
583603 elif type(self.extension) == list:
584604 match = (extension in self.extension)
585 self.logger.debug("Extension Match: [%s =/in %s] -> %s", extension, self.extension, match)
605 self.logger.debug(f"Extension Match: [{extension} =/in {self.extension}] -> {match}")
586606 return match
587607
588608
604624 match = (main_tag in self.identifier_tag)
605625 if self.identifier_tag_attributes:
606626 match = self.identifier_tag_attributes.issubset(main_tag_attributes)
607 self.logger.debug("Tag Match: [%s =/in %s] -> %s", main_tag, self.identifier_tag, match)
627 self.logger.debug(f"Tag Match: [{main_tag} =/in {self.identifier_tag}] -> {match}")
608628 return match
609629
610630
621641 if file_json_keys is None:
622642 file_json_keys = set()
623643 match = self.json_keys.issubset(file_json_keys)
624 self.logger.debug("Json Keys Match: [%s =/in %s] -> %s", file_json_keys, self.json_keys, match)
644 self.logger.debug(f"Json Keys Match: [{file_json_keys} =/in {self.json_keys}] -> {match}")
625645 return match
626646
627647
643663 matched_lines = list(filter(lambda json_line: self.json_keys.issubset(json_line.keys()),
644664 json_lines))
645665 match = len(matched_lines) == len(json_lines)
646 self.logger.debug("Json Keys Match: [%s =/in %s] -> %s", json_lines[0].keys(), self.json_keys,
647 match)
666 self.logger.debug(f"Json Keys Match: [{json_lines[0].keys()} =/in {self.json_keys}] -> {match}")
648667 except ValueError:
649668 return False
650669 return match
666685 match = bool(list(filter(lambda x: x.issubset(file_csv_headers), self.csv_headers)))
667686 else:
668687 match = self.csv_headers.issubset(file_csv_headers)
669 self.logger.debug("CSV Headers Match: [%s =/in %s] -> %s", file_csv_headers, self.csv_headers, match)
688 self.logger.debug(f"CSV Headers Match: [{file_csv_headers} =/in {self.csv_headers}] -> {match}")
670689 return match
671690
672691
687706 if files_in_zip is None:
688707 files_in_zip = set()
689708 match = bool(self.files_list & files_in_zip)
690 self.logger.debug("Files List Match: [%s =/in %s] -> %s", files_in_zip, self.files_list, match)
709 self.logger.debug(f"Files List Match: [{files_in_zip} =/in {self.files_list}] -> {match}")
691710 return match
00 """
11 Faraday Penetration Test IDE
2 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
2 Copyright (C) 2013 Infobyte LLC (https://www.faradaysec.com/)
33 See the file 'doc/LICENSE' for the license information
44
55 """
6 # Standard library imports
67 import logging
78 import os
89 import socket
10 import re
911 from urllib.parse import urlsplit
1012
1113 SERVICE_MAPPER = None
12
14 CVE_regex = re.compile(r'CVE-\d{4}-\d{4,7}')
1315 logger = logging.getLogger(__name__)
1416
1517
124126 return severity
125127 except ValueError:
126128 return 'unclassified'
129
130
131 def its_cve(cves: list):
132 r = [cve for cve in cves if CVE_regex.match(cve)]
133 return r
66
77 @property
88 def request(self) -> str:
9 if not self.node:
9 if self.node in ('', None):
1010 return ''
1111 return self.node.findtext('Request', '')
1212
1313 @property
1414 def response(self) -> str:
15 if not self.node:
15 if self.node in ('', None):
1616 return ''
1717 return self.node.findtext('Response', '')
1818
1919
20 class Cve:
21 def __init__(self, node):
22 self.node = node
23
24 @property
25 def text(self) -> str:
26 if self.node in ('', None):
27 return ''
28 return self.node.text
29
30
31 class CVEList:
32 def __init__(self, node):
33 self.node = node
34
35 @property
36 def cve(self) -> Cve:
37 if self.node is None:
38 return ''
39 return Cve(self.node.find('CVE'))
40
41
2042 class Cwe:
2143 def __init__(self, node):
2244 self.node = node
253275
254276 @property
255277 def cvelist(self):
256 return self.node.find('CVEList')
278 return CVEList(self.node.find('CVEList'))
257279
258280 @property
259281 def cvss(self) -> Cvss:
5454 parser = etree.XMLParser(recover=True)
5555 tree = etree.fromstring(xml_output, parser=parser)
5656 except SyntaxError as err:
57 print("SyntaxError: %s. %s", err, xml_output)
57 print(f"SyntaxError: {err}. {xml_output}")
5858 return None
5959
6060 return tree
144144 description += f'\nPath: {item.affects}'
145145 if item.parameter:
146146 description += f'\nParameter: {item.parameter}'
147 try:
148 cve = [item.cvelist.cve.text if item.cvelist.cve else ""]
149 except Exception:
150 cve = [""]
147151 self.createAndAddVulnWebToService(
148152 h_id,
149153 s_id,
156160 params=item.parameter,
157161 request=item.technicaldetails.request,
158162 response=item.technicaldetails.response,
159 ref=[i.url for i in item.references.reference])
163 ref=[i.url for i in item.references.reference],
164 cve=cve)
160165
161166 @staticmethod
162167 def get_domain(scan: Scan):
5555 type_id = item.attrib['id']
5656 name = item.find("name").text
5757 issue_types[type_id] = name
58 cve = item.find("cve")
59 if cve and cve.text:
60 issue_types[f"{type_id}_cve"] = cve.text
5861 return issue_types
5962
6063 @staticmethod
110113 dast_issues = []
111114 for item in tree:
112115 entity = self.entities[item.find("entity/ref").text]
113 host = entity["host"]
116 host = entity["host"].replace('\\','/')
114117 port = entity["port"]
115118 name = self.issue_types[item.find("issue-type/ref").text]
116119 severity = 0 if item.find("severity-id") is None else int(item.find("severity-id").text)
138141 else:
139142 cve = None
140143 cve_url = None
144 if cve is None:
145 cve = self.issue_types.get(f"{item.find('issue-type/ref').text}_cve", None)
141146 host_key = f"{host}-{port}"
142147 issue_data = {
143148 "host": host,
153158 "response": response,
154159 "website": entity['website'],
155160 "path": entity['path'],
156 "external_id": cve
161 "cve": []
157162 }
158163 if cve:
159 issue_data["ref"].append(cve)
164 issue_data["cve"].append(cve)
160165 if cve_url:
161166 issue_data["ref"].append(cve_url)
162167 if cwe:
176181 sast_issues = []
177182 for item in tree:
178183 name = self.issue_types[item.find("issue-type/ref").text]
179 source_file = item.attrib["filename"]
184 source_file = item.attrib["filename"].replace('\\','/')
180185 severity = 0 if item.find("severity-id") is None else int(item.find("severity-id").text)
181186 description = item.find("fix/item/general/text").text
182187 resolution = "" if item.find("variant-group/item/issue-information/fix-resolution-text") is None \
209214 "desc": description,
210215 "ref": [],
211216 "resolution": resolution,
212 "external_id": cve
217 "cve": []
213218 }
219
214220 if cve:
215 issue_data["ref"].append(cve)
221 issue_data["cve"].append(cve)
216222 if cve_url:
217223 issue_data["ref"].append(cve_url)
218224 if cwe:
+0
-6
faraday_plugins/plugins/repo/awsprowler/__init__.py less more
0 """
1 Faraday Penetration Test IDE
2 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
3 See the file 'doc/LICENSE' for the license information
4
5 """
+0
-71
faraday_plugins/plugins/repo/awsprowler/plugin.py less more
0 """
1 Faraday Penetration Test IDE
2 Copyright (C) 2020 Infobyte LLC (http://www.infobytesec.com/)
3 See the file 'doc/LICENSE' for the license information
4
5 """
6 import socket
7 import json
8 from datetime import datetime
9 import re
10 from faraday_plugins.plugins.plugin import PluginMultiLineJsonFormat
11
12 __author__ = "Blas Moyano"
13 __copyright__ = "Copyright (c) 2020, Infobyte LLC"
14 __credits__ = ["Blas Moyano"]
15 __license__ = ""
16 __version__ = "0.0.1"
17 __maintainer__ = "Blas Moyano"
18 __email__ = "[email protected]"
19 __status__ = "Development"
20
21
22 class AwsProwlerJsonParser:
23
24 def __init__(self, json_output):
25 string_manipulate = json_output.replace("}", "} #")
26 string_manipulate = string_manipulate[:len(string_manipulate) - 2]
27 self.report_aws = string_manipulate.split("#")
28
29
30 class AwsProwlerPlugin(PluginMultiLineJsonFormat):
31 """ Handle the AWS Prowler tool. Detects the output of the tool
32 and adds the information to Faraday.
33 """
34
35 def __init__(self, *arg, **kwargs):
36 super().__init__(*arg, **kwargs)
37 self.id = "awsprowler"
38 self.name = "AWS Prowler"
39 self.plugin_version = "0.1"
40 self.version = "0.0.1"
41 self.json_keys = {"Profile", "Account Number"}
42
43
44 def parseOutputString(self, output, debug=False):
45 parser = AwsProwlerJsonParser(output)
46 region_list = []
47 for region in parser.report_aws:
48 json_reg = json.loads(region)
49 region_list.append(json_reg.get('Region', 'Not Info'))
50
51 host_id = self.createAndAddHost(name=f'{self.name} - {region_list}', description="AWS Prowler")
52
53 for vuln in parser.report_aws:
54 json_vuln = json.loads(vuln)
55 vuln_name = json_vuln.get('Control', 'Not Info')
56 vuln_desc = json_vuln.get('Message', 'Not Info')
57 vuln_severity = json_vuln.get('Level', 'Not Info')
58 vuln_run_date = json_vuln.get('Timestamp', 'Not Info')
59 vuln_external_id = json_vuln.get('Control ID', 'Not Info')
60 vuln_policy = f'{vuln_name}:{vuln_external_id}'
61 vuln_run_date = vuln_run_date.replace('T', ' ')
62 vuln_run_date = vuln_run_date.replace('Z', '')
63 self.createAndAddVulnToHost(host_id=host_id, name=vuln_name, desc=vuln_desc,
64 severity=self.normalize_severity(vuln_severity),
65 run_date=datetime.strptime(vuln_run_date, '%Y-%m-%d %H:%M:%S'),
66 external_id=vuln_external_id, policyviolations=[vuln_policy])
67
68
69 def createPlugin(ignore_info=False):
70 return AwsProwlerPlugin(ignore_info=ignore_info)
55 """
66 import base64
77 import distutils.util # pylint: disable=import-error
8 import re
89 import xml.etree.ElementTree as ET
910 from urllib.parse import urlsplit
1011
1112 from bs4 import BeautifulSoup, Comment
1213
1314 from faraday_plugins.plugins.plugin import PluginXMLFormat
15 from faraday_plugins.plugins.plugins_utils import CVE_regex
1416
1517 __author__ = "Francisco Amato"
1618 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
106108 detail = self.do_clean(item_node.findall('issueDetail'))
107109 remediation = self.do_clean(item_node.findall('remediationBackground'))
108110 background = self.do_clean(item_node.findall('issueBackground'))
111 self.cve = []
112 if background:
113 cve = CVE_regex.search(background)
114 if cve:
115 self.cve = [cve.group()]
109116
110117 self.url = host_node.text
111118
226233 request=item.request,
227234 response=item.response,
228235 resolution=resolution,
229 external_id=item.external_id)
236 external_id=item.external_id,
237 cve=item.cve
238 )
230239
231240 del parser
232241
00 """
11 Faraday Penetration Test IDE
2 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
2 Copyright (C) 2013 Infobyte LLC (https://www.faradaysec.com/)
33 See the file 'doc/LICENSE' for the license information
44 """
5 # Standard library imports
56 import sys
67 import re
78 import csv
89 from ast import literal_eval
910
11 # Local application imports
1012 from faraday_plugins.plugins.plugin import PluginCSVFormat
1113
1214
4345 "impact_availability",
4446 "impact_accountability",
4547 "policyviolations",
48 "cve",
4649 "custom_fields",
4750 "website",
4851 "path",
7174 reader.fieldnames[index] = "target"
7275 custom_fields_names = self.get_custom_fields_names(reader.fieldnames)
7376 for row in reader:
74 self.data = {}
75 self.data['row_with_service'] = False
76 self.data['row_with_vuln'] = False
77 self.data = {
78 'row_with_service': False,
79 'row_with_vuln': False
80 }
7781 self.build_host(row)
7882 if "service" in obj_to_import:
7983 if row['port'] and row['protocol']:
122126 if (port and not protocol) or (protocol and not port):
123127 self.logger.error(
124128 ("Missing columns in CSV file. "
125 "In order to import services, you need to add a column called port "
126 " and a column called protocol.")
129 "In order to import services, you need to add a column called port "
130 " and a column called protocol.")
127131 )
128132 return None
129133 else:
136140 if (vuln_name and not vuln_desc) or (vuln_desc and not vuln_name):
137141 self.logger.error(
138142 ("Missing columns in CSV file. "
139 "In order to import vulnerabilities, you need to add a "
140 "column called name and a column called desc.")
143 "In order to import vulnerabilities, you need to add a "
144 "column called name and a column called desc.")
141145 )
142146 return None
143147 else:
145149
146150 return obj_to_import
147151
148 def get_custom_fields_names(self, headers):
152 @staticmethod
153 def get_custom_fields_names(headers):
149154 custom_fields_names = []
150155 for header in headers:
151156 match = re.match(r"cf_(\w+)", header)
206211 if "impact_" in item:
207212 impact = re.match(r"impact_(\w+)", item).group(1)
208213 impact_dict[impact] = True if row[item] == "True" else False
209 elif item in ["refs", "policyviolations", "tags"]:
214 elif item in ["refs", "policyviolations", "cve", "tags"]:
210215 self.data[item] = literal_eval(row[item])
211216 else:
212217 self.data[item] = row[item]
225230 self.logger.error("Hostname not valid. Faraday will set it as empty.")
226231 return hostnames
227232
228 def parse_vuln_impact(self, impact):
233 @staticmethod
234 def parse_vuln_impact(impact):
229235 impacts = [
230236 "accountability",
231237 "confidentiality",
236242 if item in impact:
237243 return item
238244
239 def parse_custom_fields(self, row, custom_fields_names):
245 @staticmethod
246 def parse_custom_fields(row, custom_fields_names):
240247 custom_fields = {}
241248 for cf_name in custom_fields_names:
242249 cf_value = row["cf_" + cf_name]
301308 easeofresolution=item['easeofresolution'] or None,
302309 impact=item['impact'],
303310 policyviolations=item['policyviolations'],
311 cve=item['cve'],
304312 custom_fields=item['custom_fields'],
305313 tags=item['tags']
306314 )
320328 easeofresolution=item['easeofresolution'] or None,
321329 impact=item['impact'],
322330 policyviolations=item['policyviolations'],
331 cve=item['cve'],
323332 custom_fields=item['custom_fields'],
324333 tags=item['tags']
325334 )
347356 easeofresolution=item['easeofresolution'] or None,
348357 impact=item['impact'],
349358 policyviolations=item['policyviolations'],
359 cve=item['cve'],
350360 status_code=item['status_code'] or None,
351361 custom_fields=item['custom_fields'],
352362 tags=item['tags']
0 """
1 Faraday Penetration Test IDE
2 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
3 See the file 'doc/LICENSE' for the license information
4 """
5
6 import json
7 import re
8 from faraday_plugins.plugins.plugin import PluginJsonFormat
9
10 __author__ = "Joachim Bauernberger"
11 __license__ = "MIT"
12 __version__ = "1.0.0"
13 __maintainer__ = "Joachim Bauernberger"
14 __email__ = "[email protected]"
15 __status__ = "Development"
16
17
18 class GrypePlugin(PluginJsonFormat):
19 def __init__(self, *arg, **kwargs):
20 super().__init__(*arg, **kwargs)
21 self.id = 'grype'
22 self.name = 'Grype JSON Plugin'
23 self.plugin_version = '1.0.0'
24 self._command_regex = re.compile(r'^grype\s+.*')
25 self._use_temp_file = True
26 self._temp_file_extension = "json"
27 self.json_keys = {"source", "matches", "descriptor"}
28
29 def parseOutputString(self, output, debug=True):
30 grype_json = json.loads(output)
31 if "userInput" in grype_json["source"]["target"]:
32 name = grype_json["source"]["target"]["userInput"]
33 else:
34 name = grype_json["source"]["target"]
35 host_id = self.createAndAddHost(name, description=f"Type: {grype_json['source']['type']}")
36 for match in grype_json['matches']:
37 name = match.get('vulnerability').get('id')
38 cve = name
39 references = []
40 if match["relatedVulnerabilities"]:
41 description = match["relatedVulnerabilities"][0].get('description')
42 references.append(match["relatedVulnerabilities"][0]["dataSource"])
43 related_vuln = match["relatedVulnerabilities"][0]
44 severity = related_vuln["severity"].lower().replace("negligible", "info")
45 for url in related_vuln["urls"]:
46 references.append(url)
47 else:
48 description = match.get('vulnerability').get('description')
49 severity = match.get('vulnerability').get('severity').lower().replace("negligible", "info")
50 for url in match.get('vulnerability').get('urls'):
51 references.append(url)
52 if not match['artifact']['metadata']:
53 data = f"Artifact: {match['artifact']['name']}" \
54 f"Version: {match['artifact']['version']} " \
55 f"Type: {match['artifact']['type']}"
56 else:
57 if "Source" in match['artifact']['metadata']:
58 data = f"Artifact: {match['artifact']['name']} [{match['artifact']['metadata']['Source']}] " \
59 f"Version: {match['artifact']['version']} " \
60 f"Type: {match['artifact']['type']}"
61 elif "VirtualPath" in match['artifact']['metadata']:
62 data = f"Artifact: {match['artifact']['name']} [{match['artifact']['metadata']['VirtualPath']}] " \
63 f"Version: {match['artifact']['version']} " \
64 f"Type: {match['artifact']['type']}"
65 self.createAndAddVulnToHost(host_id,
66 name=name,
67 desc=description,
68 ref=references,
69 severity=severity,
70 data=data,
71 cve=cve)
72
73 def processCommandString(self, username, current_path, command_string):
74 super().processCommandString(username, current_path, command_string)
75 command_string += f" -o json --file {self._output_file_path}"
76 return command_string
77
78
79 def createPlugin(ignore_info=False):
80 return GrypePlugin(ignore_info=ignore_info)
77 import xml.etree.ElementTree as ET
88
99 from faraday_plugins.plugins.plugin import PluginXMLFormat
10 from faraday_plugins.plugins.plugins_utils import CVE_regex
1011
1112 __author__ = "Francisco Amato"
1213 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
159160 return None
160161
161162
162 class Results():
163 class Results:
163164
164165 def __init__(self, issue_node):
165166 self.node = issue_node
166 self.ref = [issue_node.get("key")]
167 match = CVE_regex.match(issue_node.get("key", ""))
168 if match:
169 self.ref = []
170 self.cve = [match.group()]
171 else:
172 self.ref = [issue_node.get("key")]
173 self.cve = []
167174 self.severity = ""
168175 self.port = "Unknown"
169176 self.service_name = "n/a"
250257 v.name,
251258 desc=v.desc,
252259 severity=v.severity,
253 ref=v.ref)
260 ref=v.ref,
261 cve=v.cve)
254262 else:
255263 s_id = mapped_services.get(v.service_name) or mapped_ports.get(v.port)
256264 self.createAndAddVulnToService(
259267 v.name,
260268 desc=v.desc,
261269 severity=v.severity,
262 ref=v.ref)
270 ref=v.ref,
271 cve=v.cve)
263272
264273 for p in item.ports:
265274 s_id = self.createAndAddServiceToHost(
5757 try:
5858 tree = ET.fromstring(xml_output)
5959 except SyntaxError as err:
60 print("SyntaxError: %s. %s" % (err, xml_output))
60 print(f"SyntaxError: {err}. {xml_output}")
6161 return None
6262
6363 return tree
275275 self.service_id = self.get_text_from_subnode('service-id')
276276 self.name = self.get_text_from_subnode('name')
277277 self.desc = self.get_text_from_subnode('info')
278 self.refs = [r.text for r in self.node.findall('refs/ref')]
278 self.refs = [r.text for r in self.node.findall('refs/ref') if not r.text.startswith('CVE')]
279 self.cve = [r.text for r in self.node.findall('refs/ref') if r.text.startswith('CVE')]
279280 self.exploited_date = self.get_text_from_subnode('exploited-at')
280281 self.exploited = (self.exploited_date is not None)
281282 self.isWeb = False
331332
332333 for v in item.vulnsByHost:
333334 self.createAndAddVulnToHost(
334 h_id, v.name, v.desc, ref=v.refs)
335 h_id, v.name, v.desc, ref=v.refs, cve=v.cve)
335336
336337 for s in item.services:
337338 s_id = self.createAndAddServiceToHost(h_id, s['name'],
344345 for n in item.notesByService[item.id + "_" + s['id']]:
345346 self.createAndAddNoteToService(
346347 h_id, s_id, n.ntype, n.data)
347
348348 if s['port'] in item.credsByService:
349349 for c in item.credsByService[s['port']]:
350350 self.createAndAddCredToService(
362362 category=v.category)
363363 else:
364364 self.createAndAddVulnToService(
365 h_id, s_id, v.name, v.desc, ref=v.refs)
365 h_id, s_id, v.name, v.desc, ref=v.refs, cve=v.cve)
366366
367367 del parser
368368
183183 if item.xref:
184184 kwargs["ref"].append(item.xref)
185185 if item.cve:
186 kwargs["ref"] = kwargs["ref"] + item.cve
186 kwargs["cve"] = item.cve
187187 if item.cvss3_base_score:
188188 kwargs["ref"].append(item.cvss3_base_score)
189189 if item.cvss3_vector:
190190 kwargs["ref"].append(item.cvss3_vector)
191
192191 return kwargs
193192
194193
33 See the file 'doc/LICENSE' for the license information
44
55 """
6 import sys
67 import re
78 import xml.etree.ElementTree as ET
89
910 from bs4 import BeautifulSoup
1011
1112 from faraday_plugins.plugins.plugin import PluginXMLFormat
12 from faraday_plugins.plugins.plugins_utils import resolve_hostname
13 from faraday_plugins.plugins.plugins_utils import resolve_hostname, CVE_regex
1314
1415 __author__ = "Francisco Amato"
1516 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
3233 @param netsparker_xml_filepath A proper xml generated by netsparker
3334 """
3435
35 def __init__(self, xml_output):
36 def __init__(self, xml_output, plugin):
3637 self.filepath = xml_output
38 self.plugin = plugin
3739
3840 tree = self.parse_xml(xml_output)
3941 if tree:
5355 try:
5456 tree = ET.fromstring(xml_output)
5557 except SyntaxError as err:
56 self.logger.error("SyntaxError: %s. %s" % (err, xml_output))
58 self.plugin.logger.error("SyntaxError: %s. %s" % (err, xml_output))
5759 return None
5860
5961 return tree
109111 self.request = self.get_text_from_subnode("rawrequest")
110112 self.response = self.get_text_from_subnode("rawresponse")
111113 self.kvulns = []
114 self.cve = []
112115 for v in self.node.findall("knownvulnerabilities/knownvulnerability"):
113116 self.node = v
117 recheck = CVE_regex.search(v.find('title').text)
118 if recheck:
119 self.cve.append(recheck.group())
114120 self.kvulns.append(self.get_text_from_subnode(
115121 "severity") + "-" + self.get_text_from_subnode("title"))
116122
117123 self.extra = []
118124 for v in item_node.findall("extrainformation/info"):
119 name = v.get('name')
120 if name:
121 self.extra.append("{name}:{v.text}")
125 name_tag = v.find('name')
126 value_tag = v.find('value')
127 if name_tag is not None:
128 self.extra.append(f"{name_tag.text}:{value_tag.text}")
122129
123130 self.node = item_node
124131 self.node = item_node.find("classification")
189196 self.options = None
190197
191198 def parseOutputString(self, output):
192 parser = NetsparkerXmlParser(output)
199 parser = NetsparkerXmlParser(output, self)
193200 host_names_resolve = {}
194201 for i in parser.items:
195202
221228 self.createAndAddVulnWebToService(h_id, s_id, name, ref=i.ref, website=i.hostname,
222229 severity=i.severity, desc=desc, path=i.url, method=i.method,
223230 request=i.request, response=i.response, resolution=resolution,
224 pname=i.param, data=i.data)
231 pname=i.param, data=i.data, cve=i.cve)
225232
226233 del parser
227234
77 import xml.etree.ElementTree as ET
88
99 from faraday_plugins.plugins.plugin import PluginXMLFormat
10 from faraday_plugins.plugins.plugins_utils import CVE_regex
1011
1112 __author__ = "Micaela Ranea Sanchez"
1213 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
148149 'tags': list(),
149150 'is_web': vid.startswith('http-'),
150151 'risk': vulnDef.get('riskScore'),
152 'CVE': []
151153 }
152154
153155 for item in list(vulnDef):
171173 for ref in list(item):
172174 if ref.text:
173175 rf = ref.text.strip()
174 vuln['refs'].append(rf)
176 check = CVE_regex.search(rf.upper())
177 if check:
178 vuln["CVE"].append(check.group())
179 else:
180 vuln['refs'].append(rf)
175181 if item.tag == 'solution':
176182 for htmlType in list(item):
177183 vuln['resolution'] += self.parse_html_type(htmlType)
178 """
179 # there is currently no method to register tags in vulns
180 if item.tag == 'tags':
181 for tag in list(item):
182 vuln['tags'].append(tag.text.lower())
183 """
184
185 """
186 # there is currently no method to register tags in vulns
187 if item.tag == 'tags':
188 for tag in list(item):
189 vuln['tags'].append(tag.text.lower())
190 """
184191 vulns[vid] = vuln
185192 return vulns
186193
269276 v['desc'],
270277 v['refs'],
271278 v['severity'],
272 v['resolution']
279 v['resolution'],
280 cve=v.get('CVE')
273281 )
274282
275283 for s in item['services']:
293301 v['refs'],
294302 v['severity'],
295303 v['resolution'],
296 path=v.get('path', ''))
304 cve=v.get('CVE'),
305 path=v.get('path', '')
306 )
297307 else:
298308 self.createAndAddVulnToService(
299309 h_id,
302312 v['desc'],
303313 v['refs'],
304314 v['severity'],
305 v['resolution']
315 v['resolution'],
316 cve=v.get('CVE')
306317 )
307318
308319 del parser
111111 self.uri = self.get_uri()
112112 self.desc = self.get_desc()
113113 self.params = self.get_params(self.uri)
114 self.CVE = self.get_cve(self.desc)
114115
115116 def get_uri(self):
116117
163164
164165 return None
165166
166 def __str__(self):
167 ports = []
168 for port in self.ports:
169 var = " %s" % port
170 ports.append(var)
171 ports = "\n".join(ports)
172
173 return "%s, %s, %s [%s], %s\n%s" % (self.hostnames, self.status,
174 self.ipv4_address, self.mac_address, self.os, ports)
167 def get_cve(self, desc):
168 match = plugins_utils.CVE_regex.search(desc)
169 if match:
170 return [match.group()]
171 return []
175172
176173
177174 class Host:
200197 for item_node in self.node.findall('item'):
201198 yield Item(item_node)
202199
203 def __str__(self):
204 ports = []
205 for port in self.ports:
206 var = " %s" % port
207 ports.append(var)
208 ports = "\n".join(ports)
209
210 return "%s, %s, %s [%s], %s\n%s" % (self.hostnames, self.status,
211 self.ipv4_address, self.mac_address, self.os, ports)
212200
213201
214202 class NiktoPlugin(PluginXMLFormat):
304292 ref=item.osvdbid,
305293 method=item.method,
306294 params=', '.join(item.params),
295 cve=item.CVE,
307296 **plugins_utils.get_vulnweb_url_fields(item.namelink)
308297 )
309298
1111 __mantainer__ = "@rfocke"
1212 __status__ = "Development"
1313
14
1415 class VulnSoftNipper:
1516 def __init__(self, **kwargs):
1617 self.name = ''
1718 self.data = ''
1819 self.device = ''
1920 self.refs = []
21
2022
2123 class VulnerabilityNipper:
2224 def __init__(self, **kwargs):
2931 self.data = ''
3032 self.recommendation2 = ''
3133
34
3235 class NipperParser:
3336 def __init__(self, output, debug=False):
3437 self.vulns_first = []
3740 self.debug = debug
3841
3942 self.tree = ET.fromstring(output)
40 self.report_tree = self.tree.find("report/part/[@index='2']/section/[@title='Recommendations']/table/[@title='Security Audit recommendations list']/tablebody")
43 self.report_tree = self.tree.find(
44 "report/part/[@index='2']/section/[@title='Recommendations']/table/[@title='Security Audit recommendations list']/tablebody")
4145 self.process_xml()
4246
4347 def process_xml(self):
4751 for tablerow in self.report_tree:
4852 for i, tablecell in enumerate(tablerow.findall('tablecell')):
4953 if len(tablecell.findall('item')) == 1:
50 if i == 0: #Item
54 if i == 0: # Item
5155 vuln = VulnerabilityNipper()
5256 vuln.name = tablecell.find('item').text
53 elif i == 1: #Rating
57 elif i == 1: # Rating
5458 vuln.rating = tablecell.find('item').text
55 elif i == 2: #Recommendations
59 elif i == 2: # Recommendations
5660 vuln.recommendation = tablecell.find('item').text
57 elif i == 3: #Affected devices (with 1 element only)
61 elif i == 3: # Affected devices (with 1 element only)
5862 vuln.affected_devices = []
5963 vuln.affected_devices.append(tablecell.find('item').text)
60 elif i == 4: #Section
64 elif i == 4: # Section
6165 subdetail = tablecell.find('item').text
6266 vuln.section = subdetail
6367
7983 # recomendacion de la vuln
8084 vuln.recommendation2 = detail.find('text').text
8185
82 self.vulns_first.append(vuln) # <- GUARDADO
86 self.vulns_first.append(vuln) # <- GUARDADO
8387 elif len(tablecell.findall('item')) > 1 and i == 3:
8488 # affected devices
8589 vuln.affected_devices = []
111115
112116 self.vulns_audit.append(vuln_soft)
113117
118
114119 class NipperPlugin(PluginXMLFormat):
115120 def __init__(self, *arg, **kwargs):
116121 super().__init__(*arg, **kwargs)
129134 for vuln in parser.vulns_first:
130135 for device in vuln.affected_devices:
131136 ip = resolve_hostname(device)
132 h_id = self.createAndAddHost(ip, hostnames = device)
137 h_id = self.createAndAddHost(ip, hostnames=device)
133138 self.createAndAddVulnToHost(h_id,
134 name = vuln.name,
135 desc = vuln.data,
136 severity = vuln.rating,
137 resolution = vuln.recommendation,
138 data = vuln.data,
139 ref = [],
140 policyviolations = []
139 name=vuln.name,
140 desc=vuln.data,
141 severity=vuln.rating,
142 resolution=vuln.recommendation,
143 data=vuln.data,
144 ref=[],
145 policyviolations=[],
146 cve=[vuln.name]
141147 )
142148 for vuln in parser.vulns_audit:
143149 if vuln.data:
144150 ip = resolve_hostname(device)
145 h_id = self.createAndAddHost(ip, hostnames = vuln.device)
151 h_id = self.createAndAddHost(ip, hostnames=vuln.device)
146152 self.createAndAddVulnToHost(h_id,
147 name = vuln.name,
148 desc = vuln.data,
149 severity = '',
150 resolution = '',
151 data = vuln.data,
152 ref = vuln.refs
153 name=vuln.name,
154 desc=vuln.data,
155 severity='',
156 resolution='',
157 data=vuln.data,
158 ref=vuln.refs,
159 cve=[vuln.name]
153160 )
154161
155162
156163 def createPlugin(ignore_info=False):
157164 return NipperPlugin(ignore_info=ignore_info)
158
456456 self.framework_version = "1.0.0"
457457 self.options = None
458458 self._current_output = None
459 self._command_regex = re.compile(r'^(sudo nmap|nmap|\.\/nmap)\s+.*?')
459 self._command_regex = re.compile(r'^(sudo nmap|nmap|\.\/nmap|sudo masscan|masscan)\s+.*?')
460460 self._use_temp_file = True
461461 self._temp_file_extension = "xml"
462462 self.xml_arg_re = re.compile(r"^.*(-oX\s*[^\s]+).*$")
496496 desc=v.desc,
497497 ref=v.refs,
498498 severity=0,
499 external_id=v.name)
499 cve=[v.name]
500 )
500501
501502 for port in host.ports:
502503
542543 ref=refs,
543544 severity=severity,
544545 website=minterfase,
545 external_id=v.name)
546 cve=[v.name])
546547 else:
547548 v_id = self.createAndAddVulnToService(
548549 h_id,
551552 desc=v.desc,
552553 ref=refs,
553554 severity=severity,
554 external_id=v.name)
555 cve=[v.name]
556 )
555557 del parser
556558
557559 def processCommandString(self, username, current_path, command_string):
562564 super().processCommandString(username, current_path, command_string)
563565 arg_match = self.xml_arg_re.match(command_string)
564566 if arg_match is None:
565 return re.sub(r"(^.*?nmap)",
566 r"\1 -oX %s" % self._output_file_path,
567 command_string)
567 if "masscan" in command_string:
568 return re.sub(r"(^.*?masscan)",
569 r"\1 -oX %s" % self._output_file_path,
570 command_string)
571 else:
572 return re.sub(r"(^.*?nmap)",
573 r"\1 -oX %s" % self._output_file_path,
574 command_string)
568575 else:
569576 return re.sub(arg_match.group(1),
570577 r"-oX %s" % self._output_file_path,
33 See the file 'doc/LICENSE' for the license information
44
55 """
6 import socket
6 import subprocess
77 import re
8 import sys
89 import json
910 import dateutil
10 from collections import defaultdict
1111 from urllib.parse import urlparse
12 from packaging import version
1213 from faraday_plugins.plugins.plugin import PluginMultiLineJsonFormat
1314 from faraday_plugins.plugins.plugins_utils import resolve_hostname
1415
3132 super().__init__(*arg, **kwargs)
3233 self.id = "nuclei"
3334 self.name = "Nuclei"
34 self.plugin_version = "1.0.1"
35 self.version = "2.3.8"
36 self.json_keys = {"matched", "templateID", "host"}
35 self.plugin_version = "1.0.2"
36 self.version = "2.5.3"
37 self.json_keys = {"matched-at", "template-id", "host"}
3738 self._command_regex = re.compile(r'^(sudo nuclei|nuclei|\.\/nuclei|^.*?nuclei)\s+.*?')
3839 self.xml_arg_re = re.compile(r"^.*(-o\s*[^\s]+).*$")
3940 self._use_temp_file = True
6263 status='open',
6364 version='',
6465 description='web server')
65 matched = vuln_dict.get('matched')
66 matched_data = urlparse(matched)
66 matched = vuln_dict.get('matched-at', '')
67 if matched:
68 matched_data = urlparse(matched)
69 else:
70 print('Version not supported, use nuclei 2.5.3 or higher')
71 sys.exit(1)
6772 reference = vuln_dict["info"].get('reference', [])
6873 if not reference:
6974 reference = []
96101 method = request.split(" ")[0]
97102 else:
98103 method = ""
99 data = [f"Matched: {vuln_dict.get('matched')}",
104
105 data = [f"Matched: {vuln_dict.get('matched-at')}",
100106 f"Tags: {vuln_dict['info'].get('tags', '')}",
101 f"Template ID: {vuln_dict['templateID']}"]
107 f"Template ID: {vuln_dict.get('template-id', '')}"]
102108
103109 name = vuln_dict["info"].get("name")
104110 run_date = vuln_dict.get('timestamp')
123129 params=matched_data.params,
124130 path=matched_data.path,
125131 data="\n".join(data),
126 external_id=f"NUCLEI-{vuln_dict.get('templateID', '')}",
132 external_id=f"NUCLEI-{vuln_dict.get('template-id', '')}",
127133 run_date=run_date
128134 )
129
135
130136 def processCommandString(self, username, current_path, command_string):
131 """
132 Adds the -oX parameter to get xml output to the command string that the
133 user has set.
134 """
135137 super().processCommandString(username, current_path, command_string)
136138 arg_match = self.xml_arg_re.match(command_string)
137139 if arg_match is None:
141143 else:
142144 return re.sub(arg_match.group(1),
143145 r"--json -irr -o %s" % self._output_file_path,
144 command_string)
146 command_string)
147
148 def canParseCommandString(self, current_input):
149 can_parse = super().canParseCommandString(current_input)
150 if can_parse:
151 try:
152 proc = subprocess.Popen([self.command, '-version'], stderr=subprocess.PIPE)
153 output = proc.stderr.read()
154 match = re.search(r"Current Version: ([0-9.]+)", output.decode('UTF-8'))
155 if match:
156 nuclei_version = match.groups()[0]
157 return version.parse(nuclei_version) >= version.parse("2.5.3")
158 else:
159 return False
160 except Exception as e:
161 return False
145162
146163
147164 def createPlugin(ignore_info=False):
0 import subprocess
1 import re
2 import json
3 import dateutil
4 from packaging import version
5 from urllib.parse import urlparse
6 from faraday_plugins.plugins.plugin import PluginMultiLineJsonFormat
7 from faraday_plugins.plugins.plugins_utils import resolve_hostname
8
9 __author__ = "Emilio Couto"
10 __copyright__ = "Copyright (c) 2021, Faraday Security"
11 __credits__ = ["Emilio Couto"]
12 __license__ = ""
13 __version__ = "1.0.0"
14 __maintainer__ = "Emilio Couto"
15 __email__ = "[email protected]"
16 __status__ = "Development"
17
18
19 class NucleiLegacyPlugin(PluginMultiLineJsonFormat):
20 """ Handle the Nuclei tool. Detects the output of the tool
21 and adds the information to Faraday.
22 """
23
24 def __init__(self, *arg, **kwargs):
25 super().__init__(*arg, **kwargs)
26 self.id = "nuclei_legacy"
27 self.name = "Nuclei"
28 self.plugin_version = "1.0.0"
29 self.version = "2.5.2"
30 self._command_regex = re.compile(r'^(sudo nuclei|nuclei|\.\/nuclei|^.*?nuclei)\s+.*?')
31 self.xml_arg_re = re.compile(r"^.*(-o\s*[^\s]+).*$")
32 self._use_temp_file = True
33 self._temp_file_extension = "json"
34 self.json_keys = {"matched", "templateID", "host"}
35
36 def parseOutputString(self, output, debug=False):
37 for vuln_json in filter(lambda x: x != '', output.split("\n")):
38 vuln_dict = json.loads(vuln_json)
39 host = vuln_dict.get('host')
40 url_data = urlparse(host)
41 ip = vuln_dict.get("ip", resolve_hostname(url_data.hostname))
42 host_id = self.createAndAddHost(
43 name=ip,
44 hostnames=[url_data.hostname])
45 port = url_data.port
46 if not port:
47 if url_data.scheme == 'https':
48 port = 443
49 else:
50 port = 80
51 service_id = self.createAndAddServiceToHost(
52 host_id,
53 name=url_data.scheme,
54 ports=port,
55 protocol="tcp",
56 status='open',
57 version='',
58 description='web server')
59 matched = vuln_dict.get('matched')
60 matched_data = urlparse(matched)
61 reference = vuln_dict["info"].get('reference', [])
62 if not reference:
63 reference = []
64 else:
65 if isinstance(reference, str):
66 if re.match('^- ', reference):
67 reference = list(filter(None, [re.sub('^- ', '', elem) for elem in reference.split('\n')]))
68 else:
69 reference = [reference]
70 references = vuln_dict["info"].get('references', [])
71 if references:
72 if isinstance(references, str):
73 if re.match('^- ', references):
74 references = list(filter(None, [re.sub('^- ', '', elem) for elem in references.split('\n')]))
75 else:
76 references = [references]
77 else:
78 references = []
79 cwe = vuln_dict['info'].get('cwe', [])
80 capec = vuln_dict['info'].get('capec', [])
81 refs = sorted(list(set(reference + references + cwe + capec)))
82 tags = vuln_dict['info'].get('tags', [])
83 if isinstance(tags, str):
84 tags = tags.split(',')
85 impact = vuln_dict['info'].get('impact')
86 resolution = vuln_dict['info'].get('resolution', '')
87 easeofresolution = vuln_dict['info'].get('easeofresolution')
88 request = vuln_dict.get('request', '')
89 if request:
90 method = request.split(" ")[0]
91 else:
92 method = ""
93 data = [f"Matched: {vuln_dict.get('matched', '')}",
94 f"Tags: {vuln_dict['info'].get('tags', '')}",
95 f"Template ID: {vuln_dict.get('templateID', '')}"]
96
97 name = vuln_dict["info"].get("name")
98 run_date = vuln_dict.get('timestamp')
99 if run_date:
100 run_date = dateutil.parser.parse(run_date)
101 self.createAndAddVulnWebToService(
102 host_id,
103 service_id,
104 name=name,
105 desc=vuln_dict["info"].get("description", name),
106 ref=refs,
107 severity=vuln_dict["info"].get('severity'),
108 tags=tags,
109 impact=impact,
110 resolution=resolution,
111 easeofresolution=easeofresolution,
112 website=host,
113 request=request,
114 response=vuln_dict.get('response', ''),
115 method=method,
116 query=matched_data.query,
117 params=matched_data.params,
118 path=matched_data.path,
119 data="\n".join(data),
120 external_id=f"NUCLEI-{vuln_dict.get('templateID', '')}",
121 run_date=run_date
122 )
123
124 def processCommandString(self, username, current_path, command_string):
125 super().processCommandString(username, current_path, command_string)
126 arg_match = self.xml_arg_re.match(command_string)
127 if arg_match is None:
128 return re.sub(r"(^.*?nuclei)",
129 r"\1 --json -irr -o %s" % self._output_file_path,
130 command_string)
131 else:
132 return re.sub(arg_match.group(1),
133 r"--json -irr -o %s" % self._output_file_path,
134 command_string)
135
136 def canParseCommandString(self, current_input):
137 can_parse = super().canParseCommandString(current_input)
138 if can_parse:
139 try:
140 proc = subprocess.Popen([self.command, '-version'], stderr=subprocess.PIPE)
141 output = proc.stderr.read()
142 match = re.search(r"Current Version: ([0-9.]+)", output.decode('UTF-8'))
143 if match:
144 nuclei_version = match.groups()[0]
145 return version.parse(nuclei_version) <= version.parse("2.5.2")
146 else:
147 return False
148 except Exception as e:
149 return False
150
151 def createPlugin(ignore_info=False):
152 return NucleiLegacyPlugin(ignore_info=ignore_info)
194194 if rule['id'] == info['rule_id']:
195195 vuln_name = info['rule_title']
196196 vuln_data = info['rule_check']
197 vuln_ref = info['rule_ident']
197 vuln_cve = info['rule_ident']
198198
199199 self.createAndAddVulnToHost(
200200 host_id,
201201 vuln_name,
202202 desc=desc,
203 ref=[vuln_ref],
204203 severity=severity,
205204 data=vuln_data,
206205 external_id=rule['id'],
207 run_date=vuln_run_date)
206 run_date=vuln_run_date,
207 cve=[vuln_cve])
208208
209209
210210 def createPlugin(ignore_info=False):
8080 try:
8181 yield Item(node, hosts)
8282 except Exception as e:
83 self.logger.error("Error generating Iteem from %s [%s]", node.attrib, e)
83 self.logger.error(f"Error generating Iteem from {node.attrib} [{e}]")
8484
8585 except Exception as e:
86 self.logger.error("Tag not found: %s", e)
86 self.logger.error(f"Tag not found: {e}")
8787
8888 def get_hosts(self, tree):
8989 # Hosts are located in: /report/report/host
153153 self.severity_nr = self.get_text_from_subnode("severity")
154154 self.service = "Unknown"
155155 self.protocol = ""
156 self.cpe = self.node.findall("detection/result/details/detail")
157 self.cpe = self.cpe[0].findtext("value") if self.cpe else None
156 details = self.node.findall("detection/result/details/detail")
157 self.cpe = details[0].findtext("value") if details else None
158158 port_string = self.get_text_from_subnode('port')
159159 info = port_string.split("/")
160 if len(info) < 2:
161 info = details[1].findtext("value").split("/") if details else ["", ""]
160162 self.protocol = "".join(filter(lambda x: x.isalpha() or x in ("-", "_"), info[1]))
161163 self.port = "".join(filter(lambda x: x.isdigit(), info[0])) or None
162164 if not self.port:
180182 self.cvss_vector = ''
181183 self.tags = self.get_text_from_subnode('tags')
182184 self.data = self.get_text_from_subnode('description')
185 self.data += f'\n\nid {item_node.attrib.get("id")}'
183186 if self.tags:
184187 tags_data = self.get_data_from_tags(self.tags)
185188 self.description = tags_data['description']
186189 self.resolution = tags_data['solution']
187190 self.cvss_vector = tags_data['cvss_base_vector']
188191 if tags_data['impact']:
189 self.data += '\n\nImpact: {}'.format(tags_data['impact'])
192 self.data += f'\n\nImpact: {tags_data["impact"]}'
190193
191194 def get_text_from_subnode(self, subnode_xpath_expr):
192195 """
337340
338341 if item.name is not None:
339342 ref = []
343 cve = []
340344 if item.cve:
341345 cves = item.cve.split(',')
342 for cve in cves:
343 ref.append(cve.strip())
346 for i in cves:
347 cve.append(i.strip())
344348 if item.bid:
345349 bids = item.bid.split(',')
346350 for bid in bids:
376380 resolution=item.resolution,
377381 ref=ref,
378382 external_id=item.id,
379 data=item.data)
383 data=item.data,
384 cve=cve)
380385 else:
381386 if item.service:
382387 web = re.search(
407412 ref=ref,
408413 resolution=item.resolution,
409414 external_id=item.id,
410 data=item.data)
415 data=item.data,
416 cve=cve)
411417 elif item.severity not in self.ignored_severities:
412418 self.createAndAddVulnToService(
413419 h_id,
418424 ref=ref,
419425 resolution=item.resolution,
420426 external_id=item.id,
421 data=item.data)
427 data=item.data,
428 cve=cve)
422429 del parser
423430
424431 @staticmethod
0 """
1 Faraday Penetration Test IDE
2 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
3 See the file 'doc/LICENSE' for the license information
4
5 """
0 """
1 Faraday Penetration Test IDE
2 Copyright (C) 2020 Infobyte LLC (http://www.infobytesec.com/)
3 See the file 'doc/LICENSE' for the license information
4
5 """
6 from dateutil.parser import parse
7 import json
8 from datetime import datetime
9 from dataclasses import dataclass
10 from faraday_plugins.plugins.plugin import PluginMultiLineJsonFormat
11
12 __author__ = "Nicolas Rebagliati"
13 __copyright__ = "Copyright (c) 2020, Infobyte LLC"
14 __credits__ = ["Nicolas Rebagliati"]
15 __license__ = ""
16 __version__ = "0.0.1"
17 __maintainer__ = "Nicolas Rebagliati"
18 __email__ = "[email protected]"
19 __status__ = "Development"
20
21
22 @dataclass
23 class Issue:
24 region: str
25 profile: str
26 severity: str
27 scored: str
28 account: str
29 message: str
30 control: str
31 status: str
32 level: str
33 control_id: str
34 timestamp: datetime
35 compliance: str
36 service: str
37 caf_epic: str
38 risk: str
39 doc_link: str
40 remediation: str
41 resource_id: str
42
43
44 class ProwlerJsonParser:
45
46 def parse_issues(self, records):
47 for record in records:
48 json_data = json.loads(record)
49 region = json_data.get("Region", "AWS_REGION")
50 profile = json_data.get("Profile", "")
51 severity = json_data.get("Severity", "info").lower()
52 scored = json_data.get("Status", "")
53 account = json_data.get("Account Number", "")
54 message = json_data.get("Message", "")
55 control = json_data.get("Control", "")
56 status = json_data.get("Status", "")
57 level = json_data.get("Level", "")
58 control_id = json_data.get("Control ID", "")
59 timestamp = json_data.get("Timestamp", None)
60 if timestamp:
61 timestamp = parse(timestamp)
62 compliance = json_data.get("Compliance", "")
63 service = json_data.get("Service", "")
64 caf_epic = json_data.get("CAF Epic", "")
65 risk = json_data.get("Risk", "")
66 doc_link = json_data.get("Doc link", "")
67 remediation = json_data.get("Remediation", "")
68 resource_id = json_data.get("Resource ID", "")
69 if status == "FAIL":
70 self.issues.append(Issue(region=region, profile=profile, severity=severity, scored=scored,
71 account=account, message=message, control=control, status=status,
72 level=level, control_id=control_id, timestamp=timestamp, compliance=compliance,
73 service=service, caf_epic=caf_epic, risk=risk, doc_link=doc_link,
74 remediation=remediation, resource_id=resource_id))
75
76 def __init__(self, json_output):
77 self.issues = []
78 self.parse_issues(json_output.splitlines())
79
80
81 class ProwlerPlugin(PluginMultiLineJsonFormat):
82 """ Handle the AWS Prowler tool. Detects the output of the tool
83 and adds the information to Faraday.
84 """
85
86 def __init__(self, *arg, **kwargs):
87 super().__init__(*arg, **kwargs)
88 self.id = "prowler"
89 self.name = "Prowler"
90 self.plugin_version = "0.1"
91 self.version = "0.0.1"
92 self.json_keys = {"Profile", "Account Number", "Region"}
93
94 def parseOutputString(self, output, debug=False):
95 parser = ProwlerJsonParser(output)
96 for issue in parser.issues:
97 host_name = f"{issue.service}-{issue.account}-{issue.region}"
98 host_id = self.createAndAddHost(name=host_name,
99 description=f"AWS Service: {issue.service} - Account: {issue.account}"
100 f" - Region: {issue.region}")
101
102 vuln_desc = f"{issue.risk}\nCompliance: {issue.compliance}\nMessage: {issue.message}"
103 self.createAndAddVulnToHost(host_id=host_id, name=issue.control, desc=vuln_desc,
104 data=f"Resource ID: {issue.resource_id}",
105 severity=self.normalize_severity(issue.severity), resolution=issue.remediation,
106 run_date=issue.timestamp, external_id=f"{self.name.upper()}-{issue.control_id}",
107 ref=[issue.doc_link])
108
109
110 def createPlugin(ignore_info=False):
111 return ProwlerPlugin(ignore_info=ignore_info)
172172
173173 # References
174174 self.ref = []
175 self.cve = []
175176
176177 cve_id = self.get_text_from_glossary('CVE_ID_LIST/CVE_ID/ID')
177178 if cve_id:
178 self.ref.append(cve_id)
179 self.cve.append(cve_id)
179180
180181 if self.cvss:
181182 self.ref.append('CVSS SCORE: {}'.format(self.cvss))
303304 self.desc += ''
304305
305306 self.ref = []
307 self.cve = []
306308 for r in issue_node.findall('CVE_ID_LIST/CVE_ID'):
307309 self.node = r
308 self.ref.append(self.get_text_from_subnode('ID'))
310 self.cve.append(self.get_text_from_subnode('ID'))
309311 for r in issue_node.findall('BUGTRAQ_ID_LIST/BUGTRAQ_ID'):
310312 self.node = r
311313 self.ref.append('bid-' + self.get_text_from_subnode('ID'))
360362 severity=v.severity,
361363 resolution=v.solution if v.solution else '',
362364 desc=v.desc,
363 external_id=v.external_id)
365 external_id=v.external_id,
366 cve=v.cve)
364367
365368 else:
366369 web = False
393396 severity=v.severity,
394397 desc=v.desc,
395398 resolution=v.solution if v.solution else '',
396 external_id=v.external_id)
399 external_id=v.external_id,
400 cve=v.cve)
397401
398402 else:
399403 self.createAndAddVulnToService(
404408 severity=v.severity,
405409 desc=v.desc,
406410 resolution=v.solution if v.solution else '',
407 external_id=v.external_id)
411 external_id=v.external_id,
412 cve=v.cve)
408413
409414 del parser
410415
140140 self.desc += "\nContext: " + self.context if self.context else ""
141141
142142 self.ref = []
143 if self.cve:
144 self.ref = self.cve.split(",")
145143
146144 def get_text_from_subnode(self, subnode_xpath_expr):
147145 """
189187 for k, vulns in item.ports.items():
190188 if k:
191189 for v in vulns:
190 cve = v.cve.split(",") if v.cve else []
192191 web = False
193192 s_id = self.createAndAddServiceToHost(h_id, 'unknown', v.protocol.lower(), ports=[str(v.port)],
194193 status="open")
199198 if web:
200199 v_id = self.createAndAddVulnWebToService(h_id, s_id, v.name, ref=v.ref,
201200 website=hostname, severity=v.severity,
202 resolution=v.solution, desc=v.desc)
201 resolution=v.solution, desc=v.desc, cve=cve)
203202 else:
204203 v_id = self.createAndAddVulnToService(h_id, s_id, v.name, ref=v.ref,
205204 severity=v.severity, resolution=v.solution,
206 desc=v.desc)
205 desc=v.desc, cve=cve)
207206 else:
208207 for v in vulns:
208 cve = v.cve.split(",") if v.cve else []
209209 v_id = self.createAndAddVulnToHost(h_id, v.name, ref=v.ref, severity=v.severity,
210 resolution=v.solution, desc=v.desc)
210 resolution=v.solution, desc=v.desc, cve=cve)
211211 del parser
212212
213213
5555 elif os.path.isdir(filename):
5656 shutil.rmtree(filename)
5757 except Exception as e:
58 self.logger.error("Error on delete file: (%s) [%s]", filename, e)
58 self.logger.error(f"Error on delete file: ({filename}) [{e}]")
5959
6060 def parseOutputString(self, output):
6161 for vuln_json in filter(lambda x: x != '', output.split("\n")):
7171 for name, vuln_info in vulns.items():
7272 description = vuln_info.get('summary')
7373 references = vuln_info.get('references')
74 self.createAndAddVulnToService(h_id, s_id, name, desc=description, severity=0, ref=references)
75
74 self.createAndAddVulnToService(h_id, s_id, name, desc=description, severity=0, ref=references, cve=name)
7675
7776 def processCommandString(self, username, current_path, command_string):
7877 """
2929 list_vuln = []
3030 if scan_result:
3131 for scan in scan_result:
32 try:
33 host = self.get_host(scan['server_info']['server_location'])
34 except KeyError:
35 host = {}
36
37 try:
38 certif = self.get_certification(scan['scan_commands_results']['certificate_info'])
39 except KeyError:
40 certif = {}
41
42 if not scan['scan_commands']:
32 server_info = scan.get('server_info', scan).get('server_location')
33 host = self.get_host(server_info) if server_info else {}
34
35 scan_commands_results = scan.get('scan_commands_results', scan.get("scan_result", {}))
36
37 if len(scan_commands_results) > 0:
38 commands = []
39 for command in scan_commands_results:
40 if command.find("cipher") >= 0 and scan_commands_results[command].get('result', True):
41 commands.append(command)
42 ciphers = self.get_cipher(scan_commands_results, commands)
43 else:
4344 ciphers = {}
44 else:
45 commands = []
46 for command in scan['scan_commands']:
47 if command.find("cipher") >= 0:
48 commands.append(command)
49 ciphers = self.get_cipher(scan['scan_commands_results'], commands)
50
51 try:
52 heartbleed = self.get_heartbleed(scan['scan_commands_results']['heartbleed'])
53 except KeyError:
54 heartbleed = {}
55
56 try:
57 openssl_ccs = self.get_openssl_ccs(scan['scan_commands_results']['openssl_ccs_injection'])
58 except KeyError:
59 openssl_ccs = {}
60
45
46
47 certificate_info = scan_commands_results.get('certificate_info')
48 certif = self.get_certification(certificate_info) if certificate_info else {}
49
50 heartbleed_reulsts = scan_commands_results.get('heartbleed')
51 heartbleed = self.get_heartbleed(heartbleed_reulsts) if heartbleed_reulsts else {}
52
53 openssl_ccs_injection = scan_commands_results.get('openssl_ccs_injection')
54 openssl_ccs = self.get_openssl_ccs(openssl_ccs_injection) if openssl_ccs_injection else {}
6155 json_vuln = {
6256 "host_info": host,
6357 "certification": certif,
6458 "ciphers": ciphers,
6559 "heartbleed": heartbleed,
66 "openssl_ccs":openssl_ccs
60 "openssl_ccs": openssl_ccs
6761 }
6862
6963 list_vuln.append(json_vuln)
9084 return json_host
9185
9286 def get_certification(self, certificate):
93 certif_deploy = certificate['certificate_deployments']
87 certif_deploy = certificate.get('certificate_deployments', certificate.get('result'))
88 certif_deploy = certif_deploy.get('certificate_deployments', [{}]) if isinstance(certif_deploy, dict) else [{}]
9489 send_certif = certif_deploy[0].get('leaf_certificate_subject_matches_hostname', True)
9590
9691 if not send_certif:
97 why = certif_deploy[0]['received_certificate_chain'][0]['subject']['rfc4514_string']
92 subject = certif_deploy[0]['received_certificate_chain'][0]['subject']
93 why = subject.get('rfc4514_string')
94 if not why:
95 why = subject.get('attributes', {}).get('rfc4514_string')
96 hostname_used_for_server = certificate.get('hostname_used_for_server_name_indication', certificate.get("result", {})
97 .get('hostname_used_for_server_name_indication', 'Not hostname'))
9898 json_certif = {
9999 "name": "SSL/TLS Certificate Mismatch",
100100 "desc": "The software communicates with a host that provides a certificate, but the software does not properly ensure that the certificate is actually associated with that host.",
101 "data": f"Certificate {why} does not match server hostname {certificate.get('hostname_used_for_server_name_indication', 'Not hostname')}",
101 "data": f"Certificate {why} does not match server hostname {hostname_used_for_server}",
102102 "impact": {"integrity": True},
103103 "ref": ["https://cwe.mitre.org/data/definitions/297.html"],
104104 "external_id": "CWE-297",
148148
149149 def get_heartbleed(self, heartbleed):
150150 json_heartbleed = {}
151 if heartbleed.get('is_vulnerable_to_heartbleed', False):
151 vulnerable_to_heartbleed = heartbleed.get('is_vulnerable_to_heartbleed', heartbleed.get('result', False))
152 vulnerable_to_heartbleed = vulnerable_to_heartbleed.get('is_vulnerable_to_heartbleed') if vulnerable_to_heartbleed else False
153 if vulnerable_to_heartbleed:
152154 json_heartbleed = {
153155 "name": "OpenSSL Heartbleed",
154156 "desc": "OpenSSL versions 1.0.1 through 1.0.1f contain a flaw in its implementation of the TLS/DTLS heartbeat functionality. This flaw allows an attacker to retrieve private memory of an application that uses the vulnerable OpenSSL library in chunks of 64k at a time. Note that an attacker can repeatedly leverage the vulnerability to retrieve as many 64k chunks of memory as are necessary to retrieve the intended secrets. The sensitive information that may be retrieved using this vulnerability include:\n\n Primary key material (secret keys)\n Secondary key material (user names and passwords used by vulnerable services)\n Protected content (sensitive data used by vulnerable services)\n Collateral (memory addresses and content that can be leveraged to bypass exploit mitigations)\n Exploit code is publicly available for this vulnerability. Additional details may be found in CERT/CC Vulnerability Note VU#720951.",
161163
162164 def get_openssl_ccs(self, openssl_ccs):
163165 json_openssl_ccs = {}
164 if openssl_ccs.get('is_vulnerable_to_ccs_injection', False):
166 is_vulnerable_to_ccs_injection = openssl_ccs.get('is_vulnerable_to_ccs_injection', openssl_ccs.get('result', False))
167 is_vulnerable_to_ccs_injection = is_vulnerable_to_ccs_injection.get('is_vulnerable_to_ccs_injection', False) if is_vulnerable_to_ccs_injection else False
168 if is_vulnerable_to_ccs_injection:
165169 json_openssl_ccs = {
166170 "name": "OpenSSL CCS Injection",
167171 "desc": 'OpenSSL before 0.9.8za, 1.0.0 before 1.0.0m, and 1.0.1 before 1.0.1h does not properly restrict processing of ChangeCipherSpec messages, which allows man-in-the-middle attackers to trigger use of a zero-length master key in certain OpenSSL-to-OpenSSL communications, and consequently hijack sessions or obtain sensitive information, via a crafted TLS handshake, aka the "CCS Injection" vulnerability."',
181185 self.name = "Sslyze Json"
182186 self.plugin_version = "0.1"
183187 self.version = "3.4.5"
184 self.json_keys = {'server_scan_results', 'sslyze_url'}
188 self.json_keys = {'server_scan_results', 'sslyze_url', 'sslyze_version'}
185189 self._command_regex = re.compile(r'^(sudo sslyze|sslyze|\.\/sslyze)\s+.*?')
186190 self.json_arg_re = re.compile(r"^.*(--json_out\s*[^\s]+).*$")
187191 self._use_temp_file = True
5454 data=data,
5555 resolution=vulnerability['topFix']['fixResolution'],
5656 ref=refs,
57 severity=vulnerability['severity'])
57 severity=vulnerability['severity'],
58 cve=[vulnerability['name']])
5859 else:
5960 self.createAndAddVulnToHost(host_id,
6061 name=vulnerability['name'],
7576 name=vulnerability['vulnerability'],
7677 desc=vulnerability['description'],
7778 ref=[vulnerability['link']],
78 severity=vulnerability['severity']
79 severity=vulnerability['severity'],
80 cve=[vulnerability['vulnerability']]
7981 )
82
8083 elif 'package' in vulnerability:
8184 host_id = self.createAndAddHost(vulnerability['feed_group'])
8285 service_id = self.createAndAddServiceToHost(
8992 service_id,
9093 name=f'{vulnerability["vuln"]} {vulnerability["package_name"]}',
9194 ref=[vulnerability['url']],
92 severity=vulnerability['severity']
95 severity=vulnerability['severity'],
96 cve=[vulnerability['vuln']]
9397 )
9498
9599
5555
5656 def list_report_files():
5757 report_filenames = os.walk(REPORTS_SUMMARY_DIR)
58 for root, directory, filenames in report_filenames:
58 for plugin_folder, directory, filenames in report_filenames:
5959 if '.git' in directory or 'faraday_plugins_tests' in directory:
6060 continue
6161 for filename in filenames:
6262 if filename in BLACK_LIST:
6363 continue
64 if '.git' in root:
64 if '.git' in plugin_folder:
6565 continue
6666 if not filename.endswith('_summary.json'):
67 yield os.path.join(root, filename)
67 yield Path(plugin_folder).name, os.path.join(plugin_folder, filename)
6868
6969
7070 def is_valid_ipv4_address(address):
9595 def test_reports_collection_exists():
9696 assert os.path.isdir(REPORTS_SUMMARY_DIR) is True, "Please clone the report-collection repo!"
9797
98 @pytest.mark.parametrize("report_filename", list_report_files())
99 def test_autodetected_on_all_report_collection(report_filename):
98 @pytest.mark.parametrize("report_filename_and_folder", list_report_files())
99 def test_autodetected_on_all_report_collection(report_filename_and_folder):
100 plugin_folder = report_filename_and_folder[0]
101 report_filename = report_filename_and_folder[1]
100102 plugin: PluginBase = get_plugin_from_cache(report_filename)
101103 assert plugin, report_filename
104 assert plugin.id == plugin_folder
102105
103106
104 @pytest.mark.parametrize("report_filename", list_report_files())
105 def test_schema_on_all_reports(report_filename):
107 @pytest.mark.parametrize("report_filename_and_folder", list_report_files())
108 def test_schema_on_all_reports(report_filename_and_folder):
109 report_filename = report_filename_and_folder[1]
106110 plugin, plugin_json = get_report_json_from_cache(report_filename)
107111 if plugin_json:
108112 serializer = BulkCreateSchema()
113117
114118
115119 @pytest.mark.skip(reason="Skip validate ip format")
116 @pytest.mark.parametrize("report_filename", list_report_files())
117 def test_host_ips_all_reports(report_filename):
120 @pytest.mark.parametrize("report_filename_and_folder", list_report_files())
121 def test_host_ips_all_reports(report_filename_and_folder):
122 report_filename = report_filename_and_folder[1]
118123 plugin, plugin_json = get_report_json_from_cache(report_filename)
119124 if plugin_json:
120125 if plugin.id not in SKIP_IP_PLUGINS:
122127 assert is_valid_ip_address(host['ip']) is True
123128
124129
125 @pytest.mark.parametrize("report_filename", list_report_files())
126 def test_summary_reports(report_filename):
130 @pytest.mark.parametrize("report_filename_and_folder", list_report_files())
131 def test_summary_reports(report_filename_and_folder):
132 report_filename = report_filename_and_folder[1]
127133 plugin, plugin_json = get_report_json_from_cache(report_filename)
128134 if plugin_json:
129135 summary_file = f"{os.path.splitext(report_filename)[0]}_summary.json"
142148
143149
144150 @pytest.mark.performance
145 @pytest.mark.parametrize("report_filename", list_report_files())
146 def test_detected_tools_on_all_report_collection(report_filename, benchmark):
151 @pytest.mark.parametrize("report_filename_and_folder", list_report_files())
152 def test_detected_tools_on_all_report_collection(report_filename_and_folder, benchmark):
153 report_filename = report_filename_and_folder[1]
147154 plugins_manager = PluginsManager()
148155 analyzer = ReportAnalyzer(plugins_manager)
149156 plugin: PluginBase = analyzer.get_plugin(report_filename)