Codebase list faraday-plugins / a9e7446
New upstream version 1.2 Sophie Brun 3 years ago
94 changed file(s) with 3161 addition(s) and 3371 deletion(s). Raw diff Collapse all Expand all
126126
127127 .idea
128128 /report-collection/
129
130 #project settings
131 *.project
00 stages:
11 - pre_testing
22 - testing
3 - post_testing
34 - publish
45
56 before_script:
67 - apt-get update -qy
78 - pip install pip -U
9
10 workflow:
11 rules:
12 - if: $CI_MERGE_REQUEST_ID
13 when: never
14 - when: always
815
916 flake8:
1017 image: python:3
2027 - wc -l files.processed
2128
2229 tests:
23 image: python:3
30 image: python:3.7
2431 stage: testing
2532 coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/'
2633 before_script:
2835 - virtualenv -p python3 faraday_venv
2936 - source faraday_venv/bin/activate
3037 - pip3 install pytest pytest-xdist pytest-cov
31 - mkdir run_from
32 - cd run_from && git clone https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.com/faradaysec/support/report-collection.git
38 - git clone https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.com/faradaysec/faraday.git
39 - git clone https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.com/faradaysec/support/report-collection.git
40 - cd faraday
41 - python3 setup.py install
42 - pip uninstall faraday-plugins -y # we need to install fardaysec for marshmallow schemas, we remove plugins from pypi
43 - cd ..
3344 script:
34 - cd -
3545 - source faraday_venv/bin/activate
3646 - python3 setup.py install
37 - cd run_from && pytest ../tests --capture=sys -v --cov=faraday_plugins --color=yes --disable-warnings
47 - pytest tests --capture=sys -v --cov=faraday_plugins --color=yes --disable-warnings
3848
39 publish_pipy:
49
50 test_performance:
51 image: python:3.7
52 stage: post_testing
53 coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/'
54 allow_failure: true
55 before_script:
56 - pip3 install virtualenv
57 - virtualenv -p python3 faraday_venv
58 - source faraday_venv/bin/activate
59 - pip3 install pytest pytest-xdist pytest-cov
60 - git clone https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.com/faradaysec/faraday.git
61 - git clone https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.com/faradaysec/support/report-collection.git
62 - cd faraday
63 - python3 setup.py install
64 - pip uninstall faraday-plugins -y # we need to install fardaysec for marshmallow schemas, we remove plugins from pypi
65 - cd ..
66 script:
67 - source faraday_venv/bin/activate
68 - python3 setup.py install
69 - pytest tests --capture=sys -v --cov=faraday_plugins --color=yes --disable-warnings --performance
70 rules:
71 - if: '$CI_COMMIT_BRANCH == "develop"'
72 when: on_success
73
74 publish_pypi:
4075 image: python:3
4176 stage: publish
42 before_script:
43 - pip3 install virtualenv
44 - virtualenv -p python3 faraday_venv twine
45 - source faraday_venv/bin/activate
4677 script:
78 - apt-get update -qy
79 - apt-get install twine -y
4780 - python setup.py sdist bdist_wheel
48 - twine upload dist/* -u $TWINE_USERNAME -p $TWINE_PASSWORD
81 - twine upload -u $PYPI_USER -p $PYPI_PASS dist/* --verbose
82 rules:
83 - if: '$CI_COMMIT_TAG'
84 when: on_success
4985
50 only:
51 variables:
52 - $CI_COMMIT_TAG =~ /^v[0-9.]+$/
88 > List Plugins
99
1010 ```shell script
11 python -m faraday_plugins list
11 faraday-plugins show
12 ```
13
14 > Test autodetect plugin from command
15
16 ```shell script
17 faraday-plugins detect-command "ping -c 4 www.google.com"
18 > Faraday Plugin: ping
19 ```
20
21 > Test command with plugin
22
23 Optional params:
24
25 - -dr: Dont run, just show the generated command
26
27 ```shell script
28 faraday-plugins process-command "ping -c4 www.google.com"
29 Running command: ping -c4 www.google.com
30
31 PING www.google.com (216.58.222.36): 56 data bytes
32 64 bytes from 216.58.222.36: icmp_seq=0 ttl=54 time=11.144 ms
33 64 bytes from 216.58.222.36: icmp_seq=1 ttl=54 time=14.330 ms
34 64 bytes from 216.58.222.36: icmp_seq=2 ttl=54 time=11.997 ms
35 64 bytes from 216.58.222.36: icmp_seq=3 ttl=54 time=11.190 ms
36
37 --- www.google.com ping statistics ---
38 4 packets transmitted, 4 packets received, 0.0% packet loss
39 round-trip min/avg/max/stddev = 11.144/12.165/14.330/1.295 ms
40
41 Faraday API json:
42 {
43 "hosts": [
44 {
45 "ip": "216.58.222.36",
46 "os": "unknown",
47 "hostnames": [
48 "www.google.com"
49 ],
50 "description": "",
51 "mac": "00:00:00:00:00:00",
52 "credentials": [],
53 "services": [],
54 "vulnerabilities": []
55 }
56 ],
57 "command": {
58 "tool": "ping",
59 "command": "ping",
60 "params": "-c4 www.google.com",
61 "user": "aenima",
62 "hostname": "",
63 "start_date": "2020-05-05T23:09:39.656132",
64 "duration": 56789,
65 "import_source": "report"
66 }
67 }
1268 ```
1369
1470 > Test autodetect plugin from report
1571
1672 ```shell script
17 python -m faraday_plugins detect /path/to/report.xml
73 faraday-plugins detect-report /path/to/report.xml
1874 ```
1975
2076
2177 > Test report with plugin
2278
2379 ```shell script
24 python -m faraday_plugins process appscan /path/to/report.xml
80 faraday-plugins process-report /path/to/report.xml
2581 ```
82
83 > Process options:
84
85 Both process-xxx command have this optional parameters
86
87 - --plugin_id: If given will use that plugin instead of try to detect it
88 - --summary: If given will generate a summary of the findings instead of the result
89 - -cpf/--custom-plugins-folder: If given will also look for custom plugins if that path
90
91 NOTE: you can also use -cpf in **show** command to test if your custom plugins load ok
2692
2793 > Plugin Logger
2894
3096
3197 ```shell script
3298 export PLUGIN_DEBUG=1
33 python -m faraday_plugins process appscan /path/to/report.xml
99 faraday-plugins proces-report /path/to/report.xml
34100 2019-11-15 20:37:03,355 - faraday.faraday_plugins.plugins.manager - INFO [manager.py:113 - _load_plugins()] Loading Native Plugins...
35101 2019-11-15 20:37:03,465 - faraday.faraday_plugins.plugins.manager - DEBUG [manager.py:123 - _load_plugins()] Load Plugin [acunetix]
36102 2019-11-15 20:37:03,495 - faraday.faraday_plugins.plugins.manager - DEBUG [manager.py:123 - _load_plugins()] Load Plugin [amap]
44110 ...
45111 ```
46112
113
114 > More documentation here https://github.com/infobyte/faraday/wiki/Basic-plugin-development
0 __version__ = '1.0.2'
1
2
0 __version__ = '1.2'
0 import logging
1 import os
2 import sys
3 import click
4
5 from faraday_plugins.plugins.manager import PluginsManager, ReportAnalyzer
6
7 root_logger = logging.getLogger("faraday")
8 if not root_logger.handlers:
9 PLUGIN_DEBUG = os.environ.get("PLUGIN_DEBUG", "0")
10 if PLUGIN_DEBUG == "1":
11 out_hdlr = logging.StreamHandler(sys.stdout)
12 out_hdlr.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s [%(filename)s:%(lineno)s - %(funcName)s()] %(message)s'))
13 out_hdlr.setLevel(logging.DEBUG)
14 root_logger.addHandler(out_hdlr)
15 root_logger.setLevel(logging.DEBUG)
16
17
18 @click.group()
19 def cli():
20 pass
21
22
23 @cli.command()
24 def list():
25 plugins_manager = PluginsManager()
26 click.echo("Available Plugins:")
27 loaded_plugins = 0
28 for plugin_id, plugin in plugins_manager.get_plugins():
29 click.echo(f"{plugin.id} - {plugin.name}")
30 loaded_plugins += 1
31 click.echo(f"Loaded Plugins: {loaded_plugins}")
32
33
34 @cli.command()
35 @click.argument('plugin_id')
36 @click.argument('report_file')
37 def process(plugin_id, report_file):
38 if not os.path.isfile(report_file):
39 click.echo(f"File {report_file} Don't Exists")
40 else:
41 plugins_manager = PluginsManager()
42 plugin = plugins_manager.get_plugin(plugin_id)
43 if plugin:
44 plugin.processReport(report_file)
45 click.echo(plugin.get_json())
46 else:
47 click.echo(f"Unknown Plugin: {plugin_id}")
48
49
50 @cli.command()
51 @click.argument('report_file')
52 def detect(report_file):
53 if not os.path.isfile(report_file):
54 click.echo(f"File {report_file} Don't Exists")
55 else:
56 plugins_manager = PluginsManager()
57 analyzer = ReportAnalyzer(plugins_manager)
58 plugin = analyzer.get_plugin(report_file)
59 if plugin:
60 click.echo(plugin)
61 else:
62 click.echo(f"Failed to detect")
63
0 from faraday_plugins.commands import cli
641
652 if __name__ == "__main__":
663 cli()
0 import io
1 import logging
2 import os
3 import sys
4 import json
5 import click
6 import subprocess
7 import shlex
8 import getpass
9
10 from faraday_plugins.plugins.manager import PluginsManager, ReportAnalyzer, CommandAnalyzer
11 from faraday_plugins.plugins.plugin import PluginByExtension
12
13 root_logger = logging.getLogger("faraday")
14 if not root_logger.handlers:
15 PLUGIN_DEBUG = os.environ.get("PLUGIN_DEBUG", "0")
16 if PLUGIN_DEBUG == "1":
17 out_hdlr = logging.StreamHandler(sys.stdout)
18 out_hdlr.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s [%(filename)s:%(lineno)s - %(funcName)s()] %(message)s'))
19 out_hdlr.setLevel(logging.DEBUG)
20 root_logger.addHandler(out_hdlr)
21 root_logger.setLevel(logging.DEBUG)
22
23
24 @click.group()
25 def cli():
26 pass
27
28
29 @cli.command()
30 @click.option('-cpf', '--custom-plugins-folder', type=str)
31 def list_plugins(custom_plugins_folder):
32 plugins_manager = PluginsManager(custom_plugins_folder)
33 click.echo(click.style("Available Plugins:", fg="cyan"))
34 loaded_plugins = 0
35 for plugin_id, plugin in plugins_manager.get_plugins():
36 console_enabled = plugin._command_regex is not None
37 console_enabled_color = "green" if console_enabled else "red"
38 console_enabled_text = click.style(f"{'Yes' if console_enabled else 'No'}", fg=console_enabled_color)
39 report_enabled = isinstance(plugin, PluginByExtension)
40 report_enabled_color = "green" if report_enabled else "red"
41 report_enabled_text = click.style(f"{'Yes' if report_enabled else 'No'}", fg=report_enabled_color)
42 click.echo(f"{plugin.id:15} - [Command: {console_enabled_text:>12} - Report: {report_enabled_text:>12}] - {plugin.name} ")
43
44 loaded_plugins += 1
45 click.echo(click.style(f"Loaded Plugins: {loaded_plugins}", fg="cyan"))
46
47
48 @cli.command()
49 @click.argument('report_file')
50 @click.option('--plugin_id', type=str)
51 @click.option('-cpf', '--custom-plugins-folder', type=str)
52 @click.option('--summary', is_flag=True)
53 def process_report(report_file, plugin_id, custom_plugins_folder, summary):
54 if not os.path.isfile(report_file):
55 click.echo(click.style(f"File {report_file} Don't Exists", fg="red"))
56 else:
57 plugins_manager = PluginsManager(custom_plugins_folder)
58 analyzer = ReportAnalyzer(plugins_manager)
59 if plugin_id:
60 plugin = plugins_manager.get_plugin(plugin_id)
61 if not plugin:
62 click.echo(click.style(f"Invalid Plugin: {plugin_id}", fg="red"))
63 return
64 else:
65 plugin = analyzer.get_plugin(report_file)
66 if not plugin:
67 click.echo(click.style(f"Failed to detect report: {report_file}", fg="red"))
68 return
69 plugin.processReport(report_file, getpass.getuser())
70 if summary:
71 click.echo(click.style("\nPlugin Summary: ", fg="cyan"))
72 click.echo(json.dumps(plugin.get_summary(), indent=4))
73 else:
74 click.echo(click.style("\nFaraday API json: ", fg="cyan"))
75 click.echo(json.dumps(plugin.get_data(), indent=4))
76
77
78 @cli.command()
79 @click.argument('command')
80 @click.option('--plugin_id', type=str)
81 @click.option('-cpf', '--custom-plugins-folder', type=str)
82 @click.option('-dr', '--dont-run', is_flag=True)
83 @click.option('--summary', is_flag=True)
84 def process_command(command, plugin_id, custom_plugins_folder, dont_run, summary):
85 plugins_manager = PluginsManager(custom_plugins_folder)
86 analyzer = CommandAnalyzer(plugins_manager)
87 if plugin_id:
88 plugin = plugins_manager.get_plugin(plugin_id)
89 if not plugin:
90 click.echo(click.style(f"Invalid Plugin: {plugin_id}", fg="red"))
91 return
92 else:
93 plugin = analyzer.get_plugin(command)
94 if not plugin:
95 click.echo(click.style(f"Failed to detect command: {command}", fg="red"))
96 return
97 current_path = os.path.abspath(os.getcwd())
98 modified_command = plugin.processCommandString(getpass.getuser(), current_path, command)
99 if modified_command:
100 command = modified_command
101 if not dont_run:
102 color_message = click.style("Running command: ", fg="green")
103 click.echo(f"{color_message} {command}\n")
104 p = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
105 output = io.StringIO()
106 while True:
107 retcode = p.poll()
108 line = p.stdout.readline().decode('utf-8')
109 sys.stdout.write(line)
110 output.write(line)
111 if retcode is not None:
112 extra_lines = map(lambda x: x.decode('utf-8'), p.stdout.readlines())
113 sys.stdout.writelines(line)
114 output.writelines(extra_lines)
115 break
116 output_value = output.getvalue()
117 if retcode == 0:
118 plugin.processOutput(output_value)
119 if summary:
120 click.echo(click.style("\nPlugin Summary: ", fg="cyan"))
121 click.echo(json.dumps(plugin.get_summary(), indent=4))
122 else:
123 click.echo(click.style("\nFaraday API json: ", fg="cyan"))
124 click.echo(json.dumps(plugin.get_data(), indent=4))
125 else:
126 click.echo(click.style("Command execution error!!", fg="red"))
127 else:
128 color_message = click.style("Command: ", fg="green")
129 click.echo(f"{color_message} {command}")
130
131
132
133 @cli.command()
134 @click.argument('report_file')
135 @click.option('-cpf', '--custom-plugins-folder', type=str)
136 def detect_report(report_file, custom_plugins_folder):
137 if not os.path.isfile(report_file):
138 click.echo(click.style(f"File {report_file} Don't Exists", fg="red"))
139 else:
140 plugins_manager = PluginsManager(custom_plugins_folder)
141 analyzer = ReportAnalyzer(plugins_manager)
142 plugin = analyzer.get_plugin(report_file)
143 if plugin:
144 click.echo(click.style(f"Faraday Plugin: {plugin.id}", fg="cyan"))
145 else:
146 click.echo(click.style(f"Failed to detect report: {report_file}", fg="red"))
147
148
149 @cli.command()
150 @click.argument('command')
151 @click.option('-cpf', '--custom-plugins-folder', type=str)
152 def detect_command(command, custom_plugins_folder):
153 plugins_manager = PluginsManager(custom_plugins_folder)
154 analyzer = CommandAnalyzer(plugins_manager)
155 plugin = analyzer.get_plugin(command)
156 if plugin:
157 click.echo(click.style(f"Faraday Plugin: {plugin.id}", fg="cyan"))
158 else:
159 click.echo(click.style(f"Failed to detect command: {command}", fg="red"))
44 import sys
55 import json
66 import pkgutil
7 import zipfile
78 from importlib import import_module
89 from importlib.machinery import SourceFileLoader
10 import csv
11 from io import StringIO
912
1013 from . import repo
1114
6568 file_extension = file_extension.lower()
6669 main_tag = None
6770 file_json_keys = {}
71 file_csv_headers = set()
72 file_json_keys = set()
73 files_in_zip = set()
6874 logger.debug("Analyze report File")
6975 # Try to parse as xml
7076 try:
8692 logger.debug("Found JSON content on file: %s - Keys: %s", report_path, file_json_keys)
8793 except Exception as e:
8894 logger.debug("Non JSON content [%s] - %s", report_path, e)
95 try:
96 report_file.seek(0)
97 reader_file_string = StringIO(report_file.read().decode('utf-8'))
98 reader = csv.DictReader(reader_file_string)
99 file_csv_headers = set(reader.fieldnames)
100 logger.debug("Found JSON content on file: %s - Keys: %s", report_path, file_json_keys)
101 except Exception as e:
102 logger.debug("Non JSON content [%s] - %s", report_path, e)
103 try:
104 file_zip = zipfile.ZipFile(report_path, "r")
105 files_in_zip = set(file_zip.namelist())
106 logger.debug("List of files found in ZIP %s", file_zip)
107 except Exception as e:
108 logger.debug("Non ZIP content [%s] - %s", report_path, e)
89109 finally:
90110 report_file.close()
91111 for _plugin_id, _plugin in self.plugin_manager.get_plugins():
92112 logger.debug("Try plugin: %s", _plugin_id)
93113 try:
94114 if _plugin.report_belongs_to(main_tag=main_tag, report_path=report_path,
95 extension=file_extension, file_json_keys=file_json_keys):
115 extension=file_extension, file_json_keys=file_json_keys,
116 file_csv_headers=file_csv_headers, files_in_zip=files_in_zip):
96117 plugin = _plugin
97118 logger.debug("Plugin by File Found: %s", plugin.id)
98119 break
101122 return plugin
102123
103124
125 class CommandAnalyzer:
126
127 def __init__(self, plugin_manager):
128 self.plugin_manager = plugin_manager
129
130 def get_plugin(self, command_string):
131 plugin = None
132 logger.debug("Look plugin for command: %s", command_string)
133 for _plugin_id, _plugin in self.plugin_manager.get_plugins():
134 logger.debug("Try plugin: %s", _plugin_id)
135 try:
136 if _plugin.canParseCommandString(command_string):
137 plugin = _plugin
138 except Exception as e:
139 logger.error("Error in plugin analysis: (%s) %s", _plugin_id, e)
140 return plugin
141
142
104143 class PluginsManager:
105144
106 def __init__(self):
145 def __init__(self, custom_plugins_folder=None):
107146 self.plugins = {}
108147 self.plugin_modules = {}
109 self._load_plugins()
110
111 def _load_plugins(self):
148 self._load_plugins(custom_plugins_folder)
149
150 def _load_plugins(self, custom_plugins_folder):
112151 logger.info("Loading Native Plugins...")
113152 if not self.plugins:
114153 for _, name, _ in filter(lambda x: x[2], pkgutil.iter_modules(repo.__path__)):
117156 if hasattr(plugin_module, "createPlugin"):
118157 plugin_instance = plugin_module.createPlugin()
119158 plugin_id = plugin_instance.id.lower()
159 if not plugin_instance.auto_load:
160 logger.debug("Skip load plugin [%s]", plugin_id)
161 continue
120162 if plugin_id not in self.plugin_modules:
121163 self.plugin_modules[plugin_id] = plugin_module
122164 logger.debug("Load Plugin [%s]", name)
126168 logger.error("Invalid Plugin [%s]", name)
127169 except Exception as e:
128170 logger.error("Cant load plugin module: %s [%s]", name, e)
129 try:
130 import faraday.server.config
131 if os.path.isdir(faraday.server.config.faraday_server.custom_plugins_folder):
171 if custom_plugins_folder:
172 if os.path.isdir(custom_plugins_folder):
132173 logger.info("Loading Custom Plugins...")
133174 dir_name_regexp = re.compile(r"^[\d\w\-\_]+$")
134 for name in os.listdir(faraday.server.config.faraday_server.custom_plugins_folder):
175 for name in os.listdir(custom_plugins_folder):
135176 if dir_name_regexp.match(name) and name != "__pycache__":
177 module_path = os.path.join(custom_plugins_folder, name)
178 module_filename = os.path.join(module_path, "plugin.py")
136179 try:
137 module_path = os.path.join(faraday.server.config.faraday_server.custom_plugins_folder,
138 name)
139180 sys.path.append(module_path)
140 module_filename = os.path.join(module_path, "plugin.py")
141181 file_ext = os.path.splitext(module_filename)[1]
142182 if file_ext.lower() == '.py':
143183 if name not in self.plugin_modules:
145185 plugin_module = loader.load_module()
146186 plugin_instance = plugin_module.createPlugin()
147187 plugin_id = plugin_instance.id.lower()
188 if not plugin_instance.auto_load:
189 logger.info("Skip load plugin [%s]", plugin_id)
190 continue
148191 if plugin_id not in self.plugin_modules:
149192 self.plugin_modules[plugin_id] = plugin_module
150193 else:
151194 logger.debug("Plugin with same name already loaded [%s]", name)
152 logger.debug('Loading plugin {0}'.format(name))
195 logger.debug("Load Plugin [%s]", name)
153196 except Exception as e:
154197 logger.debug("An error ocurred while loading plugin %s.\n%s", module_filename,
155198 traceback.format_exc())
156199 logger.warning(e)
157 except Exception as e:
158 logger.info("Can't import faraday server, no custom plugins will be loaded")
200 else:
201 logger.warning("Invalid custom plugins folder [%s]", custom_plugins_folder)
159202 logger.info("%s plugins loaded", len(self.plugin_modules))
160203
161204 def get_plugin(self, plugin_id):
44
55 """
66 import os
7 import shutil
8 import tempfile
9
10 from collections import defaultdict
11
12 import pytz
713 import re
814 import uuid
915 import logging
1016 import simplejson as json
17 import zipfile
1118 from datetime import datetime
19 import hashlib
1220
1321
1422 logger = logging.getLogger("faraday").getChild(__name__)
1523
24 VALID_SERVICE_STATUS = ("open", "closed", "filtered")
25 VULN_SKIP_FIELDS_TO_HASH = ['run_date']
1626
1727 class PluginBase:
1828 # TODO: Add class generic identifier
2333 # an existant plugin with the same id.
2434 # TODO: Make script that list current ids.
2535 self.id = None
36 self.auto_load = True
2637 self._rid = id(self)
2738 self.version = None
2839 self.name = None
2940 self.description = ""
3041 self._command_regex = None
3142 self._output_file_path = None
43 self._use_temp_file = False
44 self._delete_temp_file = False
45 self._temp_file_extension = "tmp"
46 self._current_path = None
3247 self.framework_version = None
3348 self._completition = {}
3449 self._new_elems = []
3550 self._settings = {}
3651 self.command_id = None
37 self.cache = {}
52 self._cache = {}
3853 self._hosts_cache = {}
54 self._service_cache = {}
55 self._vulns_cache = {}
3956 self.start_date = datetime.now()
4057 self.logger = logger.getChild(self.__class__.__name__)
4158 self.open_options = {"mode": "r", "encoding": "utf-8"}
5067
5168 def __str__(self):
5269 return f"Plugin: {self.id}"
70
71 def _get_temp_file(self, extension="tmp"):
72 temp_dir = tempfile.gettempdir()
73 temp_filename = f"{self.id}_{next(tempfile._get_candidate_names())}.{extension}"
74 temp_file_path = os.path.join(temp_dir, temp_filename)
75 return temp_file_path
76
77 @staticmethod
78 def get_utctimestamp(date):
79 if date is not None:
80 try:
81 utc_date = date.astimezone(pytz.UTC)
82 return utc_date.timestamp()
83 except Exception as e:
84 logger.error("Error generating timestamp: %s", e)
85 return None
86 else:
87 return date
5388
5489 @staticmethod
5590 def normalize_severity(severity):
75110 severity = numeric_severities.get(severity, 'unclassified')
76111 return severity
77112
113 # Caches
78114 def get_from_cache(self, cache_id):
79 return self.cache.get(cache_id, None)
80
81 def save_host_cache(self, obj):
82 cache_id = f"ip:{obj['ip']}_os:{obj['os']}"
115 return self._cache.get(cache_id, None)
116
117 def save_host_cache(self, host):
118 cache_id = self.get_host_cache_id(host)
83119 if cache_id not in self._hosts_cache:
84 obj_uuid = self.save_cache(obj)
85 self.vulns_data["hosts"].append(obj)
120 obj_uuid = self.save_cache(host)
121 self.vulns_data["hosts"].append(host)
86122 self._hosts_cache[cache_id] = obj_uuid
87123 else:
88124 obj_uuid = self._hosts_cache[cache_id]
89125 return obj_uuid
90126
127 def save_service_cache(self, host_id, service):
128 cache_id = self.get_host_service_cache_id(host_id, service)
129 if cache_id not in self._service_cache:
130 obj_uuid = self.save_cache(service)
131 host = self.get_from_cache(host_id)
132 host["services"].append(service)
133 self._service_cache[cache_id] = obj_uuid
134 else:
135 obj_uuid = self._service_cache[cache_id]
136 return obj_uuid
137
138 def save_service_vuln_cache(self, host_id, service_id, vuln):
139 cache_id = self.get_service_vuln_cache_id(host_id, service_id, vuln)
140 if cache_id not in self._vulns_cache:
141 obj_uuid = self.save_cache(vuln)
142 service = self.get_from_cache(service_id)
143 service["vulnerabilities"].append(vuln)
144 self._vulns_cache[cache_id] = obj_uuid
145 else:
146 obj_uuid = self._vulns_cache[cache_id]
147 return obj_uuid
148
149 def save_host_vuln_cache(self, host_id, vuln):
150 cache_id = self.get_host_vuln_cache_id(host_id, vuln)
151 if cache_id not in self._vulns_cache:
152 obj_uuid = self.save_cache(vuln)
153 host = self.get_from_cache(host_id)
154 host["vulnerabilities"].append(vuln)
155 self._vulns_cache[cache_id] = obj_uuid
156 else:
157 obj_uuid = self._vulns_cache[cache_id]
158 return obj_uuid
159
160 @staticmethod
161 def _get_dict_hash(d, keys):
162 return hash(frozenset(map(lambda x: (x, d.get(x, None)), keys)))
163
164
165 @classmethod
166 def get_host_cache_id(cls, host):
167 cache_id = cls._get_dict_hash(host, ['ip'])
168 return cache_id
169
170 @classmethod
171 def get_host_service_cache_id(cls, host_id, service):
172 service_copy = service.copy()
173 service_copy.update({"host_cache_id": host_id})
174 cache_id = cls._get_dict_hash(service_copy, ['host_cache_id', 'protocol', 'port'])
175 return cache_id
176
177 @classmethod
178 def get_service_vuln_cache_id(cls, host_id, service_id, vuln):
179 vuln_copy = vuln.copy()
180 vuln_copy.update({"host_cache_id": host_id, "service_cache_id": service_id})
181 cache_id = cls._get_dict_hash(vuln_copy, ['host_cache_id', 'service_cache_id', 'name', 'desc', 'website', 'path', 'pname', 'method'])
182 return cache_id
183
184 @classmethod
185 def get_host_vuln_cache_id(cls, host_id, vuln):
186 vuln_copy = vuln.copy()
187 vuln_copy.update({"host_cache_id": host_id})
188 cache_id = cls._get_dict_hash(vuln_copy, ['host_cache_id', 'name', 'desc', 'website', 'path', 'pname', 'method'])
189 return cache_id
190
91191 def save_cache(self, obj):
92192 obj_uuid = uuid.uuid1()
93 self.cache[obj_uuid] = obj
193 self._cache[obj_uuid] = obj
94194 return obj_uuid
95195
96196 def report_belongs_to(self, **kwargs):
142242 """
143243 return (self._command_regex is not None and
144244 self._command_regex.match(current_input.strip()) is not None)
245
246 def processCommandString(self, username, current_path, command_string):
247 """
248 With this method a plugin can add additional arguments to the
249 command that it's going to be executed.
250 """
251 self._current_path = current_path
252 if command_string.startswith("sudo"):
253 params = " ".join(command_string.split()[2:])
254 else:
255 params = " ".join(command_string.split()[1:])
256 self.vulns_data["command"]["params"] = params
257 self.vulns_data["command"]["user"] = username
258 self.vulns_data["command"]["import_source"] = "shell"
259 if self._use_temp_file:
260 self._delete_temp_file = True
261 self._output_file_path = self._get_temp_file(extension=self._temp_file_extension)
262 return None
145263
146264 def getCompletitionSuggestionsList(self, current_input):
147265 """
156274 options[k] = v
157275 return options
158276
159 def processOutput(self, term_output):
160 output = term_output
161 if self.has_custom_output() and os.path.isfile(self.get_custom_file_path()):
277 def processOutput(self, command_output):
278 if self.has_custom_output():
162279 self._parse_filename(self.get_custom_file_path())
163280 else:
164 self.parseOutputString(output)
281 self.parseOutputString(command_output)
165282
166283 def _parse_filename(self, filename):
167284 with open(filename, **self.open_options) as output:
168285 self.parseOutputString(output.read())
286 if self._delete_temp_file:
287 try:
288 if os.path.isfile(filename):
289 os.remove(filename)
290 elif os.path.isdir(filename):
291 shutil.rmtree(filename)
292 except Exception as e:
293 self.logger.error("Error on delete file: (%s) [%s]", filename, e)
169294
170295 def processReport(self, filepath, user="faraday"):
171296 if os.path.isfile(filepath):
172 self._parse_filename(filepath)
173297 self.vulns_data["command"]["params"] = filepath
174298 self.vulns_data["command"]["user"] = user
299 self.vulns_data["command"]["import_source"] = "report"
300 self._parse_filename(filepath)
175301 else:
176302 raise FileNotFoundError(filepath)
177303
185311 """
186312 raise NotImplementedError('This method must be implemented.')
187313
188 def createAndAddHost(self, name, os="unknown", hostnames=None, mac=None, scan_template="", site_name="",
189 site_importance="", risk_score="", fingerprints="", fingerprints_software=""):
314 def createAndAddHost(self, name, os="unknown", hostnames=None, mac=None, description="", tags=None):
190315
191316 if not hostnames:
192317 hostnames = []
318 # Some plugins sends a list with None, we filter empty and None values.
319 hostnames = [hostname for hostname in hostnames if hostname]
193320 if os is None:
194321 os = "unknown"
195 host = {"ip": name, "os": os, "hostnames": hostnames, "description": "", "mac": mac,
196 "credentials": [], "services": [], "vulnerabilities": [], "scan_template": scan_template,
197 "site_name": site_name, "site_importance": site_importance, "risk_score": risk_score,
198 "fingerprints": fingerprints, "fingerprints_software": fingerprints_software
199 }
322 if tags is None:
323 tags = []
324 if isinstance(tags, str):
325 tags = [tags]
326 host = {"ip": name, "os": os, "hostnames": hostnames, "description": description, "mac": mac,
327 "credentials": [], "services": [], "vulnerabilities": [], "tags": tags}
200328 host_id = self.save_host_cache(host)
201329 return host_id
202330
235363 # details="Interface object removed. Use host or service instead. Service will be attached
236364 # to Host!")
237365 def createAndAddServiceToInterface(self, host_id, interface_id, name,
238 protocol="tcp?", ports=None,
366 protocol="tcp", ports=None,
239367 status="open", version="unknown",
240 description=""):
241 return self.createAndAddServiceToHost(host_id, name, protocol, ports, status, version, description)
368 description="", tags=None):
369 return self.createAndAddServiceToHost(host_id, name, protocol, ports, status, version, description, tags)
242370
243371 def createAndAddServiceToHost(self, host_id, name,
244 protocol="tcp?", ports=None,
372 protocol="tcp", ports=None,
245373 status="open", version="unknown",
246 description=""):
374 description="", tags=None):
247375 if ports:
248376 if isinstance(ports, list):
249377 ports = int(ports[0])
250378 elif isinstance(ports, str):
251379 ports = int(ports)
252380
253 if status not in ("open", "closed", "filtered"):
254 self.logger.warning('Unknown service status %s. Using "open" instead', status)
381 if status not in VALID_SERVICE_STATUS:
255382 status = 'open'
383 if tags is None:
384 tags = []
385 if isinstance(tags, str):
386 tags = [tags]
256387 service = {"name": name, "protocol": protocol, "port": ports, "status": status,
257 "version": version, "description": description, "credentials": [], "vulnerabilities": []}
258 host = self.get_from_cache(host_id)
259 host["services"].append(service)
260 service_id = self.save_cache(service)
388 "version": version, "description": description, "credentials": [], "vulnerabilities": [],
389 "tags": tags}
390
391 service_id = self.save_service_cache(host_id, service)
392
261393 return service_id
262394
263395 def createAndAddVulnToHost(self, host_id, name, desc="", ref=None,
264 severity="", resolution="", vulnerable_since="", scan_id="", pci="", data="",
265 external_id=None):
396 severity="", resolution="", data="", external_id=None, run_date=None,
397 impact=None, custom_fields=None, status="", policyviolations=None,
398 easeofresolution=None, confirmed=False, tags=None):
266399 if ref is None:
267400 ref = []
268 vulnerability = {"name": name, " desc": desc, "severity": self.normalize_severity(severity), "refs": ref,
269 "external_id": external_id, "type": "Vulnerability", "resolution": resolution,
270 "vulnerable_since": vulnerable_since, "scan_id": scan_id, "pci": pci, "data": data}
271 host = self.get_from_cache(host_id)
272
273 host["vulnerabilities"].append(vulnerability)
274 vulnerability_id = len(host["vulnerabilities"]) - 1
401 if status == "":
402 status = "opened"
403 if impact is None:
404 impact = {}
405 if policyviolations is None:
406 policyviolations = []
407 if custom_fields is None:
408 custom_fields = {}
409 if tags is None:
410 tags = []
411 if isinstance(tags, str):
412 tags = [tags]
413 vulnerability = {"name": name, "desc": desc, "severity": self.normalize_severity(severity), "refs": ref,
414 "external_id": external_id, "type": "Vulnerability", "resolution": resolution, "data": data,
415 "custom_fields": custom_fields, "status": status, "impact": impact, "policyviolations": policyviolations,
416 "confirmed": confirmed, "easeofresolution": easeofresolution, "tags": tags
417 }
418 if run_date:
419 vulnerability["run_date"] = self.get_utctimestamp(run_date)
420 vulnerability_id = self.save_host_vuln_cache(host_id, vulnerability)
275421 return vulnerability_id
276422
277423 # @deprecation.deprecated(deprecated_in="3.0", removed_in="3.5",
280426 # to Host")
281427 def createAndAddVulnToInterface(self, host_id, interface_id, name,
282428 desc="", ref=None, severity="",
283 resolution="", data=""):
429 resolution="", data="", tags=None):
284430 return self.createAndAddVulnToHost(host_id, name, desc=desc, ref=ref, severity=severity, resolution=resolution,
285 data=data)
431 data=data, tags=tags)
286432
287433 def createAndAddVulnToService(self, host_id, service_id, name, desc="",
288 ref=None, severity="", resolution="", risk="", data="", external_id=None):
434 ref=None, severity="", resolution="", data="", external_id=None, run_date=None,
435 custom_fields=None, policyviolations=None, impact=None, status="",
436 confirmed=False, easeofresolution=None, tags=None):
289437 if ref is None:
290438 ref = []
439 if status == "":
440 status = "opened"
441 if impact is None:
442 impact = {}
443 if policyviolations is None:
444 policyviolations = []
445 if custom_fields is None:
446 custom_fields = {}
447 if tags is None:
448 tags = []
449 if isinstance(tags, str):
450 tags = [tags]
291451 vulnerability = {"name": name, "desc": desc, "severity": self.normalize_severity(severity), "refs": ref,
292 "external_id": external_id, "type": "Vulnerability", "resolution": resolution, "riskB": risk,
293 "data": data}
294 service = self.get_from_cache(service_id)
295 service["vulnerabilities"].append(vulnerability)
296 vulnerability_id = self.save_cache(vulnerability)
452 "external_id": external_id, "type": "Vulnerability", "resolution": resolution, "data": data,
453 "custom_fields": custom_fields, "status": status, "impact": impact, "policyviolations": policyviolations,
454 "easeofresolution": easeofresolution, "confirmed": confirmed, "tags": tags
455 }
456 if run_date:
457 vulnerability["run_date"] = self.get_utctimestamp(run_date)
458 vulnerability_id = self.save_service_vuln_cache(host_id, service_id, vulnerability)
297459 return vulnerability_id
298460
299461 def createAndAddVulnWebToService(self, host_id, service_id, name, desc="",
300462 ref=None, severity="", resolution="",
301463 website="", path="", request="",
302464 response="", method="", pname="",
303 params="", query="", category="", data="", external_id=None):
465 params="", query="", category="", data="", external_id=None,
466 confirmed=False, status="", easeofresolution=None, impact=None,
467 policyviolations=None, status_code=None, custom_fields=None, run_date=None, tags=None):
304468 if params is None:
305469 params = ""
306470 if response is None:
323487 response = ""
324488 if ref is None:
325489 ref = []
490 if status == "":
491 status = "opened"
492 if impact is None:
493 impact = {}
494 if policyviolations is None:
495 policyviolations = []
496 if custom_fields is None:
497 custom_fields = {}
498 if tags is None:
499 tags = []
500 if isinstance(tags, str):
501 tags = [tags]
326502 vulnerability = {"name": name, "desc": desc, "severity": self.normalize_severity(severity), "refs": ref,
327503 "external_id": external_id, "type": "VulnerabilityWeb", "resolution": resolution,
328504 "data": data, "website": website, "path": path, "request": request, "response": response,
329 "method": method, "pname": pname, "params": params, "query": query, "category": category}
330 service = self.get_from_cache(service_id)
331 service["vulnerabilities"].append(vulnerability)
332 vulnerability_id = self.save_cache(vulnerability)
505 "method": method, "pname": pname, "params": params, "query": query, "category": category,
506 "confirmed": confirmed, "status": status, "easeofresolution": easeofresolution,
507 "impact": impact, "policyviolations": policyviolations,
508 "status_code": status_code, "custom_fields": custom_fields, "tags": tags}
509 if run_date:
510 vulnerability["run_date"] = self.get_utctimestamp(run_date)
511 vulnerability_id = self.save_service_vuln_cache(host_id, service_id, vulnerability)
333512 return vulnerability_id
334
335513
336514 def createAndAddNoteToHost(self, host_id, name, text):
337515 return None
369547 def get_json(self):
370548 self.logger.debug("Generate Json")
371549 return json.dumps(self.get_data())
550
551 def get_summary(self):
552 plugin_json = self.get_data()
553 summary = {'hosts': len(plugin_json['hosts']), 'services': 0,
554 'hosts_vulns': sum(list(map(lambda x: len(x['vulnerabilities']), plugin_json['hosts']))),
555 'services_vulns': 0, 'severity_vulns': defaultdict(int),
556 'vuln_hashes': []
557 }
558 hosts_with_services = filter(lambda x: len(x['services']) > 0, plugin_json['hosts'])
559 host_services = list(map(lambda x: x['services'], hosts_with_services))
560 summary['services'] = sum(map(lambda x: len(x), host_services))
561 services_vulns = 0
562 for host in plugin_json['hosts']:
563 for vuln in host['vulnerabilities']:
564 summary['severity_vulns'][vuln['severity']] += 1
565 for services in host_services:
566 for service in services:
567 services_vulns += len(service['vulnerabilities'])
568 for vuln in service['vulnerabilities']:
569 summary['severity_vulns'][vuln['severity']] += 1
570 summary['services_vulns'] = services_vulns
571 for obj_uuid in self._vulns_cache.values():
572 vuln = self.get_from_cache(obj_uuid)
573 vuln_copy = vuln.copy()
574 for field in VULN_SKIP_FIELDS_TO_HASH:
575 vuln_copy.pop(field, None)
576 dict_hash = hashlib.sha1(json.dumps(vuln_copy).encode()).hexdigest()
577 summary['vuln_hashes'].append(dict_hash)
578 return summary
372579
373580 # TODO Borrar
374581 class PluginTerminalOutput(PluginBase):
438645 match = False
439646 if super().report_belongs_to(**kwargs):
440647 if file_json_keys is None:
441 file_json_keys = {}
648 file_json_keys = set()
442649 match = self.json_keys.issubset(file_json_keys)
443650 self.logger.debug("Json Keys Match: [%s =/in %s] -> %s", file_json_keys, self.json_keys, match)
444651 return match
445 # I'm Py3
652
653
654 class PluginCSVFormat(PluginByExtension):
655
656 def __init__(self):
657 super().__init__()
658 self.extension = ".csv"
659 self.csv_headers = set()
660
661 def report_belongs_to(self, file_csv_headers=None, **kwargs):
662 match = False
663 if file_csv_headers is None:
664 file_csv_headers = set()
665 if super().report_belongs_to(**kwargs):
666 if isinstance(self.csv_headers, list):
667 match = bool(list(filter(lambda x: x.issubset(file_csv_headers), self.csv_headers)))
668 else:
669 match = self.csv_headers.issubset(file_csv_headers)
670 self.logger.debug("CSV Headers Match: [%s =/in %s] -> %s", file_csv_headers, self.csv_headers, match)
671 return match
672
673
674 class PluginZipFormat(PluginByExtension):
675
676 def __init__(self):
677 super().__init__()
678 self.extension = ".zip"
679 self.files_list = set()
680
681 def _parse_filename(self, filename):
682 file = zipfile.ZipFile(filename, "r")
683 self.parseOutputString(file)
684
685 def report_belongs_to(self, files_in_zip=None, **kwargs):
686 match = False
687 if super().report_belongs_to(**kwargs):
688 if files_in_zip is None:
689 files_in_zip = set()
690 match = bool(self.files_list & files_in_zip)
691 self.logger.debug("Files List Match: [%s =/in %s] -> %s", files_in_zip, self.files_list, match)
692 return match
693
694
55 """
66 import os
77 import logging
8 import faraday_plugins
8 import socket
9 from collections import defaultdict
10
911 from urllib.parse import urlsplit
1012
1113
9597 yield item
9698
9799
98 # I'm Py3
100 def resolve_hostname(hostname):
101 try:
102 ip_address = socket.gethostbyname(hostname)
103 except Exception as e:
104 return hostname
105 else:
106 return ip_address
44
55 """
66 from urllib.parse import urlsplit
7 import socket
87 import re
98 import os
9
10 from lxml import etree
1011
1112 try:
1213 import xml.etree.cElementTree as ET
1718 ETREE_VERSION = ET.VERSION
1819
1920 from faraday_plugins.plugins.plugin import PluginXMLFormat
21 from faraday_plugins.plugins.plugins_utils import resolve_hostname
2022
2123 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
2224
23 current_path = os.path.abspath(os.getcwd())
2425
2526 __author__ = "Francisco Amato"
2627 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
4647
4748 def __init__(self, xml_output):
4849 tree = self.parse_xml(xml_output)
49 if tree:
50 if len(tree):
5051 self.sites = list(self.get_items(tree))
5152 else:
5253 self.sites = []
6162 @return xml_tree An xml tree instance. None if error.
6263 """
6364 try:
64 tree = ET.fromstring(xml_output)
65 parser = etree.XMLParser(recover=True)
66 tree = etree.fromstring(xml_output, parser=parser)
6567 except SyntaxError as err:
6668 print("SyntaxError: %s. %s", err, xml_output)
6769 return None
117119 url_data = self.get_url(self.node)
118120
119121 self.protocol = url_data.scheme
120 self.host = url_data.hostname
121
122 if url_data.hostname:
123 self.host = url_data.hostname
124 else:
125 self.host = None
122126 # Use the port in the URL if it is defined, or 80 or 443 by default
123127 self.port = url_data.port or (443 if url_data.scheme == "https" else 80)
124128
125 self.ip = self.resolve(self.host)
129 self.ip = resolve_hostname(self.host)
126130 self.os = self.get_text_from_subnode('Os')
127131 self.banner = self.get_text_from_subnode('Banner')
128132 self.items = []
140144 return sub_node.text
141145
142146 return None
143
144 def resolve(self, host):
145 try:
146 return socket.gethostbyname(host)
147 except:
148 print('[ERROR] Acunetix XML Plugin: Ip of host unknown ' + host)
149 return None
150 return host
151147
152148 def get_url(self, node):
153149 url = self.get_text_from_subnode('StartURL')
176172 self.response = self.get_text_from_subnode('TechnicalDetails/Response')
177173 self.parameter = self.get_text_from_subnode('Parameter')
178174 self.uri = self.get_text_from_subnode('Affects')
179 self.desc = self.get_text_from_subnode('Description')
175
176 if self.get_text_from_subnode('Description'):
177 self.desc = self.get_text_from_subnode('Description')
178 else:
179 self.desc = ""
180180
181181 if self.get_text_from_subnode('Recommendation'):
182182 self.resolution = self.get_text_from_subnode('Recommendation')
185185
186186 if self.get_text_from_subnode('reference'):
187187 self.desc += "\nDetails: " + self.get_text_from_subnode('Details')
188 else:
189 self.desc += ""
190188
191189 # Add path and params to the description to create different IDs if at
192190 # least one of this fields is different
243241 for site in parser.sites:
244242 if site.ip is None:
245243 continue
246 host = []
244
247245 if site.host != site.ip:
248 host = [site.host]
246 host = site.host
249247 h_id = self.createAndAddHost(site.ip, site.os)
250 i_id = self.createAndAddInterface(
251 h_id,
252 site.ip,
253 ipv4_address=site.ip,
254 hostname_resolution=host)
248 if site.host is None:
249 i_id = self.createAndAddInterface(h_id, site.ip, ipv4_address=site.ip)
250 else:
251 i_id = self.createAndAddInterface(h_id, site.ip,
252 ipv4_address=site.ip,
253 hostname_resolution=[host])
255254 s_id = self.createAndAddServiceToInterface(
256255 h_id,
257256 i_id,
261260 version=site.banner,
262261 status='open')
263262 for item in site.items:
264 self.createAndAddVulnWebToService(
265 h_id,
266 s_id,
267 item.name,
268 item.desc,
269 website=site.host,
270 severity=item.severity,
271 resolution=item.resolution,
272 path=item.uri,
273 params=item.parameter,
274 request=item.request,
275 response=item.response,
276 ref=item.ref)
263
264 if item.desc is None:
265 self.createAndAddVulnWebToService(
266 h_id,
267 s_id,
268 item.name,
269 desc="",
270 website=site.host,
271 severity=item.severity,
272 resolution=item.resolution,
273 path=item.uri,
274 params=item.parameter,
275 request=item.request,
276 response=item.response,
277 ref=item.ref)
278 else:
279 self.createAndAddVulnWebToService(
280 h_id,
281 s_id,
282 item.name,
283 item.desc,
284 website=site.host,
285 severity=item.severity,
286 resolution=item.resolution,
287 path=item.uri,
288 params=item.parameter,
289 request=item.request,
290 response=item.response,
291 ref=item.ref)
277292 del parser
278293
279294 def setHost(self):
281296
282297
283298 def createPlugin():
284 return AcunetixPlugin()
299 return AcunetixPlugin()
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44 """
5 import argparse
6 import random
7 import shlex
8 import tempfile
9
510 from faraday_plugins.plugins.plugin import PluginBase
611 import socket
712 import re
813 import os
914
10 current_path = os.path.abspath(os.getcwd())
15 from faraday_plugins.plugins.plugins_utils import resolve_hostname
16
1117
1218
1319 class AmapPlugin(PluginBase):
2127 self.version = "5.4"
2228 self.options = None
2329 self._current_output = None
24 self._command_regex = re.compile(r'^(amap|sudo amap).*?')
30 self._command_regex = re.compile(r'^(amap|sudo amap)\s+.*?')
31 self._use_temp_file = True
2532 self._hosts = []
2633
27 def parseOutputString(self, output, debug=False):
28 # if not os.path.exists(self._file_output_path):
29 # return False
30 #
31 # if not debug:
32 # with open(self._file_output_path) as f:
33 # output = f.read()
34
34 def parseOutputString(self, output):
3535 services = {}
3636 for line in output.split('\n'):
3737 if line.startswith('#'):
7272 self.ip = self.get_ip_6(self.args.m)
7373 args['ipv6_address'] = address
7474 else:
75 self.ip = self.getAddress(self.args.m)
75 self.ip = resolve_hostname(self.args.m)
7676 args['ipv4_address'] = address
7777
7878 if address != self.args.m:
122122
123123 return ip6[0][4][0]
124124
125 def getAddress(self, hostname):
126 """
127 Returns remote IP address from hostname.
128 """
129 try:
130 return socket.gethostbyname(hostname)
131 except socket.error as msg:
132 return hostname
133
134125 def setHost(self):
135126 pass
127
128 def processCommandString(self, username, current_path, command_string):
129 """
130 Adds the -m parameter to get machine readable output.
131 """
132 super().processCommandString(username, current_path, command_string)
133 arg_match = self.file_arg_re.match(command_string)
134 parser = argparse.ArgumentParser()
135 parser.add_argument('-6', action='store_true')
136 parser.add_argument('-o')
137 parser.add_argument('-m')
138 if arg_match is None:
139 final = re.sub(
140 r"(^.*?amap)",
141 r"\1 -o %s -m " % self._output_file_path,
142 command_string)
143 else:
144 final = re.sub(
145 arg_match.group(1),
146 r"-o %s -m " % self._output_file_path,
147 command_string)
148
149 cmd = shlex.split(re.sub(r'\-h|\-\-help', r'', final))
150 if "-6" in cmd:
151 cmd.remove("-6")
152 cmd.insert(1, "-6")
153
154 args = None
155 if len(cmd) > 4:
156 try:
157 args, unknown = parser.parse_known_args(cmd)
158 except SystemExit:
159 pass
160
161 self.args = args
162 return final
136163
137164
138165 def createPlugin():
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
30 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2017 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
74 """
85
9 import socket
106 from faraday_plugins.plugins.plugin import PluginXMLFormat
7 from faraday_plugins.plugins.plugins_utils import resolve_hostname
118 from lxml import objectify
129 from urllib.parse import urlparse
1310
2017 __status__ = "Development"
2118
2219
23 def cleaner_unicode(string):
24 return string
25 # if string is not None:
26 # return string.encode('ascii', errors='backslashreplace')
27 # else:
28 # return string
29
3020
3121 class AppscanParser():
3222
3323 def __init__(self, output, logger):
3424 self.issue_list = []
3525 self.logger = logger
36 self.obj_xml = objectify.fromstring(output.encode('utf-8'))
26 self.obj_xml = objectify.fromstring(output)
3727
3828 def parse_issues(self):
3929 issue_type = self.parse_issue_type()
6656 hosts_list = []
6757 for host in self.obj_xml['scan-configuration']['scanned-hosts']['item']:
6858 hosts_dict = {}
69 hosts_dict['ip'] = socket.gethostbyname(host['host'].text)
59 hosts_dict['ip'] = resolve_hostname(host['host'].text)
7060 hosts_dict['hostname'] = host['host'].text
7161 hosts_dict['os'] = host['operating-system'].text
7262 hosts_dict['port'] = host['port'].text
143133 self.options = None
144134 self.open_options = {"mode": "r", "encoding": "utf-8"}
145135
146 def parseOutputString(self, output, debug=False):
136 def parseOutputString(self, output):
147137 try:
148138 parser = AppscanParser(output, self.logger)
149139 issues = parser.parse_issues()
157147 ports=[host['port']],
158148 protocol="tcp?HTTP")
159149 if host['port']:
160 key_url = f"{host['scheme']}://{host['hostname']}:{host['port']}"
150 if host['port'] not in ('443', '80'):
151 key_url = f"{host['scheme']}://{host['hostname']}:{host['port']}"
152 else:
153 key_url = f"{host['scheme']}://{host['hostname']}"
161154 else:
162155 key_url = f"{host['scheme']}://{host['hostname']}"
163156 hosts_dict[key_url] = {'host_id': host_id, 'service_id': service_id}
164157 for issue in issues:
165 url_parsed = urlparse(str(issue['url']))
166 url_string = '://'.join([url_parsed.scheme, url_parsed.netloc])
158 url_parsed = urlparse(issue['url'])
159 url_string = f'{url_parsed.scheme}://{url_parsed.netloc}'
167160 for key in hosts_dict:
168161 if url_string == key:
169162 h_id = hosts_dict[key]['host_id']
180173 self.createAndAddVulnWebToService(
181174 h_id,
182175 s_id,
183 cleaner_unicode(issue["name"]),
184 desc=cleaner_unicode(issue["issue_description"]) if "issue_description" in issue else "",
176 issue["name"],
177 desc=issue["issue_description"] if "issue_description" in issue else "",
185178 ref=refs,
186179 severity=issue["severity"],
187 resolution=cleaner_unicode(issue["recomendation"]),
180 resolution=issue["recomendation"],
188181 website=url_parsed.netloc,
189182 path=url_parsed.path,
190 request=cleaner_unicode(issue["request"]) if "request" in issue else "",
191 response=cleaner_unicode(issue["response"]) if issue["response"] else "",
183 request=issue["request"] if "request" in issue else "",
184 response=issue["response"] if issue["response"] else "",
192185 method=issue["method"] if issue["method"] else "")
193186 except Exception as e:
194187 self.logger.error("Parsing Output Error: %s", e)
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
30 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
74 """
8 from faraday_plugins.plugins.plugin import PluginXMLFormat
9 import socket
10 import random
115 import re
126 from urllib.parse import urlparse
137 import os
8 from faraday_plugins.plugins.plugin import PluginXMLFormat
9 from faraday_plugins.plugins.plugins_utils import resolve_hostname
1410
1511 try:
1612 import xml.etree.cElementTree as ET
4238 try:
4339 tree = ET.fromstring(xml_output)
4440 except SyntaxError as err:
45 print('SyntaxError In xml: %s. %s' % (err, xml_output))
4641 return None
4742 return tree
4843
8681 description = self.node.find(tag)
8782
8883 if description is not None and description.text is not None:
89 return description.text.encode('ascii', 'ignore')
84 return description.text
9085 else:
9186 return 'None'
9287
10196 result = main_entity.find(child_tag)
10297
10398 if result is not None and result.text is not None:
104 return result.text.encode('ascii', 'ignore')
99 return result.text
105100 else:
106101 return 'None'
107102
144139 try:
145140
146141 raw_data = self.node.find('page').find('request').find('raw')
147 data = raw_data.text.encode('ascii', 'ignore')
142 data = raw_data.text
148143 return data
149144
150145 except:
155150 # Get data about response.
156151 try:
157152
158 raw_data = self.node.find('page').find(
159 'response').find('raw_headers')
160 data = raw_data.text.encode('ascii', 'ignore')
153 raw_data = self.node.find('page').find('response').find('raw_headers')
154 data = raw_data.text
161155 return data
162156
163157 except:
240234 self.plugins_node = plugins_node
241235 self.healthmap = self.getHealthmap()
242236 self.waf = self.getWaf()
243 self.ip = plugins_node.find('resolver').find('results').find('hostname').get('ipaddress')
237 try:
238 self.ip = plugins_node.find('resolver').find('results') \
239 .find('hostname').get('ipaddress')
240 except Exception:
241 self.ip = '0.0.0.0'
244242
245243 def getHealthmap(self):
246244
327325 self.version = '1.3.2'
328326 self.framework_version = '1.0.0'
329327 self.options = None
330 self._command_regex = re.compile(r'^(arachni |\.\/arachni).*?')
328 self._command_regex = re.compile(r'^(arachni|\.\/arachni)\s+.*?')
331329 self.protocol = None
332330 self.hostname = None
333331 self.port = '80'
334332 self.address = None
333 self._use_temp_file = True
334 self._temp_file_extension = ["afr", "xml"]
335335
336336 def report_belongs_to(self, **kwargs):
337337 if super().report_belongs_to(**kwargs):
341341 return re.search("/Arachni/arachni/", output) is not None
342342 return False
343343
344 def _parse_filename(self, filename):
345 """
346 This plugin gets a dict of files, not just one file if it runs the command.
347 We just need the xml.
348 """
349 if isinstance(filename, dict):
350 filename = filename['xml']
351 with open(filename, **self.open_options) as output:
352 self.parseOutputString(output.read())
353 if self._delete_temp_file:
354 if isinstance(filename, dict):
355 for _file in filename.values():
356 try:
357 os.remove(_file)
358 except Exception as e:
359 self.logger.error("Error on delete file: (%s) [%s]", _file, e)
360 else:
361 try:
362 os.remove(filename)
363 except Exception as e:
364 self.logger.error("Error on delete file: (%s) [%s]", filename, e)
365
344366 def parseOutputString(self, output, debug=False):
345367 """
346368 This method will discard the output the shell sends, it will read it
347369 from the xml where it expects it to be present.
348370 """
349
350371 parser = ArachniXmlParser(output)
351372
352373 # Check xml parsed ok...
353374 if not parser.system:
354 print('Error in xml report... Exiting...')
355375 return
356376
357377 self.hostname = self.getHostname(parser.system.url)
358 self.address = self.getAddress(parser.plugins.ip)
359
378 self.address = resolve_hostname(parser.plugins.ip)
360379
361380 # Create host and interface
362381 host_id = self.createAndAddHost(self.address)
412431 """
413432 Use bash to run sequentialy arachni and arachni_reporter
414433 """
415
416 afr_output_file_path = os.path.join(
417 self.data_path,
418 "%s_%s_output-%s.afr" % (
419 self.get_ws(),
420 self.id,
421 random.uniform(1, 10))
422 )
423
434 # Dont call the parent beacuse this plugin needs a different implementation
435 if command_string.startswith("sudo"):
436 params = " ".join(command_string.split()[2:])
437 else:
438 params = " ".join(command_string.split()[1:])
439 self.vulns_data["command"]["params"] = params
440 self.vulns_data["command"]["user"] = username
441 self._output_file_path = {}
442 self._delete_temp_file = True
443 for ext in self._temp_file_extension:
444 self._output_file_path[ext] = self._get_temp_file(extension=ext)
445 afr_file_path = self._output_file_path['afr']
446 xml_file_path = self._output_file_path['xml']
424447 report_arg_re = r"^.*(--report-save-path[=\s][^\s]+).*$"
425 arg_match = re.match(report_arg_re,command_string)
448 arg_match = re.match(report_arg_re, command_string)
426449 if arg_match is None:
427 main_cmd = re.sub(r"(^.*?arachni)",
428 r"\1 --report-save-path=%s" % afr_output_file_path,
429 command_string)
430 else:
431 main_cmd = re.sub(arg_match.group(1),
432 r"--report-save-path=%s" % afr_output_file_path,
433 command_string)
450 main_cmd = re.sub(r"(^.*?arachni)", r"\1 --report-save-path=%s" % afr_file_path, command_string)
451 else:
452 main_cmd = re.sub(arg_match.group(1), r"--report-save-path=%s" % afr_file_path, command_string)
434453
435454 # add reporter
436 self._output_file_path = re.sub('.afr', '.xml', afr_output_file_path)
437455 cmd_prefix_match = re.match(r"(^.*?)arachni ", command_string)
438456 cmd_prefix = cmd_prefix_match.group(1)
439 reporter_cmd = "%s%s --reporter=\"xml:outfile=%s\" \"%s\"" % (
440 cmd_prefix,
441 "arachni_reporter",
442 self._output_file_path,
443 afr_output_file_path)
457 reporter_cmd = "%s%s --reporter=\"xml:outfile=%s\" \"%s\"" % (cmd_prefix, "arachni_reporter", xml_file_path,
458 afr_file_path)
444459 return "/usr/bin/env -- bash -c '%s 2>&1 && if [ -e \"%s\" ];then %s 2>&1;fi'" % (main_cmd,
445 afr_output_file_path,
460 afr_file_path,
446461 reporter_cmd)
447462
448463 def getHostname(self, url):
460475
461476 return self.hostname
462477
463 def getAddress(self, hostname):
464
465 # Returns remote IP address from hostname.
466 try:
467 return socket.gethostbyname(hostname)
468 except socket.error as msg:
469 return self.hostname
470
471478
472479 def createPlugin():
473480 return ArachniPlugin()
3030 self.framework_version = "1.0.0"
3131 self.options = None
3232 self._current_output = None
33 self._command_regex = re.compile(
34 r'^(sudo arp-scan|\.\/arp-scan|arp-scan).*?')
33 self._command_regex = re.compile(r'^(sudo arp-scan|\.\/arp-scan|arp-scan)\s+.*?')
3534 self._host_ip = None
3635
37 def parseOutputString(self, output, debug=False):
36 def parseOutputString(self, output):
3837
3938 host_info = re.search(
4039 r"(\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b)",
5958
6059 return True
6160
62 def processCommandString(self, username, current_path, command_string):
63 return
6461
6562
6663 def createPlugin():
6764 return CmdArpScanPlugin()
6865
69 # I'm Py3
3232 self.options = None
3333 self._current_output = None
3434 self.target = None
35 self._command_regex = re.compile(r'^(beef|sudo beef|\.\/beef).*?')
35 self._command_regex = re.compile(r'^(beef|sudo beef|\.\/beef)\s+.*?')
3636
3737 self.addSetting("Host", str, "http://127.0.0.1:3000/")
3838 self.addSetting(
9393 ref=["http://http://beefproject.com/"],
9494 severity=3)
9595
96 def processCommandString(self, username, current_path, command_string):
97 return None
9896
9997 def setHost(self):
10098 pass
33 See the file 'doc/LICENSE' for the license information
44 """
55 import re
6 import socket
76 from urllib.parse import urlparse
8 from faraday_plugins.plugins.plugin import PluginBase
97
108 __author__ = "Roberto Focke"
119 __copyright__ = "Copyright (c) 2017, Infobyte LLC"
1210 __license__ = ""
1311 __version__ = "1.0.0"
12
13 from faraday_plugins.plugins.plugin import PluginBase
14 from faraday_plugins.plugins.plugins_utils import resolve_hostname
1415
1516
1617 class brutexss (PluginBase):
2324 self.version = "1.0.0"
2425 self.protocol ='tcp'
2526 self._command_regex = re.compile(r'^(sudo brutexss|brutexss|sudo brutexss\.py|brutexss\.py|python brutexss\.py|'
26 r'\.\/brutexss\.py).*?')
27 r'\.\/brutexss\.py)\s+.*?')
2728
2829 def parseOutputString(self, output, debug=False):
2930 lineas = output.split("\n")
3132 found_vuln = False
3233 for linea in lineas:
3334 if linea.find("is available! Good!") > 0:
34 print(linea)
3535 url = re.findall('(?:[-\w.]|(?:%[\da-fA-F]{2}))+', linea)[0]
3636 port = 80
3737 if urlparse(url).scheme == 'https':
4545 parametro.append(vuln_list[1])
4646 found_vuln=len(parametro) > 0
4747 host_id = self.createAndAddHost(url)
48 address=socket.gethostbyname(url)
48 address = resolve_hostname(url)
4949 interface_id = self.createAndAddInterface(host_id, address, ipv4_address=address,
5050 hostname_resolution=[url])
5151 service_id = self.createAndAddServiceToInterface(host_id, interface_id, self.protocol, 'tcp',
5252 ports=[port], status='Open', version="",
5353 description="")
5454 if found_vuln:
55 self.createAndAddVulnWebToService(host_id,service_id, name="xss", desc="XSS", ref='', severity='med',
55 self.createAndAddVulnWebToService(host_id, service_id, name="xss", desc="XSS", ref='', severity='med',
5656 website=url, path='', method='', pname='', params=''.join(parametro),
5757 request='', response='')
5858
59 def processCommandString(self, username, current_path, command_string):
60 return None
6159
6260
6361 def createPlugin():
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
30 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
85 """
96 import re
107 import os
11 import sys
128 import base64
139 from bs4 import BeautifulSoup, Comment
1410 from faraday_plugins.plugins.plugin import PluginXMLFormat
2622
2723 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
2824
29 current_path = os.path.abspath(os.getcwd())
3025
3126 __author__ = "Francisco Amato"
3227 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
269264
270265 del parser
271266
272 def processCommandString(self, username, current_path, command_string):
273 return None
274267
275268 def removeHtml(self, markup):
276269 soup = BeautifulSoup(markup, "html.parser")
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 # I'm Py3
0 """
1 Faraday Penetration Test IDE
2 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
3 See the file 'doc/LICENSE' for the license information
4 """
5 import re
6 from urllib.parse import urlparse
7
8 from faraday_plugins.plugins.plugin import PluginXMLFormat
9
10
11 try:
12 import xml.etree.cElementTree as ET
13 except ImportError:
14 import xml.etree.ElementTree as ET
15
16 __author__ = 'Blas Moyano'
17 __copyright__ = 'Copyright 2020, Faraday Project'
18 __credits__ = ['Blas Moyano']
19 __license__ = ''
20 __version__ = '1.0.0'
21 __status__ = 'Development'
22
23
24 class CheckmarxXmlParser:
25 def __init__(self, xml_output):
26 self.tree = self.parse_xml(xml_output)
27 if self.tree:
28 self.cx_xml_results_attribs = self.tree.attrib
29 self.query = self.getQuery(self.tree)
30 else:
31 self.query = None
32
33 def parse_xml(self, xml_output):
34 try:
35 tree = ET.fromstring(xml_output)
36 except SyntaxError as err:
37 print('SyntaxError In xml: %s. %s' % (err, xml_output))
38 return None
39 return tree
40
41 def getQuery(self, tree):
42 for self.query_node in tree:
43 yield Querys(self.query_node)
44
45
46 class Querys():
47 def __init__(self, query_node):
48
49 self.query_attrib = query_node.attrib
50 self.result, self.path, self.path_node = self.get_Result(query_node.findall('Result'))
51
52 def get_Result(self, result):
53 result_atr = []
54 path = []
55 path_node = []
56 for r in result:
57 self.result_attrib = r.attrib
58 self.path_attrib = r.find('Path').attrib
59 self.path_node = self.get_path_node_info(r.find('Path').findall('PathNode'))
60 result_atr.append(self.result_attrib)
61 path.append(self.path_attrib)
62 path_node.append(self.path_node)
63 return result_atr, path, path_node
64
65 def get_path_node_info(self, path_node):
66 lista = []
67 for pn in path_node:
68 lista_v = []
69 for info_pn in pn:
70 if info_pn.tag == 'Snippet':
71 valor = (
72 'Number', info_pn.find('Line').find('Number').text, 'Code', info_pn.find('Line').find('Code').text)
73 else:
74 valor = (info_pn.tag, info_pn.text)
75 lista_v.append(valor)
76 lista.append(lista_v)
77 return lista
78
79
80 class CheckmarxPlugin(PluginXMLFormat):
81 def __init__(self):
82 super().__init__()
83 self.identifier_tag = ["CxXMLResults"]
84 self.id = 'Checkmarx'
85 self.name = 'Checkmarx XML Output Plugin'
86 self.plugin_version = '1.0.0'
87 self.version = '1.0.0'
88 self.framework_version = '1.0.0'
89 self.options = None
90
91
92 def parseOutputString(self, output):
93 parser = CheckmarxXmlParser(output)
94 if not parser.query:
95 self.logger.warning('Error in xml report... Exiting...')
96 return
97
98 url = urlparse(parser.cx_xml_results_attribs['DeepLink'])
99 port = url.port
100 if not port:
101 if url.scheme == 'https':
102 port = 443
103 elif url.scheme == 'http':
104 port = 80
105 else:
106 port = 0
107 project_name = 'ProjectName' in parser.cx_xml_results_attribs
108 if project_name:
109 host_id = self.createAndAddHost(url.hostname, hostnames=[url.hostname])
110 interface_id = self.createAndAddInterface(host_id, url.hostname, ipv4_address=url.hostname,
111 hostname_resolution=[url.netloc])
112 service_to_interface = self.createAndAddServiceToInterface(host_id, interface_id, name=url.scheme,
113 ports=port)
114 else:
115 host_id = self.createAndAddHost(url.hostname, hostnames=[url.hostname])
116 interface_id = self.createAndAddInterface(host_id, url.hostname, ipv4_address=url.hostname,
117 hostname_resolution=[url.netloc])
118 service_to_interface = self.createAndAddServiceToInterface(host_id, interface_id, name=url.scheme,
119 ports=port)
120 for vulns in parser.query:
121 refs = []
122 categories = 'categories' in vulns.query_attrib
123 vuln_desc = ''
124 if categories:
125 vuln_desc = vulns.query_attrib['categories']
126 vuln_name = vulns.query_attrib['name']
127 vuln_severity = vulns.query_attrib['Severity']
128 vuln_external_id = vulns.query_attrib['id']
129 refs.append(f'CWE-{vulns.query_attrib["cweId"]}')
130 data = ''
131 for files_data in vulns.path_node:
132 for file_data in files_data:
133 data += 60 * '-' + '\n'
134 for row_data in file_data:
135 data += ' '.join([data for data in row_data if data]) + '\n'
136
137 for v_result in vulns.result:
138 refs.append(v_result['DeepLink'])
139 refs.append(v_result['FileName'])
140
141 self.createAndAddVulnToHost(host_id, vuln_name, severity=vuln_severity,
142 resolution=data, external_id=vuln_external_id)
143
144 self.createAndAddVulnWebToService(host_id, service_to_interface, vuln_name,
145 desc=vuln_desc, severity=vuln_severity,
146 resolution=data, ref=refs)
147
148
149 def createPlugin():
150 return CheckmarxPlugin()
151
0 """
1 Faraday Penetration Test IDE
2 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
3 See the file 'doc/LICENSE' for the license information
4
5 """
6 # I'm Py3
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
7 from faraday_plugins.plugins.plugin import PluginCSVFormat
8 from urllib.parse import urlparse
9 import csv
10 import io
11 import dateutil
12
13
14 __author__ = "Blas"
15 __copyright__ = "Copyright (c) 2019, Infobyte LLC"
16 __credits__ = ["Blas"]
17 __license__ = ""
18 __version__ = "1.0.0"
19 __maintainer__ = "Blas"
20 __email__ = "[email protected]"
21 __status__ = "Development"
22
23 from faraday_plugins.plugins.plugins_utils import resolve_hostname
24
25
26 class CobaltParser:
27 """
28 The objective of this class is to parse an CSV file generated by the Cobalt tool.
29
30 TODO: Handle errors.
31 TODO: Test Cobalt output version. Handle what happens if the parser doesn't support it.
32 TODO: Test cases.
33
34 @param Cobalt_filepath A proper simple report generated by Cobalt
35 """
36 def __init__(self, output):
37
38 reader = csv.DictReader(io.StringIO(output))
39 self.headers = reader.fieldnames
40 self.rows = []
41 for row in reader:
42 for k, v in row.items():
43 if v.startswith("'"):
44 row[k] = v[1:]
45 self.rows.append(row)
46
47
48 class CobaltPlugin(PluginCSVFormat):
49 """
50 Example plugin to parse Cobalt output.
51 """
52
53 def __init__(self):
54 super().__init__()
55 self.csv_headers = [{'Token'}, {'Tag'}]
56 self.id = "Cobalt"
57 self.name = "Cobalt CSV Output Plugin"
58 self.plugin_version = "0.0.1"
59 self.version = "0.0.1"
60 self.framework_version = "1.0.1"
61
62 def parseOutputString(self, output):
63 try:
64 parser = CobaltParser(output)
65 except:
66 print("Error parser output")
67 return None
68
69 for row in parser.rows:
70 url = row['BrowserUrl']
71 if not url:
72 continue
73 url_data = urlparse(url)
74 scheme = url_data.scheme
75 port = url_data.port
76 try:
77 run_date = dateutil.parser.parse(row['CreatedAt'])
78 except:
79 run_date = None
80 if url_data.port is None:
81 if scheme == 'https':
82 port = 443
83 elif scheme == 'http':
84 port = 80
85 else:
86 port = url_data.port
87 name = resolve_hostname(url_data.netloc)
88 references = []
89 if row['RefKey']:
90 references.append(row['RefKey'])
91 if row['ResearcherUrl']:
92 references.append(row['ResearcherUrl'])
93 references.append(row['ReportUrl'])
94 request = row['HttpRequest'] if row['HttpRequest'] else row['BrowserUrl']
95 h_id = self.createAndAddHost(name=name, hostnames=[url_data.netloc])
96 s_id = self.createAndAddServiceToHost(h_id, scheme, "tcp", ports=port, status="open")
97 self.createAndAddVulnWebToService(h_id, s_id, name=row['Title'], desc=row['Description'],
98 ref=references, resolution=row['SuggestedFix'],
99 website=url_data.netloc, request=request,
100 pname=url_data.params, category=row['Type'], path=url_data.path,
101 data=row['StepsToReproduce'], external_id=row['Tag'], run_date=run_date)
102
103
104 def createPlugin():
105 return CobaltPlugin()
106
55 See the file 'doc/LICENSE' for the license information
66 """
77 import re
8 import socket
9
10 from faraday_plugins.plugins.plugin import PluginBase
118
129 __author__ = u"Andres Tarantini"
1310 __copyright__ = u"Copyright (c) 2015 Andres Tarantini"
1714 __maintainer__ = u"Andres Tarantini"
1815 __email__ = u"[email protected]"
1916 __status__ = u"Development"
17
18 from faraday_plugins.plugins.plugin import PluginBase
19 from faraday_plugins.plugins.plugins_utils import resolve_hostname
2020
2121
2222 class DigPlugin(PluginBase):
3030 self.name = u"DiG"
3131 self.plugin_version = u"0.0.1"
3232 self.version = u"9.9.5-3"
33 self._command_regex = re.compile(r'^(dig).*?')
33 self._command_regex = re.compile(r'^(dig)\s+.*?')
3434
3535 def parseOutputString(self, output):
3636 # Ignore all lines that start with ";"
6363 if result.get(u"type") == u"A": # A = IPv4 address from dig
6464 ip_address = result.get(u"data")[0]
6565 else: # if not, from socket
66 ip_address = socket.gethostbyname(domain)
66 ip_address = resolve_hostname(domain)
6767
6868 # Create host
6969 host_id = self.createAndAddHost(ip_address)
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44 """
5 from faraday_plugins.plugins.plugin import PluginBase
65 import re
7 import socket
86
97 __author__ = "Federico Fernandez - @q3rv0"
108 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
1311 __maintainer__ = "Federico Fernandez"
1412 __email__ = "[email protected]"
1513 __status__ = "Development"
14
15 from faraday_plugins.plugins.plugin import PluginBase
16 from faraday_plugins.plugins.plugins_utils import resolve_hostname
1617
1718
1819 class dirbPlugin(PluginBase):
3940
4041 def getIP(self, host):
4142 try:
42 ip = socket.gethostbyname(host)
43 ip = resolve_hostname(host)
4344 except Exception:
4445 pass
4546
7273
7374 self.text = '\n'.join(self.text)
7475
75 def parseOutputString(self, output, debug=False):
76 def parseOutputString(self, output):
7677
7778 url = re.search(r"URL_BASE: " + self.regexpUrl, output)
7879 paths = self.pathsDirListing(output)
106107 Adds the -oX parameter to get xml output to the command string that the
107108 user has set.
108109 """
109
110 super().processCommandString(username, current_path, command_string)
110111 no_stop_on_warn_msg_re = r"\s+-w"
111112 arg_search = re.search(no_stop_on_warn_msg_re,command_string)
112113 extra_arg = ""
55 import re
66 import json
77 import shlex
8 import socket
98 import argparse
10 import tempfile
119 import urllib.parse as urlparse
12 from faraday_plugins.plugins.plugin import PluginTerminalOutput
13 from faraday_plugins.plugins.plugins_utils import get_vulnweb_url_fields
14 import os
10 from faraday_plugins.plugins.plugin import PluginBase
11 from faraday_plugins.plugins.plugins_utils import get_vulnweb_url_fields, resolve_hostname
1512
1613
1714 __author__ = "Matías Lang"
5350 }
5451
5552
56 class DirsearchPlugin(PluginTerminalOutput):
53 class DirsearchPlugin(PluginBase):
5754 def __init__(self):
5855 super().__init__()
5956 self.id = "dirsearch"
6057 self.name = "dirsearch"
6158 self.plugin_version = "0.0.1"
6259 self.version = "0.0.1"
63 self._command_regex = re.compile(
64 r'^(sudo )?(python[0-9\.]? )?dirsearch(\.py)?')
65 self.ignore_parsing = False
66 self.json_report_file = None
60 self._command_regex = re.compile(r'^(sudo )?(python[0-9\.]? )?dirsearch(\.py)\s+?')
6761 self.addSetting("Ignore 403", str, "1")
62 self._use_temp_file = True
63 self._temp_file_extension = "json"
6864
69 def parseOutputString(self, output, debug=False):
70 if self.ignore_parsing:
71 return
72 if self.json_report_file:
73 # We ran the plugin via command line
74 try:
75 fp = open(self.json_report_file)
76 except IOError:
77 self.log('Error opening JSON in the file {}'.format(
78 self.json_report_file
79 ), 'ERROR')
80 else:
81 self.parse_json(fp.read())
82 if self.remove_report:
83 os.unlink(self.json_report_file)
84 else:
85 # We are importing a report
86 self.parse_json(output)
65 def parseOutputString(self, output):
66 self.parse_json(output)
8767
88 def resolve(self, domain):
89 return socket.gethostbyname(domain)
9068
9169 @property
9270 def should_ignore_403(self):
10482 return
10583 for (base_url, items) in data.items():
10684 base_split = urlparse.urlsplit(base_url)
107 ip = self.resolve(base_split.hostname)
85 ip = resolve_hostname(base_split.hostname)
10886 h_id = self.createAndAddHost(ip)
10987
11088 i_id = self.createAndAddInterface(
150128 parser.add_argument('-h', '--help', action='store_true')
151129 parser.add_argument('--json-report')
152130 args, unknown = parser.parse_known_args(shlex.split(command_string))
153
154131 if args.help:
155 self.devlog('help detected, ignoring parsing')
156 return command_string
132 return None
157133 if args.json_report:
158134 # The user already defined a path to the JSON report
159 self.json_report_file = args.json_report
160 self.remove_report = False
161 return command_string
135 self._output_file_path = args.json_report
136 return None
162137 else:
163 # Use temporal file to save the report data
164 self.json_report_file = tempfile.mktemp(
165 prefix="dirsearch_report_", suffix=".json")
166 self.devlog('Setting report file to {}'.format(
167 self.json_report_file))
168 self.remove_report = True
169 return '{} --json-report {}'.format(command_string,
170 self.json_report_file)
138 super().processCommandString(username, current_path, command_string)
139 return '{} --json-report {}'.format(command_string, self._output_file_path)
171140
172141
173142 def createPlugin():
1717
1818 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
1919
20 current_path = os.path.abspath(os.getcwd())
2120
2221 __author__ = "Francisco Amato"
2322 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
158157 self.version = "1.2.2"
159158 self.options = None
160159 self._current_output = None
160 self._use_temp_file = True
161 self._temp_file_extension = "txt"
161162 self._command_regex = re.compile(
162 r'^(sudo dnsenum|dnsenum|sudo dnsenum\.pl|dnsenum\.pl|perl dnsenum\.pl|\.\/dnsenum\.pl).*?')
163
163 r'^(sudo dnsenum|dnsenum|sudo dnsenum\.pl|dnsenum\.pl|perl dnsenum\.pl|\.\/dnsenum\.pl)\s+.*?')
164 self.xml_arg_re = re.compile(r"^.*(-o\s*[^\s]+).*$")
164165
165166 def parseOutputString(self, output, debug=False):
166167 """
183184
184185 del parser
185186
186 xml_arg_re = re.compile(r"^.*(-o\s*[^\s]+).*$")
187
188187 def processCommandString(self, username, current_path, command_string):
189188 """
190189 Adds the -oX parameter to get xml output to the command string that the
191190 user has set.
192191 """
193
192 super().processCommandString(username, current_path, command_string)
194193 arg_match = self.xml_arg_re.match(command_string)
195194
196195 if arg_match is None:
197 return re.sub(
198 r"(^.*?dnsenum(\.pl)?)",
199 r"\1 -o %s" % self._output_file_path,
200 command_string)
196 return re.sub(r"(^.*?dnsenum(\.pl)?)", r"\1 -o %s" % self._output_file_path, command_string)
201197 else:
202 return re.sub(arg_match.group(1),
203 r"-o %s" % self._output_file_path,
204 command_string)
198 return re.sub(arg_match.group(1), r"-o %s" % self._output_file_path, command_string)
205199
206200 def setHost(self):
207201 pass
210204 def createPlugin():
211205 return DnsenumPlugin()
212206
213 # I'm Py3
207 # I'm Py3
0 """from __future__ import print_function
1
0 """
21 Faraday Penetration Test IDE
32 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
43 See the file 'doc/LICENSE' for the license information
54 """
65 from faraday_plugins.plugins.plugin import PluginBase
76 import re
8 import os
9 import random
107 from collections import defaultdict
118
12 current_path = os.path.abspath(os.getcwd())
139
1410 __author__ = "Francisco Amato"
1511 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
10298 self.version = "0.30"
10399 self.options = None
104100 self._current_output = None
105 self.current_path = None
106 self._command_regex = re.compile(r'^(sudo dnsmap|dnsmap|\.\/dnsmap).*?')
101 self._command_regex = re.compile(r'^(sudo dnsmap|dnsmap|\.\/dnsmap)\s+.*?')
107102 self.xml_arg_re = re.compile(r"^.*(-r\s*[^\s]+).*$")
103 self._use_temp_file = True
104 self._temp_file_extension = "txt"
108105
109106
110107 def canParseCommandString(self, current_input):
128125 Adds the parameter to get output to the command string that the
129126 user has set.
130127 """
128 super().processCommandString(username, current_path, command_string)
131129 arg_match = self.xml_arg_re.match(command_string)
132130
133131 if arg_match is None:
77 """
88 from faraday_plugins.plugins.plugin import PluginBase
99 import re
10 import os
11 import sys
1210
1311 try:
1412 import xml.etree.cElementTree as ET
2018
2119 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
2220
23 current_path = os.path.abspath(os.getcwd())
2421
2522 __author__ = "Francisco Amato"
2623 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
171168 self.options = None
172169 self._current_output = None
173170 self._command_regex = re.compile(
174 r'^(sudo dnsrecon|dnsrecon|sudo dnsrecon\.py|dnsrecon\.py|python dnsrecon\.py|\.\/dnsrecon\.py).*?')
171 r'^(sudo dnsrecon|dnsrecon|sudo dnsrecon\.py|dnsrecon\.py|python dnsrecon\.py|\.\/dnsrecon\.py)\s+.*?')
172 self._use_temp_file = True
173 self._temp_file_extension = "xml"
175174
176175 def validHosts(self, hosts):
177176 valid_records = ["NS", "CNAME", "A", "MX", "info"]
246245 Adds the -oX parameter to get xml output to the command string that the
247246 user has set.
248247 """
248 super().processCommandString(username, current_path, command_string)
249249 arg_match = self.xml_arg_re.match(command_string)
250250
251251 if arg_match is None:
33 See the file 'doc/LICENSE' for the license information
44
55 """
6 import re
7 import os
68
79 from faraday_plugins.plugins.plugin import PluginBase
8 import re
9 import os
10 import socket
10 from faraday_plugins.plugins.plugins_utils import resolve_hostname
1111
12 current_path = os.path.abspath(os.getcwd())
1312
1413 __author__ = "Francisco Amato"
1514 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
5554 line)
5655
5756 if mregex is not None:
58 ip = self.getAddress(mregex.group(2))
57 ip = resolve_hostname(mregex.group(2))
5958 item = {
6059 'host': mregex.group(1),
6160 'ip': ip,
6261 'type': 'info'}
6362 self.items.append(item)
6463
65 def getAddress(self, hostname):
66 """Returns remote IP address from hostname."""
67 try:
68 return socket.gethostbyname(hostname)
69 except socket.error:
70 return hostname
7164
7265
7366 class DnswalkPlugin(PluginBase):
8376 self.version = "2.0.2"
8477 self.options = None
8578 self._current_output = None
86 self._current_path = None
8779 self._command_regex = re.compile(
88 r'^(sudo dnswalk|dnswalk|\.\/dnswalk).*?')
80 r'^(sudo dnswalk|dnswalk|\.\/dnswalk)\s+.*?')
8981
90 global current_path
9182
9283 def canParseCommandString(self, current_input):
9384 if self._command_regex.match(current_input.strip()):
138129
139130 return True
140131
141 def processCommandString(self, username, current_path, command_string):
142 return None
143
144132
145133 def createPlugin():
146134 return DnswalkPlugin()
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 # I'm Py3
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 import re
6 import csv
7 from ast import literal_eval
8
9 from faraday_plugins.plugins.plugin import PluginCSVFormat
10
11
12 class CSVParser:
13 def __init__(self, csv_output, logger):
14 self.logger = logger
15 self.host_data = [
16 "host_description",
17 "os",
18 "mac",
19 "hostnames",
20 "host_tags"
21 ]
22 self.service_data = [
23 "service_name",
24 "service_description",
25 "version",
26 "service_status",
27 "service_tags"
28 ]
29 self.vuln_data = [
30 "name",
31 "desc",
32 "refs",
33 "severity",
34 "resolution",
35 "data",
36 "external_id",
37 "confirmed",
38 "status",
39 "easeofresolution",
40 "impact_confidentiality",
41 "impact_integrity",
42 "impact_availability",
43 "impact_accountability",
44 "policyviolations",
45 "custom_fields",
46 "website",
47 "path",
48 "request",
49 "response",
50 "method",
51 "pname",
52 "params",
53 "query",
54 "status_code",
55 "tags"
56 ]
57
58 self.items = self.parse_csv(csv_output)
59
60 def parse_csv(self, output):
61 items = []
62 reader = csv.DictReader(output, delimiter=',')
63 obj_to_import = self.check_objects_to_import(reader.fieldnames)
64 if not obj_to_import:
65 return items
66
67 if 'ip' in reader.fieldnames and 'target' not in reader.fieldnames:
68 index = reader.fieldnames.index('ip')
69 reader.fieldnames[index] = "target"
70 custom_fields_names = self.get_custom_fields_names(reader.fieldnames)
71 for row in reader:
72 self.data = {}
73 self.data['row_with_service'] = False
74 self.data['row_with_vuln'] = False
75 self.build_host(row)
76 if "service" in obj_to_import:
77 if row['port'] and row['protocol']:
78 self.data['row_with_service'] = True
79 self.build_service(row)
80 else:
81 self.data['row_with_service'] = False
82 if "vuln" in obj_to_import:
83 if row['name'] and row['desc']:
84 self.data['row_with_vuln'] = True
85 self.build_vulnerability(row, custom_fields_names)
86 else:
87 self.data['row_with_service'] = False
88
89 items.append(self.data)
90 return items
91
92 def check_objects_to_import(self, headers):
93 obj_to_import = []
94
95 # From valid_headers, Faraday will define which objects to import
96 valid_headers = [
97 "ip",
98 "port", "protocol",
99 "name", "desc", "target"
100 ]
101
102 matching_headers = set(valid_headers) & set(headers)
103
104 if "ip" not in matching_headers and "target" not in matching_headers:
105 self.logger.error("No host specified. Please, specify at least one host.")
106 return None
107
108 if "ip" in matching_headers:
109 # Remove ip field to leave only target field
110 matching_headers.remove("ip")
111 if "target" not in matching_headers:
112 matching_headers.add("target")
113
114 obj_to_import.append('host')
115
116 if "port" in matching_headers or "protocol" in matching_headers:
117 port = True if "port" in matching_headers else False
118 protocol = True if "protocol" in matching_headers else False
119
120 if (port and not protocol) or (protocol and not port):
121 self.logger.error(
122 ("Missing columns in CSV file. "
123 "In order to import services, you need to add a column called port "
124 " and a column called protocol.")
125 )
126 return None
127 else:
128 obj_to_import.append('service')
129
130 if "name" in matching_headers or "desc" in matching_headers:
131 vuln_name = True if "name" in matching_headers else False
132 vuln_desc = True if "desc" in matching_headers else False
133
134 if (vuln_name and not vuln_desc) or (vuln_desc and not vuln_name):
135 self.logger.error(
136 ("Missing columns in CSV file. "
137 "In order to import vulnerabilities, you need to add a "
138 "column called name and a column called desc.")
139 )
140 return None
141 else:
142 obj_to_import.append('vuln')
143
144 return obj_to_import
145
146 def get_custom_fields_names(self, headers):
147 custom_fields_names = []
148 for header in headers:
149 match = re.match(r"cf_(\w+)", header)
150 if match:
151 custom_fields_names.append(match.group(1))
152
153 return custom_fields_names
154
155 def build_host(self, row):
156 self.data['target'] = row['target']
157 for item in self.host_data:
158 if item == "hostnames":
159 self.data[item] = self.build_hostnames_list(row)
160 continue
161
162 if item in row:
163 if item == "host_tags":
164 self.data[item] = literal_eval(row[item])
165 else:
166 self.data[item] = row[item]
167 else:
168 self.data[item] = None
169
170 def build_service(self, row):
171 self.data['port'] = row['port']
172 self.data['protocol'] = row['protocol']
173 for item in self.service_data:
174 if item in row:
175 if item == 'service_status':
176 if row[item] == '':
177 # If status is not specified, set it as 'open'
178 self.data[item] = "open"
179 continue
180 elif item == 'service_tags':
181 self.data[item] = literal_eval(row[item])
182 continue
183 self.data[item] = row[item]
184 else:
185 self.data[item] = None
186
187 def build_vulnerability(self, row, custom_fields_names):
188 self.data['vuln_name'] = row['name']
189 self.data['vuln_desc'] = row['desc']
190 impact_dict = {
191 "accountability": False,
192 "confidentiality": False,
193 "availability": False,
194 "integrity": False,
195 }
196
197 if "web_vulnerability" in row:
198 self.data['web_vulnerability'] = True if row['web_vulnerability'] == "True" else False
199 else:
200 self.data['web_vulnerability'] = False
201
202 for item in self.vuln_data:
203 if item in row:
204 if "impact_" in item:
205 impact = re.match(r"impact_(\w+)", item).group(1)
206 impact_dict[impact] = True if row[item] == "True" else False
207 elif item in ["refs", "policyviolations", "tags"]:
208 self.data[item] = literal_eval(row[item])
209 else:
210 self.data[item] = row[item]
211 else:
212 self.data[item] = None
213
214 self.data['impact'] = impact_dict
215 self.data['custom_fields'] = self.parse_custom_fields(row, custom_fields_names)
216
217 def build_hostnames_list(self, row):
218 hostnames = []
219 if "hostnames" in row:
220 try:
221 hostnames = literal_eval(row['hostnames'])
222 except (ValueError, SyntaxError):
223 self.logger.error("Hostname not valid. Faraday will set it as empty.")
224 return hostnames
225
226 def parse_vuln_impact(self, impact):
227 impacts = [
228 "accountability",
229 "confidentiality",
230 "availability",
231 "integrity"
232 ]
233 for item in impacts:
234 if item in impact:
235 return item
236
237 def parse_custom_fields(self, row, custom_fields_names):
238 custom_fields = {}
239 for cf_name in custom_fields_names:
240 cf_value = row["cf_" + cf_name]
241 try:
242 custom_fields[cf_name] = literal_eval(cf_value)
243 except (ValueError, SyntaxError):
244 custom_fields[cf_name] = cf_value
245
246 return custom_fields
247
248
249 class FaradayCSVPlugin(PluginCSVFormat):
250 def __init__(self):
251 super().__init__()
252 self.id = "faraday_csv"
253 self.name = "Faraday CSV Plugin"
254 self.plugin_version = "1.0"
255 self.csv_headers = [{'ip'}, {'target'}]
256
257 def _parse_filename(self, filename):
258 with open(filename, **self.open_options) as output:
259 self.parseOutputString(output)
260
261 def parseOutputString(self, output, debug=False):
262 parser = CSVParser(output, self.logger)
263
264 for item in parser.items:
265 h_id = self.createAndAddHost(
266 name=item['target'],
267 os=item['os'],
268 hostnames=item['hostnames'],
269 mac=item['mac'],
270 description=item['host_description'] or "",
271 tags=item['host_tags']
272 )
273 s_id = None
274 if item['row_with_service']:
275 s_id = self.createAndAddServiceToHost(
276 h_id,
277 name=item['service_name'],
278 protocol=item['protocol'],
279 ports=item['port'],
280 status=item['service_status'] or None,
281 version=item['version'],
282 description=item['service_description'],
283 tags=item['service_tags']
284 )
285 if item['row_with_vuln']:
286 if not item['web_vulnerability'] and not s_id:
287 self.createAndAddVulnToHost(
288 h_id,
289 name=item['vuln_name'],
290 desc=item['vuln_desc'],
291 ref=item['refs'],
292 severity=item['severity'],
293 resolution=item['resolution'],
294 data=item['data'],
295 external_id=item['external_id'],
296 confirmed=item['confirmed'] or False,
297 status=item['status'] or "",
298 easeofresolution=item['easeofresolution'] or None,
299 impact=item['impact'],
300 policyviolations=item['policyviolations'],
301 custom_fields=item['custom_fields'],
302 tags=item['tags']
303 )
304 if not item['web_vulnerability'] and s_id:
305 self.createAndAddVulnToService(
306 h_id,
307 s_id,
308 name=item['vuln_name'],
309 desc=item['vuln_desc'],
310 ref=item['refs'],
311 severity=item['severity'],
312 resolution=item['resolution'],
313 data=item['data'],
314 external_id=item['external_id'],
315 confirmed=item['confirmed'] or False,
316 status=item['status'] or "",
317 easeofresolution=item['easeofresolution'] or None,
318 impact=item['impact'],
319 policyviolations=item['policyviolations'],
320 custom_fields=item['custom_fields'],
321 tags=item['tags']
322 )
323 elif item['web_vulnerability']:
324 self.createAndAddVulnWebToService(
325 h_id,
326 s_id,
327 name=item['vuln_name'],
328 desc=item['vuln_desc'],
329 ref=item['refs'],
330 severity=item['severity'],
331 resolution=item['resolution'],
332 website=item['website'],
333 path=item['path'],
334 request=item['request'],
335 response=item['response'],
336 method=item['method'],
337 pname=item['pname'],
338 params=item['params'],
339 query=item['query'],
340 data=item['data'],
341 external_id=item['external_id'],
342 confirmed=item['confirmed'] or False,
343 status=item['status'] or "",
344 easeofresolution=item['easeofresolution'] or None,
345 impact=item['impact'],
346 policyviolations=item['policyviolations'],
347 status_code=item['status_code'] or None,
348 custom_fields=item['custom_fields'],
349 tags=item['tags']
350 )
351
352
353 def createPlugin():
354 return FaradayCSVPlugin()
33 See the file 'doc/LICENSE' for the license information
44
55 """
6 from faraday_plugins.plugins.plugin import PluginBase
7 import socket
86 import re
97 import os
108 import random
119
12 current_path = os.path.abspath(os.getcwd())
10 from faraday_plugins.plugins.plugin import PluginBase
11 from faraday_plugins.plugins.plugins_utils import resolve_hostname
12
1313
1414 __author__ = "Francisco Amato"
1515 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
108108 self.version = "0.9.9"
109109 self.options = None
110110 self._current_output = None
111 self._current_path = None
112111 self._command_regex = re.compile(
113 r'^(sudo fierce|fierce|sudo fierce\.pl|fierce\.pl|perl fierce\.pl|\.\/fierce\.pl).*?')
114 global current_path
112 r'^(sudo fierce|fierce|sudo fierce\.pl|fierce\.pl|perl fierce\.pl|\.\/fierce\.pl)\s+.*?')
115113
116 self.xml_arg_re = re.compile(r"^.*(>\s*[^\s]+).*$")
117
118 def canParseCommandString(self, current_input):
119 if self._command_regex.match(current_input.strip()):
120 return True
121 else:
122 return False
123114
124115 def resolveCNAME(self, item, items):
125116 for i in items:
127118 item['ip'] = i['ip']
128119 return item
129120 try:
130 item['ip'] = socket.gethostbyname(item['ip'])
121 item['ip'] = resolve_hostname(item['ip'])
131122 except:
132123 pass
133124 return item
135126 def resolveNS(self, item, items):
136127 try:
137128 item['hosts'][0] = item['ip']
138 item['ip'] = socket.gethostbyname(item['ip'])
129 item['ip'] = resolve_hostname(item['ip'])
139130 except:
140131 pass
141132 return item
182173 desc="A Dns server allows unrestricted zone transfers",
183174 ref=["CVE-1999-0532"])
184175
185 def processCommandString(self, username, current_path, command_string):
186 self._output_file_path = os.path.join(
187 self.data_path,
188 "%s_%s_output-%s.txt" % (
189 self.get_ws(),
190 self.id,
191 random.uniform(1, 10))
192 )
193
194 arg_match = self.xml_arg_re.match(command_string)
195
196 if arg_match is None:
197 return "%s > %s" % (command_string, self._output_file_path)
198 else:
199 return re.sub(arg_match.group(1),
200 r"> %s" % self._output_file_path,
201 command_string)
202176
203177
204178 def createPlugin():
00 import base64
11 import io
22 import re
3 from html.parser import HTMLParser
3 import html
44 from zipfile import ZipFile
55
66 import html2text
2626 for host in fp.hosts.keys():
2727 fp.hosts[host] = self.createAndAddHost(host)
2828
29 for vuln in fp.vulns.keys():
29 for vuln_key, vuln in fp.vulns.items():
3030 self.createAndAddVulnToHost(
31 host_id=fp.hosts[fp.vulns[vuln]['host']],
32 name=fp.vulns[vuln]['name'],
33 desc=fp.format_description(vuln),
34 ref=fp.descriptions[fp.vulns[vuln]['class']]['references'],
35 severity=fp.vulns[vuln]['severity'],
31 host_id=fp.hosts[vuln['host']],
32 name=vuln['name'],
33 desc=fp.format_description(vuln_key),
34 ref=fp.descriptions[vuln['class']]['references'],
35 severity=vuln['severity'],
3636 resolution="",
3737 data="",
38 external_id=vuln.text
38 external_id=vuln_key.text
3939 )
4040
4141 def _process_webinspect_vulns(self, fp):
330330 if self.fvdl is None:
331331 return
332332 for description in self.fvdl.Description:
333
334 self.descriptions[description.get("classID")] = {}
335
336 if description.get('classID') not in self.vuln_classes:
333 class_id = description.get("classID")
334 self.descriptions[class_id] = {}
335 if class_id not in self.vuln_classes:
337336 continue
338
339 tips = ""
340337 if hasattr(description, 'Tips'):
341 for tip in description.Tips.getchildren():
342 tips += "\n" + tip.text
343
344 htmlparser = HTMLParser()
345 self.descriptions[description.get("classID")]['text'] = htmlparser.unescape(
346 "Summary:\n{}\n\nExplanation:\n{}\n\nRecommendations:\n{}\n\nTips:{}".format(
347 description.Abstract, description.Explanation, description.Recommendations, tips))
348
338 tips = "\n".join(map(lambda x: x.text, description.Tips.getchildren()))
339 else:
340 tips = ""
341 text = f"Summary:\n{description.Abstract}\n\nExplanation:\n{description.Explanation}\n\nRecommendations:\n{description.Recommendations}\n\nTips:{tips}"
342 self.descriptions[description.get("classID")]['text'] = html.unescape(text)
349343 # group vuln references
350344 references = []
351 try:
352 children = description.References.getchildren()
353 except AttributeError:
354 children = []
355
356 for reference in children:
357
358 for attr in dir(reference):
359 if attr == '__class__':
360 break
361
362 references.append("{}: {}\n".format(attr, getattr(reference, attr)))
363
345 if hasattr(description, "References"):
346 references_elements = description.References.getchildren()
347 for reference in references_elements:
348 for children in reference.getchildren():
349 name = children.tag.split("}")[1]
350 value = children.text
351 references.append(f"{name}: {value}")
364352 self.descriptions[description.get("classID")]['references'] = references
365353
366354 def format_description(self, vulnID):
3434 self._current_output = None
3535 self.target = None
3636
37 self._command_regex = re.compile(
38 r'^(fruitywifi).*?')
37 self._command_regex = re.compile(r'^(fruitywifi)\s+.*?')
3938
4039 self.addSetting("Token", str, "e5dab9a69988dd65e578041416773149ea57a054")
4140 self.addSetting("Server", str, "http://127.0.0.1:8000")
7978
8079 def parseOutputString(self, output, debug=False):
8180
82 try:
81 try:
8382 output = json.loads(output)
8483
8584 if len(output) > 0:
128127 else:
129128 return False
130129
131 def processCommandString(self, username, current_path, command_string, debug=False):
130 def processCommandString(self, username, current_path, command_string):
132131 """
133132 """
134 #params = command_string.replace("fruitywifi","")
133 super().processCommandString(username, current_path, command_string)
135134 params = "-t %s -s %s" % (self.getSetting("Token"), self.getSetting("Server"))
136135
137136 return "python " + os.path.dirname(__file__) + "/fruitywifi.py " + params
138 #return None
137
139138
140139
141140 def createPlugin():
33 See the file 'doc/LICENSE' for the license information
44
55 """
6 from faraday_plugins.plugins.plugin import PluginBase
76 import re
87 import os
9 import socket
108
11 current_path = os.path.abspath(os.getcwd())
9 from faraday_plugins.plugins.plugin import PluginBase
10 from faraday_plugins.plugins.plugins_utils import resolve_hostname
11
1212
1313 __author__ = "Javier Victor Mariano Bruno"
1414 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
3535 self.framework_version = "1.0.0"
3636 self.options = None
3737 self._current_output = None
38 self._command_regex = re.compile(r'^ftp.*?')
38 self._command_regex = re.compile(r'^ftp\s+.*?')
3939 self._host_ip = None
4040 self._port = "21"
4141 self._info = 0
4242 self._version = None
4343
44 global current_path
4544
46 def resolve(self, host):
47 try:
48 return socket.gethostbyname(host)
49 except:
50 pass
51 return host
5245
5346 def parseOutputString(self, output, debug=False):
5447
5649 banner = re.search("220?([\w\W]+)$", output)
5750 if re.search("Connection timed out", output) is None and host_info is not None:
5851 hostname = host_info.group(1)
59 ip_address = self.resolve(hostname)
52 ip_address = resolve_hostname(hostname)
6053 self._version = banner.groups(0) if banner else ""
6154 if debug:
6255 print(ip_address)
8578 def processCommandString(self, username, current_path, command_string):
8679 """
8780 """
81 super().processCommandString(username, current_path, command_string)
8882 count_args = command_string.split()
89
9083 c = count_args.__len__()
9184 self._port = "21"
9285 if re.search("[\d]+", count_args[c - 1]):
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44 """
5 from faraday_plugins.plugins.plugin import PluginBase
6 import socket
75 import re
86 import os
97
10 current_path = os.path.abspath(os.getcwd())
8 from faraday_plugins.plugins.plugin import PluginBase
9 from faraday_plugins.plugins.plugins_utils import resolve_hostname
1110
1211 __author__ = "Francisco Amato"
1312 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
4140 self.add_host_info_to_items(item['ip'], item['host'])
4241 elif goohost_scantype == 'host':
4342 data = line.strip()
44 item = {'host': data, 'ip': self.resolve(data)}
43 item = {'host': data, 'ip': resolve_hostname(data)}
4544 self.add_host_info_to_items(item['ip'], item['host'])
4645 else:
4746 item = {'data': line}
48
49 def resolve(self, host):
50 try:
51 return socket.gethostbyname(host)
52 except:
53 pass
54 return host
5547
5648 def add_host_info_to_items(self, ip_address, hostname):
5749 data = {}
7971 self.plugin_version = "0.0.1"
8072 self.version = "v.0.0.1"
8173 self.options = None
82 self._current_output = None
83 self._current_path = None
84 self._command_regex = re.compile(
85 r'^(sudo goohost\.sh|goohost\.sh|sh goohost\.sh|\.\/goohost\.sh).*?')
74 self._command_regex = re.compile(r'^(sudo goohost\.sh|goohost\.sh|sh goohost\.sh|\.\/goohost\.sh)\s+.*?')
8675 self.host = None
87
88 global current_path
89 self.output_path = None
9076 self._command_string = None
9177
92 def parseOutputString(self, output, debug=False):
78 def parseOutputString(self, output):
9379 """
9480 This method will check if the import was made through the console or by importing a Goohost report.
9581
10086
10187 self.scantype defines the method used to generate the Goohost report
10288
103 NOTE: if 'debug' is true then it is being run from a test case and the
104 output being sent is valid.
10589 """
106
107 if self._command_string:
108 # Import from console
109 self.scantype = self.define_scantype_by_command(self._command_string)
110 report_output = output
111 output = self.read_output_file(report_output)
112 else:
113 # Import from report
114 self.scantype = self.define_scantype_by_output(output)
115
116 if debug:
117 parser = GoohostParser(output, self.scantype)
118 else:
119 parser = GoohostParser(output, self.scantype)
120 if self.scantype == 'host' or self.scantype == 'ip':
121 for item in parser.items:
122 h_id = self.createAndAddHost(
123 item['ip'],
124 hostnames=item['hosts'])
125
90 scantype = self.define_scantype_by_output(output)
91 parser = GoohostParser(output, scantype)
92 if scantype == 'host' or scantype == 'ip':
93 for item in parser.items:
94 h_id = self.createAndAddHost(item['ip'], hostnames=item['hosts'])
12695 del parser
127
128 def processCommandString(self, username, current_path, command_string):
129 """
130 Set output path for parser...
131 """
132 self._current_path = current_path
133 self._command_string = command_string
13496
13597 def define_scantype_by_command(self, command):
13698 method_regex = re.compile(r'-m (mail|host|ip)')
13799 method = method_regex.search(command)
138100 if method:
139101 return method.group(1)
140
141102 return 'host'
142103
143104 def define_scantype_by_output(self, output):
144105 lines = output.split('\n')
145106 line = lines[0].split(' ')
146
147107 if len(line) == 1:
148108 return 'host'
149109 elif len(line) == 2:
150110 return 'ip'
151111
152 def read_output_file(self, report_path):
153 mypath = re.search("Results saved in file (\S+)", report_path)
154 if not mypath:
112 def get_report_path_from_output(self, command_output):
113 report_name = re.search("Results saved in file (\S+)", command_output)
114 if not report_name:
155115 return False
156116 else:
157 self.output_path = self._current_path + "/" + mypath.group(1)
158 if not os.path.exists(self.output_path):
117 self._output_file_path = os.path.join(self._current_path, report_name.group(1))
118 if not os.path.exists(self._output_file_path):
159119 return False
160 with open(self.output_path, 'r') as report:
161 output = report.read()
120 else:
121 self._delete_temp_file = True
162122
163 return output
123 def processOutput(self, command_output):
124 self.get_report_path_from_output(command_output)
125 if self.has_custom_output():
126 self._parse_filename(self.get_custom_file_path())
127 else:
128 self.parseOutputString(command_output)
164129
165130
166131 def createPlugin():
167132 return GoohostPlugin()
168133
169 # I'm Py3
3636 ip_address = regex_ipv4.group(0).rstrip("):") # Regex pls
3737 else:
3838 # Exit plugin, ip address not found. bad output
39 self.log("Abort plugin: Ip address not found", "INFO")
39 self.logger.warning("Abort plugin: Ip address not found", "INFO")
4040 return
4141
4242 hostname = output.split(" ")[1]
6969 s_id = self.createAndAddServiceToInterface(
7070 host_id, i_id, service, protocol="tcp", ports=port, status="open")
7171
72 def processCommandString(self, username, current_path, command_string):
73 return None
74
7572
7673 def createPlugin():
7774 return hping3()
44 """
55 from faraday_plugins.plugins.plugin import PluginBase
66 import re
7 import os
8 import random
9
10 current_path = os.path.abspath(os.getcwd())
117
128 __author__ = "Francisco Amato"
139 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
2925 def __init__(self, xml_output):
3026 lines = xml_output.splitlines()
3127 self.items = []
32 for l in lines:
28 for line in lines:
3329
3430 reg = re.search(
3531 "\[([^$]+)\]\[([^$]+)\] host: ([^$]+) login: ([^$]+) password: ([^$]+)",
36 l)
32 line)
3733
3834 if reg:
39
4035 item = {
4136 'port': reg.group(1),
4237 'plugin': reg.group(2),
5954 self.plugin_version = "0.0.1"
6055 self.version = "7.5"
6156 self.options = None
62 self._current_output = None
63 self._current_path = None
64 self._command_regex = re.compile(
65 r'^(sudo hydra|sudo \.\/hydra|hydra|\.\/hydra).*?')
57 self._command_regex = re.compile(r'^(sudo hydra|sudo \.\/hydra|hydra|\.\/hydra)\s+.*?')
6658 self.host = None
67
59 self._use_temp_file = True
60 self._temp_file_extension = "txt"
61 self.xml_arg_re = re.compile(r"^.*(-o\s*[^\s]+).*$")
6862
6963 def parseOutputString(self, output, debug=False):
7064 """
133127
134128 del parser
135129
136 xml_arg_re = re.compile(r"^.*(-o\s*[^\s]+).*$")
137
138130 def processCommandString(self, username, current_path, command_string):
139
140 self._output_file_path = os.path.join(
141 self.data_path,
142 "hydra_output-%s.txt" % random.uniform(1, 10))
143
131 super().processCommandString(username, current_path, command_string)
144132 arg_match = self.xml_arg_re.match(command_string)
145
146133 if arg_match is None:
147134 return re.sub(r"(^.*?hydra?)", r"\1 -o %s" % self._output_file_path, command_string)
148135 else:
149 return re.sub(
150 arg_match.group(1),
151 r"-o %s" % self._output_file_path,
152 command_string)
136 return re.sub(arg_match.group(1), r"-o %s" % self._output_file_path, command_string)
153137
154138 def _isIPV4(self, ip):
155139 if len(ip.split(".")) == 4:
44
55 """
66 import re
7 import os
87
98 from faraday_plugins.plugins.plugin import PluginXMLFormat
109 try:
1716
1817 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
1918
20 current_path = os.path.abspath(os.getcwd())
2119
2220 __author__ = "Francisco Amato"
2321 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
5957 try:
6058 tree = ET.fromstring(xml_output)
6159 except SyntaxError as err:
62 #logger.error("SyntaxError: %s. %s" % (err, xml_output))
6360 return None
6461
6562 return tree
224221 self.version = "Core Impact 2013R1/2017R2"
225222 self.framework_version = "1.0.0"
226223 self.options = None
227 self._current_output = None
228 self._command_regex = re.compile(r'^(sudo impact|\.\/impact).*?')
229224
230225 def parseOutputString(self, output, debug=False):
231226 parser = ImpactXmlParser(output)
277272 ref=v.ref)
278273 else:
279274 s_id = mapped_services.get(v.service_name) or mapped_ports.get(v.port)
280 print(v.service_name)
281 print(s_id)
282275 self.createAndAddVulnToService(
283276 h_id,
284277 s_id,
297290 status=p['status'])
298291 del parser
299292
300 def processCommandString(self, username, current_path, command_string):
301 return None
302293
303294 def setHost(self):
304295 pass
307298 def createPlugin():
308299 return ImpactPlugin()
309300
310 # I'm Py3
301
55 """
66 import os
77 from lxml import etree
8 from faraday_plugins.plugins.plugin import PluginBase
8 from faraday_plugins.plugins.plugin import PluginXMLFormat
99
1010
1111 try:
116116 return None
117117
118118
119 class JunitPlugin(PluginBase):
119 class JunitPlugin(PluginXMLFormat):
120120 """
121121 Example plugin to parse junit output.
122122 """
130130 self.framework_version = "1.0.0"
131131 self.options = None
132132 self._current_output = None
133 self._command_regex = None
134133
135134 def parseOutputString(self, output, debug=False):
136135
1010 from faraday_plugins.plugins.plugins_utils import filter_services, get_all_protocols
1111
1212
13 current_path = os.path.abspath(os.getcwd())
14
15
1613 class LynisLogDataExtracter():
17 def __init__(self, datfile=None, output=None):
14 def __init__(self, output):
1815 self.services = defaultdict(list)
19 if datfile and os.path.exists(datfile):
20 with open(datfile) as f:
21 self.rawcontents = f.read()
22
23 if output:
24 self.rawcontents = output
16 self.rawcontents = output
2517
2618 def _svcHelper(self, ip, port, protocol, name):
2719 self.services[ip].append({'port': port, 'protocol': protocol, 'name': name})
3224 domain_match = re.search('^domainname=(.+)$', self.rawcontents, re.MULTILINE)
3325 if domain_match:
3426 domain = domain_match.group(1).strip()
35 return ".".join([hostname,domain])
27 return f"{hostname}.{domain}"
3628 else:
3729 return hostname
3830
4436 return " ".join([name, version])
4537
4638 def ipv4(self):
47 ipv4addrs = []
4839 ipv4s = re.findall('^network_ipv4_address\[\]=(.+)$',
4940 self.rawcontents, re.MULTILINE)
5041 ipv4addrs = self.ipv4_filter(ipv4s)
5142 return(ipv4addrs)
5243
5344 def ipv6(self):
54 ipv6addrs = []
5545 ipv6s = re.findall('^network_ipv6_address\[\]=(.+)$',
5646 self.rawcontents, re.MULTILINE)
5747 ipv6addrs = self.ipv6_filter(ipv6s)
7565
7666 def kernelVersion(self):
7767 versions_dict = {}
78
79 version = re.search('^os_kernel_version=(.+)$',
80 self.rawcontents, re.MULTILINE)
68 version = re.search('^os_kernel_version=(.+)$', self.rawcontents, re.MULTILINE)
8169 if version:
8270 versions_dict['Kernel Version'] = version.group(1).strip()
83
84 version_full = re.search('^os_kernel_version_full=(.+)$',
85 self.rawcontents, re.MULTILINE)
71 version_full = re.search('^os_kernel_version_full=(.+)$', self.rawcontents, re.MULTILINE)
8672 if version_full:
8773 versions_dict['Kernel Version Full'] = version_full.group(1).strip()
88
8974 return versions_dict
9075
9176 def listeningservices(self):
237222
238223 def __init__(self):
239224 super().__init__()
240 self.extension = [".dat", ".log"]
241225 self.id = "Lynis"
242226 self.name = "Lynis DAT Output Plugin"
243227 self.plugin_version = "0.4"
244228 self.version = "2.7.1"
245229 self.options = None
246 self._current_output = None
247 rr = r'^(lynis|sudo lynis|\.\/lynis|sudo \.\/lynis).*?'
248 self._command_regex = re.compile(rr)
230 self._command_regex = re.compile(r'^(lynis|\.\/lynis|)\s+.*?')
249231 self._hosts = []
250
251 global current_path
232 self.extension = [".dat", ".log"]
252233
253234 def report_belongs_to(self, **kwargs):
254235 if super().report_belongs_to(**kwargs):
258239 return output.startswith("# Lynis Report")
259240 return False
260241
261 def parseOutputString(self, output, debug=False):
262 datpath = self.getDatPath(output)
263
264 if datpath:
265 lde = LynisLogDataExtracter(datfile=datpath)
266 elif '# Lynis Report' in output:
267 lde = LynisLogDataExtracter(output=output)
242 def parseOutputString(self, output):
243 """
244 Lynis does not have a means to specify the location for the
245 DAT file, which by default goes to /var/log/lynis-report.dat
246 or /tmp/lynis-report.dat, depending on privileges.
247 Because of that, we will extract the DAT location off
248 lynis' output via parseOutputString().
249 """
250 lde = LynisLogDataExtracter(output)
268251 hostname = lde.hostname()
269252 ipv4s = lde.ipv4()
270253 ipv6s = lde.ipv6()
334317 desc=warns[warn]
335318 )
336319
337 def processCommandString(self, username, current_path, command_string):
338 """
339 Lynis does not have a means to specify the location for the
340 DAT file, which by default goes to /var/log/lynis-report.dat
341 or /tmp/lynis-report.dat, depending on privileges.
342 Because of that, we will extract the DAT location off
343 lynis' output via parseOutputString().
344 """
345 return
346
347 def getDatPath(self, output):
348 m = re.search('(\/.+\.dat)$', output, re.MULTILINE)
349 if m:
350 return(m.group(0).strip())
320 def processOutput(self, command_output):
321 m = re.search('(\/.+\.dat)$', command_output, re.MULTILINE)
322 file_path = m.group(0).strip()
323 self._parse_filename(file_path)
351324
352325
353326 def createPlugin():
+0
-602
faraday_plugins/plugins/repo/maltego/Graph1.graphml less more
0 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
1 <graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
2 <!--Created by yFiles for Java 2.7.0.2-->
3 <key for="graphml" id="d0" yfiles.type="resources"/>
4 <key attr.name="MaltegoEntity" for="node" id="d1"/>
5 <key for="node" id="d2" yfiles.type="nodegraphics"/>
6 <key attr.name="MaltegoLink" for="edge" id="d3"/>
7 <key for="edge" id="d4" yfiles.type="edgegraphics"/>
8 <graph edgedefault="directed" id="G">
9 <node id="n0">
10 <data key="d1">
11 <mtg:MaltegoEntity xmlns:mtg="http://maltego.paterva.com/xml/mtgx" type="maltego.Domain">
12 <mtg:Properties displayValue="fqdn" value="fqdn">
13 <mtg:Property displayName="Domain Name" hidden="false" name="fqdn" nullable="true" readonly="false" type="string">
14 <mtg:Value>ekoparty.org</mtg:Value>
15 </mtg:Property>
16 <mtg:Property displayName="WHOIS Info" hidden="false" name="whois-info" nullable="true" readonly="false" type="string">
17 <mtg:Value/>
18 </mtg:Property>
19 </mtg:Properties>
20 <mtg:DisplayInformation/>
21 <mtg:Weight>0</mtg:Weight>
22 </mtg:MaltegoEntity>
23 </data>
24 <data key="d2">
25 <mtg:EntityRenderer xmlns:mtg="http://maltego.paterva.com/xml/mtgx">
26 <mtg:Position x="412.79999999999995" y="46.46875"/>
27 </mtg:EntityRenderer>
28 </data>
29 </node>
30 <node id="n1">
31 <data key="d1">
32 <mtg:MaltegoEntity xmlns:mtg="http://maltego.paterva.com/xml/mtgx" type="maltego.MXRecord">
33 <mtg:Properties displayValue="fqdn" value="fqdn">
34 <mtg:Property displayName="Priority" hidden="false" name="mxrecord.priority" nullable="true" readonly="false" type="int">
35 <mtg:Value>0</mtg:Value>
36 </mtg:Property>
37 <mtg:Property displayName="MX Record" hidden="false" name="fqdn" nullable="true" readonly="false" type="string">
38 <mtg:Value>mail.ekoparty.org</mtg:Value>
39 </mtg:Property>
40 </mtg:Properties>
41 <mtg:DisplayInformation>
42 <mtg:DisplayElement name="Generator detail"><![CDATA[<table><tr><td class=three>Source</td><td class=two>ekoparty.org</td><td class=three>(Domain)</td></tr><tr><td class=three>Transform</td><td class=two colspan=2>To DNS Name - MX (mail server)</td></tr><tr><td class=three>Result</td><td class=two>mail.ekoparty.org</td><td class=three>(MXrecord)</td></tr><tr><td class=three>Gen. date</td><td class=two colspan=2>2011-6-27 23:46</td></tr></table></font>]]></mtg:DisplayElement>
43 </mtg:DisplayInformation>
44 <mtg:Weight>100</mtg:Weight>
45 </mtg:MaltegoEntity>
46 </data>
47 <data key="d2">
48 <mtg:EntityRenderer xmlns:mtg="http://maltego.paterva.com/xml/mtgx">
49 <mtg:Position x="546.375" y="445.28125"/>
50 </mtg:EntityRenderer>
51 </data>
52 </node>
53 <node id="n2">
54 <data key="d1">
55 <mtg:MaltegoEntity xmlns:mtg="http://maltego.paterva.com/xml/mtgx" type="maltego.EmailAddress">
56 <mtg:Properties displayValue="email" value="email">
57 <mtg:Property displayName="URLs" hidden="false" name="URLS" nullable="true" readonly="false" type="string">
58 <mtg:Value>http://www.diariodecuyo.com.ar/home/new_noticia.php?noticia_id=420508 Diario de Cuyo - Conferencias internacionales de seguridad
59 http://www.diariodecuyo.com.ar/imagenes/2010/09/EDICION/7menu24.pdf Pagina24y25_Maquetaci��n 1
60 </mtg:Value>
61 </mtg:Property>
62 <mtg:Property displayName="Email Address" hidden="false" name="email" nullable="true" readonly="false" type="string">
63 <mtg:Value>[email protected]</mtg:Value>
64 </mtg:Property>
65 </mtg:Properties>
66 <mtg:DisplayInformation>
67 <mtg:DisplayElement name="Generator detail"><![CDATA[<table><tr><td class=three>Source</td><td class=two>ekoparty.org</td><td class=three>(Domain)</td></tr><tr><td class=three>Transform</td><td class=two colspan=2>To Emails @domain [using Search Engine]</td></tr><tr><td class=three>Result</td><td class=two>[email protected]</td><td class=three>(EmailAddress)</td></tr><tr><td class=three>Gen. date</td><td class=two colspan=2>2011-6-27 23:49</td></tr></table></font>]]></mtg:DisplayElement>
68 <mtg:DisplayElement name="Snippet(s):"><![CDATA[<table><tr><td class=one>Diario de Cuyo - Conferencias internacionales de seguridad</td><td class=three>Indexed on:2011/1/21</td></tr><tr><td class=one colspan=2><a href="http://www.diariodecuyo.com.ar/home/new_noticia.php?noticia_id=420508">[www.diariodecuyo.com.ar]</a></td></tr><tr><td class=two colspan=2>Son 23 las conferencias confirmadas para los d��as 16 y 17 de ' Sitio oficial: www.ekoparty.org. Contacto: [email protected] - (11) 6841 1010. Otras '</td></tr></table><br><table><tr><td class=one>Pagina24y25_Maquetaci��n 1</td><td class=three>Indexed on:2010/9/7</td></tr><tr><td class=one colspan=2><a href="http://www.diariodecuyo.com.ar/imagenes/2010/09/EDICION/7menu24.pdf">[www.diariodecuyo.com.ar]</a></td></tr><tr><td class=two colspan=2>M��s que la p��rdida material, lo. duro para cualquier usuario es el ' www.ekoparty.org - Contacto: [email protected] (11) 6841 1010. net. Una vez que el '</td></tr></table><br>]]></mtg:DisplayElement>
69 </mtg:DisplayInformation>
70 <mtg:Weight>200</mtg:Weight>
71 </mtg:MaltegoEntity>
72 </data>
73 <data key="d2">
74 <mtg:EntityRenderer xmlns:mtg="http://maltego.paterva.com/xml/mtgx">
75 <mtg:Position x="250.0" y="194.40625"/>
76 </mtg:EntityRenderer>
77 </data>
78 </node>
79 <node id="n3">
80 <data key="d1">
81 <mtg:MaltegoEntity xmlns:mtg="http://maltego.paterva.com/xml/mtgx" type="maltego.EmailAddress">
82 <mtg:Properties displayValue="email" value="email">
83 <mtg:Property displayName="URLs" hidden="false" name="URLS" nullable="true" readonly="false" type="string">
84 <mtg:Value>http://zh-hk.facebook.com/pages/ekoparty-security-conference/16162244291 ekoparty security conference | Facebook
85 http://www.tecnozona.com/zona_del_que_diran/ekoparty-ya-esta-en-marcha/ Ekoparty ya est�� en marcha
86 </mtg:Value>
87 </mtg:Property>
88 <mtg:Property displayName="Email Address" hidden="false" name="email" nullable="true" readonly="false" type="string">
89 <mtg:Value>[email protected]</mtg:Value>
90 </mtg:Property>
91 </mtg:Properties>
92 <mtg:DisplayInformation>
93 <mtg:DisplayElement name="Generator detail"><![CDATA[<table><tr><td class=three>Source</td><td class=two>ekoparty.org</td><td class=three>(Domain)</td></tr><tr><td class=three>Transform</td><td class=two colspan=2>To Emails @domain [using Search Engine]</td></tr><tr><td class=three>Result</td><td class=two>[email protected]</td><td class=three>(EmailAddress)</td></tr><tr><td class=three>Gen. date</td><td class=two colspan=2>2011-6-27 23:49</td></tr></table></font>]]></mtg:DisplayElement>
94 <mtg:DisplayElement name="Snippet(s):"><![CDATA[<table><tr><td class=one>ekoparty security conference | Facebook</td><td class=three>Indexed on:2010/7/15</td></tr><tr><td class=one colspan=2><a href="http://zh-hk.facebook.com/pages/ekoparty-security-conference/16162244291">[zh-hk.facebook.com]</a></td></tr><tr><td class=two colspan=2>������������Facebook������ ekoparty security conference ���������������������������Facebook��� ekoparty security conference ��������� ' Env��a tu slogans a [email protected]. Una vez recibidos y ordenados '</td></tr></table><br><table><tr><td class=one>Ekoparty ya est�� en marcha</td><td class=three>Indexed on:2011/6/9</td></tr><tr><td class=one colspan=2><a href="http://www.tecnozona.com/zona_del_que_diran/ekoparty-ya-esta-en-marcha/">[www.tecnozona.com]</a></td></tr><tr><td class=two colspan=2>Ten��s hasta el 26 de mayo para enviar tus slogans (hasta 3) a [email protected]. Una vez recibidos y ordenados, se va a hacer online una votaci��n '</td></tr></table><br>]]></mtg:DisplayElement>
95 </mtg:DisplayInformation>
96 <mtg:Weight>200</mtg:Weight>
97 </mtg:MaltegoEntity>
98 </data>
99 <data key="d2">
100 <mtg:EntityRenderer xmlns:mtg="http://maltego.paterva.com/xml/mtgx">
101 <mtg:Position x="86.5" y="194.40625"/>
102 </mtg:EntityRenderer>
103 </data>
104 </node>
105 <node id="n4">
106 <data key="d1">
107 <mtg:MaltegoEntity xmlns:mtg="http://maltego.paterva.com/xml/mtgx" type="maltego.EmailAddress">
108 <mtg:Properties displayValue="email" value="email">
109 <mtg:Property displayName="URLs" hidden="false" name="URLS" nullable="true" readonly="false" type="string">
110 <mtg:Value>http://twitter.com/ekoparty/status/23111351685 Twitter / ekoparty: Just for fun! Resolve this '
111 http://zh-tw.facebook.com/pages/ekoparty-security-conference/16162244291?_fb_noscript=1 ekoparty security conference | Facebook
112 http://www.madrimasd.org/iberoamerica/actividades/mostrar_info.asp?id=45272 Actividades
113 http://www.canal-ar.com.ar/Sosnoticia/sosnoticiamuestra.asp?Id=1955 CanalAR - Core Security renueva su presencia en ekoparty '
114 </mtg:Value>
115 </mtg:Property>
116 <mtg:Property displayName="Email Address" hidden="false" name="email" nullable="true" readonly="false" type="string">
117 <mtg:Value>[email protected]</mtg:Value>
118 </mtg:Property>
119 </mtg:Properties>
120 <mtg:DisplayInformation>
121 <mtg:DisplayElement name="Generator detail"><![CDATA[<table><tr><td class=three>Source</td><td class=two>ekoparty.org</td><td class=three>(Domain)</td></tr><tr><td class=three>Transform</td><td class=two colspan=2>To Emails @domain [using Search Engine]</td></tr><tr><td class=three>Result</td><td class=two>[email protected]</td><td class=three>(EmailAddress)</td></tr><tr><td class=three>Gen. date</td><td class=two colspan=2>2011-6-27 23:49</td></tr></table></font>]]></mtg:DisplayElement>
122 <mtg:DisplayElement name="Snippet(s):"><![CDATA[<table><tr><td class=one>Twitter / ekoparty: Just for fun! Resolve this '</td><td class=three>Indexed on:2010/9/6</td></tr><tr><td class=one colspan=2><a href="http://twitter.com/ekoparty/status/23111351685">[twitter.com]</a></td></tr><tr><td class=two colspan=2>Just for fun! Resolve this challenge and get a 15% off Send the token to organizacion @ ekoparty.org | DM http://bit.ly/avQpcx</td></tr></table><br><table><tr><td class=one>CanalAR - Core Security renueva su presencia en ekoparty '</td><td class=three>Indexed on:2011/6/3</td></tr><tr><td class=one colspan=2><a href="http://www.canal-ar.com.ar/Sosnoticia/sosnoticiamuestra.asp?Id=1955">[www.canal-ar.com.ar]</a></td></tr><tr><td class=two colspan=2>Periodismo y An��lisis en el mundo argentino de las tecnolog��as de la informaci��n ' en http://www.ekoparty.org/, escribir a [email protected] o comunicarse al (+54 '</td></tr></table><br><table><tr><td class=one>Actividades</td><td class=three>Indexed on:2011/6/10</td></tr><tr><td class=one colspan=2><a href="http://www.madrimasd.org/iberoamerica/actividades/mostrar_info.asp?id=45272">[www.madrimasd.org]</a></td></tr><tr><td class=two colspan=2>E-mail: [email protected]. Resumen: Asistentes, invitados, especialistas y referentes de todo el mundo tienen la oportunidad de involucrarse '</td></tr></table><br><table><tr><td class=one>ekoparty security conference | Facebook</td><td class=three>Indexed on:2011/5/26</td></tr><tr><td class=one colspan=2><a href="http://zh-tw.facebook.com/pages/ekoparty-security-conference/16162244291?_fb_noscript=1">[zh-tw.facebook.com]</a></td></tr><tr><td class=two colspan=2>ekoparty security conference - A security conference hosted yearly in Buenos Aires | Facebook ' un email a organizacion @ ekoparty.org te enviaremos una carpeta con '</td></tr></table><br>]]></mtg:DisplayElement>
123 </mtg:DisplayInformation>
124 <mtg:Weight>28</mtg:Weight>
125 </mtg:MaltegoEntity>
126 </data>
127 <data key="d2">
128 <mtg:EntityRenderer xmlns:mtg="http://maltego.paterva.com/xml/mtgx">
129 <mtg:Position x="714.875" y="445.28125"/>
130 </mtg:EntityRenderer>
131 </data>
132 </node>
133 <node id="n5">
134 <data key="d1">
135 <mtg:MaltegoEntity xmlns:mtg="http://maltego.paterva.com/xml/mtgx" type="maltego.EmailAddress">
136 <mtg:Properties displayValue="email" value="email">
137 <mtg:Property displayName="URLs" hidden="false" name="URLS" nullable="true" readonly="false" type="string">
138 <mtg:Value>http://cfp.ekoparty.org/ ekoparty - CALL FOR PAPERS
139 </mtg:Value>
140 </mtg:Property>
141 <mtg:Property displayName="Email Address" hidden="false" name="email" nullable="true" readonly="false" type="string">
142 <mtg:Value>[email protected]</mtg:Value>
143 </mtg:Property>
144 </mtg:Properties>
145 <mtg:DisplayInformation>
146 <mtg:DisplayElement name="Generator detail"><![CDATA[<table><tr><td class=three>Source</td><td class=two>ekoparty.org</td><td class=three>(Domain)</td></tr><tr><td class=three>Transform</td><td class=two colspan=2>To Emails @domain [using Search Engine]</td></tr><tr><td class=three>Result</td><td class=two>[email protected]</td><td class=three>(EmailAddress)</td></tr><tr><td class=three>Gen. date</td><td class=two colspan=2>2011-6-27 23:49</td></tr></table></font>]]></mtg:DisplayElement>
147 <mtg:DisplayElement name="Snippet(s):"><![CDATA[<table><tr><td class=one>ekoparty - CALL FOR PAPERS</td><td class=three>Indexed on:2011/5/30</td></tr><tr><td class=one colspan=2><a href="http://cfp.ekoparty.org/">[cfp.ekoparty.org]</a></td></tr><tr><td class=two colspan=2>If you don't have an account, please Signup to get started. For training submissions, or questions about our CFP process, contact us directly at [email protected] '</td></tr></table><br>]]></mtg:DisplayElement>
148 </mtg:DisplayInformation>
149 <mtg:Weight>0</mtg:Weight>
150 </mtg:MaltegoEntity>
151 </data>
152 <data key="d2">
153 <mtg:EntityRenderer xmlns:mtg="http://maltego.paterva.com/xml/mtgx">
154 <mtg:Position x="86.5" y="297.34375"/>
155 </mtg:EntityRenderer>
156 </data>
157 </node>
158 <node id="n6">
159 <data key="d1">
160 <mtg:MaltegoEntity xmlns:mtg="http://maltego.paterva.com/xml/mtgx" type="maltego.EmailAddress">
161 <mtg:Properties displayValue="email" value="email">
162 <mtg:Property displayName="URLs" hidden="false" name="URLS" nullable="true" readonly="false" type="string">
163 <mtg:Value>http://www.pay2pay.com.ar/clientes/ekoparty-security-conference/register-eng.php ekoparty Security Conference
164 </mtg:Value>
165 </mtg:Property>
166 <mtg:Property displayName="Email Address" hidden="false" name="email" nullable="true" readonly="false" type="string">
167 <mtg:Value>[email protected]</mtg:Value>
168 </mtg:Property>
169 </mtg:Properties>
170 <mtg:DisplayInformation>
171 <mtg:DisplayElement name="Generator detail"><![CDATA[<table><tr><td class=three>Source</td><td class=two>ekoparty.org</td><td class=three>(Domain)</td></tr><tr><td class=three>Transform</td><td class=two colspan=2>To Emails @domain [using Search Engine]</td></tr><tr><td class=three>Result</td><td class=two>[email protected]</td><td class=three>(EmailAddress)</td></tr><tr><td class=three>Gen. date</td><td class=two colspan=2>2011-6-27 23:49</td></tr></table></font>]]></mtg:DisplayElement>
172 <mtg:DisplayElement name="Snippet(s):"><![CDATA[<table><tr><td class=one>ekoparty Security Conference</td><td class=three>Indexed on:2011/6/17</td></tr><tr><td class=one colspan=2><a href="http://www.pay2pay.com.ar/clientes/ekoparty-security-conference/register-eng.php">[www.pay2pay.com.ar]</a></td></tr><tr><td class=two colspan=2>For press or coporate groups acreditations please send a mail to: organizacion ' sponsor at the seventh edition of ekoparty contact us at: [email protected] '</td></tr></table><br>]]></mtg:DisplayElement>
173 </mtg:DisplayInformation>
174 <mtg:Weight>0</mtg:Weight>
175 </mtg:MaltegoEntity>
176 </data>
177 <data key="d2">
178 <mtg:EntityRenderer xmlns:mtg="http://maltego.paterva.com/xml/mtgx">
179 <mtg:Position x="425.25" y="179.40625"/>
180 </mtg:EntityRenderer>
181 </data>
182 </node>
183 <node id="n7">
184 <data key="d1">
185 <mtg:MaltegoEntity xmlns:mtg="http://maltego.paterva.com/xml/mtgx" type="maltego.DNSName">
186 <mtg:Properties displayValue="fqdn" value="fqdn">
187 <mtg:Property displayName="DNS Name" hidden="false" name="fqdn" nullable="true" readonly="false" type="string">
188 <mtg:Value>mail.ekoparty.org</mtg:Value>
189 </mtg:Property>
190 </mtg:Properties>
191 <mtg:DisplayInformation>
192 <mtg:DisplayElement name="Generator detail"><![CDATA[<table><tr><td class=three>Source</td><td class=two>ekoparty.org</td><td class=three>(Domain)</td></tr><tr><td class=three>Transform</td><td class=two colspan=2>To DNS Name [Find common DNS names]</td></tr><tr><td class=three>Result</td><td class=two>mail.ekoparty.org</td><td class=three>(DNSName)</td></tr><tr><td class=three>Gen. date</td><td class=two colspan=2>2011-6-27 23:50</td></tr></table></font>]]></mtg:DisplayElement>
193 </mtg:DisplayInformation>
194 <mtg:Weight>100</mtg:Weight>
195 </mtg:MaltegoEntity>
196 </data>
197 <data key="d2">
198 <mtg:EntityRenderer xmlns:mtg="http://maltego.paterva.com/xml/mtgx">
199 <mtg:Position x="622.75" y="245.875"/>
200 </mtg:EntityRenderer>
201 </data>
202 </node>
203 <node id="n8">
204 <data key="d1">
205 <mtg:MaltegoEntity xmlns:mtg="http://maltego.paterva.com/xml/mtgx" type="maltego.DNSName">
206 <mtg:Properties displayValue="fqdn" value="fqdn">
207 <mtg:Property displayName="DNS Name" hidden="false" name="fqdn" nullable="true" readonly="false" type="string">
208 <mtg:Value>blog.ekoparty.org</mtg:Value>
209 </mtg:Property>
210 </mtg:Properties>
211 <mtg:DisplayInformation>
212 <mtg:DisplayElement name="Generator detail"><![CDATA[<table><tr><td class=three>Source</td><td class=two>ekoparty.org</td><td class=three>(Domain)</td></tr><tr><td class=three>Transform</td><td class=two colspan=2>To DNS Name [Find common DNS names]</td></tr><tr><td class=three>Result</td><td class=two>blog.ekoparty.org</td><td class=three>(DNSName)</td></tr><tr><td class=three>Gen. date</td><td class=two colspan=2>2011-6-27 23:50</td></tr></table></font>]]></mtg:DisplayElement>
213 </mtg:DisplayInformation>
214 <mtg:Weight>100</mtg:Weight>
215 </mtg:MaltegoEntity>
216 </data>
217 <data key="d2">
218 <mtg:EntityRenderer xmlns:mtg="http://maltego.paterva.com/xml/mtgx">
219 <mtg:Position x="250.0" y="297.34375"/>
220 </mtg:EntityRenderer>
221 </data>
222 </node>
223 <edge id="e0" source="n0" target="n1">
224 <data key="d3">
225 <mtg:MaltegoLink xmlns:mtg="http://maltego.paterva.com/xml/mtgx" type="maltego.link.transform-link">
226 <mtg:Properties>
227 <mtg:Property displayName="Weight" hidden="false" name="maltego.link.weight" nullable="true" readonly="false" type="int">
228 <mtg:Value>0</mtg:Value>
229 </mtg:Property>
230 <mtg:Property displayName="Show Label" hidden="false" name="maltego.link.show-label" nullable="true" readonly="false" type="int">
231 <mtg:Value>0</mtg:Value>
232 </mtg:Property>
233 <mtg:Property displayName="Thickness" hidden="false" name="maltego.link.thickness" nullable="true" readonly="false" type="int">
234 <mtg:Value>1</mtg:Value>
235 </mtg:Property>
236 <mtg:Property displayName="Date run" hidden="false" name="maltego.link.transform.run-date" nullable="true" readonly="true" type="date">
237 <mtg:Value>2011-06-27 19:46:28.176 EDT</mtg:Value>
238 </mtg:Property>
239 <mtg:Property displayName="Transform name" hidden="false" name="maltego.link.transform.display-name" nullable="true" readonly="true" type="string">
240 <mtg:Value>To DNS Name - MX (mail server)</mtg:Value>
241 </mtg:Property>
242 <mtg:Property displayName="Style" hidden="false" name="maltego.link.style" nullable="true" readonly="false" type="int">
243 <mtg:Value>0</mtg:Value>
244 </mtg:Property>
245 <mtg:Property displayName="Transform" hidden="true" name="maltego.link.transform.name" nullable="true" readonly="true" type="string">
246 <mtg:Value>paterva.v2.DomainToMXrecord_DNS</mtg:Value>
247 </mtg:Property>
248 <mtg:Property displayName="Color" hidden="false" name="maltego.link.color" nullable="true" readonly="false" type="color">
249 <mtg:Value>-4144960</mtg:Value>
250 </mtg:Property>
251 <mtg:Property displayName="Transform version" hidden="false" name="maltego.link.transform.version" nullable="true" readonly="true" type="string">
252 <mtg:Value>1.0.0</mtg:Value>
253 </mtg:Property>
254 </mtg:Properties>
255 </mtg:MaltegoLink>
256 </data>
257 <data key="d4">
258 <mtg:LinkRenderer xmlns:mtg="http://maltego.paterva.com/xml/mtgx"/>
259 </data>
260 </edge>
261 <edge id="e1" source="n0" target="n2">
262 <data key="d3">
263 <mtg:MaltegoLink xmlns:mtg="http://maltego.paterva.com/xml/mtgx" type="maltego.link.transform-link">
264 <mtg:Properties>
265 <mtg:Property displayName="Weight" hidden="false" name="maltego.link.weight" nullable="true" readonly="false" type="int">
266 <mtg:Value>0</mtg:Value>
267 </mtg:Property>
268 <mtg:Property displayName="Show Label" hidden="false" name="maltego.link.show-label" nullable="true" readonly="false" type="int">
269 <mtg:Value>0</mtg:Value>
270 </mtg:Property>
271 <mtg:Property displayName="Thickness" hidden="false" name="maltego.link.thickness" nullable="true" readonly="false" type="int">
272 <mtg:Value>1</mtg:Value>
273 </mtg:Property>
274 <mtg:Property displayName="Date run" hidden="false" name="maltego.link.transform.run-date" nullable="true" readonly="true" type="date">
275 <mtg:Value>2011-06-27 19:49:20.365 EDT</mtg:Value>
276 </mtg:Property>
277 <mtg:Property displayName="Transform name" hidden="false" name="maltego.link.transform.display-name" nullable="true" readonly="true" type="string">
278 <mtg:Value>To Emails @domain [using Search Engine]</mtg:Value>
279 </mtg:Property>
280 <mtg:Property displayName="Style" hidden="false" name="maltego.link.style" nullable="true" readonly="false" type="int">
281 <mtg:Value>0</mtg:Value>
282 </mtg:Property>
283 <mtg:Property displayName="Transform" hidden="true" name="maltego.link.transform.name" nullable="true" readonly="true" type="string">
284 <mtg:Value>paterva.v2.DomainToEmailAddress_AtDomain_SE</mtg:Value>
285 </mtg:Property>
286 <mtg:Property displayName="Color" hidden="false" name="maltego.link.color" nullable="true" readonly="false" type="color">
287 <mtg:Value>-4144960</mtg:Value>
288 </mtg:Property>
289 <mtg:Property displayName="Transform version" hidden="false" name="maltego.link.transform.version" nullable="true" readonly="true" type="string">
290 <mtg:Value>1.0.0</mtg:Value>
291 </mtg:Property>
292 </mtg:Properties>
293 </mtg:MaltegoLink>
294 </data>
295 <data key="d4">
296 <mtg:LinkRenderer xmlns:mtg="http://maltego.paterva.com/xml/mtgx"/>
297 </data>
298 </edge>
299 <edge id="e2" source="n0" target="n3">
300 <data key="d3">
301 <mtg:MaltegoLink xmlns:mtg="http://maltego.paterva.com/xml/mtgx" type="maltego.link.transform-link">
302 <mtg:Properties>
303 <mtg:Property displayName="Weight" hidden="false" name="maltego.link.weight" nullable="true" readonly="false" type="int">
304 <mtg:Value>0</mtg:Value>
305 </mtg:Property>
306 <mtg:Property displayName="Show Label" hidden="false" name="maltego.link.show-label" nullable="true" readonly="false" type="int">
307 <mtg:Value>0</mtg:Value>
308 </mtg:Property>
309 <mtg:Property displayName="Thickness" hidden="false" name="maltego.link.thickness" nullable="true" readonly="false" type="int">
310 <mtg:Value>1</mtg:Value>
311 </mtg:Property>
312 <mtg:Property displayName="Date run" hidden="false" name="maltego.link.transform.run-date" nullable="true" readonly="true" type="date">
313 <mtg:Value>2011-06-27 19:49:20.365 EDT</mtg:Value>
314 </mtg:Property>
315 <mtg:Property displayName="Transform name" hidden="false" name="maltego.link.transform.display-name" nullable="true" readonly="true" type="string">
316 <mtg:Value>To Emails @domain [using Search Engine]</mtg:Value>
317 </mtg:Property>
318 <mtg:Property displayName="Style" hidden="false" name="maltego.link.style" nullable="true" readonly="false" type="int">
319 <mtg:Value>0</mtg:Value>
320 </mtg:Property>
321 <mtg:Property displayName="Transform" hidden="true" name="maltego.link.transform.name" nullable="true" readonly="true" type="string">
322 <mtg:Value>paterva.v2.DomainToEmailAddress_AtDomain_SE</mtg:Value>
323 </mtg:Property>
324 <mtg:Property displayName="Color" hidden="false" name="maltego.link.color" nullable="true" readonly="false" type="color">
325 <mtg:Value>-4144960</mtg:Value>
326 </mtg:Property>
327 <mtg:Property displayName="Transform version" hidden="false" name="maltego.link.transform.version" nullable="true" readonly="true" type="string">
328 <mtg:Value>1.0.0</mtg:Value>
329 </mtg:Property>
330 </mtg:Properties>
331 </mtg:MaltegoLink>
332 </data>
333 <data key="d4">
334 <mtg:LinkRenderer xmlns:mtg="http://maltego.paterva.com/xml/mtgx"/>
335 </data>
336 </edge>
337 <edge id="e3" source="n0" target="n4">
338 <data key="d3">
339 <mtg:MaltegoLink xmlns:mtg="http://maltego.paterva.com/xml/mtgx" type="maltego.link.transform-link">
340 <mtg:Properties>
341 <mtg:Property displayName="Weight" hidden="false" name="maltego.link.weight" nullable="true" readonly="false" type="int">
342 <mtg:Value>0</mtg:Value>
343 </mtg:Property>
344 <mtg:Property displayName="Show Label" hidden="false" name="maltego.link.show-label" nullable="true" readonly="false" type="int">
345 <mtg:Value>0</mtg:Value>
346 </mtg:Property>
347 <mtg:Property displayName="Thickness" hidden="false" name="maltego.link.thickness" nullable="true" readonly="false" type="int">
348 <mtg:Value>1</mtg:Value>
349 </mtg:Property>
350 <mtg:Property displayName="Date run" hidden="false" name="maltego.link.transform.run-date" nullable="true" readonly="true" type="date">
351 <mtg:Value>2011-06-27 19:49:20.365 EDT</mtg:Value>
352 </mtg:Property>
353 <mtg:Property displayName="Transform name" hidden="false" name="maltego.link.transform.display-name" nullable="true" readonly="true" type="string">
354 <mtg:Value>To Emails @domain [using Search Engine]</mtg:Value>
355 </mtg:Property>
356 <mtg:Property displayName="Style" hidden="false" name="maltego.link.style" nullable="true" readonly="false" type="int">
357 <mtg:Value>0</mtg:Value>
358 </mtg:Property>
359 <mtg:Property displayName="Transform" hidden="true" name="maltego.link.transform.name" nullable="true" readonly="true" type="string">
360 <mtg:Value>paterva.v2.DomainToEmailAddress_AtDomain_SE</mtg:Value>
361 </mtg:Property>
362 <mtg:Property displayName="Color" hidden="false" name="maltego.link.color" nullable="true" readonly="false" type="color">
363 <mtg:Value>-4144960</mtg:Value>
364 </mtg:Property>
365 <mtg:Property displayName="Transform version" hidden="false" name="maltego.link.transform.version" nullable="true" readonly="true" type="string">
366 <mtg:Value>1.0.0</mtg:Value>
367 </mtg:Property>
368 </mtg:Properties>
369 </mtg:MaltegoLink>
370 </data>
371 <data key="d4">
372 <mtg:LinkRenderer xmlns:mtg="http://maltego.paterva.com/xml/mtgx"/>
373 </data>
374 </edge>
375 <edge id="e4" source="n0" target="n5">
376 <data key="d3">
377 <mtg:MaltegoLink xmlns:mtg="http://maltego.paterva.com/xml/mtgx" type="maltego.link.transform-link">
378 <mtg:Properties>
379 <mtg:Property displayName="Weight" hidden="false" name="maltego.link.weight" nullable="true" readonly="false" type="int">
380 <mtg:Value>0</mtg:Value>
381 </mtg:Property>
382 <mtg:Property displayName="Show Label" hidden="false" name="maltego.link.show-label" nullable="true" readonly="false" type="int">
383 <mtg:Value>0</mtg:Value>
384 </mtg:Property>
385 <mtg:Property displayName="Thickness" hidden="false" name="maltego.link.thickness" nullable="true" readonly="false" type="int">
386 <mtg:Value>1</mtg:Value>
387 </mtg:Property>
388 <mtg:Property displayName="Date run" hidden="false" name="maltego.link.transform.run-date" nullable="true" readonly="true" type="date">
389 <mtg:Value>2011-06-27 19:49:20.365 EDT</mtg:Value>
390 </mtg:Property>
391 <mtg:Property displayName="Transform name" hidden="false" name="maltego.link.transform.display-name" nullable="true" readonly="true" type="string">
392 <mtg:Value>To Emails @domain [using Search Engine]</mtg:Value>
393 </mtg:Property>
394 <mtg:Property displayName="Style" hidden="false" name="maltego.link.style" nullable="true" readonly="false" type="int">
395 <mtg:Value>0</mtg:Value>
396 </mtg:Property>
397 <mtg:Property displayName="Transform" hidden="true" name="maltego.link.transform.name" nullable="true" readonly="true" type="string">
398 <mtg:Value>paterva.v2.DomainToEmailAddress_AtDomain_SE</mtg:Value>
399 </mtg:Property>
400 <mtg:Property displayName="Color" hidden="false" name="maltego.link.color" nullable="true" readonly="false" type="color">
401 <mtg:Value>-4144960</mtg:Value>
402 </mtg:Property>
403 <mtg:Property displayName="Transform version" hidden="false" name="maltego.link.transform.version" nullable="true" readonly="true" type="string">
404 <mtg:Value>1.0.0</mtg:Value>
405 </mtg:Property>
406 </mtg:Properties>
407 </mtg:MaltegoLink>
408 </data>
409 <data key="d4">
410 <mtg:LinkRenderer xmlns:mtg="http://maltego.paterva.com/xml/mtgx"/>
411 </data>
412 </edge>
413 <edge id="e5" source="n0" target="n6">
414 <data key="d3">
415 <mtg:MaltegoLink xmlns:mtg="http://maltego.paterva.com/xml/mtgx" type="maltego.link.transform-link">
416 <mtg:Properties>
417 <mtg:Property displayName="Weight" hidden="false" name="maltego.link.weight" nullable="true" readonly="false" type="int">
418 <mtg:Value>0</mtg:Value>
419 </mtg:Property>
420 <mtg:Property displayName="Show Label" hidden="false" name="maltego.link.show-label" nullable="true" readonly="false" type="int">
421 <mtg:Value>0</mtg:Value>
422 </mtg:Property>
423 <mtg:Property displayName="Thickness" hidden="false" name="maltego.link.thickness" nullable="true" readonly="false" type="int">
424 <mtg:Value>1</mtg:Value>
425 </mtg:Property>
426 <mtg:Property displayName="Date run" hidden="false" name="maltego.link.transform.run-date" nullable="true" readonly="true" type="date">
427 <mtg:Value>2011-06-27 19:49:20.365 EDT</mtg:Value>
428 </mtg:Property>
429 <mtg:Property displayName="Transform name" hidden="false" name="maltego.link.transform.display-name" nullable="true" readonly="true" type="string">
430 <mtg:Value>To Emails @domain [using Search Engine]</mtg:Value>
431 </mtg:Property>
432 <mtg:Property displayName="Style" hidden="false" name="maltego.link.style" nullable="true" readonly="false" type="int">
433 <mtg:Value>0</mtg:Value>
434 </mtg:Property>
435 <mtg:Property displayName="Transform" hidden="true" name="maltego.link.transform.name" nullable="true" readonly="true" type="string">
436 <mtg:Value>paterva.v2.DomainToEmailAddress_AtDomain_SE</mtg:Value>
437 </mtg:Property>
438 <mtg:Property displayName="Color" hidden="false" name="maltego.link.color" nullable="true" readonly="false" type="color">
439 <mtg:Value>-4144960</mtg:Value>
440 </mtg:Property>
441 <mtg:Property displayName="Transform version" hidden="false" name="maltego.link.transform.version" nullable="true" readonly="true" type="string">
442 <mtg:Value>1.0.0</mtg:Value>
443 </mtg:Property>
444 </mtg:Properties>
445 </mtg:MaltegoLink>
446 </data>
447 <data key="d4">
448 <mtg:LinkRenderer xmlns:mtg="http://maltego.paterva.com/xml/mtgx"/>
449 </data>
450 </edge>
451 <edge id="e6" source="n0" target="n8">
452 <data key="d3">
453 <mtg:MaltegoLink xmlns:mtg="http://maltego.paterva.com/xml/mtgx" type="maltego.link.transform-link">
454 <mtg:Properties>
455 <mtg:Property displayName="Weight" hidden="false" name="maltego.link.weight" nullable="true" readonly="false" type="int">
456 <mtg:Value>0</mtg:Value>
457 </mtg:Property>
458 <mtg:Property displayName="Show Label" hidden="false" name="maltego.link.show-label" nullable="true" readonly="false" type="int">
459 <mtg:Value>0</mtg:Value>
460 </mtg:Property>
461 <mtg:Property displayName="Thickness" hidden="false" name="maltego.link.thickness" nullable="true" readonly="false" type="int">
462 <mtg:Value>1</mtg:Value>
463 </mtg:Property>
464 <mtg:Property displayName="Date run" hidden="false" name="maltego.link.transform.run-date" nullable="true" readonly="true" type="date">
465 <mtg:Value>2011-06-27 19:50:35.637 EDT</mtg:Value>
466 </mtg:Property>
467 <mtg:Property displayName="Transform name" hidden="false" name="maltego.link.transform.display-name" nullable="true" readonly="true" type="string">
468 <mtg:Value>To DNS Name [Find common DNS names]</mtg:Value>
469 </mtg:Property>
470 <mtg:Property displayName="Style" hidden="false" name="maltego.link.style" nullable="true" readonly="false" type="int">
471 <mtg:Value>0</mtg:Value>
472 </mtg:Property>
473 <mtg:Property displayName="Transform" hidden="true" name="maltego.link.transform.name" nullable="true" readonly="true" type="string">
474 <mtg:Value>paterva.v2.DomainToDNSName_DNSBrute</mtg:Value>
475 </mtg:Property>
476 <mtg:Property displayName="Color" hidden="false" name="maltego.link.color" nullable="true" readonly="false" type="color">
477 <mtg:Value>-4144960</mtg:Value>
478 </mtg:Property>
479 <mtg:Property displayName="Transform version" hidden="false" name="maltego.link.transform.version" nullable="true" readonly="true" type="string">
480 <mtg:Value>1.0.0</mtg:Value>
481 </mtg:Property>
482 </mtg:Properties>
483 </mtg:MaltegoLink>
484 </data>
485 <data key="d4">
486 <mtg:LinkRenderer xmlns:mtg="http://maltego.paterva.com/xml/mtgx"/>
487 </data>
488 </edge>
489 <edge id="e7" source="n0" target="n7">
490 <data key="d3">
491 <mtg:MaltegoLink xmlns:mtg="http://maltego.paterva.com/xml/mtgx" type="maltego.link.transform-link">
492 <mtg:Properties>
493 <mtg:Property displayName="Weight" hidden="false" name="maltego.link.weight" nullable="true" readonly="false" type="int">
494 <mtg:Value>0</mtg:Value>
495 </mtg:Property>
496 <mtg:Property displayName="Show Label" hidden="false" name="maltego.link.show-label" nullable="true" readonly="false" type="int">
497 <mtg:Value>0</mtg:Value>
498 </mtg:Property>
499 <mtg:Property displayName="Thickness" hidden="false" name="maltego.link.thickness" nullable="true" readonly="false" type="int">
500 <mtg:Value>1</mtg:Value>
501 </mtg:Property>
502 <mtg:Property displayName="Date run" hidden="false" name="maltego.link.transform.run-date" nullable="true" readonly="true" type="date">
503 <mtg:Value>2011-06-27 19:50:35.636 EDT</mtg:Value>
504 </mtg:Property>
505 <mtg:Property displayName="Transform name" hidden="false" name="maltego.link.transform.display-name" nullable="true" readonly="true" type="string">
506 <mtg:Value>To DNS Name [Find common DNS names]</mtg:Value>
507 </mtg:Property>
508 <mtg:Property displayName="Style" hidden="false" name="maltego.link.style" nullable="true" readonly="false" type="int">
509 <mtg:Value>0</mtg:Value>
510 </mtg:Property>
511 <mtg:Property displayName="Transform" hidden="true" name="maltego.link.transform.name" nullable="true" readonly="true" type="string">
512 <mtg:Value>paterva.v2.DomainToDNSName_DNSBrute</mtg:Value>
513 </mtg:Property>
514 <mtg:Property displayName="Color" hidden="false" name="maltego.link.color" nullable="true" readonly="false" type="color">
515 <mtg:Value>-4144960</mtg:Value>
516 </mtg:Property>
517 <mtg:Property displayName="Transform version" hidden="false" name="maltego.link.transform.version" nullable="true" readonly="true" type="string">
518 <mtg:Value>1.0.0</mtg:Value>
519 </mtg:Property>
520 </mtg:Properties>
521 </mtg:MaltegoLink>
522 </data>
523 <data key="d4">
524 <mtg:LinkRenderer xmlns:mtg="http://maltego.paterva.com/xml/mtgx"/>
525 </data>
526 </edge>
527 <edge id="e8" source="n7" target="n1">
528 <data key="d3">
529 <mtg:MaltegoLink xmlns:mtg="http://maltego.paterva.com/xml/mtgx" type="maltego.link.manual-link">
530 <mtg:Properties>
531 <mtg:Property displayName="Weight" hidden="false" name="maltego.link.weight" nullable="true" readonly="false" type="int">
532 <mtg:Value>0</mtg:Value>
533 </mtg:Property>
534 <mtg:Property displayName="Show Label" hidden="false" name="maltego.link.show-label" nullable="true" readonly="false" type="int">
535 <mtg:Value>0</mtg:Value>
536 </mtg:Property>
537 <mtg:Property displayName="Reference" hidden="false" name="maltego.link.manual.reference" nullable="true" readonly="false" type="string">
538 <mtg:Value/>
539 </mtg:Property>
540 <mtg:Property displayName="Thickness" hidden="false" name="maltego.link.thickness" nullable="true" readonly="false" type="int">
541 <mtg:Value>1</mtg:Value>
542 </mtg:Property>
543 <mtg:Property displayName="Label" hidden="false" name="maltego.link.manual.type" nullable="true" readonly="false" type="string">
544 <mtg:Value/>
545 </mtg:Property>
546 <mtg:Property displayName="Style" hidden="false" name="maltego.link.style" nullable="true" readonly="false" type="int">
547 <mtg:Value>0</mtg:Value>
548 </mtg:Property>
549 <mtg:Property displayName="Description" hidden="false" name="maltego.link.manual.description" nullable="true" readonly="false" type="string">
550 <mtg:Value/>
551 </mtg:Property>
552 <mtg:Property displayName="Color" hidden="false" name="maltego.link.color" nullable="true" readonly="false" type="color">
553 <mtg:Value>-6895466</mtg:Value>
554 </mtg:Property>
555 </mtg:Properties>
556 </mtg:MaltegoLink>
557 </data>
558 <data key="d4">
559 <mtg:LinkRenderer xmlns:mtg="http://maltego.paterva.com/xml/mtgx"/>
560 </data>
561 </edge>
562 <edge id="e9" source="n7" target="n4">
563 <data key="d3">
564 <mtg:MaltegoLink xmlns:mtg="http://maltego.paterva.com/xml/mtgx" type="maltego.link.manual-link">
565 <mtg:Properties>
566 <mtg:Property displayName="Weight" hidden="false" name="maltego.link.weight" nullable="true" readonly="false" type="int">
567 <mtg:Value>0</mtg:Value>
568 </mtg:Property>
569 <mtg:Property displayName="Show Label" hidden="false" name="maltego.link.show-label" nullable="true" readonly="false" type="int">
570 <mtg:Value>0</mtg:Value>
571 </mtg:Property>
572 <mtg:Property displayName="Reference" hidden="false" name="maltego.link.manual.reference" nullable="true" readonly="false" type="string">
573 <mtg:Value/>
574 </mtg:Property>
575 <mtg:Property displayName="Thickness" hidden="false" name="maltego.link.thickness" nullable="true" readonly="false" type="int">
576 <mtg:Value>1</mtg:Value>
577 </mtg:Property>
578 <mtg:Property displayName="Label" hidden="false" name="maltego.link.manual.type" nullable="true" readonly="false" type="string">
579 <mtg:Value/>
580 </mtg:Property>
581 <mtg:Property displayName="Style" hidden="false" name="maltego.link.style" nullable="true" readonly="false" type="int">
582 <mtg:Value>0</mtg:Value>
583 </mtg:Property>
584 <mtg:Property displayName="Description" hidden="false" name="maltego.link.manual.description" nullable="true" readonly="false" type="string">
585 <mtg:Value/>
586 </mtg:Property>
587 <mtg:Property displayName="Color" hidden="false" name="maltego.link.color" nullable="true" readonly="false" type="color">
588 <mtg:Value>-6895466</mtg:Value>
589 </mtg:Property>
590 </mtg:Properties>
591 </mtg:MaltegoLink>
592 </data>
593 <data key="d4">
594 <mtg:LinkRenderer xmlns:mtg="http://maltego.paterva.com/xml/mtgx"/>
595 </data>
596 </edge>
597 </graph>
598 <data key="d0">
599 <y:Resources/>
600 </data>
601 </graphml>
22 Copyright (C) 2015 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44 """
5 from faraday_plugins.plugins.plugin import PluginXMLFormat
6
7 import zipfile
5 from faraday_plugins.plugins.plugin import PluginZipFormat
86 import re
97 import os
8 import zipfile
109
1110 try:
1211 import xml.etree.cElementTree as ET
2928 __status__ = "Development"
3029
3130
32 def openMtgx(mtgx_file):
33
31 def readMtgx(mtgl_file):
32 maltego_file_graph = "Graphs/Graph1.graphml"
33 xml_graph = ET.parse(mtgl_file.open(maltego_file_graph))
34 mtgl_file.close()
35 return xml_graph
36
37
38 def readMtgl(mtgl_file):
3439 try:
35 file = zipfile.ZipFile(mtgx_file, "r")
36 xml = ET.parse(file.open('Graphs/Graph1.graphml'))
37
38 except:
39 print("Bad report format")
40 maltego_file_company = "Entities/maltego.Company.entity"
41 maltego_file_dns = "Entities/maltego.DNSName.entity"
42 maltego_file_domain = "Entities/maltego.Domain.entity"
43 maltego_file_email = "Entities/maltego.EmailAddress.entity"
44 maltego_file_ipv4 = "Entities/maltego.IPv4Address.entity"
45 maltego_file_location = "Entities/maltego.Location.entity"
46 maltego_file_mxrecord = "Entities/maltego.MXRecord.entity"
47 maltego_file_nsrecord = "Entities/maltego.NSRecord.entity"
48 maltego_file_organization = "Entities/maltego.Organization.entity"
49 maltego_file_person = "Entities/maltego.Person.entity"
50 maltego_file_number = "Entities/maltego.PhoneNumber.entity"
51 maltego_file_website = "Entities/maltego.Website.entity"
52 check_files = {}
53
54 if maltego_file_company in mtgl_file.namelist():
55 xml_company = ET.parse(mtgl_file.open(maltego_file_company))
56 check_files.update({"company": xml_company})
57
58 if maltego_file_dns in mtgl_file.namelist():
59 xml_dns = ET.parse(mtgl_file.open(maltego_file_dns))
60 check_files.update({"DNS": xml_dns})
61
62 if maltego_file_domain in mtgl_file.namelist():
63 xml_domain = ET.parse(mtgl_file.open(maltego_file_domain))
64 check_files.update({"domain": xml_domain})
65
66 if maltego_file_email in mtgl_file.namelist():
67 xml_email = ET.parse(mtgl_file.open(maltego_file_email))
68 check_files.update({"email": xml_email})
69
70 if maltego_file_ipv4 in mtgl_file.namelist():
71 xml_ipv4 = ET.parse(mtgl_file.open(maltego_file_ipv4))
72 check_files.update({"ipv4": xml_ipv4})
73
74 if maltego_file_location in mtgl_file.namelist():
75 xml_location = ET.parse(mtgl_file.open(maltego_file_location))
76 check_files.update({"location": xml_location})
77
78 if maltego_file_mxrecord in mtgl_file.namelist():
79 xml_mxrecord = ET.parse(mtgl_file.open(maltego_file_mxrecord))
80 check_files.update({"mxrecord": xml_mxrecord})
81
82 if maltego_file_nsrecord in mtgl_file.namelist():
83 xml_nsrecord = ET.parse(mtgl_file.open(maltego_file_nsrecord))
84 check_files.update({"nsrecord": xml_nsrecord})
85
86 if maltego_file_organization in mtgl_file.namelist():
87 xml_organization = ET.parse(mtgl_file.open(maltego_file_organization))
88 check_files.update({"organization": xml_organization})
89
90 if maltego_file_person in mtgl_file.namelist():
91 xml_person = ET.parse(mtgl_file.open(maltego_file_person))
92 check_files.update({"person": xml_person})
93
94 if maltego_file_number in mtgl_file.namelist():
95 xml_number = ET.parse(mtgl_file.open(maltego_file_number))
96 check_files.update({"number": xml_number})
97
98 if maltego_file_website in mtgl_file.namelist():
99 xml_web = ET.parse(mtgl_file.open(maltego_file_website))
100 check_files.update({"web": xml_web})
101
102 except zipfile.BadZipFile:
40103 return None
41104
42 file.close()
43 return xml
105 mtgl_file.close()
106 return check_files
44107
45108
46109 class Host():
58121
59122 class MaltegoMtgxParser():
60123
61 def __init__(self, xml_file):
62
63 self.xml = openMtgx(xml_file)
64
65 self.nodes = self.xml.findall(
66 "{http://graphml.graphdrawing.org/xmlns}graph/"
67 "{http://graphml.graphdrawing.org/xmlns}node")
68
69 self.edges = self.xml.findall(
70 "{http://graphml.graphdrawing.org/xmlns}graph/"
71 "{http://graphml.graphdrawing.org/xmlns}edge")
72
73 self.list_hosts = []
74 self.relations = {}
124 def __init__(self, xml_file, extension):
125
126 if extension == '.mtgx':
127 self.xml = readMtgx(xml_file)
128 self.nodes = self.xml.findall(
129 "{http://graphml.graphdrawing.org/xmlns}graph/"
130 "{http://graphml.graphdrawing.org/xmlns}node")
131 self.edges = self.xml.findall(
132 "{http://graphml.graphdrawing.org/xmlns}graph/"
133 "{http://graphml.graphdrawing.org/xmlns}edge")
134
135 self.list_hosts = []
136 self.relations = {}
137 elif extension == '.mtgl':
138 self.xml = readMtgl(xml_file)
75139
76140 def getRelations(self):
77141 """
99163 self.relations.update({target: values})
100164
101165 def getIpAndId(self, node):
102
103166 # Find node ID and maltego entity
104167 node_id = node.get("id")
105168 entity = node.find(
106169 "{http://graphml.graphdrawing.org/xmlns}data/"
107170 "{http://maltego.paterva.com/xml/mtgx}MaltegoEntity")
108
109171 # Check if is IPv4Address
110172 if entity.get("type") != "maltego.IPv4Address":
111173 return None
188250 def getLocation(self, target_node):
189251
190252 # Parse Location Entity
191 result = {
192 "name": "",
193 "area": "",
194 "country_code": "",
195 "longitude": "",
196 "latitude": "",
197 "area_2": ""}
253 result = {"name": "", "area": "", "country_code": "", "longitude": "", "latitude": "", "area_2": ""}
198254
199255 # Get relations with other nodes
200256 node_relations = self.relations[target_node.get("id")]
258314 self.getRelations()
259315
260316 for node in self.nodes:
261
262317 # Get IP Address if not continue with other node...
263318 result = self.getIpAndId(node)
264319 if not result:
266321
267322 # Create host with values by default
268323 host = Host()
269 host.ip = result["ip"]
270 host.node_id = result["node_id"]
324 host.ip = result.get("ip")
325 host.node_id = result.get("node_id")
271326
272327 # Get relations with other nodes
273328 node_relations = self.relations[host.node_id]
296351
297352 return self.list_hosts
298353
299
300 class MaltegoPlugin(PluginXMLFormat):
354 def getInfoMtgl(self, xml, name):
355 sample_value = xml.findall(f'Properties/Fields/Field[@name="{name}"]')
356 for data in sample_value:
357 mtgl_data = data.find('SampleValue').text
358 return mtgl_data
359
360
361 class MaltegoPlugin(PluginZipFormat):
301362
302363 def __init__(self):
303364 super().__init__()
304365 self.identifier_tag = "maltego"
305366 self.id = "Maltego"
306 self.name = "Maltego MTGX Output Plugin"
367 self.name = "Maltego MTGX & MTGL Output Plugin"
307368 self.plugin_version = "1.0.1"
308369 self.version = "Maltego 3.6"
309370 self.framework_version = "1.0.0"
371 self.extension = [".mtgl", ".mtgx"]
372 self.files_list = {"Graphs/Graph1.graphml", "Entities/maltego.Company.entity",
373 "Entities/maltego.DNSName.entity", "Entities/maltego.Domain.entity",
374 "Entities/maltego.EmailAddress.entity", "Entities/maltego.IPv4Address.entity",
375 "Entities/maltego.Location.entity", "Entities/maltego.MXRecord.entity",
376 "Entities/maltego.Organization.entity", "Entities/maltego.NSRecord.entity",
377 "Entities/maltego.Person.entity", "Entities/maltego.PhoneNumber.entity",
378 "Entities/maltego.Website.entity", "Entities/maltego.Hash.entity",
379 "Entities/maltego.hashtag.entity", "Entities/maltego.TwitterUserList.entity"}
310380 self.current_path = None
311381 self.options = None
312382 self._current_output = None
313
314383 self._command_regex = re.compile(
315384 r'^(sudo maltego|maltego|\.\/maltego).*?')
316
317385 global current_path
318386
319 def parseOutputString(self, filename, debug=False):
320
321 maltego_parser = MaltegoMtgxParser(filename)
322 for host in maltego_parser.parse():
323 # Create host
324 try:
325 old_hostname = host.dns_name["value"]
326 except:
327 old_hostname = "unknown"
328
329 host_id = self.createAndAddHost(
330 name=host.ip)
331
332 # Create interface
333 try:
334 network_segment = host.netblock["ipv4_range"]
335 hostname_resolution = [host.dns_name["value"]]
336 except:
337 network_segment = "unknown"
338 hostname_resolution = "unknown"
339
340 interface_id = self.createAndAddInterface(
341 host_id=host_id,
342 name=host.ip,
343 ipv4_address=host.ip,
344 network_segment=network_segment,
345 hostname_resolution=hostname_resolution)
346
347 # Create note with NetBlock information
348 if host.netblock:
349 try:
350 text = (
351 "Network owner:\n" +
352 host.netblock["network_owner"] or "unknown" +
353 "Country:\n" + host.netblock["country"] or "unknown")
354 except:
355 text = "unknown"
356
357 self.createAndAddNoteToHost(
358 host_id=host_id,
359 name="Netblock Information",
360 text=text.encode('ascii', 'ignore')
361 )
362
363 # Create note with host location
364 if host.location:
365 try:
366 text = (
367 "Location:\n" +
368 host.location["name"] +
369 "\nArea:\n" +
370 host.location["area"] +
371 "\nArea 2:\n" +
372 host.location["area_2"] +
373 "\nCountry_code:\n" +
374 host.location["country_code"] +
375 "\nLatitude:\n" +
376 host.location["latitude"] +
377 "\nLongitude:\n" +
378 host.location["longitude"])
379 except:
380 text = "unknown"
381
382 self.createAndAddNoteToHost(
383 host_id=host_id,
384 name="Location Information",
385 text=text.encode('ascii', 'ignore'))
386
387 # Create service web server
388 if host.website:
389 try:
390 description = "SSL Enabled: " + host.website["ssl_enabled"]
391 except:
392 description = "unknown"
393
394 service_id = self.createAndAddServiceToInterface(
395 host_id=host_id,
396 interface_id=interface_id,
397 name=host.website["name"],
398 protocol="TCP:HTTP",
399 ports=[80],
400 description=description)
401
402 try:
403 text = "Urls:\n" + host.website["urls"]
404
405 self.createAndAddNoteToService(
406 host_id=host_id,
407 service_id=service_id,
408 name="URLs",
409 text=text.encode('ascii', 'ignore'))
410 except:
387 def parseOutputString(self, output, debug=False):
388
389 if 'Graphs/Graph1.graphml' in output.namelist():
390 maltego_parser = MaltegoMtgxParser(output, self.extension[1])
391 if not maltego_parser.parse():
411392 pass
412
413 if host.mx_record:
414
415 self.createAndAddServiceToInterface(
416 host_id=host_id,
417 interface_id=interface_id,
418 name=host.mx_record["value"],
419 protocol="SMTP",
420 ports=[25],
421 description="E-mail Server")
422
423 if host.ns_record:
424
425 self.createAndAddServiceToInterface(
426 host_id=host_id,
427 interface_id=interface_id,
428 name=host.ns_record["value"],
429 protocol="DNS",
430 ports=[53],
431 description="DNS Server")
432
433 def processReport(self, filepath):
434 self.parseOutputString(filepath)
435
436 def processCommandString(self, username, current_path, command_string):
437 pass
393 else:
394 for host in maltego_parser.parse():
395 if host.ip is None:
396 ip = '0.0.0.0'
397 else:
398 ip = host.ip
399 host_id = self.createAndAddHost(name=ip)
400 # Create interface
401 try:
402 network_segment = host.netblock["ipv4_range"]
403 hostname_resolution = [host.dns_name["value"]]
404 except TypeError:
405 pass
406 network_segment = "unknown"
407 hostname_resolution = "unknown"
408 interface_id = self.createAndAddInterface(host_id=host_id, name=ip, ipv4_address=ip,
409 network_segment=network_segment,
410 hostname_resolution=[hostname_resolution])
411 # Create note with NetBlock information
412 if host.netblock:
413 try:
414 text = f'Network owner:\n {host.netblock["network_owner"]} ' \
415 f'Country:\n {host.netblock["country"]}'
416 except TypeError:
417 text = "unknown"
418
419 self.createAndAddNoteToHost(host_id=host_id, name="Netblock Information",
420 text=text.encode('ascii', 'ignore'))
421
422 # Create note with host location
423 if host.location:
424 try:
425 text = f'Location:\n {host.location["name"]} \nArea:\n {host.location["area"]} ' \
426 f'\nArea 2:\n {host.location["area_2"]} ' \
427 f'\nCountry_code:\n { host.location["country_code"]} ' \
428 f'\nLatitude:\n {host.location["latitude"]} \nLongitude:\n {host.location["longitude"]}'
429 except TypeError:
430 text = "unknown"
431
432 self.createAndAddNoteToHost(host_id=host_id, name="Location Information",
433 text=text.encode('ascii', 'ignore'))
434
435 # Create service web server
436 if host.website:
437 try:
438 description = f'SSL Enabled: {host.website["ssl_enabled"]}'
439 except TypeError:
440 description = "unknown"
441
442 service_id = self.createAndAddServiceToInterface(host_id=host_id, interface_id=interface_id,
443 name=host.website["name"], protocol="TCP:HTTP",
444 ports=[80], description=description)
445
446 try:
447 text = f'Urls: \n {host.website["urls"]}'
448 self.createAndAddNoteToService(host_id=host_id, service_id=service_id, name="URLs",
449 text=text.encode('ascii', 'ignore'))
450 except TypeError:
451 pass
452
453 if host.mx_record:
454 self.createAndAddServiceToInterface(host_id=host_id, interface_id=interface_id,
455 name=host.mx_record["value"], protocol="SMTP", ports=[25],
456 description="E-mail Server")
457
458 if host.ns_record:
459 self.createAndAddServiceToInterface(host_id=host_id, interface_id=interface_id,
460 name=host.ns_record["value"], protocol="DNS", ports=[53],
461 description="DNS Server")
462 else:
463 maltego_parser = MaltegoMtgxParser(output, self.extension[0])
464 if maltego_parser.xml.get('ipv4'):
465 host_ip = maltego_parser.getInfoMtgl(maltego_parser.xml['ipv4'], 'ipv4-address')
466 host_id = self.createAndAddHost(name=host_ip)
467 else:
468 host_id = self.createAndAddHost(name=self.name)
469 host_ip = '0.0.0.0'
470
471 if maltego_parser.xml.get('DNS'):
472 hostname_resolution = maltego_parser.getInfoMtgl(maltego_parser.xml['DNS'], 'fqdn')
473 interface_id = self.createAndAddInterface(host_id=host_id, name=host_ip, ipv4_address=host_ip,
474 hostname_resolution=[hostname_resolution])
475 else:
476 interface_id = self.createAndAddInterface(host_id=host_id, name=host_ip, ipv4_address=host_ip)
477
478 if maltego_parser.xml.get('location'):
479 location_name = maltego_parser.getInfoMtgl(maltego_parser.xml['location'], 'location.name')
480 location_area = maltego_parser.getInfoMtgl(maltego_parser.xml['location'], 'location.area')
481 location_country = maltego_parser.getInfoMtgl(maltego_parser.xml['location'], 'countrycode')
482 location_longitude = maltego_parser.getInfoMtgl(maltego_parser.xml['location'], 'longitude')
483 location_latitude = maltego_parser.getInfoMtgl(maltego_parser.xml['location'], 'latitude')
484 text = f'Location:\n {location_name} \n Area:\n {location_area} \nCountry_code:\n {location_country} ' \
485 f'\nLatitude:\n {location_latitude} \nLongitude:\n {location_longitude}'
486
487 self.createAndAddNoteToHost(host_id=host_id, name="Location Information",
488 text=text.encode('ascii', 'ignore'))
489 else:
490 self.createAndAddNoteToHost(host_id=host_id, name="Location Information", text="unknown")
491
492 if maltego_parser.xml.get('web'):
493 web_name = maltego_parser.getInfoMtgl(maltego_parser.xml['web'], 'fqdn')
494 text = f'Urls: \n {web_name}'
495 web_port = maltego_parser.getInfoMtgl(maltego_parser.xml['web'], 'ports')
496 if web_port is None:
497 web_port = 80
498
499 web_ssh = maltego_parser.getInfoMtgl(maltego_parser.xml['web'], 'website.ssl-enabled')
500 description = f'SSL Enabled: {web_ssh}'
501
502 service_id = self.createAndAddServiceToInterface(host_id=host_id, interface_id=interface_id,
503 name=web_name, protocol="TCP:HTTP", ports=web_port,
504 description=description)
505
506 self.createAndAddNoteToService(host_id=host_id, service_id=service_id, name="URLs",
507 text=text.encode('ascii', 'ignore'))
508
509 if maltego_parser.xml.get('mxrecord'):
510 mx_name = maltego_parser.getInfoMtgl(maltego_parser.xml['mxrecord'], 'fqdn')
511
512 self.createAndAddServiceToInterface(host_id=host_id, interface_id=interface_id, name=mx_name,
513 protocol="SMTP", ports=[25], description="E-mail Server")
514
515 if maltego_parser.xml.get('nsrecord'):
516 ns_name = maltego_parser.getInfoMtgl(maltego_parser.xml['nsrecord'], 'fqdn')
517 self.createAndAddServiceToInterface(host_id=host_id, interface_id=interface_id, name=ns_name,
518 protocol="DNS", ports=[53], description="DNS Server")
519
520
438521
439522
440523 def createPlugin():
441524 return MaltegoPlugin()
442
443 # I'm Py3
33 See the file 'doc/LICENSE' for the license information
44 """
55 import re
6 import os
7 import random
8 import socket
96 from faraday_plugins.plugins.plugin import PluginBase
7 from faraday_plugins.plugins.plugins_utils import resolve_hostname
108
11
12 current_path = os.path.abspath(os.getcwd())
139
1410 __author__ = "Francisco Amato"
1511 __copyright__ = "Copyright 2013, Faraday Project"
4137 lines = xml_output.splitlines()
4238 self.items = []
4339
44 for l in lines:
40 for line in lines:
4541
46 reg = re.search(
47 "ACCOUNT FOUND: \[([^$]+)\] Host: ([^$]+) User: ([^$]+) Password: ([^$]+) \[SUCCESS\]",
48 l)
49
50 print("REG" + str(reg))
51
42 reg = re.search("ACCOUNT FOUND: \[([^$]+)\] Host: ([^$]+) User: ([^$]+) Password: ([^$]+) \[SUCCESS\]", line)
5243 if reg:
5344
5445 item = {
5748 'user': reg.group(3),
5849 'pass': reg.group(4)
5950 }
60
61 print("ITEM" + str(item))
62 item['ip'] = self.getAddress(item['host'])
51 item['ip'] = resolve_hostname(item['host'])
6352 item['port'] = self.srv[item['service']]
64 print("ITEM" + str(item))
6553 self.items.append(item)
66
67 def getAddress(self, hostname):
68 """
69 Returns remote IP address from hostname.
70 """
71 try:
72 return socket.gethostbyname(hostname)
73 except socket.error as msg:
74 return hostname
7554
7655
7756 class MedusaPlugin(PluginBase):
8665 self.plugin_version = "0.0.1"
8766 self.version = "2.1.1"
8867 self.options = None
89 self._current_output = None
90 self._current_path = None
91 self._command_regex = re.compile(
92 r'^(sudo medusa|sudo \.\/medusa|medusa|\.\/medusa).*?')
93
68 self._command_regex = re.compile(r'^(sudo medusa|sudo \.\/medusa|medusa|\.\/medusa)\s+.*?')
9469 self.host = None
9570 self.port = ""
71 self._use_temp_file = True
72 self._temp_file_extension = "txt"
73 self.xml_arg_re = re.compile(r"^.*(-O\s*[^\s]+).*$")
9674
97 def parseOutputString(self, output, debug=False):
75 def parseOutputString(self, output):
9876 """
9977 This method will discard the output the shell sends, it will read it from
10078 the xml where it expects it to be present.
11290 h_id,
11391 item['ip'],
11492 ipv4_address=item['ip'],
115 hostname_resolution=item['host'])
93 hostname_resolution=[item['host']])
11694 else:
11795 i_id = self.createAndAddInterface(
11896 h_id,
11997 item['ip'],
12098 ipv6_address=item['ip'],
121 hostname_resolution=item['host'])
99 hostname_resolution=[item['host']])
122100
123101 port = self.port if self.port else item['port']
124102
144122
145123 del parser
146124
147 xml_arg_re = re.compile(r"^.*(-O\s*[^\s]+).*$")
148
149125 def processCommandString(self, username, current_path, command_string):
150
126 super().processCommandString(username, current_path, command_string)
151127 self.port = ""
152 self._output_file_path = os.path.join(
153 self.data_path, "medusa_output-%s.txt" % random.uniform(1, 10))
154128 arg_match = self.xml_arg_re.match(command_string)
155129
156130 mreg = re.search(r"\-n( |)([\d]+)", command_string)
158132 self.port = mreg.group(2)
159133
160134 if arg_match is None:
161 return re.sub(
162 r"(^.*?medusa?)", r"\1 -O %s" % self._output_file_path,
163 command_string)
135 return re.sub(r"(^.*?medusa?)", r"\1 -O %s" % self._output_file_path, command_string)
164136 else:
165 return re.sub(
166 arg_match.group(1),
167 r"-O %s" % self._output_file_path,
168 command_string)
137 return re.sub(arg_match.group(1), r"-O %s" % self._output_file_path, command_string)
169138
170139 def _isIPV4(self, ip):
171140 if len(ip.split(".")) == 4:
+0
-7
faraday_plugins/plugins/repo/metagoofil/__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 """
6 # I'm Py3
+0
-120
faraday_plugins/plugins/repo/metagoofil/plugin.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 """
6 from faraday_plugins.plugins.plugin import PluginBase
7 import re
8 import os
9 import socket
10
11
12
13 current_path = os.path.abspath(os.getcwd())
14
15 __author__ = "Francisco Amato"
16 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
17 __credits__ = ["Francisco Amato"]
18 __license__ = ""
19 __version__ = "1.0.0"
20 __maintainer__ = "Francisco Amato"
21 __email__ = "[email protected]"
22 __status__ = "Development"
23
24
25 class MetagoofilParser:
26 """
27 The objective of this class is to parse an xml file generated by the metagoofil tool.
28
29 TODO: Handle errors.
30 TODO: Test metagoofil output version. Handle what happens if the parser doesn't support it.
31 TODO: Test cases.
32
33 @param metagoofil_filepath A proper simple report generated by metagoofil
34 """
35
36 def __init__(self, output):
37
38 self.items = []
39
40 mfile = open("/root/dev/faraday/trunk/src/del", "r")
41 output = mfile.read()
42 mfile.close()
43
44 mregex = re.search(
45 "\[\+\] List of paths and servers found:[-\s]+([^$]+)\[\+\] List of e-mails found:", output, re.M)
46 if mregex is None:
47 return
48
49 self.users = mregex.group(1).split("\n")
50 self.software = mregex.group(2).split("\n")
51 self.servers = mregex.group(1).strip().split("\n")
52
53 for line in self.servers:
54 line = line.strip()
55 item = {'host': line, 'ip': self.resolve(line)}
56 self.items.append(item)
57
58 def resolve(self, host):
59 try:
60 return socket.gethostbyname(host)
61 except:
62 pass
63 return host
64
65
66 class MetagoofilPlugin(PluginBase):
67 """
68 Example plugin to parse metagoofil output.
69 """
70
71 def __init__(self):
72 super().__init__()
73 self.id = "Metagoofil"
74 self.name = "Metagoofil XML Output Plugin"
75 self.plugin_version = "0.0.1"
76 self.version = "2.2"
77 self.options = None
78 self._current_output = None
79 self._current_path = None
80 self._command_regex = re.compile(
81 r'^(sudo metagoofil|metagoofil|sudo metagoofil\.py|metagoofil\.py|python metagoofil\.py|\.\/metagoofil\.py).*?')
82 self._completition = {
83 "": "metagoofil.py -d microsoft.com -t doc,pdf -l 200 -n 50 -o microsoftfiles -f results.html",
84 "-d": "domain to search",
85 "-t": "filetype to download (pdf,doc,xls,ppt,odp,ods,docx,xlsx,pptx)",
86 "-l": "limit of results to search (default 200)",
87 "-h": "work with documents in directory (use \"yes\" for local analysis)",
88 "-n": "limit of files to download",
89 "-o": "working directory",
90 "-f": "output file",
91 }
92
93 global current_path
94
95 def canParseCommandString(self, current_input):
96 if self._command_regex.match(current_input.strip()):
97 return True
98 else:
99 return False
100
101 def parseOutputString(self, output, debug=False):
102 """
103 This method will discard the output the shell sends, it will read it from
104 the xml where it expects it to be present.
105
106 NOTE: if 'debug' is true then it is being run from a test case and the
107 output being sent is valid.
108 """
109
110 def processCommandString(self, username, current_path, command_string):
111 """
112 """
113 return None
114
115
116 def createPlugin():
117 return MetagoofilPlugin()
118
119 # I'm Py3
44 """
55 from faraday_plugins.plugins.plugin import PluginXMLFormat
66 import re
7 import os
8 import sys
7
98
109 try:
1110 import xml.etree.cElementTree as ET
1615 ETREE_VERSION = ET.VERSION
1716
1817 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
19
20 current_path = os.path.abspath(os.getcwd())
2118
2219 __author__ = "Francisco Amato"
2320 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
336333 self.version = "4.7.2"
337334 self.framework_version = "1.0.0"
338335 self.options = None
339 self._current_output = None
340336 self.target = None
341 self._command_regex = re.compile(r'^(metasploit|sudo metasploit|\.\/metasploit).*?')
342337
343338
344339 def parseOutputString(self, output, debug=False):
405400 else:
406401 return False
407402
408 def processCommandString(self, username, current_path, command_string):
409 return None
410403
411404 def setHost(self):
412405 pass
114114 self.name = "ndiff"
115115 self.plugin_version = "0.0.1"
116116 self.version = "1.0.0"
117 self._command_regex = re.compile(r'^(sudo ndiff|ndiff).*?')
117 self._command_regex = re.compile(r'^(sudo ndiff|ndiff)\s+.*?')
118118
119119 def parseOutputString(self, output, debug=False):
120
121120 parser = NdiffXmlParser(output)
122
123121 for host in parser.hostDiff:
124
125122 if host.ip is None:
126123 continue
127
128124 if host.isNewHost:
129125 hostId = self.createAndAddHost(host.ip, '')
130
131126 description = '%s is a NEW host active.\n' % host.ip
132127 for port in host.ports:
133128 description += 'Port: %s/%s\n' % (port[0], port[1])
134
135129 self.createAndAddVulnToHost(
136130 hostId,
137131 'New host active',
140134 'INFO'
141135 )
142136 else:
143
144137 if host.ports == []:
145138 continue
146
147139 hostId = self.createAndAddHost(host.ip, '')
148
149140 description = 'New service/s found.\n'
150141 for port in host.ports:
151142 description += 'Port: %s/%s\n' % (port[0], port[1])
159150 )
160151
161152 def processCommandString(self, username, current_path, command_string):
153 super().processCommandString(username, current_path, command_string)
162154 if command_string.find('--xml') < 0:
163 return command_string + '--xml'
155 return f"{command_string} --xml "
164156
165157
166158 def createPlugin():
+0
-331
faraday_plugins/plugins/repo/nessus/dotnessus_v2.py less more
0 # dotnessus_v2.py
1 # Python module to deal with Nessus .nessus (v2) files
2 # http://code.google.com/p/pynessus/
3 #
4
5 # Copyright (C) 2010 Dustin Seibel
6 #
7 # GNU General Public Licence (GPL)
8 #
9 # This program is free software; you can redistribute it and/or modify it under
10 # the terms of the GNU General Public License as published by the Free Software
11 # Foundation; either version 2 of the License, or (at your option) any later
12 # version.
13 # This program is distributed in the hope that it will be useful, but WITHOUT
14 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16 # details.
17 # You should have received a copy of the GNU General Public License along with
18 # this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 # Place, Suite 330, Boston, MA 02111-1307 USA
20 #
21 # 2011-03-12: 0.1.1: Initial version.
22 from __future__ import absolute_import
23
24 import sys
25 import re
26 import xml.etree.ElementTree as ET
27 from datetime import datetime
28 from io import StringIO, BytesIO
29
30
31 # List all nodes in a ReportItem object that can have multiple values
32 MULTI_VALUED_ATTS = [
33 'cve',
34 'bid',
35 'xref',
36 'cvss_base_score'
37 ]
38
39 # HOST_(START|END) date format
40 HOST_DATE_FORMAT = '%a %b %d %H:%M:%S %Y'
41
42 # Regex defs
43 re_ip = re.compile('\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}')
44 re_wmi_ip = re.compile(
45 'IPAddress/IPSubnet.*?(?P<value>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})', re.I)
46 re_wmi_man = re.compile(
47 'Computer Manufacturer : (?P<manufacturer>.*?)\n.*?Computer Model : (?P<model>.*?)\n.*?Computer Memory : (?P<memory>\d+)\s', re.I | re.M | re.S)
48 re_shares = re.compile('- (?P<value>.*?)\n', re.I | re.M | re.S)
49 re_local_admins = re.compile('- (?P<value>.*?)\s\(', re.I | re.M | re.S)
50 re_wsus = re.compile('WUServer: (?P<wsus_server>.*?)\n.*?AUOptions: (?P<wsus_auoption>.*?)\n.*?Detect LastSuccessTime: (?P<wsus_lastdetect>.*?)\n.*?Download LastSuccessTime: (?P<wsus_lastdownload>.*?)\n.*?Install LastSuccessTime: (?P<wsus_lastinstall>.*?)\n.*?RebootRequired: (?P<wsus_rebootneeded>.*?)\n.*?ServiceStatus: (?P<wsus_auenabled>.*?)(\n|$)', re.I | re.M | re.S)
51 re_unix_memory = re.compile('Total memory: (?P<memory>\d+)\s', re.I)
52 re_unix_model = re.compile(
53 'Serial Number\s+: (?P<serial>.*?)\s.*?\nProduct Name\s+: (?P<model>.*?)(\n|$)', re.I | re.M)
54 re_unix_cpu = re.compile('Current Speed\s+: (?P<cpu_speed>.*?)\s*\nManufacturer\s+: (?P<cpu_vendor>.*?)\s*\nFamily\s+: (?P<cpu_model>.*?)\s*\nExternal Clock\s+: (?P<cpu_externalclock>.*?)\s*\nVersion\s+: (?P<cpu_version>.*?)\s*\nType\s+: (?P<cpu_type>.*?)($|\s*\n)', re.I | re.M)
55
56 # Plugin to regex map
57 # Format is plugin_id: (attribute_name, regex_object, attribute_to_parse,
58 # multi_valued)
59 REGEX_MAP = {
60 '24272': ('ips', re_wmi_ip, 'plugin_output', True),
61 '25203': ('ips', re_ip, 'plugin_output', True),
62 '24270': ('', re_wmi_man, 'description', False),
63 '10395': ('shares', re_shares, 'plugin_output', True),
64 '10902': ('local_admins', re_local_admins, 'plugin_output', True),
65 '10860': ('local_users', re_local_admins, 'plugin_output', True),
66 '55555': ('', re_wsus, 'description', False),
67 '45433': ('', re_unix_memory, 'plugin_output', False),
68 '35351': ('', re_unix_model, 'plugin_output', False),
69 '45432': ('', re_unix_cpu, 'plugin_output', False),
70 }
71
72 # Local IP list
73 LOCAL_IP_LIST = [
74 '0.0.0.0',
75 '127.0.0.1',
76 ]
77
78
79 class Report:
80
81 def __init__(self):
82 self.name = None
83 self.targets = []
84 self.scan_start = None
85 self.scan_end = None
86
87 def parse(self, xml_file, from_string=False):
88 """Import .nessus file"""
89 # Parse XML file
90 #getLogger(self).debug("Parsing report start")
91 if from_string:
92 try:
93 xml_file = BytesIO(xml_file)
94 except Exception as e1:
95 try:
96 xml_file = StringIO(xml_file)
97 except Exception as e2:
98 raise Exception(str(e1) + "\n" + str(e2))
99
100 # Iterate through each host scanned and create objects for each
101 for event, elem in ET.iterparse(xml_file):
102
103 #getLogger(self).debug("Parsing elemn %s" % elem[0:20])
104 # Grab the report name from the Report element
105 if event == 'end' and elem.tag == 'Report':
106 self.name = elem.attrib.get('name')
107 continue
108
109 # Only process ReportHost elements
110 elif event == 'end' and elem.tag != 'ReportHost':
111 continue
112
113 rh_obj = ReportHost(elem)
114 if rh_obj:
115 self.targets.append(rh_obj)
116
117 # Update Report dates
118 if not self.scan_start:
119 self.scan_start = rh_obj.host_start
120 if not self.scan_end:
121 self.scan_end = rh_obj.host_end
122 if rh_obj.get('host_start'):
123 if rh_obj.host_start < self.scan_start:
124 self.scan_start = rh_obj.host_start
125 if rh_obj.host_end > self.scan_end:
126 self.scan_end = rh_obj.host_end
127
128 def __repr__(self):
129 return "<Report: %s>" % self.name
130
131 def get_target(self, name):
132 """Returns a target object given a name"""
133 for t in self.targets:
134 if name.lower() == t.name.lower():
135 return t
136
137
138 class ReportHost:
139
140 def __init__(self, xml_report_host):
141 self.name = None
142 self.dead = False
143 self.vulns = []
144
145 # Do a check to make sure it's well formed
146 # ...
147
148 # Get ReportHost name
149 self.name = xml_report_host.attrib.get('name')
150
151 # Get HostProperties tags
152 for n in xml_report_host.findall('HostProperties/tag'):
153 setattr(self, n.attrib.get('name'), n.text)
154
155 #getLogger(self).debug("Parsing host start tag")
156 tmp = Report()
157 # Convert scan dates and check for dead status
158 if self.get('HOST_START'):
159
160 self.host_start = self.get('HOST_START')
161 #getLogger(self).info("Host start found %s" % self.host_start)
162
163 #self.host_start = datetime.strptime(self.get('HOST_START'), HOST_DATE_FORMAT)
164 else:
165 self.dead = True
166 self.host_end = self.get('HOST_END')
167 #self.host_end = datetime.strptime(self.get('HOST_END'), HOST_DATE_FORMAT)
168
169 # Get all ReportItems
170 for ri in xml_report_host.findall('ReportItem'):
171 ri_obj = ReportItem(ri)
172 if ri_obj:
173 self.vulns.append(ri_obj)
174 xml_report_host.clear()
175
176 # Do an additional check for deadness
177 for v in self.find_vuln(plugin_id='10180'):
178 if 'dead' in str(v.get('plugin_output')):
179 self.dead = True
180
181 # Parse additional fields into host attributes
182 for plugin_id in REGEX_MAP:
183 att, regex, dest_att, multi = REGEX_MAP[plugin_id]
184 vulns = self.find_vuln(plugin_id=plugin_id)
185
186 # If multi flag is set, store results in a dict
187 if multi:
188 results = []
189
190 # Grab all plugins
191 for v in vulns:
192 if multi:
193 setattr(self, att, regex.findall(v.get(dest_att)))
194 else:
195 plugin_output = v.get(dest_att)
196 if not plugin_output:
197 continue
198
199 res = regex.search(v.get(dest_att))
200 if not res:
201 continue
202
203 # Check to see if named fields were given
204 if res.groupdict():
205 # Store each named field as an attribute
206 for k, v in res.groupdict().items():
207 setattr(self, k, v)
208
209 # No named fields, just grab whatever matched
210 else:
211 setattr(self, att, res.group())
212
213 def __repr__(self):
214 return "<ReportHost: %s>" % self.name
215
216 def get(self, attr):
217 """Returns attribute value if it exists"""
218 try:
219 return getattr(self, attr)
220 except AttributeError:
221 return None
222
223 def find_vuln(self, **kwargs):
224 """Find a ReportItem given the search params"""
225 results = []
226
227 # Iterate through preferences
228 for r in self.vulns:
229 match = True
230 # If one of the search criteria doesn't match, set the flag
231 for k in kwargs:
232 if kwargs.get(k) != r.get(k):
233 match = False
234
235 # If it's a match, add it to results
236 if match:
237 results.append(r)
238 return results
239
240 def get_ips(self, exclude_local=True):
241 """Return a list of IPs for host"""
242 ip_list = set()
243 if re_ip.search(self.name):
244 ip_list.add(self.name)
245 if self.get('host-ip'):
246 ip_list.add(self.get('host-ip'))
247 if self.get('ips'):
248 ip_list.update(self.ips)
249
250 # If exclude_local is set, remove local IPs from list
251 if exclude_local:
252 for i in LOCAL_IP_LIST:
253 if i in ip_list:
254 ip_list.remove(i)
255
256 return list(ip_list)
257
258 def get_open_ports(self):
259 """Returns a dict of open ports found"""
260 results = {}
261
262 # Fetch results
263 vulns = self.find_vuln(plugin_id='0')
264
265 # For each port, put it in a dict
266 for v in vulns:
267 proto = v.get('protocol')
268 port = v.get('port')
269 if proto not in results:
270 results[proto] = []
271 results[proto].append(port)
272 return results
273
274 def get_name(self):
275 """Returns a friendly name for host"""
276 if re_ip.search(self.name):
277 if self.get('netbios-name'):
278 return self.get('netbios-name').lower()
279 elif self.get('host-fqdn'):
280 return self.get('host-fqdn').lower()
281 else:
282 return self.name
283 else:
284 return self.name
285
286
287 class ReportItem:
288
289 def __init__(self, xml_report_item):
290 # Make sure object is well formed
291 # ...
292
293 # Get ReportItem attributes
294 self.port = xml_report_item.attrib.get('port')
295 self.svc_name = xml_report_item.attrib.get('svc_name')
296 self.protocol = xml_report_item.attrib.get('protocol')
297 self.severity = xml_report_item.attrib.get('severity')
298 self.plugin_id = xml_report_item.attrib.get('pluginID')
299 self.plugin_name = xml_report_item.attrib.get('pluginName')
300 self.plugin_family = xml_report_item.attrib.get('pluginFamily')
301
302 # Create multi-valued atts
303 for m in MULTI_VALUED_ATTS:
304 setattr(self, m, list())
305
306 # Get optional nodes
307 for n in xml_report_item.getchildren():
308 # If it's a multi-valued att, append to list
309 if n.tag in MULTI_VALUED_ATTS:
310 v = getattr(self, n.tag)
311 v.append(n.text.strip())
312 setattr(self, n.tag, v)
313 continue
314
315 # If it's not a multi-valued att, store it as a string
316 if n.text is not None:
317 setattr(self, n.tag, n.text.strip())
318
319 xml_report_item.clear()
320
321 def __repr__(self):
322 return "<ReportItem: %s/%s %s %s>" % (self.port, self.protocol, self.plugin_id, self.plugin_name)
323
324 def get(self, attr):
325 """Returns attribute value if it exists"""
326 try:
327 return getattr(self, attr)
328 except AttributeError:
329 return None
330 # I'm Py3
33 See the file 'doc/LICENSE' for the license information
44
55 """
6 import dateutil
67
78 from faraday_plugins.plugins.plugin import PluginXMLFormat
8 import re
9 import os
10 import socket
11
12 import faraday_plugins.plugins.repo.nessus.dotnessus_v2 as dotnessus_v2
13
14
15
16 current_path = os.path.abspath(os.getcwd())
17
18 __author__ = "Francisco Amato"
19 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
20 __credits__ = ["Francisco Amato"]
9 import xml.etree.ElementTree as ET
10
11
12 __author__ = "Blas"
13 __copyright__ = "Copyright (c) 2019, Infobyte LLC"
14 __credits__ = ["Blas"]
2115 __license__ = ""
2216 __version__ = "1.0.0"
23 __maintainer__ = "Francisco Amato"
24 __email__ = "[email protected]"
17 __maintainer__ = "Blas"
18 __email__ = "[email protected]"
2519 __status__ = "Development"
2620
2721
3731 """
3832
3933 def __init__(self, output):
40 lists = output.split("\r\n")
41 i = 0
42 self.items = []
43 if re.search("Could not reach", output) is not None:
44 self.fail = True
45 return
46
47 for line in lists:
48 if i > 8:
49 item = {'link': line}
50 self.items.append(item)
51 i = i + 1
34 self.tree = ET.fromstring(output)
35 self.tag_control = []
36 for x in self.tree:
37 self.tag_control.append(x)
38 if self.tree:
39 self.policy = self.getPolicy(self.tree)
40 self.report = self.getReport(self.tree)
41 else:
42 self.policy = None
43 self.report = None
44
45 def getPolicy(self, tree):
46 policy_tree = tree.find('Policy')
47 return Policy(policy_tree)
48
49 def getReport(self, tree):
50 report_tree = tree.find('Report')
51 return Report(report_tree)
52
53
54 class Policy():
55 def __init__(self, policy_node):
56 self.node = policy_node
57 self.policy_name = self.node.find('policyName').text
58 self.preferences = self.getPreferences(self.node.find('Preferences'))
59 self.family_selection = self.getFamilySelection(self.node.find('FamilySelection'))
60 self.individual_plugin_selection = self.getIndividualPluginSelection(
61 self.node.find('IndividualPluginSelection'))
62
63 def getPreferences(self, preferences):
64 server_preferences = preferences.find('ServerPreferences')
65 plugins_preferences = preferences.find('PluginsPreferences')
66 server_preferences_all = []
67 plugins_preferences_json = {}
68 plugins_preferences_all = []
69 for sp in server_preferences:
70 sp_value = sp.find('value').text
71 sp_name = sp.find('name').text
72 server_preferences_all.append("Server Preferences name: {}, Server Preferences value: {}".format(sp_name,
73 sp_value))
74 for pp in plugins_preferences:
75 for pp_detail in pp:
76 plugins_preferences_json.setdefault(pp_detail.tag, pp_detail.text)
77 plugins_preferences_all.append(plugins_preferences_json)
78 return server_preferences_all, plugins_preferences_all
79
80 def getFamilySelection(self, family):
81 family_all = []
82 for f in family:
83 family_name = f.find('FamilyName').text
84 family_value = f.find('Status').text
85 family_all.append("Family Name: {}, Family Value: {}".format(family_name, family_value))
86 return family_all
87
88 def getIndividualPluginSelection(self, individual):
89 item_plugin = []
90 for i in individual:
91 plugin_id = i.find('PluginId').text
92 plugin_name = i.find('PluginName').text
93 plugin_family = i.find('Family').text
94 plugin_status = i.find('Status').text
95 item_plugin.append("Plugin ID: {}, Plugin Name: {}, Family: {}, Status: {}".format(plugin_id, plugin_name,
96 plugin_family,
97 plugin_status))
98 return item_plugin
99
100
101 class Report():
102 def __init__(self, report_node):
103 self.node = report_node
104 self.report_name = self.node.attrib.get('name')
105 self.report_host = self.node.find('ReportHost')
106 self.report_desc = []
107 self.report_ip = []
108 self.report_serv = []
109 self.report_json = {}
110 if self.report_host is not None:
111 for x in self.node:
112 self.report_host_ip = x.attrib.get('name')
113 self.host_properties = self.gethosttag(x.find('HostProperties'))
114 self.report_item = self.getreportitems(x.findall('ReportItem'))
115 self.report_ip.append(self.report_host_ip)
116 self.report_desc.append(self.host_properties)
117 self.report_serv.append(self.report_item)
118 self.report_json['ip'] = self.report_ip
119 self.report_json['desc'] = self.report_desc
120 self.report_json['serv'] = self.report_serv
121 self.report_json['host_end'] = self.host_properties.get('HOST_END')
122
123 else:
124 self.report_host_ip = None
125 self.host_properties = None
126 self.report_item = None
127 self.report_json = None
128
129 def getreportitems(self, items):
130 result_item = []
131
132 for item in items:
133 self.port = item.attrib.get('port')
134 self.svc_name = item.attrib.get('svc_name')
135 self.protocol = item.attrib.get('protocol')
136 self.severity = item.attrib.get('severity')
137 self.plugin_id = item.attrib.get('pluginID')
138 self.plugin_name = item.attrib.get('pluginName')
139 self.plugin_family = item.attrib.get('pluginFamily')
140 if item.find('plugin_output') is not None:
141 self.plugin_output = item.find('plugin_output').text
142 else:
143 self.plugin_output = "Not Description"
144 if item.find('description') is not None:
145 self.description = item.find('description').text
146 else:
147 self.description = "Not Description"
148
149 self.info = self.getinfoitem(item)
150 result_item.append((self.port, self.svc_name, self.protocol, self.severity, self.plugin_id,
151 self.plugin_name, self.plugin_family, self.description, self.plugin_output, self.info))
152 return result_item
153
154 def getinfoitem(self, item):
155 item_tags = {}
156 for i in item:
157 item_tags.setdefault(i.tag, i.text)
158 return item_tags
159
160 def gethosttag(self, tags):
161 host_tags = {}
162 for t in tags:
163 host_tags.setdefault(t.attrib.get('name'), t.text)
164 return host_tags
52165
53166
54167 class NessusPlugin(PluginXMLFormat):
66179 self.version = "5.2.4"
67180 self.framework_version = "1.0.1"
68181 self.options = None
69 self._current_output = None
70 self._current_path = None
71 self._command_regex = re.compile(
72 r'^(nessus|sudo nessus|\.\/nessus).*?')
73 self.host = None
74 self.port = None
75 self.protocol = None
76 self.fail = None
77
78
79 def canParseCommandString(self, current_input):
80 if self._command_regex.match(current_input.strip()):
81 return True
82 else:
83 return False
84
85 def parseOutputString(self, output, debug=False):
182
183 def parseOutputString(self, output):
86184 """
87185 This method will discard the output the shell sends, it will read it from
88186 the xml where it expects it to be present.
90188 NOTE: if 'debug' is true then it is being run from a test case and the
91189 output being sent is valid.
92190 """
93 p = dotnessus_v2.Report()
94191 try:
95 p.parse(output, from_string=True)
96 except Exception as e:
97 self.logger.error("Exception - %s", e)
98
99 for t in p.targets:
100 mac = ""
101 host = ""
102 ip = ""
103
104 if t.get('mac-address'):
105 mac = t.get('mac-address')
106 if t.get('host-fqdn'):
107 host = t.get('host-fqdn')
108 if t.get('host-ip'):
109 ip = t.get('host-ip')
110
111 if not ip:
112 if not t.get_ips():
113 continue
114 ip = t.get_ips().pop()
115
116 h_id = self.createAndAddHost(ip, t.get('operating-system'), hostnames=[host])
117
118 if self._isIPV4(ip):
119 i_id = self.createAndAddInterface(
120 h_id, ip, mac, ipv4_address=ip, hostname_resolution=[host])
121 else:
122 i_id = self.createAndAddInterface(
123 h_id, ip, mac, ipv6_address=ip, hostname_resolution=[host])
124
125 srv = {}
126 web = False
127 for v in t.vulns:
128 external_id = ""
129
130 external_id = v.get('plugin_id')
131
132 desc = ""
133 desc += v.get('description') if v.get('description') else ""
134 resolution = ""
135 resolution = v.get('solution') if v.get('solution') else ""
136
137 data = "\nOutput: " + v.get('plugin_output') if v.get('plugin_output') else ""
138
139 ref = []
140 if v.get('cve'):
141 cves = v.get('cve')
142 for cve in cves:
143 #logger.debug('Appending %s', cve.encode("utf-8"))
144 ref.append(cve.encode("utf-8").strip())
145 if v.get('bid'):
146 bids = v.get('bid')
147 for bid in bids:
148 #logger.debug('Appending %s', bid.encode("utf-8"))
149 ref.append("BID-%s" % bid.encode("utf-8").strip() )
150 if v.get('cvss_base_score'):
151 ref.append("CVSS: " + ", ".join(v.get('cvss_base_score')))
152 if v.get('xref'):
153 ref.append(", ".join(v.get('xref')))
154 if v.get('svc_name') == "general":
155 v_id = self.createAndAddVulnToHost(h_id, v.get('plugin_name'),
156 desc=desc, ref=ref, data=data, severity=v.get('severity'), resolution=resolution, external_id=external_id)
192 parser = NessusParser(output)
193 except:
194 return None
195
196 if parser.report.report_json is not None:
197 run_date = parser.report.report_json.get('host_end')
198 if run_date:
199 run_date = dateutil.parser.parse(run_date)
200 for set_info, ip in enumerate(parser.report.report_json['ip'], start=1):
201 if 'mac-address' in parser.report.report_json['desc'][set_info - 1]:
202 mac = parser.report.report_json['desc'][set_info - 1]['mac-address']
157203 else:
158
159 s_id = self.createAndAddServiceToInterface(h_id, i_id, v.get('svc_name'),
160 v.get(
161 'protocol'),
162 ports=[
163 str(v.get('port'))],
164 status="open")
165
166 web = re.search(r'^(www|http)', v.get('svc_name'))
167 if v.get('svc_name') in srv:
168 srv[v.get('svc_name')] = 1
169
170 if web:
171 v_id = self.createAndAddVulnWebToService(h_id, s_id, v.get('plugin_name'),
172 desc=desc, data=data, website=host, severity=v.get('severity'),
173 resolution=resolution, ref=ref, external_id=external_id)
204 mac = ''
205 if 'operating-system' in parser.report.report_json['desc'][set_info - 1]:
206 os = parser.report.report_json['desc'][set_info - 1]['operating-system']
207 else:
208 os = None
209
210 if 'host-ip' in parser.report.report_json['desc'][set_info - 1]:
211 ip_host = parser.report.report_json['desc'][set_info - 1]['host-ip']
212 else:
213 ip_host = "0.0.0.0"
214 if 'host-fqdn' in parser.report.report_json['desc'][set_info - 1]:
215 website = parser.report.report_json['desc'][set_info - 1]['host-fqdn']
216 host_name = []
217 host_name.append(parser.report.report_json['desc'][set_info - 1]['host-fqdn'])
218 else:
219 website = None
220 host_name = None
221
222 host_id = self.createAndAddHost(ip_host, os=os, hostnames=host_name, mac=mac)
223
224 interface_id = self.createAndAddInterface(host_id, ip, ipv6_address=ip, mac=mac)
225 cve = []
226 for serv in parser.report.report_json['serv'][set_info -1]:
227 serv_name = serv[1]
228 serv_port = serv[0]
229 serv_protocol = serv[2]
230 serv_status = serv[3]
231 external_id = serv[4]
232 serv_description = serv[7]
233 cve.append(serv[8])
234 severity = serv[3]
235 desc = serv[8]
236
237 if serv_name == 'general':
238 ref = []
239 vulnerability_name = serv[5]
240 data = serv[9]
241 if not data:
242 continue
243 if 'description' in data:
244 desc = data['description']
245 else:
246 desc = "No description"
247 if 'solution' in data:
248 resolution = data['solution']
249 else:
250 resolution = "No Solution"
251 if 'plugin_output' in data:
252 data_po = data['plugin_output']
253 else:
254 data_po = "Not data"
255
256 risk_factor = "unclassified"
257 if 'risk_factor' in data:
258 risk_factor = data['risk_factor']
259 if risk_factor == 'None':
260 risk_factor = "info" # I checked several external id and most of them were info
261
262 if 'cvss_base_score' in data:
263 cvss_base_score = "CVSS :{}".format(data['cvss_base_score'])
264 ref.append(cvss_base_score)
265 else:
266 ref = []
267
268 policyviolations = []
269 if serv[6] == 'Policy Compliance':
270 # This condition was added to support CIS Benchmark in policy violation field.
271 risk_factor = 'info'
272 bis_benchmark_data = serv[7].split('\n')
273 policy_item = bis_benchmark_data[0]
274
275 for policy_check_data in bis_benchmark_data:
276 if 'ref.' in policy_check_data:
277 ref.append(policy_check_data)
278
279 if 'FAILED' in policy_item:
280 risk_factor = 'high'
281 policyviolations.append(policy_item)
282
283 vulnerability_name = f'{serv[6]} {vulnerability_name} {policy_item}'
284
285 self.createAndAddVulnToHost(host_id,
286 vulnerability_name,
287 desc=desc,
288 severity=risk_factor,
289 resolution=resolution,
290 data=data_po,
291 ref=ref,
292 policyviolations=policyviolations,
293 external_id=external_id,
294 run_date=run_date)
174295 else:
175 v_id = self.createAndAddVulnToService(h_id, s_id, v.get('plugin_name'),
176 desc=desc, data=data, severity=v.get('severity'), resolution=resolution,
177 ref=ref, external_id=external_id)
178
179 def _isIPV4(self, ip):
180 if len(ip.split(".")) == 4:
181 return True
296 data = serv[9]
297 if not data:
298 continue
299 ref = []
300 vulnerability_name = serv[5]
301 if 'description' in data:
302 desc = data['description']
303 else:
304 desc = "No description"
305 if 'solution' in data:
306 resolution = data['solution']
307 else:
308 resolution = "No Solution"
309 if 'plugin_output' in data:
310 data_po = data['plugin_output']
311 else:
312 data_po = "Not data"
313
314 risk_factor = "info"
315 if 'risk_factor' in data:
316 risk_factor = data['risk_factor']
317
318 if risk_factor == 'None':
319 risk_factor = 'info'
320
321 if 'cvss_base_score' in data:
322 cvss_base_score = f"CVSS:{data['cvss_base_score']}"
323 ref.append(cvss_base_score)
324 if 'cvss_vector' in data:
325 cvss_vector = f"CVSSVECTOR:{data['cvss_vector']}"
326 ref.append(cvss_vector)
327 if 'see_also' in data:
328 ref.append(data['see_also'])
329 if 'cpe' in data:
330 ref.append(data['cpe'])
331 if 'xref' in data:
332 ref.append(data['xref'])
333
334 service_id = self.createAndAddServiceToInterface(host_id,
335 interface_id,
336 name=serv_name,
337 protocol=serv_protocol,
338 ports=serv_port)
339
340 if serv_name == 'www' or serv_name == 'http':
341 self.createAndAddVulnWebToService(host_id,
342 service_id,
343 name=vulnerability_name,
344 desc=desc,
345 data=data_po,
346 severity=risk_factor,
347 resolution=resolution,
348 ref=ref,
349 external_id=external_id,
350 website=website,
351 run_date=run_date)
352 else:
353 self.createAndAddVulnToService(host_id,
354 service_id,
355 name=vulnerability_name,
356 severity=risk_factor,
357 desc=desc,
358 ref=ref,
359 data=data_po,
360 external_id=external_id,
361 resolution=resolution,
362 run_date=run_date)
182363 else:
183 return False
184
185 def processCommandString(self, username, current_path, command_string):
186 return None
187
188 def setHost(self):
189 pass
190
191 def resolve(self, host):
192 try:
193 return socket.gethostbyname(host)
194 except:
195 pass
196 return host
364 ip = '0.0.0.0'
365 host_id = self.createAndAddHost(ip, hostnames="Not Information")
366 interface_id = self.createAndAddInterface(host_id, ip)
367 service_id = self.createAndAddServiceToInterface(host_id, interface_id, name="Not Information")
368 self.createAndAddVulnToService(host_id,
369 service_id,
370 name=parser.policy.policy_name,
371 desc="No Description")
197372
198373
199374 def createPlugin():
200375 return NessusPlugin()
201
202 # I'm Py3
+0
-586
faraday_plugins/plugins/repo/nessus/pynessus.py less more
0 # pynessus.py
1 # Python module to interact with a Nessus 4.x scanner via XMLRPC.
2 # http://code.google.com/p/pynessus/
3 #
4 # Copyright (C) 2010 Dustin Seibel
5 #
6 # GNU General Public Licence (GPL)
7 #
8 # This program is free software; you can redistribute it and/or modify it under
9 # the terms of the GNU General Public License as published by the Free Software
10 # Foundation; either version 2 of the License, or (at your option) any later
11 # version.
12 # This program is distributed in the hope that it will be useful, but WITHOUT
13 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15 # details.
16 # You should have received a copy of the GNU General Public License along with
17 # this program; if not, write to the Free Software Foundation, Inc., 59 Temple
18 # Place, Suite 330, Boston, MA 02111-1307 USA
19 #
20 # 2010-08-12: 0.1.0: Initial version.
21 # 2011-03-12: 0.2.1: Added a bunch of methods and robustified everything.
22 import ctypes
23 import sys
24 try:
25 from urllib2 import ProxyHandler, build_opener, urlopen, install_opener
26 from urlparse import urljoin
27 from urllib import quote
28 except ImportError:
29 from urllib.request import ProxyHandler, build_opener, urlopen, install_opener
30 from urllib.parse import urljoin, quote
31
32 import xml.etree.ElementTree as ET
33 import re
34 import datetime
35 import os
36 from random import randint
37
38 # Regex defs
39 re_unix_timestamp = re.compile('^\d{10}$')
40 re_unauthorized = re.compile('<title>200 Unauthorized</title>')
41
42 TOKEN_FILE = '.nessus_token'
43
44 # Plugin multi-value tags
45 PLUGIN_MULTI_VAL = [
46 'bid',
47 'xref',
48 'cve',
49 ]
50
51
52 class NessusServer:
53
54 def __init__(self, server, port, username, password, verbose=False):
55 self.server = server
56 self.port = port
57 self.username = username
58 self.password = password
59 self.base_url = 'https://%s:%s' % (self.server, self.port)
60 self.verbose = verbose
61 self.launched_scans = {}
62
63 # Force urllib2 to not use a proxy
64 hand = ProxyHandler({})
65 opener = build_opener(hand)
66 install_opener(opener)
67
68 self.login()
69
70 # If token file exists, use it
71 #self.token = get_token_file()
72 # if not self.check_auth():
73 # self.login()
74 # success = create_token_file(self.token)
75 # # if not success...
76
77 def login(self):
78 """Login to server"""
79 # If token file exists, try to use it
80 self.token = get_token_file()
81 if self.check_auth():
82 return True
83
84 # Make call to server
85 data = make_args(login=self.username, password=quote(self.password))
86 resp = self._call('login', data)
87 if self.verbose:
88 print(resp)
89
90 # Parse token
91 seq, status, parsed = parse_reply(resp, ['token'])
92 if 'token' in parsed:
93 self.token = parsed['token']
94 else:
95 return False
96
97 # Store it on the filesystem
98 success = create_token_file(self.token)
99 if success:
100 return True
101 else:
102 return False
103
104 def logout(self):
105 """Logout from server"""
106 data = make_args(token=self.token)
107 resp = self._call('logout', data)
108 self.token = None
109
110 def check_auth(self):
111 """Does a quick check to make sure token is still valid"""
112 if not self.token:
113 return False
114 data = make_args(token=self.token)
115 resp = self._call('scan/list', data)
116 if not resp:
117 return False
118 elif re_unauthorized.search(resp):
119 return False
120 else:
121 return True
122
123 def download_plugins(self):
124 """Downloads all plugins"""
125 data = make_args(token=self.token)
126 resp = self._call('plugins/descriptions', data)
127
128 # Get parsed data
129 keys = []
130 seq, status, parsed = parse_reply(
131 resp, keys, uniq='pluginID', start_node='pluginsList')
132 return parsed
133
134 def download_report(self, uuid, v1=False):
135 """Retrieves a report"""
136 if v1:
137 data = make_args(token=self.token, report=uuid, v1='true')
138 else:
139 data = make_args(token=self.token, report=uuid)
140 url = urljoin(self.base_url, 'file/report/download/?%s' % data)
141 req = urlopen(url)
142 resp = req.read()
143 if not check_auth(resp):
144 print("Unauthorized", file=sys.stderr)
145 return None
146 return resp
147
148 def launch_scan(self, name, policy_id, target_list):
149 """Launches scan. Returns UUID of scan."""
150 arg_targets = quote('\n'.join(target_list))
151 data = make_args(token=self.token, scan_name=quote(
152 name), policy_id=policy_id, target=arg_targets)
153 resp = self._call('/scan/new', data)
154 if self.verbose:
155 print(resp)
156
157 # Get parsed data
158 keys = ['uuid', 'owner', 'start_time', 'scan_name']
159 seq, status, parsed = parse_reply(resp, keys)
160 self.launched_scans[parsed['uuid']] = parsed
161 return parsed['uuid']
162
163 def list_plugins(self):
164 """List plugins"""
165 data = make_args(token=self.token)
166 resp = self._call('plugins/list', data)
167
168 def list_policies(self):
169 """List policies"""
170 data = make_args(token=self.token)
171 resp = self._call('policy/list', data)
172
173 # Get parsed data
174 seq, status, parsed = parse_reply(
175 resp, ['policyName', 'policyOwner', 'policyComments'], uniq='policyID')
176 return parsed
177
178 def get_policy_id(self, policy_name):
179 """Attempts to grab the policy ID for a name"""
180 pols = self.list_policies()
181 for k, v in pols.items():
182 if v.get('policyName').lower() == policy_name:
183 return k
184
185 def list_reports(self):
186 """List reports"""
187 data = make_args(token=self.token)
188 resp = self._call('report/list', data)
189
190 # Get parsed data
191 seq, status, parsed = parse_reply(
192 resp, ['name', 'readableName', 'timestamp', 'status'], uniq='name')
193 return parsed
194
195 def list_scans(self):
196 """List scans"""
197 data = make_args(token=self.token)
198 resp = self._call('scan/list', data)
199
200 # Get parsed data
201 keys = ['owner', 'start_time',
202 'completion_current', 'completion_total']
203 seq, status, parsed = parse_reply(
204 resp, keys, uniq='uuid', start_node='scans/scanList')
205 return parsed
206
207 def list_hosts(self, report_uuid):
208 """List hosts for a given report"""
209 data = make_args(token=self.token, report=report_uuid)
210 resp = self._call('report/hosts', data)
211
212 # Get parsed data
213 keys = ['hostname', 'severity']
214 seq, status, parsed = parse_reply(
215 resp, keys, uniq='hostname', start_node='hostList')
216 return parsed
217
218 def list_ports(self, report_uuid, hostname):
219 """List hosts for a given report"""
220 data = make_args(token=self.token, report=report_uuid,
221 hostname=hostname)
222 resp = self._call('report/ports', data)
223 # return resp
224
225 # Get parsed data
226 seq, status, parsed = parse_ports(resp)
227 return parsed
228
229 def list_detail(self, report_uuid, hostname, protocol, port):
230 """List details for a given host/protocol/port"""
231 data = make_args(token=self.token, report=report_uuid,
232 hostname=hostname, protocol=protocol, port=port)
233 resp = self._call('report/detail', data)
234 # return resp
235
236 # Get parsed data
237 seq, status, parsed = parse_ports(resp)
238 return parsed
239
240 def list_tags(self, report_uuid, hostname):
241 """List hosts for a given report"""
242 data = make_args(token=self.token, report=report_uuid,
243 hostname=hostname)
244 resp = self._call('report/tags', data)
245 # return resp
246
247 # Get parsed data
248 seq, status, tags = parse_tags(resp)
249 return tags
250
251 # Template methods
252 def create_template(self, name, policy_id, target_list):
253 """Creates a new scan template. Returns """
254 arg_targets = quote('\n'.join(target_list))
255 data = make_args(token=self.token, template_name=quote(
256 name), policy_id=policy_id, target=arg_targets)
257 resp = self._call('/scan/template/new', data)
258
259 def edit_template(self, template_id, name, policy_id, target_list):
260 """Edits an existing scan template."""
261 arg_targets = quote('\n'.join(target_list))
262 data = make_args(token=self.token, template=template_id, template_name=quote(
263 name), policy_id=policy_id, target=arg_targets)
264 resp = self._call('/scan/template/edit', data)
265
266 def list_templates(self):
267 """List templates"""
268 data = make_args(token=self.token)
269 resp = self._call('scan/list', data)
270
271 # Get parsed data
272 keys = ['policy_id', 'readableName', 'owner', 'startTime']
273 seq, status, parsed = parse_reply(
274 resp, keys, uniq='name', start_node='templates')
275 return parsed
276
277 def _call(self, func_url, args):
278 url = urljoin(self.base_url, func_url)
279 if self.verbose:
280 print("URL: '%s'" % url)
281 print("POST: '%s'" % args)
282 req = urlopen(url, args)
283 resp = req.read()
284 if not check_auth(resp):
285 print("200 Unauthorized", file=sys.stderr)
286 return resp
287 return resp
288
289
290 def check_auth(resp_str):
291 """Checks for an unauthorized message in HTTP response."""
292 if re_unauthorized.search(resp_str):
293 return False
294 else:
295 return True
296
297
298 def create_token_file(token, token_file=TOKEN_FILE):
299 """Creates token file"""
300 if not token:
301 return False
302 # Write to file
303 try:
304 fout = open(token_file, 'w')
305 except IOError:
306 return False
307 fout.write(token)
308 fout.close()
309
310 # Confirm the file was created and has the right token
311 new_token = get_token_file(token_file)
312 if new_token != token:
313 return False
314 else:
315 return True
316
317
318 def get_token_file(token_file=TOKEN_FILE):
319 """Checks token from file"""
320 if not os.path.isfile(token_file):
321 return False
322 fin = open(token_file, 'r')
323 token = fin.read()
324 fin.close()
325 return token
326
327
328 def convert_date(unix_timestamp):
329 """Converts UNIX timestamp to a datetime object"""
330 # try:
331 # return datetime.datetime.fromtimestamp(float(unix_timestamp))
332 # except Exception:
333 # return unix_timestamp
334 return datetime.datetime.fromtimestamp(float(unix_timestamp))
335
336
337 def parse_reply(xml_string, key_list, start_node=None, uniq=None):
338 """Gets all key/value pairs from XML"""
339 ROOT_NODES = ['seq', 'status', 'contents']
340 if not xml_string:
341 return (0, 'Not a valid string', {})
342
343 # Parse xml
344 try:
345 xml = ET.fromstring(xml_string)
346 except ET.ExpatError:
347 return (0, 'Cannot parse XML', {})
348
349 # Make sure it looks like what we expect it to be
350 if [t.tag for t in xml.getchildren()] != ROOT_NODES:
351 return (0, 'XML not formatted correctly', {})
352
353 # Get seq and status
354 seq = xml.findtext('seq')
355 status = xml.findtext('status')
356
357 # If start node was given, append it to contents node
358 if start_node:
359 start_node = 'contents/%s' % start_node
360 else:
361 start_node = 'contents'
362 if not xml.find(start_node):
363 return (seq, 'start_node not found', {})
364
365 # If a unique value was given, make sure it is a valid tag
366 if uniq:
367 found = False
368 for x in xml.find(start_node).getiterator():
369 if x.tag == uniq:
370 found = True
371 break
372 if not found:
373 return (seq, 'uniq not a valid tag', {})
374
375 # Parse keys from contents
376 d = {}
377 for x in xml.find(start_node).getiterator():
378 if uniq:
379 # If tag is a unique field, start a new dict
380 if x.tag == uniq:
381 d[x.text] = {}
382 k = x.text
383
384 # Store key/value pair if tag is in key list or if no key list was
385 # given
386 if not x.text:
387 continue
388 if ((x.tag in key_list) or (not key_list)) and x.text.strip():
389 # If the tag has the word time and the value is a UNIX
390 # timestamp, convert it
391 if 'time' in x.tag and re_unix_timestamp.search(x.text):
392 d[k][x.tag] = convert_date(x.text)
393 else:
394 # Check to see if this is multi-valued
395 if x.tag in PLUGIN_MULTI_VAL:
396 if x.tag in d[k]:
397 d[k][x.tag].append(x.text)
398 else:
399 d[k][x.tag] = [x.text]
400 else:
401 d[k][x.tag] = x.text
402
403 else:
404 # Store key/value pair if tag is in key list
405 if not x.text:
406 continue
407 if ((x.tag in key_list) or (not key_list)) and x.text.strip():
408 # If the tag has the word time and the value is a UNIX
409 # timestamp, convert it
410 if 'time' in x.tag and re_unix_timestamp.search(x.text):
411 d[x.tag] = convert_date(x.text)
412 else:
413 d[x.tag] = x.text
414 return (seq, status, d)
415
416
417 def parse_reply_orig(xml_string, key_list, start_node=None, uniq=None):
418 """Gets all key/value pairs from XML"""
419 ROOT_NODES = ['seq', 'status', 'contents']
420 if not xml_string:
421 return (0, 'Not a valid string', {})
422
423 # Parse xml
424 try:
425 xml = ET.fromstring(xml_string)
426 except ET.ExpatError:
427 return (0, 'Cannot parse XML', {})
428
429 # Make sure it looks like what we expect it to be
430 if [t.tag for t in xml.getchildren()] != ROOT_NODES:
431 return (0, 'XML not formatted correctly', {})
432
433 # Get seq and status
434 seq = xml.findtext('seq')
435 status = xml.findtext('status')
436
437 # If start node was given, append it to contents node
438 if start_node:
439 start_node = 'contents/%s' % start_node
440 else:
441 start_node = 'contents'
442 if not xml.find(start_node):
443 return (seq, 'start_node not found', {})
444
445 # If a unique value was given, make sure it is a valid tag
446 if uniq:
447 found = False
448 for x in xml.find(start_node).getiterator():
449 if x.tag == uniq:
450 found = True
451 break
452 if not found:
453 return (seq, 'uniq not a valid tag', {})
454
455 # Parse keys from contents
456 d = {}
457 for x in xml.find(start_node).getiterator():
458 if uniq:
459 # If tag is a unique field, start a new dict
460 if x.tag == uniq:
461 d[x.text] = {}
462 k = x.text
463
464 # Store key/value pair if tag is in key list
465 if x.tag in key_list:
466 # If the tag has the word time and the value is a UNIX
467 # timestamp, convert it
468 if 'time' in x.tag and re_unix_timestamp.search(x.text):
469 d[k][x.tag] = convert_date(x.text)
470 else:
471 d[k][x.tag] = x.text
472
473 else:
474 # Store key/value pair if tag is in key list
475 if x.tag in key_list:
476 # If the tag has the word time and the value is a UNIX
477 # timestamp, convert it
478 if 'time' in x.tag and re_unix_timestamp.search(x.text):
479 d[x.tag] = convert_date(x.text)
480 else:
481 d[x.tag] = x.text
482 return (seq, status, d)
483
484
485 def parse_ports(xml_string):
486 """Parses ports from report/ports"""
487 ROOT_NODES = ['seq', 'status', 'contents']
488 if not xml_string:
489 return (0, 'Not a valid string', {})
490
491 # Parse xml
492 try:
493 xml = ET.fromstring(xml_string)
494 except ET.ExpatError:
495 return (0, 'Cannot parse XML', {})
496
497 # Make sure it looks like what we expect it to be
498 if [t.tag for t in xml.getchildren()] != ROOT_NODES:
499 return (0, 'XML not formatted correctly', {})
500
501 # Get seq and status
502 seq = xml.findtext('seq')
503 status = xml.findtext('status')
504
505 # Parse ports
506 d = {'tcp': {}, 'udp': {}, 'icmp': {}}
507 for t in xml.findall('contents/portList/port'):
508 port_d = {}
509 prot = t.findtext('protocol')
510 num = t.findtext('portNum')
511
512 # Get additional attributes
513 port_d['severity'] = t.findtext('severity')
514 port_d['svcName'] = t.findtext('svcName')
515
516 d[prot][num] = port_d
517 return (seq, status, d)
518
519
520 def parse_tags(xml_string):
521 """Parses tags from report/tags"""
522 ROOT_NODES = ['seq', 'status', 'contents']
523 if not xml_string:
524 return (0, 'Not a valid string', {})
525
526 # Parse xml
527 try:
528 xml = ET.fromstring(xml_string)
529 except ET.ExpatError:
530 return (0, 'Cannot parse XML', {})
531
532 # Make sure it looks like what we expect it to be
533 if [t.tag for t in xml.getchildren()] != ROOT_NODES:
534 return (0, 'XML not formatted correctly', {})
535
536 # Get seq and status
537 seq = xml.findtext('seq')
538 status = xml.findtext('status')
539
540 # Parse tags
541 d = {}
542 for t in xml.findall('contents/tags/tag'):
543 k = t.findtext('name')
544 v = t.findtext('value')
545 d[k] = v
546 return (seq, status, d)
547
548
549 def make_args(**kwargs):
550 """Returns arg list suitable for GET or POST requests"""
551 args = []
552 for k in kwargs:
553 args.append('%s=%s' % (k, str(kwargs[k])))
554
555 # Add a random number
556 seq = randint(1, 1000)
557 args.append('seq=%d' % seq)
558
559 return '&'.join(args)
560
561
562 def zerome(string):
563 # taken from http://www.codexon.com/posts/clearing-passwords-in-memory-with-python
564 # to be used to secure the password in memory
565 # find the header size with a dummy string
566 temp = "finding offset"
567 header = ctypes.string_at(id(temp), sys.getsizeof(temp)).find(temp)
568
569 location = id(string) + header
570 size = sys.getsizeof(string) - header
571
572 # Check platform
573 if 'windows' in sys.platform.lower():
574 memset = ctypes.cdll.msvcrt.memset
575 else:
576 # For Linux, use the following. Change the 6 to whatever it is on your
577 # computer.
578 memset = ctypes.CDLL("libc.so.6").memset
579
580 print("Clearing 0x%08x size %i bytes" % (location, size))
581
582 memset(location, 0, size)
583
584
585 # I'm Py3
1717
1818 def __init__(self):
1919 super().__init__()
20 self.id = "Netdiscover"
21 self.name = "netdiscover"
20 self.id = "Netdiscover"
21 self.name = "netdiscover"
2222 self.plugin_version = "0.0.1"
23 self.version = "1.0.0"
24 self._command_regex = re.compile(r'^(sudo netdiscover|netdiscover).*?')
23 self.version = "1.0.0"
24 self._command_regex = re.compile(r'^(sudo netdiscover|netdiscover)\s+.*?')
2525
26 def parseOutputString(self, output, debug=False):
26 def parseOutputString(self, output):
2727 #regexp get ip, mac and hostname
2828 reg = re.findall(r"(([0-9]+\.?){4})\s+(([0-9a-f]+\:?){6})((\s+[0-9]+){2})(.*)", output)
2929
3939
4040 return True
4141
42 def processCommandString(self, username, current_path, command_string):
43 return None
4442
4543
4644 def createPlugin():
33 See the file 'doc/LICENSE' for the license information
44
55 """
6 import re
7 from bs4 import BeautifulSoup
68 from faraday_plugins.plugins.plugin import PluginXMLFormat
7 import re
8 import os
9 import socket
10 from bs4 import BeautifulSoup
9 from faraday_plugins.plugins.plugins_utils import resolve_hostname
1110
1211 try:
1312 import xml.etree.cElementTree as ET
1918
2019 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
2120
22 current_path = os.path.abspath(os.getcwd())
2321
2422 __author__ = "Francisco Amato"
2523 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
121119 self.resolution = self.get_text_from_subnode("actionsToTake")
122120 self.request = self.get_text_from_subnode("rawrequest")
123121 self.response = self.get_text_from_subnode("rawresponse")
124 if self.response:
125 self.response = self.response.encode(encoding,errors="backslashreplace").decode(encoding)
126 if self.request:
127 self.request = self.request.encode(encoding,errors="backslashreplace").decode(encoding)
128 if self.reference:
129 self.reference = self.reference.encode(encoding,errors="backslashreplace").decode(encoding)
130
131
132122 self.kvulns = []
133123 for v in self.node.findall("knownvulnerabilities/knownvulnerability"):
134124 self.node = v
158148 if self.owasp:
159149 self.ref.append("OWASP-" + self.owasp)
160150 if self.reference:
161 self.ref.extend(list(set(re.findall('https?://(?:[-\w.]|(?:%[\da-fA-F]{2}))+', self.reference))))
151 self.ref.extend(sorted(set(re.findall('https?://(?:[-\w.]|(?:%[\da-fA-F]{2}))+', self.reference))))
162152 if self.cvss:
163153 self.ref.append(self.cvss)
164154
202192 self.version = "Netsparker 3.1.1.0"
203193 self.framework_version = "1.0.0"
204194 self.options = None
205 self._current_output = None
206 self._command_regex = re.compile(
207 r'^(sudo netsparker|\.\/netsparker).*?')
208
209
210 def resolve(self, host):
211 try:
212 return socket.gethostbyname(host)
213 except:
214 pass
215 return host
216
217 def parseOutputString(self, output, debug=False):
218
195
196 def parseOutputString(self, output):
219197 parser = NetsparkerXmlParser(output)
220198 first = True
221199 for i in parser.items:
222200 if first:
223 ip = self.resolve(i.hostname)
201 ip = resolve_hostname(i.hostname)
224202 h_id = self.createAndAddHost(ip, hostnames=[ip])
225203
226204 s_id = self.createAndAddServiceToHost(h_id, str(i.port),
245223
246224 del parser
247225
248 def processCommandString(self, username, current_path, command_string):
249 return None
250
251226
252227 def createPlugin():
253228 return NetsparkerPlugin()
33 See the file 'doc/LICENSE' for the license information
44
55 """
6 import re
7 from urllib.parse import urlparse
68 from faraday_plugins.plugins.plugin import PluginXMLFormat
7 import re
8 import os
9 import socket
9 from faraday_plugins.plugins.plugins_utils import resolve_hostname
1010
1111 try:
1212 import xml.etree.cElementTree as ET
1818
1919 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
2020
21 current_path = os.path.abspath(os.getcwd())
2221
2322 __author__ = "Francisco Amato"
2423 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
3029 __status__ = "Development"
3130
3231
33 def cleaner_unicode(string):
34 if string is not None:
35 return string.encode('ascii', errors='backslashreplace')
32
33 def get_urls(string):
34 if isinstance(string, bytes):
35 string_decode = string.decode("utf-8")
36 urls = re.findall('(?:(?:https?|ftp):\/\/)?[\w/\-?=%.]+\.[\w/\-?=%.]+', string_decode)
3637 else:
37 return string
38
39 def cleaner_results(string):
40
41 try:
42 q = re.compile(r'<.*?>', re.IGNORECASE)
43 return re.sub(q, '', string)
44
45 except:
46 return ''
47
48 def get_urls(string):
49 urls = re.findall(r'href=[\'"]?([^\'" >]+)', string)
38 urls = re.findall('(?:(?:https?|ftp):\/\/)?[\w/\-?=%.]+\.[\w/\-?=%.]+', string)
5039 return urls
5140
5241
6352
6453 def __init__(self, xml_output):
6554 self.filepath = xml_output
66
6755 tree = self.parse_xml(xml_output)
6856 if tree:
6957 self.items = [data for data in self.get_items(tree)]
8472 except SyntaxError as err:
8573 self.devlog("SyntaxError: %s. %s" % (err, xml_output))
8674 return None
87
8875 return tree
8976
9077 def get_items(self, tree):
10794 return "high"
10895 return severity
10996
110 def __init__(self, item_node):
97 def __init__(self, item_node, encoding="ascii"):
11198 self.node = item_node
112 self.url = self.get_text_from_subnode("url")
113
114 host = re.search(
115 "(http|https|ftp)\://([a-zA-Z0-9\.\-]+(\:[a-zA-Z0-9\.&amp;%\$\-]+)*@)*((25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])|localhost|([a-zA-Z0-9\-]+\.)*[a-zA-Z0-9\-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))[\:]*([0-9]+)*([/]*($|[a-zA-Z0-9\.\,\?\'\\\+&amp;%\$#\=~_\-]+)).*?$", self.url)
116
117 self.protocol = host.group(1)
118 self.hostname = host.group(4)
119 self.port = 80
120
121 if self.protocol == 'https':
122 self.port = 443
123 if host.group(11) is not None:
124 self.port = host.group(11)
125
99 self.url = urlparse(self.get_text_from_subnode("url"))
100 self.protocol = self.url.scheme
101 self.hostname = self.url.netloc
102 self.port = self.url.port
103 if self.port is None:
104 self.port = '80'
126105 self.type = self.get_text_from_subnode("type")
127106 self.name = self.get_text_from_subnode("name")
128107 self.severity = self.re_map_severity(self.get_text_from_subnode("severity"))
129108 self.certainty = self.get_text_from_subnode("certainty")
130
131
132109 self.node = item_node.find("http-request")
133110 self.method = self.get_text_from_subnode("method")
134111 self.request = self.get_text_from_subnode("content")
135
136 #print self.node
137112 self.param = ""
138113 self.paramval = ""
139114 for p in self.node.findall("parameters/parameter"):
142117
143118 self.node = item_node.find("http-response")
144119 self.response = self.get_text_from_subnode("content")
145
146120 self.extra = []
147121 for v in item_node.findall("extra-information/info"):
148122 self.extra.append(v.get('name') + ":" + v.get('value') )
158132
159133 self.ref = []
160134 if self.cwe:
161 self.ref.append("CWE-" + self.cwe)
135 self.ref.append("CWE-{}".format(self.cwe))
162136 if self.owasp:
163 self.ref.append("OWASP-" + self.owasp)
137 self.ref.append("OWASP-{}".format(self.owasp))
164138
165139 self.node = item_node
166140 self.remedyreferences = self.get_text_from_subnode("remedy-references")
172146 for u in get_urls(self.externalreferences):
173147 self.ref.append(u)
174148
175 self.impact = cleaner_results(self.get_text_from_subnode("impact"))
176 self.remedialprocedure = cleaner_results(self.get_text_from_subnode("remedial-procedure"))
177 self.remedialactions = cleaner_results(self.get_text_from_subnode("remedial-actions"))
178 self.exploitationskills = cleaner_results(self.get_text_from_subnode("exploitation-skills"))
179 self.proofofconcept = cleaner_results(self.get_text_from_subnode("proof-of-concept"))
180
181 self.resolution = self.remedialprocedure
182 self.resolution += "\nRemedial Actions: " + self.remedialactions if self.remedialactions is not None else ""
183
184
185 self.desc = cleaner_results(self.get_text_from_subnode("description"))
186 self.desc += "\nImpact: " + self.impact if self.impact else ""
187 self.desc += "\nExploitation Skills: " + self.exploitationskills if self.exploitationskills else ""
188 self.desc += "\nProof of concept: " + self.proofofconcept if self.proofofconcept else ""
189 self.desc += "\nWASC: " + self.wasc if self.wasc else ""
190 self.desc += "\nPCI31: " + self.pci if self.pci else ""
191 self.desc += "\nPCI32: " + self.pci2 if self.pci2 else ""
192 self.desc += "\nCAPEC: " + self.capec if self.capec else ""
193 self.desc += "\nHIPA: " + self.hipaa if self.hipaa else ""
194 self.desc += "\nExtra: " + "\n".join(self.extra) if self.extra else ""
149 self.impact = self.get_text_from_subnode("impact")
150 self.remedialprocedure = self.get_text_from_subnode("remedial-procedure")
151 self.remedialactions = self.get_text_from_subnode("remedial-actions")
152 self.exploitationskills = self.get_text_from_subnode("exploitation-skills")
153 self.proofofconcept = self.get_text_from_subnode("proof-of-concept")
154
155 self.resolution = "Remerdial Procedure: {} \nRemedial Actions: {}".format(self.remedialprocedure,
156 self.remedialactions)
157
158 self.desc = self.get_text_from_subnode("description")
159 self.desc = "\nImpact: {} \nExploitation Skills: {} \nProof of concept: {} \nWASC: {} \nPCI31: {} \nPCI32: {}" \
160 " \nCAPEC: {} \nHIPA: {} \nExtra: {}".format(self.impact, self.exploitationskills,
161 self.proofofconcept, self.wasc, self.pci, self.pci2,
162 self.capec, self.hipaa, self.extra)
195163
196164 def get_text_from_subnode(self, subnode_xpath_expr):
197165 """
202170 if self.node:
203171 sub_node = self.node.find(subnode_xpath_expr)
204172 if sub_node is not None:
205 if sub_node.text is not None:
206 return cleaner_unicode(sub_node.text)
207
208 return ""
173 return sub_node.text
174 return None
209175
210176
211177 class NetsparkerCloudPlugin(PluginXMLFormat):
222188 self.version = "NetsparkerCloud"
223189 self.framework_version = "1.0.0"
224190 self.options = None
225 self._current_output = None
226 self._command_regex = re.compile(
227 r'^(sudo netsparkercloud|\.\/netsparkercloud).*?')
228
229
230 def resolve(self, host):
231 try:
232 return socket.gethostbyname(host)
233 except:
234 pass
235 return host
236191
237192 def parseOutputString(self, output, debug=False):
238
239193 parser = NetsparkerCloudXmlParser(output)
240194 first = True
241195 for i in parser.items:
242196 if first:
243 ip = self.resolve(i.hostname)
197 ip = resolve_hostname(i.hostname)
244198 h_id = self.createAndAddHost(ip)
245 i_id = self.createAndAddInterface(
246 h_id, ip, ipv4_address=ip, hostname_resolution=[i.hostname])
247
248 s_id = self.createAndAddServiceToInterface(h_id, i_id, str(i.port),
249 str(i.protocol),
250 ports=[str(i.port)],
251 status="open")
199 i_id = self.createAndAddInterface(h_id, ip, ipv4_address=ip, hostname_resolution=[i.hostname])
200 s_id = self.createAndAddServiceToInterface(h_id, i_id, i.protocol, ports=[i.port], status="open")
252201
253202 first = False
254
255203 v_id = self.createAndAddVulnWebToService(h_id, s_id, i.name, ref=i.ref, website=i.hostname,
256 severity=i.severity, desc=i.desc, path=i.url, method=i.method,
204 severity=i.severity, desc=i.desc, path=i.url.path, method=i.method,
257205 request=i.request, response=i.response, resolution=i.resolution,
258206 pname=i.param)
259
260207 del parser
261208
262 def processCommandString(self, username, current_path, command_string):
263 return None
264209
265210 def setHost(self):
266211 pass
268213
269214 def createPlugin():
270215 return NetsparkerCloudPlugin()
271
55 """
66 from faraday_plugins.plugins.plugin import PluginXMLFormat
77 import re
8 import os
98
109 try:
1110 import xml.etree.cElementTree as ET
1918
2019 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
2120
22 current_path = os.path.abspath(os.getcwd())
2321
2422 __author__ = "Micaela Ranea Sanchez"
2523 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
6462 try:
6563 tree = ET.fromstring(xml_output)
6664 except SyntaxError as err:
67 print("SyntaxError: %s. %s" % (err, xml_output))
6865 return None
6966
7067 return tree
171168 if item.tag == 'exploits':
172169 for exploit in list(item):
173170 if exploit.get('title') and exploit.get('link') and exploit.get('type') \
174 and exploit.get('sklLevel'):
175 title = exploit.get('title').encode(
176 "ascii", errors="backslashreplace").strip()
177 link = exploit.get('link').encode(
178 "ascii", errors="backslashreplace").strip()
179 type = exploit.get('type').encode(
180 "ascii", errors="backslashreplace").strip()
181 skillLevel = exploit.get('sklLevel').encode(
182 "ascii", errors="backslashreplace").strip()
183 vuln['refs'].append(title + b' ' + link + b' ' + type + b' ' + skillLevel)
171 and exploit.get('skillLevel'):
172 title = exploit.get('title').strip()
173 link = exploit.get('link').strip()
174 type = exploit.get('type').strip()
175 skillLevel = exploit.get('skillLevel').strip()
176 vuln['refs'].append(" ".join([title, link, type, skillLevel]))
184177 if item.tag == 'malware':
185178 for names in item.findall("name"):
186179 nameMalware = names.text
188181 if item.tag == 'references':
189182 for ref in list(item):
190183 if ref.text:
191 rf = ref.text.encode(
192 "ascii", errors="backslashreplace").strip()
184 rf = ref.text.strip()
193185 vuln['refs'].append(rf)
194186 if item.tag == 'solution':
195187 for htmlType in list(item):
216208 host['name'] = node.get('address')
217209 host['mac'] = node.get('hardware-address')
218210 host['hostnames'] = list()
219 host['os'] = list()
211 host['os'] = ""
220212 host['services'] = list()
221 host['fingerprints'] = list()
222 host['fingerprints_software'] = list()
223213 host['vulns'] = self.parse_tests_type(node, vulns)
224 host['scan-template'] = node.get('scan-template')
225 host['scan-name'] = node.get('scan-name')
226 host['scan-importance'] = node.get('scan-importance')
227 host['risk-score'] = node.get('risk-score')
228214
229215 for names in node.iter('names'):
230216 for name in list(names):
232218
233219 for fingerprints in node.iter('fingerprints'):
234220 for os_data in fingerprints.iter('os'):
235 data = {
236 'certainty': os_data.get('certainty'),
237 'vendor': os_data.get('vendor'),
238 'family': os_data.get('family'),
239 'product': os_data.get('product'),
240 'version': os_data.get('version'),
241 'arch': os_data.get('arch'),
242 'device-class': os_data.get('device-class'),
243 }
244 host['os'].append(data)
245
246 for fingerprints_tag in fingerprints.iter('fingerprint'):
247 data_fingerprints_tag = {
248 'certainty': fingerprints_tag.get('certainty'),
249 'product': fingerprints_tag.get('product'),
250 'version': fingerprints_tag.get('version'),
251 }
252 host['fingerprints'].append(data_fingerprints_tag)
253
221 os_name = os_data.get('product')
222 if os_name:
223 host['os'] = os_name
254224 for endpoints in node.iter('endpoints'):
255225 for endpoint in list(endpoints):
256226 svc = {
268238 if "banner" in config.get('name'):
269239 svc['version'] = config.get('name')
270240 host['services'].append(svc)
271
272 for softwaretag in node.iter('software'):
273 for soft_data in softwaretag.iter('fingerprint'):
274 data_soft = {
275 'certainty': soft_data.get('certainty'),
276 'vendor': soft_data.get('vendor'),
277 'family': soft_data.get('family'),
278 'product': soft_data.get('product'),
279 'version': soft_data.get('version'),
280 }
281 host['fingerprints_software'].append(data_soft)
282241 hosts.append(host)
283242
284243 return hosts
298257 self.version = "Nexpose Enterprise 5.7.19"
299258 self.framework_version = "1.0.0"
300259 self.options = None
301 self._current_output = None
302 self._command_regex = re.compile(r'^(sudo nexpose|\.\/nexpose).*?')
303260
304261 def parseOutputString(self, output, debug=False):
305262
306263 parser = NexposeFullXmlParser(output)
307264
308265 for item in parser.items:
309 h_id = self.createAndAddHost(item['name'], item['os'], hostnames=item['hostnames'],
310 scan_template=item['scan-template'], site_name=item['scan-name'],
311 site_importance=item['scan-importance'], risk_score=item['risk-score'],
312 fingerprints=item['fingerprints'],
313 fingerprints_software=item['fingerprints_software']
314 )
266 h_id = self.createAndAddHost(item['name'], item['os'], hostnames=item['hostnames'])
315267 pattern = '([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$'
316268 if not item['mac']:
317269 item['mac'] = '0000000000000000'
324276 item['name'],
325277 mac=item['mac'],
326278 ipv4_address=item['name'],
327 hostname_resolution=item['hostnames'],
328 scan_template=item['scan-template'],
329 site_name=item['scan-name'],
330 site_importance=item['scan-importance'],
331 risk_score=item['risk-score'],
332 fingerprints=item['fingerprints'],
333 fingerprints_software=item['fingerprints_software'],
279 hostname_resolution=item['hostnames']
334280 )
335281 else:
336282 i_id = self.createAndAddInterface(
348294 v['desc'],
349295 v['refs'],
350296 v['severity'],
351 v['resolution'],
352 v['vulnerable_since'],
353 v['scan_id'],
354 v['pci']
297 v['resolution']
355298 )
356299
357300 for s in item['services']:
378321 v['refs'],
379322 v['severity'],
380323 v['resolution'],
381 v['risk'],
382324 path=v.get('path', ''))
383325 else:
384326 v_id = self.createAndAddVulnToService(
388330 v['desc'],
389331 v['refs'],
390332 v['severity'],
391 v['resolution'],
392 v['risk']
333 v['resolution']
393334 )
394335
395336 del parser
396337
397 def processCommandString(self, username, current_path, command_string):
398 return None
399338
400339 def setHost(self):
401340 pass
33 See the file 'doc/LICENSE' for the license information
44 """
55 import re
6 import os
7 import random
86 from html.parser import HTMLParser
97 from faraday_plugins.plugins.plugin import PluginXMLFormat
108 from faraday_plugins.plugins import plugins_utils
2018
2119 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
2220
23 current_path = os.path.abspath(os.getcwd())
2421
2522 __author__ = "Francisco Amato"
2623 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
257254 self.plugin_version = "0.0.2"
258255 self.version = "2.1.5"
259256 self.options = None
260 self._current_output = None
261257 self.parent = None
258 self._use_temp_file = True
259 self._temp_file_extension = "xml"
260 self.xml_arg_re = re.compile(r"^.*(-output\s*[^\s]+).*$")
262261 self._command_regex = re.compile(
263 r'^(sudo nikto|nikto|sudo nikto\.pl|nikto\.pl|perl nikto\.pl|\.\/nikto\.pl|\.\/nikto).*?')
262 r'^(sudo nikto|nikto|sudo nikto\.pl|nikto\.pl|perl nikto\.pl|\.\/nikto\.pl|\.\/nikto)\s+.*?')
264263 self._completition = {
265264 "": "",
266265 "-ask+": "Whether to ask about submitting updates",
343342
344343 del parser
345344
346 xml_arg_re = re.compile(r"^.*(-output\s*[^\s]+).*$")
345
347346
348347 def processCommandString(self, username, current_path, command_string):
349348 """
350349 Adds the -oX parameter to get xml output to the command string that the
351350 user has set.
352351 """
353 self._output_file_path = os.path.join(
354 self.data_path,
355 "%s_%s_output-%s.xml" % (
356 self.get_ws(),
357 self.id,
358 random.uniform(1, 10)
359 )
360 )
352 super().processCommandString(username, current_path, command_string)
361353
362354 arg_match = self.xml_arg_re.match(command_string)
363355
364356 if arg_match is None:
365 return re.sub(r"(^.*?nikto(\.pl)?)",
366 r"\1 -output %s -Format XML" % self._output_file_path,
367 command_string)
357 return re.sub(r"(^.*?nikto(\.pl)?)", r"\1 -output %s -Format XML" % self._output_file_path, command_string)
368358 else:
369359 data = re.sub(" \-Format XML", "", command_string)
370 return re.sub(arg_match.group(1),
371 r"-output %s -Format XML" % self._output_file_path,
372 data)
360 return re.sub(arg_match.group(1), r"-output %s -Format XML" % self._output_file_path, data)
373361
374362 def setHost(self):
375363 pass
77 from faraday_plugins.plugins.plugin import PluginXMLFormat
88 import re
99 import os
10 import sys
11 import random
1210
1311
1412 try:
401399 name = service_node.get("name")
402400 self.name = name if name else 'unknown'
403401
402 self.tunnel = service_node.get("tunnel")
403 if self.tunnel == "ssl":
404 if self.name == "http":
405 self.name = "https"
406 elif self.name == 'imap':
407 self.name = 'imaps'
408 elif self.name == 'pop3':
409 self.name = 'pop3s'
410
404411 product = service_node.get("product")
405412 self.product = product if product else 'unknown'
406413
430437 self.framework_version = "1.0.0"
431438 self.options = None
432439 self._current_output = None
433 self._command_regex = re.compile(r'^(sudo nmap|nmap|\.\/nmap).*?')
434
435
436
440 self._command_regex = re.compile(r'^(sudo nmap|nmap|\.\/nmap)\s+.*?')
441 self._use_temp_file = True
442 self._temp_file_extension = "xml"
437443 self.xml_arg_re = re.compile(r"^.*(-oX\s*[^\s]+).*$")
438444 self.addSetting("Scan Technique", str, "-sS")
439445
506512 description=srvname)
507513
508514 for v in port.vulns:
509 severity = 0
515 severity = "info"
510516 desc = v.desc
511517 refs = v.refs
512518
513 if re.search(r"VULNERABLE", desc):
519 if re.search(r"(?<!NOT )VULNERABLE", desc):
514520 severity = "high"
515521 if re.search(r"ERROR", desc):
516522 severity = "unclassified"
537543 severity=severity,
538544 external_id=v.name)
539545 del parser
540 return True
541546
542547 def processCommandString(self, username, current_path, command_string):
543548 """
544549 Adds the -oX parameter to get xml output to the command string that the
545550 user has set.
546551 """
547
548 self._output_file_path = os.path.join(
549 self.data_path,
550 "%s_%s_output-%s.xml" % (
551 self.get_ws(),
552 self.id,
553 random.uniform(1, 10))
554 )
555
552 super().processCommandString(username, current_path, command_string)
556553 arg_match = self.xml_arg_re.match(command_string)
557
558554 if arg_match is None:
559555 return re.sub(r"(^.*?nmap)",
560556 r"\1 -oX %s" % self._output_file_path,
564560 r"-oX %s" % self._output_file_path,
565561 command_string)
566562
567 def setHost(self):
568 pass
569
570
571563 def createPlugin():
572564 return NmapPlugin()
573565
44
55 """
66 import re
7 import os
87 from collections import defaultdict
9
108 from copy import copy
119
1210 try:
2220
2321 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
2422
25 current_path = os.path.abspath(os.getcwd())
2623
2724 __author__ = "Francisco Amato"
2825 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
7976 """
8077 try:
8178 report = tree.find('report')
82 results = report.findall('results')
83 if results:
84 nodes = report.findall('results')[0]
79 if report:
80 results = report.findall('results')
81 if results:
82 nodes = report.findall('results')[0]
83 else:
84 nodes = tree.findall('result')
8585 else:
8686 nodes = tree.findall('result')
87
8788 for node in nodes:
8889 try:
8990 yield Item(node, hosts)
9091 except Exception as e:
91 self.logger.error("Error generating Item from %s [%s]", node.attrib, e)
92 self.logger.error("Error generating Iteem from %s [%s]", node.attrib, e)
93
9294 except Exception as e:
9395 self.logger.error("Tag not found: %s", e)
9496
185187 if not self.port:
186188 self.service = info[0]
187189 else:
188 host_details = hosts[self.host].get('details')
189 self.service = self.get_service(port_string, self.port, host_details)
190 if hosts:
191 host_details = hosts[self.host].get('details')
192 self.service = self.get_service(port_string, self.port, host_details)
193 else:
194 self.service = "Not Service"
190195 self.nvt = self.node.findall('nvt')[0]
191196 self.node = self.nvt
192197 self.id = self.node.get('oid')
316321
317322 def __init__(self):
318323 super().__init__()
319 self.identifier_tag = "report"
324 self.identifier_tag = ["report", "get_results_response"]
320325 self.id = "Openvas"
321326 self.name = "Openvas XML Output Plugin"
322327 self.plugin_version = "0.3"
323328 self.version = "9.0.3"
324329 self.framework_version = "1.0.0"
325330 self.options = None
326 self._current_output = None
327 self.target = None
328 self._command_regex = re.compile(
329 r'^(openvas|sudo openvas|\.\/openvas).*?')
330
331331
332332 def report_belongs_to(self, **kwargs):
333333 if super().report_belongs_to(**kwargs):
357357 hostnames=values['hostnames']
358358 )
359359 ids[ip] = h_id
360
361360 for item in parser.items:
361
362362 if item.name is not None:
363363 ref = []
364364 if item.cve:
365365 cves = item.cve.split(',')
366366 for cve in cves:
367 ref.append(cve.encode("utf-8").strip())
367 ref.append(cve.strip())
368368 if item.bid:
369369 bids = item.bid.split(',')
370370 for bid in bids:
371 ref.append("BID-%s" % bid.encode("utf-8").strip() )
371 ref.append("BID-%s" % bid.strip())
372372 if item.xref:
373 ref.append(item.xref.encode("utf-8"))
373 ref.append(item.xref)
374374 if item.tags and item.cvss_vector:
375 ref.append(item.cvss_vector.encode("utf-8"))
375 ref.append(item.cvss_vector)
376376
377377 if item.subnet in ids:
378378 h_id = ids[item.host]
443443 else:
444444 return False
445445
446 def processCommandString(self, username, current_path, command_string):
447 return None
448446
449447 def setHost(self):
450448 pass
2626 self.name = "pasteAnalyzer JSON Output Plugin"
2727 self.plugin_version = "1.0.0"
2828 self.command_string = ""
29 self.current_path = ""
3029 self._command_regex = re.compile(
31 r'^(pasteAnalyzer|python pasteAnalyzer.py|\./pasteAnalyzer.py|sudo python pasteAnalyzer.py|sudo \./pasteAnalyzer.py).*?')
30 r'^(pasteAnalyzer|python pasteAnalyzer.py|\./pasteAnalyzer.py|sudo python pasteAnalyzer.py|sudo \./pasteAnalyzer.py)\s+.*?')
3231
33 def parseOutputString(self, output, debug=False):
34
35 print("[*]Parsing Output...")
36
32 def parseOutputString(self, output):
3733 # Generating file name with full path.
3834 indexStart = self.command_string.find("-j") + 3
39
4035 fileJson = self.command_string[
4136 indexStart:self.command_string.find(" ", indexStart)]
42
43 fileJson = self.current_path + "/" + fileJson
44
37 fileJson = self._current_path + "/" + fileJson
4538 try:
4639 with open(fileJson, "r") as fileJ:
4740 results = json.loads(fileJ.read())
48
4941 except Exception as e:
50 print("\n[!]Exception opening file\n" + str(e))
5142 return
52
5343 if results == []:
5444 return
55
56 print("[*]Results loaded...")
57
5845 # Configuration initial.
5946 hostId = self.createAndAddHost("pasteAnalyzer")
6047 interfaceId = self.createAndAddInterface(hostId, "Results")
6552 "TcpHTTP",
6653 ['80']
6754 )
68 print("[*]Initial Configuration ready....")
6955
7056 # Loading results.
7157 for i in range(0, len(results), 2):
8369 else:
8470 for element2 in element:
8571 description += "\n" + element2
86
8772 self.createAndAddVulnWebToService(
8873 hostId,
8974 serviceId,
9176 description
9277 )
9378
94 print("[*]Parse finished, API faraday called...")
95
9679 def processCommandString(self, username, current_path, command_string):
97
98 print("[*]pasteAnalyzer Plugin running...")
80 super().processCommandString(username, current_path, command_string)
9981
10082 if command_string.find("-j") < 0:
10183 command_string += " -j JSON_OUTPUT "
10284
10385 self.command_string = command_string
104 self.current_path = current_path
10586
10687 return command_string
10788
33 See the file 'doc/LICENSE' for the license information
44 """
55 import re
6 import socket
76 from os import path
8 from faraday_plugins.plugins.plugin import PluginBase
97 from urllib.parse import urlparse
108
119 __author__ = "Andres Tarantini"
1614 __maintainer__ = "Andres Tarantini"
1715 __email__ = "[email protected]"
1816 __status__ = "Development"
17
18 from faraday_plugins.plugins.plugin import PluginBase
19 from faraday_plugins.plugins.plugins_utils import resolve_hostname
1920
2021
2122 class PeepingTomPlugin(PluginBase):
3031 self.plugin_version = "0.0.1"
3132 self.version = "02.19.15"
3233 self._command_regex = re.compile(
33 r'^(python peepingtom.py|\./peepingtom.py).*?')
34 r'^(python peepingtom.py|\./peepingtom.py)\s+.*?')
3435 self._path = None
3536
3637 def parseOutputString(self, output):
4748 for url in re.findall(r'href=[\'"]?([^\'" >]+)', html):
4849 if "://" in url:
4950 url_parsed = urlparse(url)
50 address = socket.gethostbyname(url_parsed.netloc)
51 address = resolve_hostname(url_parsed.netloc)
5152 host = self.createAndAddHost(address)
5253 iface = self.createAndAddInterface(
5354 host, address, ipv4_address=address)
6768 return True
6869
6970 def processCommandString(self, username, current_path, command_string):
71 super().processCommandString(username, current_path, command_string)
7072 self._path = current_path
7173
7274
2828 self.name = "Ping"
2929 self.plugin_version = "0.0.1"
3030 self.version = "1.0.0"
31 self._command_regex = re.compile(
32 r'^(sudo ping|ping|sudo ping6|ping6).*?')
31 self._command_regex = re.compile(r'^(sudo ping|ping|sudo ping6|ping6)\s+.*?')
3332
3433 def parseOutputString(self, output, debug=False):
3534
5554 else:
5655 return False
5756
58 def processCommandString(self, username, current_path, command_string):
59 """
60 """
61 return None
6257
6358
6459 def createPlugin():
3333 self.options = None
3434 self._current_output = None
3535 self._command_regex = re.compile(
36 r'^(sudo propecia|\.\/propecia|propecia).*?')
36 r'^(sudo propecia|\.\/propecia|propecia)\s+.*?')
3737 self._host_ip = None
3838 self._port = "23"
3939
6262 return True
6363
6464 def processCommandString(self, username, current_path, command_string):
65 """
66 """
65 super().processCommandString(username, current_path, command_string)
6766 count_args = command_string.split()
6867
6968 if count_args.__len__() == 3:
33 See the file 'doc/LICENSE' for the license information
44 """
55 import re
6 import os
7 import logging
86 from faraday_plugins.plugins.plugin import PluginXMLFormat
97
10
11 try:
12 import xml.etree.cElementTree as ET
13 import xml.etree.ElementTree as ET_ORIG
14 ETREE_VERSION = ET_ORIG.VERSION
15 except ImportError:
16 import xml.etree.ElementTree as ET
17 ETREE_VERSION = ET.VERSION
8 import xml.etree.ElementTree as ET
9 ETREE_VERSION = ET.VERSION
1810
1911 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split('.')]
2012
21 logger = logging.getLogger(__name__)
22
23 current_path = os.path.abspath(os.getcwd())
2413
2514 __author__ = 'Francisco Amato'
2615 __copyright__ = 'Copyright (c) 2013, Infobyte LLC'
3221 __status__ = 'Development'
3322
3423
35 def cleaner_unicode(string):
36 if string is not None:
37 return string.encode('ascii', errors='backslashreplace')
38 else:
39 return string
40
41
4224 def cleaner_results(string):
43
4425 try:
4526 result = string.replace('<P>', '').replace('<UL>', ''). \
4627 replace('<LI>', '').replace('<BR>', ''). \
4728 replace('<A HREF="', '').replace('</A>', ' '). \
4829 replace('" TARGET="_blank">', ' ').replace('&quot;', '"')
4930 return result
50
5131 except:
5232 return ''
5333
10181 type_report = None
10282
10383 except SyntaxError as err:
104 logger.error('SyntaxError: %s.' % (err))
10584 return None, None
10685
10786 return tree, type_report
136115 self.vulns = self.getResults(tree)
137116
138117 def getResults(self, tree):
139
140118 glossary = tree.find('GLOSSARY/VULN_DETAILS_LIST')
141
142119 for self.issue in self.node.find('VULN_INFO_LIST'):
143120 yield ResultsAssetReport(self.issue, glossary)
144121
179156
180157 # GLOSSARY TAG
181158 self.glossary = glossary
182 self.severity = self.severity_dict.get(
183 self.get_text_from_glossary('SEVERITY'), 'info')
159 self.severity = self.severity_dict.get(self.get_text_from_glossary('SEVERITY'), 'info')
184160 self.title = self.get_text_from_glossary('TITLE')
185161 self.cvss = self.get_text_from_glossary('CVSS_SCORE/CVSS_BASE')
186162 self.pci = self.get_text_from_glossary('PCI_FLAG')
206182 self.ref.append(cve_id)
207183
208184 if self.cvss:
209 self.ref.append('CVSS SCORE: ' + self.cvss)
185 self.ref.append('CVSS SCORE: {}'.format(self.cvss))
210186
211187 if self.pci:
212 self.ref.append('PCI: ' + self.pci)
188 self.ref.append('PCI: {}'.format(self.pci))
213189
214190 def get_text_from_glossary(self, tag):
215191 """
220196 """
221197
222198 for vuln_detail in self.glossary:
223
224199 id_act = vuln_detail.get('id').strip('qid_')
225200 if id_act == self.name:
226
227201 text = vuln_detail.find(tag)
228202 if text is not None:
229 return cleaner_unicode(text.text)
203 return text.text
230204 else:
231205 return None
232206
238212 """
239213 sub_node = node.find(subnode_xpath_expr)
240214 if sub_node is not None:
241 return cleaner_unicode(sub_node.text)
242
215 return sub_node.text
243216 return None
244217
245218
352325 """
353326 sub_node = self.node.find(subnode_xpath_expr)
354327 if sub_node is not None:
355 return cleaner_results(cleaner_unicode(sub_node.text))
356
328 return sub_node.text
357329 return None
358330
359331
371343 self.version = 'Qualysguard 8.17.1.0.2'
372344 self.framework_version = '1.0.0'
373345 self.options = None
374 self._current_output = None
375 self._command_regex = re.compile(
376 r'^(sudo qualysguard|\.\/qualysguard).*?')
377346 self.open_options = {"mode": "r", "encoding": "utf-8"}
378347
379348 def parseOutputString(self, output, debug=False):
380349
381350 parser = QualysguardXmlParser(output)
351
382352
383353 for item in parser.items:
384354 h_id = self.createAndAddHost(
401371 web = False
402372
403373 try:
404 port = v.port.decode("utf-8")
405 name = v.name.decode("utf-8")
374 port = v.port
375 name = v.name
406376 except (UnicodeDecodeError, AttributeError):
407377 port = v.port
408378 name = v.name
443413
444414 del parser
445415
446 def processCommandString(self, username, current_path, command_string):
447 return None
448416
449417 def setHost(self):
450418 pass
454422 return QualysguardPlugin()
455423
456424
457 if __name__ == "__main__":
458 import sys
459 import os
460 if len(sys.argv) == 2:
461 report_file = sys.argv[1]
462 if os.path.isfile(report_file):
463 plugin = createPlugin()
464 plugin.processReport(report_file)
465 print(plugin.get_json())
466 else:
467 print(f"Report not found: {report_file}")
468 else:
469 print(f"USAGE {sys.argv[0]} REPORT_FILE")
470 # I'm Py3
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 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2 """
3 Faraday Penetration Test IDE
4 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
5 See the file 'doc/LICENSE' for the license information
6 """
7 import re
8 from urllib.parse import urlparse
9
10 from dateutil.parser import parse
11
12 from faraday_plugins.plugins.plugin import PluginXMLFormat
13
14 try:
15 import xml.etree.cElementTree as ET
16 except ImportError:
17 import xml.etree.ElementTree as ET
18
19 __author__ = 'Blas Moyano'
20 __copyright__ = 'Copyright 2020, Faraday Project'
21 __credits__ = ['Blas Moyano']
22 __license__ = ''
23 __version__ = '1.0.0'
24 __status__ = 'Development'
25
26
27 class QualysWebappParser:
28 def __init__(self, xml_output):
29 self.tree = self.parse_xml(xml_output)
30 if self.tree:
31 self.info_results = self.get_results_vul(self.tree.find('RESULTS'))
32 self.info_glossary = self.get_glossary_qid(self.tree.find('GLOSSARY'))
33 self.info_appendix = self.get_appendix(self.tree.find('APPENDIX'))
34 else:
35 self.tree = None
36
37 def parse_xml(self, xml_output):
38 try:
39 tree = ET.fromstring(xml_output)
40 except SyntaxError as err:
41 print('SyntaxError In xml: %s. %s' % (err, xml_output))
42 return None
43 return tree
44
45 def get_appendix(self, tree):
46 for self.appendix_tags in tree:
47 yield Appendix(self.appendix_tags)
48
49 def get_glossary_qid(self, tree):
50 for self.glossary_tags in tree.find('QID_LIST'):
51 yield Glossary(self.glossary_tags)
52
53 def get_results_vul(self, tree):
54 for self.results_tags in tree.find('VULNERABILITY_LIST'):
55 yield Results(self.results_tags)
56
57 class Appendix():
58 def __init__(self, appendix_tags):
59 if appendix_tags.tag == 'SCAN_LIST':
60 self.lista_scan = self.get_scan(appendix_tags.find('SCAN'))
61 elif appendix_tags.tag == 'WEBAPP':
62 self.lista_webapp = self.get_webapp(appendix_tags)
63
64 def get_scan(self, appendix_tags):
65 self.result_scan = {}
66 for scan in appendix_tags:
67 self.result_scan[scan.tag] = scan.text
68 return self.result_scan
69
70 def get_webapp(self, appendix_tags):
71 self.result_webapp = {}
72 for webapp in appendix_tags:
73 self.result_webapp[webapp.tag] = webapp.text
74 return self.result_webapp
75
76
77 class Glossary():
78 def __init__(self, glossary_tags):
79 self.lista_qid = self.get_qid_list(glossary_tags)
80
81
82 def get_qid_list(self, qid_list_tags):
83 self.dict_result_qid = {}
84 for qid in qid_list_tags:
85 self.dict_result_qid[qid.tag] = qid.text
86 return self.dict_result_qid
87
88
89 class Results():
90 def __init__(self, glossary_tags):
91 self.lista_vul = self.get_qid_list(glossary_tags)
92
93 def get_qid_list(self, vul_list_tags):
94 self.dict_result_vul = {}
95 for vul in vul_list_tags:
96 self.dict_result_vul[vul.tag] = vul.text
97 return self.dict_result_vul
98
99
100 class QualysWebappPlugin(PluginXMLFormat):
101 def __init__(self):
102 super().__init__()
103 self.identifier_tag = ["WAS_SCAN_REPORT"]
104 self.id = 'QualysWebapp'
105 self.name = 'QualysWebapp XML Output Plugin'
106 self.plugin_version = '1.0.0'
107 self.version = '1.0.0'
108 self.framework_version = '1.0.0'
109 self.options = None
110 self.protocol = None
111 self.port = '80'
112 self.address = None
113
114 def parseOutputString(self, output):
115 hostnames = []
116
117 parser = QualysWebappParser(output)
118
119 if not parser.info_appendix:
120 return
121
122 self.scan_list_result = []
123 for host_create in parser.info_appendix:
124 self.scan_list_result.append(host_create)
125
126 self.credential = self.scan_list_result[0].lista_scan.get('AUTHENTICATION_RECORD')
127 os = self.scan_list_result[1].lista_webapp.get('OPERATING_SYSTEM')
128
129 if self.scan_list_result[1].lista_webapp.get('URL'):
130 initial_url = self.scan_list_result[1].lista_webapp.get('URL')
131 parsed_url = urlparse(initial_url)
132 hostnames = [parsed_url.netloc]
133
134 glossary = []
135 for glossary_qid in parser.info_glossary:
136 glossary.append(glossary_qid.dict_result_qid)
137
138 for v in parser.info_results:
139 url = urlparse(v.dict_result_vul.get('URL'))
140
141 host_id = self.createAndAddHost(name=url.netloc, os=os, hostnames=hostnames)
142
143 vuln_scan_id = v.dict_result_vul.get('QID')
144
145 # Data in the xml is in different parts, we look into the glossary
146 vuln_data = next((item for item in glossary if item["QID"] == vuln_scan_id), None)
147 vuln_name = vuln_data.get('TITLE')
148 vuln_desc = vuln_data.get('DESCRIPTION')
149
150 raw_severity = int(vuln_data.get('SEVERITY', 0))
151 vuln_severity = raw_severity - 1
152
153 run_date = parse(v.dict_result_vul.get('FIRST_TIME_DETECTED'))
154 vuln_resolution = vuln_data.get('SOLUTION')
155
156 vuln_ref = []
157 if vuln_data.get('CVSS_BASE'):
158 vuln_ref = ["CVSS: {}".format(vuln_data.get('CVSS_BASE'))]
159
160 vuln_data_add = "ID: {}, DETECTION_ID: {}, CATEGORY: {}, GROUP: {}, URL: {}, IMPACT: {}".format(
161 v.dict_result_vul.get('ID'), v.dict_result_vul.get('DETECTION_ID'), vuln_data.get('CATEGORY'),
162 vuln_data.get('GROUP'), v.dict_result_vul.get('URL'), vuln_data.get('IMPACT'))
163
164 self.createAndAddVulnToHost(host_id=host_id, name=vuln_name, desc=vuln_desc, ref=vuln_ref,
165 severity=vuln_severity, resolution=vuln_resolution, run_date=run_date,
166 external_id=vuln_scan_id, data=vuln_data_add)
167
168
169 def createPlugin():
170 return QualysWebappPlugin()
44 """
55 import re
66 import json
7 import socket
87 import logging
8
99 try:
1010 from lxml import etree as ET
1111 except ImportError:
1212 import xml.etree.ElementTree as ET
1313
1414 from faraday_plugins.plugins.plugin import PluginXMLFormat
15 from faraday_plugins.plugins.plugins_utils import resolve_hostname
1516
1617 __author__ = 'Leonardo Lazzaro'
1718 __copyright__ = 'Copyright (c) 2017, Infobyte LLC'
135136 self.version = ''
136137 self.framework_version = ''
137138 self.options = None
138 self._current_output = None
139 self._command_regex = re.compile(
140 r'records added to')
141
142139 self.host_mapper = {}
143140
144141 def parseOutputString(self, output):
152149 self.host_mapper[host['host']] = h_id
153150 for vuln in parser.vulns:
154151 if vuln['host'] not in list(self.host_mapper.keys()):
155 ip = self.resolve_host(vuln['host'])
152 ip = resolve_hostname(vuln['host'])
156153 h_id = self.createAndAddHost(
157154 ip,
158155 hostnames=[vuln['host']]
170167 data=vuln['example']
171168 )
172169
173 def processCommandString(self, username, current_path, command_string):
174 return
175170
176 def resolve_host(self, host):
177 try:
178 return socket.gethostbyname(host)
179 except Exception:
180 pass
181 return host
182171
183172
184173 def createPlugin():
44
55 """
66 import re
7 import os
87 from faraday_plugins.plugins.plugin import PluginXMLFormat
98
109
1817
1918 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
2019
21 current_path = os.path.abspath(os.getcwd())
2220
2321 __author__ = "Francisco Amato"
2422 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
182180 self.version = "Retina Network 5.19.2.2718"
183181 self.framework_version = "1.0.0"
184182 self.options = None
185 self._current_output = None
186 self._command_regex = re.compile(r'^(sudo retina|\.\/retina).*?')
187183
188184
189185 def parseOutputString(self, output, debug=False):
211207 v.protocol.lower(),
212208 ports=[str(v.port)],
213209 status="open")
214
215210 if v.port in ['80', '443'] or re.search("ssl|http", v.name.lower()):
216211 web = True
217212 else:
218213 web = False
219
220214 if web:
221 v_id = self.createAndAddVulnWebToService(h_id, s_id, v.name.encode(
222 "utf-8"), ref=v.ref, website=hostname, severity=v.severity, resolution=v.solution.encode("utf-8"), desc=v.desc.encode("utf-8"))
215 v_id = self.createAndAddVulnWebToService(h_id, s_id, v.name, ref=v.ref,
216 website=hostname, severity=v.severity,
217 resolution=v.solution, desc=v.desc)
223218 else:
224 v_id = self.createAndAddVulnToService(h_id, s_id, v.name.encode(
225 "utf-8"), ref=v.ref, severity=v.severity, resolution=v.solution.encode("utf-8"), desc=v.desc.encode("utf-8"))
219 v_id = self.createAndAddVulnToService(h_id, s_id, v.name, ref=v.ref,
220 severity=v.severity, resolution=v.solution,
221 desc=v.desc)
226222 else:
227223 for v in vulns:
228 v_id = self.createAndAddVulnToHost(h_id, v.name.encode(
229 "utf-8"), ref=v.ref, severity=v.severity, resolution=v.solution.encode("utf-8"), desc=v.desc.encode("utf-8"))
224 v_id = self.createAndAddVulnToHost(h_id, v.name, ref=v.ref, severity=v.severity,
225 resolution=v.solution, desc=v.desc)
230226 del parser
231227
232 def processCommandString(self, username, current_path, command_string):
233 return None
234228
235229 def setHost(self):
236230 pass
55 """
66 from faraday_plugins.plugins.plugin import PluginBase
77 import re
8 import os
98
109
11
12 current_path = os.path.abspath(os.getcwd())
1310
1411 __author__ = "Francisco Amato"
1512 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
5855 self.plugin_version = "0.0.1"
5956 self.version = "0.7.6"
6057 self.options = None
61 self._current_output = None
62 self._current_path = None
63 self._command_regex = re.compile(
64 r'^(sudo \.\/reverseraider|\.\/reverseraider).*?')
58 self._command_regex = re.compile(r'^(sudo \.\/reverseraider|\.\/reverseraider)\s+.*?')
6559 self._completition = {
6660 "": "reverseraider -d domain | -r range [options]",
6761 "-r": "range of ipv4 or ipv6 addresses, for reverse scanning",
7468 "-R": "don't set the recursion bit on queries",
7569 }
7670
77 global current_path
7871
79 def canParseCommandString(self, current_input):
80 if self._command_regex.match(current_input.strip()):
81 return True
82 else:
83 return False
84
85 def parseOutputString(self, output, debug=False):
86 """
87 This method will discard the output the shell sends, it will read it from
88 the xml where it expects it to be present.
89
90 NOTE: if 'debug' is true then it is being run from a test case and the
91 output being sent is valid.
92 """
93
94 if debug:
95 parser = ReverseraiderParser(output)
96 else:
97
98 parser = ReverseraiderParser(output)
99
100 for item in parser.items:
101 h_id = self.createAndAddHost(item['ip'])
102 i_id = self.createAndAddInterface(
103 h_id, item['ip'], ipv4_address=item['ip'])
104
72 def parseOutputString(self, output):
73 parser = ReverseraiderParser(output)
74 for item in parser.items:
75 h_id = self.createAndAddHost(item['ip'])
76 i_id = self.createAndAddInterface(h_id, item['ip'], ipv4_address=item['ip'])
10577 del parser
10678
107 def processCommandString(self, username, current_path, command_string):
108 """
109 """
110 return None
11179
11280
11381 def createPlugin():
66 import re
77 import os
88 import json
9 import socket
109 import random
10 import shutil
11 import tempfile
12
1113 from faraday_plugins.plugins.plugin import PluginBase
12
13 current_path = os.path.abspath(os.getcwd())
14 from faraday_plugins.plugins.plugins_utils import resolve_hostname
15
1416
1517 __author__ = "Nicolas Rodriguez"
1618 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
111113 def __init__(self):
112114 super().__init__()
113115 self.id = "Skipfish"
114 self.name = "Skipfish XML Output Plugin"
116 self.name = "Skipfish Output Plugin"
115117 self.plugin_version = "0.0.2"
116118 self.version = "2.1.5"
117119 self.options = None
118 self._current_output = None
119120 self.parent = None
120121 self._command_regex = re.compile(
121 r'^(sudo skipfish|skipfish|sudo skipfish\.pl|skipfish\.pl|perl skipfish\.pl|\.\/skipfish\.pl|\.\/skipfish).*?')
122 global current_path
123
124 def parseOutputString(self, output, debug=False):
122 r'^(sudo skipfish|skipfish|sudo skipfish\.pl|skipfish\.pl|perl skipfish\.pl|\.\/skipfish\.pl|\.\/skipfish)\s+.*?')
123
124 def _parse_filename(self, filename):
125 self.parseOutputString(filename)
126 if self._delete_temp_file:
127 try:
128 if os.path.isfile(filename):
129 os.remove(filename)
130 elif os.path.isdir(filename):
131 shutil.rmtree(filename)
132 except Exception as e:
133 self.logger.error("Error on delete file: (%s) [%s]", filename, e)
134
135 def parseOutputString(self, output):
125136 """
126137 This method will discard the output the shell sends, it will read it
127138 from the xml where it expects it to be present.
130141 output being sent is valid.
131142 """
132143
133 if not os.path.exists(self._output_path):
144 if not os.path.isdir(self._output_file_path):
134145 return False
135146
136 p = SkipfishParser(self._output_path)
147 p = SkipfishParser(self._output_file_path)
137148
138149 hostc = {}
139150 port = 80
152163 else:
153164 port = 443 if protocol == "https" else 80
154165
155 ip = self.resolve(host)
166 ip = resolve_hostname(host)
156167
157168 h_id = self.createAndAddHost(ip)
158169 i_id = self.createAndAddInterface(
198209 path=sample["url"],
199210 severity=issue["severity"])
200211
201 def resolve(self, host):
202 try:
203 return socket.gethostbyname(host)
204 except Exception:
205 pass
206 return host
207212
208213 xml_arg_re = re.compile(r"^.*(-o\s*[^\s]+).*$")
209214
212217 Adds the -o parameter to get report of the command string that the
213218 user has set.
214219 """
220 super().processCommandString(username, current_path, command_string)
215221 arg_match = self.xml_arg_re.match(command_string)
216
217 self._output_path = os.path.join(
218 self.data_path,
219 "skipfish_output-%s" % random.uniform(1, 10))
220
222 self._output_file_path = os.path.join(tempfile.gettempdir(), "faraday_plugin_skipfish_%d" % random.randint(1, 999999))
223 self._delete_temp_file = True
221224 if arg_match is None:
222 return re.sub(
223 r"(^.*?skipfish)",
224 r"\1 -o %s" % self._output_path,
225 command_string,
226 1)
225 return re.sub(r"(^.*?skipfish)", r"\1 -o %s" % self._output_file_path, command_string, 1)
227226 else:
228 return re.sub(
229 arg_match.group(1),
230 r"-o %s" % self._output_path,
231 command_string,
232 1)
227 return re.sub(arg_match.group(1), r"-o %s" % self._output_file_path, command_string, 1)
233228
234229 def setHost(self):
235230 pass
2828 self.plugin_version = "0.0.1"
2929 self.version = "1.0.0"
3030 self._command_regex = re.compile(
31 r'^(python sshdefaultscan.py|\./sshdefaultscan.py).*?')
31 r'^(python sshdefaultscan.py|\./sshdefaultscan.py)\s+.*?')
3232 self._completition = {"--fast": "Fast scan mode"}
3333
34 def parseOutputString(self, output, debug=False):
35 for line in [l.strip() for l in output.split("\n")]:
34 def parseOutputString(self, output):
35 for line in [line.strip() for line in output.split("\n")]:
3636 output_rexeg_match = re.match(
3737 r".*:.*@\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$", line)
3838 if output_rexeg_match:
5757 severity=3
5858 )
5959
60 return True
6160
6261 def processCommandString(self, username, current_path, command_string):
62 super().processCommandString(username, current_path, command_string)
6363 if "--batch" not in command_string:
6464 return "{command} --batch --batch-template {template}".format(
6565 command=command_string,
6666 template="{username}:{password}@{host}"
6767 )
68
69 return None
68 else:
69 return None
7070
7171
7272 def createPlugin():
00 import re
1 import os
2 import random
31 from faraday_plugins.plugins.plugin import PluginXMLFormat
42
53 try:
9694 self.framework_version = "1.0.0"
9795 self.options = None
9896 self._current_output = None
99 self._command_regex = re.compile(r'^(sudo sslyze|sslyze|\.\/sslyze).*?')
97 self._command_regex = re.compile(r'^(sudo sslyze|sslyze|\.\/sslyze)\s+.*?')
10098 self.xml_arg_re = re.compile(r"^.*(--xml_output\s*[^\s]+).*$")
99 self._use_temp_file = True
100 self._temp_file_extension = "xml"
101101
102102 def report_belongs_to(self, **kwargs):
103103 if super().report_belongs_to(**kwargs):
170170 severity="medium")
171171
172172 def processCommandString(self, username, current_path, command_string):
173 self._output_file_path = os.path.join(
174 self.data_path,
175 "%s_%s_output-%s.xml" % (
176 self.get_ws(),
177 self.id,
178 random.uniform(1, 10))
179 )
180
173 super().processCommandString(username, current_path, command_string)
181174 arg_match = self.xml_arg_re.match(command_string)
182
183175 if arg_match is None:
184176 return re.sub(r"(^.*?sslyze)",
185177 r"\1 --xml_out %s" % self._output_file_path,
33 See the file 'doc/LICENSE' for the license information
44
55 """
6 import re
67 from faraday_plugins.plugins.plugin import PluginBase
7 import re
8 import os
9 import socket
10
11 current_path = os.path.abspath(os.getcwd())
8 from faraday_plugins.plugins.plugins_utils import resolve_hostname
129
1310 __author__ = "Facundo de Guzmán, Esteban Guillardoy"
1411 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
3532 self.framework_version = "1.0.0"
3633 self.options = None
3734 self._current_output = None
38 self._command_regex = re.compile(r'^telnet.*?')
35 self._command_regex = re.compile(r'^telnet\s+.*?')
3936 self._host_ip = None
4037 self._host = []
4138 self._port = "23"
5653 "-n": "-n &lt;tracefile&gt; Opens tracefile for recording trace information. See the set tracefile command below.",
5754 }
5855
59 global current_path
6056
61 def resolve(self, host):
62 try:
63 return socket.gethostbyname(host)
64 except:
65 pass
66 return host
6757
6858 def parseOutputString(self, output, debug=False):
6959
7060 host_info = re.search(r"Connected to (.+)\.", output)
7161
7262 hostname = host_info.group(1)
73 ip_address = self.resolve(hostname)
63 ip_address = resolve_hostname(hostname)
7464
7565 if host_info is not None:
7666 h_id = self.createAndAddHost(ip_address)
8373 return True
8474
8575 def processCommandString(self, username, current_path, command_string):
86
76 super().processCommandString(username, current_path, command_string)
8777 count_args = command_string.split()
88
8978 c = count_args.__len__()
9079 self._port = "23"
9180 if re.search(r"[\d]+", count_args[c - 1]):
55 """
66 from faraday_plugins.plugins.plugin import PluginBase
77 import re
8 import os
98
109
11
12 current_path = os.path.abspath(os.getcwd())
1310
1411 __author__ = "Francisco Amato"
1512 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
8582 self._current_output = None
8683 self._current_path = None
8784 self._command_regex = re.compile(
88 r'^(theharvester|sudo theharvester|sudo theHarvester\.py|theHarvester\.py|python theHarvester\.py|\.\/theHarvester\.py).*?')
85 r'^(sudo theHarvester\.py|theHarvester\.py|python theHarvester\.py|\.\/theHarvester\.py)\s+.*?')
8986 self._completition = {
9087 "": "Examples:./theharvester.py -d microsoft.com -l 500 -b google",
9188 "-d": "Domain to search or company name",
10198 "-h": "use SHODAN database to query discovered hosts. google 100 to 100, and pgp doesn't use this option)",
10299 }
103100
104 global current_path
105101
106102 def parseOutputString(self, output, debug=False):
107103 """
131127
132128 del parser
133129
134 def processCommandString(self, username, current_path, command_string):
135 """
136 """
137 return None
138130
139131
140132 def createPlugin():
2121 self.name = "Traceroute"
2222 self.plugin_version = "1.0.0"
2323 self.command_string = ""
24 self._command_regex = re.compile(
25 r'^(traceroute|traceroute6).*?')
24 self._command_regex = re.compile(r'^(traceroute|traceroute6)\s+.*?')
2625
2726 def parseOutputString(self, output, debug=False):
2827
5150 print("[*]Parse finished, API faraday called...")
5251
5352 def processCommandString(self, username, current_path, command_string):
54
55 print("[*]traceroute Plugin running...")
53 super().processCommandString(username, current_path, command_string)
5654 self.command_string = command_string
57 return command_string
55 return None
5856
5957
6058 def createPlugin():
33 See the file 'doc/LICENSE' for the license information
44
55 """
6
6 import re
7 from urllib.parse import urlparse
78 from faraday_plugins.plugins.plugin import PluginXMLFormat
8 import re
9 import os
10 import socket
11 import pprint
12 import sys
9 from faraday_plugins.plugins.plugins_utils import resolve_hostname
1310
1411 try:
1512 import xml.etree.cElementTree as ET
2118
2219 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
2320
24 current_path = os.path.abspath(os.getcwd())
2521
2622 __author__ = "Francisco Amato"
2723 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
6864 try:
6965 tree = ET.fromstring(xml_output)
7066 except SyntaxError as err:
71 print("SyntaxError: %s. %s" % (err, xml_output))
7267 return None
7368
7469 return tree
8580 scaninfo = tree.findall('scan-info')[0]
8681
8782 self.target = scaninfo.get('target')
88 host = re.search(
89 "(http|https|ftp)\://([a-zA-Z0-9\.\-]+(\:[a-zA-Z0-9\.&amp;%\$\-]+)*@)*((25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])|localhost|([a-zA-Z0-9\-]+\.)*[a-zA-Z0-9\-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))[\:]*([0-9]+)*([/]*($|[a-zA-Z0-9\.\,\?\'\\\+&amp;%\$#\=~_\-]+)).*?$", self.target)
90
91 self.protocol = host.group(1)
92 self.host = host.group(4)
93 if self.protocol == 'https':
94 self.port = 443
95 if host.group(11) is not None:
96 self.port = host.group(11)
83 url_parse = urlparse(self.target)
84
85 self.protocol = url_parse.scheme
86 self.host = url_parse.netloc
87 self.port = url_parse.port
88 if self.port is None:
89 if self.protocol == 'https':
90 self.port = 443
91 elif self.protocol == 'http':
92 self.port = 80
9793
9894 for node in tree.findall('vulnerability'):
9995 yield Item(node)
151147 self.url = self.url if self.url != 'None' else "/"
152148 self.plugin = self.node.get('plugin')
153149 self.detail = self.get_text_from_subnode('description')
150 if not self.detail:
151 self.detail = self.node.text.strip('\n').strip()
154152 self.resolution = self.get_text_from_subnode('fix-guidance')
155153 self.fix_effort = self.get_text_from_subnode('fix-effort')
156154 self.longdetail = self.get_text_from_subnode('description')
220218 self.options = None
221219 self._current_output = None
222220 self.target = None
223 self._command_regex = re.compile(r'^(w3af|sudo w3af|\.\/w3af).*?')
221 self._command_regex = re.compile(r'^(w3af|sudo w3af|\.\/w3af)\s+.*?')
224222 self._completition = {
225223 "": "",
226224 "-h": "Display this help message.",
229227 def parseOutputString(self, output, debug=False):
230228
231229 parser = W3afXmlParser(output)
232 ip = self.resolve(parser.host)
230 ip = resolve_hostname(parser.host)
233231 h_id = self.createAndAddHost(ip)
234232 i_id = self.createAndAddInterface(h_id, ip, ipv4_address=ip, hostname_resolution=[parser.host])
235233 s_id = self.createAndAddServiceToInterface(h_id, i_id, "http", "tcp", ports=[parser.port], status="open")
241239 resolution=item.resolution, ref=item.ref, response=item.resp)
242240 del parser
243241
244 def resolve(self, host):
245 try:
246 return socket.gethostbyname(host)
247 except:
248 pass
249 return host
250
251 def processCommandString(self, username, current_path, command_string):
252 return None
253242
254243 def setHost(self):
255244 pass
44
55 """
66 import re
7 import os
8 import socket
9
107 from urllib.parse import urlparse
118 from faraday_plugins.plugins.plugin import PluginXMLFormat
9 from faraday_plugins.plugins.plugins_utils import resolve_hostname
10
1211 try:
1312 import xml.etree.cElementTree as ET
1413 import xml.etree.ElementTree as ET_ORIG
1918
2019 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
2120
22 current_path = os.path.abspath(os.getcwd())
2321
2422 __author__ = "Francisco Amato"
2523 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
119117 self.node = item_node
120118 self.url = self.get_url(item_node)
121119 if self.url.hostname is not None:
122 self.ip = socket.gethostbyname(self.url.hostname)
120 self.ip = resolve_hostname(self.url.hostname)
123121 else:
124122 self.ip = '0.0.0.0'
125123 self.hostname = self.url.hostname
233231 self.plugin_version = "0.0.1"
234232 self.version = "2.2.1"
235233 self.options = None
236 self._current_output = None
237234 self.protocol = None
238235 self.host = None
239236 self.port = "80"
237 self._use_temp_file = True
238 self._temp_file_extension = "xml"
240239 self.xml_arg_re = re.compile(r"^.*(-oX\s*[^\s]+).*$")
241240 self._command_regex = re.compile(
242 r'^(python wapiti|wapiti|sudo wapiti|sudo wapiti\.py|wapiti\.py|python wapiti\.py|\.\/wapiti\.py|wapiti|\.'
243 r'\/wapiti|python wapiti|python \.\/wapiti).*?')
241 r'^(python wapiti|wapiti|sudo wapiti|sudo wapiti\.py|wapiti\.py|python wapiti\.py|\.\/wapiti\.py|wapiti |\.'
242 r'\.\/wapiti|python wapiti|python \.\/wapiti)\s+.*?')
244243 self._completition = {
245244 "": "python wapiti.py http://server.com/base/url/ [options]",
246245 "-s": "&lt;url&gt; ",
324323 Adds the -oX parameter to get xml output to the command string that the
325324 user has set.
326325 """
326 super().processCommandString(username, current_path, command_string)
327327 host = re.search(
328328 "(http|https|ftp)\://([a-zA-Z0-9\.\-]+(\:[a-zA-Z0-9\.&amp;%\$\-]+)*@)*((25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]"
329329 "{2}|[1-9]{1}[0-9]{1}|[1-9])\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2"
339339 if self.protocol == 'https':
340340 self.port = 443
341341 self.logger.debug("host = %s, port = %s",self.host, self.port)
342 arg_match = self.xml_arg_re.match(command_string)
343342 return "%s -o %s -f xml \n" % (command_string, self._output_file_path)
344343
345344 def setHost(self):
1818
1919 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
2020
21 current_path = os.path.abspath(os.getcwd())
2221
2322 __author__ = "Morgan Lemarechal"
2423 __copyright__ = "Copyright 2014, Faraday Project"
8786 }
8887
8988 self.options = None
90 self._current_output = None
91 self.current_path = None
92 self._command_regex = re.compile(
93 r'^(sudo wcscan|wcscan|\.\/wcscan).*?')
89 self._command_regex = re.compile(r'^(sudo wcscan|wcscan|\.\/wcscan)\s+.*?')
90 self._use_temp_file = True
91 self._temp_file_extension = "xml"
92 self.xml_arg_re = re.compile(r"^.*(--xml\s*[^\s]+).*$")
9493
9594
96 def canParseCommandString(self, current_input):
97 if self._command_regex.match(current_input.strip()):
98 return True
99 else:
100 return False
101
102 def parseOutputString(self, output, debug=False):
95 def parseOutputString(self, output):
10396 """
10497 This method will discard the output the shell sends, it will read it from
10598 the xml where it expects it to be present.
10699 NOTE: if 'debug' is true then it is being run from a test case and the
107100 output being sent is valid.
108101 """
109 if debug:
110 parser = WcscanParser(self._output_file_path)
111 else:
102 parser = WcscanParser(output)
103 for file in parser.scaninfo:
104 host = parser.scaninfo[file]['host']
105 port = parser.scaninfo[file]['port']
106 h_id = self.createAndAddHost(host)
107 if(re.match("(^[2][0-5][0-5]|^[1]{0,1}[0-9]{1,2})\.([0-2][0-5][0-5]|[1]{0,1}[0-9]{1,2})\.([0-2][0-5][0-5]|[1]{0,1}[0-9]{1,2})\.([0-2][0-5][0-5]|[1]{0,1}[0-9]{1,2})$", host)):
108 i_id = self.createAndAddInterface(h_id,
109 host,
110 ipv4_address=host)
111 else:
112 i_id = self.createAndAddInterface(h_id,
113 host,
114 ipv6_address=host)
112115
113 if not os.path.exists(self._output_file_path):
114 return False
115 parser = WcscanParser(self._output_file_path)
116 s_id = self.createAndAddServiceToInterface(
117 h_id, i_id, "http", protocol="tcp", ports=port)
118 for vuln in parser.result[file]:
119 if parser.scaninfo[file]['type'] == "phpini":
120 vuln_name = f"{parser.scaninfo[file]['file']}: {vuln}"
121 vuln_description = f"{vuln}: {str(parser.result[file][vuln][0])}\n{str(parser.result[file][vuln][1])}"
122 v_id = self.createAndAddVulnToService(h_id, s_id, vuln_name, desc=vuln_description, severity=0)
116123
117 for file in parser.scaninfo:
118 host = parser.scaninfo[file]['host']
119 port = parser.scaninfo[file]['port']
120 h_id = self.createAndAddHost(host)
121 if(re.match("(^[2][0-5][0-5]|^[1]{0,1}[0-9]{1,2})\.([0-2][0-5][0-5]|[1]{0,1}[0-9]{1,2})\.([0-2][0-5][0-5]|[1]{0,1}[0-9]{1,2})\.([0-2][0-5][0-5]|[1]{0,1}[0-9]{1,2})$", host)):
122 i_id = self.createAndAddInterface(h_id,
123 host,
124 ipv4_address=host)
125 else:
126 i_id = self.createAndAddInterface(h_id,
127 host,
128 ipv6_address=host)
124 if parser.scaninfo[file]['type'] == "webconfig":
125 vuln_name = f"{parser.scaninfo[file]['file']}: {str(parser.result[file][vuln][3])}"
126 vuln_description = f"{str(parser.result[file][vuln][3])} : {str(parser.result[file][vuln][2])} = {str(parser.result[file][vuln][0])}\n{str(parser.result[file][vuln][1])}"
127 v_id = self.createAndAddVulnToService(h_id, s_id, vuln_name, desc=vuln_description, severity=0)
129128
130 s_id = self.createAndAddServiceToInterface(
131 h_id, i_id, "http", protocol="tcp", ports=port)
132 for vuln in parser.result[file]:
133 if parser.scaninfo[file]['type'] == "phpini":
134 v_id = self.createAndAddVulnToService(h_id, s_id,
135 parser.scaninfo[file][
136 'file'] + ":" + vuln,
137 desc="{} : {}\n{}".format(vuln,
138 str(parser.result[
139 file][vuln][0]),
140 str(parser.result[file][vuln][1])),
141 severity=0)
142129
143 if parser.scaninfo[file]['type'] == "webconfig":
144 v_id = self.createAndAddVulnToService(h_id, s_id,
145 parser.scaninfo[file][
146 'file'] + ":" + str(parser.result[file][vuln][3]),
147 desc="{} : {} = {}\n{}".format(str(parser.result[file][vuln][3]),
148 str(parser.result[
149 file][vuln][2]),
150 str(parser.result[
151 file][vuln][0]),
152 str(parser.result[file][vuln][1])),
153 severity=0)
154 del parser
155
156 return True
157
158 xml_arg_re = re.compile(r"^.*(--xml\s*[^\s]+).*$")
159130
160131 def processCommandString(self, username, current_path, command_string):
161132 """
162133 Adds the parameter to get output to the command string that the
163134 user has set.
164135 """
165
136 super().processCommandString(username, current_path, command_string)
166137 arg_match = self.xml_arg_re.match(command_string)
167138
168139 if arg_match is None:
169140 return "%s --xml %s" % (command_string, self._output_file_path)
170141 else:
171 return re.sub(arg_match.group(1),
172 r"-xml %s" % self._output_file_path,
173 command_string)
142 return re.sub(arg_match.group(1), r"-xml %s" % self._output_file_path, command_string)
174143
175144
176145 def createPlugin():
6464 self.items.append(vuln)
6565
6666 except SyntaxError as err:
67 print("SyntaxError: %s. %s" % (err, self.filepath))
6867 return None
6968
7069
8281 self.options = None
8382 self._current_output = None
8483 self.host = None
85 self._command_regex = re.compile(
86 r'^(sudo webfuzzer|webfuzzer|\.\/webfuzzer).*?')
84 self._command_regex = re.compile(r'^(sudo webfuzzer|webfuzzer|\.\/webfuzzer)\s+.*?')
8785 self._completition = {'': '__Usage: ./webfuzzer -G|-P URL [OPTIONS]',
8886 '-G': '<url> get this as starting url (with parameters)',
8987 '-P': '<url> post this as starting url (with parameters)',
141139 def processCommandString(self, username, current_path, command_string):
142140 """
143141 """
142 super().processCommandString(username, current_path, command_string)
144143 host = re.search("\-([G|P]) ([\w\.\-]+)", command_string)
145
146144 if host is not None:
147145 self.host = host.group(2)
148146 self._output_path = current_path + "/" + self.host + ".txt"
44 """
55 import re
66
7 from faraday_plugins.plugins.plugin import PluginBase
7 from faraday_plugins.plugins.plugin import PluginXMLFormat
88 from faraday_plugins.plugins.plugins_utils import get_vulnweb_url_fields
99
1010 try:
11 import xml.etree.ElementTree as ET
11 import xml.etree.cElementTree as ET
1212 except ImportError:
1313 import xml.etree.ElementTree as ET
1414
4242
4343 def return_text(self, tag,element):
4444 try:
45 text = element.find(tag).text.encode("ascii", errors="backslashreplace")
45 text = element.find(tag).text
4646 return text
4747 except:
4848 return ""
9494 for section in issue.findall("ReportSection"):
9595
9696 try:
97 field = section.find("Name").text.encode("ascii", errors="backslashreplace")
98 value = section.find("SectionText").text.encode("ascii", errors="backslashreplace")
97 field = section.find("Name").text
98 value = section.find("SectionText").text
9999
100100 faraday_obj_name = map_objects_fields.get(field)[0]
101101 faraday_field = map_objects_fields.get(field)[1]
111111 return result
112112
113113
114 class WebInspectPlugin(PluginBase):
114 class WebInspectPlugin(PluginXMLFormat):
115115 """
116116 This plugin handles WebInspect reports.
117117 """
122122 self.name = "Webinspect"
123123 self.plugin_version = "0.0.1"
124124 self.version = "1.0.0"
125 self.identifier_tag = ["Scan"]
125126
126 def parseOutputString(self, output, debug=False):
127 def parseOutputString(self, output):
127128
128129 parser = WebInspectParser(output)
129130 vulns = parser.parse()
157158 severity=parser.parse_severity(vuln.get("Vuln").get("severity"))
158159 )
159160
160 return True
161161
162 def processCommandString(self, username, current_path, command_string):
163 return None
164162
165163
166164 def createPlugin():
1818 self.port = None
1919 self.protocol = None
2020 self.fail = None
21 self._command_regex = re.compile(
22 r'^(wfuzz).*?')
21 self._command_regex = re.compile(r'^(wfuzz)\s+.*?')
2322
2423 def parseData(self, output):
2524
7978 words = item['words']
8079 name = "Wfuzz found: {path} with status {status} on url {url}".format(path=path, status=status, url=url)
8180 desc = 'Wfuzz found a response with status {status}. Response contains: \n* {words} words \n* {lines} ' \
82 'lines \n* {chars} chars'.format(words=words, url=url, lines=lines, chars=chars, status=status)
81 'lines \n* {chars} chars'.format(words=words, lines=lines, chars=chars, status=status)
8382 self.createAndAddVulnWebToService(host_id, service_id, name, desc, severity="info", website=target,
8483 path=path)
8584
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 import re
7 import socket
8 import json
9 from faraday_plugins.plugins.plugin import PluginJsonFormat
10 from urllib.parse import urlparse
11
12
13 __author__ = "Blas Moyano"
14 __copyright__ = "Copyright (c) 2020, Infobyte LLC"
15 __credits__ = ["Blas Moyano"]
16 __license__ = ""
17 __version__ = "0.0.1"
18 __maintainer__ = "Blas Moyano"
19 __email__ = "[email protected]"
20 __status__ = "Development"
21
22
23 class WhitesourcePlugin(PluginJsonFormat):
24 def __init__(self):
25 super().__init__()
26 self.id = "whitesource"
27 self.name = "whitesource"
28 self.plugin_version = "0.1"
29 self.version = "3.4.5"
30 self.json_keys = {"vulnerabilities"}
31
32 def parseOutputString(self, output, debug=False):
33 parser = json.loads(output)
34 if parser.get('vulnerabilities'):
35 for vulnerability in parser['vulnerabilities']:
36
37 if 'project' in vulnerability:
38 application_name = vulnerability.get('project')
39 host_id = self.createAndAddHost(application_name)
40 data = ''
41 for key, value in vulnerability['library'].items():
42 data += f'{key}: {value} \n'
43 refs = [
44 f"CVSS: {vulnerability['score']}",
45 ]
46 if 'cvss3_score' in vulnerability:
47 refs.append(f"CVSS3: {vulnerability['cvss3_score']}")
48 if 'topFix' in vulnerability:
49 refs.append(f"URL: {vulnerability['topFix']['url']}")
50 self.createAndAddVulnToHost(host_id,
51 name=vulnerability['name'],
52 desc=vulnerability['description'],
53 data=data,
54 resolution=vulnerability['topFix']['fixResolution'],
55 ref=refs,
56 severity=vulnerability['severity'])
57 else:
58 self.createAndAddVulnToHost(host_id,
59 name=vulnerability['name'],
60 desc=vulnerability['description'],
61 data=data,
62 ref=refs,
63 severity=vulnerability['severity'])
64 elif 'namespace' in vulnerability:
65 host_id = self.createAndAddHost(vulnerability['namespace'])
66 service_id = self.createAndAddServiceToHost(
67 host_id,
68 vulnerability['featurename'],
69 ports=0
70 )
71 self.createAndAddVulnToService(
72 host_id,
73 service_id,
74 name=vulnerability['vulnerability'],
75 desc=vulnerability['description'],
76 ref=[vulnerability['link']],
77 severity=vulnerability['severity']
78 )
79 elif 'package' in vulnerability:
80 host_id = self.createAndAddHost(vulnerability['feed_group'])
81 service_id = self.createAndAddServiceToHost(
82 host_id,
83 vulnerability['package'],
84 ports=0
85 )
86 self.createAndAddVulnToService(
87 host_id,
88 service_id,
89 name=f'{vulnerability["vuln"]} {vulnerability["package_name"]}',
90 ref=[vulnerability['url']],
91 severity=vulnerability['severity']
92 )
93
94
95
96
97
98 def createPlugin():
99 return WhitesourcePlugin()
33 See the file 'doc/LICENSE' for the license information
44
55 """
6 from faraday_plugins.plugins.plugin import PluginBase
76 import re
87 import os
9 import socket
10 current_path = os.path.abspath(os.getcwd())
8
9 from faraday_plugins.plugins.plugin import PluginBase
10 from faraday_plugins.plugins.plugins_utils import resolve_hostname
11
1112
1213 __author__ = "Facundo de Guzmán, Esteban Guillardoy"
1314 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
3435 self.framework_version = "1.0.0"
3536 self.options = None
3637 self._current_output = None
37 self._command_regex = re.compile(r'^whois.*?')
38 self._command_regex = re.compile(r'^whois\s+.*?')
3839 self._host_ip = None
3940 self._info = 0
4041 self._completition = {
6667 "--version": "output version information and exit",
6768 }
6869
69 global current_path
7070
71 def resolve(self, host):
72 try:
73 return socket.gethostbyname(host)
74 except:
75 pass
76 return host
7771
7872 def parseOutputString(self, output, debug=False):
7973 matches = re.findall("Name Server:\s*(.*)\s*", output)
8074 for m in matches:
8175 m = m.strip()
82 ip = self.resolve(m)
76 ip = resolve_hostname(m)
8377 h_id = self.createAndAddHost(ip, "os unknown")
8478 i_id = self.createAndAddInterface(
8579 h_id, ip, "00:00:00:00:00:00", ip, hostname_resolution=[m])
8680 return True
8781
88 def processCommandString(self, username, current_path, command_string):
89 """
90 """
91 return None
9282
9383
9484 def createPlugin():
33 See the file 'doc/LICENSE' for the license information
44
55 """
6 import re
7 import socket
86 import json
9 from faraday_plugins.plugins.plugin import PluginJsonFormat
107 from urllib.parse import urlparse
118
129
1815 __maintainer__ = "Nicolas Rebagliati"
1916 __email__ = "[email protected]"
2017 __status__ = "Development"
18
19 from faraday_plugins.plugins.plugin import PluginJsonFormat
20 from faraday_plugins.plugins.plugins_utils import resolve_hostname
2121
2222
2323 class WPScanJsonParser:
3838 elif protocol == 'http':
3939 if not port:
4040 port = 80
41 address = self.get_address(hostname)
41 address = resolve_hostname(hostname)
4242 return {'protocol': protocol, 'hostname': hostname, 'port': port, 'address': address}
4343
44 def get_address(self, hostname):
45 # Returns remote IP address from hostname.
46 try:
47 return socket.gethostbyname(hostname)
48 except socket.error as msg:
49 return None
5044
5145
5246 class WPScanPlugin(PluginJsonFormat):
164164 self.framework_version = "1.0.0"
165165 self.options = None
166166 self._current_output = None
167 self._command_regex = re.compile(r'^(sudo x1|\.\/x1).*?')
167 self._command_regex = re.compile(r'^(sudo x1|\.\/x1)\s+.*?')
168168
169169
170170
191191
192192 del parser
193193
194 def processCommandString(self, username, current_path, command_string):
195 return None
196194
197195 def setHost(self):
198196 pass
33 See the file 'doc/LICENSE' for the license information
44 """
55 import re
6 import socket
7 from faraday_plugins.plugins.plugin import PluginBase
86
97 __author__ = "Roberto Focke"
108 __copyright__ = "Copyright (c) 2017, Infobyte LLC"
119 __license__ = ""
1210 __version__ = "1.0.0"
11
12 from faraday_plugins.plugins.plugin import PluginBase
13 from faraday_plugins.plugins.plugins_utils import resolve_hostname
1314
1415
1516 class xsssniper(PluginBase):
2021 self.name = "xsssniper"
2122 self.plugin_version = "0.0.1"
2223 self.version = "1.0.0"
23 self.protocol="tcp"
24 self._command_regex = re.compile(r'^(sudo xsssniper|xsssniper|sudo xsssniper\.py|xsssniper\.py|sudo python '
25 r'xsssniper\.py|.\/xsssniper\.py|python xsssniper\.py)')
24 self.protocol = "tcp"
25 self._command_regex = re.compile(r'^(sudo xsssniper|xsssniper|sudo xsssniper\.py|xsssniper\.py|sudo python'
26 r'xsssniper\.py|.\/xsssniper\.py|python xsssniper\.py)\s+')
2627
2728 def parseOutputString(self, output, debug=False):
2829 parametro = []
3435 linea = linea.lower()
3536 if ((linea.find("target:")>0)):
3637 url = re.findall('(?:[-\w.]|(?:%[\da-fA-F]{2}))+', linea)
38 print(url)
3739 host_id = self.createAndAddHost(url[3])
38 address=socket.gethostbyname(url[3])
40 address = resolve_hostname(url[3])
3941 interface_id = self.createAndAddInterface(host_id,address,ipv4_address=address,hostname_resolution=url[3])
4042 if ((linea.find("method")>0)):
4143 list_a = re.findall("\w+", linea)
5355 website=url[0], path='', method=metodo, pname='',
5456 params=''.join(parametro), request='', response='')
5557
56 def processCommandString(self, username, current_path, command_string):
57 return None
58
5958
6059 def createPlugin():
6160 return xsssniper()
33 See the file 'doc/LICENSE' for the license information
44 """
55 import re
6 import os
7 import socket
6 from urllib.parse import urlparse
87 from faraday_plugins.plugins.plugin import PluginXMLFormat
9 from urllib.parse import urlparse
8 from faraday_plugins.plugins.plugins_utils import resolve_hostname
109
1110 try:
1211 import xml.etree.cElementTree as ET
1716
1817 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
1918
20 current_path = os.path.abspath(os.getcwd())
2119
2220 __author__ = "Francisco Amato"
2321 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
2927 __status__ = "Development"
3028
3129
32 class ParserEtToAscii(ET.TreeBuilder):
33 def __init__(self, *args, **kwargs):
34 super().__init__(*args, **kwargs)
35 print(self._data)
36
37 def data(self, data):
38 self._data.append(data.encode("ascii", errors="backslashreplace"))
39
4030
4131 class ZapXmlParser:
4232 """
134124 self.node = item_node
135125
136126 self.host = self.node.get('host')
137 self.ip = self.resolve(self.host)
127 self.ip = resolve_hostname(self.host)
138128 self.port = self.node.get('port')
139129
140130 self.items = []
152142 return sub_node.text
153143 return None
154144
155 def resolve(self, host):
156
157 try:
158 return socket.gethostbyname(host)
159 except:
160 pass
161
162 return host
163145
164146
165147 class Item:
253235 self.version = "2.4.3"
254236 self.framework_version = "1.0.0"
255237 self.options = None
256 self._current_output = None
257 self.target = None
258 self._command_regex = re.compile(r'^(zap|sudo zap|\.\/zap).*?')
238
259239
260240 def parseOutputString(self, output, debug=False):
261241 """
309289
310290 del parser
311291
312 def processCommandString(self, username, current_path, command_string):
313 return None
314292
315293 def setHost(self):
316294 pass
1111 'lxml',
1212 'html2text',
1313 'beautifulsoup4',
14 'pytz',
15 'python-dateutil',
16 'colorama'
1417 ]
1518
1619
2124 url='',
2225 license='',
2326 author='Faradaysec',
24 author_email='',
25 description='',
27 author_email='[email protected]',
28 description='Faraday plugins package',
2629 include_package_data=True,
2730 install_requires=install_requires,
31 entry_points={
32 'console_scripts': [
33 'faraday-plugins=faraday_plugins.commands:cli',
34 ],
35 },
2836 )
0 {
1 "commands": [
2 {"plugin_id": "ping", "command": "ping -c4 faradaysec.com"},
3 {"plugin_id": "whois", "command": "whois fradaysec.com"},
4 {"plugin_id": "nmap", "command": "nmap fradaysec.com"},
5 {"plugin_id": "skipfish", "command": "skipfish http://fradaysec.com"}
6 ]
7 }
0 import pytest
1
2
3 def pytest_addoption(parser):
4 parser.addoption(
5 "--performance", action="store_true", default=False, help="run performance tests"
6 )
7
8
9 def pytest_configure(config):
10 config.addinivalue_line("markers", "performance: mark test as performance")
11
12
13 def pytest_collection_modifyitems(config, items):
14 if config.getoption("--performance"):
15 # --performance given in cli: do not skip performance tests
16 return
17 performance = pytest.mark.skip(reason="need --performance option to run")
18 for item in items:
19 if "performance" in item.keywords:
20 item.add_marker(performance)
0 #!/usr/bin/env python
1 import hashlib
2 import os
3 import shutil
4 import json
5 import click
6 import colorama
7 from faraday_plugins.plugins.manager import PluginsManager, ReportAnalyzer
8 from faraday_plugins.plugins.plugin import PluginBase
9
10 colorama.init(autoreset=True)
11
12 BLACK_LIST = [
13 'LICENSE',
14 'README.md',
15 '.gitignore',
16 '.gitkeep',
17 'faraday_plugins_tests',
18 ]
19
20 REPORT_COLLECTION_DIR = '../report-collection'
21 FARADAY_PLUGINS_TESTS_DIR = 'faraday_plugins_tests'
22 REPORTS_CHECKSUM = []
23
24 def list_report_files():
25 report_filenames = os.walk(os.path.join(REPORT_COLLECTION_DIR))
26 for root, directory, filenames in report_filenames:
27 if '.git' in directory or FARADAY_PLUGINS_TESTS_DIR in root:
28 continue
29 for filename in filenames:
30 if filename in BLACK_LIST:
31 continue
32 if '.git' in root:
33 continue
34 yield os.path.join(root, filename)
35
36
37 @click.command()
38 @click.option('--force', is_flag=True)
39 def generate_reports_tests(force):
40 generated_summaries = 0
41 analysed_reports = 0
42 click.echo(f"{colorama.Fore.GREEN}Generate Faraday Plugins Tests Summary")
43 plugins_manager = PluginsManager()
44 analyzer = ReportAnalyzer(plugins_manager)
45 for report_file_path in list_report_files():
46 plugin: PluginBase = analyzer.get_plugin(report_file_path)
47 if not plugin:
48 click.echo(f"{colorama.Fore.YELLOW}Plugin for file: ({report_file_path}) not found")
49 else:
50 with open(report_file_path, 'rb') as f:
51 m = hashlib.md5(f.read())
52 file_checksum = m.hexdigest()
53 if file_checksum not in REPORTS_CHECKSUM:
54 REPORTS_CHECKSUM.append(file_checksum)
55 else:
56 click.echo(f"{colorama.Fore.YELLOW}Ignore duplicated file: ({report_file_path})")
57 continue
58 analysed_reports += 1
59 report_file_name = os.path.basename(report_file_path)
60 plugin_name = plugin.id
61 plugin_path = os.path.join(REPORT_COLLECTION_DIR, FARADAY_PLUGINS_TESTS_DIR, plugin_name)
62 if not os.path.isdir(plugin_path):
63 os.mkdir(plugin_path)
64 dst_report_file_path = os.path.join(plugin_path, report_file_name)
65 summary_needed = False
66 summary_file = f"{os.path.splitext(dst_report_file_path)[0]}_summary.json"
67 if not os.path.isfile(dst_report_file_path) or force:
68 summary_needed = True
69 shutil.copyfile(report_file_path, dst_report_file_path)
70 if not os.path.isfile(summary_file) or force:
71 summary_needed = True
72 if summary_needed:
73 try:
74 plugin.processReport(report_file_path)
75 click.echo(f"{colorama.Fore.GREEN}Generate Summary for: {dst_report_file_path} [{plugin}]")
76 summary = plugin.get_summary()
77 with open(summary_file, "w") as f:
78 json.dump(summary, f)
79 generated_summaries += 1
80 except Exception as e:
81 click.echo(f"{colorama.Fore.RED}Error generating summary for file: {report_file_path} [{plugin}]: [{e}]")
82 click.echo(f"Generated {generated_summaries} summaries of {analysed_reports} reports")
83
84
85 if __name__ == "__main__":
86 generate_reports_tests()
0 import json
1 import pytest
2 from faraday_plugins.plugins.manager import PluginsManager, CommandAnalyzer
3 from faraday_plugins.plugins.plugin import PluginBase
4
5
6 plugins_manager = PluginsManager()
7 analyzer = CommandAnalyzer(plugins_manager)
8
9 COMMANDS_FILE = './tests/commands.json'
10
11 def list_commands():
12 with open(COMMANDS_FILE) as f:
13 commands_dict = json.load(f)
14 for command_data in commands_dict["commands"]:
15 yield command_data
16
17
18 @pytest.mark.parametrize("command_data", list_commands())
19 def test_autodetected_on_commands(command_data):
20 plugin_id = command_data["plugin_id"]
21 command_string = command_data["command"]
22 plugin: PluginBase = analyzer.get_plugin(command_string)
23 assert plugin, command_string
24 assert plugin.id.lower() == plugin_id.lower()
25
0 from faraday_plugins.plugins.plugin import PluginBase
1
2
3 def test_get_host_cache_id_with_same_host():
4 host_1 = {'ip': '127.0.0.1'}
5 host_2 = {'ip': '127.0.0.1', 'description': 'test desc'}
6 cache_id_1 = PluginBase.get_host_cache_id(host_1)
7 cache_id_2 = PluginBase.get_host_cache_id(host_2)
8
9 assert cache_id_1 == cache_id_2
10
11
12 def test_get_host_cache_id_with_diffent_ip():
13 host_1 = {'ip': '127.0.0.1'}
14 host_2 = {'ip': '192.168.0.1', 'description': 'test desc'}
15 cache_id_1 = PluginBase.get_host_cache_id(host_1)
16 cache_id_2 = PluginBase.get_host_cache_id(host_2)
17
18 assert cache_id_1 != cache_id_2
19
20
21 def test_get_host_service_cache_id_same_objects():
22 host_1 = {'ip': '127.0.0.1'}
23 host_cache_id_1 = PluginBase.get_host_cache_id(host_1)
24 service_1 = {'protocol': 'tcp', 'port': 80}
25 host_2 = {'ip': '127.0.0.1'}
26 host_cache_id_2 = PluginBase.get_host_cache_id(host_2)
27 service_2 = {'protocol': 'tcp', 'port': 80}
28
29 cache_1 = PluginBase.get_host_service_cache_id(host_cache_id_1, service_1)
30 cache_2 = PluginBase.get_host_service_cache_id(host_cache_id_2, service_2)
31
32 assert cache_1 == cache_2
33
34
35 def test_get_host_service_cache_id_different_host():
36 host_1 = {'ip': '127.0.0.1'}
37 host_cache_id_1 = PluginBase.get_host_cache_id(host_1)
38 service_1 = {'protocol': 'tcp', 'port': 80}
39 host_2 = {'ip': '192.168.0.1'}
40 host_cache_id_2 = PluginBase.get_host_cache_id(host_2)
41 service_2 = {'protocol': 'tcp', 'port': 80}
42
43 cache_1 = PluginBase.get_host_service_cache_id(host_cache_id_1, service_1)
44 cache_2 = PluginBase.get_host_service_cache_id(host_cache_id_2, service_2)
45
46 assert cache_1 != cache_2
47
48
49 def test_get_host_vuln_cache_id_severity_does_not_affect_duplicate():
50 host_1 = {'ip': '127.0.0.1'}
51 host_cache_id_1 = PluginBase.get_host_cache_id(host_1)
52 vuln_1 = {'name': 'test', 'desc': 'test', 'severity': 'low'}
53
54 host_2 = {'ip': '127.0.0.1'}
55 host_cache_id_2 = PluginBase.get_host_cache_id(host_2)
56 vuln_2 = {'name': 'test', 'desc': 'test', 'severity': 'high'}
57
58
59 cache_1 = PluginBase.get_host_vuln_cache_id(host_cache_id_1, vuln_1)
60 cache_2 = PluginBase.get_host_vuln_cache_id(host_cache_id_2, vuln_2)
61
62 assert cache_1 == cache_2
63
64
65 def test_get_host_vuln_cache_id_description_makes_different_cache_ids():
66 host_1 = {'ip': '127.0.0.1'}
67 host_cache_id_1 = PluginBase.get_host_cache_id(host_1)
68 vuln_1 = {'name': 'test', 'desc': 'test', 'severity': 'low'}
69
70 host_2 = {'ip': '127.0.0.1'}
71 host_cache_id_2 = PluginBase.get_host_cache_id(host_2)
72 vuln_2 = {'name': 'test', 'new desc': 'test', 'severity': 'high'}
73
74
75 cache_1 = PluginBase.get_host_vuln_cache_id(host_cache_id_1, vuln_1)
76 cache_2 = PluginBase.get_host_vuln_cache_id(host_cache_id_2, vuln_2)
77
78 assert cache_1 != cache_2
79
80
81 def test_get_service_vuln_cache_id_severity_does_not_affect_cache_id():
82 host_1 = {'ip': '127.0.0.1'}
83 host_cache_id_1 = PluginBase.get_host_cache_id(host_1)
84 service_1 = {'protocol': 'tcp', 'port': 80}
85
86 host_2 = {'ip': '127.0.0.1'}
87 host_cache_id_2 = PluginBase.get_host_cache_id(host_2)
88 host_2 = {'ip': '127.0.0.1'}
89 host_cache_id_2 = PluginBase.get_host_cache_id(host_2)
90 service_2 = {'protocol': 'tcp', 'port': 80}
91
92 service_cache_1 = PluginBase.get_host_service_cache_id(host_cache_id_1, service_1)
93 service_cache_2 = PluginBase.get_host_service_cache_id(host_cache_id_2, service_2)
94
95 vuln_2 = {'name': 'test', 'desc': 'test', 'severity': 'high', 'method': 'GET'}
96 vuln_1 = {'name': 'test', 'desc': 'test', 'severity': 'low', 'method': 'GET'}
97
98 cache_1 = PluginBase.get_service_vuln_cache_id(host_cache_id_1, service_cache_1, vuln_1)
99 cache_2 = PluginBase.get_service_vuln_cache_id(host_cache_id_2, service_cache_2, vuln_2)
100
101 assert cache_1 == cache_2
102
103 def test_get_service_vuln_cache_id_with_different_service_return_different_id():
104 host_1 = {'ip': '127.0.0.1'}
105 host_cache_id_1 = PluginBase.get_host_cache_id(host_1)
106 service_1 = {'protocol': 'tcp', 'port': 80}
107
108 host_2 = {'ip': '127.0.0.1'}
109 host_cache_id_2 = PluginBase.get_host_cache_id(host_2)
110 host_2 = {'ip': '127.0.0.1'}
111 host_cache_id_2 = PluginBase.get_host_cache_id(host_2)
112 service_2 = {'protocol': 'tcp', 'port': 22}
113
114 service_cache_1 = PluginBase.get_host_service_cache_id(host_cache_id_1, service_1)
115 service_cache_2 = PluginBase.get_host_service_cache_id(host_cache_id_2, service_2)
116
117 vuln_2 = {'name': 'test', 'desc': 'test', 'severity': 'high', 'method': 'GET'}
118 vuln_1 = {'name': 'test', 'desc': 'test', 'severity': 'low', 'method': 'GET'}
119
120 cache_1 = PluginBase.get_service_vuln_cache_id(host_cache_id_1, service_cache_1, vuln_1)
121 cache_2 = PluginBase.get_service_vuln_cache_id(host_cache_id_2, service_cache_2, vuln_2)
122
123 assert cache_1 != cache_2
00 import os
1
1 import socket
22 import json
33 import pytest
44 from faraday_plugins.plugins.manager import PluginsManager, ReportAnalyzer
55 from faraday_plugins.plugins.plugin import PluginBase
6 from faraday.server.api.modules.bulk_create import BulkCreateSchema
67
78 BLACK_LIST = [
89 'LICENSE',
910 'README.md',
1011 '.gitignore',
1112 '.gitkeep',
13 'faraday_plugins_tests',
14
1215 ]
1316
17 plugins_manager = PluginsManager()
18 analyzer = ReportAnalyzer(plugins_manager)
19
20 PLUGINS_CACHE = {}
21 REPORTS_JSON_CACHE = {}
22
23 SKIP_IP_PLUGINS = ['Fortify']
24
25 REPORTS_SUMMARY_DIR = './report-collection/faraday_plugins_tests'
26
27
28 def get_plugin_from_cache(report_file):
29 plugin = PLUGINS_CACHE.get(report_file)
30 if not plugin:
31 plugin: PluginBase = analyzer.get_plugin(report_file)
32 if plugin:
33 save_plugin_in_cache(report_file, plugin)
34 return plugin
35
36
37 def save_plugin_in_cache(report_file, plugin):
38 if report_file not in PLUGINS_CACHE:
39 PLUGINS_CACHE[report_file] = plugin
40
41
42 def get_report_json_from_cache(report_file):
43 plugin_json = REPORTS_JSON_CACHE.get(report_file)
44 if not plugin_json:
45 plugin = get_plugin_from_cache(report_file)
46 if plugin:
47 plugin.processReport(report_file)
48 plugin_json = json.loads(plugin.get_json())
49 REPORTS_JSON_CACHE[report_file] = plugin_json
50 else:
51 plugin = get_plugin_from_cache(report_file)
52 return plugin, plugin_json
53
54
1455 def list_report_files():
15 report_filenames = os.walk('./report-collection')
16
56 report_filenames = os.walk(REPORTS_SUMMARY_DIR)
1757 for root, directory, filenames in report_filenames:
18 if '.git' in directory:
58 if '.git' in directory or 'faraday_plugins_tests' in directory:
1959 continue
2060 for filename in filenames:
2161 if filename in BLACK_LIST:
2262 continue
2363 if '.git' in root:
2464 continue
25 yield os.path.join(root, filename)
65 if not filename.endswith('_summary.json'):
66 yield os.path.join(root, filename)
2667
2768
28 @pytest.mark.skip(reason="Skip auto detection test until we review all the report files")
69 def is_valid_ipv4_address(address):
70 try:
71 socket.inet_pton(socket.AF_INET, address)
72 except AttributeError: # no inet_pton here, sorry
73 try:
74 socket.inet_aton(address)
75 except socket.error:
76 return False
77 return address.count('.') == 3
78 except socket.error: # not a valid address
79 return False
80 return True
81
82
83 def is_valid_ipv6_address(address):
84 try:
85 socket.inet_pton(socket.AF_INET6, address)
86 except socket.error: # not a valid address
87 return False
88 return True
89
90
91 def is_valid_ip_address(address):
92 return (is_valid_ipv4_address(address) or is_valid_ipv6_address(address))
93
94 def test_reports_collection_exists():
95 assert os.path.isdir(REPORTS_SUMMARY_DIR) is True, "Please clone the report-collection repo!"
96
2997 @pytest.mark.parametrize("report_filename", list_report_files())
3098 def test_autodetected_on_all_report_collection(report_filename):
31 plugins_manager = PluginsManager()
32 analyzer = ReportAnalyzer(plugins_manager)
33 plugin: PluginBase = analyzer.get_plugin(report_filename)
99 plugin: PluginBase = get_plugin_from_cache(report_filename)
34100 assert plugin, report_filename
35101
36102
37103 @pytest.mark.parametrize("report_filename", list_report_files())
38 def test_detected_tools_on_all_report_collection(report_filename):
104 def test_schema_on_all_reports(report_filename):
105 plugin, plugin_json = get_report_json_from_cache(report_filename)
106 if plugin_json:
107 serializer = BulkCreateSchema()
108 res = serializer.loads(json.dumps(plugin_json))
109 assert not res.errors
110
111
112 @pytest.mark.skip(reason="Skip validate ip format")
113 @pytest.mark.parametrize("report_filename", list_report_files())
114 def test_host_ips_all_reports(report_filename):
115 plugin, plugin_json = get_report_json_from_cache(report_filename)
116 if plugin_json:
117 if plugin.id not in SKIP_IP_PLUGINS:
118 for host in plugin_json['hosts']:
119 assert is_valid_ip_address(host['ip']) is True
120
121
122 @pytest.mark.parametrize("report_filename", list_report_files())
123 def test_summary_reports(report_filename):
124 plugin, plugin_json = get_report_json_from_cache(report_filename)
125 if plugin_json:
126 summary_file = f"{os.path.splitext(report_filename)[0]}_summary.json"
127 assert os.path.isfile(summary_file) is True
128 with open(summary_file) as f:
129 saved_summary = json.load(f)
130 summary = plugin.get_summary()
131 vuln_hashes = set(summary['vuln_hashes'])
132 saved_vuln_hashes = set(saved_summary.get('vuln_hashes', []))
133 assert summary['hosts'] == saved_summary['hosts']
134 assert summary['services'] == saved_summary['services']
135 assert summary['hosts_vulns'] == saved_summary['hosts_vulns']
136 assert summary['services_vulns'] == saved_summary['services_vulns']
137 assert summary['severity_vulns'] == saved_summary['severity_vulns']
138 assert vuln_hashes == saved_vuln_hashes
139
140
141 @pytest.mark.performance
142 @pytest.mark.parametrize("report_filename", list_report_files())
143 def test_detected_tools_on_all_report_collection(report_filename, benchmark):
39144 plugins_manager = PluginsManager()
40145 analyzer = ReportAnalyzer(plugins_manager)
41146 plugin: PluginBase = analyzer.get_plugin(report_filename)
42147 if not plugin:
43148 return
44149 assert plugin, report_filename
45 plugin.processReport(report_filename)
150 benchmark(plugin.processReport, report_filename)
46151 plugin_json = json.loads(plugin.get_json())
47152 assert "hosts" in plugin_json
48153 assert "command" in plugin_json
49 assert len(plugin_json) == 2
154 assert os.path.isfile(report_filename) is True
155