Codebase list faraday-plugins / upstream/1.0
New upstream version 1.0 Sophie Brun 4 years ago
157 changed file(s) with 20483 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 [flake8]
1 ignore = W391, E722, E303, E701, W291, E222, E241, E225, E115, E117, E122, E123, E124, E501, E261, E127, E265, F401, W292, W293, W605, E231, E221, E222, F841, E128, W504, E202, F811, E251, E226, E203, E262, E225, E305, E302, E305
2 exclude= report-collection
0 # Created by .ignore support plugin (hsz.mobi)
1 ### Python template
2 # Byte-compiled / optimized / DLL files
3 __pycache__/
4 *.py[cod]
5 *$py.class
6
7 # C extensions
8 *.so
9
10 # Distribution / packaging
11 .Python
12 build/
13 develop-eggs/
14 dist/
15 downloads/
16 eggs/
17 .eggs/
18 lib/
19 lib64/
20 parts/
21 sdist/
22 var/
23 wheels/
24 pip-wheel-metadata/
25 share/python-wheels/
26 *.egg-info/
27 .installed.cfg
28 *.egg
29 MANIFEST
30
31 # PyInstaller
32 # Usually these files are written by a python script from a template
33 # before PyInstaller builds the exe, so as to inject date/other infos into it.
34 *.manifest
35 *.spec
36
37 # Installer logs
38 pip-log.txt
39 pip-delete-this-directory.txt
40
41 # Unit test / coverage reports
42 htmlcov/
43 .tox/
44 .nox/
45 .coverage
46 .coverage.*
47 .cache
48 nosetests.xml
49 coverage.xml
50 *.cover
51 .hypothesis/
52 .pytest_cache/
53
54 # Translations
55 *.mo
56 *.pot
57
58 # Django stuff:
59 *.log
60 local_settings.py
61 db.sqlite3
62 db.sqlite3-journal
63
64 # Flask stuff:
65 instance/
66 .webassets-cache
67
68 # Scrapy stuff:
69 .scrapy
70
71 # Sphinx documentation
72 docs/_build/
73
74 # PyBuilder
75 target/
76
77 # Jupyter Notebook
78 .ipynb_checkpoints
79
80 # IPython
81 profile_default/
82 ipython_config.py
83
84 # pyenv
85 .python-version
86
87 # pipenv
88 # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89 # However, in case of collaboration, if having platform-specific dependencies or dependencies
90 # having no cross-platform support, pipenv may install dependencies that don't work, or not
91 # install all needed dependencies.
92 #Pipfile.lock
93
94 # celery beat schedule file
95 celerybeat-schedule
96
97 # SageMath parsed files
98 *.sage.py
99
100 # Environments
101 .env
102 .venv
103 env/
104 venv/
105 ENV/
106 env.bak/
107 venv.bak/
108
109 # Spyder project settings
110 .spyderproject
111 .spyproject
112
113 # Rope project settings
114 .ropeproject
115
116 # mkdocs documentation
117 /site
118
119 # mypy
120 .mypy_cache/
121 .dmypy.json
122 dmypy.json
123
124 # Pyre type checker
125 .pyre/
126
127 .idea
128 /report-collection/
0 stages:
1 - pre_testing
2 - testing
3
4 before_script:
5 - apt-get update -qy
6 - pip install pip -U
7
8 flake8:
9 image: python:3
10 stage: pre_testing
11 before_script:
12 - pip install flake8
13 # Help flake8 to find the Python files without .py extension.
14 - find * -type f -name '*.py' > files.txt
15 - find * -type f -print0 | xargs -0 file | grep 'Python script' | cut -d':' -f1 >> files.txt
16 - sort -u files.txt | tee files.processed
17 script:
18 - python -m flake8 --statistics --count $(cat files.processed)
19 after_script:
20 - wc -l files.processed
21
22 tests:
23 image: python:3
24 stage: testing
25 coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/'
26 before_script:
27 - pip3 install virtualenv
28 - virtualenv -p python3 faraday_venv
29 - source faraday_venv/bin/activate
30 - 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
33 script:
34 - cd -
35 - source faraday_venv/bin/activate
36 - python3 setup.py install
37 - cd run_from && pytest ../tests --capture=sys -v --cov=faraday_plugins --color=yes --disable-warnings
0 include faraday_plugins/plugins/port_mapper.txt
0 ## Install
1
2 ```shell script
3 cd faraday-plugins
4 python setup.py install
5 ```
6
7 ## Commands
8
9 > List Plugins
10
11 ```shell script
12 python -m faraday_plugins list
13 ```
14
15 > Test autodetect plugin from report
16
17 ```shell script
18 python -m faraday_plugins detect /path/to/report.xml
19 ```
20
21
22 > Test report with plugin
23
24 ```shell script
25 python -m faraday_plugins process appscan /path/to/report.xml
26 ```
27
0 __version__ = '1.0rc1'
1
2
0 import os
1 import click
2 from .plugins.manager import PluginsManager, ReportAnalyzer
3
4
5 @click.group()
6 def cli():
7 pass
8
9
10 @cli.command()
11 def list():
12 plugins_manager = PluginsManager()
13 click.echo("Available Plugins:")
14 loaded_plugins = 0
15 for plugin_id, plugin in plugins_manager.get_plugins():
16 click.echo(f"{plugin.id} - {plugin.name}")
17 loaded_plugins += 1
18 click.echo(f"Loaded Plugins: {loaded_plugins}")
19
20
21 @cli.command()
22 @click.argument('plugin_id')
23 @click.argument('report_file')
24 def process(plugin_id, report_file):
25 if not os.path.isfile(report_file):
26 click.echo(f"File {report_file} Don't Exists")
27 else:
28 plugins_manager = PluginsManager()
29 plugin = plugins_manager.get_plugin(plugin_id)
30 if plugin:
31 plugin.processReport(report_file)
32 click.echo(plugin.get_json())
33 else:
34 click.echo(f"Unknown Plugin: {plugin_id}")
35
36
37 @cli.command()
38 @click.argument('report_file')
39 def detect(report_file):
40 if not os.path.isfile(report_file):
41 click.echo(f"File {report_file} Don't Exists")
42 else:
43 plugins_manager = PluginsManager()
44 analyzer = ReportAnalyzer(plugins_manager)
45 plugin = analyzer.get_plugin(report_file)
46 if plugin:
47 click.echo(plugin)
48 else:
49 click.echo(f"Failed to detect")
50
51
52 if __name__ == "__main__":
53 cli()
0 import logging
1 import traceback
2 import re
3 import os
4 import sys
5 import pkgutil
6 from importlib import import_module
7 from importlib.machinery import SourceFileLoader
8
9 from . import repo
10
11 logger = logging.getLogger("faraday").getChild(__name__)
12
13 try:
14 import xml.etree.cElementTree as ET
15 except ImportError:
16 logger.warning("cElementTree could not be imported. Using ElementTree instead")
17 import xml.etree.ElementTree as ET
18
19
20 class ReportAnalyzer:
21
22 def __init__(self, plugin_manager):
23 self.plugin_manager = plugin_manager
24
25 def get_plugin(self, report_path):
26 plugin = None
27 if not os.path.isfile(report_path):
28 logger.error("Report [%s] don't exists", report_path)
29 return plugin
30 else:
31 file_name = os.path.basename(report_path)
32 plugin = self._get_plugin_by_name(file_name)
33 if not plugin: # Was unable to detect plugin from report file name
34 logger.debug("Plugin by name not found")
35 plugin = self._get_plugin_by_file_type(report_path)
36 if not plugin:
37 logger.debug("Plugin by file not found")
38 if not plugin:
39 logger.debug("Plugin for file (%s) not found", report_path)
40 return plugin
41
42 def _get_plugin_by_name(self, file_name_base):
43 plugin_id = None
44 plugin_name_regex = r".*_faraday_(?P<plugin_name>.+)\..*$"
45 match = re.match(plugin_name_regex, file_name_base)
46 if match:
47 plugin_id = match.groupdict()['plugin_name'].lower()
48 logger.debug("Plugin name match: %s", plugin_id)
49 plugin = self.plugin_manager.get_plugin(plugin_id)
50 if plugin:
51 logger.debug("Plugin by name Found: %s", plugin.id)
52 return plugin
53 else:
54 logger.debug("Invalid plugin from file name: %s", plugin_id)
55 return None
56 else:
57 logger.debug("Could not extract plugin_id from filename: %s", file_name_base)
58 return plugin_id
59
60 def _get_plugin_by_file_type(self, report_path):
61 plugin = None
62 file_name = os.path.basename(report_path)
63 file_name_base, file_extension = os.path.splitext(file_name)
64 file_extension = file_extension.lower()
65 main_tag = None
66 logger.debug("Analyze report File")
67 # Try to parse as xml
68 try:
69 report_file = open(report_path, "rb")
70 except Exception as e:
71 logger.error("Error reading report content [%s]", e)
72 else:
73 try:
74 for event, elem in ET.iterparse(report_file, ('start',)):
75 main_tag = elem.tag
76 break
77 logger.debug("Found XML content on file: %s - Main tag: %s", report_path, main_tag)
78 except Exception as e:
79 logger.debug("Non XML content [%s] - %s", report_path, e)
80 finally:
81 report_file.close()
82 for _plugin_id, _plugin in self.plugin_manager.get_plugins():
83 logger.debug("Try: %s", _plugin_id)
84 try:
85 if _plugin.report_belongs_to(main_tag=main_tag, report_path=report_path, extension=file_extension):
86 plugin = _plugin
87 logger.debug("Plugin by File Found: %s", plugin.id)
88 break
89 except Exception as e:
90 logger.error("Error in plugin analysis: (%s) %s", _plugin_id, e)
91 return plugin
92
93
94 class PluginsManager:
95
96 def __init__(self):
97 self.plugins = {}
98 self.plugin_modules = {}
99 self._load_plugins()
100
101 def _load_plugins(self):
102 logger.info("Loading Native Plugins...")
103 if not self.plugins:
104 for _, name, _ in filter(lambda x: x[2], pkgutil.iter_modules(repo.__path__)):
105 try:
106 plugin_module = import_module(f"faraday_plugins.plugins.repo.{name}.plugin")
107 if hasattr(plugin_module, "createPlugin"):
108 plugin_instance = plugin_module.createPlugin()
109 plugin_id = plugin_instance.id.lower()
110 if plugin_id not in self.plugin_modules:
111 self.plugin_modules[plugin_id] = plugin_module
112 logger.debug("Load Plugin [%s]", name)
113 else:
114 logger.debug("Plugin already loaded [%s]", plugin_id)
115 else:
116 logger.error("Invalid Plugin [%s]", name)
117 except Exception as e:
118 logger.error("Cant load plugin module: %s [%s]", name, e)
119 try:
120 import faraday.server.config
121 if os.path.isdir(faraday.server.config.faraday_server.custom_plugins_folder):
122 logger.info("Loading Custom Plugins...")
123 dir_name_regexp = re.compile(r"^[\d\w\-\_]+$")
124 for name in os.listdir(faraday.server.config.faraday_server.custom_plugins_folder):
125 if dir_name_regexp.match(name) and name != "__pycache__":
126 try:
127 module_path = os.path.join(faraday.server.config.faraday_server.custom_plugins_folder,
128 name)
129 sys.path.append(module_path)
130 module_filename = os.path.join(module_path, "plugin.py")
131 file_ext = os.path.splitext(module_filename)[1]
132 if file_ext.lower() == '.py':
133 if name not in self.plugin_modules:
134 loader = SourceFileLoader(name, module_filename)
135 plugin_module = loader.load_module()
136 plugin_instance = plugin_module.createPlugin()
137 plugin_id = plugin_instance.id.lower()
138 if plugin_id not in self.plugin_modules:
139 self.plugin_modules[plugin_id] = plugin_module
140 else:
141 logger.debug("Plugin with same name already loaded [%s]", name)
142 logger.debug('Loading plugin {0}'.format(name))
143 except Exception as e:
144 logger.debug("An error ocurred while loading plugin %s.\n%s", module_filename,
145 traceback.format_exc())
146 logger.warning(e)
147 except Exception as e:
148 logger.info("Can't import faraday server, no custom plugins will be loaded")
149 logger.info("%s plugins loaded", len(self.plugin_modules))
150
151 def get_plugin(self, plugin_id):
152 plugin = None
153 plugin_id = plugin_id.lower()
154 if plugin_id in self.plugin_modules:
155 plugin = self.plugin_modules[plugin_id].createPlugin()
156 else:
157 logger.debug("Unknown Plugin: %s", plugin_id)
158 return plugin
159
160 def get_plugins(self):
161 for plugin_id, plugin_module in self.plugin_modules.items():
162 logger.debug("Instance Plugin: %s", plugin_id)
163 yield plugin_id, plugin_module.createPlugin()
0 """
1 Faraday Penetration Test IDE
2 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
3 See the file 'doc/LICENSE' for the license information
4
5 """
6 import os
7 import re
8 import uuid
9 import logging
10 import simplejson as json
11 from datetime import datetime
12
13
14 logger = logging.getLogger("faraday").getChild(__name__)
15
16
17 class PluginBase:
18 # TODO: Add class generic identifier
19 class_signature = "PluginBase"
20
21 def __init__(self):
22 # Must be unique. Check that there is not
23 # an existant plugin with the same id.
24 # TODO: Make script that list current ids.
25 self.id = None
26 self._rid = id(self)
27 self.version = None
28 self.name = None
29 self.description = ""
30 self._command_regex = None
31 self._output_file_path = None
32 self.framework_version = None
33 self._completition = {}
34 self._new_elems = []
35 self._settings = {}
36 self.command_id = None
37 self.cache = {}
38 self._hosts_cache = {}
39 self.start_date = datetime.now()
40 self.logger = logger.getChild(self.__class__.__name__)
41 self.open_options = {"mode": "r", "encoding": "utf-8"}
42 self.vulns_data = {"hosts": [], "command": {"tool": "",
43 "command": "",
44 "params": "",
45 "user": "",
46 "hostname": "",
47 "start_date": self.start_date.isoformat(),
48 "duration": 0,
49 "import_source": "report"}}
50
51 def __str__(self):
52 return f"Plugin: {self.id}"
53
54 @staticmethod
55 def normalize_severity(severity):
56 if severity is not None:
57 severity = str(severity).lower()
58 else:
59 severity = ""
60
61 def align_string_based_vulns(severity):
62 severities = ['info', 'low', 'med', 'high', 'critical']
63 for sev in severities:
64 if severity[0:3] in sev:
65 return sev
66 return severity
67 severity = align_string_based_vulns(severity)
68 # Transform numeric severity into desc severity
69 numeric_severities = {"0": "info",
70 "1": "low",
71 "2": "med",
72 "3": "high",
73 "4": "critical"}
74 if severity not in numeric_severities.values():
75 severity = numeric_severities.get(severity, 'unclassified')
76 return severity
77
78 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']}"
83 if cache_id not in self._hosts_cache:
84 obj_uuid = self.save_cache(obj)
85 self.vulns_data["hosts"].append(obj)
86 self._hosts_cache[cache_id] = obj_uuid
87 else:
88 obj_uuid = self._hosts_cache[cache_id]
89 return obj_uuid
90
91 def save_cache(self, obj):
92 obj_uuid = uuid.uuid1()
93 self.cache[obj_uuid] = obj
94 return obj_uuid
95
96 def report_belongs_to(self, **kwargs):
97 return False
98
99 def has_custom_output(self):
100 return bool(self._output_file_path)
101
102 def get_custom_file_path(self):
103 return self._output_file_path
104
105 def set_actions_queue(self, _pending_actions):
106 """
107 We use plugin controller queue to add actions created by plugins.
108 Plugin controller will consume this actions.
109
110 :param controller: plugin controller
111 :return: None
112 """
113 self._pending_actions = _pending_actions
114
115 def setCommandID(self, command_id):
116 self.command_id = command_id
117
118 def getSettings(self):
119 for param, (param_type, value) in self._settings.items():
120 yield param, value
121
122 def get_ws(self): # TODO Borrar
123 return ""
124
125 def getSetting(self, name):
126 setting_type, value = self._settings[name]
127 return value
128
129 def addSetting(self, param, param_type, value):
130 self._settings[param] = param_type, value
131
132 def updateSettings(self, new_settings):
133 for name, value in new_settings.items():
134 if name in self._settings:
135 setting_type, curr_value = self._settings[name]
136 self._settings[name] = setting_type, setting_type(value)
137
138 def canParseCommandString(self, current_input):
139 """
140 This method can be overriden in the plugin implementation
141 if a different kind of check is needed
142 """
143 return (self._command_regex is not None and
144 self._command_regex.match(current_input.strip()) is not None)
145
146 def getCompletitionSuggestionsList(self, current_input):
147 """
148 This method can be overriden in the plugin implementation
149 if a different kind of check is needed
150 """
151 words = current_input.split(" ")
152 cword = words[len(words) - 1]
153 options = {}
154 for k, v in self._completition.items():
155 if re.search(str("^" + cword), k, flags=re.IGNORECASE):
156 options[k] = v
157 return options
158
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()):
162 self._parse_filename(self.get_custom_file_path())
163 else:
164 self.parseOutputString(output)
165
166 def _parse_filename(self, filename):
167 with open(filename, **self.open_options) as output:
168 self.parseOutputString(output.read())
169
170 def processReport(self, filepath, user="faraday"):
171 if os.path.isfile(filepath):
172 self._parse_filename(filepath)
173 self.vulns_data["command"]["params"] = filepath
174 self.vulns_data["command"]["user"] = user
175 else:
176 raise FileNotFoundError(filepath)
177
178 def parseOutputString(self, output):
179 """
180 This method must be implemented.
181 This method will be called when the command finished executing and
182 the complete output will be received to work with it
183 Using the output the plugin can create and add hosts, interfaces,
184 services, etc.
185 """
186 raise NotImplementedError('This method must be implemented.')
187
188
189 def createAndAddHost(self, name, os="unknown", hostnames=None, mac=None):
190 if not hostnames:
191 hostnames = []
192 if os is None:
193 os = "unknown"
194 host = {"ip": name, "os": os, "hostnames": hostnames, "description": "", "mac": mac,
195 "credentials": [], "services": [], "vulnerabilities": [],
196 }
197 host_id = self.save_host_cache(host)
198 return host_id
199
200 # @deprecation.deprecated(deprecated_in="3.0", removed_in="3.5",
201 # current_version=VERSION,
202 # details="Interface object removed. Use host or service instead")
203 def createAndAddInterface(
204 self, host_id, name="", mac="00:00:00:00:00:00",
205 ipv4_address="0.0.0.0", ipv4_mask="0.0.0.0", ipv4_gateway="0.0.0.0",
206 ipv4_dns=None, ipv6_address="0000:0000:0000:0000:0000:0000:0000:0000",
207 ipv6_prefix="00",
208 ipv6_gateway="0000:0000:0000:0000:0000:0000:0000:0000", ipv6_dns=None,
209 network_segment="", hostname_resolution=None):
210 if ipv4_dns is None:
211 ipv4_dns = []
212 if ipv6_dns is None:
213 ipv6_dns = []
214 if hostname_resolution is None:
215 hostname_resolution = []
216 if not isinstance(hostname_resolution, list):
217 self.logger.warning("hostname_resolution parameter must be a list and is (%s)", type(hostname_resolution))
218 hostname_resolution = [hostname_resolution]
219 # We don't use interface anymore, so return a host id to maintain
220 # backwards compatibility
221 # Little hack because we dont want change all the plugins for add hostnames in Host object.
222 # SHRUG
223 host = self.get_from_cache(host_id)
224 for hostname in hostname_resolution:
225 if hostname not in host["hostnames"]:
226 host["hostnames"].append(hostname)
227 host["mac"] = mac
228 return host_id
229
230 # @deprecation.deprecated(deprecated_in="3.0", removed_in="3.5",
231 # current_version=VERSION,
232 # details="Interface object removed. Use host or service instead. Service will be attached to Host!")
233 def createAndAddServiceToInterface(self, host_id, interface_id, name,
234 protocol="tcp?", ports=None,
235 status="open", version="unknown",
236 description=""):
237 return self.createAndAddServiceToHost(host_id, name, protocol, ports, status, version, description)
238
239 def createAndAddServiceToHost(self, host_id, name,
240 protocol="tcp?", ports=None,
241 status="open", version="unknown",
242 description=""):
243 if ports:
244 if isinstance(ports, list):
245 ports = int(ports[0])
246 elif isinstance(ports, str):
247 ports = int(ports)
248
249 if status not in ("open", "closed", "filtered"):
250 self.logger.warning('Unknown service status %s. Using "open" instead', status)
251 status = 'open'
252 service = {"name": name, "protocol": protocol, "port": ports, "status": status,
253 "version": version, "description": description, "credentials": [], "vulnerabilities": []}
254 host = self.get_from_cache(host_id)
255 host["services"].append(service)
256 service_id = self.save_cache(service)
257 return service_id
258
259 def createAndAddVulnToHost(self, host_id, name, desc="", ref=None,
260 severity="", resolution="", data="", external_id=None):
261 if ref is None:
262 ref = []
263 vulnerability = {"name": name, "desc": desc, "severity": self.normalize_severity(severity), "refs": ref, "external_id": external_id,
264 "type": "Vulnerability", "resolution": resolution, "data": data}
265 host = self.get_from_cache(host_id)
266 host["vulnerabilities"].append(vulnerability)
267 vulnerability_id = len(host["vulnerabilities"]) - 1
268 return vulnerability_id
269
270 # @deprecation.deprecated(deprecated_in="3.0", removed_in="3.5",
271 # current_version=VERSION,
272 # details="Interface object removed. Use host or service instead. Vuln will be added to Host")
273 def createAndAddVulnToInterface(self, host_id, interface_id, name,
274 desc="", ref=None, severity="",
275 resolution="", data=""):
276 return self.createAndAddVulnToHost(host_id, name, desc=desc, ref=ref, severity=severity,
277 resolution=resolution, data=data)
278
279 def createAndAddVulnToService(self, host_id, service_id, name, desc="",
280 ref=None, severity="", resolution="", data="", external_id=None):
281 if ref is None:
282 ref = []
283 vulnerability = {"name": name, "desc": desc, "severity": self.normalize_severity(severity), "refs": ref, "external_id": external_id,
284 "type": "Vulnerability", "resolution": resolution, "data": data}
285 service = self.get_from_cache(service_id)
286 service["vulnerabilities"].append(vulnerability)
287 vulnerability_id = self.save_cache(vulnerability)
288 return vulnerability_id
289
290 def createAndAddVulnWebToService(self, host_id, service_id, name, desc="",
291 ref=None, severity="", resolution="",
292 website="", path="", request="",
293 response="", method="", pname="",
294 params="", query="", category="", data="", external_id=None):
295 if params is None:
296 params = ""
297 if response is None:
298 response = ""
299 if method is None:
300 method = ""
301 if pname is None:
302 pname = ""
303 if params is None:
304 params = ""
305 if query is None:
306 query = ""
307 if ref is None:
308 ref = []
309 vulnerability = {"name": name, "desc": desc, "severity": self.normalize_severity(severity), "refs": ref, "external_id": external_id,
310 "type": "VulnerabilityWeb", "resolution": resolution, "data": data, "website": website,
311 "path": path, "request": request, "response": response, "method": method, "pname": pname,
312 "params": params, "query": query, "category": category}
313 service = self.get_from_cache(service_id)
314 service["vulnerabilities"].append(vulnerability)
315 vulnerability_id = self.save_cache(vulnerability)
316 return vulnerability_id
317
318
319 def createAndAddNoteToHost(self, host_id, name, text):
320 return None
321
322 def createAndAddNoteToInterface(self, host_id, interface_id, name, text):
323 return None
324
325 def createAndAddNoteToService(self, host_id, service_id, name, text):
326 return None
327
328 def createAndAddNoteToNote(self, host_id, service_id, note_id, name, text):
329 return None
330
331 def createAndAddCredToService(self, host_id, service_id, username,
332 password):
333 credential = {"name": "credential", "username": username, "password": password}
334 service = self.get_from_cache(service_id)
335 service["credentials"].append(credential)
336 credential_id = self.save_cache(credential)
337 return credential_id
338
339 def log(self, msg, level='INFO'):# TODO borrar
340 pass
341 #self.__addPendingAction(Modelactions.LOG, msg, level)
342
343 def devlog(self, msg): # TODO borrar
344 pass
345 #self.__addPendingAction(Modelactions.DEVLOG, msg)
346
347 def get_data(self):
348 self.vulns_data["command"]["tool"] = self.id
349 self.vulns_data["command"]["command"] = self.id
350 self.vulns_data["command"]["duration"] = (datetime.now() - self.start_date).microseconds
351 return self.vulns_data
352
353 def get_json(self):
354 self.logger.debug("Generate Json")
355 return json.dumps(self.get_data())
356
357 class PluginTerminalOutput(PluginBase):
358 def __init__(self):
359 super().__init__()
360
361 def processOutput(self, term_output):
362 try:
363 self.parseOutputString(term_output)
364 except Exception as e:
365 self.logger.error(e)
366
367
368 class PluginCustomOutput(PluginBase):
369 def __init__(self):
370 super().__init__()
371
372 def processOutput(self, term_output):
373 # we discard the term_output since it's not necessary
374 # for this type of plugins
375 self.processReport(self._output_file_path)
376
377
378 class PluginByExtension(PluginBase):
379 def __init__(self):
380 super().__init__()
381 self.extension = []
382
383 def report_belongs_to(self, extension="", **kwargs):
384 match = False
385 if type(self.extension) == str:
386 match = (self.extension == extension)
387 elif type(self.extension) == list:
388 match = (extension in self.extension)
389 self.logger.debug("Extension Match: [%s =/in %s] -> %s", extension, self.extension, match)
390 return match
391
392
393 class PluginXMLFormat(PluginByExtension):
394
395 def __init__(self):
396 super().__init__()
397 self.identifier_tag = []
398 self.extension = ".xml"
399 self.open_options = {"mode": "rb"}
400
401 def report_belongs_to(self, main_tag="", **kwargs):
402 match = False
403 if super().report_belongs_to(**kwargs):
404 if type(self.identifier_tag) == str:
405 match = (main_tag == self.identifier_tag)
406 elif type(self.identifier_tag) == list:
407 match = (main_tag in self.identifier_tag)
408 self.logger.debug("Tag Match: [%s =/in %s] -> %s", main_tag, self.identifier_tag, match)
409 return match
410
411
412 class PluginJsonFormat(PluginByExtension):
413
414 def __init__(self):
415 super().__init__()
416 self.json_keys = set()
417 self.extension = ".json"
418
419 def report_belongs_to(self, **kwargs):
420 match = False
421 if super().report_belongs_to(**kwargs):
422 pass
423 return match
424
425
426
427 # 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 import os
7 import logging
8 import faraday_plugins
9 from urllib.parse import urlsplit
10
11
12 SERVICE_MAPPER = None
13
14 logger = logging.getLogger(__name__)
15
16
17 def get_vulnweb_url_fields(url):
18 """Given a URL, return kwargs to pass to createAndAddVulnWebToService."""
19 parse = urlsplit(url)
20 return {
21 "website": "{}://{}".format(parse.scheme, parse.netloc),
22 "path": parse.path,
23 "query": parse.query
24 }
25
26 def filter_services():
27 global SERVICE_MAPPER
28 if not SERVICE_MAPPER:
29 logger.debug("Load service mappers")
30 filename = os.path.join(os.path.dirname(__file__), "port_mapper.txt")
31 with open(filename, encoding='utf-8') as fp:
32 SERVICE_MAPPER = list(map(lambda x: x.strip().split('\t'), list(filter(len, fp.readlines()))))
33 return SERVICE_MAPPER
34
35
36 def get_all_protocols():
37 protocols = [
38 'ip',
39 'tcp',
40 'udp',
41 'icmp',
42 'sctp',
43 'hopopt',
44 'igmp',
45 'ggp',
46 'ip-encap',
47 'st',
48 'egp',
49 'igp',
50 'pup',
51 'hmp',
52 'xns-idp',
53 'rdp',
54 'iso-tp4',
55 'dccp',
56 'xtp',
57 'ddp',
58 'idpr-cmtp',
59 'ipv6',
60 'ipv6-route',
61 'ipv6-frag',
62 'idrp',
63 'rsvp',
64 'gre',
65 'ipsec-esp',
66 'ipsec-ah',
67 'skip',
68 'ipv6-icmp',
69 'ipv6-nonxt',
70 'ipv6-opts',
71 'rspf cphb',
72 'vmtp',
73 'eigrp',
74 'ospfigp',
75 'ax.25',
76 'ipip',
77 'etherip',
78 'encap',
79 'pim',
80 'ipcomp',
81 'vrrp',
82 'l2tp',
83 'isis',
84 'fc',
85 'udplite',
86 'mpls-in-ip',
87 'hip',
88 'shim6',
89 'wesp',
90 'rohc',
91 'mobility-header'
92 ]
93
94 for item in protocols:
95 yield item
96
97
98 # I'm Py3
0 1/tcp tcpmux
1 7/tcp echo
2 7/udp echo
3 9/tcp discard
4 9/udp discard
5 11/tcp systat
6 13/tcp daytime
7 13/udp daytime
8 15/tcp netstat
9 17/tcp qotd
10 18/tcp msp
11 18/udp msp
12 19/tcp chargen
13 19/udp chargen
14 20/tcp ftp-data
15 21/tcp ftp
16 21/udp fsp
17 22/tcp ssh
18 22/udp ssh
19 23/tcp telnet
20 25/tcp smtp
21 37/tcp time
22 37/udp time
23 39/udp rlp
24 42/tcp nameserver
25 43/tcp whois
26 49/tcp tacacs
27 49/udp tacacs
28 50/tcp re-mail-ck
29 50/udp re-mail-ck
30 53/tcp domain
31 53/udp domain
32 57/tcp mtp
33 65/tcp tacacs-ds
34 65/udp tacacs-ds
35 67/tcp bootps
36 67/udp bootps
37 68/tcp bootpc
38 68/udp bootpc
39 69/udp tftp
40 70/tcp gopher
41 70/udp gopher
42 77/tcp rje
43 79/tcp finger
44 80/tcp http
45 80/udp http
46 87/tcp link
47 88/tcp kerberos
48 88/udp kerberos
49 95/tcp supdup
50 101/tcp hostnames
51 102/tcp iso-tsap
52 104/tcp acr-nema
53 104/udp acr-nema
54 105/tcp csnet-ns
55 105/udp csnet-ns
56 107/tcp rtelnet
57 107/udp rtelnet
58 109/tcp pop2
59 109/udp pop2
60 110/tcp pop3
61 110/udp pop3
62 111/tcp sunrpc
63 111/udp sunrpc
64 113/tcp auth
65 115/tcp sftp
66 117/tcp uucp-path
67 119/tcp nntp
68 123/tcp ntp
69 123/udp ntp
70 129/tcp pwdgen
71 129/udp pwdgen
72 135/tcp loc-srv
73 135/udp loc-srv
74 137/tcp netbios-ns
75 137/udp netbios-ns
76 138/tcp netbios-dgm
77 138/udp netbios-dgm
78 139/tcp netbios-ssn
79 139/udp netbios-ssn
80 143/tcp imap2
81 143/udp imap2
82 161/tcp snmp
83 161/udp snmp
84 162/tcp snmp-trap
85 162/udp snmp-trap
86 163/tcp cmip-man
87 163/udp cmip-man
88 164/tcp cmip-agent
89 164/udp cmip-agent
90 174/tcp mailq
91 174/udp mailq
92 177/tcp xdmcp
93 177/udp xdmcp
94 178/tcp nextstep
95 178/udp nextstep
96 179/tcp bgp
97 179/udp bgp
98 191/tcp prospero
99 191/udp prospero
100 194/tcp irc
101 194/udp irc
102 199/tcp smux
103 199/udp smux
104 201/tcp at-rtmp
105 201/udp at-rtmp
106 202/tcp at-nbp
107 202/udp at-nbp
108 204/tcp at-echo
109 204/udp at-echo
110 206/tcp at-zis
111 206/udp at-zis
112 209/tcp qmtp
113 209/udp qmtp
114 210/tcp z3950
115 210/udp z3950
116 213/tcp ipx
117 213/udp ipx
118 220/tcp imap3
119 220/udp imap3
120 345/tcp pawserv
121 345/udp pawserv
122 346/tcp zserv
123 346/udp zserv
124 347/tcp fatserv
125 347/udp fatserv
126 369/tcp rpc2portmap
127 369/udp rpc2portmap
128 370/tcp codaauth2
129 370/udp codaauth2
130 371/tcp clearcase
131 371/udp clearcase
132 372/tcp ulistserv
133 372/udp ulistserv
134 389/tcp ldap
135 389/udp ldap
136 406/tcp imsp
137 406/udp imsp
138 427/tcp svrloc
139 427/udp svrloc
140 443/tcp https
141 443/udp https
142 444/tcp snpp
143 444/udp snpp
144 445/tcp microsoft-ds
145 445/udp microsoft-ds
146 464/tcp kpasswd
147 464/udp kpasswd
148 465/tcp urd
149 487/tcp saft
150 487/udp saft
151 500/tcp isakmp
152 500/udp isakmp
153 554/tcp rtsp
154 554/udp rtsp
155 607/tcp nqs
156 607/udp nqs
157 610/tcp npmp-local
158 610/udp npmp-local
159 611/tcp npmp-gui
160 611/udp npmp-gui
161 612/tcp hmmp-ind
162 612/udp hmmp-ind
163 623/udp asf-rmcp
164 628/tcp qmqp
165 628/udp qmqp
166 631/tcp ipp
167 631/udp ipp
168 512/tcp exec
169 512/udp biff
170 513/tcp login
171 513/udp who
172 514/tcp shell
173 514/udp syslog
174 515/tcp printer
175 517/udp talk
176 518/udp ntalk
177 520/udp route
178 525/udp timed
179 526/tcp tempo
180 530/tcp courier
181 531/tcp conference
182 532/tcp netnews
183 533/udp netwall
184 538/tcp gdomap
185 538/udp gdomap
186 540/tcp uucp
187 543/tcp klogin
188 544/tcp kshell
189 546/tcp dhcpv6-client
190 546/udp dhcpv6-client
191 547/tcp dhcpv6-server
192 547/udp dhcpv6-server
193 548/tcp afpovertcp
194 548/udp afpovertcp
195 549/tcp idfp
196 549/udp idfp
197 556/tcp remotefs
198 563/tcp nntps
199 563/udp nntps
200 587/tcp submission
201 587/udp submission
202 636/tcp ldaps
203 636/udp ldaps
204 655/tcp tinc
205 655/udp tinc
206 706/tcp silc
207 706/udp silc
208 749/tcp kerberos-adm
209 765/tcp webster
210 765/udp webster
211 873/tcp rsync
212 873/udp rsync
213 989/tcp ftps-data
214 990/tcp ftps
215 992/tcp telnets
216 992/udp telnets
217 993/tcp imaps
218 993/udp imaps
219 994/tcp ircs
220 994/udp ircs
221 995/tcp pop3s
222 995/udp pop3s
223 1080/tcp socks
224 1080/udp socks
225 1093/tcp proofd
226 1093/udp proofd
227 1094/tcp rootd
228 1094/udp rootd
229 1194/tcp openvpn
230 1194/udp openvpn
231 1099/tcp rmiregistry
232 1099/udp rmiregistry
233 1214/tcp kazaa
234 1214/udp kazaa
235 1241/tcp nessus
236 1241/udp nessus
237 1352/tcp lotusnote
238 1352/udp lotusnote
239 1433/tcp ms-sql-s
240 1433/udp ms-sql-s
241 1434/tcp ms-sql-m
242 1434/udp ms-sql-m
243 1524/tcp ingreslock
244 1524/udp ingreslock
245 1525/tcp prospero-np
246 1525/udp prospero-np
247 1645/tcp datametrics
248 1645/udp datametrics
249 1646/tcp sa-msg-port
250 1646/udp sa-msg-port
251 1649/tcp kermit
252 1649/udp kermit
253 1677/tcp groupwise
254 1677/udp groupwise
255 1701/tcp l2f
256 1701/udp l2f
257 1812/tcp radius
258 1812/udp radius
259 1813/tcp radius-acct
260 1813/udp radius-acct
261 1863/tcp msnp
262 1863/udp msnp
263 1957/tcp unix-status
264 1958/tcp log-server
265 1959/tcp remoteping
266 2000/tcp cisco-sccp
267 2000/udp cisco-sccp
268 2010/tcp search
269 2010/tcp pipe-server
270 2049/tcp nfs
271 2049/udp nfs
272 2086/tcp gnunet
273 2086/udp gnunet
274 2101/tcp rtcm-sc104
275 2101/udp rtcm-sc104
276 2119/tcp gsigatekeeper
277 2119/udp gsigatekeeper
278 2135/tcp gris
279 2135/udp gris
280 2401/tcp cvspserver
281 2401/udp cvspserver
282 2430/tcp venus
283 2430/udp venus
284 2431/tcp venus-se
285 2431/udp venus-se
286 2432/tcp codasrv
287 2432/udp codasrv
288 2433/tcp codasrv-se
289 2433/udp codasrv-se
290 2583/tcp mon
291 2583/udp mon
292 2628/tcp dict
293 2628/udp dict
294 2792/tcp f5-globalsite
295 2792/udp f5-globalsite
296 2811/tcp gsiftp
297 2811/udp gsiftp
298 2947/tcp gpsd
299 2947/udp gpsd
300 3050/tcp gds-db
301 3050/udp gds-db
302 3130/tcp icpv2
303 3130/udp icpv2
304 3260/tcp iscsi-target
305 3306/tcp mysql
306 3306/udp mysql
307 3493/tcp nut
308 3493/udp nut
309 3632/tcp distcc
310 3632/udp distcc
311 3689/tcp daap
312 3689/udp daap
313 3690/tcp svn
314 3690/udp svn
315 4031/tcp suucp
316 4031/udp suucp
317 4094/tcp sysrqd
318 4094/udp sysrqd
319 4190/tcp sieve
320 4369/tcp epmd
321 4369/udp epmd
322 4373/tcp remctl
323 4373/udp remctl
324 4353/tcp f5-iquery
325 4353/udp f5-iquery
326 4500/udp ipsec-nat-t
327 4569/tcp iax
328 4569/udp iax
329 4691/tcp mtn
330 4691/udp mtn
331 4899/tcp radmin-port
332 4899/udp radmin-port
333 5002/udp rfe
334 5002/tcp rfe
335 5050/tcp mmcc
336 5050/udp mmcc
337 5060/tcp sip
338 5060/udp sip
339 5061/tcp sip-tls
340 5061/udp sip-tls
341 5190/tcp aol
342 5190/udp aol
343 5222/tcp xmpp-client
344 5222/udp xmpp-client
345 5269/tcp xmpp-server
346 5269/udp xmpp-server
347 5308/tcp cfengine
348 5308/udp cfengine
349 5353/tcp mdns
350 5353/udp mdns
351 5432/tcp postgresql
352 5432/udp postgresql
353 5556/tcp freeciv
354 5556/udp freeciv
355 5671/tcp amqps
356 5672/tcp amqp
357 5672/udp amqp
358 5672/sctp amqp
359 5688/tcp ggz
360 5688/udp ggz
361 6000/tcp x11
362 6000/udp x11
363 6001/tcp x11-1
364 6001/udp x11-1
365 6002/tcp x11-2
366 6002/udp x11-2
367 6003/tcp x11-3
368 6003/udp x11-3
369 6004/tcp x11-4
370 6004/udp x11-4
371 6005/tcp x11-5
372 6005/udp x11-5
373 6006/tcp x11-6
374 6006/udp x11-6
375 6007/tcp x11-7
376 6007/udp x11-7
377 6346/tcp gnutella-svc
378 6346/udp gnutella-svc
379 6347/tcp gnutella-rtr
380 6347/udp gnutella-rtr
381 6444/tcp sge-qmaster
382 6444/udp sge-qmaster
383 6445/tcp sge-execd
384 6445/udp sge-execd
385 6446/tcp mysql-proxy
386 6446/udp mysql-proxy
387 7000/tcp afs3-fileserver
388 7000/udp afs3-fileserver
389 7001/tcp afs3-callback
390 7001/udp afs3-callback
391 7002/tcp afs3-prserver
392 7002/udp afs3-prserver
393 7003/tcp afs3-vlserver
394 7003/udp afs3-vlserver
395 7004/tcp afs3-kaserver
396 7004/udp afs3-kaserver
397 7005/tcp afs3-volser
398 7005/udp afs3-volser
399 7006/tcp afs3-errors
400 7006/udp afs3-errors
401 7007/tcp afs3-bos
402 7007/udp afs3-bos
403 7008/tcp afs3-update
404 7008/udp afs3-update
405 7009/tcp afs3-rmtsys
406 7009/udp afs3-rmtsys
407 7100/tcp font-service
408 7100/udp font-service
409 8080/tcp http-alt
410 8080/udp http-alt
411 9101/tcp bacula-dir
412 9101/udp bacula-dir
413 9102/tcp bacula-fd
414 9102/udp bacula-fd
415 9103/tcp bacula-sd
416 9103/udp bacula-sd
417 9667/tcp xmms2
418 9667/udp xmms2
419 10809/tcp nbd
420 10050/tcp zabbix-agent
421 10050/udp zabbix-agent
422 10051/tcp zabbix-trapper
423 10051/udp zabbix-trapper
424 10080/tcp amanda
425 10080/udp amanda
426 11112/tcp dicom
427 11371/tcp hkp
428 11371/udp hkp
429 13720/tcp bprd
430 13720/udp bprd
431 13721/tcp bpdbm
432 13721/udp bpdbm
433 13722/tcp bpjava-msvc
434 13722/udp bpjava-msvc
435 13724/tcp vnetd
436 13724/udp vnetd
437 13782/tcp bpcd
438 13782/udp bpcd
439 13783/tcp vopied
440 13783/udp vopied
441 17500/tcp db-lsp
442 22125/tcp dcap
443 22128/tcp gsidcap
444 22273/tcp wnn6
445 22273/udp wnn6
446 1/ddp rtmp
447 2/ddp nbp
448 4/ddp echo
449 6/ddp zip
450 750/udp kerberos4
451 750/tcp kerberos4
452 751/udp kerberos-master
453 751/tcp kerberos-master
454 752/udp passwd-server
455 754/tcp krb-prop
456 760/tcp krbupdate
457 901/tcp swat
458 1109/tcp kpop
459 2053/tcp knetd
460 2102/udp zephyr-srv
461 2103/udp zephyr-clt
462 2104/udp zephyr-hm
463 2105/tcp eklogin
464 2111/tcp kx
465 2121/tcp iprop
466 871/tcp supfilesrv
467 1127/tcp supfiledbg
468 98/tcp linuxconf
469 106/tcp poppassd
470 106/udp poppassd
471 775/tcp moira-db
472 777/tcp moira-update
473 779/udp moira-ureg
474 783/tcp spamd
475 808/tcp omirr
476 808/udp omirr
477 1001/tcp customs
478 1001/udp customs
479 1178/tcp skkserv
480 1210/udp predict
481 1236/tcp rmtcfg
482 1300/tcp wipld
483 1313/tcp xtel
484 1314/tcp xtelw
485 1529/tcp support
486 2003/tcp cfinger
487 2121/tcp frox
488 2150/tcp ninstall
489 2150/udp ninstall
490 2600/tcp zebrasrv
491 2601/tcp zebra
492 2602/tcp ripd
493 2603/tcp ripngd
494 2604/tcp ospfd
495 2605/tcp bgpd
496 2606/tcp ospf6d
497 2607/tcp ospfapi
498 2608/tcp isisd
499 2988/tcp afbackup
500 2988/udp afbackup
501 2989/tcp afmbackup
502 2989/udp afmbackup
503 4224/tcp xtell
504 4557/tcp fax
505 4559/tcp hylafax
506 4600/tcp distmp3
507 4949/tcp munin
508 5051/tcp enbd-cstatd
509 5052/tcp enbd-sstatd
510 5151/tcp pcrd
511 5354/tcp noclog
512 5354/udp noclog
513 5355/tcp hostmon
514 5355/udp hostmon
515 5555/udp rplay
516 5666/tcp nrpe
517 5667/tcp nsca
518 5674/tcp mrtd
519 5675/tcp bgpsim
520 5680/tcp canna
521 6514/tcp syslog-tls
522 6566/tcp sane-port
523 6667/tcp ircd
524 8021/tcp zope-ftp
525 8081/tcp tproxy
526 8088/tcp omniorb
527 8088/udp omniorb
528 8990/tcp clc-build-daemon
529 9098/tcp xinetd
530 9359/udp mandelspawn
531 9418/tcp git
532 9673/tcp zope
533 10000/tcp webmin
534 10081/tcp kamanda
535 10081/udp kamanda
536 10082/tcp amandaidx
537 10083/tcp amidxtape
538 11201/tcp smsqp
539 11201/udp smsqp
540 15345/tcp xpilot
541 15345/udp xpilot
542 17001/udp sgi-cmsd
543 17002/udp sgi-crsd
544 17003/udp sgi-gcd
545 17004/tcp sgi-cad
546 20011/tcp isdnlog
547 20011/udp isdnlog
548 20012/tcp vboxd
549 20012/udp vboxd
550 24554/tcp binkp
551 27374/tcp asp
552 27374/udp asp
553 30865/tcp csync2
554 57000/tcp dircproxy
555 60177/tcp tfido
556 60179/tcp fido
557 1027/udp Native IPv6 behind IPv4-to-IPv4 NAT Customer Premises Equipment (6a44)
558 1058/tcp nim, IBM AIX Network Installation Manager (NIM)
559 1058/udp nim, IBM AIX Network Installation Manager (NIM)
560 1059/tcp nimreg, IBM AIX Network Installation Manager (NIM)
561 1059/udp nimreg, IBM AIX Network Installation Manager (NIM)
562 1080/tcp SOCKS proxy
563 1080/udp SOCKS proxy
564 1085/tcp WebObjects
565 1085/udp WebObjects
566 1098/tcp rmiactivation, Java remote method invocation (RMI) activation
567 1098/udp rmiactivation, Java remote method invocation (RMI) activation
568 1099/tcp rmiregistry, Java remote method invocation (RMI) registry
569 1099/assigned rmiregistry, Java remote method invocation (RMI) registry
570 1119/tcp Battle.net chat/game protocol, used by Blizzard's games
571 1119/udp Battle.net chat/game protocol, used by Blizzard's games
572 1167/udp Cisco IP SLA (Service Assurance Agent)
573 1167/tcp Cisco IP SLA (Service Assurance Agent)
574 1194/udp OpenVPN
575 1194/tcp OpenVPN
576 1198/udp The cajo project Free dynamic transparent distributed computing in Java
577 1198/tcp The cajo project Free dynamic transparent distributed computing in Java
578 1214/udp Kazaa
579 1214/tcp Kazaa
580 1234/udp Infoseek search agent
581 1234/tcp Infoseek search agent
582 1241/udp Nessus Security Scanner
583 1241/tcp Nessus Security Scanner
584 1270/udp Microsoft System Center Operations Manager (SCOM) (formerly Microsoft Operations Manager (MOM)) agent
585 1270/tcp Microsoft System Center Operations Manager (SCOM) (formerly Microsoft Operations Manager (MOM)) agent
586 1293/udp Internet Protocol Security (IPSec)
587 1293/tcp Internet Protocol Security (IPSec)
588 1311/udp Windows RxMon.exe
589 1311/tcp Windows RxMon.exe
590 1341/udp Qubes (Manufacturing Execution System)
591 1341/tcp Qubes (Manufacturing Execution System)
592 1344/udp Internet Content Adaptation Protocol
593 1344/tcp Internet Content Adaptation Protocol
594 1352/udp IBM Lotus Notes/Domino (RPC) protocol
595 1352/tcp IBM Lotus Notes/Domino (RPC) protocol
596 1360/udp Mimer SQL
597 1360/tcp Mimer SQL
598 1414/udp IBM WebSphere MQ (formerly known as MQSeries)
599 1414/tcp IBM WebSphere MQ (formerly known as MQSeries)
600 1417/udp Timbuktu Service 1 Port
601 1417/tcp Timbuktu Service 1 Port
602 1418/udp Timbuktu Service 2 Port
603 1418/tcp Timbuktu Service 2 Port
604 1419/udp Timbuktu Service 3 Port
605 1419/tcp Timbuktu Service 3 Port
606 1420/udp Timbuktu Service 4 Port
607 1420/tcp Timbuktu Service 4 Port
608 1433/udp Microsoft SQL Server database management system (MSSQL) server
609 1433/tcp Microsoft SQL Server database management system (MSSQL) server
610 1434/udp Microsoft SQL Server database management system (MSSQL) monitor
611 1434/tcp Microsoft SQL Server database management system (MSSQL) monitor
612 1512/udp Microsoft's Windows Internet Name Service (WINS)
613 1512/tcp Microsoft's Windows Internet Name Service (WINS)
614 1521/udp nCUBE License Manager
615 1521/tcp nCUBE License Manager
616 1524/udp ingreslock, ingres
617 1524/tcp ingreslock, ingres
618 1527/udp Oracle Net Services, formerly known as SQL*Net
619 1527/tcp Oracle Net Services, formerly known as SQL*Net
620 1533/udp IBM Sametime Virtual Places Chat
621 1533/tcp IBM Sametime Virtual Places Chat
622 1547/udp Laplink
623 1547/tcp Laplink
624 1581/udp MIL STD 2045-47001 VMF
625 1581/tcp MIL STD 2045-47001 VMF
626 1589/udp Cisco VLAN Query Protocol (VQP)
627 1589/tcp Cisco VLAN Query Protocol (VQP)
628 1628/udp LonTalk normal
629 1628/tcp LonTalk normal
630 1629/udp LonTalk urgent
631 1629/tcp LonTalk urgent
632 1677/udp Novell GroupWise clients in client/server access mode
633 1677/tcp Novell GroupWise clients in client/server access mode
634 1701/udp Layer 2 Forwarding Protocol (L2F)
635 1701/tcp Layer 2 Forwarding Protocol (L2F)
636 1701/udp Layer 2 Tunneling Protocol (L2TP)
637 1701/assigned Layer 2 Tunneling Protocol (L2TP)
638 1707/udp Windward Studios games (vdmplay)
639 1707/tcp Windward Studios games (vdmplay)
640 1719/udp H.323 registration and alternate communication
641 1719/tcp H.323 registration and alternate communication
642 1720/udp H.323 call signaling
643 1720/tcp H.323 call signaling
644 1755/udp Microsoft Media Services (MMS, ms-streaming)
645 1755/tcp Microsoft Media Services (MMS, ms-streaming)
646 1801/udp Microsoft Message Queuing
647 1801/tcp Microsoft Message Queuing
648 1812/udp RADIUS authentication protocol, radius
649 1812/tcp RADIUS authentication protocol, radius
650 1813/udp RADIUS accounting protocol, radius-acct
651 1813/tcp RADIUS accounting protocol, radius-acct
652 1863/udp Microsoft Notification Protocol (MSNP), used by the Microsoft Messenger service and a number of instant messaging Messenger clients
653 1863/tcp Microsoft Notification Protocol (MSNP), used by the Microsoft Messenger service and a number of instant messaging Messenger clients
654 1883/udp MQTT (formerly MQ Telemetry Transport)
655 1883/tcp MQTT (formerly MQ Telemetry Transport)
656 1900/udp Simple Service Discovery Protocol (SSDP), discovery of UPnP devices
657 1900/assigned Simple Service Discovery Protocol (SSDP), discovery of UPnP devices
658 1935/udp Macromedia Flash Communications Server MX, the precursor to Adobe Flash Media Server before Macromedia's acquisition by Adobe on December 3, 2005
659 1935/tcp Macromedia Flash Communications Server MX, the precursor to Adobe Flash Media Server before Macromedia's acquisition by Adobe on December 3, 2005
660 1970/udp Netop Remote Control
661 1970/tcp Netop Remote Control
662 1972/udp InterSystems Cache
663 1972/tcp InterSystems Cache
664 1984/udp Big Brother
665 1984/tcp Big Brother
666 1985/udp Cisco Hot Standby Router Protocol (HSRP)
667 1985/assigned Cisco Hot Standby Router Protocol (HSRP)
668 1998/udp Cisco X.25 over TCP (XOT) service
669 1998/tcp Cisco X.25 over TCP (XOT) service
670 2000/udp Cisco Skinny Client Control Protocol (SCCP)
671 2000/tcp Cisco Skinny Client Control Protocol (SCCP)
672 2080/udp Autodesk NLM (FLEXlm)
673 2080/tcp Autodesk NLM (FLEXlm)
674 2083/udp Secure RADIUS Service (radsec)
675 2083/tcp Secure RADIUS Service (radsec)
676 2086/udp GNUnet
677 2086/tcp GNUnet
678 2102/udp Zephyr Notification Service server
679 2102/tcp Zephyr Notification Service server
680 2103/udp Zephyr Notification Service serv-hm connection
681 2103/tcp Zephyr Notification Service serv-hm connection
682 2104/udp Zephyr Notification Service hostmanager
683 2104/tcp Zephyr Notification Service hostmanager
684 2123/udp GTP control messages (GTP-C)
685 2123/tcp GTP control messages (GTP-C)
686 2142/udp TDMoIP (TDM over IP)
687 2142/tcp TDMoIP (TDM over IP)
688 2152/udp GTP user data messages (GTP-U)
689 2152/tcp GTP user data messages (GTP-U)
690 2159/udp GDB remote debug port
691 2159/tcp GDB remote debug port
692 2181/udp EForward-document transport system
693 2181/tcp EForward-document transport system
694 2210/udp NOAAPORT Broadcast Network
695 2210/tcp NOAAPORT Broadcast Network
696 2211/udp EMWIN
697 2211/tcp EMWIN
698 2222/udp EtherNet/IP implicit messaging for IO data
699 2222/tcp EtherNet/IP implicit messaging for IO data
700 2261/udp CoMotion master
701 2261/tcp CoMotion master
702 2262/udp CoMotion backup
703 2262/tcp CoMotion backup
704 2266/udp M-Files
705 2266/tcp M-Files
706 2399/udp FileMaker Data Access Layer (ODBC/JDBC)
707 2399/tcp FileMaker Data Access Layer (ODBC/JDBC)
708 2401/udp CVS version control system password-based server
709 2401/tcp CVS version control system password-based server
710 2404/udp IEC 60870-5-104, used to send electric power telecontrol messages between two systems via directly connected data circuits
711 2404/tcp IEC 60870-5-104, used to send electric power telecontrol messages between two systems via directly connected data circuits
712 2427/udp Media Gateway Control Protocol (MGCP) media gateway
713 2427/tcp Media Gateway Control Protocol (MGCP) media gateway
714 2447/udp ovwdb OpenView Network Node Manager (NNM) daemon
715 2447/tcp ovwdb OpenView Network Node Manager (NNM) daemon
716 2483/udp Oracle database listening for insecure client connections to the listener, replaces port 1521
717 2483/tcp Oracle database listening for insecure client connections to the listener, replaces port 1521
718 2484/udp Oracle database listening for SSL client connections to the listener
719 2484/tcp Oracle database listening for SSL client connections to the listener
720 2535/udp Multicast Address Dynamic Client Allocation Protocol (MADCAP). All standard messages are UDP datagrams.
721 2535/tcp Multicast Address Dynamic Client Allocation Protocol (MADCAP). All standard messages are UDP datagrams.
722 2541/udp LonTalk/IP
723 2541/tcp LonTalk/IP
724 2546/udp EVault data protection services
725 2546/tcp EVault data protection services
726 2547/udp EVault data protection services
727 2547/tcp EVault data protection services
728 2548/udp EVault data protection services
729 2548/tcp EVault data protection services
730 2638/udp SQL Anywhere database server
731 2638/tcp SQL Anywhere database server
732 2727/udp Media Gateway Control Protocol (MGCP) media gateway controller (call agent)
733 2727/tcp Media Gateway Control Protocol (MGCP) media gateway controller (call agent)
734 2809/udp corbaloc:iiop URL, per the CORBA 3.0.3 specification
735 2809/tcp corbaloc:iiop URL, per the CORBA 3.0.3 specification
736 2811/udp gsi ftp, per the GridFTP specification
737 2811/tcp gsi ftp, per the GridFTP specification
738 2944/udp Megaco text H.248
739 2944/tcp Megaco text H.248
740 2945/udp Megaco binary (ASN.1) H.248
741 2945/tcp Megaco binary (ASN.1) H.248
742 2947/udp gpsd, GPS daemon
743 2947/tcp gpsd, GPS daemon
744 2948/udp WAP push Multimedia Messaging Service (MMS)
745 2948/tcp WAP push Multimedia Messaging Service (MMS)
746 2949/udp WAP push secure (MMS)
747 2949/tcp WAP push secure (MMS)
748 2967/udp Symantec System Center agent (SSC-AGENT)
749 2967/tcp Symantec System Center agent (SSC-AGENT)
750 3020/udp Common Internet File System (CIFS). See also port 445 for Server Message Block (SMB), a dialect of CIFS.
751 3020/tcp Common Internet File System (CIFS). See also port 445 for Server Message Block (SMB), a dialect of CIFS.
752 3050/udp gds-db (Interbase/Firebird databases)
753 3050/tcp gds-db (Interbase/Firebird databases)
754 3052/udp APC PowerChute Network
755 3052/tcp APC PowerChute Network
756 3074/udp Xbox LIVE and Games for Windows Live
757 3074/tcp Xbox LIVE and Games for Windows Live
758 3225/udp Fibre Channel over IP (FCIP)
759 3225/tcp Fibre Channel over IP (FCIP)
760 3233/udp WhiskerControl research control protocol
761 3233/tcp WhiskerControl research control protocol
762 3260/udp iSCSI
763 3260/tcp iSCSI
764 3268/udp msft-gc, Microsoft Global Catalog (LDAP service which contains data from Active Directory forests)
765 3268/tcp msft-gc, Microsoft Global Catalog (LDAP service which contains data from Active Directory forests)
766 3269/udp msft-gc-ssl, Microsoft Global Catalog over SSL (similar to port 3268, LDAP over SSL)
767 3269/tcp msft-gc-ssl, Microsoft Global Catalog over SSL (similar to port 3268, LDAP over SSL)
768 3283/udp Net Assistant, a predecessor to Apple Remote Desktop
769 3283/tcp Net Assistant, a predecessor to Apple Remote Desktop
770 3305/udp Odette File Transfer Protocol (OFTP)
771 3305/tcp Odette File Transfer Protocol (OFTP)
772 3386/udp GTP' 3GPP GSM/UMTS CDR logging protocol
773 3386/tcp GTP' 3GPP GSM/UMTS CDR logging protocol
774 3389/udp Microsoft Terminal Server (RDP) officially registered as Windows Based Terminal (WBT)
775 3389/tcp Microsoft Terminal Server (RDP) officially registered as Windows Based Terminal (WBT)
776 3396/udp Novell NDPS Printer Agent
777 3396/tcp Novell NDPS Printer Agent
778 3412/udp xmlBlaster
779 3412/tcp xmlBlaster
780 3455/udp Resource Reservation Protocol (RSVP)
781 3455/tcp Resource Reservation Protocol (RSVP)
782 3478/udp STUN, a protocol for NAT traversal
783 3478/tcp STUN, a protocol for NAT traversal
784 3478/udp TURN, a protocol for NAT traversal (extension to STUN)
785 3478/tcp TURN, a protocol for NAT traversal (extension to STUN)
786 3478/udp STUN Behavior Discovery. See also port 5349.
787 3478/tcp STUN Behavior Discovery. See also port 5349.
788 3493/udp Network UPS Tools (NUT)
789 3493/tcp Network UPS Tools (NUT)
790 3516/udp Smartcard Port
791 3516/tcp Smartcard Port
792 3645/udp Cyc
793 3645/tcp Cyc
794 3659/udp Apple SASL, used by Mac OS X Server Password Server
795 3659/tcp Apple SASL, used by Mac OS X Server Password Server
796 3667/udp Information Exchange
797 3667/tcp Information Exchange
798 3690/udp Subversion (SVN) version control system
799 3690/tcp Subversion (SVN) version control system
800 3702/udp Web Services Dynamic Discovery (WS-Discovery), used by various components of Windows Vista and later
801 3702/tcp Web Services Dynamic Discovery (WS-Discovery), used by various components of Windows Vista and later
802 3724/udp Some Blizzard games
803 3724/tcp Some Blizzard games
804 3725/udp Netia NA-ER Port
805 3725/tcp Netia NA-ER Port
806 3768/udp RBLcheckd server daemon
807 3768/tcp RBLcheckd server daemon
808 3804/udp Harman Professional HiQnet protocol
809 3804/tcp Harman Professional HiQnet protocol
810 3826/udp WarMUX game server
811 3826/tcp WarMUX game server
812 3830/udp System Management Agent, developed and used by Cerner to monitor and manage solutions
813 3830/tcp System Management Agent, developed and used by Cerner to monitor and manage solutions
814 3880/udp IGRS
815 3880/tcp IGRS
816 3999/udp Norman distributed scanning service
817 3999/tcp Norman distributed scanning service
818 4018/udp Protocol information and warnings
819 4018/tcp Protocol information and warnings
820 4089/udp OpenCORE Remote Control Service
821 4089/tcp OpenCORE Remote Control Service
822 4090/udp Kerio
823 4090/tcp Kerio
824 4093/udp PxPlus Client server interface ProvideX
825 4093/tcp PxPlus Client server interface ProvideX
826 4096/udp Ascom Timeplex Bridge Relay Element (BRE)
827 4096/tcp Ascom Timeplex Bridge Relay Element (BRE)
828 4105/udp Shofar (ShofarNexus)
829 4105/tcp Shofar (ShofarNexus)
830 4116/udp Smartcard-TLS
831 4116/tcp Smartcard-TLS
832 4172/udp Teradici PCoIP
833 4172/tcp Teradici PCoIP
834 4303/udp Simple Railroad Command Protocol (SRCP)
835 4303/tcp Simple Railroad Command Protocol (SRCP)
836 4486/udp Integrated Client Message Service (ICMS)
837 4486/tcp Integrated Client Message Service (ICMS)
838 4500/udp IPSec NAT Traversal (RFC 3947, RFC 4306)
839 4500/assigned IPSec NAT Traversal (RFC 3947, RFC 4306)
840 4662/udp OrbitNet Message Service
841 4662/tcp OrbitNet Message Service
842 4730/udp Gearman's job server
843 4730/tcp Gearman's job server
844 4739/udp IP Flow Information Export
845 4739/tcp IP Flow Information Export
846 4753/udp SIMON (service and discovery)
847 4753/tcp SIMON (service and discovery)
848 4840/udp OPC UA Connection Protocol (TCP) and OPC UA Multicast Datagram Protocol (UDP) for OPC Unified Architecture from OPC Foundation
849 4840/tcp OPC UA Connection Protocol (TCP) and OPC UA Multicast Datagram Protocol (UDP) for OPC Unified Architecture from OPC Foundation
850 4843/udp OPC UA TCP Protocol over TLS/SSL for OPC Unified Architecture from OPC Foundation
851 4843/tcp OPC UA TCP Protocol over TLS/SSL for OPC Unified Architecture from OPC Foundation
852 4847/udp Web Fresh Communication, Quadrion Software & Odorless Entertainment
853 4847/tcp Web Fresh Communication, Quadrion Software & Odorless Entertainment
854 4894/udp LysKOM Protocol A
855 4894/tcp LysKOM Protocol A
856 4950/udp Cylon Controls UC32 Communications Port
857 4950/tcp Cylon Controls UC32 Communications Port
858 5010/udp Registered to: TelePath (the IBM FlowMark workflow-management system messaging platform). The TCP port is now used for: IBM WebSphere MQ Workflow
859 5010/tcp Registered to: TelePath (the IBM FlowMark workflow-management system messaging platform). The TCP port is now used for: IBM WebSphere MQ Workflow
860 5011/udp TelePath (the IBM FlowMark workflow-management system messaging platform)
861 5011/tcp TelePath (the IBM FlowMark workflow-management system messaging platform)
862 5025/udp scpi-raw Standard Commands for Programmable Instruments
863 5025/tcp scpi-raw Standard Commands for Programmable Instruments
864 5060/udp Session Initiation Protocol (SIP)
865 5060/tcp Session Initiation Protocol (SIP)
866 5062/udp Localisation access
867 5062/tcp Localisation access
868 5064/udp EPICS Channel Access server
869 5064/tcp EPICS Channel Access server
870 5065/udp EPICS Channel Access repeater beacon
871 5065/tcp EPICS Channel Access repeater beacon
872 5084/udp EPCglobal Low Level Reader Protocol (LLRP)
873 5084/tcp EPCglobal Low Level Reader Protocol (LLRP)
874 5085/udp EPCglobal Low Level Reader Protocol (LLRP) over TLS
875 5085/tcp EPCglobal Low Level Reader Protocol (LLRP) over TLS
876 5099/udp SafeNet, Inc Sentinel LM, Sentinel RMS, License Manager, server-to-server
877 5099/tcp SafeNet, Inc Sentinel LM, Sentinel RMS, License Manager, server-to-server
878 5150/udp ATMP Ascend Tunnel Management Protocol
879 5150/tcp ATMP Ascend Tunnel Management Protocol
880 5154/udp BZFlag
881 5154/tcp BZFlag
882 5190/udp AOL Instant Messenger protocol. The chat app is defunct as of 15 December 2017.
883 5190/tcp AOL Instant Messenger protocol. The chat app is defunct as of 15 December 2017.
884 5298/udp Extensible Messaging and Presence Protocol (XMPP)
885 5298/tcp Extensible Messaging and Presence Protocol (XMPP)
886 5310/udp Outlaws (1997 video game). Both UDP and TCP are reserved, but only UDP is used
887 5310/tcp Outlaws (1997 video game). Both UDP and TCP are reserved, but only UDP is used
888 5353/udp Multicast DNS (mDNS)
889 5353/assigned Multicast DNS (mDNS)
890 5355/udp Link-Local Multicast Name Resolution (LLMNR), allows hosts to perform name resolution for hosts on the same local link (only provided by Windows Vista and Server 2008)
891 5355/tcp Link-Local Multicast Name Resolution (LLMNR), allows hosts to perform name resolution for hosts on the same local link (only provided by Windows Vista and Server 2008)
892 5402/udp Multicast File Transfer Protocol (MFTP)
893 5402/tcp Multicast File Transfer Protocol (MFTP)
894 5405/udp NetSupport Manager
895 5405/tcp NetSupport Manager
896 5412/udp IBM Rational Synergy (Telelogic Synergy) (Continuus CM) Message Router
897 5412/tcp IBM Rational Synergy (Telelogic Synergy) (Continuus CM) Message Router
898 5413/udp Wonderware SuiteLink service
899 5413/tcp Wonderware SuiteLink service
900 5417/udp SNS Agent
901 5417/tcp SNS Agent
902 5421/udp NetSupport Manager
903 5421/tcp NetSupport Manager
904 5556/udp Freeciv, Oracle WebLogic Server Node Manager
905 5556/tcp Freeciv, Oracle WebLogic Server Node Manager
906 5568/udp Session Data Transport (SDT), a part of Architecture for Control Networks (ACN)
907 5568/tcp Session Data Transport (SDT), a part of Architecture for Control Networks (ACN)
908 5722/udp Microsoft RPC, DFSR (SYSVOL) Replication Service
909 5722/tcp Microsoft RPC, DFSR (SYSVOL) Replication Service
910 5741/udp IDA Discover Port 1
911 5741/tcp IDA Discover Port 1
912 5742/udp IDA Discover Port 2
913 5742/tcp IDA Discover Port 2
914 5900/udp Remote Frame Buffer protocol (RFB)
915 5900/tcp Remote Frame Buffer protocol (RFB)
916 5931/udp AMMYY admin Remote Control
917 5931/tcp AMMYY admin Remote Control
918 5984/udp CouchDB database server
919 5984/tcp CouchDB database server
920 6000/udp X11-used between an X client and server over the network
921 6000/tcp X11-used between an X client and server over the network
922 6001/udp X11-used between an X client and server over the network
923 6001/tcp X11-used between an X client and server over the network
924 6002/udp X11-used between an X client and server over the network
925 6002/tcp X11-used between an X client and server over the network
926 6003/udp X11-used between an X client and server over the network
927 6003/tcp X11-used between an X client and server over the network
928 6004/udp X11-used between an X client and server over the network
929 6004/tcp X11-used between an X client and server over the network
930 6005/udp X11-used between an X client and server over the network
931 6005/tcp X11-used between an X client and server over the network
932 6006/udp X11-used between an X client and server over the network
933 6006/tcp X11-used between an X client and server over the network
934 6007/udp X11-used between an X client and server over the network
935 6007/tcp X11-used between an X client and server over the network
936 6008/udp X11-used between an X client and server over the network
937 6008/tcp X11-used between an X client and server over the network
938 6009/udp X11-used between an X client and server over the network
939 6009/tcp X11-used between an X client and server over the network
940 6010/udp X11-used between an X client and server over the network
941 6010/tcp X11-used between an X client and server over the network
942 6011/udp X11-used between an X client and server over the network
943 6011/tcp X11-used between an X client and server over the network
944 6012/udp X11-used between an X client and server over the network
945 6012/tcp X11-used between an X client and server over the network
946 6013/udp X11-used between an X client and server over the network
947 6013/tcp X11-used between an X client and server over the network
948 6014/udp X11-used between an X client and server over the network
949 6014/tcp X11-used between an X client and server over the network
950 6015/udp X11-used between an X client and server over the network
951 6015/tcp X11-used between an X client and server over the network
952 6016/udp X11-used between an X client and server over the network
953 6016/tcp X11-used between an X client and server over the network
954 6017/udp X11-used between an X client and server over the network
955 6017/tcp X11-used between an X client and server over the network
956 6018/udp X11-used between an X client and server over the network
957 6018/tcp X11-used between an X client and server over the network
958 6019/udp X11-used between an X client and server over the network
959 6019/tcp X11-used between an X client and server over the network
960 6020/udp X11-used between an X client and server over the network
961 6020/tcp X11-used between an X client and server over the network
962 6021/udp X11-used between an X client and server over the network
963 6021/tcp X11-used between an X client and server over the network
964 6022/udp X11-used between an X client and server over the network
965 6022/tcp X11-used between an X client and server over the network
966 6023/udp X11-used between an X client and server over the network
967 6023/tcp X11-used between an X client and server over the network
968 6024/udp X11-used between an X client and server over the network
969 6024/tcp X11-used between an X client and server over the network
970 6025/udp X11-used between an X client and server over the network
971 6025/tcp X11-used between an X client and server over the network
972 6026/udp X11-used between an X client and server over the network
973 6026/tcp X11-used between an X client and server over the network
974 6027/udp X11-used between an X client and server over the network
975 6027/tcp X11-used between an X client and server over the network
976 6028/udp X11-used between an X client and server over the network
977 6028/tcp X11-used between an X client and server over the network
978 6029/udp X11-used between an X client and server over the network
979 6029/tcp X11-used between an X client and server over the network
980 6030/udp X11-used between an X client and server over the network
981 6030/tcp X11-used between an X client and server over the network
982 6031/udp X11-used between an X client and server over the network
983 6031/tcp X11-used between an X client and server over the network
984 6032/udp X11-used between an X client and server over the network
985 6032/tcp X11-used between an X client and server over the network
986 6033/udp X11-used between an X client and server over the network
987 6033/tcp X11-used between an X client and server over the network
988 6034/udp X11-used between an X client and server over the network
989 6034/tcp X11-used between an X client and server over the network
990 6035/udp X11-used between an X client and server over the network
991 6035/tcp X11-used between an X client and server over the network
992 6036/udp X11-used between an X client and server over the network
993 6036/tcp X11-used between an X client and server over the network
994 6037/udp X11-used between an X client and server over the network
995 6037/tcp X11-used between an X client and server over the network
996 6038/udp X11-used between an X client and server over the network
997 6038/tcp X11-used between an X client and server over the network
998 6039/udp X11-used between an X client and server over the network
999 6039/tcp X11-used between an X client and server over the network
1000 6040/udp X11-used between an X client and server over the network
1001 6040/tcp X11-used between an X client and server over the network
1002 6041/udp X11-used between an X client and server over the network
1003 6041/tcp X11-used between an X client and server over the network
1004 6042/udp X11-used between an X client and server over the network
1005 6042/tcp X11-used between an X client and server over the network
1006 6043/udp X11-used between an X client and server over the network
1007 6043/tcp X11-used between an X client and server over the network
1008 6044/udp X11-used between an X client and server over the network
1009 6044/tcp X11-used between an X client and server over the network
1010 6045/udp X11-used between an X client and server over the network
1011 6045/tcp X11-used between an X client and server over the network
1012 6046/udp X11-used between an X client and server over the network
1013 6046/tcp X11-used between an X client and server over the network
1014 6047/udp X11-used between an X client and server over the network
1015 6047/tcp X11-used between an X client and server over the network
1016 6048/udp X11-used between an X client and server over the network
1017 6048/tcp X11-used between an X client and server over the network
1018 6049/udp X11-used between an X client and server over the network
1019 6049/tcp X11-used between an X client and server over the network
1020 6050/udp X11-used between an X client and server over the network
1021 6050/tcp X11-used between an X client and server over the network
1022 6051/udp X11-used between an X client and server over the network
1023 6051/tcp X11-used between an X client and server over the network
1024 6052/udp X11-used between an X client and server over the network
1025 6052/tcp X11-used between an X client and server over the network
1026 6053/udp X11-used between an X client and server over the network
1027 6053/tcp X11-used between an X client and server over the network
1028 6054/udp X11-used between an X client and server over the network
1029 6054/tcp X11-used between an X client and server over the network
1030 6055/udp X11-used between an X client and server over the network
1031 6055/tcp X11-used between an X client and server over the network
1032 6056/udp X11-used between an X client and server over the network
1033 6056/tcp X11-used between an X client and server over the network
1034 6057/udp X11-used between an X client and server over the network
1035 6057/tcp X11-used between an X client and server over the network
1036 6058/udp X11-used between an X client and server over the network
1037 6058/tcp X11-used between an X client and server over the network
1038 6059/udp X11-used between an X client and server over the network
1039 6059/tcp X11-used between an X client and server over the network
1040 6060/udp X11-used between an X client and server over the network
1041 6060/tcp X11-used between an X client and server over the network
1042 6061/udp X11-used between an X client and server over the network
1043 6061/tcp X11-used between an X client and server over the network
1044 6062/udp X11-used between an X client and server over the network
1045 6062/tcp X11-used between an X client and server over the network
1046 6063/udp X11-used between an X client and server over the network
1047 6063/tcp X11-used between an X client and server over the network
1048 6110/udp softcm, HP Softbench CM
1049 6110/tcp softcm, HP Softbench CM
1050 6111/udp spc, HP Softbench Sub-Process Control
1051 6111/tcp spc, HP Softbench Sub-Process Control
1052 6112/udp dtspcd, execute commands and launch applications remotely
1053 6112/tcp dtspcd, execute commands and launch applications remotely
1054 6346/udp gnutella-svc, gnutella (FrostWire, Limewire, Shareaza, etc.)
1055 6346/tcp gnutella-svc, gnutella (FrostWire, Limewire, Shareaza, etc.)
1056 6347/udp gnutella-rtr, Gnutella alternate
1057 6347/tcp gnutella-rtr, Gnutella alternate
1058 6350/udp App Discovery and Access Protocol
1059 6350/tcp App Discovery and Access Protocol
1060 6444/udp Sun Grid Engine Qmaster Service
1061 6444/tcp Sun Grid Engine Qmaster Service
1062 6445/udp Sun Grid Engine Execution Service
1063 6445/tcp Sun Grid Engine Execution Service
1064 6464/udp Port assignment for medical device communication in accordance to IEEE 11073-20701
1065 6464/tcp Port assignment for medical device communication in accordance to IEEE 11073-20701
1066 6515/udp Elipse RPC Protocol (REC)
1067 6515/tcp Elipse RPC Protocol (REC)
1068 6619/udp odette-ftps, Odette File Transfer Protocol (OFTP) over TLS/SSL
1069 6619/tcp odette-ftps, Odette File Transfer Protocol (OFTP) over TLS/SSL
1070 6622/udp Multicast FTP
1071 6622/tcp Multicast FTP
1072 6679/udp Osorno Automation Protocol (OSAUT)
1073 6679/tcp Osorno Automation Protocol (OSAUT)
1074 6888/udp MUSE
1075 6888/tcp MUSE
1076 6969/udp acmsoda
1077 6969/tcp acmsoda
1078 7262/udp CNAP (Calypso Network Access Protocol)
1079 7262/tcp CNAP (Calypso Network Access Protocol)
1080 7272/udp WatchMe - WatchMe Monitoring
1081 7272/tcp WatchMe - WatchMe Monitoring
1082 7400/udp RTPS (Real Time Publish Subscribe) DDS Discovery
1083 7400/tcp RTPS (Real Time Publish Subscribe) DDS Discovery
1084 7401/udp RTPS (Real Time Publish Subscribe) DDS User-Traffic
1085 7401/tcp RTPS (Real Time Publish Subscribe) DDS User-Traffic
1086 7402/udp RTPS (Real Time Publish Subscribe) DDS Meta-Traffic
1087 7402/tcp RTPS (Real Time Publish Subscribe) DDS Meta-Traffic
1088 7542/udp Saratoga file transfer protocol
1089 7542/tcp Saratoga file transfer protocol
1090 7547/udp CPE WAN Management Protocol (CWMP) Technical Report 069
1091 7547/tcp CPE WAN Management Protocol (CWMP) Technical Report 069
1092 7624/udp Instrument Neutral Distributed Interface
1093 7624/tcp Instrument Neutral Distributed Interface
1094 8008/udp Alternative port for HTTP. See also ports 80 and 8080.
1095 8008/tcp Alternative port for HTTP. See also ports 80 and 8080.
1096 8074/udp Gadu-Gadu
1097 8074/tcp Gadu-Gadu
1098 8080/udp Alternative port for HTTP. See also ports 80 and 8008.
1099 8080/tcp Alternative port for HTTP. See also ports 80 and 8008.
1100 8243/udp HTTPS listener for Apache Synapse
1101 8243/tcp HTTPS listener for Apache Synapse
1102 8280/udp HTTP listener for Apache Synapse
1103 8280/tcp HTTP listener for Apache Synapse
1104 8883/udp Secure MQTT (MQTT over TLS)
1105 8883/tcp Secure MQTT (MQTT over TLS)
1106 9001/udp ETL Service Manager
1107 9001/tcp ETL Service Manager
1108 9080/udp glrpc, Groove Collaboration software GLRPC
1109 9080/tcp glrpc, Groove Collaboration software GLRPC
1110 9101/udp Bacula Director
1111 9101/tcp Bacula Director
1112 9102/udp Bacula File Daemon
1113 9102/tcp Bacula File Daemon
1114 9103/udp Bacula Storage Daemon
1115 9103/tcp Bacula Storage Daemon
1116 9119/udp MXit Instant Messenger
1117 9119/tcp MXit Instant Messenger
1118 9389/udp adws, Microsoft AD DS Web Services, Powershell uses this port
1119 9389/tcp adws, Microsoft AD DS Web Services, Powershell uses this port
1120 9418/udp git, Git pack transfer service
1121 9418/tcp git, Git pack transfer service
1122 9535/udp mngsuite, LANDesk Management Suite Remote Control
1123 9535/tcp mngsuite, LANDesk Management Suite Remote Control
1124 9536/udp laes-bf, IP Fabrics Surveillance buffering function
1125 9536/tcp laes-bf, IP Fabrics Surveillance buffering function
1126 9800/udp WebDAV Source
1127 9800/tcp WebDAV Source
1128 10000/udp Network Data Management Protocol
1129 10000/tcp Network Data Management Protocol
1130 10050/udp Zabbix agent
1131 10050/tcp Zabbix agent
1132 10051/udp Zabbix trapper
1133 10051/tcp Zabbix trapper
1134 10110/udp NMEA 0183 Navigational Data. Transport of NMEA 0183 sentences over TCP or UDP
1135 10110/tcp NMEA 0183 Navigational Data. Transport of NMEA 0183 sentences over TCP or UDP
1136 11001/udp metasys ( Johnson Controls Metasys java AC control environment )
1137 11001/tcp metasys ( Johnson Controls Metasys java AC control environment )
1138 11112/udp ACR/NEMA Digital Imaging and Communications in Medicine (DICOM)
1139 11112/tcp ACR/NEMA Digital Imaging and Communications in Medicine (DICOM)
1140 11371/udp OpenPGP HTTP key server
1141 11371/tcp OpenPGP HTTP key server
1142 13720/udp Symantec NetBackup-bprd (formerly VERITAS)
1143 13720/tcp Symantec NetBackup-bprd (formerly VERITAS)
1144 13721/udp Symantec NetBackup-bpdbm (formerly VERITAS)
1145 13721/tcp Symantec NetBackup-bpdbm (formerly VERITAS)
1146 13724/udp Symantec Network Utility-vnetd (formerly VERITAS)
1147 13724/tcp Symantec Network Utility-vnetd (formerly VERITAS)
1148 13782/udp Symantec NetBackup-bpcd (formerly VERITAS)
1149 13782/tcp Symantec NetBackup-bpcd (formerly VERITAS)
1150 13783/udp Symantec VOPIED protocol (formerly VERITAS)
1151 13783/tcp Symantec VOPIED protocol (formerly VERITAS)
1152 13785/udp Symantec NetBackup Database-nbdb (formerly VERITAS)
1153 13785/tcp Symantec NetBackup Database-nbdb (formerly VERITAS)
1154 13786/udp Symantec nomdb (formerly VERITAS)
1155 13786/tcp Symantec nomdb (formerly VERITAS)
1156 15345/udp XPilot Contact
1157 15345/tcp XPilot Contact
1158 17500/udp Dropbox LanSync Protocol (db-lsp); used to synchronize file catalogs between Dropbox clients on a local network.
1159 17500/tcp Dropbox LanSync Protocol (db-lsp); used to synchronize file catalogs between Dropbox clients on a local network.
1160 19813/udp 4D database Client Server Communication
1161 19813/tcp 4D database Client Server Communication
1162 24465/udp Tonido Directory Server for Tonido which is a Personal Web App and P2P platform
1163 24465/tcp Tonido Directory Server for Tonido which is a Personal Web App and P2P platform
1164 24554/udp BINKP, Fidonet mail transfers over TCP/IP
1165 24554/tcp BINKP, Fidonet mail transfers over TCP/IP
1166 26000/udp id Software's Quake server
1167 26000/tcp id Software's Quake server
1168 27000/udp FlexNet Publisher's License server (from the range of default ports)
1169 27000/tcp FlexNet Publisher's License server (from the range of default ports)
1170 27001/udp FlexNet Publisher's License server (from the range of default ports)
1171 27001/tcp FlexNet Publisher's License server (from the range of default ports)
1172 27002/udp FlexNet Publisher's License server (from the range of default ports)
1173 27002/tcp FlexNet Publisher's License server (from the range of default ports)
1174 27003/udp FlexNet Publisher's License server (from the range of default ports)
1175 27003/tcp FlexNet Publisher's License server (from the range of default ports)
1176 27004/udp FlexNet Publisher's License server (from the range of default ports)
1177 27004/tcp FlexNet Publisher's License server (from the range of default ports)
1178 27005/udp FlexNet Publisher's License server (from the range of default ports)
1179 27005/tcp FlexNet Publisher's License server (from the range of default ports)
1180 27006/udp FlexNet Publisher's License server (from the range of default ports)
1181 27006/tcp FlexNet Publisher's License server (from the range of default ports)
1182 27007/udp FlexNet Publisher's License server (from the range of default ports)
1183 27007/tcp FlexNet Publisher's License server (from the range of default ports)
1184 27008/udp FlexNet Publisher's License server (from the range of default ports)
1185 27008/tcp FlexNet Publisher's License server (from the range of default ports)
1186 27009/udp FlexNet Publisher's License server (from the range of default ports)
1187 27009/tcp FlexNet Publisher's License server (from the range of default ports)
1188 33434/udp traceroute
1189 33434/tcp traceroute
1190 40000/udp SafetyNET p a real-time Industrial Ethernet protocol
1191 40000/tcp SafetyNET p a real-time Industrial Ethernet protocol
1192 44818/udp EtherNet/IP explicit messaging
1193 44818/tcp EtherNet/IP explicit messaging
1194 47808/udp BACnet Building Automation and Control Networks (4780810 = BAC016)
1195 47808/tcp BACnet Building Automation and Control Networks (4780810 = BAC016)
1196 49151/udp Reserved
1197 49151/tcp Reserved
0 """
1 Faraday Penetration Test IDE
2 Copyright (C) 2019 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) 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) 2013 Infobyte LLC (http://www.infobytesec.com/)
3 See the file 'doc/LICENSE' for the license information
4
5 """
6 from urllib.parse import urlsplit
7 import socket
8 import re
9 import os
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
18
19 from faraday_plugins.plugins.plugin import PluginXMLFormat
20
21 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
22
23 current_path = os.path.abspath(os.getcwd())
24
25 __author__ = "Francisco Amato"
26 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
27 __credits__ = ["Francisco Amato"]
28 __version__ = "1.0.0"
29 __maintainer__ = "Francisco Amato"
30 __email__ = "[email protected]"
31 __status__ = "Development"
32
33
34 class AcunetixXmlParser:
35 """
36 The objective of this class is to parse an xml file generated by
37 the acunetix tool.
38
39 TODO: Handle errors.
40 TODO: Test acunetix output version. Handle what happens if
41 the parser doesn't support it.
42 TODO: Test cases.
43
44 @param acunetix_xml_filepath A proper xml generated by acunetix
45 """
46
47 def __init__(self, xml_output):
48 tree = self.parse_xml(xml_output)
49 if tree:
50 self.sites = list(self.get_items(tree))
51 else:
52 self.sites = []
53
54 def parse_xml(self, xml_output):
55 """
56 Open and parse an xml file.
57
58 TODO: Write custom parser to just read the nodes that we need instead
59 of reading the whole file.
60
61 @return xml_tree An xml tree instance. None if error.
62 """
63 try:
64 tree = ET.fromstring(xml_output)
65 except SyntaxError as err:
66 print("SyntaxError: %s. %s", err, xml_output)
67 return None
68
69 return tree
70
71 def get_items(self, tree):
72 """
73 @return items A list of Host instances
74 """
75
76 for node in tree.findall('Scan'):
77 yield Site(node)
78
79
80 def get_attrib_from_subnode(xml_node, subnode_xpath_expr, attrib_name):
81 """
82 Finds a subnode in the item node and the retrieves a value from it
83
84 @return An attribute value
85 """
86 global ETREE_VERSION
87 node = None
88
89 if ETREE_VERSION[0] <= 1 and ETREE_VERSION[1] < 3:
90
91 match_obj = re.search("([^\@]+?)\[\@([^=]*?)=\'([^\']*?)\'", subnode_xpath_expr)
92
93 if match_obj is not None:
94 node_to_find = match_obj.group(1)
95 xpath_attrib = match_obj.group(2)
96 xpath_value = match_obj.group(3)
97 for node_found in xml_node.findall(node_to_find):
98 if node_found.attrib[xpath_attrib] == xpath_value:
99 node = node_found
100 break
101 else:
102 node = xml_node.find(subnode_xpath_expr)
103
104 else:
105 node = xml_node.find(subnode_xpath_expr)
106
107 if node is not None:
108 return node.get(attrib_name)
109
110 return None
111
112
113 class Site:
114
115 def __init__(self, item_node):
116 self.node = item_node
117 url_data = self.get_url(self.node)
118
119 self.protocol = url_data.scheme
120 self.host = url_data.hostname
121
122 # Use the port in the URL if it is defined, or 80 or 443 by default
123 self.port = url_data.port or (443 if url_data.scheme == "https" else 80)
124
125 self.ip = self.resolve(self.host)
126 self.os = self.get_text_from_subnode('Os')
127 self.banner = self.get_text_from_subnode('Banner')
128 self.items = []
129 for alert in self.node.findall('ReportItems/ReportItem'):
130 self.items.append(Item(alert))
131
132 def get_text_from_subnode(self, subnode_xpath_expr):
133 """
134 Finds a subnode in the host node and the retrieves a value from it.
135
136 @return An attribute value
137 """
138 sub_node = self.node.find(subnode_xpath_expr)
139 if sub_node is not None:
140 return sub_node.text
141
142 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
151
152 def get_url(self, node):
153 url = self.get_text_from_subnode('StartURL')
154 url_data = urlsplit(url)
155 if not url_data.scheme:
156 # Getting url from subnode 'Crawler'
157 url_aux = get_attrib_from_subnode(node, 'Crawler', 'StartUrl')
158 url_data = urlsplit(url_aux)
159
160 return url_data
161
162
163 class Item:
164 """
165 An abstract representation of a Item
166
167
168 @param item_node A item_node taken from an acunetix xml tree
169 """
170
171 def __init__(self, item_node):
172 self.node = item_node
173 self.name = self.get_text_from_subnode('Name')
174 self.severity = self.get_text_from_subnode('Severity')
175 self.request = self.get_text_from_subnode('TechnicalDetails/Request')
176 self.response = self.get_text_from_subnode('TechnicalDetails/Response')
177 self.parameter = self.get_text_from_subnode('Parameter')
178 self.uri = self.get_text_from_subnode('Affects')
179 self.desc = self.get_text_from_subnode('Description')
180
181 if self.get_text_from_subnode('Recommendation'):
182 self.resolution = self.get_text_from_subnode('Recommendation')
183 else:
184 self.resolution = ""
185
186 if self.get_text_from_subnode('reference'):
187 self.desc += "\nDetails: " + self.get_text_from_subnode('Details')
188 else:
189 self.desc += ""
190
191 # Add path and params to the description to create different IDs if at
192 # least one of this fields is different
193 if self.uri:
194 self.desc += '\nPath: ' + self.uri
195 if self.parameter:
196 self.desc += '\nParameter: ' + self.parameter
197
198 self.ref = []
199 for n in item_node.findall('References/Reference'):
200 n2 = n.find('URL')
201 self.ref.append(n2.text)
202
203 def get_text_from_subnode(self, subnode_xpath_expr):
204 """
205 Finds a subnode in the host node and the retrieves a value from it.
206
207 @return An attribute value
208 """
209 sub_node = self.node.find(subnode_xpath_expr)
210 if sub_node is not None:
211 return sub_node.text
212
213 return None
214
215
216 class AcunetixPlugin(PluginXMLFormat):
217 """
218 Example plugin to parse acunetix output.
219 """
220
221 def __init__(self):
222 super().__init__()
223 self.identifier_tag = "ScanGroup"
224 self.id = "Acunetix"
225 self.name = "Acunetix XML Output Plugin"
226 self.plugin_version = "0.0.1"
227 self.version = "9"
228 self.framework_version = "1.0.0"
229 self.options = None
230 self._current_output = None
231 self.target = None
232
233
234 def parseOutputString(self, output, debug=False):
235 """
236 This method will discard the output the shell sends, it will read it
237 from the xml where it expects it to be present.
238
239 NOTE: if 'debug' is true then it is being run from a test case and the
240 output being sent is valid.
241 """
242 parser = AcunetixXmlParser(output)
243
244 for site in parser.sites:
245 if site.ip is None:
246 continue
247 host = []
248 if site.host != site.ip:
249 host = [site.host]
250 h_id = self.createAndAddHost(site.ip, site.os)
251 i_id = self.createAndAddInterface(
252 h_id,
253 site.ip,
254 ipv4_address=site.ip,
255 hostname_resolution=host)
256 s_id = self.createAndAddServiceToInterface(
257 h_id,
258 i_id,
259 "http",
260 "tcp",
261 ports=[site.port],
262 version=site.banner,
263 status='open')
264 for item in site.items:
265 self.createAndAddVulnWebToService(
266 h_id,
267 s_id,
268 item.name,
269 item.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 del parser
279
280
281 def setHost(self):
282 pass
283
284
285 def createPlugin():
286 return AcunetixPlugin()
287
288
289 if __name__ == "__main__":
290 import sys
291 import os
292 if len(sys.argv) == 2:
293 report_file = sys.argv[1]
294 if os.path.isfile(report_file):
295 plugin = createPlugin()
296 plugin.processReport(report_file)
297 print(plugin.get_json())
298 else:
299 print(f"Report not found: {report_file}")
300 else:
301 print(f"USAGE {sys.argv[0]} REPORT_FILE")
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) 2013 Infobyte LLC (http://www.infobytesec.com/)
3 See the file 'doc/LICENSE' for the license information
4 """
5 from faraday_plugins.plugins.plugin import PluginBase
6 import socket
7 import re
8 import os
9
10 current_path = os.path.abspath(os.getcwd())
11
12
13 class AmapPlugin(PluginBase):
14 """ Example plugin to parse amap output."""
15
16 def __init__(self):
17 super().__init__()
18 self.id = "Amap"
19 self.name = "Amap Output Plugin"
20 self.plugin_version = "0.0.3"
21 self.version = "5.4"
22 self.options = None
23 self._current_output = None
24 self._command_regex = re.compile(r'^(amap|sudo amap).*?')
25 self._hosts = []
26
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
35 services = {}
36 for line in output.split('\n'):
37 if line.startswith('#'):
38 continue
39
40 fields = self.get_info(line)
41
42 if len(fields) < 6:
43 continue
44
45 address = fields[0]
46 h_id = self.createAndAddHost(address)
47
48 port = fields[1]
49 protocol = fields[2]
50 port_status = fields[3]
51
52 identification = fields[5]
53 printable_banner = fields[6]
54
55 if port in services.keys():
56 if identification != 'unidentified':
57 services[port][5] += ', ' + identification
58 else:
59 services[port] = [
60 address,
61 port,
62 protocol,
63 port_status,
64 None,
65 identification,
66 printable_banner,
67 None]
68
69 args = {}
70
71 if self.args.__getattribute__("6"):
72 self.ip = self.get_ip_6(self.args.m)
73 args['ipv6_address'] = address
74 else:
75 self.ip = self.getAddress(self.args.m)
76 args['ipv4_address'] = address
77
78 if address != self.args.m:
79 args['hostname_resolution'] = [self.args.m]
80
81 i_id = self.createAndAddInterface(h_id, name=address, **args)
82
83 for key in services:
84 service = services.get(key)
85 self.createAndAddServiceToInterface(
86 h_id,
87 i_id,
88 service[5],
89 service[2],
90 ports=[service[1]],
91 status=service[3],
92 description=service[6])
93
94 return True
95
96 file_arg_re = re.compile(r"^.*(-o \s*[^\s]+\s+(?:-m|)).*$")
97
98 def get_info(self, data):
99 if self.args.__getattribute__("6"):
100 f = re.search(
101 r"^\[(.*)\]:(.*):(.*):(.*):(.*):(.*):(.*):(.*)",
102 data)
103
104 return [
105 f.group(1),
106 f.group(2),
107 f.group(3),
108 f.group(4),
109 f.group(5),
110 f.group(6),
111 f.group(7),
112 f.group(8)] if f else []
113
114 else:
115 return data.split(':')
116
117 def get_ip_6(self, host, port=0):
118 alladdr = socket.getaddrinfo(host, port)
119 ip6 = list(filter(
120 lambda x: x[0] == socket.AF_INET6,
121 alladdr))
122
123 return ip6[0][4][0]
124
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
134 def setHost(self):
135 pass
136
137
138 def createPlugin():
139 return AmapPlugin()
140
141 if __name__ == "__main__":
142 import sys
143 import os
144 if len(sys.argv) == 2:
145 report_file = sys.argv[1]
146 if os.path.isfile(report_file):
147 plugin = createPlugin()
148 plugin.processReport(report_file)
149 print(plugin.get_json())
150 else:
151 print(f"Report not found: {report_file}")
152 else:
153 print(f"USAGE {sys.argv[0]} REPORT_FILE")
154 # I'm Py3
0 """
1 Faraday Penetration Test IDE
2 Copyright (C) 2017 Infobyte LLC (http://www.infobytesec.com/)
3 See the file 'doc/LICENSE' for the license information
4
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 """
4 Faraday Penetration Test IDE
5 Copyright (C) 2017 Infobyte LLC (http://www.infobytesec.com/)
6 See the file 'doc/LICENSE' for the license information
7 """
8
9 import socket
10 from faraday_plugins.plugins.plugin import PluginXMLFormat
11 from lxml import objectify
12 from urllib.parse import urlparse
13
14 __author__ = "Alejando Parodi, Ezequiel Tavella"
15 __copyright__ = "Copyright (c) 2015, Infobyte LLC"
16 __credits__ = ["Alejando Parodi", "Ezequiel Tavella"]
17 __license__ = ""
18 __version__ = "1.0"
19 __maintainer__ = "Ezequiel Tavella"
20 __status__ = "Development"
21
22
23
24 def cleaner_unicode(string):
25 return string
26 # if string is not None:
27 # return string.encode('ascii', errors='backslashreplace')
28 # else:
29 # return string
30
31
32 class AppscanParser():
33
34 def __init__(self, output, logger):
35 self.issue_list = []
36 self.logger = logger
37 self.obj_xml = objectify.fromstring(output.encode('utf-8'))
38
39 def parse_issues(self):
40 issue_type = self.parse_issue_type()
41 for issue in self.obj_xml["issue-group"]["item"]:
42 issue_data = issue_type[issue['issue-type']['ref']]
43 obj_issue = {}
44 obj_issue["name"] = issue_data["name"]
45 obj_issue['advisory'] = issue_data["advisory"]
46 if "cve" in issue_data:
47 obj_issue['cve'] = issue_data["cve"].text
48 obj_issue['url'] = self.get_url(issue['url']['ref'].text)
49 obj_issue['cvss_score'] = issue["cvss-score"].text
50 obj_issue['response'] = self.get_response(issue)
51 obj_issue['request'] = issue['variant-group']['item']["test-http-traffic"].text
52 obj_issue['method'] = self.get_method(issue['variant-group']['item']["test-http-traffic"].text)
53 obj_issue['severity'] = issue['severity'].text
54 obj_issue['issue-description'] = self.parse_advisory_group(issue_data['advisory'])
55 for recommendation in self.obj_xml["fix-recommendation-group"]["item"]:
56 full_data = ""
57 if recommendation.attrib['id'] == issue_data["fix-recommendation"]:
58 for data in recommendation['general']['fixRecommendation']["text"]:
59 full_data += '' + data
60 obj_issue["recomendation"] = full_data
61 if hasattr(recommendation['general']['fixRecommendation'], 'link'):
62 obj_issue["ref_link"] = recommendation['general']['fixRecommendation']['link'].text
63 self.issue_list.append(obj_issue)
64 return self.issue_list
65
66 def parse_hosts(self):
67 hosts_list = []
68 for host in self.obj_xml['scan-configuration']['scanned-hosts']['item']:
69 hosts_dict = {}
70 hosts_dict['ip'] = socket.gethostbyname(host['host'].text)
71 hosts_dict['hostname'] = host['host'].text
72 hosts_dict['os'] = host['operating-system'].text
73 hosts_dict['port'] = host['port'].text
74 if host['port'].text == '443':
75 hosts_dict['scheme'] = 'https'
76 else:
77 hosts_dict['scheme'] = 'http'
78 hosts_list.append(hosts_dict)
79 return hosts_list
80
81 def parse_issue_type(self):
82 res = {}
83 for issue_type in self.obj_xml["issue-type-group"]["item"]:
84 res[issue_type.attrib['id']] = {
85 'name': issue_type.name.text,
86 'advisory': issue_type["advisory"]["ref"].text,
87 'fix-recommendation': issue_type["fix-recommendation"]["ref"].text
88 }
89 if "cve" in issue_type:
90 res[issue_type.attrib['id']] = {'cve': issue_type["cve"].text}
91 return res
92
93 def parse_advisory_group(self, advisory):
94 """
95 Function that parse advisory-group in order to get the item's description
96 """
97 for item in self.obj_xml["advisory-group"]["item"]:
98 if item.attrib['id'] == advisory:
99 return item['advisory']['testTechnicalDescription']['text'].text
100
101 def get_url(self, ref):
102 for item in self.obj_xml['url-group']['item']:
103 if item.attrib['id'] == ref:
104 return item['name'].text
105
106 def get_method(self, http_traffic):
107 methods_list = ['GET', 'POST', 'PUT', 'DELETE', 'CONNECT', 'PATCH', 'HEAD', 'OPTIONS']
108 try:
109 if http_traffic:
110 for item in methods_list:
111 if http_traffic.startswith(item):
112 return item
113 except TypeError:
114 return None
115 return None
116
117 def get_response(self, node):
118 try:
119 response = node['variant-group']['item']['issue-information']["testResponseChunk"].text
120 return response
121 except AttributeError:
122 return None
123
124 def get_scan_information(self):
125
126 scan_information = "File: " + self.obj_xml["scan-information"]["scan-file-name"]\
127 + "\nStart: " + self.obj_xml["scan-information"]["scan-date-and-time"]\
128 + "\nSoftware: " + self.obj_xml["scan-information"]["product-name"]\
129 + "\nVersion: " + self.obj_xml["scan-information"]["product-version"]\
130 + "\nScanner Elapsed time: " + self.obj_xml["scan-summary"]["scan-Duration"]
131
132 return scan_information
133
134
135 class AppscanPlugin(PluginXMLFormat):
136 """ Example plugin to parse Appscan XML report"""
137
138 def __init__(self):
139 super().__init__()
140 self.identifier_tag = "xml-report"
141 self.id = "Appscan"
142 self.name = "Appscan XML Plugin"
143 self.plugin_version = "0.0.1"
144 self.options = None
145 self.open_options = {"mode": "r", "encoding": "utf-8"}
146
147 def parseOutputString(self, output, debug=False):
148 try:
149 parser = AppscanParser(output, self.logger)
150 issues = parser.parse_issues()
151 scanned_hosts = parser.parse_hosts()
152 hosts_dict = {}
153 for host in scanned_hosts:
154 host_id = self.createAndAddHost(host['ip'], os=host['os'], hostnames=[host['hostname']])
155 service_id = self.createAndAddServiceToHost(
156 host_id,
157 host['scheme'],
158 ports=[host['port']],
159 protocol="tcp?HTTP")
160 if host['port']:
161 key_url = f"{host['scheme']}://{host['hostname']}:{host['port']}"
162 else:
163 key_url = f"{host['scheme']}://{host['hostname']}"
164 hosts_dict[key_url] = {'host_id': host_id, 'service_id': service_id}
165 for issue in issues:
166 url_parsed = urlparse(str(issue['url']))
167 url_string = '://'.join([url_parsed.scheme, url_parsed.netloc])
168 for key in hosts_dict:
169 if url_string == key:
170 h_id = hosts_dict[key]['host_id']
171 s_id = hosts_dict[key]['service_id']
172 refs = []
173 if "ref_link" in issue:
174 refs.append(f"Fix link: {issue['ref_link']}" )
175 if "cvss_score" in issue:
176 refs.append(f"CVSS Score: {issue['cvss_score']}")
177 if "cve" in issue:
178 refs.append(f"CVE: {issue['cve']}")
179 if "advisory" in issue:
180 refs.append(f"Advisory: {issue['advisory']}")
181 self.createAndAddVulnWebToService(
182 h_id,
183 s_id,
184 cleaner_unicode(issue["name"]),
185 desc=cleaner_unicode(issue["issue_description"]) if "issue_description" in issue else "",
186 ref=refs,
187 severity=issue["severity"],
188 resolution=cleaner_unicode(issue["recomendation"]),
189 website=url_parsed.netloc,
190 path=url_parsed.path,
191 request=cleaner_unicode(issue["request"]) if "request" in issue else "",
192 response=cleaner_unicode(issue["response"]) if issue["response"] else "",
193 method=issue["method"] if issue["method"] else "")
194 except Exception as e:
195 self.logger.error("Parsing Output Error: %s", e)
196
197
198 def createPlugin():
199 return AppscanPlugin()
200
201 if __name__ == "__main__":
202 import sys
203 import os
204 if len(sys.argv) == 2:
205 report_file = sys.argv[1]
206 if os.path.isfile(report_file):
207 plugin = createPlugin()
208 plugin.processReport(report_file)
209 print(plugin.get_json())
210 else:
211 print(f"Report not found: {report_file}")
212 else:
213 print(f"USAGE {sys.argv[0]} REPORT_FILE")
214 # 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 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 """
4 Faraday Penetration Test IDE
5 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
6 See the file 'doc/LICENSE' for the license information
7 """
8 from faraday_plugins.plugins.plugin import PluginXMLFormat
9 import socket
10 import random
11 import re
12
13 try:
14 import xml.etree.cElementTree as ET
15 except ImportError:
16 import xml.etree.ElementTree as ET
17
18 __author__ = 'Ezequiel Tavella'
19 __copyright__ = 'Copyright 2016, Faraday Project'
20 __credits__ = ['Ezequiel Tavella', 'Matías Ariel Ré Medina', 'Conrad Stein K']
21 __license__ = ''
22 __version__ = '1.0.2'
23 __status__ = 'Development'
24
25
26 class ArachniXmlParser():
27
28 def __init__(self, xml_output):
29 self.tree = self.parse_xml(xml_output)
30 if self.tree:
31 self.issues = self.getIssues(self.tree)
32 self.plugins = self.getPlugins(self.tree)
33 self.system = self.getSystem(self.tree)
34 else:
35 self.system = None
36 self.issues = None
37 self.plugins = None
38
39 def parse_xml(self, xml_output):
40 try:
41 tree = ET.fromstring(xml_output)
42 except SyntaxError as err:
43 print('SyntaxError In xml: %s. %s' % (err, xml_output))
44 return None
45
46 return tree
47
48 def getIssues(self, tree):
49
50 # Get vulnerabilities.
51 issues_tree = tree.find('issues')
52 for self.issue_node in issues_tree:
53 yield Issue(self.issue_node)
54
55 def getPlugins(self, tree):
56
57 # Get info about plugins executed in scan.
58 plugins_tree = tree.find('plugins')
59 return Plugins(plugins_tree)
60
61 def getSystem(self, tree):
62
63 # Get options of scan.
64 return System(tree)
65
66
67 class Issue():
68
69 def __init__(self, issue_node):
70
71 self.node = issue_node
72
73 self.name = self.getDesc('name')
74 self.severity = self.getDesc('severity')
75 self.cwe = self.getDesc('cwe')
76
77 self.remedy_guidance = self.getDesc('remedy_guidance')
78 self.description = self.getDesc('description')
79
80 self.var = self.getChildTag('vector', 'affected_input_name')
81 self.url = self.getChildTag('vector', 'url')
82 self.method = self.getChildTag('vector', 'method')
83
84 self.references = self.getReferences()
85 self.parameters = self.getParameters()
86
87 self.request = self.getRequest()
88 self.response = self.getResponse()
89
90 def getDesc(self, tag):
91
92 # Get value of tag xml
93 description = self.node.find(tag)
94
95 if description is not None and description.text is not None:
96 return description.text.encode('ascii', 'ignore')
97 else:
98 return 'None'
99
100 def getChildTag(self, main_tag, child_tag):
101
102 # Get value of tag child xml
103 main_entity = self.node.find(main_tag)
104
105 if not main_entity:
106 return 'None'
107
108 result = main_entity.find(child_tag)
109
110 if result is not None and result.text is not None:
111 return result.text.encode('ascii', 'ignore')
112 else:
113 return 'None'
114
115 def getReferences(self):
116 """
117 Returns current issue references on this format
118 {'url': 'http://www.site.com', 'name': 'WebSite'}.
119 """
120
121 result = []
122
123 references = self.node.find('references')
124
125 if not references:
126 return result
127
128 for tag in references.findall('reference'):
129 url = tag.get('url')
130 result.append(url)
131
132 return result
133
134 def getParameters(self):
135
136 # Get parameters of query
137 result = []
138
139 parameters = self.node.find('vector').find('inputs')
140
141 if not parameters:
142 return ''
143
144 for param in parameters.findall('input'):
145 name = param.get('name')
146 result.append(name)
147
148 return ' - '.join(result)
149
150 def getRequest(self):
151
152 # Get data about request.
153 try:
154
155 raw_data = self.node.find('page').find('request').find('raw')
156 data = raw_data.text.encode('ascii', 'ignore')
157 return data
158
159 except:
160 return 'None'
161
162 def getResponse(self):
163
164 # Get data about response.
165 try:
166
167 raw_data = self.node.find('page').find(
168 'response').find('raw_headers')
169 data = raw_data.text.encode('ascii', 'ignore')
170 return data
171
172 except:
173 return 'None'
174
175
176 class System():
177
178 def __init__(self, node):
179
180 self.node = node
181
182 self.user_agent = 'None'
183 self.url = 'None'
184 self.audited_elements = 'None'
185 self.modules = 'None'
186 self.cookies = 'None'
187
188 self.getOptions()
189
190 self.version = self.getDesc('version')
191 self.start_time = self.getDesc('start_datetime')
192 self.finish_time = self.getDesc('finish_datetime')
193
194 self.note = self.getNote()
195
196 def getOptions(self):
197
198 # Get values of options scan
199 options = self.node.find('options')
200 if options:
201 options_string = options.text
202 else:
203 return
204
205
206 regex_modules = re.compile('checks:\n([\w\d\s\W\D\S]{0,})(platforms:)')
207 regex_user_agent = re.compile('user_agent:(.+)')
208 regex_cookies = re.compile('cookies: {()}')
209 regex_url = re.compile('url:(.+)')
210
211 regex_audited_elements = re.compile(
212 'audit:\n([\w\d\s\W\D\S]{0,})input:|session:'
213 )
214
215 result = re.search(regex_modules, options_string)
216 if result.group(1):
217 self.modules = result.group(1)
218
219 result = re.search(regex_user_agent, options_string)
220 if result.group(1):
221 self.user_agent = result.group(1)
222
223 result = re.search(regex_cookies, options_string)
224 if result.group(1):
225 self.cookies = result.group(1)
226
227 result = re.search(regex_url, options_string)
228 if result.group(1):
229 self.url = result.group(1)
230
231 result = re.search(regex_audited_elements, options_string)
232 if result.group(1):
233 self.audited_elements = result.group(1)
234
235 def getDesc(self, tag):
236
237 # Return value of tag
238 description = self.node.find(tag)
239
240 if description and description.text:
241 return description.text
242 else:
243 return 'None'
244
245 def getNote(self):
246
247 # Create string with scan information.
248 result = (
249 'Scan url:\n' +
250 self.url +
251 '\nUser Agent:\n' +
252 self.user_agent +
253 '\nVersion Arachni:\n' +
254 self.version +
255 '\nStart time:\n' +
256 self.start_time +
257 '\nFinish time:\n' +
258 self.finish_time +
259 '\nAudited Elements:\n' +
260 self.audited_elements +
261 '\nModules:\n' +
262 self.modules +
263 '\nCookies:\n' +
264 self.cookies)
265
266 return result
267
268
269 class Plugins():
270
271 """
272 Support:
273 WAF (Web Application Firewall) Detector (waf_detector)
274 Healthmap (healthmap)
275 """
276
277 def __init__(self, plugins_node):
278
279 self.plugins_node = plugins_node
280
281 self.healthmap = self.getHealthmap()
282 self.waf = self.getWaf()
283
284
285 def getHealthmap(self):
286
287 # Get info about healthmap
288 healthmap_tree = self.plugins_node.find('healthmap')
289 if not healthmap_tree:
290 return 'None'
291
292 # Create urls list.
293 list_urls = []
294 map_results = healthmap_tree.find('results').find('map')
295
296 for url in map_results:
297
298 if url.tag == 'with_issues':
299 list_urls.append(f"With Issues: {url.text}")
300 else:
301 list_urls.append(f"Without Issues: {url.text}")
302
303 def get_value(name, node=None):
304 if not node:
305 node = healthmap_tree
306 x = healthmap_tree.find(name)
307 if x:
308 return x.text
309 else:
310 return ""
311
312 try:
313 plugin_name = get_value('name')
314 description = get_value('description')
315 results = get_value('results')
316 total = get_value('total', results)
317 with_issues = get_value('with_issues', results)
318 without_issues = get_value('without_issues', results)
319 issue_percentage = get_value('issue_percentage', results)
320
321 urls = '\n'.join(list_urls)
322 result = (f"Plugin Name: {plugin_name}\nDescription: {description}\nStatistics:\nTotal: {total}"
323 f"\nWith Issues: {with_issues}\nWithout Issues: {without_issues}"
324 f"\nIssues percentage: {issue_percentage}\nResults Map:\n {urls}")
325 return result
326
327 except:
328 return 'None'
329
330 def getWaf(self):
331
332 # Get info about waf plugin.
333 waf_tree = self.plugins_node.find('waf_detector')
334
335 def get_value(name, node=None):
336 if not node:
337 node = waf_tree
338 x = waf_tree.find(name)
339 if x:
340 return x.text
341 else:
342 return ""
343
344 try:
345 plugin_name = get_value('name')
346 description = get_value('description')
347 results = waf_tree.find('results')
348 message = get_value('message', results)
349 status = get_value('status', results)
350 result = (f"Plugin Name: {plugin_name}\nDescription: {description}\nResults:"
351 f"\nMessage: {message}\nStatus: {status}")
352 return result
353 except:
354 return 'None'
355
356
357 class ArachniPlugin(PluginXMLFormat):
358
359 # Plugin that parses Arachni's XML report files.
360
361 def __init__(self):
362 super().__init__()
363 self.identifier_tag = ["report", "arachni_report"]
364 self.id = 'Arachni'
365 self.name = 'Arachni XML Output Plugin'
366 self.plugin_version = '1.0.1'
367 self.version = '1.3.2'
368 self.framework_version = '1.0.0'
369 self.options = None
370 self._command_regex = re.compile(r'^(arachni |\.\/arachni).*?')
371 self.protocol = None
372 self.hostname = None
373 self.port = '80'
374 self.address = None
375
376 def report_belongs_to(self, **kwargs):
377 if super().report_belongs_to(**kwargs):
378 report_path = kwargs.get("report_path", "")
379 with open(report_path) as f:
380 output = f.read()
381 return re.search("/Arachni/arachni/", output) is not None
382 return False
383
384 def parseOutputString(self, output, debug=False):
385 """
386 This method will discard the output the shell sends, it will read it
387 from the xml where it expects it to be present.
388 """
389
390 parser = ArachniXmlParser(output)
391
392 # Check xml parsed ok...
393 if not parser.system:
394 print('Error in xml report... Exiting...')
395 return
396
397 self.hostname = self.getHostname(parser.system.url)
398 self.address = self.getAddress(self.hostname)
399
400 # Create host and interface
401 host_id = self.createAndAddHost(self.address)
402
403 interface_id = self.createAndAddInterface(
404 host_id,
405 self.address,
406 ipv4_address=self.address,
407 hostname_resolution=[self.hostname])
408
409 # Create service
410 service_id = self.createAndAddServiceToInterface(
411 host_id,
412 interface_id,
413 self.protocol,
414 'tcp',
415 ports=[self.port],
416 status='Open',
417 version='',
418 description='')
419
420
421 # Create issues.
422 for issue in parser.issues:
423
424 description = issue.description.replace(' ', ' ').replace('\n', ' ').replace('. ', '.\n\n')
425 resol = issue.remedy_guidance.replace(' ', ' ').replace('\n', ' ').replace('. ', '.\n\n')
426
427 references = issue.references
428 if issue.cwe != 'None':
429 references.append('CWE-' + issue.cwe)
430
431 if resol == 'None':
432 resol = ''
433
434 self.createAndAddVulnWebToService(
435 host_id,
436 service_id,
437 name=issue.name,
438 desc=description,
439 resolution=resol,
440 ref=references,
441 severity=issue.severity,
442 website=self.hostname,
443 path=issue.url,
444 method=issue.method,
445 pname=issue.var,
446 params=issue.parameters,
447 request=issue.request,
448 response=issue.response)
449
450 return
451
452 def processCommandString(self, username, current_path, command_string):
453 """
454 Use bash to run sequentialy arachni and arachni_reporter
455 """
456
457 afr_output_file_path = os.path.join(
458 self.data_path,
459 "%s_%s_output-%s.afr" % (
460 self.get_ws(),
461 self.id,
462 random.uniform(1, 10))
463 )
464
465 report_arg_re = r"^.*(--report-save-path[=\s][^\s]+).*$"
466 arg_match = re.match(report_arg_re,command_string)
467 if arg_match is None:
468 main_cmd = re.sub(r"(^.*?arachni)",
469 r"\1 --report-save-path=%s" % afr_output_file_path,
470 command_string)
471 else:
472 main_cmd = re.sub(arg_match.group(1),
473 r"--report-save-path=%s" % afr_output_file_path,
474 command_string)
475
476 # add reporter
477 self._output_file_path = re.sub('.afr', '.xml', afr_output_file_path)
478 cmd_prefix_match = re.match(r"(^.*?)arachni ", command_string)
479 cmd_prefix = cmd_prefix_match.group(1)
480 reporter_cmd = "%s%s --reporter=\"xml:outfile=%s\" \"%s\"" % (
481 cmd_prefix,
482 "arachni_reporter",
483 self._output_file_path,
484 afr_output_file_path)
485 return "/usr/bin/env -- bash -c '%s 2>&1 && if [ -e \"%s\" ];then %s 2>&1;fi'" % (main_cmd, afr_output_file_path, reporter_cmd)
486
487
488 def getHostname(self, url):
489
490 # Strips protocol and gets hostname from URL.
491 reg = re.search(
492 '(http|https|ftp)\://([a-zA-Z0-9\.\-]+(\:[a-zA-Z0-9\.&amp;%\$\-]+)*@)*('
493 '(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'
494 ']|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'
495 '-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'
496 '-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])|localhost|([a-zA-Z0-9\-]+'
497 '\.)*[a-zA-Z0-9\-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pr'
498 'o|aero|coop|museum|[a-zA-Z]{2}))[\:]*([0-9]+)*([/]*($|[a-zA-Z0-9\.\,\?'
499 '\'\\\+&amp;%\$#\=~_\-]+)).*?$',
500 url
501 )
502
503 self.protocol = reg.group(1)
504 self.hostname = reg.group(4)
505
506 if self.protocol == 'https':
507 self.port = 443
508 if reg.group(11) is not None:
509 self.port = reg.group(11)
510
511 return self.hostname
512
513 def getAddress(self, hostname):
514
515 # Returns remote IP address from hostname.
516 try:
517 return socket.gethostbyname(hostname)
518 except socket.error as msg:
519 return self.hostname
520
521
522 def createPlugin():
523 return ArachniPlugin()
524
525 if __name__ == "__main__":
526 import sys
527 import os
528 if len(sys.argv) == 2:
529 report_file = sys.argv[1]
530 if os.path.isfile(report_file):
531 plugin = createPlugin()
532 plugin.processReport(report_file)
533 print(plugin.get_json())
534 else:
535 print(f"Report not found: {report_file}")
536 else:
537 print(f"USAGE {sys.argv[0]} REPORT_FILE")
538 # 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 # 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 from faraday_plugins.plugins.plugin import PluginBase
6 import re
7
8 __author__ = "Federico Kirschbaum"
9 __copyright__ = "Copyright 2013, Faraday Project"
10 __credits__ = ["Federico Kirschbaum"]
11 __license__ = ""
12 __version__ = "1.0.0"
13 __maintainer__ = "Federico Kirschbaum"
14 __email__ = "[email protected]"
15 __status__ = "Development"
16
17
18 class CmdArpScanPlugin(PluginBase):
19 """
20 This plugin handles arp-scan command.
21 Basically inserts into the tree the ouput of this tool
22 """
23
24 def __init__(self):
25 super().__init__()
26 self.id = "arp-scan"
27 self.name = "arp-scan network scanner"
28 self.plugin_version = "0.0.2"
29 self.version = "1.8.1"
30 self.framework_version = "1.0.0"
31 self.options = None
32 self._current_output = None
33 self._command_regex = re.compile(
34 r'^(sudo arp-scan|\.\/arp-scan|arp-scan).*?')
35 self._host_ip = None
36
37 def parseOutputString(self, output, debug=False):
38
39 host_info = re.search(
40 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)",
41 output)
42
43 host_mac_addr = re.search(r"([\dA-F]{2}(?:[-:][\dA-F]{2}){5})", output, re.IGNORECASE)
44
45 if host_info is None:
46 self.logger.info("No hosts detected")
47 else:
48
49 for line in output.split('\n'):
50 vals = line.split("\t")
51
52 if len(vals) == 3:
53
54 if len(vals[0].split(".")) == 4:
55
56 host = vals[0]
57 h_id = self.createAndAddHost(host)
58 i_id = self.createAndAddInterface(h_id, host, ipv4_address=host, mac=vals[1])
59
60 return True
61
62 def processCommandString(self, username, current_path, command_string):
63 return
64
65
66 def createPlugin():
67 return CmdArpScanPlugin()
68
69 if __name__ == "__main__":
70 import sys
71 import os
72 if len(sys.argv) == 2:
73 report_file = sys.argv[1]
74 if os.path.isfile(report_file):
75 plugin = createPlugin()
76 plugin.processReport(report_file)
77 print(plugin.get_json())
78 else:
79 print(f"Report not found: {report_file}")
80 else:
81 print(f"USAGE {sys.argv[0]} REPORT_FILE")
82 # 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 # 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 from faraday_plugins.plugins.plugin import PluginBase
6 import re
7 from urllib.request import urlopen
8 import json
9
10 __author__ = "Francisco Amato"
11 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
12 __credits__ = ["Francisco Amato"]
13 __license__ = ""
14 __version__ = "1.0.0"
15 __maintainer__ = "Francisco Amato"
16 __email__ = "[email protected]"
17 __status__ = "Development"
18
19
20 class BeefPlugin(PluginBase):
21 """
22 Example plugin to parse beef output.
23 """
24
25 def __init__(self):
26 super().__init__()
27 self.id = "Beef"
28 self.name = "BeEF Online Service Plugin"
29 self.plugin_version = "0.0.1"
30 self.version = "0.4.4.9-alpha"
31 self.framework_version = "1.0.0"
32 self.options = None
33 self._current_output = None
34 self.target = None
35 self._command_regex = re.compile(r'^(beef|sudo beef|\.\/beef).*?')
36
37 self.addSetting("Host", str, "http://127.0.0.1:3000/")
38 self.addSetting(
39 "Authkey", str, "c818c7798ae1da38b45a6406c8dd0d6d4d007098")
40 self.addSetting("Enable", str, "0")
41
42 def parseOutputString(self, output, debug=False):
43 """
44 This method will discard the output the shell sends, it will read it from
45 the xml where it expects it to be present.
46
47 NOTE: if 'debug' is true then it is being run from a test case and the
48 output being sent is valid.
49 """
50 try:
51 f = urlopen(self.getSetting(
52 "Host") + "/api/hooks?token=" + self.getSetting("Authkey"))
53 data = json.loads(f.read())
54 except:
55 self.logger.info("[BeEF] - Connection with api")
56 return
57
58 if "hooked-browsers" in data:
59
60 for t in ["online", "offlne"]:
61 for h in data["hooked-browsers"][t]:
62
63 name = str(data["hooked-browsers"][t][h]['name'])
64 version = str(data["hooked-browsers"][t][h]['version'])
65 os = str(data["hooked-browsers"][t][h]['os'])
66 platform = str(data["hooked-browsers"][t][h]['platform'])
67 session = str(data["hooked-browsers"][t][h]['session'])
68 ip = str(data["hooked-browsers"][t][h]['ip'])
69 domain = str(data["hooked-browsers"][t][h]['domain'])
70 port = str(data["hooked-browsers"][t][h]['port'])
71 page_uri = str(data["hooked-browsers"][t][h]['page_uri'])
72
73 desc = "Client ip:" + ip + \
74 " has been injected with BeEF using the url:" + page_uri + "\n"
75
76 desc += "More information:"
77 desc += "\ntype:" + t
78 desc += "\nname:" + name
79 desc += "\nversion:" + version
80 desc += "\nos:" + os
81 desc += "\nplatform:" + platform
82 desc += "\nsession:" + session
83 desc += "\nip:" + ip
84 desc += "\ndomain:" + domain
85 desc += "\nport:" + port
86 desc += "\npage_uri:" + page_uri
87
88 h_id = self.createAndAddHost(ip)
89 v_id = self.createAndAddVulnToHost(
90 h_id,
91 "BeEF injected " + t + " session:" + session,
92 desc=desc,
93 ref=["http://http://beefproject.com/"],
94 severity=3)
95
96 def processCommandString(self, username, current_path, command_string):
97 return None
98
99 def setHost(self):
100 pass
101
102
103 def createPlugin():
104 return BeefPlugin()
105
106 if __name__ == "__main__":
107 import sys
108 import os
109 if len(sys.argv) == 2:
110 report_file = sys.argv[1]
111 if os.path.isfile(report_file):
112 plugin = createPlugin()
113 plugin.processReport(report_file)
114 print(plugin.get_json())
115 else:
116 print(f"Report not found: {report_file}")
117 else:
118 print(f"USAGE {sys.argv[0]} REPORT_FILE")
119 # I'm Py3
0 """
1 Faraday Penetration Test IDE
2 Copyright (C) 2018 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) 2018 Infobyte LLC (http://www.infobytesec.com/)
3 See the file 'doc/LICENSE' for the license information
4 """
5 import re
6 import socket
7 from urllib.parse import urlparse
8 from faraday_plugins.plugins.plugin import PluginBase
9
10 __author__ = "Roberto Focke"
11 __copyright__ = "Copyright (c) 2017, Infobyte LLC"
12 __license__ = ""
13 __version__ = "1.0.0"
14
15
16 class brutexss (PluginBase):
17
18 def __init__(self):
19 super().__init__()
20 self.id = "brutexss"
21 self.name = "brutexss"
22 self.plugin_version = "0.0.2"
23 self.version = "1.0.0"
24 self.protocol ='tcp'
25 self._command_regex = re.compile(r'^(sudo brutexss|brutexss|sudo brutexss\.py|brutexss\.py|python brutexss\.py|\.\/brutexss\.py).*?')
26
27 def parseOutputString(self, output, debug=False):
28 lineas = output.split("\n")
29 parametro = []
30 found_vuln = False
31 for linea in lineas:
32 if linea.find("is available! Good!") > 0:
33 print(linea)
34 url = re.findall('(?:[-\w.]|(?:%[\da-fA-F]{2}))+', linea)[0]
35 port = 80
36 if urlparse(url).scheme == 'https':
37 port = 443
38 netloc_splitted = urlparse(url).netloc.split(':')
39 if len(netloc_splitted) > 1:
40 port = netloc_splitted[1]
41 if linea.find("Vulnerable") > 0 and "No" not in linea:
42 vuln_list = re.findall("\w+", linea)
43 if vuln_list[2] == "Vulnerable":
44 parametro.append(vuln_list[1])
45 found_vuln=len(parametro) > 0
46 host_id = self.createAndAddHost(url)
47 address=socket.gethostbyname(url)
48 interface_id = self.createAndAddInterface(host_id,address,ipv4_address=address,hostname_resolution=[url])
49 service_id = self.createAndAddServiceToInterface(host_id, interface_id, self.protocol, 'tcp',
50 ports=[port], status='Open', version="", description="")
51 if found_vuln:
52 self.createAndAddVulnWebToService(host_id,service_id, name="xss", desc="XSS", ref='', severity='med',
53 website=url, path='', method='', pname='', params=''.join(parametro),
54 request='', response='')
55
56 def processCommandString(self, username, current_path, command_string):
57 return None
58
59
60 def createPlugin():
61 return brutexss()
62
63 if __name__ == "__main__":
64 import sys
65 import os
66 if len(sys.argv) == 2:
67 report_file = sys.argv[1]
68 if os.path.isfile(report_file):
69 plugin = createPlugin()
70 plugin.processReport(report_file)
71 print(plugin.get_json())
72 else:
73 print(f"Report not found: {report_file}")
74 else:
75 print(f"USAGE {sys.argv[0]} REPORT_FILE")
76 # 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 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 """
4 Faraday Penetration Test IDE
5 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
6 See the file 'doc/LICENSE' for the license information
7
8 """
9 import re
10 import os
11 import sys
12 import base64
13 from bs4 import BeautifulSoup, Comment
14 from faraday_plugins.plugins.plugin import PluginXMLFormat
15 from urllib.parse import urlsplit
16 import distutils.util #pylint: disable=import-error
17
18
19 try:
20 import xml.etree.cElementTree as ET
21 import xml.etree.ElementTree as ET_ORIG
22 ETREE_VERSION = ET_ORIG.VERSION
23 except ImportError:
24 import xml.etree.ElementTree as ET
25 ETREE_VERSION = ET.VERSION
26
27 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
28
29 current_path = os.path.abspath(os.getcwd())
30
31 __author__ = "Francisco Amato"
32 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
33 __credits__ = ["Francisco Amato", "Micaela Ranea Sanchez"]
34 __license__ = ""
35 __version__ = "1.1.0"
36 __maintainer__ = "Francisco Amato"
37 __email__ = "[email protected]"
38 __status__ = "Development"
39
40
41 class BurpXmlParser:
42 """
43 The objective of this class is to parse an xml file generated by the burp tool.
44
45 TODO: Handle errors.
46 TODO: Test burp output version. Handle what happens if the parser doesn't support it.
47 TODO: Test cases.
48
49 @param burp_xml_filepath A proper xml generated by burp
50 """
51
52 def __init__(self, xml_output):
53
54 self.target = None
55 self.port = "80"
56 self.host = None
57
58 tree = self.parse_xml(xml_output)
59 if tree:
60 self.items = [data for data in self.get_items(tree)]
61 else:
62 self.items = []
63
64 def parse_xml(self, xml_output):
65 """
66 Open and parse an xml file.
67
68 TODO: Write custom parser to just read the nodes that we need instead of
69 reading the whole file.
70
71 @return xml_tree An xml tree instance. None if error.
72 """
73 try:
74 tree = ET.fromstring(xml_output)
75 except SyntaxError as err:
76 print("SyntaxError: %s. %s" % (err, xml_output))
77 return None
78
79 return tree
80
81 def get_items(self, tree):
82 """
83 @return items A list of Host instances
84 """
85 bugtype = ''
86
87 for node in tree.findall('issue'):
88 yield Item(node)
89
90
91 def get_attrib_from_subnode(xml_node, subnode_xpath_expr, attrib_name):
92 """
93 Finds a subnode in the item node and the retrieves a value from it
94
95 @return An attribute value
96 """
97 global ETREE_VERSION
98 node = None
99
100 if ETREE_VERSION[0] <= 1 and ETREE_VERSION[1] < 3:
101
102 match_obj = re.search(
103 "([^\@]+?)\[\@([^=]*?)=\'([^\']*?)\'", subnode_xpath_expr)
104 if match_obj is not None:
105
106 node_to_find = match_obj.group(1)
107 xpath_attrib = match_obj.group(2)
108 xpath_value = match_obj.group(3)
109 for node_found in xml_node.findall(node_to_find):
110 if node_found.attrib[xpath_attrib] == xpath_value:
111 node = node_found
112 break
113 else:
114 node = xml_node.find(subnode_xpath_expr)
115
116 else:
117 node = xml_node.find(subnode_xpath_expr)
118
119 if node is not None:
120 return node.get(attrib_name)
121
122 return None
123
124
125 class Item:
126 """
127 An abstract representation of a Item
128 @param item_node A item_node taken from an burp xml tree
129 """
130
131 def __init__(self, item_node):
132 self.node = item_node
133
134 name = item_node.findall('name')[0]
135 host_node = item_node.findall('host')[0]
136 path = item_node.findall('path')[0]
137 location = item_node.findall('location')[0]
138 severity = item_node.findall('severity')[0]
139 external_id = item_node.findall('type')[0]
140 request = self.decode_binary_node('./requestresponse/request')
141 response = self.decode_binary_node('./requestresponse/response')
142
143 detail = self.do_clean(item_node.findall('issueDetail'))
144 remediation = self.do_clean(item_node.findall('remediationBackground'))
145 background = self.do_clean(item_node.findall('issueBackground'))
146
147 self.url = host_node.text
148
149 url_data = urlsplit(self.url)
150
151 self.protocol = url_data.scheme
152 self.host = url_data.hostname
153
154 # Use the port in the URL if it is defined, or 80 or 443 by default
155 self.port = url_data.port or (443 if url_data.scheme == "https"
156 else 80)
157
158 self.name = name.text
159 self.location = location.text
160 self.path = path.text
161
162 self.ip = host_node.get('ip')
163 self.url = self.node.get('url')
164 self.severity = severity.text
165 self.request = request
166 self.response = response
167 self.detail = detail
168 self.remediation = remediation
169 self.background = background
170 self.external_id = external_id.text
171
172
173 def do_clean(self, value):
174
175 myreturn = ""
176 if value is not None:
177 if len(value) > 0:
178 myreturn = value[0].text
179 return myreturn
180
181 def decode_binary_node(self, path):
182 """
183 Finds a subnode matching `path` and returns its inner text if
184 it has no base64 attribute or its base64 decoded inner text if
185 it has it.
186 """
187 nodes = self.node.findall(path)
188 try:
189 subnode = nodes[0]
190 except IndexError:
191 return ""
192 encoded = distutils.util.strtobool(subnode.get('base64', 'false'))
193 if encoded:
194 res = base64.b64decode(subnode.text).decode('utf-8', errors="backslashreplace")
195 else:
196 res = subnode.text
197 return "".join([ch for ch in res if ord(ch) <= 128])
198
199 def get_text_from_subnode(self, subnode_xpath_expr):
200 """
201 Finds a subnode in the host node and the retrieves a value from it.
202 @return An attribute value
203 """
204
205 sub_node = self.node.find(subnode_xpath_expr)
206 if sub_node is not None:
207 return sub_node.text
208
209 return None
210
211
212 class BurpPlugin(PluginXMLFormat):
213 """
214 Example plugin to parse burp output.
215 """
216
217 def __init__(self):
218 super().__init__()
219 self.identifier_tag = "issues"
220 self.id = "Burp"
221 self.name = "Burp XML Output Plugin"
222 self.plugin_version = "0.0.2"
223 self.version = "1.6.05 BurpPro"
224 self.framework_version = "1.0.0"
225 self.options = None
226 self._current_output = None
227 self.target = None
228
229
230 def parseOutputString(self, output, debug=False):
231
232 parser = BurpXmlParser(output)
233 for item in parser.items:
234
235 h_id = self.createAndAddHost(item.ip)
236
237 i_id = self.createAndAddInterface(
238 h_id,
239 item.ip,
240 ipv4_address=item.ip,
241 hostname_resolution=[item.host])
242
243 s_id = self.createAndAddServiceToInterface(
244 h_id,
245 i_id,
246 item.protocol,
247 "tcp",
248 ports=[str(item.port)],
249 status="open")
250
251 desc = "Detail\n" + item.detail
252 if item.background:
253 desc += "\nBackground\n" + item.background
254 desc = self.removeHtml(desc)
255 resolution = self.removeHtml(item.remediation) if item.remediation else ""
256
257 v_id = self.createAndAddVulnWebToService(
258 h_id,
259 s_id,
260 item.name,
261 desc=desc,
262 severity=item.severity,
263 website=item.host,
264 path=item.path,
265 request=item.request,
266 response=item.response,
267 resolution=resolution,
268 external_id=item.external_id)
269
270 del parser
271
272 def processCommandString(self, username, current_path, command_string):
273 return None
274
275 def removeHtml(self, markup):
276 soup = BeautifulSoup(markup, "html.parser")
277
278 # Replace line breaks and paragraphs for new lines
279 for tag in soup.find_all(["br", "p"]):
280 tag.append("\n")
281 tag.unwrap()
282
283 # Replace lists for * and new lines
284 for tag in soup.find_all(["ul", "ol"]):
285 for item in tag.find_all("li"):
286 item.insert_before("* ")
287 item.append("\n")
288 item.unwrap()
289 tag.unwrap()
290
291 # Remove all other HTML tags
292 for tag in soup.find_all():
293 tag.unwrap()
294
295 # Remove all comments
296 for child in soup.children:
297 if isinstance(child, Comment):
298 child.extract()
299
300 return str(soup)
301
302 def setHost(self):
303 pass
304
305
306 def createPlugin():
307 return BurpPlugin()
308
309
310 if __name__ == "__main__":
311 import sys
312 import os
313 if len(sys.argv) == 2:
314 report_file = sys.argv[1]
315 if os.path.isfile(report_file):
316 plugin = createPlugin()
317 plugin.processReport(report_file)
318 print(plugin.get_json())
319 else:
320 print(f"Report not found: {report_file}")
321 else:
322 print(f"USAGE {sys.argv[0]} REPORT_FILE")
323 # 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 # I'm Py3
0 """
1 Updated by Mike Zhong, 25 Oct 2017.
2
3 Faraday Penetration Test IDE
4 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
5 See the file 'doc/LICENSE' for the license information
6 """
7 import re
8 import socket
9
10 from faraday_plugins.plugins.plugin import PluginBase
11
12 __author__ = u"Andres Tarantini"
13 __copyright__ = u"Copyright (c) 2015 Andres Tarantini"
14 __credits__ = [u"Andres Tarantini"]
15 __license__ = u"MIT"
16 __version__ = u"0.0.1"
17 __maintainer__ = u"Andres Tarantini"
18 __email__ = u"[email protected]"
19 __status__ = u"Development"
20
21
22 class DigPlugin(PluginBase):
23 """
24 Handle DiG (http://linux.die.net/man/1/dig) output
25 """
26
27 def __init__(self):
28 super().__init__()
29 self.id = u"dig"
30 self.name = u"DiG"
31 self.plugin_version = u"0.0.1"
32 self.version = u"9.9.5-3"
33 self._command_regex = re.compile(r'^(dig).*?')
34
35 def parseOutputString(self, output):
36 # Ignore all lines that start with ";"
37 parsed_output = [line for line in output.splitlines() if line and line[
38 0] != u";"]
39 if not parsed_output:
40 return True
41
42 # Parse results
43 results = []
44 answer_section_columns = [u"domain",
45 u"ttl", u"class", u"type", u"data"]
46 for line in parsed_output:
47 line_split = line.split() # the first 4 elements are domain, ttl, class, type; everything else data
48 results.append(dict(zip(answer_section_columns, line_split[:4] + [line_split[4:]] )))
49
50 # Create hosts is results information is relevant
51 try:
52 for result in results:
53 relevant_types = [u"A", u"AAAA", u"MX", u"NS", u"SOA", u"TXT"]
54 # TODO implement more types from https://en.wikipedia.org/wiki/List_of_DNS_record_types
55
56 if result.get(u"type") in relevant_types:
57
58 # get domain
59 domain = result.get(u"domain")
60
61
62 # get IP address (special if type "A")
63 if result.get(u"type") == u"A": # A = IPv4 address from dig
64 ip_address = result.get(u"data")[0]
65 else: # if not, from socket
66 ip_address = socket.gethostbyname(domain)
67
68 # Create host
69 host_id = self.createAndAddHost(ip_address)
70
71 # create interface (special if type "AAAA")
72 if result.get(u"type") == u"AAAA": # AAAA = IPv6 address
73 # TODO is there a function to dynamically update the paramter ipv6_address of an already-created interface?
74 ipv6_address = result.get(u"data")[0]
75 interface_id = self.createAndAddInterface(
76 host_id,
77 ip_address,
78 ipv4_address=ip_address,
79 ipv6_address=ipv6_address,
80 hostname_resolution=[domain])
81 else:
82 interface_id = self.createAndAddInterface(
83 host_id,
84 ip_address,
85 ipv4_address=ip_address,
86 hostname_resolution=[domain])
87
88
89 # all other TYPES that aren't 'A' and 'AAAA' are dealt here:
90 if result.get(u"type") == u"MX": # Mail exchange record
91 mx_priority = result.get(u"data")[0]
92 mx_record = result.get(u"data")[1]
93
94 service_id = self.createAndAddServiceToInterface(
95 host_id=host_id,
96 interface_id=interface_id,
97 name=mx_record,
98 protocol="SMTP",
99 ports=[25],
100 description="E-mail Server")
101
102 text = "Priority: " + mx_priority
103 self.createAndAddNoteToService(
104 host_id=host_id,
105 service_id=service_id,
106 name="priority",
107 text=text.encode('ascii', 'ignore'))
108
109 elif result.get(u"type") == u"NS": # Name server record
110 ns_record = result.get(u"data")[0]
111 self.createAndAddServiceToInterface(
112 host_id=host_id,
113 interface_id=interface_id,
114 name=ns_record,
115 protocol="DNS",
116 ports=[53],
117 description="DNS Server")
118
119 elif result.get(u"type") == u"SOA": # Start of Authority Record
120 ns_record = result.get(u"data")[0] # primary namer server
121 responsible_party = result.get(u"data")[1] # responsible party of domain
122 timestamp = result.get(u"data")[2]
123 refresh_zone_time = result.get(u"data")[3]
124 retry_refresh_time = result.get(u"data")[4]
125 upper_limit_time = result.get(u"data")[5]
126 negative_result_ttl = result.get(u"data")[6]
127
128 service_id = self.createAndAddServiceToInterface(
129 host_id=host_id,
130 interface_id=interface_id,
131 name=ns_record,
132 protocol="DNS",
133 ports=[53],
134 description="Authority Record")
135
136 text = (
137 "Responsible Party: " + responsible_party +
138 "\nTimestep: " + timestamp +
139 "\nTime before zone refresh (sec): " + refresh_zone_time +
140 "\nTime before retry refresh (sec): " + retry_refresh_time +
141 "\nUpper Limit before Zone is no longer authoritive (sec): " + upper_limit_time +
142 "\nNegative Result TTL: " + negative_result_ttl)
143
144 self.createAndAddNoteToService(
145 host_id=host_id,
146 service_id=service_id,
147 name="priority",
148 text=text.encode('ascii', 'ignore'))
149
150 elif result.get(u"type") == u"TXT": # TXT record
151 text = " ".join(result.get(u"data")[:])
152 self.createAndAddNoteToHost(
153 host_id=host_id,
154 name="TXT Information",
155 text=text.encode('ascii', 'ignore'))
156
157 except Exception as ex:
158 print("some part of the dig plug-in caused an error! Please check repo/dig/plugin.py")
159 return False
160
161
162 return True
163
164
165 def createPlugin():
166 return DigPlugin()
167
168 if __name__ == "__main__":
169 import sys
170 import os
171 if len(sys.argv) == 2:
172 report_file = sys.argv[1]
173 if os.path.isfile(report_file):
174 plugin = createPlugin()
175 plugin.processReport(report_file)
176 print(plugin.get_json())
177 else:
178 print(f"Report not found: {report_file}")
179 else:
180 print(f"USAGE {sys.argv[0]} REPORT_FILE")
181 # 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 # 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 from faraday_plugins.plugins.plugin import PluginBase
6 import re
7 import socket
8
9 __author__ = "Federico Fernandez - @q3rv0"
10 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
11 __license__ = ""
12 __version__ = "1.0.0"
13 __maintainer__ = "Federico Fernandez"
14 __email__ = "[email protected]"
15 __status__ = "Development"
16
17
18 class dirbPlugin(PluginBase):
19
20 def __init__(self):
21 super().__init__()
22 self.id = "dirb"
23 self.name = "Dirb"
24 self.plugin_version = "0.0.1"
25 self.version = "2.22"
26 self.regexpUrl = r'((http[s]?)\:\/\/([\w\.]+)[.\S]+)'
27 self._command_regex = re.compile(r'^(?:sudo dirb|dirb|\.\/dirb|sudo \.\/dirb)\s+(?:(http[s]?)\:\/\/([\w\.]+)[.\S]+)')
28 self.text = []
29
30 def getPort(self, host, proto):
31 p = re.search(r"\:([0-9]+)\/", host)
32 if p is not None:
33 return p.group(1)
34 elif proto == 'https':
35 return 443
36 else:
37 return 80
38
39 def getIP(self, host):
40 try:
41 ip = socket.gethostbyname(host)
42 except Exception:
43 pass
44
45 return ip
46
47 def state(self, output):
48 if output.find('COULDNT CONNECT') != -1:
49 return "close"
50 else:
51 return "open"
52
53 def pathsDirListing(self, output):
54 data = []
55 r = re.findall(self.regexpUrl + r"[\-\._\w\*\s]+\s+\(!\) WARNING: Directory IS LISTABLE",
56 output)
57 for u in r:
58 data.append(u[0])
59
60 paths = "\n".join(data)
61 return paths
62
63 def note(self, output):
64 dirs = re.findall(r"==> DIRECTORY: "+self.regexpUrl, output)
65 files = re.findall(r"\+ " + self.regexpUrl + r" \(.+\)", output)
66 for d in dirs:
67 self.text.append("DIRECTORY: " + d[0])
68
69 for f in files:
70 self.text.append("FILE: " + f[0])
71
72 self.text = '\n'.join(self.text)
73
74 def parseOutputString(self, output, debug=False):
75
76 url = re.search(r"URL_BASE: " + self.regexpUrl, output)
77 paths = self.pathsDirListing(output)
78 status = self.state(output)
79 self.note(output)
80
81 if output.find('END_TIME') != -1 and url is not None:
82 proto = url.group(2)
83 domain = url.group(3)
84 ip = self.getIP(domain)
85 puerto = self.getPort(url.group(1), proto)
86
87 host_id = self.createAndAddHost(ip)
88 iface_id = self.createAndAddInterface(host_id, ip, ipv4_address = ip)
89
90 serv_id = self.createAndAddServiceToInterface(host_id, iface_id, proto, protocol = proto, ports =[puerto], status = status)
91
92 if len(self.text) > 0:
93 self.createAndAddVulnWebToService(host_id, serv_id, 'Url Fuzzing', severity=0, desc=self.text, website=domain)
94
95 if len(paths) > 0:
96 self.createAndAddVulnWebToService(host_id, serv_id, "Directory Listing", severity = "med", website = domain, request = paths, method = "GET")
97
98 return True
99
100 def processCommandString(self, username, current_path, command_string):
101 """
102 Adds the -oX parameter to get xml output to the command string that the
103 user has set.
104 """
105
106 no_stop_on_warn_msg_re = r"\s+-w"
107 arg_search = re.search(no_stop_on_warn_msg_re,command_string)
108 extra_arg = ""
109 if arg_search is None:
110 extra_arg +=" -w"
111
112 silent_mode_re = r"\s+-S"
113 arg_search = re.search(silent_mode_re,command_string)
114 if arg_search is None:
115 extra_arg +=" -S"
116 return "%s%s" % (command_string, extra_arg)
117
118 def createPlugin():
119 return dirbPlugin()
120
121 if __name__ == "__main__":
122 import sys
123 import os
124 if len(sys.argv) == 2:
125 report_file = sys.argv[1]
126 if os.path.isfile(report_file):
127 plugin = createPlugin()
128 plugin.processReport(report_file)
129 print(plugin.get_json())
130 else:
131 print(f"Report not found: {report_file}")
132 else:
133 print(f"USAGE {sys.argv[0]} REPORT_FILE")
134 # 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 # 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 json
7 import shlex
8 import socket
9 import argparse
10 import tempfile
11 import urllib.parse as urlparse
12
13
14 from faraday_plugins.plugins.plugin import PluginTerminalOutput
15 from faraday_plugins.plugins.plugins_utils import get_vulnweb_url_fields
16
17
18 __author__ = "Matías Lang"
19 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
20 __credits__ = ["Matías Lang"]
21 __license__ = ""
22 __version__ = "0.0.1"
23 __maintainer__ = "Matías Lang"
24 __email__ = "[email protected]"
25 __status__ = "Development"
26
27
28 status_codes = {
29 200: "OK", 201: "Created", 202: "Accepted",
30 203: "Non-Authoritative Information", 204: "No Content",
31 205: "Reset Content", 206: "Partial Content", 207: "Multi-Status",
32 208: "Already Reported", 226: "IM Used", 300: "Multiple Choices",
33 301: "Moved Permanently", 302: "Found", 303: "See Other",
34 304: "Not Modified", 305: "Use Proxy", 306: "Switch Proxy",
35 307: "Temporary Redirect", 308: "Permanent Redirect",
36 400: "Bad Request", 401: "Unauthorized", 402: "Payment Required",
37 403: "Forbidden", 404: "Not Found", 405: "Method Not Allowed",
38 406: "Not Acceptable", 407: "Proxy Authentication Required",
39 408: "Request Timeout", 409: "Conflict", 410: "Gone",
40 411: "Length Required", 412: "Precondition Failed",
41 413: "Payload Too Large", 414: "URI Too Long",
42 415: "Unsupported Media Type", 416: "Range Not Satisfiable",
43 417: "Expectation Failed", 418: "I'm a teapot",
44 421: "Misdirected Request", 422: "Unprocessable Entity", 423: "Locked",
45 424: "Failed Dependency", 426: "Upgrade Required",
46 428: "Precondition Required", 429: "Too Many Requests",
47 431: "Request Header Fields Too Large",
48 451: "Unavailable For Legal Reasons", 500: "Internal Server Error",
49 501: "Not Implemented", 502: "Bad Gateway", 503: "Service Unavailable",
50 504: "Gateway Timeout", 505: "HTTP Version Not Supported",
51 506: "Variant Also Negotiates", 507: "Insufficient Storage",
52 508: "Loop Detected", 510: "Not Extended",
53 511: "Network Authentication Required",
54 }
55
56
57 class DirsearchPlugin(PluginTerminalOutput):
58 def __init__(self):
59 super().__init__()
60 self.id = "dirsearch"
61 self.name = "dirsearch"
62 self.plugin_version = "0.0.1"
63 self.version = "0.0.1"
64 self._command_regex = re.compile(
65 r'^(sudo )?(python[0-9\.]? )?dirsearch(\.py)?')
66 self.ignore_parsing = False
67 self.json_report_file = None
68 self.addSetting("Ignore 403", str, "1")
69
70 def parseOutputString(self, output, debug=False):
71 if self.ignore_parsing:
72 return
73 if self.json_report_file:
74 # We ran the plugin via command line
75 try:
76 fp = open(self.json_report_file)
77 except IOError:
78 self.log('Error opening JSON in the file {}'.format(
79 self.json_report_file
80 ), 'ERROR')
81 else:
82 self.parse_json(fp.read())
83 if self.remove_report:
84 os.unlink(self.json_report_file)
85 else:
86 # We are importing a report
87 self.parse_json(output)
88
89 def resolve(self, domain):
90 return socket.gethostbyname(domain)
91
92 @property
93 def should_ignore_403(self):
94 val = self.getSetting('Ignore 403')
95 if not val or not int(val):
96 return False
97 return True
98
99 def parse_json(self, contents):
100 try:
101 data = json.loads(contents)
102 except ValueError:
103 self.log('Error parsing report. Make sure the file has valid '
104 'JSON', 'ERROR')
105 return
106 for (base_url, items) in data.items():
107 base_split = urlparse.urlsplit(base_url)
108 ip = self.resolve(base_split.hostname)
109 h_id = self.createAndAddHost(ip)
110
111 i_id = self.createAndAddInterface(
112 h_id,
113 name=ip,
114 ipv4_address=ip,
115 hostname_resolution=[base_split.hostname])
116
117 s_id = self.createAndAddServiceToInterface(
118 h_id,
119 i_id,
120 base_split.scheme,
121 'tcp',
122 [base_split.port],
123 status="open")
124
125 for item in items:
126 self.parse_found_url(base_url, h_id, s_id, item)
127
128 def parse_found_url(self, base_url, h_id, s_id, item):
129 if self.should_ignore_403 and item['status'] == 403:
130 return
131 url = urlparse.urlsplit(urlparse.urljoin(base_url, item['path']))
132 response = "HTTP/1.1 {} {}\nContent-Length: {}".format(
133 item['status'], status_codes.get(item['status'], 'unknown'),
134 item['content-length'])
135 redirect = item.get('redirect')
136 if redirect is not None:
137 response += '\nLocation: {}'.format(redirect)
138 self.createAndAddVulnWebToService(
139 h_id,
140 s_id,
141 name='Path found: {} ({})'.format(item['path'], item['status']),
142 desc="Dirsearch tool found the following URL: {}".format(
143 url.geturl()),
144 severity="info",
145 method='GET',
146 response=response,
147 **get_vulnweb_url_fields(url.geturl()))
148
149 def processCommandString(self, username, current_path, command_string):
150 parser = argparse.ArgumentParser(conflict_handler='resolve')
151 parser.add_argument('-h', '--help', action='store_true')
152 parser.add_argument('--json-report')
153 args, unknown = parser.parse_known_args(shlex.split(command_string))
154
155 if args.help:
156 self.devlog('help detected, ignoring parsing')
157 return command_string
158 if args.json_report:
159 # The user already defined a path to the JSON report
160 self.json_report_file = args.json_report
161 self.remove_report = False
162 return command_string
163 else:
164 # Use temporal file to save the report data
165 self.json_report_file = tempfile.mktemp(
166 prefix="dirsearch_report_", suffix=".json")
167 self.devlog('Setting report file to {}'.format(
168 self.json_report_file))
169 self.remove_report = True
170 return '{} --json-report {}'.format(command_string,
171 self.json_report_file)
172
173
174 def createPlugin():
175 return DirsearchPlugin()
176
177 if __name__ == "__main__":
178 import sys
179 import os
180 if len(sys.argv) == 2:
181 report_file = sys.argv[1]
182 if os.path.isfile(report_file):
183 plugin = createPlugin()
184 plugin.processReport(report_file)
185 print(plugin.get_json())
186 else:
187 print(f"Report not found: {report_file}")
188 else:
189 print(f"USAGE {sys.argv[0]} REPORT_FILE")
190 # 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 # 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 from faraday_plugins.plugins.plugin import PluginBase
7 import re
8 import os
9
10 try:
11 import xml.etree.cElementTree as ET
12 import xml.etree.ElementTree as ET_ORIG
13 ETREE_VERSION = ET_ORIG.VERSION
14 except ImportError:
15 import xml.etree.ElementTree as ET
16 ETREE_VERSION = ET.VERSION
17
18 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
19
20 current_path = os.path.abspath(os.getcwd())
21
22 __author__ = "Francisco Amato"
23 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
24 __credits__ = ["Francisco Amato"]
25 __license__ = ""
26 __version__ = "1.0.0"
27 __maintainer__ = "Francisco Amato"
28 __email__ = "[email protected]"
29 __status__ = "Development"
30
31
32 class DnsenumXmlParser:
33 """
34 The objective of this class is to parse an xml file generated by the dnsenum tool.
35
36 TODO: Handle errors.
37 TODO: Test dnsenum output version. Handle what happens if the parser doesn't support it.
38 TODO: Test cases.
39
40 @param dnsenum_xml_filepath A proper xml generated by dnsenum
41 """
42
43 def __init__(self, xml_output):
44 tree = self.parse_xml(xml_output)
45
46 if tree:
47 self.items = [data for data in self.get_items(tree)]
48 else:
49 self.items = []
50
51 def parse_xml(self, xml_output):
52 """
53 Open and parse an xml file.
54
55 TODO: Write custom parser to just read the nodes that we need instead of
56 reading the whole file.
57
58 @return xml_tree An xml tree instance. None if error.
59 """
60 try:
61 tree = ET.fromstring(xml_output)
62 except SyntaxError as err:
63 print("SyntaxError: %s. %s" % (err, xml_output))
64 return None
65
66 return tree
67
68 def get_items(self, tree):
69 """
70 @return items A list of Host instances
71 """
72 bugtype = ''
73
74 node = tree.findall('testdata')[0]
75 for hostnode in node.findall('host'):
76 yield Item(hostnode)
77
78
79 def get_attrib_from_subnode(xml_node, subnode_xpath_expr, attrib_name):
80 """
81 Finds a subnode in the item node and the retrieves a value from it
82
83 @return An attribute value
84 """
85 global ETREE_VERSION
86 node = None
87
88 if ETREE_VERSION[0] <= 1 and ETREE_VERSION[1] < 3:
89
90 match_obj = re.search(
91 "([^\@]+?)\[\@([^=]*?)=\'([^\']*?)\'", subnode_xpath_expr)
92 if match_obj is not None:
93 node_to_find = match_obj.group(1)
94 xpath_attrib = match_obj.group(2)
95 xpath_value = match_obj.group(3)
96 for node_found in xml_node.findall(node_to_find):
97 if node_found.attrib[xpath_attrib] == xpath_value:
98 node = node_found
99 break
100 else:
101 node = xml_node.find(subnode_xpath_expr)
102
103 else:
104 node = xml_node.find(subnode_xpath_expr)
105
106 if node is not None:
107 return node.get(attrib_name)
108
109 return None
110
111
112 class Item:
113 """
114 An abstract representation of a Item
115
116 TODO: Consider evaluating the attributes lazily
117 TODO: Write what's expected to be present in the nodes
118 TODO: Refactor both Host and the Port clases?
119
120 @param item_node A item_node taken from an dnsenum xml tree
121 """
122
123 def __init__(self, item_node):
124 self.node = item_node
125
126 self.hostname = self.get_text_from_subnode('hostname')
127 self.ip = self.node.text
128
129 def do_clean(self, value):
130 myreturn = ""
131 if value is not None:
132 myreturn = re.sub("\n", "", value)
133 return myreturn
134
135 def get_text_from_subnode(self, subnode_xpath_expr):
136 """
137 Finds a subnode in the host node and the retrieves a value from it.
138
139 @return An attribute value
140 """
141 sub_node = self.node.find(subnode_xpath_expr)
142 if sub_node is not None:
143 return sub_node.text
144
145 return None
146
147
148 class DnsenumPlugin(PluginBase):
149 """
150 Example plugin to parse dnsenum output.
151 """
152
153 def __init__(self):
154 super().__init__()
155 self.id = "Dnsenum"
156 self.name = "Dnsenum XML Output Plugin"
157 self.plugin_version = "0.0.1"
158 self.version = "1.2.2"
159 self.options = None
160 self._current_output = None
161 self._command_regex = re.compile(
162 r'^(sudo dnsenum|dnsenum|sudo dnsenum\.pl|dnsenum\.pl|perl dnsenum\.pl|\.\/dnsenum\.pl).*?')
163
164
165 def parseOutputString(self, output, debug=False):
166 """
167 This method will discard the output the shell sends, it will read it from
168 the xml where it expects it to be present.
169
170 NOTE: if 'debug' is true then it is being run from a test case and the
171 output being sent is valid.
172 """
173
174 parser = DnsenumXmlParser(output)
175
176 for item in parser.items:
177 h_id = self.createAndAddHost(item.ip)
178 i_id = self.createAndAddInterface(
179 h_id,
180 item.ip,
181 ipv4_address=item.ip,
182 hostname_resolution=[item.hostname])
183
184 del parser
185
186 xml_arg_re = re.compile(r"^.*(-o\s*[^\s]+).*$")
187
188 def processCommandString(self, username, current_path, command_string):
189 """
190 Adds the -oX parameter to get xml output to the command string that the
191 user has set.
192 """
193
194 arg_match = self.xml_arg_re.match(command_string)
195
196 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)
201 else:
202 return re.sub(arg_match.group(1),
203 r"-o %s" % self._output_file_path,
204 command_string)
205
206 def setHost(self):
207 pass
208
209
210 def createPlugin():
211 return DnsenumPlugin()
212
213 if __name__ == "__main__":
214 import sys
215 import os
216 if len(sys.argv) == 2:
217 report_file = sys.argv[1]
218 if os.path.isfile(report_file):
219 plugin = createPlugin()
220 plugin.processReport(report_file)
221 print(plugin.get_json())
222 else:
223 print(f"Report not found: {report_file}")
224 else:
225 print(f"USAGE {sys.argv[0]} REPORT_FILE")
226 # 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 # I'm Py3
0 """from __future__ import print_function
1
2 Faraday Penetration Test IDE
3 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
4 See the file 'doc/LICENSE' for the license information
5 """
6 from faraday_plugins.plugins.plugin import PluginBase
7 import re
8 import os
9 import random
10 from collections import defaultdict
11
12 current_path = os.path.abspath(os.getcwd())
13
14 __author__ = "Francisco Amato"
15 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
16 __credits__ = ["Francisco Amato"]
17 __license__ = ""
18 __version__ = "1.0.0"
19 __maintainer__ = "Francisco Amato"
20 __email__ = "[email protected]"
21 __status__ = "Development"
22
23
24 class DnsmapParser:
25 """
26 The objective of this class is to parse an xml file generated by the
27 dnsmap tool.
28
29 TODO: Handle errors.
30 TODO: Test dnsmap output version. Handle what happens if the parser
31 doesn't support it.
32 TODO: Test cases.
33
34 @param dnsmap_filepath A proper simple report generated by dnsmap
35 """
36
37 def __init__(self, output):
38 self.items = defaultdict(list)
39 if "\n\n" in output:
40 self.parse_txt(output)
41 else:
42 self.parse_csv(output)
43
44 def parse_txt(self, output):
45 hosts = self.split_output_lines(output)
46
47 for host_data in hosts:
48 if len(host_data) == 2:
49 ip = self.clean_ip(host_data[1])
50 hostname = host_data[0]
51 self.add_host_info_to_items(ip, hostname)
52 elif len(host_data) > 2:
53 hostname = host_data.pop(0)
54 for ip_address in host_data:
55 ip = self.clean_ip(ip_address)
56 self.add_host_info_to_items(ip, hostname)
57
58 def parse_csv(self, output):
59 hosts = list(filter(None, output.splitlines()))
60
61 for host in hosts:
62 host_data = host.split(",", 1)
63 if host_data[1].count(',') == 0:
64 ip = host_data[1]
65 hostname = host_data[0]
66 self.add_host_info_to_items(ip, hostname)
67 else:
68 hostname = host_data.pop(0)
69 ips = host_data[0].split(",")
70 for ip_address in ips:
71 self.add_host_info_to_items(ip_address, hostname)
72
73 def split_output_lines(self, output):
74 splitted = output.splitlines()
75 hosts_list = []
76 aux_list = []
77 for i in range(0, len(splitted)):
78 if not splitted[i]:
79 hosts_list.append(aux_list)
80 aux_list = []
81 continue
82 else:
83 aux_list.append(splitted[i])
84 return hosts_list
85
86 def clean_ip(self, item):
87 ip = item.split(':', 1)
88 return ip[1].strip()
89
90 def add_host_info_to_items(self, ip_address, hostname):
91 self.items[ip_address].append(hostname)
92
93
94 class DnsmapPlugin(PluginBase):
95 """Example plugin to parse dnsmap output."""
96
97 def __init__(self):
98 super().__init__()
99 self.id = "Dnsmap"
100 self.name = "Dnsmap Output Plugin"
101 self.plugin_version = "0.3"
102 self.version = "0.30"
103 self.options = None
104 self._current_output = None
105 self.current_path = None
106 self._command_regex = re.compile(r'^(sudo dnsmap|dnsmap|\.\/dnsmap).*?')
107 self.xml_arg_re = re.compile(r"^.*(-r\s*[^\s]+).*$")
108
109
110 def canParseCommandString(self, current_input):
111 if self._command_regex.match(current_input.strip()):
112 return True
113 else:
114 return False
115
116 def parseOutputString(self, output, debug=False):
117 """
118 This method will discard the output the shell sends, it will read it
119 from the xml where it expects it to be present.
120 """
121 parser = DnsmapParser(output)
122 for ip_address, hostnames in parser.items.items():
123 h_id = self.createAndAddHost(ip_address, hostnames=hostnames)
124 return True
125
126 def processCommandString(self, username, current_path, command_string):
127 """
128 Adds the parameter to get output to the command string that the
129 user has set.
130 """
131 arg_match = self.xml_arg_re.match(command_string)
132
133 if arg_match is None:
134 return "%s -r %s \\n" % (command_string, self._output_file_path)
135 else:
136 return re.sub(arg_match.group(1),
137 r"-r %s" % self._output_file_path,
138 command_string)
139
140
141 def createPlugin():
142 return DnsmapPlugin()
143
144 if __name__ == "__main__":
145 import sys
146 import os
147 if len(sys.argv) == 2:
148 report_file = sys.argv[1]
149 if os.path.isfile(report_file):
150 plugin = createPlugin()
151 plugin.processReport(report_file)
152 print(plugin.get_json())
153 else:
154 print(f"Report not found: {report_file}")
155 else:
156 print(f"USAGE {sys.argv[0]} REPORT_FILE")
157 # 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 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 """
4 Faraday Penetration Test IDE
5 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
6 See the file 'doc/LICENSE' for the license information
7 """
8 from faraday_plugins.plugins.plugin import PluginBase
9 import re
10 import os
11 import sys
12
13 try:
14 import xml.etree.cElementTree as ET
15 import xml.etree.ElementTree as ET_ORIG
16 ETREE_VERSION = ET_ORIG.VERSION
17 except ImportError:
18 import xml.etree.ElementTree as ET
19 ETREE_VERSION = ET.VERSION
20
21 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
22
23 current_path = os.path.abspath(os.getcwd())
24
25 __author__ = "Francisco Amato"
26 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
27 __credits__ = ["Francisco Amato"]
28 __license__ = ""
29 __version__ = "1.0.0"
30 __maintainer__ = "Francisco Amato"
31 __email__ = "[email protected]"
32 __status__ = "Development"
33
34
35 class DnsreconXmlParser:
36 """
37 The objective of this class is to parse an xml file generated by the dnsrecon tool.
38
39 TODO: Handle errors.
40 TODO: Test dnsrecon output version. Handle what happens if the parser doesn't support it.
41 TODO: Test cases.
42
43 @param dnsrecon_xml_filepath A proper xml generated by dnsrecon
44 """
45
46 def __init__(self, xml_output):
47
48 tree = self.parse_xml(xml_output)
49
50 if tree:
51 self.hosts = [host for host in self.get_hosts(tree)]
52 else:
53 self.hosts = []
54
55 def parse_xml(self, xml_output):
56 """
57 Open and parse an xml file.
58
59 TODO: Write custom parser to just read the nodes that we need instead of
60 reading the whole file.
61
62 @return xml_tree An xml tree instance. None if error.
63 """
64 try:
65 tree = ET.fromstring(xml_output)
66 except SyntaxError as err:
67 print("SyntaxError: %s. %s" % (err, xml_output))
68 return None
69
70 return tree
71
72 def get_hosts(self, tree):
73 """
74 @return items A list of Host instances
75 """
76 for item_node in tree.findall('record'):
77 yield Item(item_node)
78
79
80 def get_attrib_from_subnode(xml_node, subnode_xpath_expr, attrib_name):
81 """
82 Finds a subnode in the item node and the retrieves a value from it
83
84 @return An attribute value
85 """
86 global ETREE_VERSION
87 node = None
88
89 if ETREE_VERSION[0] <= 1 and ETREE_VERSION[1] < 3:
90
91 match_obj = re.search(
92 "([^\@]+?)\[\@([^=]*?)=\'([^\']*?)\'", subnode_xpath_expr)
93 if match_obj is not None:
94
95 node_to_find = match_obj.group(1)
96 xpath_attrib = match_obj.group(2)
97 xpath_value = match_obj.group(3)
98 for node_found in xml_node.findall(node_to_find):
99 if node_found.attrib[xpath_attrib] == xpath_value:
100 node = node_found
101 break
102 else:
103 node = xml_node.find(subnode_xpath_expr)
104
105 else:
106 node = xml_node.find(subnode_xpath_expr)
107
108 if node is not None:
109 return node.get(attrib_name)
110
111 return None
112
113
114 class Item:
115 """
116 An abstract representation of a Item
117
118 TODO: Consider evaluating the attributes lazily
119 TODO: Write what's expected to be present in the nodes
120 TODO: Refactor both Host and the Port clases?
121
122 @param item_node A item_node taken from an dnsrecon xml tree
123 """
124
125 def __init__(self, item_node):
126 self.node = item_node
127
128 self.type = self.do_clean(self.node.get('type'))
129 self.zonetransfer = self.do_clean(self.node.get('zone_transfer'))
130 self.ns_server = self.do_clean(self.node.get('ns_server'))
131 self.address = self.do_clean(self.node.get(
132 'address')) if not self.type == "info" else self.ns_server
133
134 self.target = self.do_clean(self.node.get('target'))
135 self.name = self.do_clean(self.node.get('name'))
136 self.exchange = self.do_clean(self.node.get('exchange'))
137
138 print("GENERATION:" + self.type, self.address, self.zonetransfer)
139
140 def do_clean(self, value):
141 myreturn = ''
142 if value is not None:
143 myreturn = re.sub(" |\n", "", value)
144 return myreturn
145
146 def get_text_from_subnode(self, subnode_xpath_expr):
147 """
148 Finds a subnode in the host node and the retrieves a value from it.
149
150 @return An attribute value
151 """
152 sub_node = self.node.find(subnode_xpath_expr)
153 if sub_node is not None:
154 return sub_node.text
155
156 return None
157
158
159 class DnsreconPlugin(PluginBase):
160 """
161 Example plugin to parse dnsrecon output.
162 """
163
164 def __init__(self):
165 super().__init__()
166 self.id = "Dnsrecon"
167 self.name = "Dnsrecon XML Output Plugin"
168 self.plugin_version = "0.0.2"
169 self.version = "0.8.7"
170 self.framework_version = "1.0.0"
171 self.options = None
172 self._current_output = None
173 self._command_regex = re.compile(
174 r'^(sudo dnsrecon|dnsrecon|sudo dnsrecon\.py|dnsrecon\.py|python dnsrecon\.py|\.\/dnsrecon\.py).*?')
175
176
177 def validHosts(self, hosts):
178 valid_records = ["NS", "CNAME", "A", "MX", "info"]
179 hosts = list(filter(lambda h: h.type in valid_records, hosts))
180 return hosts
181
182 def parseOutputString(self, output, debug=False):
183 """
184 This method will discard the output the shell sends, it will read it from
185 the xml where it expects it to be present.
186
187 NOTE: if 'debug' is true then it is being run from a test case and the
188 output being sent is valid.
189 """
190
191 parser = DnsreconXmlParser(output)
192
193 for host in self.validHosts(parser.hosts):
194
195 print(host.type, host.name, host.zonetransfer)
196 hostname = host.target
197 if host.type == "MX":
198 hostname = host.exchange
199 elif host.type == "A":
200 hostname = host.name
201
202 h_id = self.createAndAddHost(host.address)
203
204 if self._isIPV4(str(host.address)):
205 i_id = self.createAndAddInterface(
206 h_id,
207 name=host.address,
208 ipv4_address=host.address,
209 hostname_resolution=[hostname])
210 else:
211 i_id = self.createAndAddInterface(
212 h_id,
213 name=host.address,
214 ipv6_address=host.address,
215 hostname_resolution=[hostname])
216
217 if host.type == "info":
218
219 s_id = self.createAndAddServiceToInterface(
220 h_id,
221 i_id,
222 "domain",
223 protocol="tcp",
224 ports=["53"],
225 status="open")
226
227 if host.zonetransfer == "success":
228 v_id = self.createAndAddVulnToService(
229 h_id,
230 s_id,
231 name="Zone transfer",
232 desc="A Dns server allows unrestricted zone transfers",
233 ref=["CVE-1999-0532"])
234
235 del parser
236
237 def _isIPV4(self, ip):
238 if len(ip.split(".")) == 4:
239 return True
240 else:
241 return False
242
243 xml_arg_re = re.compile(r"^.*(--xml\s*[^\s]+).*$")
244
245 def processCommandString(self, username, current_path, command_string):
246 """
247 Adds the -oX parameter to get xml output to the command string that the
248 user has set.
249 """
250 arg_match = self.xml_arg_re.match(command_string)
251
252 if arg_match is None:
253 return re.sub(r"(^.*?dnsrecon(\.py)?)",
254 r"\1 --xml %s" % self._output_file_path,
255 command_string)
256 else:
257 return re.sub(arg_match.group(1),
258 r"--xml %s" % self._output_file_path,
259 command_string)
260
261 def setHost(self):
262 pass
263
264
265 def createPlugin():
266 return DnsreconPlugin()
267
268 if __name__ == "__main__":
269 import sys
270 import os
271 if len(sys.argv) == 2:
272 report_file = sys.argv[1]
273 if os.path.isfile(report_file):
274 plugin = createPlugin()
275 plugin.processReport(report_file)
276 print(plugin.get_json())
277 else:
278 print(f"Report not found: {report_file}")
279 else:
280 print(f"USAGE {sys.argv[0]} REPORT_FILE")
281
282 # 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 # 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 PluginBase
8 import re
9 import os
10 import socket
11
12 current_path = os.path.abspath(os.getcwd())
13
14 __author__ = "Francisco Amato"
15 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
16 __credits__ = ["Francisco Amato"]
17 __license__ = ""
18 __version__ = "1.0.0"
19 __maintainer__ = "Francisco Amato"
20 __email__ = "[email protected]"
21 __status__ = "Development"
22
23
24 class DnswalkParser:
25 """
26 The objective of this class is to parse an xml file generated
27 by the dnswalk tool.
28
29 TODO: Handle errors.
30 TODO: Test dnswalk output version. Handle what happens if the parser
31 doesn't support it.
32 TODO: Test cases.
33
34 @param dnswalk_filepath A proper simple report generated by dnswalk
35 """
36
37 def __init__(self, output):
38
39 lists = output.split("\n")
40 self.items = []
41
42 for line in lists:
43 mregex = re.search("WARN: ([\w\.]+) ([\w]+) ([\w\.]+):", line)
44 if mregex is not None:
45
46 item = {
47 'host': mregex.group(1),
48 'ip': mregex.group(3),
49 'type': mregex.group(2)}
50
51 self.items.append(item)
52
53 mregex = re.search(
54 "Getting zone transfer of ([\w\.]+) from ([\w\.]+)\.\.\.done\.",
55 line)
56
57 if mregex is not None:
58 ip = self.getAddress(mregex.group(2))
59 item = {
60 'host': mregex.group(1),
61 'ip': ip,
62 'type': 'info'}
63 self.items.append(item)
64
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
71
72
73 class DnswalkPlugin(PluginBase):
74 """
75 Example plugin to parse dnswalk output.
76 """
77
78 def __init__(self):
79 super().__init__()
80 self.id = "Dnswalk"
81 self.name = "Dnswalk XML Output Plugin"
82 self.plugin_version = "0.0.1"
83 self.version = "2.0.2"
84 self.options = None
85 self._current_output = None
86 self._current_path = None
87 self._command_regex = re.compile(
88 r'^(sudo dnswalk|dnswalk|\.\/dnswalk).*?')
89
90 global current_path
91
92 def canParseCommandString(self, current_input):
93 if self._command_regex.match(current_input.strip()):
94 return True
95 else:
96 return False
97
98 def parseOutputString(self, output, debug=False):
99 """
100 output is the shell output of command Dnswalk.
101 """
102 parser = DnswalkParser(output)
103
104 for item in parser.items:
105
106 if item['type'] == "A":
107
108 h_id = self.createAndAddHost(item['ip'])
109 i_id = self.createAndAddInterface(
110 h_id,
111 item['ip'],
112 ipv4_address=item['ip'],
113 hostname_resolution=[item['host']])
114
115 elif item['type'] == "info":
116
117 h_id = self.createAndAddHost(item['ip'])
118
119 i_id = self.createAndAddInterface(
120 h_id,
121 item['ip'],
122 ipv4_address=item['ip'],
123 hostname_resolution=[item['host']])
124
125 s_id = self.createAndAddServiceToInterface(
126 h_id,
127 i_id,
128 "domain",
129 "tcp",
130 ports=['53'])
131
132 self.createAndAddVulnToService(
133 h_id,
134 s_id,
135 "Zone transfer",
136 desc="A Dns server allows unrestricted zone transfers",
137 ref=["CVE-1999-0532"])
138
139 return True
140
141 def processCommandString(self, username, current_path, command_string):
142 return None
143
144
145 def createPlugin():
146 return DnswalkPlugin()
147
148 if __name__ == "__main__":
149 import sys
150 import os
151 if len(sys.argv) == 2:
152 report_file = sys.argv[1]
153 if os.path.isfile(report_file):
154 plugin = createPlugin()
155 plugin.processReport(report_file)
156 print(plugin.get_json())
157 else:
158 print(f"Report not found: {report_file}")
159 else:
160 print(f"USAGE {sys.argv[0]} REPORT_FILE")
161 # 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 # 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 from faraday_plugins.plugins.plugin import PluginBase
7 import socket
8 import re
9 import os
10 import random
11
12 current_path = os.path.abspath(os.getcwd())
13
14 __author__ = "Francisco Amato"
15 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
16 __credits__ = ["Francisco Amato"]
17 __license__ = ""
18 __version__ = "1.0.0"
19 __maintainer__ = "Francisco Amato"
20 __email__ = "[email protected]"
21 __status__ = "Development"
22
23 valid_records = ["NS", "CNAME", "A"]
24
25
26 class FierceParser:
27 """
28 The objective of this class is to parse an shell output generated by
29 the fierce tool.
30
31 TODO: Handle errors.
32 TODO: Test fierce output version. Handle what happens if the parser
33 doesn't support it.
34 TODO: Test cases.
35
36 @param fierce_filepath A proper simple report generated by fierce
37 """
38
39 def __init__(self, output):
40 self.target = None
41 self.items = []
42
43 regex = re.search(
44 "DNS Servers for ([\w\.-]+):\n([^$]+)Trying zone transfer first...",
45 output)
46
47 if regex is not None:
48 self.target = regex.group(1)
49 mstr = re.sub("\t", "", regex.group(2))
50 self.dns = list(filter(None, mstr.splitlines()))
51
52 regex = re.search(
53 "Now performing [\d]+ test\(s\)...\n([^$]+)\nSubnets found ",
54 output)
55 if regex is not None:
56 hosts_list = regex.group(1).splitlines()
57 for i in hosts_list:
58 if i != "":
59 mstr = i.split("\t")
60 host = mstr[1]
61 record = "A"
62 ip = mstr[0]
63 self.add_host_info_to_items(ip, host, record)
64
65 self.isZoneVuln = False
66 output = output.replace('\\$', '')
67 regex = re.search(
68 "Whoah, it worked - misconfigured DNS server found:([^$]+)\nThere isn't much point continuing, you have everything.", output)
69
70 if regex is not None:
71 self.isZoneVuln = True
72 dns_list = regex.group(1).splitlines()
73 for i in dns_list:
74 if i != "":
75 mstr = i.split()
76 if (mstr and mstr[0] != "" and len(mstr) > 3 and mstr[3] in valid_records):
77 host = mstr[0]
78 record = mstr[3]
79 ip = mstr[4]
80 self.add_host_info_to_items(ip, host, record)
81
82 def add_host_info_to_items(self, ip_address, hostname, record):
83 data = {}
84 exists = False
85 for item in self.items:
86 if ip_address in item['ip']:
87 item['hosts'].append(hostname)
88 exists = True
89
90 if not exists:
91 data['ip'] = ip_address
92 data['hosts'] = [hostname]
93 data['record'] = record
94 self.items.append(data)
95
96
97 class FiercePlugin(PluginBase):
98 """
99 Example plugin to parse fierce output.
100 """
101
102 def __init__(self):
103 super().__init__()
104 self.id = "Fierce"
105 self.name = "Fierce Output Plugin"
106 self.plugin_version = "0.0.1"
107 self.version = "0.9.9"
108 self.options = None
109 self._current_output = None
110 self._current_path = None
111 self._command_regex = re.compile(
112 r'^(sudo fierce|fierce|sudo fierce\.pl|fierce\.pl|perl fierce\.pl|\.\/fierce\.pl).*?')
113 global current_path
114
115 self.xml_arg_re = re.compile(r"^.*(>\s*[^\s]+).*$")
116
117 def canParseCommandString(self, current_input):
118 if self._command_regex.match(current_input.strip()):
119 return True
120 else:
121 return False
122
123 def resolveCNAME(self, item, items):
124 for i in items:
125 if (item['ip'] in i['hosts']):
126 item['ip'] = i['ip']
127 return item
128 try:
129 item['ip'] = socket.gethostbyname(item['ip'])
130 except:
131 pass
132 return item
133
134 def resolveNS(self, item, items):
135 try:
136 item['hosts'][0] = item['ip']
137 item['ip'] = socket.gethostbyname(item['ip'])
138 except:
139 pass
140 return item
141
142 def parseOutputString(self, output, debug=False):
143
144 parser = FierceParser(output)
145 for item in parser.items:
146
147 item['isResolver'] = False
148 item['isZoneVuln'] = False
149 if (item['record'] == "CNAME"):
150 self.resolveCNAME(item, parser.items)
151 if (item['record'] == "NS"):
152 self.resolveNS(item, parser.items)
153 item['isResolver'] = True
154 item['isZoneVuln'] = parser.isZoneVuln
155 for item2 in parser.items:
156
157 if item['ip'] == item2['ip'] and item != item2:
158 item2['isResolver'] = item['isResolver']
159 item2['isZoneVuln'] = item['isZoneVuln']
160 item['ip'] = ''
161
162 for item in parser.items:
163 if item['ip'] == "127.0.0.1" or item['ip'] == '':
164 continue
165 h_id = self.createAndAddHost(
166 item['ip'],
167 hostnames=item['hosts'])
168
169 if item['isResolver']:
170 s_id = self.createAndAddServiceToHost(
171 h_id,
172 "domain",
173 "tcp",
174 ports=['53'])
175
176 if item['isZoneVuln']:
177 self.createAndAddVulnToService(
178 h_id,
179 s_id,
180 "Zone transfer",
181 desc="A Dns server allows unrestricted zone transfers",
182 ref=["CVE-1999-0532"])
183
184 def processCommandString(self, username, current_path, command_string):
185 self._output_file_path = os.path.join(
186 self.data_path,
187 "%s_%s_output-%s.txt" % (
188 self.get_ws(),
189 self.id,
190 random.uniform(1, 10))
191 )
192
193 arg_match = self.xml_arg_re.match(command_string)
194
195 if arg_match is None:
196 return "%s > %s" % (command_string, self._output_file_path)
197 else:
198 return re.sub(arg_match.group(1),
199 r"> %s" % self._output_file_path,
200 command_string)
201
202
203 def createPlugin():
204 return FiercePlugin()
205
206 if __name__ == "__main__":
207 import sys
208 import os
209 if len(sys.argv) == 2:
210 report_file = sys.argv[1]
211 if os.path.isfile(report_file):
212 plugin = createPlugin()
213 plugin.processReport(report_file)
214 print(plugin.get_json())
215 else:
216 print(f"Report not found: {report_file}")
217 else:
218 print(f"USAGE {sys.argv[0]} REPORT_FILE")
219 # I'm Py3
0 import base64
1 import io
2 import re
3 from html.parser import HTMLParser
4 from zipfile import ZipFile
5
6 import html2text
7 from lxml import objectify
8 from faraday_plugins.plugins.plugin import PluginByExtension
9
10
11 class FortifyPlugin(PluginByExtension):
12 """
13 Example plugin to parse nmap output.
14 """
15
16 def __init__(self):
17 super().__init__()
18 self.id = "Fortify"
19 self.name = "Fortify XML Output Plugin"
20 self.plugin_version = "0.0.1"
21 self.extension = ".fpr"
22 self.open_options = {"mode": "rb"}
23
24 def _process_fvdl_vulns(self, fp):
25
26 for host in fp.hosts.keys():
27 fp.hosts[host] = self.createAndAddHost(host)
28
29 for vuln in fp.vulns.keys():
30 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'],
36 resolution="",
37 data="",
38 external_id=vuln.text
39 )
40
41 def _process_webinspect_vulns(self, fp):
42 for vuln_data in fp.sast_vulns:
43 host_id = self.createAndAddHost(
44 vuln_data['host'] or vuln_data['website'])
45
46 service_name = vuln_data['service'].get('name', 'unknown')
47 protocol_name = 'line number'
48 if vuln_data['service']['port'] == '443':
49 service_name = 'https'
50 protocol_name = 'tcp'
51 if vuln_data['service']['port'] == '80':
52 service_name = 'http'
53 protocol_name = 'tcp'
54
55 service_id = self.createAndAddServiceToHost(
56 host_id,
57 service_name,
58 protocol=protocol_name,
59 ports=[vuln_data['service']['port']])
60
61 self.createAndAddVulnWebToService(
62 host_id, service_id,
63 vuln_data['name'],
64 website=vuln_data['website'] or '',
65 path=vuln_data['path'] or '',
66 query=vuln_data['query'] or '',
67 method=vuln_data['method'] or '',
68 request=vuln_data['request'] or '',
69 ref=vuln_data['references'],
70 response=vuln_data['response'] or '',
71 desc=vuln_data['description'],
72 #resolution=vuln_data[''],
73 severity=vuln_data['severity']
74 )
75
76 def parseOutputString(self, output, debug=False):
77 fp = FortifyParser(output)
78 if fp.fvdl is not None:
79 self._process_fvdl_vulns(fp)
80 if fp.webinspect is not None:
81 self._process_webinspect_vulns(fp)
82
83 return True
84
85
86 class FortifyParser:
87 """
88 Parser for fortify on demand
89 """
90
91 def __init__(self, output):
92 self.vulns = {}
93 self.sast_vulns = []
94 self.hosts = {}
95 self.fvdl = None
96 self.webinspect = None
97 self.audit = None
98 self.suppressed = []
99 self.vuln_classes = []
100 self.descriptions = {}
101
102 self._uncompress_fpr(output)
103 self._extract_vulns()
104 self._prepare_description_templates()
105
106 # regexes used in format_description
107 self.remove_extra_chars = re.compile(r'&amp;(\w*);')
108 self.replacements_idx = re.compile(r'<Replace key="(.*?)"[\s\/].*?>')
109 self.replacements_holders = re.compile(r'<Replace key=".*?"[\s\/].*?>')
110 self.replacements_idx2 = re.compile(r'<Replace key="(.*?)"(\slink="(.*?)")?[\s\/].*?>')
111
112 def _uncompress_fpr(self, output):
113 with ZipFile(io.BytesIO(output)) as fprcontent:
114 try:
115 self.fvdl = objectify.fromstring(fprcontent.read('audit.fvdl'))
116 except KeyError:
117 pass
118 try:
119 self.webinspect = objectify.fromstring(fprcontent.read('webinspect.xml'))
120 except KeyError:
121 pass
122 try:
123 self.audit = objectify.fromstring(fprcontent.read('audit.xml'))
124 except KeyError:
125 pass
126
127 def _process_fvdl(self):
128 for vuln in self.fvdl.Vulnerabilities.iterchildren():
129
130 vulnID = vuln.InstanceInfo.InstanceID
131
132 if vulnID in self.suppressed:
133 continue
134
135 self.vulns[vulnID] = {}
136
137 # the last children of Primary (Entry tags) always contains vuln filename ,path and line
138 _last_entry = None
139 for _last_entry in vuln.AnalysisInfo.Unified.Trace.Primary.iterchildren():
140 pass
141
142 path = _last_entry.Node.SourceLocation.get('path')
143
144 self.vulns[vulnID]['host'] = path
145 self.vulns[vulnID]['name'] = "{} {}".format(vuln.ClassInfo.Type,
146 getattr(vuln.ClassInfo, "Subtype", ""))
147 self.vulns[vulnID]['class'] = vuln.ClassInfo.ClassID
148 self.vulns[vulnID]['replacements'] = {}
149
150 self.vulns[vulnID]['severity'] = self.calculate_severity(vuln)
151
152 # placeholder for storing hosts ids when created in main plugin method
153 if path not in self.hosts.keys():
154 self.hosts[path] = None
155
156 if vuln.ClassInfo.ClassID not in self.vuln_classes:
157 self.vuln_classes.append(vuln.ClassInfo.ClassID)
158
159 # fortify bug that when it has no replacements, shows blank in fortify dashboard
160 if not hasattr(vuln.AnalysisInfo.Unified, "ReplacementDefinitions"):
161 self.vulns[vulnID]['replacements'] = None
162 continue
163
164 try:
165 getattr(vuln.AnalysisInfo.Unified, "ReplacementDefinitions")
166
167 for repl in vuln.AnalysisInfo.Unified.ReplacementDefinitions.iterchildren(
168 tag="{xmlns://www.fortifysoftware.com/schema/fvdl}Def"):
169
170 repl_val = repl.get('key')
171 if repl.get('link'):
172 repl_val = repl.get('link')
173
174 self.vulns[vulnID]['replacements'][repl_val] = repl.get('value')
175 except AttributeError:
176 self.vulns[vulnID]['replacements'] = None
177
178 def _process_webinspect(self):
179 for session in self.webinspect.getchildren():
180 hostname = session.Host.text
181 port = session.Port.text
182 service_data = {}
183 if port:
184 service_data['port'] = port
185
186 path = session.Request.Path.text
187 query = session.Request.FullQuery.text
188 method = session.Request.Method.text
189 request = ''
190 if session.RawRequest.text:
191 request = base64.b64decode(session.RawRequest.text)
192 response = ''
193 if session.RawResponse.text:
194 response = base64.b64decode(session.RawResponse.text)
195 status_code = session.Response.StatusCode.text
196
197 for issues in session.Issues:
198 for issue_data in issues.getchildren():
199 params = ''
200 check_type = issue_data.CheckTypeID
201 if check_type.text.lower() != 'vulnerability':
202 # TODO: when plugins accept tags, we shoudl this as a tag.
203 pass
204 name = issue_data.Name.text
205 external_id = issue_data.VulnerabilityID.text
206 faraday_severities = {
207 0: 'info',
208 1: 'low',
209 2: 'med',
210 3: 'high',
211 4: 'critical'
212 }
213 severity = faraday_severities[issue_data.Severity]
214 references = []
215 try:
216 classifications = issue_data.Classifications.getchildren()
217 except AttributeError:
218 classifications = []
219
220 for classification in classifications:
221 references.append(classification.text)
222
223 # Build description
224 description = u''
225 for report_section in issue_data.findall('./ReportSection'):
226 description += u'{} \n'.format(report_section.Name.text)
227 description += u'{} \n'.format(report_section.SectionText.text)
228 description += u'{} \n'.format(issue_data.get('id'))
229
230 h = html2text.HTML2Text()
231 description = h.handle(description)
232
233 for repro_step in issue_data.findall('./ReproSteps'):
234 step = repro_step.ReproStep
235 if step is not None:
236 try:
237 params = step.PostParams.text
238 except AttributeError:
239 pass
240
241 if not hostname:
242 # This seems to be a mobile app
243 hostname = session.URL.text
244
245 if not port:
246 service_data['name'] = step.Url.text
247 service_data['port'] = step.sourceline
248
249 self.sast_vulns.append({
250 "host": hostname,
251 "severity": severity,
252 "service": service_data,
253 "name": name,
254 "description": description,
255 "external_id": external_id,
256 "references": references,
257 "method": method,
258 "query": query,
259 "response": response,
260 "request": request,
261 "path": path,
262 "params": params,
263 "status_code": status_code,
264 "website": session.URL.text
265 })
266
267 def _extract_vulns(self):
268 # make list of false positives
269 try:
270 issue_list = self.audit.IssueList.iterchildren()
271 except AttributeError:
272 issue_list = []
273
274 for issue in issue_list:
275 if issue.get('suppressed', 'false').lower() == 'true':
276 self.suppressed.append(issue.get('instanceId'))
277
278 if self.fvdl is not None:
279 self._process_fvdl()
280
281 if self.webinspect is not None:
282 self._process_webinspect()
283
284 def calculate_severity(self, vuln):
285
286 severity = None # ["critical", "high", "medium", "low", "informational", "unclassified"]
287 rulepath = objectify.ObjectPath("FVDL.EngineData.RuleInfo.Rule")
288 impact = None
289 probability = None
290 accuracy = None
291
292 # XML path /FVDL/EngineData/RuleInfo/Rule (many)/MetaInfo/Group (many) the attribute "name"
293 # are keys for vuln properties
294
295 for rule in rulepath(self.fvdl):
296 if rule.get('id') == vuln.ClassInfo.ClassID:
297 for group in rule.MetaInfo.iterchildren():
298 if group.get('name') == "Probability":
299 probability = group
300 if group.get('name') == "Impact":
301 impact = group
302 if group.get('name') == "Accuracy":
303 accuracy = group
304
305 likelihood = (accuracy * vuln.InstanceInfo.Confidence * probability) / 25
306
307 if impact and probability:
308
309 if impact >= 2.5 and likelihood >= 2.5:
310 severity = 'critical'
311 elif impact >= 2.5 > likelihood:
312 severity = 'high'
313 elif impact < 2.5 <= likelihood:
314 severity = 'medium'
315 elif impact < 2.5 and likelihood < 2.5:
316 severity = 'low'
317 else:
318 print("missing severity")
319
320 # print("{}:{}:{}".format(vuln.InstanceInfo.InstanceID, vuln.InstanceInfo.InstanceSeverity, severity))
321 return severity
322
323 def concat_vuln_name(self, vuln):
324 return "{} {} {}:{}".format(vuln.ClassInfo.Type, vuln.ClassInfo.Subtype,
325 self.vulns[vuln.InstanceInfo.InstanceID]['filename'],
326 self.vulns[vuln.InstanceInfo.InstanceID]['line'])
327
328 def _prepare_description_templates(self):
329 if self.fvdl is None:
330 return
331 for description in self.fvdl.Description:
332
333 self.descriptions[description.get("classID")] = {}
334
335 if description.get('classID') not in self.vuln_classes:
336 continue
337
338 tips = ""
339 if hasattr(description, 'Tips'):
340 for tip in description.Tips.getchildren():
341 tips += "\n" + tip.text
342
343 htmlparser = HTMLParser()
344 self.descriptions[description.get("classID")]['text'] = htmlparser.unescape(
345 "Summary:\n{}\n\nExplanation:\n{}\n\nRecommendations:\n{}\n\nTips:{}".format(
346 description.Abstract, description.Explanation, description.Recommendations, tips))
347
348 # group vuln references
349 references = []
350 try:
351 children = description.References.getchildren()
352 except AttributeError:
353 children = []
354
355 for reference in children:
356
357 for attr in dir(reference):
358 if attr == '__class__':
359 break
360
361 references.append("{}: {}\n".format(attr, getattr(reference, attr)))
362
363 self.descriptions[description.get("classID")]['references'] = references
364
365 def format_description(self, vulnID):
366
367 text = self.descriptions[self.vulns[vulnID]['class']]['text']
368 replacements = self.vulns[vulnID]['replacements']
369 if not replacements:
370 return text
371
372 # special chars that must shown as-is, have the hmtlentity value duplicated
373 text = self.remove_extra_chars.sub(r"&\1;", text)
374
375 for placeholder in self.replacements_holders.findall(text, re.MULTILINE):
376
377 torepl = '<Replace key="{}"/>'
378 match = self.replacements_idx2.search(placeholder)
379
380 replace_with = ""
381 if match:
382 idx = match.group(1)
383 if match.group(3):
384 idx = match.group(3)
385 _filekey = "{}.file".format(idx)
386 _linekey = "{}.line".format(idx)
387 text = text.replace(placeholder, "").replace(
388 torepl.format(_filekey), replacements[_filekey]).replace(
389 torepl.format(_linekey), replacements[_linekey])
390 continue
391
392 try:
393 replace_with = replacements[idx]
394 except KeyError:
395 # Nothing to replace, use empty string
396 text = text.replace(placeholder, "")
397
398 text = text.replace(placeholder, replace_with)
399
400 text += '{}\n Instance ID: {} \n'.format(text, vulnID)
401 h = html2text.HTML2Text()
402 return text
403
404
405 def createPlugin():
406 return FortifyPlugin()
407
408
409 if __name__ == "__main__":
410 import sys
411 import os
412 if len(sys.argv) == 2:
413 report_file = sys.argv[1]
414 if os.path.isfile(report_file):
415 plugin = createPlugin()
416 plugin.processReport(report_file)
417 print(plugin.get_json())
418 else:
419 print(f"Report not found: {report_file}")
420 else:
421 print(f"USAGE {sys.argv[0]} REPORT_FILE")
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 #!/usr/bin/python
1 """
2 Copyright (C) 2016 xtr4nge [_AT_] gmail.com
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
16 """
17
18 import sys
19 import getopt
20 import json
21 import requests
22
23 requests.packages.urllib3.disable_warnings() # DISABLE SSL CHECK WARNINGS
24
25 gVersion = "1.0"
26 server = "http://127.0.0.1:8000"
27 token = "e5dab9a69988dd65e578041416773149ea57a054"
28
29
30 def usage():
31 print("\nFruityWiFi API " + gVersion + " by @xtr4nge")
32
33 print("Usage: ./client <options>\n")
34 print("Options:")
35 print("-x <command>, --execute=<commnd> exec the command passed as parameter.")
36 print("-t <token>, --token=<token> authentication token.")
37 print("-s <server>, --server=<server> FruityWiFi server [http{s}://ip:port].")
38 print("-h Print this help message.")
39 print("")
40 print("FruityWiFi: http://www.fruitywifi.com")
41 print("")
42
43
44 def parseOptions(argv):
45
46 v_execute = "/log/dhcp"
47 v_token = token
48 v_server = server
49
50 try:
51 opts, args = getopt.getopt(argv, "hx:t:s:",
52 ["help","execute=","token=","server="])
53
54 for opt, arg in opts:
55 if opt in ("-h", "--help"):
56 usage()
57 sys.exit()
58 elif opt in ("-x", "--execute"):
59 v_execute = arg
60 elif opt in ("-t", "--token"):
61 v_token = arg
62 elif opt in ("-s", "--server"):
63 v_server = arg
64
65 return (v_execute, v_token, v_server)
66
67 except getopt.GetoptError:
68 usage()
69 sys.exit(2)
70
71
72 (execute, token, server) = parseOptions(sys.argv[1:])
73
74
75 class Webclient:
76
77 def __init__(self, server, token):
78
79 self.global_webserver = server
80 self.path = "/modules/api/includes/ws_action.php"
81 self.s = requests.session()
82 self.token = token
83
84 def login(self):
85
86 payload = {
87 'action': 'login',
88 'token': self.token
89 }
90
91 self.s = requests.session()
92 self.s.get(self.global_webserver, verify=False) # DISABLE SSL CHECK
93 self.s.post(self.global_webserver + '/login.php', data=payload)
94
95 def loginCheck(self):
96
97 response = self.s.get(self.global_webserver + '/login_check.php')
98
99 if response.text != "":
100 self.login()
101
102 if response.text != "":
103 print(json.dumps("[FruityWiFi]: Ah, Ah, Ah! You didn't say the magic word! (check API token and server)"))
104 sys.exit()
105
106 return True
107
108 def submitPost(self, data):
109 response = self.s.post(self.global_webserver + data)
110 return response.json
111
112 def submitGet(self, data):
113 response = self.s.get(self.global_webserver + self.path + "?" + data)
114
115 return response
116
117 try:
118 w = Webclient(server, token)
119 w.login()
120 w.loginCheck()
121 except Exception as e:
122 print(json.dumps("[FruityWiFi]: There is something wrong (%s)" % e))
123 sys.exit(1)
124
125 _exec = "/log/dhcp"
126 _exec = execute
127 if _exec != "":
128 try:
129 out = w.submitGet("api=" + str(_exec))
130 json_output = out.json()
131 except Exception as e:
132 print(json.dumps("[FruityWiFi]: There is something wrong (%s)" % e))
133 sys.exit(1)
134
135 output = []
136 if _exec == "/log/dhcp":
137 for item in json_output:
138 if item.strip() != "":
139 output = [item.split(" ")]
140 else:
141 output = json_output
142
143 if len(output) > 0:
144 print(json.dumps(output))
145 else:
146 print(json.dumps("No clients connected"))
147
148
149 # 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 from faraday_plugins.plugins.plugin import PluginBase
7 import re
8 import json
9 import traceback
10
11 __author__ = "xtr4nge"
12 __copyright__ = "Copyright (c) 2016, FruityWiFi"
13 __credits__ = ["xtr4nge"]
14 __license__ = ""
15 __version__ = "1.0.0"
16 __maintainer__ = "xtr4nge"
17 __email__ = "@xtr4nge"
18 __status__ = "Development"
19
20 class FruityWiFiPlugin(PluginBase):
21 """
22 This plugin handles FruityWiFi clients.
23 """
24
25 def __init__(self):
26 super().__init__()
27 self.id = "fruitywifi"
28 self.name = "FruityWiFi"
29 self.plugin_version = "0.0.1"
30 self.version = "2.4"
31 self.description = "http://www.fruitywifi.com"
32 self.options = None
33 self._current_output = None
34 self.target = None
35
36 self._command_regex = re.compile(
37 r'^(fruitywifi).*?')
38
39 self.addSetting("Token", str, "e5dab9a69988dd65e578041416773149ea57a054")
40 self.addSetting("Server", str, "http://127.0.0.1:8000")
41 self.addSetting("Severity", str, "high")
42
43 def getSeverity(self, severity):
44 if severity.lower() == "critical" or severity == "4":
45 return 4
46 elif severity.lower() == "high" or severity == "3":
47 return 3
48 elif severity.lower() == "med" or severity == "2":
49 return 2
50 elif severity.lower() == "low" or severity == "1":
51 return 1
52 elif severity.lower() == "info" or severity == "0":
53 return 0
54 else:
55 return 5
56
57 def createHostInterfaceVuln(self, ip_address, macaddress, hostname, desc, vuln_name, severity):
58 h_id = self.createAndAddHost(ip_address)
59 if self._isIPV4(ip_address):
60 i_id = self.createAndAddInterface(
61 h_id,
62 ip_address,
63 macaddress,
64 ipv4_address=ip_address,
65 hostname_resolution=[hostname]
66 )
67 else:
68 self.createAndAddInterface(
69 h_id, ip_address, ipv6_address=ip_address, hostname_resolution=[hostname])
70
71 self.createAndAddVulnToHost(
72 h_id,
73 vuln_name,
74 desc=desc,
75 ref=["http://www.fruitywifi.com/"],
76 severity=severity
77 )
78
79 def parseOutputString(self, output, debug=False):
80
81 try:
82 output = json.loads(output)
83
84 if len(output) > 0:
85
86 if len(output[0]) == 3:
87
88 severity = self.getSeverity(self.getSetting("Severity"))
89
90 for item in output:
91 ip_address = item[0]
92 macaddress = item[1]
93 hostname = item[2]
94 vuln_name = "FruityWiFi"
95 severity = severity
96
97 desc = "Client ip: " + ip_address + \
98 " has been connected to FruityWiFi\n"
99 desc += "More information:"
100 desc += "\nname: " + hostname
101
102 self.createHostInterfaceVuln(ip_address, macaddress, hostname, desc, vuln_name, severity)
103
104 elif len(output[0]) == 5:
105 for item in output:
106 ip_address = item[0]
107 macaddress = item[1]
108 hostname = item[2]
109 vuln_name = item[3]
110 severity = item[4]
111
112 desc = "Client ip: " + ip_address + \
113 " has been connected to FruityWiFi\n"
114 desc += "More information:"
115 desc += "\nname: " + hostname
116
117 self.createHostInterfaceVuln(ip_address, macaddress, hostname, desc, vuln_name, severity)
118
119 except:
120 traceback.print_exc()
121
122 return True
123
124 def _isIPV4(self, ip):
125 if len(ip.split(".")) == 4:
126 return True
127 else:
128 return False
129
130 def processCommandString(self, username, current_path, command_string, debug=False):
131 """
132 """
133 #params = command_string.replace("fruitywifi","")
134 params = "-t %s -s %s" % (self.getSetting("Token"), self.getSetting("Server"))
135
136 return "python " + os.path.dirname(__file__) + "/fruitywifi.py " + params
137 #return None
138
139 def createPlugin():
140 return FruityWiFiPlugin()
141
142 if __name__ == "__main__":
143 import sys
144 import os
145 if len(sys.argv) == 2:
146 report_file = sys.argv[1]
147 if os.path.isfile(report_file):
148 plugin = createPlugin()
149 plugin.processReport(report_file)
150 print(plugin.get_json())
151 else:
152 print(f"Report not found: {report_file}")
153 else:
154 print(f"USAGE {sys.argv[0]} REPORT_FILE")
155
156 # 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 # 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 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())
12
13 __author__ = "Javier Victor Mariano Bruno"
14 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
15 __credits__ = ["Javier Victor Mariano Bruno"]
16 __license__ = ""
17 __version__ = "1.0.0"
18 __maintainer__ = "Javier Victor Mariano Bruno"
19 __email__ = "[email protected]"
20 __status__ = "Development"
21
22
23 class CmdFtpPlugin(PluginBase):
24 """
25 This plugin handles ftp command.
26 Basically detects if user was able to connect to a device
27 """
28
29 def __init__(self):
30 super().__init__()
31 self.id = "ftp"
32 self.name = "Ftp"
33 self.plugin_version = "0.0.1"
34 self.version = "0.17"
35 self.framework_version = "1.0.0"
36 self.options = None
37 self._current_output = None
38 self._command_regex = re.compile(r'^ftp.*?')
39 self._host_ip = None
40 self._port = "21"
41 self._info = 0
42 self._version = None
43
44 global current_path
45
46 def resolve(self, host):
47 try:
48 return socket.gethostbyname(host)
49 except:
50 pass
51 return host
52
53 def parseOutputString(self, output, debug=False):
54
55 host_info = re.search(r"Connected to (.+)\.", output)
56 banner = re.search("220?([\w\W]+)$", output)
57 if re.search("Connection timed out", output) is None and host_info is not None:
58 hostname = host_info.group(1)
59 ip_address = self.resolve(hostname)
60 self._version = banner.groups(0) if banner else ""
61 if debug:
62 print(ip_address)
63
64 h_id = self.createAndAddHost(ip_address)
65
66 i_id = self.createAndAddInterface(
67 h_id,
68 ip_address,
69 ipv4_address=ip_address,
70 hostname_resolution=[hostname])
71
72 s_id = self.createAndAddServiceToInterface(
73 h_id,
74 i_id,
75 "ftp",
76 "tcp",
77 ports=[self._port],
78 status="open")
79
80 if debug is True:
81 self.logger.info("Debug is active")
82
83 return True
84
85 def processCommandString(self, username, current_path, command_string):
86 """
87 """
88 count_args = command_string.split()
89
90 c = count_args.__len__()
91 self._port = "21"
92 if re.search("[\d]+", count_args[c - 1]):
93 self._port = count_args[c - 1]
94
95
96 def createPlugin():
97 return CmdFtpPlugin()
98
99 if __name__ == "__main__":
100 import sys
101 import os
102 if len(sys.argv) == 2:
103 report_file = sys.argv[1]
104 if os.path.isfile(report_file):
105 plugin = createPlugin()
106 plugin.processReport(report_file)
107 print(plugin.get_json())
108 else:
109 print(f"Report not found: {report_file}")
110 else:
111 print(f"USAGE {sys.argv[0]} REPORT_FILE")
112 # 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 # 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 from faraday_plugins.plugins.plugin import PluginBase
6 import socket
7 import re
8 import os
9
10 current_path = os.path.abspath(os.getcwd())
11
12 __author__ = "Francisco Amato"
13 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
14 __credits__ = ["Francisco Amato"]
15 __license__ = ""
16 __version__ = "1.0.0"
17 __maintainer__ = "Francisco Amato"
18 __email__ = "[email protected]"
19 __status__ = "Development"
20
21
22 class GoohostParser:
23 """
24 The objective of this class is to parse an xml file generated by the goohost tool.
25
26 TODO: Handle errors.
27 TODO: Test goohost output version. Handle what happens if the parser doesn't support it.
28 TODO: Test cases.
29
30 @param goohost_scantype You could select scan type ip, mail or host
31 """
32
33 def __init__(self, output, goohost_scantype):
34
35 self.items = []
36 lines = list(filter(None, output.split('\n')))
37 for line in lines:
38 if goohost_scantype == 'ip':
39 data = line.split()
40 item = {'host': data[0], 'ip': data[1]}
41 self.add_host_info_to_items(item['ip'], item['host'])
42 elif goohost_scantype == 'host':
43 data = line.strip()
44 item = {'host': data, 'ip': self.resolve(data)}
45 self.add_host_info_to_items(item['ip'], item['host'])
46 else:
47 item = {'data': line}
48
49 def resolve(self, host):
50 try:
51 return socket.gethostbyname(host)
52 except:
53 pass
54 return host
55
56 def add_host_info_to_items(self, ip_address, hostname):
57 data = {}
58 exists = False
59 for item in self.items:
60 if ip_address in item['ip']:
61 item['hosts'].append(hostname)
62 exists = True
63
64 if not exists:
65 data['ip'] = ip_address
66 data['hosts'] = [hostname]
67 self.items.append(data)
68
69
70 class GoohostPlugin(PluginBase):
71 """
72 Example plugin to parse goohost output.
73 """
74
75 def __init__(self):
76 super().__init__()
77 self.id = "Goohost"
78 self.name = "Goohost XML Output Plugin"
79 self.plugin_version = "0.0.1"
80 self.version = "v.0.0.1"
81 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).*?')
86 self.host = None
87
88 global current_path
89 self.output_path = None
90 self._command_string = None
91
92 def parseOutputString(self, output, debug=False):
93 """
94 This method will check if the import was made through the console or by importing a Goohost report.
95
96 Import from Console:The method will take the path of the report generated by Goohost from the output the shell sends and will read
97 the information from the txt where it expects it to be present.
98
99 Import from Report: The method receives the output of the txt report as parameter.
100
101 self.scantype defines the method used to generate the Goohost report
102
103 NOTE: if 'debug' is true then it is being run from a test case and the
104 output being sent is valid.
105 """
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
126 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
134
135 def define_scantype_by_command(self, command):
136 method_regex = re.compile(r'-m (mail|host|ip)')
137 method = method_regex.search(command)
138 if method:
139 return method.group(1)
140
141 return 'host'
142
143 def define_scantype_by_output(self, output):
144 lines = output.split('\n')
145 line = lines[0].split(' ')
146
147 if len(line) == 1:
148 return 'host'
149 elif len(line) == 2:
150 return 'ip'
151
152 def read_output_file(self, report_path):
153 mypath = re.search("Results saved in file (\S+)", report_path)
154 if not mypath:
155 return False
156 else:
157 self.output_path = self._current_path + "/" + mypath.group(1)
158 if not os.path.exists(self.output_path):
159 return False
160 with open(self.output_path, 'r') as report:
161 output = report.read()
162
163 return output
164
165
166 def createPlugin():
167 return GoohostPlugin()
168
169 if __name__ == "__main__":
170 import sys
171 import os
172 if len(sys.argv) == 2:
173 report_file = sys.argv[1]
174 if os.path.isfile(report_file):
175 plugin = createPlugin()
176 plugin.processReport(report_file)
177 print(plugin.get_json())
178 else:
179 print(f"Report not found: {report_file}")
180 else:
181 print(f"USAGE {sys.argv[0]} REPORT_FILE")
182 # 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 # I'm Py3
0 """
1 Faraday Penetration Test IDE
2 Copyright (C) 2017 Infobyte LLC (http://www.infobytesec.com/)
3 See the file 'doc/LICENSE' for the license information
4 """
5 from faraday_plugins.plugins.plugin import PluginBase
6 import re
7
8 __author__ = "Roberto Focke"
9 __copyright__ = "Copyright (c) 2017, Infobyte LLC"
10 __license__ = ""
11 __version__ = "1.0.0"
12
13
14 class hping3(PluginBase):
15
16 def __init__(self):
17 super().__init__()
18 self.id = "Hping3"
19 self.name = "hping3"
20 self.plugin_version = "0.0.1"
21 self.version = "1.0.0"
22 self.srv = {'21': ' ftp', '80': 'http', '143': 'imap', '1433': 'mssql',
23 '3306': 'mysql', '524': 'ncp', '119': 'nntp',
24 '5631': 'pcanywhere', '110': 'pop3', '5432': 'postgres',
25 '512': 'rexec', '513': 'rlogin', '514': 'rsh',
26 '25': 'smtp', '161': 'snmp', '22': 'ssh', '3690': 'svn',
27 '23': 'telnet', '5900': 'vnc'}
28
29 self._command_regex = re.compile(r'^(sudo hping3|hping3)\s+.*$')
30
31 def parseOutputString(self, output, debug=False):
32
33 regex_ipv4 = re.search(r"(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\)\:", output)
34 if regex_ipv4:
35 ip_address = regex_ipv4.group(0).rstrip("):") # Regex pls
36 else:
37 # Exit plugin, ip address not found. bad output
38 self.log("Abort plugin: Ip address not found", "INFO")
39 return
40
41 hostname = output.split(" ")[1]
42 host_id = self.createAndAddHost(hostname)
43
44 i_id = self.createAndAddInterface(
45 host_id, ip_address, ipv4_address=ip_address, hostname_resolution=[hostname])
46
47 if re.match("HPING", output):
48
49 sport = re.search(r"sport=(\d{1,6})", output)
50 ssport = [sport.group(1)]
51 reci = re.search(r"flags=(\w{2,3})", output)
52 service = self.srv[sport.group(1)]
53
54 if reci.group(1) == "SA":
55 s_id = self.createAndAddServiceToInterface(
56 host_id, i_id, service, protocol="tcp", ports=ssport, status="open")
57
58 lineas = output.split("\n")
59
60 for linea in lineas:
61 if (re.match(" ", linea)):
62
63 list = re.findall("\w+", linea)
64 service = list[1]
65 port = [list[0]]
66
67 if list[2] == "S" and list[3] == "A":
68 s_id = self.createAndAddServiceToInterface(
69 host_id, i_id, service, protocol="tcp", ports=port, status="open")
70
71 def processCommandString(self, username, current_path, command_string):
72 return None
73
74
75 def createPlugin():
76 return hping3()
77
78 if __name__ == "__main__":
79 import sys
80 import os
81 if len(sys.argv) == 2:
82 report_file = sys.argv[1]
83 if os.path.isfile(report_file):
84 plugin = createPlugin()
85 plugin.processReport(report_file)
86 print(plugin.get_json())
87 else:
88 print(f"Report not found: {report_file}")
89 else:
90 print(f"USAGE {sys.argv[0]} REPORT_FILE")
91 # 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 # 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 from faraday_plugins.plugins.plugin import PluginBase
6 import re
7 import os
8 import random
9
10 current_path = os.path.abspath(os.getcwd())
11
12 __author__ = "Francisco Amato"
13 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
14 __credits__ = ["Francisco Amato"]
15 __license__ = ""
16 __version__ = "1.0.0"
17 __maintainer__ = "Francisco Amato"
18 __email__ = "[email protected]"
19 __status__ = "Development"
20
21
22 class HydraParser:
23 """
24 The objective of this class is to parse an xml file generated by the hydra tool.
25
26 @param hydra_filepath A proper simple report generated by hydra
27 """
28
29 def __init__(self, xml_output):
30 lines = xml_output.splitlines()
31 self.items = []
32 for l in lines:
33
34 reg = re.search(
35 "\[([^$]+)\]\[([^$]+)\] host: ([^$]+) login: ([^$]+) password: ([^$]+)",
36 l)
37
38 if reg:
39
40 item = {
41 'port': reg.group(1),
42 'plugin': reg.group(2),
43 'ip': reg.group(3),
44 'login': reg.group(4),
45 'password': reg.group(5)}
46
47 self.items.append(item)
48
49
50 class HydraPlugin(PluginBase):
51 """
52 Example plugin to parse hydra output.
53 """
54
55 def __init__(self):
56 super().__init__()
57 self.id = "Hydra"
58 self.name = "Hydra XML Output Plugin"
59 self.plugin_version = "0.0.1"
60 self.version = "7.5"
61 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).*?')
66 self.host = None
67
68
69 def parseOutputString(self, output, debug=False):
70 """
71 This method will discard the output the shell sends, it will read it from
72 the xml where it expects it to be present.
73
74 NOTE: if 'debug' is true then it is being run from a test case and the
75 output being sent is valid.
76 """
77
78 parser = HydraParser(output)
79
80 i = 0
81 hosts = {}
82 service = ''
83 port = ''
84
85 for item in parser.items:
86
87 service = item['plugin']
88 port = item['port']
89
90 if item['ip'] not in hosts:
91 hosts[item['ip']] = []
92
93 hosts[item['ip']].append([item['login'], item['password']])
94
95 for k, v in hosts.items():
96
97 h_id = self.createAndAddHost(k)
98
99 if self._isIPV4(k):
100
101 i_id = self.createAndAddInterface(
102 h_id,
103 k,
104 ipv4_address=k)
105
106 else:
107 i_id = self.createAndAddInterface(
108 h_id,
109 k,
110 ipv6_address=k)
111
112 s_id = self.createAndAddServiceToInterface(
113 h_id,
114 i_id,
115 service,
116 ports=[port],
117 protocol="tcp",
118 status="open")
119
120 for cred in v:
121 self.createAndAddCredToService(
122 h_id,
123 s_id,
124 cred[0],
125 cred[1])
126
127 self.createAndAddVulnToService(
128 h_id,
129 s_id,
130 "Weak Credentials",
131 "[hydra found the following credentials]\nuser:%s\npass:%s" % (cred[0], cred[1]),
132 severity="high")
133
134 del parser
135
136 xml_arg_re = re.compile(r"^.*(-o\s*[^\s]+).*$")
137
138 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
144 arg_match = self.xml_arg_re.match(command_string)
145
146 if arg_match is None:
147 return re.sub(r"(^.*?hydra?)", r"\1 -o %s" % self._output_file_path, command_string)
148 else:
149 return re.sub(
150 arg_match.group(1),
151 r"-o %s" % self._output_file_path,
152 command_string)
153
154 def _isIPV4(self, ip):
155 if len(ip.split(".")) == 4:
156 return True
157 else:
158 return False
159
160 def setHost(self):
161 pass
162
163
164 def createPlugin():
165 return HydraPlugin()
166
167 if __name__ == "__main__":
168 import sys
169 import os
170 if len(sys.argv) == 2:
171 report_file = sys.argv[1]
172 if os.path.isfile(report_file):
173 plugin = createPlugin()
174 plugin.processReport(report_file)
175 print(plugin.get_json())
176 else:
177 print(f"Report not found: {report_file}")
178 else:
179 print(f"USAGE {sys.argv[0]} REPORT_FILE")
180
181 # 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 # 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 import re
7 import os
8
9 from faraday_plugins.plugins.plugin import PluginXMLFormat
10 try:
11 import xml.etree.cElementTree as ET
12 import xml.etree.ElementTree as ET_ORIG
13 ETREE_VERSION = ET_ORIG.VERSION
14 except ImportError:
15 import xml.etree.ElementTree as ET
16 ETREE_VERSION = ET.VERSION
17
18 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
19
20 current_path = os.path.abspath(os.getcwd())
21
22 __author__ = "Francisco Amato"
23 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
24 __credits__ = ["Francisco Amato"]
25 __license__ = ""
26 __version__ = "1.0.0"
27 __maintainer__ = "Francisco Amato"
28 __email__ = "[email protected]"
29 __status__ = "Development"
30
31
32 class ImpactXmlParser:
33 """
34 The objective of this class is to parse an xml file generated by the impact tool.
35
36 TODO: Handle errors.
37 TODO: Test impact output version. Handle what happens if the parser doesn't support it.
38 TODO: Test cases.
39
40 @param impact_xml_filepath A proper xml generated by impact
41 """
42
43 def __init__(self, xml_output):
44 tree = self.parse_xml(xml_output)
45 if tree:
46 self.items = [data for data in self.get_items(tree)]
47 else:
48 self.items = []
49
50 def parse_xml(self, xml_output):
51 """
52 Open and parse an xml file.
53
54 TODO: Write custom parser to just read the nodes that we need instead of
55 reading the whole file.
56
57 @return xml_tree An xml tree instance. None if error.
58 """
59 try:
60 tree = ET.fromstring(xml_output)
61 except SyntaxError as err:
62 #logger.error("SyntaxError: %s. %s" % (err, xml_output))
63 return None
64
65 return tree
66
67 def get_items(self, tree):
68 """
69 @return items A list of Host instances
70 """
71 for node in tree.findall("entity/[@class='host']"):
72 yield Item(node, tree)
73
74
75 class Item:
76 """
77 An abstract representation of a Item
78
79
80 @param item_node A item_node taken from an impact xml tree
81 """
82
83 def __init__(self, item_node, parent=None):
84 self.node = item_node
85
86 self.arch = self.get_text_from_subnode("property/[@key='arch']")
87
88 self.host = self.get_text_from_subnode(
89 "property/[@key='display_name']")
90
91 self.ip = self.get_text_from_subnode("property/[@key='ip']")
92
93 self.os = self.get_text_from_subnode(
94 "property/[@key='os']/property/[@key='entity name']")
95
96 self.ports = []
97 self.services = []
98 self.process_ports(item_node)
99 self.process_services(item_node)
100
101 self.agent = False
102
103 for node in parent.findall("entity/[@class='agent']"):
104
105 self.node = node
106 agentip = node.get('name').split("/")[1]
107
108 if self.ip == agentip:
109
110 self.agentip = agentip
111
112 self.ipfrom = self.get_text_from_subnode(
113 "property/[@key='Connection Properties']/property/[@key='ip']") or agentip
114
115 self.agentype = node.get("type")
116
117 self.agentport = self.get_text_from_subnode(
118 "property/[@key='Connection Properties']//property/[@key='port']") or ""
119
120 self.agentsubtype = self.get_text_from_subnode(
121 "property/[@key='Connection Properties']//property/[@key='subtype']") or ""
122
123 self.agentcon = self.get_text_from_subnode(
124 "property/[@key='Connection Properties']//property/[@key='type']") or ""
125
126 self.agent = True
127 break
128
129 self.results = self.getResults(item_node)
130
131 def process_ports(self, item_node):
132 for p in item_node.findall("property/[@key='tcp_ports']/property/[@type='port']"):
133 self.ports.append({'port': p.get('key'),
134 'protocol': "tcp",
135 'status': "open" if p.text == "listen" else p.text})
136
137 for p in item_node.findall("property/[@key='udp_ports']/property/[@type='port']"):
138 self.ports.append({'port': p.get('key'),
139 'protocol': "udp",
140 'status': "open" if p.text == "listen" else p.text})
141
142 def process_services(self, item_node):
143 for service in item_node.findall("property/[@key='services']/property"):
144 service_name = service.get("key")
145 port, protocol = service.findall('property')[0].get('key').split('-')
146 self.services.append({
147 "name": service_name,
148 "protocol": protocol,
149 "port": port
150 })
151
152 def getResults(self, tree):
153 """
154 :param tree:
155 """
156 for self.issues in tree.findall("property/[@key='Vulnerabilities']/property/[@type='container']"):
157 yield Results(self.issues)
158 # 2017R1 compatibility
159 for self.issues in tree.findall("property/[@key='exposures']/property/[@type='container']"):
160 yield Results(self.issues)
161
162 def get_text_from_subnode(self, subnode_xpath_expr):
163 """
164 Finds a subnode in the host node and the retrieves a value from it.
165
166 @return An attribute value
167 """
168 sub_node = self.node.find(subnode_xpath_expr)
169 if sub_node is not None:
170 return sub_node.text
171
172 return None
173
174
175 class Results():
176
177 def __init__(self, issue_node):
178 self.node = issue_node
179 self.ref = [issue_node.get("key")]
180 self.severity = ""
181 self.port = "Unknown"
182 self.service_name = "n/a"
183 self.protocol = "tcp?"
184 vuln = issue_node.find("property/property")
185 if not vuln:
186 # 2017R1 compatibility
187 self.ref = []
188 vuln = issue_node.find("property")
189 self.name = self.get_text_from_subnode("property/[@key='title']")
190 self.desc = self.get_text_from_subnode("property/[@key='description']")
191 self.severity = self.get_text_from_subnode("property/[@key='severity']")
192 self.service_name = self.get_text_from_subnode("property/[@key='service']")
193 else:
194 # 2013R3 xml version
195 self.name = vuln.get("key")
196 self.node = vuln
197 self.desc = self.get_text_from_subnode("property/[@key='description']")
198 self.port = self.get_text_from_subnode("property/[@key='port']")
199
200 def get_text_from_subnode(self, subnode_xpath_expr):
201 """
202 Finds a subnode in the host node and the retrieves a value from it.
203
204 @return An attribute value
205 """
206 sub_node = self.node.find(subnode_xpath_expr)
207 if sub_node is not None:
208 return sub_node.text
209
210 return None
211
212
213 class ImpactPlugin(PluginXMLFormat):
214 """
215 Example plugin to parse impact output.
216 """
217
218 def __init__(self):
219 super().__init__()
220 self.identifier_tag = "entities"
221 self.id = "CoreImpact"
222 self.name = "Core Impact XML Output Plugin"
223 self.plugin_version = "0.0.2"
224 self.version = "Core Impact 2013R1/2017R2"
225 self.framework_version = "1.0.0"
226 self.options = None
227 self._current_output = None
228 self._command_regex = re.compile(r'^(sudo impact|\.\/impact).*?')
229
230 def parseOutputString(self, output, debug=False):
231 parser = ImpactXmlParser(output)
232 mapped_services = {}
233 mapped_ports = {}
234 for item in parser.items:
235
236 h_id = self.createAndAddHost(
237 item.ip,
238 item.os + " " + item.arch)
239
240 i_id = self.createAndAddInterface(
241 h_id,
242 item.ip,
243 ipv4_address=item.ip,
244 hostname_resolution=[item.host])
245
246 for service in item.services:
247 s_id = self.createAndAddServiceToInterface(
248 h_id,
249 i_id,
250 service['name'],
251 service['protocol'],
252 ports=[service['port']],
253 status='open')
254 mapped_services[service['name']] = s_id
255 mapped_ports[service['port']] = s_id
256
257 if item.agent:
258 desc = "Agent Type: " + item.agentype
259 desc += "\nConn from:" + item.ipfrom
260 desc += "\nPort:" + item.agentport
261 desc += "\nProtocol:" + item.agentsubtype
262 desc += "\nConn:" + item.agentcon
263
264 self.createAndAddVulnToHost(
265 h_id,
266 "Core Impact Agent",
267 desc=desc,
268 severity="HIGH")
269
270 for v in item.results:
271 if v.service_name == "n/a" and v.port == "Unknown":
272 self.createAndAddVulnToHost(
273 h_id,
274 v.name,
275 desc=v.desc,
276 severity=v.severity,
277 ref=v.ref)
278 else:
279 s_id = mapped_services.get(v.service_name) or mapped_ports.get(v.port)
280 print(v.service_name)
281 print(s_id)
282 self.createAndAddVulnToService(
283 h_id,
284 s_id,
285 v.name,
286 desc=v.desc,
287 severity=v.severity,
288 ref=v.ref)
289
290 for p in item.ports:
291 s_id = self.createAndAddServiceToInterface(
292 h_id,
293 i_id,
294 p['port'],
295 p['protocol'],
296 ports=[p['port']],
297 status=p['status'])
298 del parser
299
300 def processCommandString(self, username, current_path, command_string):
301 return None
302
303 def setHost(self):
304 pass
305
306
307 def createPlugin():
308 return ImpactPlugin()
309
310 if __name__ == "__main__":
311 import sys
312 import os
313 if len(sys.argv) == 2:
314 report_file = sys.argv[1]
315 if os.path.isfile(report_file):
316 plugin = createPlugin()
317 plugin.processReport(report_file)
318 print(plugin.get_json())
319 else:
320 print(f"Report not found: {report_file}")
321 else:
322 print(f"USAGE {sys.argv[0]} REPORT_FILE")
323
324 # I'm Py3
0 """
1 Faraday Penetration Test IDE
2 Copyright (C) 2018 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) 2018 Infobyte LLC (http://www.infobytesec.com/)
3 See the file 'doc/LICENSE' for the license information
4 """
5 import csv
6 from io import StringIO
7 from faraday_plugins.plugins.plugin import PluginBase
8
9
10 def calculate_severity(number):
11 if number is None:
12 return "info"
13 number = float(number)
14 # Based in CVSS V2
15 if 0 <= number < 4.0:
16 return "low"
17 elif 4.0 <= number < 7.0:
18 return "med"
19 elif 7.0 <= number <= 10:
20 return "high"
21
22
23 class Ip360Parser:
24
25 def __init__(self, csv_content):
26 self.csv_content = StringIO(csv_content.decode('ascii', 'ignore'))
27 self.csv_reader = csv.DictReader(self.csv_content, delimiter=',', quotechar='"')
28
29 def parse(self):
30
31 result = []
32 for row in self.csv_reader:
33
34 host = {
35 "name": row.get("IP"),
36 "os": row.get("OS")
37 }
38
39 interface = {
40 "name": row.get("IP"),
41 "hostname_resolution": [row.get("NetBIOS Name")],
42 "network_segment": row.get("NetBIOS Domain"),
43 }
44
45 service = {"port": row.get("Port")}
46
47 vulnerability = {
48 "name": row.get("Vulnerability"),
49 "description": row.get("Description"),
50 "resolution": row.get("Remediation"),
51 "ref": [
52 row.get("CVE"),
53 "Vuln ID: " + row.get("Vulnerability ID"),
54 "Risk: " + row.get("Risk"),
55 "Skill: " + row.get("Skill"),
56 "CVSS V2: " + row.get("CVSS V2"),
57 "CVSS V3: " + row.get("CVSS V3")],
58 "severity": row.get("CVSS V2")
59 }
60
61 result.append((host, interface, service, vulnerability))
62
63 return result
64
65 class Ip360Plugin(PluginBase):
66 """
67 Example plugin to parse Ip360 output.
68 """
69
70 def __init__(self):
71 super().__init__()
72 self.id = "Ip360"
73 self.name = "Ip360 CSV Output Plugin"
74 self.plugin_version = "0.0.1"
75 self.options = None
76
77 def parseOutputString(self, output, debug=False):
78
79 parser = Ip360Parser(output)
80 for host, interface, service, vulnerability in parser.parse():
81
82 h_id = self.createAndAddHost(host.get("name"), host.get("os"))
83
84 i_id = self.createAndAddInterface(
85 h_id,
86 interface.get("name"),
87 ipv4_address=interface.get("name"),
88 hostname_resolution=interface.get("hostname_resolution"),
89 network_segment=interface.get("network_segment"))
90
91
92 if service.get("port") == "-":
93 port = "0"
94 protocol = "unknown"
95 else:
96 port = service.get("port").split("/")[0]
97 protocol = service.get("port").split("/")[1]
98
99 s_id = self.createAndAddServiceToInterface(
100 h_id,
101 i_id,
102 service.get("port"),
103 protocol=protocol,
104 ports=[port])
105
106 self.createAndAddVulnToService(
107 h_id,
108 s_id,
109 vulnerability.get("name"),
110 desc=vulnerability.get("description"),
111 resolution=vulnerability.get("resolution"),
112 severity=calculate_severity(vulnerability.get("severity")),
113 ref=vulnerability.get("ref"))
114
115 def createPlugin():
116 return Ip360Plugin()
117
118 if __name__ == "__main__":
119 import sys
120 import os
121 if len(sys.argv) == 2:
122 report_file = sys.argv[1]
123 if os.path.isfile(report_file):
124 plugin = createPlugin()
125 plugin.processReport(report_file)
126 print(plugin.get_json())
127 else:
128 print(f"Report not found: {report_file}")
129 else:
130 print(f"USAGE {sys.argv[0]} REPORT_FILE")
131 # 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 # 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 import os
7 from lxml import etree
8 from faraday_plugins.plugins.plugin import PluginBase
9
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
18
19 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
20
21 current_path = os.path.abspath(os.getcwd())
22
23 __author__ = "Thierry Beauquier"
24 __license__ = ""
25 __version__ = "1.0.0"
26 __maintainer__ = "Thierry Beauquier"
27 __email__ = "[email protected]"
28 __status__ = "Development"
29
30 """
31 This plugin has been designed to be used with python-unittest2/paramiko script to perform security compliancy verification. It enables to have displayed both security scans results (nmap,
32 nessus, ..) and security verification compliancy (CIS-CAT, compagny's product security requirement) by Faraday-IPE
33
34 This plugin requires that a element "host" is added to <testcase/> (sed -i 's/<testcase/<testcase host=\"192.168.1.1\"/' junit.xml)
35
36 <testsuite errors="0" failures="1" name="AccountsWithSuperuserPrivilegesShallBeDisabledByDefault-20170118090010" skipped="0" tests="1" time="0.144">
37 <testcase host="192.168.1.1" classname="AccountsWithSuperuserPrivilegesShallBeDisabledByDefault" name="test_sshdRootLogin" time="0.144">
38 <failure message="SSH for root account is not disabled: '' matches '' in ''" type="AssertionError">
39 <![CDATA[Traceback (most recent call last):
40 File "bsr-ci.py", line 514, in test_sshdRootLogin
41 self.assertNotRegexpMatches(_ssh('cat /etc/ssh/sshd_config | egrep "^PermitRootLogin" | awk \'{print $2}\' | egrep "no|No|NO"',host),'', 'SSH for root account is not disabled')
42 AssertionError: SSH for root account is not disabled: '' matches '' in ''
43 ]]> </failure>
44 </testcase>
45 <system-out>
46 <![CDATA[]]> </system-out>
47 <system-err>
48 <![CDATA[]]> </system-err>
49 </testsuite>
50
51
52 """
53
54
55 class JunitXmlParser:
56 """
57 The objective of this class is to parse an xml file generated by the junit.
58
59 @param junit_xml_filepath A proper xml generated by junit
60 """
61
62 def __init__(self, xml_output):
63
64 tree = self.parse_xml(xml_output)
65 if tree:
66 self.items = [data for data in self.get_items(tree)]
67 else:
68 self.items = []
69
70 def parse_xml(self, xml_output):
71 """
72 Open and parse an xml file.
73
74 @return xml_tree An xml tree instance. None if error.
75 """
76 try:
77 tree = etree.fromstring(xml_output)
78 except SyntaxError as err:
79 print("SyntaxError: %s. %s" % (err, xml_output))
80 return None
81 return tree
82
83 def get_items(self, tree):
84 """
85 @return items A list of Failure instances
86 """
87
88 for node in tree.findall('testsuite/testcase/failure'):
89 yield Testsuite(node)
90
91
92 class Testsuite:
93
94 def __init__(self, testsuite_node):
95 self.node = testsuite_node
96
97 self.parent = self.node.getparent()
98 self.name = self.parent.get('name')
99 self.host = self.parent.get('host')
100 if self.host is None:
101 print('host element is missing')
102 self.host = ''
103
104 self.message = self.get_text_from_subnode('message')
105
106 def get_text_from_subnode(self, subnode_xpath_expr):
107 """
108 Finds a subnode in the host node and the retrieves a value from it.
109
110 @return An attribute value
111 """
112 sub_node = self.node.get(subnode_xpath_expr)
113 if sub_node is not None:
114 return sub_node
115
116 return None
117
118
119 class JunitPlugin(PluginBase):
120 """
121 Example plugin to parse junit output.
122 """
123
124 def __init__(self):
125 super().__init__()
126 self.id = "Junit"
127 self.name = "Junit XML Output Plugin"
128 self.plugin_version = "0.0.1"
129 self.version = ""
130 self.framework_version = "1.0.0"
131 self.options = None
132 self._current_output = None
133 self._command_regex = None
134
135 def parseOutputString(self, output, debug=False):
136
137 parser = JunitXmlParser(output)
138 for item in parser.items:
139 h_id = self.createAndAddHost(item.host, os="Linux")
140 i_id = self.createAndAddInterface(h_id, item.host, ipv4_address=item.host)
141 self.createAndAddVulnToHost(h_id, name=item.name, desc=item.message, ref=[], severity="High")
142 del parser
143
144
145 def createPlugin():
146 return JunitPlugin()
147
148
149 if __name__ == "__main__":
150 import sys
151 import os
152 if len(sys.argv) == 2:
153 report_file = sys.argv[1]
154 if os.path.isfile(report_file):
155 plugin = createPlugin()
156 plugin.processReport(report_file)
157 print(plugin.get_json())
158 else:
159 print(f"Report not found: {report_file}")
160 else:
161 print(f"USAGE {sys.argv[0]} REPORT_FILE")
0 """
1 Faraday Penetration Test IDE
2 Copyright (C) 2017 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) 2017 Infobyte LLC (http://www.infobytesec.com/)
3 See the file 'doc/LICENSE' for the license information
4 """
5 import re
6 import os
7 from collections import defaultdict
8
9 from faraday_plugins.plugins.plugin import PluginByExtension
10 from faraday_plugins.plugins.plugins_utils import filter_services, get_all_protocols
11
12
13 current_path = os.path.abspath(os.getcwd())
14
15
16 class LynisLogDataExtracter():
17 def __init__(self, datfile=None, output=None):
18 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
25
26 def _svcHelper(self, ip, port, protocol, name):
27 self.services[ip].append({'port': port, 'protocol': protocol, 'name': name})
28
29 def hostname(self):
30 hostname_match = re.search('^hostname=(.+)$', self.rawcontents, re.MULTILINE)
31 hostname = hostname_match.group(1).strip()
32 domain_match = re.search('^domainname=(.+)$', self.rawcontents, re.MULTILINE)
33 if domain_match:
34 domain = domain_match.group(1).strip()
35 return ".".join([hostname,domain])
36 else:
37 return hostname
38
39 def osfullname(self):
40 name_match = re.search('^os_name=(.+)$', self.rawcontents, re.MULTILINE)
41 name = name_match.group(1).strip()
42 version_match = re.search('^os_version=(.+)$', self.rawcontents, re.MULTILINE)
43 version = version_match.group(1).strip()
44 return " ".join([name, version])
45
46 def ipv4(self):
47 ipv4addrs = []
48 ipv4s = re.findall('^network_ipv4_address\[\]=(.+)$',
49 self.rawcontents, re.MULTILINE)
50 ipv4addrs = self.ipv4_filter(ipv4s)
51 return(ipv4addrs)
52
53 def ipv6(self):
54 ipv6addrs = []
55 ipv6s = re.findall('^network_ipv6_address\[\]=(.+)$',
56 self.rawcontents, re.MULTILINE)
57 ipv6addrs = self.ipv6_filter(ipv6s)
58 return(ipv6addrs)
59
60 def ipv4_filter(self, ips):
61 ip_list = []
62 for ip in ips:
63 if not ip == "127.0.0.1":
64 ip_list.append(ip)
65
66 return ip_list
67
68 def ipv6_filter(self, ips):
69 ip_list = []
70 for ip in ips:
71 if not ip.startswith('fe80') and not ip.startswith('::1'):
72 ip_list.append(ip)
73
74 return ip_list
75
76 def kernelVersion(self):
77 versions_dict = {}
78
79 version = re.search('^os_kernel_version=(.+)$',
80 self.rawcontents, re.MULTILINE)
81 if version:
82 versions_dict['Kernel Version'] = version.group(1).strip()
83
84 version_full = re.search('^os_kernel_version_full=(.+)$',
85 self.rawcontents, re.MULTILINE)
86 if version_full:
87 versions_dict['Kernel Version Full'] = version_full.group(1).strip()
88
89 return versions_dict
90
91 def listeningservices(self):
92 line = re.findall('^network_listen_port\[\]=(.+)$',
93 self.rawcontents, re.MULTILINE)
94 # To avoid local services, we will create the following list
95 local_services = ['*', 'localhost']
96
97 for combo in line:
98 elements = self.clean_services(combo, local_services)
99 if elements is not None:
100 self._svcHelper(elements['ip'],
101 elements['port'],
102 elements['protocol'],
103 elements['name'])
104 return self.services
105
106 def clean_services(self, combo, local_services):
107 add = False
108 #if "localhost" in combo:
109 if combo.count("|") > 1:
110 # Service with url, protocol and perhaps name
111 items_service = combo.split('|')
112 if not self.local_service(items_service, local_services):
113 # self.aux_items will be an auxiliar list. We will use it...
114 # ...for poping the url and the protocol so that the last element...
115 # ... of the list, will be the name of the service
116 self.aux_items = list(filter(None, items_service))
117 elements_ip_port, count = self.get_ip_and_port(self.aux_items, remove_from_list=True)
118 protocol = self.get_protocol()
119 name = self.aux_items[0]
120 add = True
121
122 if name == '-':
123 details = self.search_service(elements_ip_port[1])
124 name = details['name']
125 elif combo.count('|') == 1:
126 # Service only with url
127 items_service = combo.split('|')
128 if not self.local_service(items_service, local_services):
129 elements_ip_port, count = self.get_ip_and_port(items_service)
130 details = self.search_service(elements_ip_port[1])
131 protocol = details['protocol']
132 name = details['name']
133 add = True
134 else:
135 items_service = combo
136 count = items_service.count(':')
137 elements_ip_port = items_service.split(':')
138 details = self.search_service(elements_ip_port[1])
139 protocol = details['protocol']
140 name = details['name']
141 add = True
142
143 if add:
144 ip, port = self.colon_count(count, elements_ip_port, items_service)
145 elements_dict = {
146 "ip":ip,
147 "port": port,
148 "protocol": protocol,
149 "name": name
150 }
151 return elements_dict
152 else:
153 return None
154
155 def local_service(self, service_data, local_services):
156 ip = self.get_ip_and_port(service_data)[0][0]
157 local = True
158 if ip not in local_services and not ip.startswith(':'):
159 local = False
160
161 return local
162
163 def get_ip_and_port(self, service_data, remove_from_list=False):
164 url_data = [url for url in service_data if ':' in url][0]
165 count = url_data.count(':')
166 ip_port = url_data.split(':')
167
168 if remove_from_list:
169 self.aux_items.remove(url_data)
170
171 return ip_port, count
172
173 def get_protocol(self):
174 # network_listen_port variables are different in .log and .dat reports
175 # .log: tcp4|127.0.0.1:5985|zabbix_age|
176 # .dat: 127.0.0.1:5985|tcp4|zabbix_age|
177 # This method will check if the protocol (from the function get_all_protocols())
178 # matches with the protocol that network_listen_port contains
179 protocols = get_all_protocols()
180 for item in protocols:
181 protocol = [p for p in self.aux_items if item in p.lower()]
182 if protocol:
183 self.aux_items.remove(protocol[0])
184 return protocol[0]
185
186 def search_service(self, port):
187 srv = filter_services()
188 details_dict = {
189 'name' : 'Unknown',
190 'protocol' : 'Unknown'
191 }
192 for item in srv:
193 service_tuple = item[0].split('/')
194 parsed_port = service_tuple[0]
195 if parsed_port == port:
196 details_dict['name'] = item[1]
197 details_dict['protocol'] = service_tuple[1]
198 return details_dict
199 return details_dict
200
201 def colon_count(self, count, elements_ip_port, items_service):
202 #Ipv4
203 if count == 1:
204 ip, port = elements_ip_port
205
206 #Ipv6
207 elif count == 3:
208 port = elements_ip_port[3]
209 ip = '::'
210
211 #Ipv6
212 elif count == 5:
213 port = elements_ip_port[5]
214 ip = items_service[0].replace(':{}'.format(port), '')
215
216 return ip, port
217
218 def parse_suggestions(self):
219 sugs = {}
220 m = re.findall('^suggestion\[\]=(.+)$', self.rawcontents, re.MULTILINE)
221 for combo in m:
222 x = combo.split('|')
223 sugs[x[0]] = x[1]
224 return(sugs)
225
226 def parse_warnings(self):
227 warns = {}
228 m = re.findall('^warning\[\]=(.+)$', self.rawcontents, re.MULTILINE)
229 for combo in m:
230 x = combo.split('|')
231 warns[x[0]] = x[1]
232 return(warns)
233
234
235 class LynisPlugin(PluginByExtension):
236 """ Simple example plugin to parse lynis' lynis-report.dat file."""
237
238 def __init__(self):
239 super().__init__()
240 self.extension = [".dat", ".log"]
241 self.id = "Lynis"
242 self.name = "Lynis DAT Output Plugin"
243 self.plugin_version = "0.4"
244 self.version = "2.7.1"
245 self.options = None
246 self._current_output = None
247 rr = r'^(lynis|sudo lynis|\.\/lynis|sudo \.\/lynis).*?'
248 self._command_regex = re.compile(rr)
249 self._hosts = []
250
251 global current_path
252
253 def report_belongs_to(self, **kwargs):
254 if super().report_belongs_to(**kwargs):
255 report_path = kwargs.get("report_path", "")
256 with open(report_path) as f:
257 output = f.read()
258 return output.startswith("# Lynis Report")
259 return False
260
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)
268 hostname = lde.hostname()
269 ipv4s = lde.ipv4()
270 ipv6s = lde.ipv6()
271 kernel_versions = lde.kernelVersion()
272 services = lde.listeningservices()
273 suggestions = lde.parse_suggestions()
274 warnings = lde.parse_warnings()
275
276 for ipv4 in ipv4s:
277 h_id = self.createAndAddHost(name=ipv4,
278 os=lde.osfullname(),
279 hostnames=[hostname])
280
281 self.create_services(h_id, services, ipv4)
282 self.create_vulns_with_kernel(h_id, kernel_versions)
283 self.create_vulns_with_suggestions(h_id, suggestions)
284 self.create_vulns_with_warns(h_id, warnings)
285
286 for ipv6 in ipv6s:
287 h_id = self.createAndAddHost(name=ipv6,
288 os=lde.osfullname(),
289 hostnames=[hostname])
290
291 self.create_services(h_id, services, ipv6)
292 self.create_vulns_with_kernel(h_id, kernel_versions)
293 self.create_vulns_with_suggestions(h_id, suggestions)
294 self.create_vulns_with_warns(h_id, warnings)
295
296 def create_services(self, host_id, parsed_services, ip_version):
297 for service_data in parsed_services[ip_version]:
298 self.createAndAddServiceToHost(host_id=host_id,
299 name=service_data['name'],
300 protocol=service_data['protocol'],
301 ports=[service_data['port']])
302
303 if '0.0.0.0' in parsed_services:
304 for service_data in parsed_services['0.0.0.0']:
305 self.createAndAddServiceToHost(host_id=host_id,
306 name=service_data['name'],
307 protocol=service_data['protocol'],
308 ports=[service_data['port']])
309
310 def create_vulns_with_kernel(self, host_id, kernel_versions):
311 for kernel, version in kernel_versions.items():
312 self.createAndAddVulnToHost(
313 host_id=host_id,
314 name=kernel,
315 severity='info',
316 desc=version
317 )
318
319 def create_vulns_with_suggestions(self, host_id, sugs):
320 for sug in sugs:
321 self.createAndAddVulnToHost(
322 host_id=host_id,
323 name=sug,
324 severity='med',
325 desc=sugs[sug]
326 )
327
328 def create_vulns_with_warns(self, host_id, warns):
329 for warn in warns:
330 self.createAndAddVulnToHost(
331 host_id=host_id,
332 name=warn,
333 severity='high',
334 desc=warns[warn]
335 )
336
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())
351
352
353 def createPlugin():
354 return LynisPlugin()
355
356 if __name__ == "__main__":
357 import sys
358 import os
359 if len(sys.argv) == 2:
360 report_file = sys.argv[1]
361 if os.path.isfile(report_file):
362 plugin = createPlugin()
363 plugin.processReport(report_file)
364 print(plugin.get_json())
365 else:
366 print(f"Report not found: {report_file}")
367 else:
368 print(f"USAGE {sys.argv[0]} REPORT_FILE")
369 # I'm Py3
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>
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) 2015 Infobyte LLC (http://www.infobytesec.com/)
3 See the file 'doc/LICENSE' for the license information
4 """
5 from faraday_plugins.plugins.plugin import PluginXMLFormat
6
7 import zipfile
8 import re
9 import os
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
18
19 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
20
21 current_path = os.path.abspath(os.getcwd())
22
23 __author__ = "Ezequiel Tavella"
24 __copyright__ = "Copyright (c) 2015, Infobyte LLC"
25 __credits__ = ["Ezequiel Tavella"]
26 __license__ = ""
27 __version__ = "1.0.1"
28 __maintainer__ = "Ezequiel Tavella"
29 __status__ = "Development"
30
31
32 def openMtgx(mtgx_file):
33
34 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 return None
41
42 file.close()
43 return xml
44
45
46 class Host():
47
48 def __init__(self):
49 self.ip = ""
50 self.node_id = ""
51 self.dns_name = ""
52 self.website = ""
53 self.netblock = ""
54 self.location = ""
55 self.mx_record = ""
56 self.ns_record = ""
57
58
59 class MaltegoMtgxParser():
60
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 = {}
75
76 def getRelations(self):
77 """
78 Get relations between nodes.
79 Two ways: Source-> Target
80 Source <- Target
81 """
82 for edge in self.edges:
83
84 source = edge.get("source")
85 target = edge.get("target")
86
87 if source not in self.relations:
88 self.relations.update({source: [target]})
89
90 if target not in self.relations:
91 self.relations.update({target: [source]})
92
93 values = self.relations[source]
94 values.append(target)
95 self.relations.update({source: values})
96
97 values = self.relations[target]
98 values.append(source)
99 self.relations.update({target: values})
100
101 def getIpAndId(self, node):
102
103 # Find node ID and maltego entity
104 node_id = node.get("id")
105 entity = node.find(
106 "{http://graphml.graphdrawing.org/xmlns}data/"
107 "{http://maltego.paterva.com/xml/mtgx}MaltegoEntity")
108
109 # Check if is IPv4Address
110 if entity.get("type") != "maltego.IPv4Address":
111 return None
112
113 # Get IP value
114 value = entity.find(
115 "{http://maltego.paterva.com/xml/mtgx}Properties/"
116 "{http://maltego.paterva.com/xml/mtgx}Property/"
117 "{http://maltego.paterva.com/xml/mtgx}Value")
118
119 return {"node_id": node_id, "ip": value.text}
120
121 def getNode(self, node_id):
122
123 # Get node, filter by id
124 for node in self.nodes:
125
126 if node.get("id") == node_id:
127 return node
128
129 def getType(self, node):
130
131 # Get type of this node
132 entity = node.find(
133 "{http://graphml.graphdrawing.org/xmlns}data/"
134 "{http://maltego.paterva.com/xml/mtgx}MaltegoEntity")
135
136 return entity.get("type")
137
138 def getWebsite(self, target_node):
139
140 # Parse Website Entity
141 result = {"name": "", "ssl_enabled": "", "urls": ""}
142
143 props = target_node.find(
144 "{http://graphml.graphdrawing.org/xmlns}data/"
145 "{http://maltego.paterva.com/xml/mtgx}MaltegoEntity/"
146 "{http://maltego.paterva.com/xml/mtgx}Properties")
147
148 for prop in props:
149
150 name_property = prop.get("name")
151 value = prop.find(
152 "{http://maltego.paterva.com/xml/mtgx}Value").text
153
154 if name_property == "fqdn":
155 result["name"] = value
156 elif name_property == "website.ssl-enabled":
157 result["ssl_enabled"] = value
158 elif name_property == "URLS":
159 result["urls"] = value
160
161 return result
162
163 def getNetBlock(self, target_node):
164
165 # Parse Netblock Entity
166 result = {"ipv4_range": "", "network_owner": "", "country": ""}
167
168 props = target_node.find(
169 "{http://graphml.graphdrawing.org/xmlns}data/"
170 "{http://maltego.paterva.com/xml/mtgx}MaltegoEntity/"
171 "{http://maltego.paterva.com/xml/mtgx}Properties")
172
173 for prop in props:
174
175 name_property = prop.get("name")
176 value = prop.find(
177 "{http://maltego.paterva.com/xml/mtgx}Value").text
178
179 if name_property == "ipv4-range":
180 result["ipv4_range"] = value
181 elif name_property == "description":
182 result["network_owner"] = value
183 elif name_property == "country":
184 result["country"] = value
185
186 return result
187
188 def getLocation(self, target_node):
189
190 # Parse Location Entity
191 result = {
192 "name": "",
193 "area": "",
194 "country_code": "",
195 "longitude": "",
196 "latitude": "",
197 "area_2": ""}
198
199 # Get relations with other nodes
200 node_relations = self.relations[target_node.get("id")]
201
202 # Find location node based in relation with netblock node.
203 located = False
204 for node_id in node_relations:
205
206 target_node = self.getNode(node_id)
207 if self.getType(target_node) == "maltego.Location":
208 located = True
209 break
210
211 if not located:
212 return None
213
214 # Get properties and update data
215 props = target_node.find(
216 "{http://graphml.graphdrawing.org/xmlns}data/"
217 "{http://maltego.paterva.com/xml/mtgx}MaltegoEntity/"
218 "{http://maltego.paterva.com/xml/mtgx}Properties")
219
220 for prop in props:
221
222 name_property = prop.get("name")
223 value = prop.find(
224 "{http://maltego.paterva.com/xml/mtgx}Value").text
225
226 if name_property == "location.name":
227 result["name"] = value
228 elif name_property == "location.area":
229 result["area"] = value
230 elif name_property == "countrycode":
231 result["country_code"] = value
232 elif name_property == "longitude":
233 result["longitude"] = value
234 elif name_property == "latitude":
235 result["latitude"] = value
236 elif name_property == "area":
237 result["area_2"] = value
238
239 return result
240
241 def getValue(self, target_node):
242
243 # Parse Entity
244 result = {"value": ""}
245
246 value = target_node.find(
247 "{http://graphml.graphdrawing.org/xmlns}data/"
248 "{http://maltego.paterva.com/xml/mtgx}MaltegoEntity/"
249 "{http://maltego.paterva.com/xml/mtgx}Properties/"
250 "{http://maltego.paterva.com/xml/mtgx}Property/"
251 "{http://maltego.paterva.com/xml/mtgx}Value")
252
253 result["value"] = value.text
254 return result
255
256 def parse(self):
257
258 self.getRelations()
259
260 for node in self.nodes:
261
262 # Get IP Address if not continue with other node...
263 result = self.getIpAndId(node)
264 if not result:
265 continue
266
267 # Create host with values by default
268 host = Host()
269 host.ip = result["ip"]
270 host.node_id = result["node_id"]
271
272 # Get relations with other nodes
273 node_relations = self.relations[host.node_id]
274
275 for node_id in node_relations:
276
277 # Get target node and type of node.
278 target_node = self.getNode(node_id)
279 target_type = self.getType(target_node)
280
281 # Check type of node y add data to host...
282 if target_type == "maltego.DNSName":
283 host.dns_name = self.getValue(target_node)
284 elif target_type == "maltego.Website":
285 host.website = self.getWebsite(target_node)
286 elif target_type == "maltego.Netblock":
287 host.netblock = self.getNetBlock(target_node)
288 # Get location based in relation: netblock -> location
289 host.location = self.getLocation(target_node)
290 elif target_type == "maltego.MXRecord":
291 host.mx_record = self.getValue(target_node)
292 elif target_type == "maltego.NSRecord":
293 host.ns_record = self.getValue(target_node)
294
295 self.list_hosts.append(host)
296
297 return self.list_hosts
298
299
300 class MaltegoPlugin(PluginXMLFormat):
301
302 def __init__(self):
303 super().__init__()
304 self.identifier_tag = "maltego"
305 self.id = "Maltego"
306 self.name = "Maltego MTGX Output Plugin"
307 self.plugin_version = "1.0.1"
308 self.version = "Maltego 3.6"
309 self.framework_version = "1.0.0"
310 self.current_path = None
311 self.options = None
312 self._current_output = None
313
314 self._command_regex = re.compile(
315 r'^(sudo maltego|maltego|\.\/maltego).*?')
316
317 global current_path
318
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:
411 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
438
439
440 def createPlugin():
441 return MaltegoPlugin()
442
443
444 if __name__ == "__main__":
445 import sys
446 import os
447 if len(sys.argv) == 2:
448 report_file = sys.argv[1]
449 if os.path.isfile(report_file):
450 plugin = createPlugin()
451 plugin.processReport(report_file)
452 print(plugin.get_json())
453 else:
454 print(f"Report not found: {report_file}")
455 else:
456 print(f"USAGE {sys.argv[0]} REPORT_FILE")
457 # 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 # 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 os
7 import random
8 import socket
9 from faraday_plugins.plugins.plugin import PluginBase
10
11
12 current_path = os.path.abspath(os.getcwd())
13
14 __author__ = "Francisco Amato"
15 __copyright__ = "Copyright 2013, Faraday Project"
16 __credits__ = ["Francisco Amato"]
17 __license__ = ""
18 __version__ = "1.0.0"
19 __maintainer__ = "Francisco Amato"
20 __email__ = "[email protected]"
21 __status__ = "Development"
22
23
24 class MedusaParser:
25 """
26 The objective of this class is to parse an xml file generated by the medusa tool.
27
28 @param medusa_filepath A proper simple report generated by medusa
29 """
30
31 def __init__(self, xml_output):
32 self.srv = {
33 'ftp': '21', 'http': '80', 'imap': '143', 'mssql': '1433', 'mysql': '3306',
34 'ncp': '524', 'nntp': '119', 'pcanywhere': '5631', 'pop3': '110', 'postgres': '5432',
35 'rexec': '512', 'rlogin': '513', 'rsh': '514', 'smbnt': 'smbnt', 'smtp': '25',
36 'smtp-vrfy': 'smtp-vrfy', 'snmp': '161', 'ssh': '22', 'svn': '3690',
37 'telnet': '23', 'vmauthd': 'vmauthd', 'vnc': '5900', 'web-form': 'web-form',
38 'wrapper': 'wrapper'
39 }
40
41 lines = xml_output.splitlines()
42 self.items = []
43
44 for l in lines:
45
46 reg = re.search(
47 "ACCOUNT FOUND: \[([^$]+)\] Host: ([^$]+) User: ([^$]+) Password: ([^$]+) \[SUCCESS\]",
48 l)
49
50 print("REG" + str(reg))
51
52 if reg:
53
54 item = {
55 'service': reg.group(1),
56 'host': reg.group(2),
57 'user': reg.group(3),
58 'pass': reg.group(4)
59 }
60
61 print("ITEM" + str(item))
62 item['ip'] = self.getAddress(item['host'])
63 item['port'] = self.srv[item['service']]
64 print("ITEM" + str(item))
65 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
75
76
77 class MedusaPlugin(PluginBase):
78 """
79 Example plugin to parse medusa output.
80 """
81
82 def __init__(self):
83 super().__init__()
84 self.id = "Medusa"
85 self.name = "Medusa Output Plugin"
86 self.plugin_version = "0.0.1"
87 self.version = "2.1.1"
88 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
94 self.host = None
95 self.port = ""
96
97 def parseOutputString(self, output, debug=False):
98 """
99 This method will discard the output the shell sends, it will read it from
100 the xml where it expects it to be present.
101
102 NOTE: if 'debug' is true then it is being run from a test case and the
103 output being sent is valid.
104 """
105 parser = MedusaParser(output)
106
107 for item in parser.items:
108
109 h_id = self.createAndAddHost(item['ip'])
110 if self._isIPV4(item['ip']):
111 i_id = self.createAndAddInterface(
112 h_id,
113 item['ip'],
114 ipv4_address=item['ip'],
115 hostname_resolution=item['host'])
116 else:
117 i_id = self.createAndAddInterface(
118 h_id,
119 item['ip'],
120 ipv6_address=item['ip'],
121 hostname_resolution=item['host'])
122
123 port = self.port if self.port else item['port']
124
125 s_id = self.createAndAddServiceToInterface(
126 h_id,
127 i_id,
128 item['service'],
129 ports=[port],
130 protocol="tcp",
131 status="open")
132
133 self.createAndAddCredToService(
134 h_id,
135 s_id,
136 item['user'],
137 item['pass'])
138
139 self.createAndAddVulnToService(h_id,
140 s_id,
141 "Weak Credentials",
142 "[medusa found the following credentials]\nuser:%s\npass:%s" % (item['user'], item['pass']),
143 severity="high")
144
145 del parser
146
147 xml_arg_re = re.compile(r"^.*(-O\s*[^\s]+).*$")
148
149 def processCommandString(self, username, current_path, command_string):
150
151 self.port = ""
152 self._output_file_path = os.path.join(
153 self.data_path, "medusa_output-%s.txt" % random.uniform(1, 10))
154 arg_match = self.xml_arg_re.match(command_string)
155
156 mreg = re.search(r"\-n( |)([\d]+)", command_string)
157 if mreg:
158 self.port = mreg.group(2)
159
160 if arg_match is None:
161 return re.sub(
162 r"(^.*?medusa?)", r"\1 -O %s" % self._output_file_path,
163 command_string)
164 else:
165 return re.sub(
166 arg_match.group(1),
167 r"-O %s" % self._output_file_path,
168 command_string)
169
170 def _isIPV4(self, ip):
171 if len(ip.split(".")) == 4:
172 return True
173 else:
174 return False
175
176 def setHost(self):
177 pass
178
179
180 def createPlugin():
181 return MedusaPlugin()
182
183
184 if __name__ == "__main__":
185 import sys
186 import os
187 if len(sys.argv) == 2:
188 report_file = sys.argv[1]
189 if os.path.isfile(report_file):
190 plugin = createPlugin()
191 plugin.processReport(report_file)
192 print(plugin.get_json())
193 else:
194 print(f"Report not found: {report_file}")
195 else:
196 print(f"USAGE {sys.argv[0]} REPORT_FILE")
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) 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 if __name__ == "__main__":
120 import sys
121 import os
122 if len(sys.argv) == 2:
123 report_file = sys.argv[1]
124 if os.path.isfile(report_file):
125 plugin = createPlugin()
126 plugin.processReport(report_file)
127 print(plugin.get_json())
128 else:
129 print(f"Report not found: {report_file}")
130 else:
131 print(f"USAGE {sys.argv[0]} REPORT_FILE")
132
133
134 # 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 # 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 from faraday_plugins.plugins.plugin import PluginXMLFormat
6 import re
7 import os
8 import sys
9
10 try:
11 import xml.etree.cElementTree as ET
12 import xml.etree.ElementTree as ET_ORIG
13 ETREE_VERSION = ET_ORIG.VERSION
14 except ImportError:
15 import xml.etree.ElementTree as ET
16 ETREE_VERSION = ET.VERSION
17
18 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
19
20 current_path = os.path.abspath(os.getcwd())
21
22 __author__ = "Francisco Amato"
23 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
24 __credits__ = ["Francisco Amato"]
25 __license__ = ""
26 __version__ = "1.0.0"
27 __maintainer__ = "Francisco Amato"
28 __email__ = "[email protected]"
29 __status__ = "Development"
30
31
32 class MetasploitXmlParser:
33 """
34 The objective of this class is to parse an xml file generated by the metasploit tool.
35
36 TODO: Handle errors.
37 TODO: Test metasploit output version. Handle what happens if the parser doesn't support it.
38 TODO: Test cases.
39
40 @param metasploit_xml_filepath A proper xml generated by metasploit
41 """
42
43 def __init__(self, xml_output):
44 tree = self.parse_xml(xml_output)
45 if tree:
46 servicesByWebsite = {}
47 for site in tree.findall('web_sites/web_site'):
48 servicesByWebsite[site.find('id').text] = site.find('service-id').text
49 webVulnsByService = {}
50 for v in [data for data in self.get_vulns(tree, servicesByWebsite)]:
51 if v.service_id not in webVulnsByService:
52 webVulnsByService[v.service_id] = []
53 webVulnsByService[v.service_id].append(v)
54
55 self.hosts = [data for data in self.get_items(
56 tree,
57 webVulnsByService)]
58 else:
59 self.hosts = []
60
61 def parse_xml(self, xml_output):
62 """
63 Open and parse an xml file.
64
65 TODO: Write custom parser to just read the nodes that we need instead of
66 reading the whole file.
67
68 @return xml_tree An xml tree instance. None if error.
69 """
70 try:
71 tree = ET.fromstring(xml_output)
72 except SyntaxError as err:
73 print("SyntaxError: %s. %s" % (err, xml_output))
74 return None
75
76 return tree
77
78 def get_items(self, tree, webVulns):
79 """
80 @return items A list of Host instances
81 """
82 bugtype = ""
83
84 for node in tree.findall('hosts/host'):
85 yield Host(node, webVulns)
86
87 def get_vulns(self, tree, services):
88 """
89 @return items A list of WebVuln instances
90 """
91 bugtype = ""
92 for node in tree.findall('web_vulns/web_vuln'):
93 yield WebVuln(node, services)
94
95
96 def get_attrib_from_subnode(xml_node, subnode_xpath_expr, attrib_name):
97 """
98 Finds a subnode in the item node and the retrieves a value from it
99
100 @return An attribute value
101 """
102 global ETREE_VERSION
103 node = None
104
105 if ETREE_VERSION[0] <= 1 and ETREE_VERSION[1] < 3:
106
107 match_obj = re.search(
108 "([^\@]+?)\[\@([^=]*?)=\'([^\']*?)\'", subnode_xpath_expr)
109 if match_obj is not None:
110 node_to_find = match_obj.group(1)
111 xpath_attrib = match_obj.group(2)
112 xpath_value = match_obj.group(3)
113 for node_found in xml_node.findall(node_to_find):
114 if node_found.attrib[xpath_attrib] == xpath_value:
115 node = node_found
116 break
117 else:
118 node = xml_node.find(subnode_xpath_expr)
119
120 else:
121 node = xml_node.find(subnode_xpath_expr)
122
123 if node is not None:
124 return node.get(attrib_name)
125
126 return None
127
128
129 class Host:
130
131 def __init__(self, item_node, webVulnsByService):
132 self.node = item_node
133 self.id = self.get_text_from_subnode('id')
134 self.host = self.get_text_from_subnode('name')
135 self.ip = self.get_text_from_subnode('address')
136 self.os = self.get_text_from_subnode('os-name')
137
138 self.services = []
139 self.vulnsByService = {}
140 self.vulnsByHost = []
141 self.notesByService = {}
142 self.credsByService = {}
143 for s in self.node.findall('services/service'):
144 service = {'id': None, 'port': None, 'proto': None,
145 'state': None, 'name': None, 'info': None}
146 for attr in service:
147 service[attr] = s.find(attr).text
148 if not service['name']:
149 service['name'] = 'unknown'
150 if not service['state']:
151 service['state'] = 'unknown'
152 if not service['info']:
153 service['info'] = 'unknown'
154
155 self.services.append(service)
156 self.vulnsByService[service['id']] = []
157 self.notesByService[service['id']] = []
158 if service['id'] in webVulnsByService:
159 self.vulnsByService[service['id']] += webVulnsByService[service['id']]
160
161 for v in self.node.findall('vulns/vuln'):
162 vuln = HostVuln(v)
163 if vuln.service_id:
164 self.vulnsByService[vuln.service_id].append(vuln)
165 else:
166 self.vulnsByHost.append(vuln)
167
168 for n in self.node.findall('notes/note'):
169 note = HostNote(n)
170 key = self.id + "_" + note.service_id
171 if key not in self.notesByService:
172 self.notesByService[key] = []
173
174 self.notesByService[key].append(note)
175
176 for c in self.node.findall('creds/cred'):
177 cred = HostCred(c)
178 key = cred.port
179 if key not in self.credsByService:
180 self.credsByService[key] = []
181
182 self.credsByService[key].append(cred)
183
184 def get_text_from_subnode(self, subnode_xpath_expr):
185 """
186 Finds a subnode in the host node and the retrieves a value from it.
187
188 @return An attribute value
189 """
190 sub_node = self.node.find(subnode_xpath_expr)
191 if sub_node is not None:
192 if sub_node.text is not None:
193 return sub_node.text
194
195 return None
196
197
198 class WebVuln:
199
200 def __init__(self, item_node, services):
201 self.node = item_node
202 self.name = self.get_text_from_subnode('name')
203 self.desc = self.get_text_from_subnode('description')
204 self.host = self.get_text_from_subnode('vhost')
205 self.port = self.get_text_from_subnode('port')
206 self.ip = self.get_text_from_subnode('host')
207 self.path = self.get_text_from_subnode('path')
208 self.method = self.get_text_from_subnode('method')
209 self.params = self.get_text_from_subnode('params')
210 self.pname = self.get_text_from_subnode('pname')
211 self.risk = self.get_text_from_subnode('risk')
212 self.confidence = self.get_text_from_subnode('confidence')
213 self.query = self.get_text_from_subnode('query')
214 self.request = self.get_text_from_subnode('request')
215 self.category = self.get_text_from_subnode('category-id')
216 self.service_id = services[self.get_text_from_subnode('web-site-id')]
217 self.isWeb = True
218
219 def get_text_from_subnode(self, subnode_xpath_expr):
220 """
221 Finds a subnode in the host node and the retrieves a value from it.
222
223 @return An attribute value
224 """
225 sub_node = self.node.find(subnode_xpath_expr)
226 if sub_node is not None:
227 if sub_node.text is not None:
228 return sub_node.text
229
230 return ""
231
232
233 class HostNote:
234 """
235 An abstract representation of a HostNote
236
237
238 @param item_node A item_node taken from an metasploit xml tree
239 """
240
241 def __init__(self, item_node):
242 self.node = item_node
243 self.service_id = self.get_text_from_subnode(
244 'service-id') if not None else ""
245 self.host_id = self.get_text_from_subnode('host-id')
246 self.ntype = self.get_text_from_subnode('ntype')
247 self.data = self.get_text_from_subnode('data')
248
249 def get_text_from_subnode(self, subnode_xpath_expr):
250 """
251 Finds a subnode in the host node and the retrieves a value from it.
252
253 @return An attribute value
254 """
255 sub_node = self.node.find(subnode_xpath_expr)
256 if sub_node is not None:
257 if sub_node.text is not None:
258 return sub_node.text
259
260 return ""
261
262
263 class HostCred:
264 """
265 An abstract representation of a HostNote
266
267
268 @param item_node A item_node taken from an metasploit xml tree
269 """
270
271 def __init__(self, item_node):
272 self.node = item_node
273 self.port = self.get_text_from_subnode('port')
274 self.user = self.get_text_from_subnode('user')
275 self.passwd = self.get_text_from_subnode('pass')
276 self.ptype = self.get_text_from_subnode('ptype')
277 self.sname = self.get_text_from_subnode('sname')
278
279 def get_text_from_subnode(self, subnode_xpath_expr):
280 """
281 Finds a subnode in the host node and the retrieves a value from it.
282
283 @return An attribute value
284 """
285 sub_node = self.node.find(subnode_xpath_expr)
286 if sub_node is not None:
287 if sub_node.text is not None:
288 return sub_node.text
289
290 return ""
291
292
293 class HostVuln:
294 """
295 An abstract representation of a HostVuln
296
297
298 @param item_node A item_node taken from an metasploit xml tree
299 """
300
301 def __init__(self, item_node):
302 self.node = item_node
303 self.service_id = self.get_text_from_subnode('service-id')
304 self.name = self.get_text_from_subnode('name')
305 self.desc = self.get_text_from_subnode('info')
306 self.refs = [r.text for r in self.node.findall('refs/ref')]
307 self.exploited_date = self.get_text_from_subnode('exploited-at')
308 self.exploited = (self.exploited_date is not None)
309 self.isWeb = False
310
311 def get_text_from_subnode(self, subnode_xpath_expr):
312 """
313 Finds a subnode in the host node and the retrieves a value from it.
314
315 @return An attribute value
316 """
317 sub_node = self.node.find(subnode_xpath_expr)
318 if sub_node is not None:
319 if sub_node.text is not None:
320 return sub_node.text
321
322 return ""
323
324
325 class MetasploitPlugin(PluginXMLFormat):
326 """
327 Example plugin to parse metasploit output.
328 """
329
330 def __init__(self):
331 super().__init__()
332 self.identifier_tag = ["MetasploitV4", "MetasploitV5"]
333 self.id = "Metasploit"
334 self.name = "Metasploit XML Output Plugin"
335 self.plugin_version = "0.0.1"
336 self.version = "4.7.2"
337 self.framework_version = "1.0.0"
338 self.options = None
339 self._current_output = None
340 self.target = None
341 self._command_regex = re.compile(r'^(metasploit|sudo metasploit|\.\/metasploit).*?')
342
343
344 def parseOutputString(self, output, debug=False):
345 """
346 This method will discard the output the shell sends, it will read it from
347 the xml where it expects it to be present.
348
349 NOTE: if 'debug' is true then it is being run from a test case and the
350 output being sent is valid.
351 """
352
353 parser = MetasploitXmlParser(output)
354
355 for item in parser.hosts:
356 self.hostnames = []
357 if item.host:
358 self.hostnames = [item.host]
359
360 h_id = self.createAndAddHost(item.ip, os=item.os, hostnames=self.hostnames)
361
362 if item.id + "_" in item.notesByService:
363 for n in item.notesByService[item.id + "_"]:
364 self.createAndAddNoteToHost(h_id, n.ntype, n.data)
365
366 for v in item.vulnsByHost:
367 v_id = self.createAndAddVulnToHost(
368 h_id, v.name, v.desc, ref=v.refs)
369
370 for s in item.services:
371 s_id = self.createAndAddServiceToHost(h_id, s['name'],
372 protocol=s['proto'],
373 ports=[s['port']],
374 status=s['state'],
375 description=s['info'])
376
377 if item.id + "_" + s['id'] in item.notesByService:
378 for n in item.notesByService[item.id + "_" + s['id']]:
379 self.createAndAddNoteToService(
380 h_id, s_id, n.ntype, n.data)
381
382 if s['port'] in item.credsByService:
383 for c in item.credsByService[s['port']]:
384 self.createAndAddCredToService(
385 h_id, s_id, c.user, c.passwd)
386 self.createAndAddVulnToService(h_id, s_id, "Weak Credentials", "[metasploit found the following credentials]\nuser:%s\npass:%s" % (
387 c.user, c.passwd), severity="high")
388
389 for v in item.vulnsByService[s['id']]:
390 if v.isWeb:
391 v_id = self.createAndAddVulnWebToService(h_id, s_id, v.name, v.desc,
392 severity=v.risk, website=v.host,
393 path=v.path, request=v.request, method=v.method,
394 pname=v.pname, params=v.params, query=v.query,
395 category=v.category)
396 else:
397 v_id = self.createAndAddVulnToService(
398 h_id, s_id, v.name, v.desc, ref=v.refs)
399
400 del parser
401
402 def _isIPV4(self, ip):
403 if len(ip.split(".")) == 4:
404 return True
405 else:
406 return False
407
408 def processCommandString(self, username, current_path, command_string):
409 return None
410
411 def setHost(self):
412 pass
413
414
415 def createPlugin():
416 return MetasploitPlugin()
417
418
419 if __name__ == "__main__":
420 import sys
421 import os
422 if len(sys.argv) == 2:
423 report_file = sys.argv[1]
424 if os.path.isfile(report_file):
425 plugin = createPlugin()
426 plugin.processReport(report_file)
427 print(plugin.get_json())
428 else:
429 print(f"Report not found: {report_file}")
430 else:
431 print(f"USAGE {sys.argv[0]} REPORT_FILE")
432 # 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 """
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 """
6 from faraday_plugins.plugins.plugin import PluginBase
7 import re
8
9 try:
10 import xml.etree.cElementTree as ET
11 import xml.etree.ElementTree as ET_ORIG
12 except ImportError:
13 import xml.etree.ElementTree as ET
14
15 __author__ = 'Ezequiel Tavella'
16 __copyright__ = 'Copyright (c) 2016, Infobyte LLC'
17 __credits__ = ['Ezequiel Tavella']
18 __license__ = ''
19 __version__ = '1.0.0'
20 __email__ = "[email protected]"
21 __status__ = "Development"
22
23
24 class NdiffXmlParser():
25 """
26 The objective of this class is to parse an xml file generated by
27 the ndiff tool.
28 """
29
30 def __init__(self, xmlOutput):
31 self.tree = self.parse_xml(xmlOutput)
32
33 if self.tree:
34 self.hostDiff = self.getHostsDiffs(self.tree)
35 else:
36 self.hostDiff = []
37
38 def parse_xml(self, xmlOutput):
39
40 # Open and parse an xml output
41
42 try:
43 return ET.fromstring(xmlOutput)
44 except SyntaxError as err:
45 print("SyntaxError: %s" % err)
46 return None
47
48 def getHostsDiffs(self, tree):
49 """
50 @return hosts A list of HostDiff instances
51 """
52 for node in tree.findall('scandiff/hostdiff'):
53 yield HostDiff(node)
54
55
56 class HostDiff():
57
58 # Abstraction of a Hosts Diff
59 # Search for a new host in the second scan and new ports opened or changed
60 # of status...
61 def __init__(self, hostDiff):
62
63 self.isNewHost = False
64 self.hostXml = self.getHostXml(hostDiff)
65
66 self.ip = self.getIp()
67 self.ports = self.getPorts()
68
69 def getHostXml(self, hostDiff):
70
71 host = hostDiff.find('host')
72 if host is not None:
73 return host
74 else:
75 self.isNewHost = True
76 return hostDiff.find('b/host')
77
78 def getIp(self):
79 if self.hostXml is None:
80 return None
81 return self.hostXml.find('address').get('addr')
82
83 def getPorts(self):
84
85 ports = []
86 if self.hostXml is None:
87 return ports
88
89 if self.isNewHost:
90
91 for port in self.hostXml.find('ports').findall('port'):
92 ports.append(
93 [port.get('portid'), port.find('state').get('state')])
94 return ports
95
96 else:
97
98 for port in self.hostXml.find('ports').findall('portdiff'):
99 if port.find('b/port'):
100 ports.append([port.find('b/port').get('portid'),
101 port.find('b/port/state').get('state')])
102 return ports
103
104
105 class CmdNdiffPlugin(PluginBase):
106 """
107 This plugin handles ndiff command.
108 Add a new vuln INFO if detect a new host or a new port ..
109 """
110
111 def __init__(self):
112 super().__init__()
113 self.id = "Ndiff"
114 self.name = "ndiff"
115 self.plugin_version = "0.0.1"
116 self.version = "1.0.0"
117 self._command_regex = re.compile(r'^(sudo ndiff|ndiff).*?')
118
119 def parseOutputString(self, output, debug=False):
120
121 parser = NdiffXmlParser(output)
122
123 for host in parser.hostDiff:
124
125 if host.ip is None:
126 continue
127
128 if host.isNewHost:
129 hostId = self.createAndAddHost(host.ip, '')
130
131 description = '%s is a NEW host active.\n' % host.ip
132 for port in host.ports:
133 description += 'Port: %s/%s\n' % (port[0], port[1])
134
135 self.createAndAddVulnToHost(
136 hostId,
137 'New host active',
138 description,
139 ['Ndiff tool'],
140 'INFO'
141 )
142 else:
143
144 if host.ports == []:
145 continue
146
147 hostId = self.createAndAddHost(host.ip, '')
148
149 description = 'New service/s found.\n'
150 for port in host.ports:
151 description += 'Port: %s/%s\n' % (port[0], port[1])
152
153 self.createAndAddVulnToHost(
154 hostId,
155 'New ports actives',
156 description,
157 ['Ndiff tool'],
158 'INFO'
159 )
160
161 def processCommandString(self, username, current_path, command_string):
162 if command_string.find('--xml') < 0:
163 return command_string + '--xml'
164
165
166 def createPlugin():
167 return CmdNdiffPlugin()
168
169 if __name__ == "__main__":
170 import sys
171 import os
172 if len(sys.argv) == 2:
173 report_file = sys.argv[1]
174 if os.path.isfile(report_file):
175 plugin = createPlugin()
176 plugin.processReport(report_file)
177 print(plugin.get_json())
178 else:
179 print(f"Report not found: {report_file}")
180 else:
181 print(f"USAGE {sys.argv[0]} REPORT_FILE")
182 # 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 # I'm Py3
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
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 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"]
21 __license__ = ""
22 __version__ = "1.0.0"
23 __maintainer__ = "Francisco Amato"
24 __email__ = "[email protected]"
25 __status__ = "Development"
26
27
28 class NessusParser:
29 """
30 The objective of this class is to parse an xml file generated by the nessus tool.
31
32 TODO: Handle errors.
33 TODO: Test nessus output version. Handle what happens if the parser doesn't support it.
34 TODO: Test cases.
35
36 @param nessus_filepath A proper simple report generated by nessus
37 """
38
39 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
52
53
54 class NessusPlugin(PluginXMLFormat):
55 """
56 Example plugin to parse nessus output.
57 """
58
59 def __init__(self):
60 super().__init__()
61 self.extension = ".nessus"
62 self.identifier_tag = "NessusClientData_v2"
63 self.id = "Nessus"
64 self.name = "Nessus XML Output Plugin"
65 self.plugin_version = "0.0.1"
66 self.version = "5.2.4"
67 self.framework_version = "1.0.1"
68 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):
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 p = dotnessus_v2.Report()
94 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)
157 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)
174 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
182 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
197
198
199 def createPlugin():
200 return NessusPlugin()
201
202 if __name__ == "__main__":
203 import sys
204 import os
205 if len(sys.argv) == 2:
206 report_file = sys.argv[1]
207 if os.path.isfile(report_file):
208 plugin = createPlugin()
209 plugin.processReport(report_file)
210 print(plugin.get_json())
211 else:
212 print(f"Report not found: {report_file}")
213 else:
214 print(f"USAGE {sys.argv[0]} REPORT_FILE")
215 # I'm Py3
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
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 # 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 from faraday_plugins.plugins.plugin import PluginBase
6 import re
7
8 __author__ = "Federico Fernandez - @q3rv0"
9 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
10 __license__ = ""
11 __version__ = "1.0.0"
12 __maintainer__ = "Federico Fernandez"
13 __email__ = "[email protected]"
14 __status__ = "Development"
15
16 class NetdiscoverPlugin(PluginBase):
17
18 def __init__(self):
19 super().__init__()
20 self.id = "Netdiscover"
21 self.name = "netdiscover"
22 self.plugin_version = "0.0.1"
23 self.version = "1.0.0"
24 self._command_regex = re.compile(r'^(sudo netdiscover|netdiscover).*?')
25
26 def parseOutputString(self, output, debug=False):
27 #regexp get ip, mac and hostname
28 reg = re.findall(r"(([0-9]+\.?){4})\s+(([0-9a-f]+\:?){6})((\s+[0-9]+){2})(.*)", output)
29
30 if output.find('Finished!') != -1 and len(reg) > 0:
31
32 for stdout in reg:
33 ip_address = stdout[0]
34 mac = stdout[2]
35 hostname = stdout[6].strip()
36
37 h_id = self.createAndAddHost(ip_address)
38 self.createAndAddInterface(h_id, ip_address, ipv4_address=ip_address, mac=mac, hostname_resolution=[hostname])
39
40 return True
41
42 def processCommandString(self, username, current_path, command_string):
43 return None
44
45
46 def createPlugin():
47 return NetdiscoverPlugin()
48
49 if __name__ == "__main__":
50 import sys
51 import os
52 if len(sys.argv) == 2:
53 report_file = sys.argv[1]
54 if os.path.isfile(report_file):
55 plugin = createPlugin()
56 plugin.processReport(report_file)
57 print(plugin.get_json())
58 else:
59 print(f"Report not found: {report_file}")
60 else:
61 print(f"USAGE {sys.argv[0]} REPORT_FILE")
62
63 # 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 # 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 from faraday_plugins.plugins.plugin import PluginXMLFormat
7 import re
8 import os
9 import sys
10 import socket
11 import urllib
12 from bs4 import BeautifulSoup
13
14 try:
15 import xml.etree.cElementTree as ET
16 import xml.etree.ElementTree as ET_ORIG
17 ETREE_VERSION = ET_ORIG.VERSION
18 except ImportError:
19 import xml.etree.ElementTree as ET
20 ETREE_VERSION = ET.VERSION
21
22 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
23
24 current_path = os.path.abspath(os.getcwd())
25
26 __author__ = "Francisco Amato"
27 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
28 __credits__ = ["Francisco Amato"]
29 __license__ = ""
30 __version__ = "1.0.0"
31 __maintainer__ = "Francisco Amato"
32 __email__ = "[email protected]"
33 __status__ = "Development"
34
35
36 class NetsparkerXmlParser:
37 """
38 The objective of this class is to parse an xml file generated by the netsparker tool.
39
40 TODO: Handle errors.
41 TODO: Test netsparker output version. Handle what happens if the parser doesn't support it.
42 TODO: Test cases.
43
44 @param netsparker_xml_filepath A proper xml generated by netsparker
45 """
46
47 def __init__(self, xml_output):
48 self.filepath = xml_output
49
50 tree = self.parse_xml(xml_output)
51 if tree:
52 self.items = [data for data in self.get_items(tree)]
53 else:
54 self.items = []
55
56 def parse_xml(self, xml_output):
57 """
58 Open and parse an xml file.
59
60 TODO: Write custom parser to just read the nodes that we need instead of
61 reading the whole file.
62
63 @return xml_tree An xml tree instance. None if error.
64 """
65 try:
66 tree = ET.fromstring(xml_output)
67 except SyntaxError as err:
68 self.devlog("SyntaxError: %s. %s" % (err, xml_output))
69 return None
70
71 return tree
72
73 def get_items(self, tree):
74 """
75 @return items A list of Host instances
76 """
77 for node in tree.findall("vulnerability"):
78 yield Item(node)
79
80
81 class Item:
82 """
83 An abstract representation of a Item
84
85
86 @param item_node A item_node taken from an netsparker xml tree
87 """
88
89 def re_map_severity(self, severity):
90 if severity == "Important":
91 return "high"
92 return severity
93
94 def __init__(self, item_node, encoding="ascii"):
95 self.node = item_node
96 self.url = self.get_text_from_subnode("url")
97
98 host = re.search(
99 "(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)
100
101 self.protocol = host.group(1)
102 self.hostname = host.group(4)
103 self.port = 80
104
105 if self.protocol == 'https':
106 self.port = 443
107 if host.group(11) is not None:
108 self.port = host.group(11)
109
110 self.name = self.get_text_from_subnode("type")
111 self.desc = self.get_text_from_subnode("description")
112 self.severity = self.re_map_severity(self.get_text_from_subnode("severity"))
113 self.certainty = self.get_text_from_subnode("certainty")
114 self.method = self.get_text_from_subnode("vulnerableparametertype")
115 self.param = self.get_text_from_subnode("vulnerableparameter")
116 self.paramval = self.get_text_from_subnode("vulnerableparametervalue")
117 self.reference = self.get_text_from_subnode("externalReferences")
118 self.resolution = self.get_text_from_subnode("actionsToTake")
119 self.request = self.get_text_from_subnode("rawrequest")
120 self.response = self.get_text_from_subnode("rawresponse")
121 if self.response:
122 self.response = self.response.encode(encoding,errors="backslashreplace").decode(encoding)
123 if self.request:
124 self.request = self.request.encode(encoding,errors="backslashreplace").decode(encoding)
125 if self.reference:
126 self.reference = self.reference.encode(encoding,errors="backslashreplace").decode(encoding)
127
128
129 self.kvulns = []
130 for v in self.node.findall("knownvulnerabilities/knownvulnerability"):
131 self.node = v
132 self.kvulns.append(self.get_text_from_subnode(
133 "severity") + "-" + self.get_text_from_subnode("title"))
134
135 self.extra = []
136 for v in item_node.findall("extrainformation/info"):
137 self.extra.append(v.get('name') + ":" + v.text)
138
139 self.node = item_node
140 self.node = item_node.find("classification")
141 self.owasp = self.get_text_from_subnode("OWASP")
142 self.wasc = self.get_text_from_subnode("WASC")
143 self.cwe = self.get_text_from_subnode("CWE")
144 self.capec = self.get_text_from_subnode("CAPEC")
145 self.pci = self.get_text_from_subnode("PCI")
146 self.pci2 = self.get_text_from_subnode("PCI2")
147 self.node = item_node.find("classification/CVSS")
148 self.cvss = self.get_text_from_subnode("vector")
149
150 self.ref = []
151 if self.cwe:
152 self.ref.append("CWE-" + self.cwe)
153 if self.owasp:
154 self.ref.append("OWASP-" + self.owasp)
155 if self.reference:
156 self.ref.extend(list(set(re.findall('https?://(?:[-\w.]|(?:%[\da-fA-F]{2}))+', self.reference))))
157 if self.cvss:
158 self.ref.append(self.cvss)
159
160 self.data = ""
161 self.data += "\nKnowVulns: " + \
162 "\n".join(self.kvulns) if self.kvulns else ""
163 self.data += "\nWASC: " + self.wasc if self.wasc else ""
164 self.data += "\nCertainty: " + self.certainty if self.certainty else ""
165 self.data += "\nPCI: " + self.pci if self.pci else ""
166 self.data += "\nPCI2: " + self.pci2 if self.pci2 else ""
167 self.data += "\nCAPEC: " + self.capec if self.capec else ""
168 self.data += "\nPARAM: " + self.param if self.param else ""
169 self.data += "\nPARAM VAL: " + \
170 repr(self.paramval) if self.paramval else ""
171 self.data += "\nExtra: " + "\n".join(self.extra) if self.extra else ""
172
173 def get_text_from_subnode(self, subnode_xpath_expr):
174 """
175 Finds a subnode in the host node and the retrieves a value from it.
176
177 @return An attribute value
178 """
179 if self.node:
180 sub_node = self.node.find(subnode_xpath_expr)
181 if sub_node is not None:
182 return sub_node.text
183
184 return None
185
186
187 class NetsparkerPlugin(PluginXMLFormat):
188 """
189 Example plugin to parse netsparker output.
190 """
191
192 def __init__(self):
193 super().__init__()
194 self.identifier_tag = "netsparker"
195 self.id = "Netsparker"
196 self.name = "Netsparker XML Output Plugin"
197 self.plugin_version = "0.0.1"
198 self.version = "Netsparker 3.1.1.0"
199 self.framework_version = "1.0.0"
200 self.options = None
201 self._current_output = None
202 self._command_regex = re.compile(
203 r'^(sudo netsparker|\.\/netsparker).*?')
204
205
206 def resolve(self, host):
207 try:
208 return socket.gethostbyname(host)
209 except:
210 pass
211 return host
212
213 def parseOutputString(self, output, debug=False):
214
215 parser = NetsparkerXmlParser(output)
216 first = True
217 for i in parser.items:
218 if first:
219 ip = self.resolve(i.hostname)
220 h_id = self.createAndAddHost(ip, hostnames=[ip])
221
222 s_id = self.createAndAddServiceToHost(h_id, str(i.port),
223 protocol = str(i.protocol),
224 ports=[str(i.port)],
225 status="open")
226 first = False
227
228 v_id = self.createAndAddVulnWebToService(h_id, s_id, i.name, ref=i.ref, website=i.hostname,
229 severity=i.severity, desc=BeautifulSoup(i.desc, "lxml").text,
230 path=i.url, method=i.method, request=i.request, response=i.response,
231 resolution=BeautifulSoup(i.resolution, "lxml").text,pname=i.param, data=i.data)
232
233 del parser
234
235 def processCommandString(self, username, current_path, command_string):
236 return None
237
238
239 def createPlugin():
240 return NetsparkerPlugin()
241
242 if __name__ == "__main__":
243 import sys
244 import os
245 if len(sys.argv) == 2:
246 report_file = sys.argv[1]
247 if os.path.isfile(report_file):
248 plugin = createPlugin()
249 plugin.processReport(report_file)
250 print(plugin.get_json())
251 else:
252 print(f"Report not found: {report_file}")
253 else:
254 print(f"USAGE {sys.argv[0]} REPORT_FILE")
255
256 # 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 # 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 from faraday_plugins.plugins.plugin import PluginXMLFormat
7 import re
8 import os
9 import socket
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
18
19 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
20
21 current_path = os.path.abspath(os.getcwd())
22
23 __author__ = "Francisco Amato"
24 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
25 __credits__ = ["Francisco Amato"]
26 __license__ = ""
27 __version__ = "1.0.0"
28 __maintainer__ = "Francisco Amato"
29 __email__ = "[email protected]"
30 __status__ = "Development"
31
32
33 def cleaner_unicode(string):
34 if string is not None:
35 return string.encode('ascii', errors='backslashreplace')
36 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)
50 return urls
51
52
53 class NetsparkerCloudXmlParser:
54 """
55 The objective of this class is to parse an xml file generated by the netsparkercloud tool.
56
57 TODO: Handle errors.
58 TODO: Test netsparkercloud output version. Handle what happens if the parser doesn't support it.
59 TODO: Test cases.
60
61 @param netsparkercloud_xml_filepath A proper xml generated by netsparkercloud
62 """
63
64 def __init__(self, xml_output):
65 self.filepath = xml_output
66
67 tree = self.parse_xml(xml_output)
68 if tree:
69 self.items = [data for data in self.get_items(tree)]
70 else:
71 self.items = []
72
73 def parse_xml(self, xml_output):
74 """
75 Open and parse an xml file.
76
77 TODO: Write custom parser to just read the nodes that we need instead of
78 reading the whole file.
79
80 @return xml_tree An xml tree instance. None if error.
81 """
82 try:
83 tree = ET.fromstring(xml_output)
84 except SyntaxError as err:
85 self.devlog("SyntaxError: %s. %s" % (err, xml_output))
86 return None
87
88 return tree
89
90 def get_items(self, tree):
91 """
92 @return items A list of Host instances
93 """
94 for node in tree.findall("vulnerabilities/vulnerability"):
95 yield Item(node)
96
97
98 class Item:
99 """
100 An abstract representation of a Item
101
102
103 @param item_node A item_node taken from an netsparkercloud xml tree
104 """
105 def re_map_severity(self, severity):
106 if severity == "Important":
107 return "high"
108 return severity
109
110 def __init__(self, item_node):
111 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
126 self.type = self.get_text_from_subnode("type")
127 self.name = self.get_text_from_subnode("name")
128 self.severity = self.re_map_severity(self.get_text_from_subnode("severity"))
129 self.certainty = self.get_text_from_subnode("certainty")
130
131
132 self.node = item_node.find("http-request")
133 self.method = self.get_text_from_subnode("method")
134 self.request = self.get_text_from_subnode("content")
135
136 #print self.node
137 self.param = ""
138 self.paramval = ""
139 for p in self.node.findall("parameters/parameter"):
140 self.param = p.get('name')
141 self.paramval = p.get('value')
142
143 self.node = item_node.find("http-response")
144 self.response = self.get_text_from_subnode("content")
145
146 self.extra = []
147 for v in item_node.findall("extra-information/info"):
148 self.extra.append(v.get('name') + ":" + v.get('value') )
149
150 self.node = item_node.find("classification")
151 self.owasp = self.get_text_from_subnode("owasp")
152 self.wasc = self.get_text_from_subnode("wasc")
153 self.cwe = self.get_text_from_subnode("cwe")
154 self.capec = self.get_text_from_subnode("capec")
155 self.pci = self.get_text_from_subnode("pci31")
156 self.pci2 = self.get_text_from_subnode("pci32")
157 self.hipaa = self.get_text_from_subnode("hipaa")
158
159 self.ref = []
160 if self.cwe:
161 self.ref.append("CWE-" + self.cwe)
162 if self.owasp:
163 self.ref.append("OWASP-" + self.owasp)
164
165 self.node = item_node
166 self.remedyreferences = self.get_text_from_subnode("remedy-references")
167 self.externalreferences = self.get_text_from_subnode("external-references")
168 if self.remedyreferences:
169 for u in get_urls(self.remedyreferences):
170 self.ref.append(u)
171 if self.externalreferences:
172 for u in get_urls(self.externalreferences):
173 self.ref.append(u)
174
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 ""
195
196 def get_text_from_subnode(self, subnode_xpath_expr):
197 """
198 Finds a subnode in the host node and the retrieves a value from it.
199
200 @return An attribute value
201 """
202 if self.node:
203 sub_node = self.node.find(subnode_xpath_expr)
204 if sub_node is not None:
205 if sub_node.text is not None:
206 return cleaner_unicode(sub_node.text)
207
208 return ""
209
210
211 class NetsparkerCloudPlugin(PluginXMLFormat):
212 """
213 Example plugin to parse netsparkercloud output.
214 """
215
216 def __init__(self):
217 super().__init__()
218 self.identifier_tag = "netsparker-cloud"
219 self.id = "NetsparkerCloud"
220 self.name = "NetsparkerCloud XML Output Plugin"
221 self.plugin_version = "0.0.1"
222 self.version = "NetsparkerCloud"
223 self.framework_version = "1.0.0"
224 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
236
237 def parseOutputString(self, output, debug=False):
238
239 parser = NetsparkerCloudXmlParser(output)
240 first = True
241 for i in parser.items:
242 if first:
243 ip = self.resolve(i.hostname)
244 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")
252
253 first = False
254
255 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,
257 request=i.request, response=i.response, resolution=i.resolution, pname=i.param)
258
259 del parser
260
261 def processCommandString(self, username, current_path, command_string):
262 return None
263
264 def setHost(self):
265 pass
266
267
268 def createPlugin():
269 return NetsparkerCloudPlugin()
270
271
272 if __name__ == "__main__":
273 import sys
274 import os
275 if len(sys.argv) == 2:
276 report_file = sys.argv[1]
277 if os.path.isfile(report_file):
278 plugin = createPlugin()
279 plugin.processReport(report_file)
280 print(plugin.get_json())
281 else:
282 print(f"Report not found: {report_file}")
283 else:
284 print(f"USAGE {sys.argv[0]} REPORT_FILE")
285 # 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 # 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 from faraday_plugins.plugins.plugin import PluginXMLFormat
7
8 import re
9 import os
10 import sys
11
12 try:
13 import xml.etree.cElementTree as ET
14 import xml.etree.ElementTree as ET_ORIG
15 ETREE_VERSION = ET_ORIG.VERSION
16 except ImportError:
17 import xml.etree.ElementTree as ET
18 ETREE_VERSION = ET.VERSION
19
20 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
21
22 current_path = os.path.abspath(os.getcwd())
23
24 __author__ = "Micaela Ranea Sanchez"
25 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
26 __credits__ = ["Francisco Amato", "Federico Kirschbaum",
27 "Micaela Ranea Sanchez", "German Riera"]
28 __license__ = ""
29 __version__ = "1.0.0"
30 __maintainer__ = "Micaela Ranea Sanchez"
31 __email__ = "[email protected]"
32 __status__ = "Development"
33
34
35 class NexposeFullXmlParser:
36 """
37 The objective of this class is to parse Nexpose's XML 2.0 Report.
38
39 TODO: Handle errors.
40 TODO: Test nexpose output version. Handle what happens if the parser doesn't support it.
41 TODO: Test cases.
42
43 @param xml_filepath A proper xml generated by nexpose
44 """
45
46 def __init__(self, xml_output):
47 tree = self.parse_xml(xml_output)
48 self.vulns = self.get_vuln_definitions(tree)
49
50 if tree:
51 self.items = self.get_items(tree, self.vulns)
52 else:
53 self.items = []
54
55 def parse_xml(self, xml_output):
56 """
57 Open and parse an xml file.
58
59 TODO: Write custom parser to just read the nodes that we need instead of
60 reading the whole file.
61
62 @return xml_tree An xml tree instance. None if error.
63 """
64 try:
65 tree = ET.fromstring(xml_output)
66 except SyntaxError as err:
67 print("SyntaxError: %s. %s" % (err, xml_output))
68 return None
69
70 return tree
71
72 def parse_html_type(self, node: ET.Element) -> str:
73 """
74 Parse XML element of type HtmlType
75
76 @return ret A string containing the parsed element
77 """
78 ret = ""
79 tag: str = node.tag.lower()
80 if tag == 'containerblockelement':
81 if len(list(node)) > 0:
82 for child in list(node):
83 ret += self.parse_html_type(child)
84 else:
85 ret += node.text.strip() if node.text else ""
86 if tag == 'listitem':
87 if len(list(node)) > 0:
88 for child in list(node):
89 ret += self.parse_html_type(child)
90 else:
91 ret = node.text.strip() if node.text else ""
92 if tag == 'orderedlist':
93 i = 1
94 for item in list(node):
95 ret += "\t" + str(i) + " " + self.parse_html_type(item) + "\n"
96 i += 1
97 if tag == 'paragraph':
98 if len(list(node)) > 0:
99 for child in list(node):
100 ret += self.parse_html_type(child)
101 else:
102 ret += node.text.strip() if node.text else ""
103 if tag == 'unorderedlist':
104 for item in list(node):
105 ret += "\t" + "* " + self.parse_html_type(item) + "\n"
106 if tag == 'urllink':
107 if node.get('text'):
108 ret += node.text.strip() + " "
109 last = ""
110 for attr in node.attrib:
111 if node.get(attr) and node.get(attr) != node.get(last):
112 ret += node.get(attr) + " "
113 last = attr
114
115 return ret
116
117 def parse_tests_type(self, node, vulnsDefinitions):
118 """
119 Parse XML element of type TestsType
120
121 @return vulns A list of vulnerabilities according to vulnsDefinitions
122 """
123 vulns = list()
124
125 for tests in node.findall('tests'):
126 for test in tests.iter('test'):
127 vuln = dict()
128 if test.get('id').lower() in vulnsDefinitions:
129 vuln = vulnsDefinitions[test.get('id').lower()].copy()
130 key = test.get('key', '')
131 if key.startswith('/'):
132 # It has the path where the vuln was found
133 # Example key: "/comments.asp||content"
134 vuln['path'] = key[:key.find('|')]
135 for desc in list(test):
136 vuln['desc'] += self.parse_html_type(desc)
137 vulns.append(vuln)
138 return vulns
139
140 def get_vuln_definitions(self, tree):
141 """
142 @returns vulns A dict of Vulnerability Definitions
143 """
144 vulns = dict()
145 #CVSS V3
146 SEVERITY_MAPPING_DICT = {'0': 'info', '1': 'low', '2': 'low', '3': 'low', '4': 'med', '5': 'med', '6': 'med',
147 '7': 'high', '8': 'high', '9': 'critical', '10': 'critical'}
148
149 for vulnsDef in tree.iter('VulnerabilityDefinitions'):
150 for vulnDef in vulnsDef.iter('vulnerability'):
151 vid = vulnDef.get('id').lower()
152 vector = vulnDef.get('cvssVector')
153
154 vuln = {
155 'desc': "",
156 'name': vulnDef.get('title'),
157 'refs': ["vector: " + vector, vid],
158 'resolution': "",
159 'severity': SEVERITY_MAPPING_DICT[vulnDef.get('severity')],
160 'tags': list(),
161 'is_web': vid.startswith('http-')
162 }
163
164 for item in list(vulnDef):
165 if item.tag == 'description':
166 for htmlType in list(item):
167 vuln['desc'] += self.parse_html_type(htmlType)
168 if item.tag == 'exploits':
169 for exploit in list(item):
170 if exploit.get('title') and exploit.get('link'):
171 title = exploit.get('title').encode(
172 "ascii", errors="backslashreplace").strip()
173 link = exploit.get('link').encode(
174 "ascii", errors="backslashreplace").strip()
175 vuln['refs'].append(title + b' ' + link)
176 if item.tag == 'references':
177 for ref in list(item):
178 if ref.text:
179 rf = ref.text.encode(
180 "ascii", errors="backslashreplace").strip()
181 vuln['refs'].append(rf)
182 if item.tag == 'solution':
183 for htmlType in list(item):
184 vuln[
185 'resolution'] += self.parse_html_type(htmlType)
186 """
187 # there is currently no method to register tags in vulns
188 if item.tag == 'tags':
189 for tag in list(item):
190 vuln['tags'].append(tag.text.lower())
191 """
192 vulns[vid] = vuln
193 return vulns
194
195 def get_items(self, tree, vulns):
196 """
197 @return hosts A list of Host instances
198 """
199
200 hosts = list()
201
202 for nodes in tree.iter('nodes'):
203 for node in nodes.iter('node'):
204 host = dict()
205 host['name'] = node.get('address')
206 host['hostnames'] = list()
207 host['os'] = ""
208 host['services'] = list()
209 host['vulns'] = self.parse_tests_type(node, vulns)
210
211 for names in node.iter('names'):
212 for name in list(names):
213 host['hostnames'].append(name.text)
214
215 for fingerprints in node.iter('fingerprints'):
216 os = fingerprints.find('os')
217 if os is not None:
218 host['os'] = os.get('product', "")
219 if os.get('version') is not None:
220 host['os'] += " " + os.get('version')
221
222 for endpoints in node.iter('endpoints'):
223 for endpoint in list(endpoints):
224 svc = {
225 'protocol': endpoint.get('protocol'),
226 'port': endpoint.get('port'),
227 'status': endpoint.get('status'),
228 }
229 for services in endpoint.iter('services'):
230 for service in list(services):
231 svc['name'] = service.get('name')
232 svc['vulns'] = self.parse_tests_type(
233 service, vulns)
234 for configs in service.iter('configurations'):
235 for config in list(configs):
236 if "banner" in config.get('name'):
237 svc['version'] = config.get('name')
238
239 host['services'].append(svc)
240
241 hosts.append(host)
242
243 return hosts
244
245
246 class NexposeFullPlugin(PluginXMLFormat):
247 """
248 Example plugin to parse nexpose output.
249 """
250
251 def __init__(self):
252 super().__init__()
253 self.identifier_tag = "NexposeReport"
254 self.id = "NexposeFull"
255 self.name = "Nexpose XML 2.0 Report Plugin"
256 self.plugin_version = "0.0.1"
257 self.version = "Nexpose Enterprise 5.7.19"
258 self.framework_version = "1.0.0"
259 self.options = None
260 self._current_output = None
261 self._command_regex = re.compile(r'^(sudo nexpose|\.\/nexpose).*?')
262
263
264 def parseOutputString(self, output, debug=False):
265
266 parser = NexposeFullXmlParser(output)
267
268 for item in parser.items:
269
270 h_id = self.createAndAddHost(item['name'], item['os'], hostnames=item['hostnames'])
271
272 i_id = self.createAndAddInterface(
273 h_id,
274 item['name'],
275 ipv4_address=item['name'],
276 hostname_resolution=item['hostnames'])
277
278 for v in item['vulns']:
279
280 v_id = self.createAndAddVulnToHost(
281 h_id,
282 v['name'],
283 v['desc'],
284 v['refs'],
285 v['severity'],
286 v['resolution'])
287
288
289 for s in item['services']:
290 web = False
291 version = s.get("version", "")
292
293 s_id = self.createAndAddServiceToInterface(
294 h_id,
295 i_id,
296 s['name'],
297 s['protocol'],
298 ports=[str(s['port'])],
299 status=s['status'],
300 version=version)
301
302 for v in s['vulns']:
303 if v['is_web']:
304 v_id = self.createAndAddVulnWebToService(
305 h_id,
306 s_id,
307 v['name'],
308 v['desc'],
309 v['refs'],
310 v['severity'],
311 v['resolution'],
312 path=v.get('path',''))
313 else:
314 v_id = self.createAndAddVulnToService(
315 h_id,
316 s_id,
317 v['name'],
318 v['desc'],
319 v['refs'],
320 v['severity'],
321 v['resolution'])
322
323 del parser
324
325 def processCommandString(self, username, current_path, command_string):
326 return None
327
328 def setHost(self):
329 pass
330
331
332 def createPlugin():
333 return NexposeFullPlugin()
334
335 if __name__ == "__main__":
336 import sys
337 import os
338 if len(sys.argv) == 2:
339 report_file = sys.argv[1]
340 if os.path.isfile(report_file):
341 plugin = createPlugin()
342 plugin.processReport(report_file)
343 print(plugin.get_json())
344 else:
345 print(f"Report not found: {report_file}")
346 else:
347 print(f"USAGE {sys.argv[0]} REPORT_FILE")
348 # 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 # 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 os
7 import random
8 from html.parser import HTMLParser
9 from faraday_plugins.plugins.plugin import PluginXMLFormat
10 from faraday_plugins.plugins import plugins_utils
11
12
13 try:
14 import xml.etree.cElementTree as ET
15 import xml.etree.ElementTree as ET_ORIG
16 ETREE_VERSION = ET_ORIG.VERSION
17 except ImportError:
18 import xml.etree.ElementTree as ET
19 ETREE_VERSION = ET.VERSION
20
21 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
22
23 current_path = os.path.abspath(os.getcwd())
24
25 __author__ = "Francisco Amato"
26 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
27 __credits__ = ["Facundo de Guzmán", "Francisco Amato"]
28 __license__ = ""
29 __version__ = "1.0.0"
30 __maintainer__ = "Francisco Amato"
31 __email__ = "[email protected]"
32 __status__ = "Development"
33
34
35 class NiktoXmlParser:
36 """
37 The objective of this class is to parse an xml file generated by the nikto tool.
38
39 TODO: Handle errors.
40 TODO: Test nikto output version. Handle what happens if the parser doesn't support it.
41 TODO: Test cases.
42
43 @param nikto_xml_filepath A proper xml generated by nikto
44 """
45
46 def __init__(self, xml_output):
47
48 tree = self.parse_xml(xml_output)
49
50 if tree:
51 self.hosts = [host for host in self.get_hosts(tree)]
52 else:
53 self.hosts = []
54
55 def parse_xml(self, xml_output):
56 """
57 Open and parse an xml file.
58
59 TODO: Write custom parser to just read the nodes that we need instead of
60 reading the whole file.
61
62 @return xml_tree An xml tree instance. None if error.
63 """
64 try:
65 tree = ET.fromstring(xml_output)
66 except SyntaxError as err:
67 print("SyntaxError: %s. %s" % (err, xml_output))
68 return None
69
70 return tree
71
72 def get_hosts(self, tree):
73 """
74 @return items A list of Host instances
75 """
76 if tree.find('niktoscan'):
77 for host_node in tree.find('niktoscan').findall('scandetails'):
78 yield Host(host_node)
79 else:
80 for host_node in tree.findall('scandetails'):
81 yield Host(host_node)
82
83
84 def get_attrib_from_subnode(xml_node, subnode_xpath_expr, attrib_name):
85 """
86 Finds a subnode in the item node and the retrieves a value from it
87
88 @return An attribute value
89 """
90 global ETREE_VERSION
91 node = None
92
93 if ETREE_VERSION[0] <= 1 and ETREE_VERSION[1] < 3:
94
95 match_obj = re.search(
96 "([^\@]+?)\[\@([^=]*?)=\'([^\']*?)\'", subnode_xpath_expr)
97 if match_obj is not None:
98
99 node_to_find = match_obj.group(1)
100 xpath_attrib = match_obj.group(2)
101 xpath_value = match_obj.group(3)
102 for node_found in xml_node.findall(node_to_find):
103
104 if node_found.attrib[xpath_attrib] == xpath_value:
105 node = node_found
106 break
107 else:
108 node = xml_node.find(subnode_xpath_expr)
109
110 else:
111 node = xml_node.find(subnode_xpath_expr)
112
113 if node is not None:
114 return node.get(attrib_name)
115
116 return None
117
118
119 class Item:
120 """
121 An abstract representation of a Item
122
123 TODO: Consider evaluating the attributes lazily
124 TODO: Write what's expected to be present in the nodes
125 TODO: Refactor both Host and the Port clases?
126
127 @param item_node A item_node taken from an nikto xml tree
128 """
129
130 def __init__(self, item_node):
131
132 self.node = item_node
133
134 self.osvdbid = [
135 "OSVDB-ID: " + self.node.get('osvdbid')] if self.node.get('osvdbid') != "0" else []
136
137 self.namelink = self.get_text_from_subnode('namelink')
138 self.iplink = self.get_text_from_subnode('iplink')
139
140 self.id_nikto = self.node.get('id')
141 self.osvdblink = self.node.get('osvdbidlink')
142 self.method = self.node.get('method')
143
144 self.uri = self.get_uri()
145 self.desc = self.get_desc()
146 self.params = self.get_params(self.uri)
147
148 def get_uri(self):
149
150 try:
151
152 uri = self.get_text_from_subnode('uri')
153 h = HTMLParser.HTMLParser()
154 return h.unescape(uri)
155
156 except Exception as e:
157 return uri
158
159 def get_desc(self):
160
161 desc = self.get_text_from_subnode('description')
162
163 try:
164
165 uri_present = desc.split(': ', 1)[0]
166 h = HTMLParser.HTMLParser()
167 if uri_present == h.unescape(self.uri):
168
169 name = desc.split(': ', 1)[1]
170 if name is not None and name != '':
171 return name
172
173 return desc
174
175 except Exception as e:
176 return desc
177
178 def get_params(self, uri):
179 """Return the paramethers as a string"""
180 try:
181 params = uri.split('?')[1].replace('&', ',')
182 except Exception as e:
183 params = ''
184
185 return params
186
187 def get_text_from_subnode(self, subnode_xpath_expr):
188 """
189 Finds a subnode in the host node and the retrieves a value from it.
190
191 @return An attribute value
192 """
193 sub_node = self.node.find(subnode_xpath_expr)
194 if sub_node is not None:
195 return sub_node.text
196
197 return None
198
199 def __str__(self):
200 ports = []
201 for port in self.ports:
202 var = " %s" % port
203 ports.append(var)
204 ports = "\n".join(ports)
205
206 return "%s, %s, %s [%s], %s\n%s" % (self.hostnames, self.status,
207 self.ipv4_address, self.mac_address, self.os, ports)
208
209
210 class Host:
211 """
212 An abstract representation of a Host
213
214 @param host_node A host_node taken from an nmap xml tree
215 """
216
217 def __init__(self, host_node):
218
219 self.node = host_node
220 self.targetip = self.node.get('targetip')
221 self.targethostname = self.node.get('targethostname')
222 self.port = self.node.get('targetport')
223 self.targetbanner = self.node.get('targetbanner')
224 self.starttime = self.node.get('starttime')
225 self.sitename = self.node.get('sitename')
226 self.siteip = self.node.get('hostheader')
227 self.items = [item for item in self.get_items()]
228
229 def get_items(self):
230 """
231 @return items A list of Host instances
232 """
233 for item_node in self.node.findall('item'):
234 yield Item(item_node)
235
236 def __str__(self):
237 ports = []
238 for port in self.ports:
239 var = " %s" % port
240 ports.append(var)
241 ports = "\n".join(ports)
242
243 return "%s, %s, %s [%s], %s\n%s" % (self.hostnames, self.status,
244 self.ipv4_address, self.mac_address, self.os, ports)
245
246
247 class NiktoPlugin(PluginXMLFormat):
248 """
249 Example plugin to parse nikto output.
250 """
251
252 def __init__(self):
253 super().__init__()
254 self.identifier_tag = "niktoscan"
255 self.id = "Nikto"
256 self.name = "Nikto XML Output Plugin"
257 self.plugin_version = "0.0.2"
258 self.version = "2.1.5"
259 self.options = None
260 self._current_output = None
261 self.parent = None
262 self._command_regex = re.compile(
263 r'^(sudo nikto|nikto|sudo nikto\.pl|nikto\.pl|perl nikto\.pl|\.\/nikto\.pl|\.\/nikto).*?')
264 self._completition = {
265 "": "",
266 "-ask+": "Whether to ask about submitting updates",
267 "-Cgidirs+": 'Scan these CGI dirs: "none", "all", or values like "/cgi/ /cgi-a/"',
268 "-config+": "Use this config file",
269 "-Display+": "Turn on/off display outputs:",
270 "-dbcheck": "Check database and other key files for syntax errors",
271 "-evasion+": "Encoding technique:",
272 "-Format+": "Save file (-o) format:",
273 "-Help": "Extended help information",
274 "-host+": "Target host",
275 "-IgnoreCode": "Ignore Codes--treat as negative responses",
276 "-id+": "Host authentication to use, format is id:pass or id:pass:realm",
277 "-key+": "Client certificate key file",
278 "-list-plugins": "List all available plugins, perform no testing",
279 "-maxtime+": "Maximum testing time per host",
280 "-mutate+": "Guess additional file names:",
281 "-mutate-options": "Provide information for mutates",
282 "-nointeractive": "Disables interactive features",
283 "-nolookup": "Disables DNS lookups",
284 "-nossl": "Disables the use of SSL",
285 "-no404": "Disables nikto attempting to guess a 404 page",
286 "-output+": "Write output to this file ('.' for auto-name)",
287 "-Pause+": "Pause between tests (seconds, integer or float)",
288 "-Plugins+": "List of plugins to run (default: ALL)",
289 "-port+": "Port to use (default 80)",
290 "-RSAcert+": "Client certificate file",
291 "-root+": "Prepend root value to all requests, format is /directory",
292 "-Save": "Save positive responses to this directory ('.' for auto-name)",
293 "-ssl": "Force ssl mode on port",
294 "-Tuning+": "Scan tuning:",
295 "-timeout+": "Timeout for requests (default 10 seconds)",
296 "-Userdbs": "Load only user databases, not the standard databases",
297 "-until": "Run until the specified time or duration",
298 "-update": "Update databases and plugins from CIRT.net",
299 "-useproxy": "Use the proxy defined in nikto.conf",
300 "-Version": "Print plugin and database versions",
301 "-vhost+": "Virtual host (for Host header)",
302 }
303
304
305
306 def parseOutputString(self, output, debug=False):
307 """
308 This method will discard the output the shell sends, it will read it from
309 the xml where it expects it to be present.
310
311 NOTE: if 'debug' is true then it is being run from a test case and the
312 output being sent is valid.
313 """
314
315 parser = NiktoXmlParser(output)
316
317 for host in parser.hosts:
318
319 h_id = self.createAndAddHost(
320 host.targetip,
321 hostnames=[host.targethostname]
322 )
323
324 s_id = self.createAndAddServiceToHost(
325 h_id,
326 "http",
327 "tcp",
328 ports=[host.port],
329 status="open"
330 )
331
332 for item in host.items:
333
334 v_id = self.createAndAddVulnWebToService(
335 h_id,
336 s_id,
337 name=item.desc,
338 ref=item.osvdbid,
339 method=item.method,
340 params=', '.join(item.params),
341 **plugins_utils.get_vulnweb_url_fields(item.namelink)
342 )
343
344 del parser
345
346 xml_arg_re = re.compile(r"^.*(-output\s*[^\s]+).*$")
347
348 def processCommandString(self, username, current_path, command_string):
349 """
350 Adds the -oX parameter to get xml output to the command string that the
351 user has set.
352 """
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 )
361
362 arg_match = self.xml_arg_re.match(command_string)
363
364 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)
368 else:
369 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)
373
374 def setHost(self):
375 pass
376
377
378 def createPlugin():
379 return NiktoPlugin()
380
381 if __name__ == "__main__":
382 import sys
383 import os
384 if len(sys.argv) == 2:
385 report_file = sys.argv[1]
386 if os.path.isfile(report_file):
387 plugin = createPlugin()
388 plugin.processReport(report_file)
389 print(plugin.get_json())
390 else:
391 print(f"Report not found: {report_file}")
392 else:
393 print(f"USAGE {sys.argv[0]} REPORT_FILE")
394 # 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 # 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 PluginXMLFormat
8 import re
9 import os
10 import sys
11 import random
12
13
14 try:
15 import xml.etree.cElementTree as ET
16 import xml.etree.ElementTree as ET_ORIG
17 ETREE_VERSION = ET_ORIG.VERSION
18 except ImportError:
19 import xml.etree.ElementTree as ET
20 ETREE_VERSION = ET.VERSION
21
22 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
23
24 current_path = os.path.abspath(os.getcwd())
25
26
27 class NmapXmlParser:
28 """
29 The objective of this class is to parse an xml file generated by
30 the nmap tool.
31
32 TODO: Handle errors.
33 TODO: Test nmap output version. Handle what happens if the parser
34 doesn't support it.
35 TODO: Test cases.
36
37 @param nmap_xml_filepath A proper xml generated by nmap
38 """
39
40 def __init__(self, xml_output):
41 tree = self.parse_xml(xml_output)
42
43 if tree:
44 self.hosts = [host for host in self.get_hosts(tree)]
45 else:
46 self.hosts = []
47
48 def parse_xml(self, xml_output):
49 """
50 Open and parse an xml file.
51
52 TODO: Write custom parser to just read the nodes that we need instead
53 of reading the whole file.
54
55 @return xml_tree An xml tree instance. None if error.
56 """
57
58 try:
59 return ET.fromstring(xml_output)
60 except SyntaxError as err:
61 #logger.error("SyntaxError: %s." % (err))
62 return None
63
64 def get_hosts(self, tree):
65 """
66 @return hosts A list of Host instances
67 """
68 for host_node in tree.findall('host'):
69 yield Host(host_node)
70
71
72 def get_attrib_from_subnode(xml_node, subnode_xpath_expr, attrib_name):
73 """
74 Finds a subnode in the host node and the retrieves a value from it
75
76 @return An attribute value
77 """
78 global ETREE_VERSION
79 node = None
80
81 if ETREE_VERSION[0] <= 1 and ETREE_VERSION[1] < 3:
82
83 match_obj = re.search(
84 "([^\@]+?)\[\@([^=]*?)=\'([^\']*?)\'",
85 subnode_xpath_expr)
86
87 if match_obj is not None:
88
89 node_to_find = match_obj.group(1)
90 xpath_attrib = match_obj.group(2)
91 xpath_value = match_obj.group(3)
92
93 for node_found in xml_node.findall(node_to_find):
94 if node_found.attrib[xpath_attrib] == xpath_value:
95 node = node_found
96 break
97 else:
98 node = xml_node.find(subnode_xpath_expr)
99
100 else:
101 node = xml_node.find(subnode_xpath_expr)
102
103 if node is not None:
104 return node.get(attrib_name)
105
106 return None
107
108
109 class Host:
110 """
111 An abstract representation of a Host
112
113 TODO: Consider evaluating the attributes lazily
114 TODO: Write what's expected to be present in the nodes
115 TODO: Refactor both Host and the Port clases?
116
117 @param host_node A host_node taken from an nmap xml tree
118 """
119
120 def __init__(self, host_node):
121 self.node = host_node
122
123 self.hostnames = [hostname[0] for hostname in self.get_hostnames()]
124 if len(self.hostnames) != 0:
125 self.hostname = self.hostnames[0]
126 else:
127 self.hostname = 'unknown'
128
129 self.hostnames = list(set(self.hostnames))
130 self.status = self.get_status()
131 self.ipv4_address = self.get_ipv4_address()
132 self.ipv6_address = self.get_ipv6_address()
133 self.mac_address = self.get_mac_address()
134 self.os_guesses = [os_guess for os_guess in self.get_os_guesses()]
135 self.os = self.top_os_guess()
136 self.ports = [port for port in self.get_ports()]
137 self.vulns = [vuln for vuln in self.get_scripts()]
138 if self.os != 'unknown':
139 for p in self.ports:
140 if p.service is not None:
141 if p.service.ostype:
142 self.os = p.service.ostype
143 break
144
145 def get_hostnames(self):
146 """
147 Expects to find one or more
148 '<hostname name="localhost.localdomain" type="PTR"/>' in the host node.
149
150 @return A list of (hostname, hostname_type) or None
151 """
152 for hostname in self.node.findall('hostnames/hostname'):
153 yield (hostname.attrib["name"], hostname.attrib["type"])
154
155 def get_attrib_from_subnode(self, subnode_xpath_expr, attrib_name):
156 """
157 Finds a subnode in the host node and the retrieves a value from it
158
159 @return An attribute value
160 """
161 return get_attrib_from_subnode(
162 self.node,
163 subnode_xpath_expr,
164 attrib_name)
165
166 def get_status(self):
167 """
168 Expects to find '<status state="up" reason="conn-refused"/>'
169 in the node
170 TODO: Use 'reason'
171 @return An status or 'unknown'
172 """
173 status = self.get_attrib_from_subnode('status', 'state')
174
175 return status if status else 'unknown'
176
177 def get_ipv4_address(self):
178 """
179 Expects to find '<address addr="127.0.0.1" addrtype="ipv4"/>'
180 in the node
181
182 @return ip_address or 'unknown'
183 """
184 ip_address = self.get_attrib_from_subnode(
185 "address[@addrtype='ipv4']",
186 'addr')
187 return ip_address if ip_address else 'unknown'
188
189 def get_ipv6_address(self):
190 """
191 Expects to find '<address addr="127.0.0.1" addrtype="ipv6"/>'
192 in the node
193
194 @return ip_address or 'unknown'
195 """
196 ip_address = self.get_attrib_from_subnode(
197 "address[@addrtype='ipv6']",
198 'addr')
199 return ip_address if ip_address else 'unknown'
200
201 def get_mac_address(self):
202 """
203 Expects to find
204 '<address addr="00:08:54:26:A9:E5" addrtype="mac" vendor="Netronix" />'
205 in the node
206
207 @return mac_address or 'unknown'
208 """
209 mac_address = self.get_attrib_from_subnode(
210 "address[@addrtype='mac']",
211 'addr')
212 return mac_address if mac_address else 'unknown'
213
214 def get_os_guesses(self):
215 """
216 Expects to find
217 '<os>..<osclass type="general purpose" vendor="Microsoft"
218 osfamily="Windows" osgen="2003" accuracy="96" />..</os>' in the node
219
220 @return A list of (os_vendor_family_gen, accuracy)
221 """
222 # OS information about host with great acurracy.
223
224 osclasses = self.node.findall('os/osclass')
225 if osclasses == []:
226 osclasses = self.node.findall('os/osmatch/osclass')
227
228 for osclass in osclasses:
229 os_vendor = osclass.get("vendor", "unknown")
230 os_family = osclass.get("osfamily", "unknown")
231 os_gen = osclass.get("osgen", "unknown")
232 accuracy = osclass.get("accuracy", "unknown")
233
234 yield ("%s %s %s" % (os_vendor, os_family, os_gen), accuracy)
235
236 # Os information in services, bad acurracy.
237 if osclasses == []:
238 services = self.node.findall("ports/port/service")
239 for service in services:
240 ostype = service.get("ostype", "unknown")
241 yield ("%s" % ostype, 0)
242
243
244 def top_os_guess(self):
245 """
246 @return The most accurate os_guess_id or 'unknown'.
247 """
248 return self.os_guesses[0][0] if len(self.os_guesses) != 0 else 'unknown'
249
250 def get_scripts(self):
251 # Expects to find a scripts in the node.
252 for s in self.node.findall('hostscript/script'):
253 yield Script(s)
254
255 def get_ports(self):
256 """
257 Expects to find one or more
258 '<port protocol="tcp" portid="631">...</port>' in the node.
259
260 @return A list of Port instances or None
261 """
262 for port in self.node.findall('ports/port'):
263 yield Port(port)
264
265 def is_up(self):
266 """
267 Returns True if the host is up else False.
268 """
269 if self.status == 'up':
270 return True
271 else:
272 return False
273
274 def __str__(self):
275 ports = []
276 for port in self.ports:
277 var = " %s" % port
278 ports.append(var)
279 ports = "\n".join(ports)
280
281 return "%s, %s, %s [%s], %s\n%s" % (
282 self.hostnames,
283 self.status,
284 self.ipv4_address,
285 self.mac_address,
286 self.os, ports)
287
288
289 class Port:
290 """
291 An abstract representation of a Port.
292
293 @param port_node A port_node taken from an nmap xml tree
294 """
295
296 def __init__(self, port_node):
297 self.node = port_node
298
299 self.protocol = self.node.get("protocol")
300 self.number = self.node.get("portid")
301 self.state, self.reason, self.reason_ttl = self.get_state()
302 self.service = self.get_service()
303 self.vulns = [vuln for vuln in self.get_scripts()]
304
305 def get_attrib_from_subnode(self, subnode_xpath_expr, attrib_name):
306 """
307 Finds a subnode in the host node and the retrieves a value from it.
308
309 @return An attribute value
310 """
311 return get_attrib_from_subnode(
312 self.node,
313 subnode_xpath_expr,
314 attrib_name)
315
316 def get_state(self):
317 """
318 Expects to find a
319 '<state state="open" reason="syn-ack" reason_ttl="0"/>' in the node.
320
321 @return (state, reason, reason_ttl) or ('unknown','unknown','unknown')
322 """
323 state = self.get_attrib_from_subnode('state', 'state')
324 reason = self.get_attrib_from_subnode('state', 'reason')
325 reason_ttl = self.get_attrib_from_subnode('state', 'reason_ttl')
326
327 return (state if state else 'unknown',
328 reason if reason else 'unknown',
329 reason_ttl if reason_ttl else 'unknown')
330
331 def get_service(self):
332 """
333 Expects to find a service in the node.
334 """
335 service_node = self.node.find('service')
336 if service_node is not None:
337 return Service(service_node)
338
339 return None
340
341 def get_scripts(self):
342 """
343 Expects to find a scripts in the node.
344 """
345 for s in self.node.findall('script'):
346 yield Script(s)
347
348 def __str__(self):
349 return "%s, %s, Service: %s" % (self.number, self.state, self.service)
350
351
352 class Script:
353 """
354 An abstract representation of a Script.
355
356 '<script id="http-methods" output="No Allow or Public header in OPTIONS
357 response (status code 400)"/><script id="http-title"
358 output="Document Error: Unauthorized"><elem key="title">
359 Document Error: Unauthorized</elem></script>'
360
361 @param script_node A script_node taken from an nmap xml tree
362 """
363
364 def parse_output(self, output):
365 block_re = re.compile('^\s{4}References:((?:.|[\r\n])+[\r\n](?:\s{4}\w|\s*$))', re.MULTILINE)
366 m1 = block_re.findall(output)
367 if len(m1) > 0:
368 links_re = re.compile('[ \t]+([^ \t\n\r]+)[ \t]*')
369 m2 = links_re.findall(m1[0])
370 return m2
371 return []
372
373 def __init__(self, script_node):
374 self.node = script_node
375
376 self.name = script_node.get("id")
377 self.desc = script_node.get("output")
378 self.refs = self.parse_output(self.desc)
379 self.response = ""
380 for k in script_node.findall("elem"):
381 self.response += "\n" + str(k.get('key')) + ": " + str(k.text)
382 self.web = re.search("(http-|https-)", self.name)
383
384 def __str__(self):
385 return "%s, %s, %s" % (self.name, self.product, self.version)
386
387
388 class Service:
389 """
390 An abstract representation of a Service.
391
392 '<service name="ipp" product="CUPS" version="1.4" method="probed"
393 conf="10"/>'
394
395 @param service_node A service_node taken from an nmap xml tree
396 """
397
398 def __init__(self, service_node):
399 self.node = service_node
400
401 name = service_node.get("name")
402 self.name = name if name else 'unknown'
403
404 product = service_node.get("product")
405 self.product = product if product else 'unknown'
406
407 version = service_node.get("version")
408 self.version = version if version else 'unknown'
409
410 self.method = service_node.get("method")
411 self.conf = service_node.get("conf")
412 self.ostype = self.node.get("ostype")
413
414 def __str__(self):
415 return "%s, %s, %s" % (self.name, self.product, self.version)
416
417
418 class NmapPlugin(PluginXMLFormat):
419 """
420 Example plugin to parse nmap output.
421 """
422
423 def __init__(self):
424 super().__init__()
425 self.identifier_tag = "nmaprun"
426 self.id = "Nmap"
427 self.name = "Nmap XML Output Plugin"
428 self.plugin_version = "0.0.3"
429 self.version = "6.40"
430 self.framework_version = "1.0.0"
431 self.options = None
432 self._current_output = None
433 self._command_regex = re.compile(r'^(sudo nmap|nmap|\.\/nmap).*?')
434
435
436
437 self.xml_arg_re = re.compile(r"^.*(-oX\s*[^\s]+).*$")
438 self.addSetting("Scan Technique", str, "-sS")
439
440 def parseOutputString(self, output, debug=False):
441 """
442 This method will discard the output the shell sends, it will read it
443 from the xml where it expects it to be present.
444
445 NOTE: if 'debug' is true then it is being run from a test case and the
446 output being sent is valid.
447 """
448
449 parser = NmapXmlParser(output)
450
451 for host in parser.hosts:
452 # if not host.is_up():
453 # continue
454
455 if host.mac_address == 'unknown':
456 host.mac_address = "00:00:00:00:00:00"
457
458 if host.ipv4_address != 'unknown':
459 minterfase = host.ipv4_address
460 h_id = self.createAndAddHost(minterfase, host.os)
461 i_id = self.createAndAddInterface(
462 h_id,
463 minterfase,
464 host.mac_address,
465 ipv4_address=host.ipv4_address,
466 hostname_resolution=host.hostnames)
467 else:
468 minterfase = host.ipv6_address
469 h_id = self.createAndAddHost(minterfase, host.os)
470 i_id = self.createAndAddInterface(
471 h_id,
472 minterfase,
473 host.mac_address,
474 ipv6_address=host.ipv6_address,
475 hostname_resolution=host.hostnames)
476
477 for v in host.vulns:
478 desc = v.desc
479 desc += "\nOutput: " + v.response if v.response else ""
480
481 v_id = self.createAndAddVulnToHost(
482 h_id,
483 v.name,
484 desc=v.desc,
485 ref=v.refs,
486 severity=0,
487 external_id=v.name)
488
489 for port in host.ports:
490
491 srvname = str(port.number)
492 srvversion = "unknown"
493 if port.service is not None:
494 srvname = port.service.name
495 srvversion = port.service.product if port.service.product != "unknown" else ""
496 srvversion += " " + port.service.version if port.service.version != "unknown" else ""
497
498 s_id = self.createAndAddServiceToInterface(
499 h_id,
500 i_id,
501 srvname,
502 port.protocol,
503 ports=[port.number],
504 status=port.state,
505 version=srvversion,
506 description=srvname)
507
508 for v in port.vulns:
509 severity = 0
510 desc = v.desc
511 refs = v.refs
512
513 if re.search(r"VULNERABLE", desc):
514 severity = "high"
515 if re.search(r"ERROR", desc):
516 severity = "unclassified"
517 if re.search(r"Couldn't", desc):
518 severity = "unclassified"
519 if v.web:
520 v_id = self.createAndAddVulnWebToService(
521 h_id,
522 s_id,
523 v.name,
524 desc=desc,
525 response = v.response if v.response else "",
526 ref=refs,
527 severity=severity,
528 website=minterfase,
529 external_id=v.name)
530 else:
531 v_id = self.createAndAddVulnToService(
532 h_id,
533 s_id,
534 v.name,
535 desc=v.desc,
536 ref=refs,
537 severity=severity,
538 external_id=v.name)
539 del parser
540 return True
541
542 def processCommandString(self, username, current_path, command_string):
543 """
544 Adds the -oX parameter to get xml output to the command string that the
545 user has set.
546 """
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
556 arg_match = self.xml_arg_re.match(command_string)
557
558 if arg_match is None:
559 return re.sub(r"(^.*?nmap)",
560 r"\1 -oX %s" % self._output_file_path,
561 command_string)
562 else:
563 return re.sub(arg_match.group(1),
564 r"-oX %s" % self._output_file_path,
565 command_string)
566
567 def setHost(self):
568 pass
569
570
571 def createPlugin():
572 return NmapPlugin()
573
574 if __name__ == "__main__":
575 import sys
576 import os
577 if len(sys.argv) == 2:
578 report_file = sys.argv[1]
579 if os.path.isfile(report_file):
580 plugin = createPlugin()
581 plugin.processReport(report_file)
582 print(plugin.get_json())
583 else:
584 print(f"Report not found: {report_file}")
585 else:
586 print(f"USAGE {sys.argv[0]} REPORT_FILE")
587 # 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 # 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 import re
7 import os
8 from collections import defaultdict
9
10 try:
11 import xml.etree.cElementTree as ET
12 import xml.etree.ElementTree as ET_ORIG
13 ETREE_VERSION = ET_ORIG.VERSION
14 except ImportError:
15 import xml.etree.ElementTree as ET
16 ETREE_VERSION = ET.VERSION
17
18 from faraday_plugins.plugins.plugin import PluginXMLFormat
19 from faraday_plugins.plugins.plugins_utils import filter_services
20
21 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
22
23 current_path = os.path.abspath(os.getcwd())
24
25 __author__ = "Francisco Amato"
26 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
27 __credits__ = ["Francisco Amato"]
28 __license__ = ""
29 __version__ = "1.0.0"
30 __maintainer__ = "Francisco Amato"
31 __email__ = "[email protected]"
32 __status__ = "Development"
33
34
35 class OpenvasXmlParser:
36 """
37 The objective of this class is to parse an xml file generated by the openvas tool.
38
39 TODO: Handle errors.
40 TODO: Test openvas output version. Handle what happens if the parser doesn't support it.
41 TODO: Test cases.
42
43 @param openvas_xml_filepath A proper xml generated by openvas
44 """
45
46 def __init__(self, xml_output, logger):
47 self.target = None
48 self.port = "80"
49 self.host = None
50 self.logger = logger
51 tree = self.parse_xml(xml_output)
52 if tree:
53 self.hosts = self.get_hosts(tree)
54 self.items = list(self.get_items(tree, self.hosts))
55 else:
56 self.items = []
57
58 def parse_xml(self, xml_output):
59 """
60 Open and parse an xml file.
61
62 TODO: Write custom parser to just read the nodes that we need instead of
63 reading the whole file.
64
65 @return xml_tree An xml tree instance. None if error.
66 """
67 try:
68 tree = ET.fromstring(xml_output)
69 except SyntaxError as err:
70 self.logger.error("SyntaxError: %s. %s", err, xml_output)
71 return None
72 return tree
73
74 def get_items(self, tree, hosts):
75 """
76 @return items A list of Host instances
77 """
78 try:
79 report = tree.find('report')
80 results = report.findall('results')
81 if results:
82 nodes = report.findall('results')[0]
83 else:
84 nodes = tree.findall('result')
85 for node in nodes:
86 try:
87 yield Item(node, hosts)
88 except Exception as e:
89 self.logger.error("Error generating Item from %s [%s]", node.attrib, e)
90 except Exception as e:
91 self.logger.error("Tag not found: %s", e)
92
93 def get_hosts(self, tree):
94 # Hosts are located in: /report/report/host
95 # hosts_dict will contain has keys its details and its hostnames
96 hosts = tree.findall('report/host')
97 hosts_dict = {}
98 for host in hosts:
99 ip = self.do_clean(host.find('ip').text)
100 details = self.get_data_from_detail(host.findall('detail'))
101 hosts_dict[ip] = details
102 return hosts_dict
103
104 def get_data_from_detail(self, details):
105 data = {}
106 details_data = defaultdict(list)
107 hostnames = []
108 for item in details:
109 name = self.do_clean(item.find('name').text)
110 value = self.do_clean(item.find('value').text)
111 if "EXIT" in name:
112 continue
113 if name == 'hostname':
114 hostnames.append(value)
115 else:
116 details_data[name].append(value)
117 data['details'] = details_data
118 data['hostnames'] = hostnames
119 return data
120
121 def do_clean(self, value):
122 myreturn = ""
123 if value is not None:
124 myreturn = re.sub("\s+", " ", value)
125 return myreturn.strip()
126
127
128 def get_attrib_from_subnode(xml_node, subnode_xpath_expr, attrib_name):
129 """
130 Finds a subnode in the item node and the retrieves a value from it
131
132 @return An attribute value
133 """
134 global ETREE_VERSION
135 node = None
136
137 if ETREE_VERSION[0] <= 1 and ETREE_VERSION[1] < 3:
138
139 match_obj = re.search(
140 "([^\@]+?)\[\@([^=]*?)=\'([^\']*?)\'",
141 subnode_xpath_expr)
142
143 if match_obj is not None:
144 node_to_find = match_obj.group(1)
145 xpath_attrib = match_obj.group(2)
146 xpath_value = match_obj.group(3)
147 for node_found in xml_node.findall(node_to_find):
148 if node_found.attrib[xpath_attrib] == xpath_value:
149 node = node_found
150 break
151 else:
152 node = xml_node.find(subnode_xpath_expr)
153
154 else:
155 node = xml_node.find(subnode_xpath_expr)
156
157 if node is not None:
158 return node.get(attrib_name)
159
160 return None
161
162
163 class Item:
164 """
165 An abstract representation of a Item
166 @param item_node A item_node taken from an openvas xml tree
167 """
168
169 def __init__(self, item_node, hosts):
170 self.node = item_node
171 self.host = self.get_text_from_subnode('host')
172 self.subnet = self.get_text_from_subnode('subnet')
173 if self.subnet == '':
174 self.subnet = self.host
175 self.port = "None"
176 self.severity = self.severity_mapper()
177 self.service = "Unknown"
178 self.protocol = ""
179 port = self.get_text_from_subnode('port')
180
181 if "general" not in port:
182 # service vuln
183 info = port.split("/")
184 self.port = info[0]
185 self.protocol = info[1]
186 host_details = hosts[self.host].get('details')
187 self.service = self.get_service(port, host_details)
188 else:
189 # general was found in port data
190 # this is a host vuln
191 # this case will have item.port = 'None'
192 info = port.split("/")
193 self.protocol = info[1]
194 self.service = info[0] # this value is general
195 self.nvt = self.node.findall('nvt')[0]
196 self.node = self.nvt
197 self.id = self.node.get('oid')
198 self.name = self.get_text_from_subnode('name')
199 self.cve = self.get_text_from_subnode('cve') if self.get_text_from_subnode('cve') != "NOCVE" else ""
200 self.bid = self.get_text_from_subnode('bid') if self.get_text_from_subnode('bid') != "NOBID" else ""
201 self.xref = self.get_text_from_subnode('xref') if self.get_text_from_subnode('xref') != "NOXREF" else ""
202 self.description = ''
203 self.resolution = ''
204 self.cvss_vector = ''
205 self.tags = self.get_text_from_subnode('tags')
206 if self.tags:
207 tags_data = self.get_data_from_tags(self.tags)
208 self.description = tags_data['description']
209 self.resolution = tags_data['solution']
210 self.cvss_vector = tags_data['cvss_base_vector']
211
212
213 def get_text_from_subnode(self, subnode_xpath_expr):
214 """
215 Finds a subnode in the host node and the retrieves a value from it.
216
217 @return An attribute value
218 """
219 sub_node = self.node.find(subnode_xpath_expr)
220 if sub_node is not None and sub_node.text is not None:
221 return sub_node.text.strip()
222 return ''
223
224 def severity_mapper(self):
225 severity = self.get_text_from_subnode('threat')
226 if severity == 'Alarm':
227 severity = 'Critical'
228 return severity
229
230 def get_service(self, port, details_from_host):
231 # details_from_host:
232 # name: name of detail
233 # value: list with the values associated with the name
234 for name, value in details_from_host.items():
235 service_detail = self.get_service_from_details(name, value, port)
236 if service_detail:
237 return service_detail
238 # if the service is not in details_from_host, we will search it in
239 # the file port_mapper.txt
240 services_mapper = filter_services()
241 for service in services_mapper:
242 if service[0] == port:
243 return service[1]
244
245 return "Unknown"
246
247 def do_clean(self, value):
248 myreturn = ""
249 if value is not None:
250 myreturn = re.sub("\s+", " ", value)
251
252 return myreturn.strip()
253
254 def get_service_from_details(self, name, value_list, port):
255 # detail:
256 # name: name of detail
257 # value_list: list with the values associated with the name
258 res = None
259 priority = 0
260
261 for value in value_list:
262 if name == 'Services':
263 aux_port = port.split('/')[0]
264 value_splited = value.split(',')
265 if value_splited[0] == aux_port:
266 res = value_splited[2]
267 priority = 3
268
269 elif '/' in value and priority != 3:
270 auxiliar_value = value.split('/')[0]
271 if auxiliar_value == port.split('/')[0]:
272 res = name
273 priority = 2
274
275 elif value.isdigit() and priority == 0:
276 if value == port.split('/')[0]:
277 res = name
278 priority = 1
279
280 elif '::' in value and priority == 0:
281 aux_value = value.split('::')[0]
282 auxiliar_port = port.split('/')[0]
283 if aux_value == auxiliar_port:
284 res = name
285 return res
286
287 def get_data_from_tags(self, tags_text):
288 clean_text = self.do_clean(tags_text)
289 tags = clean_text.split('|')
290 summary = ''
291 insight = ''
292 data = {
293 'solution': '',
294 'cvss_base_vector': '',
295 'description': ''
296 }
297 for tag in tags:
298 splited_tag = tag.split('=', 1)
299 if splited_tag[0] in data.keys():
300 data[splited_tag[0]] = splited_tag[1]
301 elif splited_tag[0] == 'summary':
302 summary = splited_tag[1]
303 elif splited_tag[0] == 'insight':
304 insight = splited_tag[1]
305
306 data['description'] = ' '.join([summary, insight]).strip()
307
308 return data
309
310
311 class OpenvasPlugin(PluginXMLFormat):
312 """
313 Example plugin to parse openvas output.
314 """
315
316 def __init__(self):
317 super().__init__()
318 self.identifier_tag = "report"
319 self.id = "Openvas"
320 self.name = "Openvas XML Output Plugin"
321 self.plugin_version = "0.3"
322 self.version = "9.0.3"
323 self.framework_version = "1.0.0"
324 self.options = None
325 self._current_output = None
326 self.target = None
327 self._command_regex = re.compile(
328 r'^(openvas|sudo openvas|\.\/openvas).*?')
329
330
331 def report_belongs_to(self, **kwargs):
332 if super().report_belongs_to(**kwargs):
333 report_path = kwargs.get("report_path", "")
334 with open(report_path) as f:
335 output = f.read()
336 return re.search("OpenVAS", output) is not None or re.search('<omp>', output) is not None
337 return False
338
339 def parseOutputString(self, output, debug=False):
340 """
341 This method will discard the output the shell sends, it will read it
342 from the xml where it expects it to be present.
343
344 NOTE: if 'debug' is true then it is being run from a test case and the
345 output being sent is valid.
346 """
347 parser = OpenvasXmlParser(output, self.logger)
348 web = False
349 ids = {}
350 # The following threats values will not be taken as vulns
351 self.ignored_severities = ['Log', 'Debug']
352 for ip, values in parser.hosts.items():
353 # values contains: ip details and ip hostnames
354 h_id = self.createAndAddHost(
355 ip,
356 hostnames=values['hostnames']
357 )
358 ids[ip] = h_id
359
360 for item in parser.items:
361 if item.name is not None:
362 ref = []
363 if item.cve:
364 cves = item.cve.split(',')
365 for cve in cves:
366 ref.append(cve.encode("utf-8").strip())
367 if item.bid:
368 bids = item.bid.split(',')
369 for bid in bids:
370 ref.append("BID-%s" % bid.encode("utf-8").strip() )
371 if item.xref:
372 ref.append(item.xref.encode("utf-8"))
373 if item.tags and item.cvss_vector:
374 ref.append(item.cvss_vector.encode("utf-8"))
375
376 if item.subnet in ids:
377 h_id = ids[item.host]
378 else:
379 h_id = self.createAndAddHost(
380 item.subnet,
381 hostnames=[item.host])
382 ids[item.subnet] = h_id
383
384 if item.port == "None":
385 if item.severity not in self.ignored_severities:
386 v_id = self.createAndAddVulnToHost(
387 h_id,
388 item.name,
389 desc=item.description,
390 severity=item.severity,
391 resolution=item.resolution,
392 ref=ref,
393 external_id=item.id)
394 else:
395 if item.service:
396 web = re.search(
397 r'^(www|http)',
398 item.service)
399 else:
400 web = item.port in ('80', '443', '8080')
401
402 if item.subnet + "_" + item.port in ids:
403 s_id = ids[item.subnet + "_" + item.port]
404 else:
405 s_id = self.createAndAddServiceToHost(
406 h_id,
407 item.service,
408 item.protocol,
409 ports=[str(item.port)]
410 )
411 ids[item.subnet + "_" + item.port] = s_id
412 if web:
413 if item.severity not in self.ignored_severities:
414 v_id = self.createAndAddVulnWebToService(
415 h_id,
416 s_id,
417 item.name,
418 desc=item.description,
419 website=item.host,
420 severity=item.severity,
421 ref=ref,
422 resolution=item.resolution,
423 external_id=item.id)
424 elif item.severity not in self.ignored_severities:
425 self.createAndAddVulnToService(
426 h_id,
427 s_id,
428 item.name,
429 desc=item.description,
430 severity=item.severity,
431 ref=ref,
432 resolution=item.resolution,
433 external_id=item.id)
434 del parser
435
436 def _isIPV4(self, ip):
437 if len(ip.split(".")) == 4:
438 return True
439 else:
440 return False
441
442 def processCommandString(self, username, current_path, command_string):
443 return None
444
445 def setHost(self):
446 pass
447
448
449 def createPlugin():
450 return OpenvasPlugin()
451
452 if __name__ == "__main__":
453 import sys
454 import os
455 if len(sys.argv) == 2:
456 report_file = sys.argv[1]
457 if os.path.isfile(report_file):
458 plugin = createPlugin()
459 plugin.processReport(report_file)
460 print(plugin.get_json())
461 else:
462 print(f"Report not found: {report_file}")
463 else:
464 print(f"USAGE {sys.argv[0]} REPORT_FILE")
465
466 # 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 # 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 # Author: @EzequielTBH
8 from builtins import str
9
10 from faraday_plugins.plugins.plugin import PluginBase
11 import json
12 import re
13
14 __author__ = "@EzequielTBH"
15 __copyright__ = "Copyright 2015, @EzequielTBH"
16 __credits__ = "@EzequielTBH"
17 __license__ = "GPL v3"
18 __version__ = "1.0.0"
19
20
21 class pasteAnalyzerPlugin(PluginBase):
22
23 def __init__(self):
24 super().__init__()
25 self.id = "pasteAnalyzer"
26 self.name = "pasteAnalyzer JSON Output Plugin"
27 self.plugin_version = "1.0.0"
28 self.command_string = ""
29 self.current_path = ""
30 self._command_regex = re.compile(
31 r'^(pasteAnalyzer|python pasteAnalyzer.py|\./pasteAnalyzer.py|sudo python pasteAnalyzer.py|sudo \./pasteAnalyzer.py).*?')
32
33 def parseOutputString(self, output, debug=False):
34
35 print("[*]Parsing Output...")
36
37 # Generating file name with full path.
38 indexStart = self.command_string.find("-j") + 3
39
40 fileJson = self.command_string[
41 indexStart:self.command_string.find(" ", indexStart)]
42
43 fileJson = self.current_path + "/" + fileJson
44
45 try:
46 with open(fileJson, "r") as fileJ:
47 results = json.loads(fileJ.read())
48
49 except Exception as e:
50 print("\n[!]Exception opening file\n" + str(e))
51 return
52
53 if results == []:
54 return
55
56 print("[*]Results loaded...")
57
58 # Configuration initial.
59 hostId = self.createAndAddHost("pasteAnalyzer")
60 interfaceId = self.createAndAddInterface(hostId, "Results")
61 serviceId = self.createAndAddServiceToInterface(
62 hostId,
63 interfaceId,
64 "Web",
65 "TcpHTTP",
66 ['80']
67 )
68 print("[*]Initial Configuration ready....")
69
70 # Loading results.
71 for i in range(0, len(results), 2):
72
73 data = results[i + 1]
74 description = ""
75
76 for element in data:
77
78 # Is Category
79 if type(element) == str: #TODO bte arrray decode
80 description += element + ": "
81
82 # Is a list with results!
83 else:
84 for element2 in element:
85 description += "\n" + element2
86
87 self.createAndAddVulnWebToService(
88 hostId,
89 serviceId,
90 results[i],
91 description
92 )
93
94 print("[*]Parse finished, API faraday called...")
95
96 def processCommandString(self, username, current_path, command_string):
97
98 print("[*]pasteAnalyzer Plugin running...")
99
100 if command_string.find("-j") < 0:
101 command_string += " -j JSON_OUTPUT "
102
103 self.command_string = command_string
104 self.current_path = current_path
105
106 return command_string
107
108
109 def createPlugin():
110 return pasteAnalyzerPlugin()
111
112 if __name__ == "__main__":
113 import sys
114 import os
115 if len(sys.argv) == 2:
116 report_file = sys.argv[1]
117 if os.path.isfile(report_file):
118 plugin = createPlugin()
119 plugin.processReport(report_file)
120 print(plugin.get_json())
121 else:
122 print(f"Report not found: {report_file}")
123 else:
124 print(f"USAGE {sys.argv[0]} REPORT_FILE")
125 # 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 # 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 socket
7 from os import path
8 from faraday_plugins.plugins.plugin import PluginBase
9 from urllib.parse import urlparse
10
11 __author__ = "Andres Tarantini"
12 __copyright__ = "Copyright (c) 2015 Andres Tarantini"
13 __credits__ = ["Andres Tarantini"]
14 __license__ = "MIT"
15 __version__ = "0.0.1"
16 __maintainer__ = "Andres Tarantini"
17 __email__ = "[email protected]"
18 __status__ = "Development"
19
20
21 class PeepingTomPlugin(PluginBase):
22 """
23 Handle PeepingTom (https://bitbucket.org/LaNMaSteR53/peepingtom) output
24 """
25
26 def __init__(self):
27 super().__init__()
28 self.id = "peepingtom"
29 self.name = "PeepingTom"
30 self.plugin_version = "0.0.1"
31 self.version = "02.19.15"
32 self._command_regex = re.compile(
33 r'^(python peepingtom.py|\./peepingtom.py).*?')
34 self._path = None
35
36 def parseOutputString(self, output):
37 # Find data path
38 data_path_search = re.search(r"in '(.*)\/'", output)
39 print(data_path_search)
40 if not data_path_search:
41 # No data path found
42 return True
43
44 # Parse "peepingtom.html" report and extract results
45 data_path = data_path_search.groups()[0]
46 html = open(path.join(self._path, data_path, "peepingtom.html")).read()
47 for url in re.findall(r'href=[\'"]?([^\'" >]+)', html):
48 if "://" in url:
49 url_parsed = urlparse(url)
50 address = socket.gethostbyname(url_parsed.netloc)
51 host = self.createAndAddHost(address)
52 iface = self.createAndAddInterface(
53 host, address, ipv4_address=address)
54 service = self.createAndAddServiceToInterface(host, iface, "http", protocol="tcp", ports=[80])
55 self.createAndAddNoteToService(
56 host,
57 service,
58 'screenshot',
59 path.join(
60 self._path,
61 data_path_search.groups()[0],
62 "{}.png".format(url.replace(
63 "://", "").replace("/", "").replace(".", ""))
64 )
65 )
66
67 return True
68
69 def processCommandString(self, username, current_path, command_string):
70 self._path = current_path
71
72
73 def createPlugin():
74 return PeepingTomPlugin()
75
76 if __name__ == "__main__":
77 import sys
78 import os
79 if len(sys.argv) == 2:
80 report_file = sys.argv[1]
81 if os.path.isfile(report_file):
82 plugin = createPlugin()
83 plugin.processReport(report_file)
84 print(plugin.get_json())
85 else:
86 print(f"Report not found: {report_file}")
87 else:
88 print(f"USAGE {sys.argv[0]} REPORT_FILE")
89
90 # 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 # 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 from faraday_plugins.plugins.plugin import PluginBase
7 import re
8
9 __author__ = "Facundo de Guzmán, Esteban Guillardoy"
10 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
11 __credits__ = ["Facundo de Guzmán", "Esteban Guillardoy"]
12 __license__ = ""
13 __version__ = "1.0.0"
14 __maintainer__ = "Francisco Amato"
15 __email__ = "[email protected]"
16 __status__ = "Development"
17
18
19 class CmdPingPlugin(PluginBase):
20 """
21 This plugin handles ping command.
22 Basically detects if user was able to connect to a device
23 """
24
25 def __init__(self):
26 super().__init__()
27 self.id = "ping"
28 self.name = "Ping"
29 self.plugin_version = "0.0.1"
30 self.version = "1.0.0"
31 self._command_regex = re.compile(
32 r'^(sudo ping|ping|sudo ping6|ping6).*?')
33
34 def parseOutputString(self, output, debug=False):
35
36 reg = re.search(r"PING ([\w\.-:]+)( |)\(([\w\.:]+)\)", output)
37 if re.search("0 received|unknown host", output) is None and reg is not None:
38
39 ip_address = reg.group(3)
40 hostname = reg.group(1)
41
42 h_id = self.createAndAddHost(ip_address)
43 if self._isIPV4(ip_address):
44 i_id = self.createAndAddInterface(
45 h_id, ip_address, ipv4_address=ip_address, hostname_resolution=[hostname])
46 else:
47 self.createAndAddInterface(
48 h_id, ip_address, ipv6_address=ip_address, hostname_resolution=[hostname])
49
50 return True
51
52 def _isIPV4(self, ip):
53 if len(ip.split(".")) == 4:
54 return True
55 else:
56 return False
57
58 def processCommandString(self, username, current_path, command_string):
59 """
60 """
61 return None
62
63
64 def createPlugin():
65 return CmdPingPlugin()
66
67 if __name__ == "__main__":
68 import sys
69 import os
70 if len(sys.argv) == 2:
71 report_file = sys.argv[1]
72 if os.path.isfile(report_file):
73 plugin = createPlugin()
74 plugin.processReport(report_file)
75 print(plugin.get_json())
76 else:
77 print(f"Report not found: {report_file}")
78 else:
79 print(f"USAGE {sys.argv[0]} REPORT_FILE")
80
81 # 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 # 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 from faraday_plugins.plugins.plugin import PluginBase
7 import re
8
9
10 __author__ = "Federico Kirschbaum"
11 __copyright__ = "Copyright 2011, Faraday Project"
12 __credits__ = ["Federico Kirschbaum"]
13 __license__ = ""
14 __version__ = "1.0.0"
15 __maintainer__ = "Federico Kirschbaum"
16 __email__ = "[email protected]"
17 __status__ = "Development"
18
19
20 class CmdPropeciaPlugin(PluginBase):
21 """
22 This plugin handles propecia command.
23 Basically inserts into the tree the ouput of this tool
24 """
25
26 def __init__(self):
27 super().__init__()
28 self.id = "propecia"
29 self.name = "propecia port scanner"
30 self.plugin_version = "0.0.1"
31 self.version = "1.0"
32 self.framework_version = "1.0.0"
33 self.options = None
34 self._current_output = None
35 self._command_regex = re.compile(
36 r'^(sudo propecia|\.\/propecia|propecia).*?')
37 self._host_ip = None
38 self._port = "23"
39
40 def parseOutputString(self, output, debug=False):
41
42 host_info = re.search(
43 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)", output)
44
45 if host_info is None:
46 self.logger.info("No hosts detected")
47 else:
48 for host in output.splitlines():
49 if host != "":
50 h_id = self.createAndAddHost(host)
51 i_id = self.createAndAddInterface(
52 h_id, host, ipv4_address=host)
53 s_id = self.createAndAddServiceToInterface(h_id, i_id, str(self._port),
54 "tcp",
55 ports=[self._port],
56 status="open",
57 version="",
58 description="")
59 if debug is True:
60 self.logger.info("Debug is active")
61
62 return True
63
64 def processCommandString(self, username, current_path, command_string):
65 """
66 """
67 count_args = command_string.split()
68
69 if count_args.__len__() == 3:
70 self._port = count_args[2]
71
72
73 def createPlugin():
74 return CmdPropeciaPlugin()
75
76 if __name__ == "__main__":
77 import sys
78 import os
79 if len(sys.argv) == 2:
80 report_file = sys.argv[1]
81 if os.path.isfile(report_file):
82 plugin = createPlugin()
83 plugin.processReport(report_file)
84 print(plugin.get_json())
85 else:
86 print(f"Report not found: {report_file}")
87 else:
88 print(f"USAGE {sys.argv[0]} REPORT_FILE")
89
90 # 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 # 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 os
7 import logging
8 from faraday_plugins.plugins.plugin import PluginXMLFormat
9
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
18
19 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split('.')]
20
21 logger = logging.getLogger(__name__)
22
23 current_path = os.path.abspath(os.getcwd())
24
25 __author__ = 'Francisco Amato'
26 __copyright__ = 'Copyright (c) 2013, Infobyte LLC'
27 __credits__ = ['Francisco Amato']
28 __license__ = ''
29 __version__ = '1.0.0'
30 __maintainer__ = 'Francisco Amato'
31 __email__ = '[email protected]'
32 __status__ = 'Development'
33
34
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
42 def cleaner_results(string):
43
44 try:
45 result = string.replace('<P>', '').replace('<UL>', ''). \
46 replace('<LI>', '').replace('<BR>', ''). \
47 replace('<A HREF="', '').replace('</A>', ' '). \
48 replace('" TARGET="_blank">', ' ').replace('&quot;', '"')
49 return result
50
51 except:
52 return ''
53
54
55 class QualysguardXmlParser():
56 """
57 The objective of this class is to parse an xml file generated by
58 the qualysguard tool.
59
60 TODO: Handle errors.
61 TODO: Test qualysguard output version. Handle what happens if the parser
62 doesn't support it.
63 TODO: Test cases.
64
65 @param qualysguard_xml_filepath A proper xml generated by qualysguard
66 """
67
68 def __init__(self, xml_output):
69 tree, type_report = self.parse_xml(xml_output)
70
71 if not tree or type_report is None:
72 self.items = []
73 return
74
75 if type_report == 'ASSET_DATA_REPORT':
76 self.items = [data for data in self.get_items_asset_report(tree)]
77 elif type_report == 'SCAN':
78 self.items = [data for data in self.get_items_scan_report(tree)]
79
80 def parse_xml(self, xml_output):
81 """
82 Open and parse an xml file.
83
84 TODO: Write custom parser to just read the nodes that we need instead
85 of reading the whole file.
86
87 @return xml_tree An xml tree instance. None if error.
88 """
89
90 asset_data_report = '<!DOCTYPE ASSET_DATA_REPORT SYSTEM'
91 scan_report = '<!DOCTYPE SCAN SYSTEM'
92
93 try:
94 tree = ET.fromstring(xml_output)
95
96 if asset_data_report in xml_output:
97 type_report = 'ASSET_DATA_REPORT'
98 elif scan_report in xml_output:
99 type_report = 'SCAN'
100 else:
101 type_report = None
102
103 except SyntaxError as err:
104 logger.error('SyntaxError: %s.' % (err))
105 return None, None
106
107 return tree, type_report
108
109 def get_items_scan_report(self, tree):
110 """
111 @return items A list of Host instances
112 """
113 for node in tree.findall('IP'):
114 yield ItemScanReport(node)
115
116 def get_items_asset_report(self, tree):
117 """
118 @return items A list of Host instances
119 """
120 for node in tree.find('HOST_LIST').findall('HOST'):
121 yield ItemAssetReport(node, tree)
122
123
124 class ItemAssetReport():
125 """
126 An abstract representation of a Item (HOST) for a Asset Report.
127 @param item_node A item_node taken from an qualysguard xml tree
128 """
129
130 def __init__(self, item_node, tree):
131
132 self.node = item_node
133 self.ip = self.get_text_from_subnode('IP')
134
135 self.os = self.get_text_from_subnode('OPERATING_SYSTEM')
136 self.vulns = self.getResults(tree)
137
138 def getResults(self, tree):
139
140 glossary = tree.find('GLOSSARY/VULN_DETAILS_LIST')
141
142 for self.issue in self.node.find('VULN_INFO_LIST'):
143 yield ResultsAssetReport(self.issue, glossary)
144
145 def get_text_from_subnode(self, subnode_xpath_expr):
146 """
147 Finds a subnode in the host node and the retrieves a value from it.
148
149 @return An attribute value
150 """
151 sub_node = self.node.find(subnode_xpath_expr)
152 if sub_node is not None:
153 return sub_node.text
154
155 return None
156
157
158 class ResultsAssetReport():
159 """
160 A abstraction of Results for a Asset Report of Qualysguard.
161 """
162
163 def __init__(self, issue_node, glossary):
164
165 # VULN_INFO ElementTree
166 self.node = issue_node
167 self.port = self.get_text_from_subnode(self.node, 'PORT')
168 self.protocol = self.get_text_from_subnode(self.node, 'PROTOCOL')
169 self.name = self.get_text_from_subnode(self.node, 'QID')
170 self.result = self.get_text_from_subnode(self.node, 'RESULT')
171
172 self.severity_dict = {
173 '1': 'info',
174 '2': 'info',
175 '3': 'med',
176 '4': 'high',
177 '5': 'critical'}
178
179 # GLOSSARY TAG
180 self.glossary = glossary
181 self.severity = self.severity_dict.get(
182 self.get_text_from_glossary('SEVERITY'), 'info')
183 self.title = self.get_text_from_glossary('TITLE')
184 self.cvss = self.get_text_from_glossary('CVSS_SCORE/CVSS_BASE')
185 self.pci = self.get_text_from_glossary('PCI_FLAG')
186 self.solution = self.get_text_from_glossary('SOLUTION')
187 self.impact = self.get_text_from_glossary('IMPACT')
188
189 # Description
190 self.desc = cleaner_results(self.get_text_from_glossary('THREAT'))
191 if not self.desc:
192 self.desc = ''
193 if self.result:
194 self.desc += '\n\nResult: ' + cleaner_results(self.result)
195 if self.impact:
196 self.desc += '\n\nImpact: ' + cleaner_results(self.impact)
197 if self.result:
198 self.desc += '\n\nSolution: ' + cleaner_results(self.solution)
199
200 # References
201 self.ref = []
202 self.ref.append(self.get_text_from_glossary('CVE_ID_LIST/CVE_ID/ID'))
203
204 if self.cvss:
205 self.ref.append('CVSS SCORE: ' + self.cvss)
206
207 if self.pci:
208 self.ref.append('PCI: ' + self.pci)
209
210 def get_text_from_glossary(self, tag):
211 """
212 Finds a subnode in the glossary and retrieves a value of this.
213 Filter by QualysId.
214
215 @return An attribute value
216 """
217
218 for vuln_detail in self.glossary:
219
220 id_act = vuln_detail.get('id').strip('qid_')
221 if id_act == self.name:
222
223 text = vuln_detail.find(tag)
224 if text is not None:
225 return cleaner_unicode(text.text)
226 else:
227 return None
228
229 def get_text_from_subnode(self, node, subnode_xpath_expr):
230 """
231 Finds a subnode in the node and the retrieves a value from it.
232
233 @return An attribute value
234 """
235 sub_node = node.find(subnode_xpath_expr)
236 if sub_node is not None:
237 return cleaner_unicode(sub_node.text)
238
239 return None
240
241
242 class ItemScanReport():
243 """
244 An abstract representation of a Item for a 'SCAN' report of Qualysguard.
245
246 @param item_node A item_node taken from an qualysguard xml tree
247 """
248
249 def __init__(self, item_node):
250 self.node = item_node
251 self.ip = item_node.get('value')
252 self.os = self.get_text_from_subnode('OS')
253 self.hostname = self.get_hostname(item_node)
254 self.vulns = self.getResults(item_node)
255
256 def getResults(self, tree):
257 """
258 :param tree:
259 """
260 for self.issues in tree.findall('VULNS/CAT'):
261 for v in self.issues.findall('VULN'):
262 yield ResultsScanReport(v, self.issues)
263 for self.issues in tree.findall('INFOS/CAT'):
264 for v in self.issues.findall('INFO'):
265 yield ResultsScanReport(v, self.issues)
266 for self.issues in tree.findall('SERVICES/CAT'):
267 for v in self.issues.findall('SERVICE'):
268 yield ResultsScanReport(v, self.issues)
269 for self.issues in tree.findall('PRACTICES/CAT'):
270 for v in self.issues.findall('PRACTICE'):
271 yield ResultsScanReport(v, self.issues)
272
273 def get_text_from_subnode(self, subnode_xpath_expr):
274 """
275 Finds a subnode in the host node and the retrieves a value from it.
276
277 @return An attribute value
278 """
279 sub_node = self.node.find(subnode_xpath_expr)
280 if sub_node is not None:
281 return sub_node.text
282
283 return None
284
285 def get_hostname(self, node):
286 hostname = node.get('name')
287
288 if hostname == 'No registered hostname':
289 return ""
290
291 return hostname
292
293
294 class ResultsScanReport():
295 """
296 An abstraction of Result for Qualysguard 'SCAN' Report.
297 """
298
299 def __init__(self, issue_node, parent):
300 self.node = issue_node
301 self.port = parent.get('port')
302 self.protocol = parent.get('protocol')
303 self.name = self.node.get('number')
304 self.external_id = self.node.get('number')
305 self.severity = self.node.get('severity')
306 self.title = self.get_text_from_subnode('TITLE')
307 self.cvss = self.get_text_from_subnode('CVSS_BASE')
308 self.diagnosis = self.get_text_from_subnode('DIAGNOSIS')
309 self.solution = self.get_text_from_subnode('SOLUTION')
310 self.result = self.get_text_from_subnode('RESULT')
311 self.consequence = self.get_text_from_subnode('CONSEQUENCE')
312
313 self.desc = cleaner_results(self.diagnosis)
314 if self.result:
315 self.desc += '\nResult: ' + cleaner_results(self.result)
316 else:
317 self.desc += ''
318
319 if self.consequence:
320 self.desc += '\nConsequence: ' + cleaner_results(self.consequence)
321 else:
322 self.desc += ''
323
324 self.ref = []
325 for r in issue_node.findall('CVE_ID_LIST/CVE_ID'):
326 self.node = r
327 self.ref.append(self.get_text_from_subnode('ID'))
328 for r in issue_node.findall('BUGTRAQ_ID_LIST/BUGTRAQ_ID'):
329 self.node = r
330 self.ref.append('bid-' + self.get_text_from_subnode('ID'))
331
332 if self.cvss:
333 self.ref.append('CVSS BASE: ' + self.cvss)
334
335 def get_text_from_subnode(self, subnode_xpath_expr):
336 """
337 Finds a subnode in the host node and the retrieves a value from it.
338
339 @return An attribute value
340 """
341 sub_node = self.node.find(subnode_xpath_expr)
342 if sub_node is not None:
343 return cleaner_results(cleaner_unicode(sub_node.text))
344
345 return None
346
347
348 class QualysguardPlugin(PluginXMLFormat):
349 """
350 Example plugin to parse qualysguard output.
351 """
352
353 def __init__(self):
354 super().__init__()
355 self.identifier_tag = ["ASSET_DATA_REPORT", "SCAN"]
356 self.id = 'Qualysguard'
357 self.name = 'Qualysguard XML Output Plugin'
358 self.plugin_version = '0.0.2'
359 self.version = 'Qualysguard 8.17.1.0.2'
360 self.framework_version = '1.0.0'
361 self.options = None
362 self._current_output = None
363 self._command_regex = re.compile(
364 r'^(sudo qualysguard|\.\/qualysguard).*?')
365 self.open_options = {"mode": "r", "encoding": "utf-8"}
366
367 def parseOutputString(self, output, debug=False):
368
369 parser = QualysguardXmlParser(output)
370
371 for item in parser.items:
372 h_id = self.createAndAddHost(
373 item.ip,
374 item.os,
375 hostnames=[item.hostname])
376
377 for v in item.vulns:
378 if v.port is None:
379 self.createAndAddVulnToHost(
380 h_id,
381 v.title if v.title else v.name,
382 ref=v.ref,
383 severity=str(int(v.severity) - 1),
384 resolution=v.solution if v.solution else '',
385 desc=v.desc,
386 external_id=v.external_id)
387
388 else:
389
390 web = False
391 s_id = self.createAndAddServiceToHost(
392 h_id,
393 v.port,
394 v.protocol,
395 ports=[str(v.port)],
396 status='open')
397
398 if v.port in ['80', '443'] or re.search('ssl|http', v.name):
399 web = True
400 else:
401 web = False
402
403 if web:
404 self.createAndAddVulnWebToService(
405 h_id,
406 s_id,
407 v.title if v.title else v.name,
408 ref=v.ref,
409 website=item.ip,
410 severity=str(int(v.severity) - 1),
411 desc=v.desc,
412 resolution=v.solution if v.solution else '',
413 external_id=v.external_id)
414
415 else:
416 self.createAndAddVulnToService(
417 h_id,
418 s_id,
419 v.title if v.title else v.name,
420 ref=v.ref,
421 severity=str(int(v.severity) - 1),
422 desc=v.desc,
423 resolution=v.solution if v.solution else '',
424 external_id=v.external_id)
425
426 del parser
427
428 def processCommandString(self, username, current_path, command_string):
429 return None
430
431 def setHost(self):
432 pass
433
434
435 def createPlugin():
436 return QualysguardPlugin()
437
438
439 if __name__ == "__main__":
440 import sys
441 import os
442 if len(sys.argv) == 2:
443 report_file = sys.argv[1]
444 if os.path.isfile(report_file):
445 plugin = createPlugin()
446 plugin.processReport(report_file)
447 print(plugin.get_json())
448 else:
449 print(f"Report not found: {report_file}")
450 else:
451 print(f"USAGE {sys.argv[0]} REPORT_FILE")
452 # I'm Py3
0 """
1 Faraday Penetration Test IDE
2 Copyright (C) 2017 Infobyte LLC (http://www.infobytesec.com/)
3 See the file 'doc/LICENSE' for the license information
4 """
5 import re
6 import json
7 import socket
8 import logging
9 try:
10 from lxml import etree as ET
11 except ImportError:
12 import xml.etree.ElementTree as ET
13
14 from faraday_plugins.plugins.plugin import PluginXMLFormat
15
16 __author__ = 'Leonardo Lazzaro'
17 __copyright__ = 'Copyright (c) 2017, Infobyte LLC'
18 __credits__ = ['Leonardo Lazzaro']
19 __license__ = ''
20 __version__ = '0.1.0'
21 __maintainer__ = 'Leonardo Lazzaro'
22 __email__ = '[email protected]'
23 __status__ = 'Development'
24
25 logger = logging.getLogger(__name__)
26
27
28 class ReconngParser:
29 def __init__(self, output):
30 self._format = self.report_format(output)
31 self.hosts = []
32 self.vulns = []
33
34 if self._format == 'xml':
35 self.parsable_tree = self.get_parseable_xml_output(output)
36 self.parse_xml_report(self.parsable_tree)
37
38 elif self._format == 'json':
39 self.parse_json_report(output)
40
41 def report_format(self, output):
42 xml_format_regex = re.compile(r'^<(.*?)>')
43 json_format_regex = re.compile(r'(^{)')
44
45 if xml_format_regex.match(output):
46 output_format = 'xml'
47 elif json_format_regex.match(output):
48 output_format = 'json'
49 else:
50 return False
51
52 return output_format
53
54 def get_parseable_xml_output(self, xml_output):
55 try:
56 tree = ET.fromstring(xml_output)
57 return tree
58 except IndexError:
59 print("Syntax error")
60 return None
61
62 def parse_xml_report(self, tree):
63 hosts_items = tree.xpath('//hosts/item')
64 self.hosts_from_report(hosts_items)
65
66 vulnerabilities_items = tree.xpath('//vulnerabilities/item')
67 self.vulns_from_report(vulnerabilities_items)
68
69 def parse_json_report(self, output):
70 reconng_data = json.loads(output)
71 hosts_items = reconng_data.get('hosts', '')
72 self.hosts_from_report(hosts_items)
73
74 vulns_items = reconng_data.get('vulnerabilities','')
75 self.vulns_from_report(vulns_items)
76
77 def hosts_from_report(self, hosts_items):
78 for host in hosts_items:
79 host_info = self.get_info_from_host_element(host)
80 self.hosts.append(host_info)
81
82 def vulns_from_report(self, vulns_items):
83 for vuln in vulns_items:
84 vuln_info = self.get_info_from_vuln_element(vuln)
85 self.vulns.append(vuln_info)
86
87 def get_info_from_host_element(self, element):
88 info = {}
89 if self._format == 'xml':
90 info['host'] = element.find('host').text
91 info['ip'] = element.find('ip_address').text
92
93 elif self._format == 'json':
94 info['host'] = element['host']
95 info['ip'] = element['ip_address']
96
97 return info
98
99 def get_info_from_vuln_element(self, element):
100 info = {}
101 if self._format == 'xml':
102 info['host'] = element.find('host').text
103 info['reference'] = element.find('reference').text
104 info['module'] = element.find('module').text
105 info['example'] = element.find('example').text
106 info['category'] = element.find('category').text
107 elif self._format == 'json':
108 info['category'] = element['category']
109 info['host'] = element['host']
110 info['module'] = element['module']
111 info['reference'] = element['reference']
112 info['example'] = element['example']
113
114 if 'XSS' in info['category']:
115 info['severity'] = 'high'
116 elif 'SSL' in info['category']:
117 info['severity'] = 'med'
118 else:
119 info['severity'] = 'info'
120
121 return info
122
123
124 class ReconngPlugin(PluginXMLFormat):
125 """
126 Example plugin to parse qualysguard output.
127 """
128
129 def __init__(self):
130 super().__init__()
131 self.identifier_tag = "reconng"
132 self.id = 'Reconng'
133 self.name = 'Reconng XML Output Plugin'
134 self.plugin_version = '0.0.3'
135 self.version = ''
136 self.framework_version = ''
137 self.options = None
138 self._current_output = None
139 self._command_regex = re.compile(
140 r'records added to')
141
142 self.host_mapper = {}
143
144 def parseOutputString(self, output):
145 parser = ReconngParser(output)
146
147 for host in parser.hosts:
148 h_id = self.createAndAddHost(
149 host['ip'],
150 hostnames=[host['host']]
151 )
152 self.host_mapper[host['host']] = h_id
153 for vuln in parser.vulns:
154 if vuln['host'] not in list(self.host_mapper.keys()):
155 ip = self.resolve_host(vuln['host'])
156 h_id = self.createAndAddHost(
157 ip,
158 hostnames=[vuln['host']]
159 )
160 self.host_mapper[vuln['host']] = h_id
161 else:
162 h_id = self.host_mapper[vuln['host']]
163
164 self.createAndAddVulnToHost(
165 name='Recon-ng found: ' + vuln['category'] + ' vulnerability',
166 desc='Found by module: ' + vuln['module'],
167 severity=vuln['severity'],
168 ref=[vuln['reference']],
169 host_id=h_id,
170 data=vuln['example']
171 )
172
173 def processCommandString(self, username, current_path, command_string):
174 return
175
176 def resolve_host(self, host):
177 try:
178 return socket.gethostbyname(host)
179 except Exception:
180 pass
181 return host
182
183
184 def createPlugin():
185 return ReconngPlugin()
186
187
188 if __name__ == "__main__":
189 import sys
190 import os
191 if len(sys.argv) == 2:
192 report_file = sys.argv[1]
193 if os.path.isfile(report_file):
194 plugin = createPlugin()
195 plugin.processReport(report_file)
196 print(plugin.get_json())
197 else:
198 print(f"Report not found: {report_file}")
199 else:
200 print(f"USAGE {sys.argv[0]} REPORT_FILE")
201 # 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 # 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 import re
7 import os
8 from faraday_plugins.plugins.plugin import PluginXMLFormat
9
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
18
19 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
20
21 current_path = os.path.abspath(os.getcwd())
22
23 __author__ = "Francisco Amato"
24 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
25 __credits__ = ["Francisco Amato"]
26 __license__ = ""
27 __version__ = "1.0.0"
28 __maintainer__ = "Francisco Amato"
29 __email__ = "[email protected]"
30 __status__ = "Development"
31
32
33 class RetinaXmlParser:
34 """
35 The objective of this class is to parse an xml file generated by the retina tool.
36
37 TODO: Handle errors.
38 TODO: Test retina output version. Handle what happens if the parser doesn't support it.
39 TODO: Test cases.
40
41 @param retina_xml_filepath A proper xml generated by retina
42 """
43
44 def __init__(self, xml_output):
45 tree = self.parse_xml(xml_output)
46 if tree:
47 self.items = [data for data in self.get_items(tree)]
48 else:
49 self.items = []
50
51 def parse_xml(self, xml_output):
52 """
53 Open and parse an xml file.
54
55 TODO: Write custom parser to just read the nodes that we need instead of
56 reading the whole file.
57
58 @return xml_tree An xml tree instance. None if error.
59 """
60 try:
61 tree = ET.fromstring(xml_output)
62 except SyntaxError as err:
63 print("SyntaxError: %s. %s" % (err, xml_output))
64 return None
65
66 return tree
67
68 def get_items(self, tree):
69 """
70 @return items A list of Host instances
71 """
72 for node in tree.findall("hosts/host"):
73 yield Item(node)
74
75
76 class Item:
77 """
78 An abstract representation of a Item
79
80
81 @param item_node A item_node taken from an retina xml tree
82 """
83
84 def __init__(self, item_node):
85 self.node = item_node
86 self.ip = self.get_text_from_subnode("ip")
87 self.hostname = "" if self.get_text_from_subnode(
88 "dnsName") == "unknown" else self.get_text_from_subnode("dnsName")
89 self.netbiosname = self.get_text_from_subnode("netBIOSName")
90 self.netbiosdomain = self.get_text_from_subnode("netBIOSDomain")
91 self.os = self.get_text_from_subnode("os")
92 self.mac = self.get_text_from_subnode("mac")
93
94 self.vulns = self.getResults(item_node)
95 self.ports = {}
96 for v in self.vulns:
97 if v.port not in self.ports:
98 self.ports[v.port] = []
99 self.ports[v.port].append(v)
100
101 def getResults(self, tree):
102 """
103 :param tree:
104 """
105 for self.issues in tree.findall("audit"):
106 yield Results(self.issues)
107
108 def get_text_from_subnode(self, subnode_xpath_expr):
109 """
110 Finds a subnode in the host node and the retrieves a value from it.
111
112 @return An attribute value
113 """
114 sub_node = self.node.find(subnode_xpath_expr)
115 if sub_node is not None:
116 return sub_node.text
117
118 return None
119
120
121 class Results():
122
123 def __init__(self, issue_node):
124 self.node = issue_node
125 self.name = self.get_text_from_subnode('name')
126
127 self.description = self.get_text_from_subnode('description')
128 self.solution = self.get_text_from_subnode('fixInformation')
129 self.severity = self.get_text_from_subnode('risk')
130 self.cve = "" if self.get_text_from_subnode(
131 'cve') == 'N/A' else self.get_text_from_subnode('cve')
132 self.cce = self.get_text_from_subnode('cce')
133 self.date = self.get_text_from_subnode('date')
134 self.pciLevel = self.get_text_from_subnode('pciLevel')
135 self.pciReason = self.get_text_from_subnode('pciReason')
136 self.pciPassFail = self.get_text_from_subnode('pciPassFail')
137 self.cvssScore = self.get_text_from_subnode('cvssScore')
138 self.exploit = self.get_text_from_subnode('exploit')
139 self.context = self.get_text_from_subnode('context')
140 val = self.context.split(":")
141 self.port = ""
142 self.protocol = ""
143 if len(val) == 2:
144 if val[0] in ['TCP', 'UDP']:
145 self.protocol = val[0]
146 self.port = val[1]
147
148 self.desc = self.get_text_from_subnode('description')
149 self.solution = self.solution if self.solution else ""
150 self.desc += "\nExploit: " + self.exploit if self.exploit else ""
151 self.desc += "\ncvssScore: " + self.cvssScore if self.cvssScore else ""
152 self.desc += "\nContext: " + self.context if self.context else ""
153
154 self.ref = []
155 if self.cve:
156 self.ref = self.cve.split(",")
157
158 def get_text_from_subnode(self, subnode_xpath_expr):
159 """
160 Finds a subnode in the host node and the retrieves a value from it.
161
162 @return An attribute value
163 """
164 sub_node = self.node.find(subnode_xpath_expr)
165 if sub_node is not None:
166 return sub_node.text
167
168 return None
169
170
171 class RetinaPlugin(PluginXMLFormat):
172 """
173 Example plugin to parse retina output.
174 """
175
176 def __init__(self):
177 super().__init__()
178 self.identifier_tag = "scanJob"
179 self.id = "Retina"
180 self.name = "Retina XML Output Plugin"
181 self.plugin_version = "0.0.1"
182 self.version = "Retina Network 5.19.2.2718"
183 self.framework_version = "1.0.0"
184 self.options = None
185 self._current_output = None
186 self._command_regex = re.compile(r'^(sudo retina|\.\/retina).*?')
187
188
189 def parseOutputString(self, output, debug=False):
190
191 parser = RetinaXmlParser(output)
192 for item in parser.items:
193 h_id = self.createAndAddHost(item.ip, item.os)
194 hostname = item.hostname if item.hostname else item.ip
195 i_id = self.createAndAddInterface(
196 h_id, item.ip, ipv4_address=item.ip, hostname_resolution=[hostname])
197
198 if not item.netbiosname == 'N/A':
199 self.createAndAddNoteToHost(
200 h_id, "netBIOSName", item.netbiosname)
201
202 if not item.netbiosdomain == 'N/A':
203 self.createAndAddNoteToHost(
204 h_id, "netBIOSDomain", item.netbiosdomain)
205
206 for k, vulns in item.ports.items():
207 if k:
208 for v in vulns:
209 web = False
210 s_id = self.createAndAddServiceToInterface(h_id, i_id, 'unknown',
211 v.protocol.lower(),
212 ports=[str(v.port)],
213 status="open")
214
215 if v.port in ['80', '443'] or re.search("ssl|http", v.name.lower()):
216 web = True
217 else:
218 web = False
219
220 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"))
223 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"))
226 else:
227 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"))
230 del parser
231
232 def processCommandString(self, username, current_path, command_string):
233 return None
234
235 def setHost(self):
236 pass
237
238
239 def createPlugin():
240 return RetinaPlugin()
241
242 if __name__ == "__main__":
243 import sys
244 import os
245 if len(sys.argv) == 2:
246 report_file = sys.argv[1]
247 if os.path.isfile(report_file):
248 plugin = createPlugin()
249 plugin.processReport(report_file)
250 print(plugin.get_json())
251 else:
252 print(f"Report not found: {report_file}")
253 else:
254 print(f"USAGE {sys.argv[0]} REPORT_FILE")
255
256 # 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 # 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 from faraday_plugins.plugins.plugin import PluginBase
7 import re
8 import os
9
10
11
12 current_path = os.path.abspath(os.getcwd())
13
14 __author__ = "Francisco Amato"
15 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
16 __credits__ = ["Francisco Amato"]
17 __license__ = ""
18 __version__ = "1.0.0"
19 __maintainer__ = "Francisco Amato"
20 __email__ = "[email protected]"
21 __status__ = "Development"
22
23
24 class ReverseraiderParser:
25 """
26 The objective of this class is to parse an xml file generated by the reverseraider tool.
27
28 @param reverseraider_filepath A proper simple report generated by reverseraider
29 """
30
31 def __init__(self, output):
32
33 lists = output.split("\r\n")
34 self.items = []
35
36 if re.search("ReverseRaider domain scanner|Error opening", output) is not None:
37 return
38
39 for line in lists:
40 if line != "":
41 print("(%s)" % line)
42 info = line.split("\t")
43 if info.__len__() > 0:
44 item = {'host': info[0], 'ip': info[1]}
45 print("host = %s, ip = %s" % (info[0], info[1]))
46 self.items.append(item)
47
48
49 class ReverseraiderPlugin(PluginBase):
50 """
51 Example plugin to parse reverseraider output.
52 """
53
54 def __init__(self):
55 super().__init__()
56 self.id = "Reverseraider"
57 self.name = "Reverseraider XML Output Plugin"
58 self.plugin_version = "0.0.1"
59 self.version = "0.7.6"
60 self.options = None
61 self._current_output = None
62 self._current_path = None
63 self._command_regex = re.compile(
64 r'^(sudo \.\/reverseraider|\.\/reverseraider).*?')
65 self._completition = {
66 "": "reverseraider -d domain | -r range [options]",
67 "-r": "range of ipv4 or ipv6 addresses, for reverse scanning",
68 "-d": "domain, for wordlist scanning (example google.com)",
69 "-w": "wordlist file (see wordlists directory...)",
70 "-t": "requests timeout in seconds",
71 "-P": "enable numeric permutation on wordlist (default off)",
72 "-D": "nameserver to use (default: resolv.conf)",
73 "-T": "use TCP queries instead of UDP queries",
74 "-R": "don't set the recursion bit on queries",
75 }
76
77 global current_path
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):
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
105 del parser
106
107 def processCommandString(self, username, current_path, command_string):
108 """
109 """
110 return None
111
112
113 def createPlugin():
114 return ReverseraiderPlugin()
115
116
117 if __name__ == "__main__":
118 import sys
119 import os
120 if len(sys.argv) == 2:
121 report_file = sys.argv[1]
122 if os.path.isfile(report_file):
123 plugin = createPlugin()
124 plugin.processReport(report_file)
125 print(plugin.get_json())
126 else:
127 print(f"Report not found: {report_file}")
128 else:
129 print(f"USAGE {sys.argv[0]} REPORT_FILE")
130 # 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 # 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 import re
7 import os
8 import json
9 import socket
10 import random
11 from faraday_plugins.plugins.plugin import PluginBase
12
13 current_path = os.path.abspath(os.getcwd())
14
15 __author__ = "Nicolas Rodriguez"
16 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
17 __credits__ = ["Nicolas Rodriguez"]
18 __license__ = ""
19 __version__ = "1.0.0"
20 __maintainer__ = "Francisco Amato"
21 __email__ = "[email protected]"
22 __status__ = "Development"
23
24
25 class SkipfishParser:
26 """
27 The objective of this class is to parse an xml file generated by
28 the skipfish tool.
29
30 TODO: Handle errors.
31 TODO: Test skipfish output version. Handle what happens if the parser
32 doesn't support it.
33 TODO: Test cases.
34
35 @param skipfish_filepath A proper xml generated by skipfish
36 """
37
38 def __init__(self, skipfish_filepath):
39 self.filepath = skipfish_filepath
40
41 tmp = open(skipfish_filepath + "/samples.js", "r").read()
42 data = self.extract_data(
43 tmp,
44 "var issue_samples =", "];",
45 lambda x: x.replace("'", '"'),
46 False,
47 False)
48 # Escape characters not allowed in JSON, repr fix this with double Escape
49 # Also remove \n character and space for have a valid JSON.
50 issues = json.loads(repr(data[1]).replace("\\n"," ").replace("'","") + "]")
51
52 tmp = open(skipfish_filepath + "/index.html", "r").read()
53 err_msg = json.loads(
54 self.extract_data(
55 tmp,
56 "var issue_desc=",
57 "};",
58 lambda x: self.convert_quotes(x, "'", '"'),
59 False,
60 False)
61 [1] + "}")
62
63 self.err_msg = err_msg
64 self.issues = issues
65
66 def convert_quotes(self, text, quote="'", inside='"'):
67 start = 0
68 while True:
69 pos = text.find(quote, start)
70
71 if pos == -1:
72 break
73
74 ss = text[:pos - 1]
75 quotes = len(ss) - len(ss.replace(inside, ""))
76
77 if quotes % 2 == 0:
78 text = text[:pos - 1] + "\\" + quote + text[pos + 1:]
79
80 start = pos + 1
81 return text
82
83 def extract_data(self, samples, start_tag, end_tag, fn=lambda x: x, include_start_tag=True, include_end_tag=True):
84 start = samples.find(start_tag)
85
86 if start == -1:
87 return (-1, None)
88
89 end = samples.find(end_tag, start + 1)
90
91 if end == -1:
92 return (-2, None)
93
94 data = samples[start:end + len(end_tag)]
95 data = fn(data)
96
97 if not include_start_tag:
98 data = data[len(start_tag) + 1:]
99
100 if not include_end_tag:
101 data = data[:-1 * len(end_tag)]
102
103 return (0, data)
104
105
106 class SkipfishPlugin(PluginBase):
107 """
108 Example plugin to parse skipfish output.
109 """
110
111 def __init__(self):
112 super().__init__()
113 self.id = "Skipfish"
114 self.name = "Skipfish XML Output Plugin"
115 self.plugin_version = "0.0.2"
116 self.version = "2.1.5"
117 self.options = None
118 self._current_output = None
119 self.parent = None
120 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):
125 """
126 This method will discard the output the shell sends, it will read it
127 from the xml where it expects it to be present.
128
129 NOTE: if 'debug' is true then it is being run from a test case and the
130 output being sent is valid.
131 """
132
133 if not os.path.exists(self._output_path):
134 return False
135
136 p = SkipfishParser(self._output_path)
137
138 hostc = {}
139 port = 80
140 for issue in p.issues:
141 req = ""
142 res = ""
143 for sample in issue["samples"]:
144 if not sample["url"] in hostc:
145 reg = re.search(
146 "(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;%\$#\=~_\-]+)).*?$", sample["url"])
147
148 protocol = reg.group(1)
149 host = reg.group(4)
150 if reg.group(11) is not None:
151 port = reg.group(11)
152 else:
153 port = 443 if protocol == "https" else 80
154
155 ip = self.resolve(host)
156
157 h_id = self.createAndAddHost(ip)
158 i_id = self.createAndAddInterface(
159 h_id,
160 ip,
161 ipv4_address=ip,
162 hostname_resolution=[host])
163
164 s_id = self.createAndAddServiceToInterface(
165 h_id,
166 i_id,
167 "http",
168 "tcp",
169 ports=[port],
170 status="open")
171
172 hostc[sample["url"]] = {
173 'h_id': h_id,
174 'ip': ip,
175 'port': port,
176 'host': host,
177 'protocol': protocol,
178 'i_id': i_id,
179 's_id': s_id}
180
181 try:
182 req = open("%s/request.dat" % sample["dir"], "r").read()
183 except:
184 pass
185
186 try:
187 res = open("%s/request.dat" % sample["dir"], "r").read()
188 except Exception:
189 pass
190
191 d = hostc[sample["url"]]
192 self.createAndAddVulnWebToService(
193 d['h_id'],
194 d['s_id'],
195 name=p.err_msg[str(issue["type"])],
196 desc="Extra: " + sample["extra"],
197 website=d['host'],
198 path=sample["url"],
199 severity=issue["severity"])
200
201 def resolve(self, host):
202 try:
203 return socket.gethostbyname(host)
204 except Exception:
205 pass
206 return host
207
208 xml_arg_re = re.compile(r"^.*(-o\s*[^\s]+).*$")
209
210 def processCommandString(self, username, current_path, command_string):
211 """
212 Adds the -o parameter to get report of the command string that the
213 user has set.
214 """
215 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
221 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)
227 else:
228 return re.sub(
229 arg_match.group(1),
230 r"-o %s" % self._output_path,
231 command_string,
232 1)
233
234 def setHost(self):
235 pass
236
237
238 def createPlugin():
239 return SkipfishPlugin()
240
241 if __name__ == "__main__":
242 import sys
243 import os
244 if len(sys.argv) == 2:
245 report_file = sys.argv[1]
246 if os.path.isfile(report_file):
247 plugin = createPlugin()
248 plugin.processReport(report_file)
249 print(plugin.get_json())
250 else:
251 print(f"Report not found: {report_file}")
252 else:
253 print(f"USAGE {sys.argv[0]} REPORT_FILE")
254
255
256 # 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 # 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 from faraday_plugins.plugins.plugin import PluginBase
6 import re
7
8 __author__ = "Andres Tarantini"
9 __copyright__ = "Copyright (c) 2015 Andres Tarantini"
10 __credits__ = ["Andres Tarantini"]
11 __license__ = "MIT"
12 __version__ = "0.0.1"
13 __maintainer__ = "Andres Tarantini"
14 __email__ = "[email protected]"
15 __status__ = "Development"
16
17
18 class SSHDefaultScanPlugin(PluginBase):
19 """
20 Handle sshdefaultscan (https://github.com/atarantini/sshdefaultscan) output
21 using --batch and --batch-template; supports --username and --password
22 """
23
24 def __init__(self):
25 super().__init__()
26 self.id = "sshdefaultscan"
27 self.name = "sshdefaultscan"
28 self.plugin_version = "0.0.1"
29 self.version = "1.0.0"
30 self._command_regex = re.compile(
31 r'^(python sshdefaultscan.py|\./sshdefaultscan.py).*?')
32 self._completition = {"--fast": "Fast scan mode"}
33
34 def parseOutputString(self, output, debug=False):
35 for line in [l.strip() for l in output.split("\n")]:
36 output_rexeg_match = re.match(
37 r".*:.*@\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$", line)
38 if output_rexeg_match:
39 credentials, address = line.split("@")
40 host = self.createAndAddHost(address)
41 iface = self.createAndAddInterface(
42 host, address, ipv4_address=address)
43 service = self.createAndAddServiceToInterface(
44 host, iface, "ssh", protocol="tcp", ports=[22]
45 )
46 username, password = credentials.split(":")
47 cred = self.createAndAddCredToService(
48 host, service, username, password)
49 vuln = self.createAndAddVulnToService(
50 host,
51 service,
52 "Default credentials",
53 desc="The SSH server have default credentials ({username}:{password})".format(
54 username=username,
55 password=password
56 ),
57 severity=3
58 )
59
60 return True
61
62 def processCommandString(self, username, current_path, command_string):
63 if "--batch" not in command_string:
64 return "{command} --batch --batch-template {template}".format(
65 command=command_string,
66 template="{username}:{password}@{host}"
67 )
68
69 return None
70
71
72 def createPlugin():
73 return SSHDefaultScanPlugin()
74
75 if __name__ == "__main__":
76 import sys
77 import os
78 if len(sys.argv) == 2:
79 report_file = sys.argv[1]
80 if os.path.isfile(report_file):
81 plugin = createPlugin()
82 plugin.processReport(report_file)
83 print(plugin.get_json())
84 else:
85 print(f"Report not found: {report_file}")
86 else:
87 print(f"USAGE {sys.argv[0]} REPORT_FILE")
88
89 # I'm Py3
0 import re
1 import os
2 import random
3 from faraday_plugins.plugins.plugin import PluginXMLFormat
4
5 try:
6 from lxml import etree as ET
7 except ImportError:
8 import xml.etree.ElementTree as ET
9
10
11 WEAK_CIPHER_LIST = [
12 "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
13 "TLS_RSA_WITH_AES_128_CBC_SHA",
14 "TLS_RSA_WITH_AES_128_CBC_SHA256",
15 "TLS_RSA_WITH_AES_128_GCM_SHA256",
16 "TLS_RSA_WITH_AES_256_CBC_SHA",
17 "TLS_RSA_WITH_AES_256_CBC_SHA256",
18 "TLS_RSA_WITH_AES_256_GCM_SHA384",
19 "TLS_RSA_WITH_3DES_EDE_CBC_SHA",
20 "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA",
21 "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA"
22 ]
23
24
25 class SslyzeXmlParser:
26
27 def __init__(self, xml_output):
28 self.parser = self.parse_xml(xml_output)
29 self.target = self.get_target(self.parser)
30 self.certificate = self.get_hostname_validation(self.parser)
31 self.cipher_suite = self.get_weak_cipher_suite(self.parser)
32 self.heart_bleed = self.get_heartbleed(self.parser)
33 self.open_ssl_ccs = self.get_openssl_ccs(self.parser)
34
35 def parse_xml(self, xml_output):
36 try:
37 tree = ET.fromstring(xml_output)
38 return tree
39 except IndexError:
40 print("Syntax error")
41 return None
42
43 def get_target(self, tree):
44 return tree.xpath('//target')
45
46 def get_hostname_validation(self, tree):
47 return tree.xpath('//hostnameValidation')
48
49 def get_protocol_name(self, tree):
50 protocol_supported = []
51 protocols = []
52 protocols.append(tree.xpath('//sslv2'))
53 protocols.append(tree.xpath('//sslv3'))
54 protocols.append(tree.xpath('//tlsv1'))
55 protocols.append(tree.xpath('//tlsv1_1'))
56 protocols.append(tree.xpath('//tlsv1_2'))
57 protocols.append(tree.xpath('//tlsv1_3'))
58
59 for protocol in protocols:
60 if protocol[0].attrib['isProtocolSupported'] == "True":
61 protocol_supported.append(protocol[0])
62
63 return protocol_supported
64
65 def get_weak_cipher_suite(self, tree):
66 protocols = self.get_protocol_name(tree)
67 weak_cipher = {}
68
69 for protocol in protocols:
70 weak_cipher[protocol.tag] = []
71 for ciphers in protocol:
72 if ciphers.tag == 'preferredCipherSuite' or ciphers.tag == 'acceptedCipherSuites':
73 for cipher in ciphers:
74 if cipher.attrib['name'] in WEAK_CIPHER_LIST:
75 if not cipher.attrib['name'] in weak_cipher[protocol.tag]:
76 weak_cipher[protocol.tag].append(cipher.attrib['name'])
77
78 return weak_cipher
79
80 def get_heartbleed(self, tree):
81 return tree.xpath('//heartbleed')
82
83 def get_openssl_ccs(self, tree):
84 return tree.xpath('//openssl_ccs')
85
86
87 class SslyzePlugin(PluginXMLFormat):
88
89 def __init__(self):
90 super().__init__()
91 self.identifier_tag = "document"
92 self.id = "Sslyze"
93 self.name = "Sslyze Plugin"
94 self.plugin_version = "0.0.1"
95 self.version = "2.0.6"
96 self.framework_version = "1.0.0"
97 self.options = None
98 self._current_output = None
99 self._command_regex = re.compile(r'^(sudo sslyze|sslyze|\.\/sslyze).*?')
100 self.xml_arg_re = re.compile(r"^.*(--xml_output\s*[^\s]+).*$")
101
102 def report_belongs_to(self, **kwargs):
103 if super().report_belongs_to(**kwargs):
104 report_path = kwargs.get("report_path", "")
105 with open(report_path) as f:
106 output = f.read()
107 return re.search("SSLyzeVersion", output) is not None
108 return False
109
110 def parseOutputString(self, output, debug=False):
111 parser = SslyzeXmlParser(output)
112 host = parser.target[0].attrib['host']
113 ip = parser.target[0].attrib['ip']
114 port = parser.target[0].attrib['port']
115 protocol = parser.target[0].attrib['tlsWrappedProtocol']
116 cipher = parser.cipher_suite
117
118 # Creating host
119 host_id = self.createAndAddHost(ip)
120 # Creating service CHANGE NAME
121 service_id = self.createAndAddServiceToHost(
122 host_id,
123 name=protocol,
124 protocol=protocol,
125 ports=[port],
126 )
127
128 # Checking if certificate matches
129 certificate = parser.certificate[0].attrib['certificateMatchesServerHostname']
130 server_hostname = parser.certificate[0].attrib['serverHostname']
131 if certificate.lower() == 'false':
132 self.createAndAddVulnToService(
133 host_id,
134 service_id,
135 name="Certificate mismatch",
136 desc="Certificate does not match server hostname {}".format(server_hostname),
137 severity="info")
138 #Ciphers
139 cipher = parser.cipher_suite
140
141 for key in cipher:
142 for value in cipher[key]:
143 self.createAndAddVulnToService(
144 host_id,
145 service_id,
146 name=value,
147 desc="In protocol [{}], weak cipher suite: {}".format(key, value),
148 severity="low")
149
150 #Heartbleed
151 heartbleed = parser.heart_bleed
152
153 if heartbleed[0][0].attrib['isVulnerable'].lower() == 'true':
154 self.createAndAddVulnToService(
155 host_id,
156 service_id,
157 name="OpenSSL Heartbleed",
158 desc="OpenSSL Heartbleed is vulnerable",
159 severity="critical")
160
161 #OpenSsl CCS Injection
162 openssl_ccs = parser.open_ssl_ccs
163
164 if openssl_ccs[0][0].attrib['isVulnerable'].lower() == 'true':
165 self.createAndAddVulnToService(
166 host_id,
167 service_id,
168 name="OpenSSL CCS Injection",
169 desc="OpenSSL CCS Injection is vulnerable",
170 severity="medium")
171
172 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
181 arg_match = self.xml_arg_re.match(command_string)
182
183 if arg_match is None:
184 return re.sub(r"(^.*?sslyze)",
185 r"\1 --xml_out %s" % self._output_file_path,
186 command_string)
187 else:
188 return re.sub(arg_match.group(1),
189 r"--xml_out %s" % self._output_file_path,
190 command_string)
191
192
193 def createPlugin():
194 return SslyzePlugin()
195
196 if __name__ == "__main__":
197 import sys
198 import os
199 if len(sys.argv) == 2:
200 report_file = sys.argv[1]
201 if os.path.isfile(report_file):
202 plugin = createPlugin()
203 plugin.processReport(report_file)
204 print(plugin.get_json())
205 else:
206 print(f"Report not found: {report_file}")
207 else:
208 print(f"USAGE {sys.argv[0]} REPORT_FILE")
209
210 # 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 # 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 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())
12
13 __author__ = "Facundo de Guzmán, Esteban Guillardoy"
14 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
15 __credits__ = ["Facundo de Guzmán", "Esteban Guillardoy"]
16 __license__ = ""
17 __version__ = "1.0.0"
18 __maintainer__ = "Facundo de Guzmán"
19 __email__ = "[email protected]"
20 __status__ = "Development"
21
22
23 class TelnetRouterPlugin(PluginBase):
24 """
25 This plugin handles telnet command.
26 Basically detects if user was able to connect to a device
27 """
28
29 def __init__(self):
30 super().__init__()
31 self.id = "Telnet"
32 self.name = "Telnet"
33 self.plugin_version = "0.0.1"
34 self.version = "0.17"
35 self.framework_version = "1.0.0"
36 self.options = None
37 self._current_output = None
38 self._command_regex = re.compile(r'^telnet.*?')
39 self._host_ip = None
40 self._host = []
41 self._port = "23"
42 self._completition = {
43 "": "telnet [-468ELadr] [-S tos] [-b address] [-e escapechar] [-l user] [-n tracefile] [host [port]]",
44 "-4": "Force IPv4 address resolution.",
45 "-6": "Force IPv6 address resolution.",
46 "-8": "Request 8-bit operation. This causes an attempt to negotiate the TELNET BINARY option for both input and output. By default telnet is not 8-bit clean.",
47 "-E": "Disables the escape character functionality; that is, sets the escape character to ``no character''.",
48 "-L": "Specifies an 8-bit data path on output. This causes the TELNET BINARY option to be negotiated on just output.",
49 "-a": "Attempt automatic login. Currently, this sends the user name via the USER variable of the ENVIRON option if supported by the remote system. The username is retrieved via getlogin(3).",
50 "-b": "-b &lt;address&gt; Use bind(2) on the local socket to bind it to a specific local address.",
51 "-d": "Sets the initial value of the debug toggle to TRUE.",
52 "-r": "Emulate rlogin(1). In this mode, the default escape character is a tilde. Also, the interpretation of the escape character is changed: an escape character followed by a dot causes telnet to disconnect from the remote host. A ^Z instead of a dot suspends telnet, and a ^] (the default telnet escape character) generates a normal telnet prompt. These codes are accepted only at the beginning of a line.",
53 "-S": "-S &lt;tos&gt; Sets the IP type-of-service (TOS) option for the telnet connection to the value tos.",
54 "-e": "-e &lt;escapechar&gt; Sets the escape character to escapechar. If no character is supplied, no escape character will be used. Entering the escape character while connected causes telnet to drop to command mode.",
55 "-l": "-l &lt;user&gt; Specify user as the user to log in as on the remote system. This is accomplished by sending the specified name as the USER environment variable, so it requires that the remote system support the TELNET ENVIRON option. This option implies the -a option, and may also be used with the open command.",
56 "-n": "-n &lt;tracefile&gt; Opens tracefile for recording trace information. See the set tracefile command below.",
57 }
58
59 global current_path
60
61 def resolve(self, host):
62 try:
63 return socket.gethostbyname(host)
64 except:
65 pass
66 return host
67
68 def parseOutputString(self, output, debug=False):
69
70 host_info = re.search(r"Connected to (.+)\.", output)
71
72 hostname = host_info.group(1)
73 ip_address = self.resolve(hostname)
74
75 if host_info is not None:
76 h_id = self.createAndAddHost(ip_address)
77 i_id = self.createAndAddInterface(
78 h_id, ip_address, ipv4_address=ip_address, hostname_resolution=[hostname])
79 s_id = self.createAndAddServiceToInterface(h_id, i_id, self._port,
80 "tcp",
81 ports=[self._port],
82 status="open")
83 return True
84
85 def processCommandString(self, username, current_path, command_string):
86
87 count_args = command_string.split()
88
89 c = count_args.__len__()
90 self._port = "23"
91 if re.search(r"[\d]+", count_args[c - 1]):
92 self._port = count_args[c - 1]
93
94
95 def createPlugin():
96 return TelnetRouterPlugin()
97
98 if __name__ == "__main__":
99 import sys
100 import os
101 if len(sys.argv) == 2:
102 report_file = sys.argv[1]
103 if os.path.isfile(report_file):
104 plugin = createPlugin()
105 plugin.processReport(report_file)
106 print(plugin.get_json())
107 else:
108 print(f"Report not found: {report_file}")
109 else:
110 print(f"USAGE {sys.argv[0]} REPORT_FILE")
111 # 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 # 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 from faraday_plugins.plugins.plugin import PluginBase
7 import re
8 import os
9
10
11
12 current_path = os.path.abspath(os.getcwd())
13
14 __author__ = "Francisco Amato"
15 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
16 __credits__ = ["Francisco Amato"]
17 __license__ = ""
18 __version__ = "1.0.0"
19 __maintainer__ = "Francisco Amato"
20 __email__ = "[email protected]"
21 __status__ = "Development"
22
23
24 class TheharvesterParser:
25 """
26 The objective of this class is to parse an xml file generated by the theharvester tool.
27
28 TODO: Handle errors.
29 TODO: Test theharvester output version. Handle what happens if the parser doesn't support it.
30 TODO: Test cases.
31
32 @param theharvester_filepath A proper simple report generated by theharvester
33 """
34
35 def __init__(self, output):
36
37 self.items = []
38 _hosts, _vhosts = [], []
39
40 mregex = re.search(
41 "\[\+\] Hosts found in search engines:[-=\s]+([\w\W]*)\[\+\]", output)
42 if mregex is None:
43 mregex = re.search(
44 "\[\+\] Hosts found in search engines:[-=\s]+([\w\W]*)\n", output)
45 mregex2 = re.search("\[\+\] Virtual hosts:[-=\s]+([\w\W]*)\n", output)
46
47 if mregex is None and mregex2 is None:
48 return
49
50 if mregex:
51 _hosts = mregex.group(1).strip().split("\n")
52 if mregex2:
53 _vhosts = mregex2.group(1).strip().split("\n")
54
55 for line in _hosts:
56
57 info = line.split(":")
58
59 if len(info) > 1:
60 item = {'host': info[1].strip(), 'ip': info[0].strip()}
61
62 self.items.append(item)
63
64 for line in _vhosts:
65
66 info = line.split()
67 if len(info) > 1:
68 item = {'host': info[1].strip(), 'ip': info[0].strip()}
69
70 self.items.append(item)
71
72
73 class TheharvesterPlugin(PluginBase):
74 """
75 Example plugin to parse theharvester output.
76 """
77
78 def __init__(self):
79 super().__init__()
80 self.id = "Theharvester"
81 self.name = "Theharvester XML Output Plugin"
82 self.plugin_version = "0.0.1"
83 self.version = "2.2a"
84 self.options = None
85 self._current_output = None
86 self._current_path = None
87 self._command_regex = re.compile(
88 r'^(theharvester|sudo theharvester|sudo theHarvester\.py|theHarvester\.py|python theHarvester\.py|\.\/theHarvester\.py).*?')
89 self._completition = {
90 "": "Examples:./theharvester.py -d microsoft.com -l 500 -b google",
91 "-d": "Domain to search or company name",
92 "-b": "Data source (google,bing,bingapi,pgp,linkedin,google-profiles,exalead,all)",
93 "-s": "Start in result number X (default 0)",
94 "-v": "Verify host name via dns resolution and search for vhosts(basic)",
95 "-l": "Limit the number of results to work with(bing goes from 50 to 50 results,",
96 "-f": "Save the results into an XML file",
97 "-n": "Perform a DNS reverse query on all ranges discovered",
98 "-c": "Perform a DNS brute force for the domain name",
99 "-t": "Perform a DNS TLD expansion discovery",
100 "-e": "Use this DNS server",
101 "-h": "use SHODAN database to query discovered hosts. google 100 to 100, and pgp doesn't use this option)",
102 }
103
104 global current_path
105
106 def parseOutputString(self, output, debug=False):
107 """
108 This method will discard the output the shell sends, it will read it from
109 the xml where it expects it to be present.
110
111 NOTE: if 'debug' is true then it is being run from a test case and the
112 output being sent is valid.
113 """
114
115 print("este es el output (%s)" % output)
116
117 if debug:
118 parser = TheharvesterParser(output)
119 else:
120
121 parser = TheharvesterParser(output)
122
123 print(len(parser.items))
124 for item in parser.items:
125 host = []
126 if item['host'] != item['ip']:
127 host = [item['host']]
128 h_id = self.createAndAddHost(item['ip'])
129 i_id = self.createAndAddInterface(h_id, item['ip'], ipv4_address=item[
130 'ip'], hostname_resolution=host)
131
132 del parser
133
134 def processCommandString(self, username, current_path, command_string):
135 """
136 """
137 return None
138
139
140 def createPlugin():
141 return TheharvesterPlugin()
142
143 if __name__ == "__main__":
144 import sys
145 import os
146 if len(sys.argv) == 2:
147 report_file = sys.argv[1]
148 if os.path.isfile(report_file):
149 plugin = createPlugin()
150 plugin.processReport(report_file)
151 print(plugin.get_json())
152 else:
153 print(f"Report not found: {report_file}")
154 else:
155 print(f"USAGE {sys.argv[0]} REPORT_FILE")
156
157
158 # 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 # 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 import re
7 from faraday_plugins.plugins.plugin import PluginBase
8
9 __author__ = "Ezequiel Tavella - @EzequielTBH"
10 __copyright__ = "Copyright 2015, @EzequielTBH"
11 __credits__ = "Ezequiel Tavella - @EzequielTBH"
12 __license__ = "GPL v3"
13 __version__ = "1.0.0"
14
15
16 class traceroutePlugin(PluginBase):
17
18 def __init__(self):
19 super().__init__()
20 self.id = "Traceroute"
21 self.name = "Traceroute"
22 self.plugin_version = "1.0.0"
23 self.command_string = ""
24 self._command_regex = re.compile(
25 r'^(traceroute|traceroute6).*?')
26
27 def parseOutputString(self, output, debug=False):
28
29 print("[*]Parsing Output...")
30
31 # Check no results.
32 if not output.startswith("traceroute to"):
33 return
34
35 # Check if last parameter is host or ( packetlen or data size).
36 parameters = self.command_string.split(' ')
37 parameters.reverse()
38 hostName = parameters[0]
39
40 try:
41 int(hostName)
42 # No exception => host is the next item.
43 hostName = parameters[1]
44 except:
45 pass
46
47 # Add host and note with output of traceroute.
48 hostId = self.createAndAddHost(hostName)
49 self.createAndAddNoteToHost(hostId, "Traceroute Results", output)
50
51 print("[*]Parse finished, API faraday called...")
52
53 def processCommandString(self, username, current_path, command_string):
54
55 print("[*]traceroute Plugin running...")
56 self.command_string = command_string
57 return command_string
58
59
60 def createPlugin():
61 return traceroutePlugin()
62
63
64 if __name__ == "__main__":
65 import sys
66 import os
67 if len(sys.argv) == 2:
68 report_file = sys.argv[1]
69 if os.path.isfile(report_file):
70 plugin = createPlugin()
71 plugin.processReport(report_file)
72 print(plugin.get_json())
73 else:
74 print(f"Report not found: {report_file}")
75 else:
76 print(f"USAGE {sys.argv[0]} REPORT_FILE")
77 # 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 # 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 PluginXMLFormat
8 import re
9 import os
10 import socket
11 import pprint
12 import sys
13
14 try:
15 import xml.etree.cElementTree as ET
16 import xml.etree.ElementTree as ET_ORIG
17 ETREE_VERSION = ET_ORIG.VERSION
18 except ImportError:
19 import xml.etree.ElementTree as ET
20 ETREE_VERSION = ET.VERSION
21
22 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
23
24 current_path = os.path.abspath(os.getcwd())
25
26 __author__ = "Francisco Amato"
27 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
28 __credits__ = ["Francisco Amato"]
29 __license__ = ""
30 __version__ = "1.0.0"
31 __maintainer__ = "Francisco Amato"
32 __email__ = "[email protected]"
33 __status__ = "Development"
34
35
36 class W3afXmlParser:
37 """
38 The objective of this class is to parse an xml file generated by the w3af tool.
39
40 TODO: Handle errors.
41 TODO: Test w3af output version. Handle what happens if the parser doesn't support it.
42 TODO: Test cases.
43
44 @param w3af_xml_filepath A proper xml generated by w3af
45 """
46
47 def __init__(self, xml_output):
48 self.target = None
49 self.port = "80"
50 self.host = None
51
52 tree = self.parse_xml(xml_output)
53
54 if tree:
55 self.items = [data for data in self.get_items(tree)]
56 else:
57 self.items = []
58
59 def parse_xml(self, xml_output):
60 """
61 Open and parse an xml file.
62
63 TODO: Write custom parser to just read the nodes that we need instead of
64 reading the whole file.
65
66 @return xml_tree An xml tree instance. None if error.
67 """
68 try:
69 tree = ET.fromstring(xml_output)
70 except SyntaxError as err:
71 print("SyntaxError: %s. %s" % (err, xml_output))
72 return None
73
74 return tree
75
76 def get_items(self, tree):
77 """
78 @return items A list of Host instances
79 """
80 bugtype = ""
81
82 if len(tree.findall('scan-info')) == 0:
83 scaninfo = tree.findall('scaninfo')[0]
84 else:
85 scaninfo = tree.findall('scan-info')[0]
86
87 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)
97
98 for node in tree.findall('vulnerability'):
99 yield Item(node)
100 for node in tree.findall('information'):
101 yield Item(node)
102
103
104 def get_attrib_from_subnode(xml_node, subnode_xpath_expr, attrib_name):
105 """
106 Finds a subnode in the item node and the retrieves a value from it
107
108 @return An attribute value
109 """
110 global ETREE_VERSION
111 node = None
112
113 if ETREE_VERSION[0] <= 1 and ETREE_VERSION[1] < 3:
114
115 match_obj = re.search(
116 "([^\@]+?)\[\@([^=]*?)=\'([^\']*?)\'", subnode_xpath_expr)
117 if match_obj is not None:
118 node_to_find = match_obj.group(1)
119 xpath_attrib = match_obj.group(2)
120 xpath_value = match_obj.group(3)
121 for node_found in xml_node.findall(node_to_find):
122 if node_found.attrib[xpath_attrib] == xpath_value:
123 node = node_found
124 break
125 else:
126 node = xml_node.find(subnode_xpath_expr)
127
128 else:
129 node = xml_node.find(subnode_xpath_expr)
130
131 if node is not None:
132 return node.get(attrib_name)
133
134 return None
135
136
137 class Item:
138 """
139 An abstract representation of a Item
140
141
142 @param item_node A item_node taken from an w3af xml tree
143 """
144
145 def __init__(self, item_node):
146 self.node = item_node
147
148 self.id = self.node.get('id')
149 self.name = self.node.get('name')
150 self.url = self.node.get('url')
151 self.url = self.url if self.url != 'None' else "/"
152 self.plugin = self.node.get('plugin')
153 self.detail = self.get_text_from_subnode('description')
154 self.resolution = self.get_text_from_subnode('fix-guidance')
155 self.fix_effort = self.get_text_from_subnode('fix-effort')
156 self.longdetail = self.get_text_from_subnode('description')
157 self.severity = self.node.get('severity')
158 self.method = self.node.get('method')
159 self.ref = []
160 self.param = self.node.get('var') if self.node.get(
161 'var') != "None" else ""
162 for ref in self.node.findall('references/reference'):
163 self.ref.append(ref.get('url'))
164
165 self.req = self.resp = ''
166 for tx in self.node.findall('http-transactions/http-transaction'):
167 if tx.find('http-request'):
168 hreq = tx.find('http-request')
169 else:
170 hreq = tx.find('httprequest')
171
172 if tx.find('http-response'):
173 hres = tx.find('http-response')
174 else:
175 hres = tx.find('httpresponse')
176
177 self.req = hreq.find('status').text
178 for h in hreq.findall('headers/header'):
179 self.req += "\n%s: %s" % (h.get('field'), h.get('content'))
180
181 self.resp = hres.find('status').text
182 for h in hres.findall('headers/header'):
183 self.resp += "\n%s: %s" % (h.get('field'), h.get('content'))
184
185 if hres.find('body'):
186 self.resp += "\n%s" % hres.find('body').text
187
188 def do_clean(self, value):
189 myreturn = ""
190 if value is not None:
191 myreturn = re.sub("\n", "", value)
192 return myreturn
193
194 def get_text_from_subnode(self, subnode_xpath_expr):
195 """
196 Finds a subnode in the host node and the retrieves a value from it.
197
198 @return An attribute value
199 """
200 sub_node = self.node.find(subnode_xpath_expr)
201 if sub_node is not None:
202 return sub_node.text
203
204 return None
205
206
207 class W3afPlugin(PluginXMLFormat):
208 """
209 Example plugin to parse w3af output.
210 """
211
212 def __init__(self):
213 super().__init__()
214 self.identifier_tag = ["w3af-run", "w3afrun"]
215 self.id = "W3af"
216 self.name = "W3af XML Output Plugin"
217 self.plugin_version = "0.0.2"
218 self.version = "1.7.6"
219 self.framework_version = "1.0.0"
220 self.options = None
221 self._current_output = None
222 self.target = None
223 self._command_regex = re.compile(r'^(w3af|sudo w3af|\.\/w3af).*?')
224 self._completition = {
225 "": "",
226 "-h": "Display this help message.",
227 }
228
229
230 def parseOutputString(self, output, debug=False):
231
232 parser = W3afXmlParser(output)
233
234 ip = self.resolve(parser.host)
235 h_id = self.createAndAddHost(ip)
236 i_id = self.createAndAddInterface(
237 h_id, ip, ipv4_address=ip, hostname_resolution=[parser.host])
238 s_id = self.createAndAddServiceToInterface(h_id, i_id, "http",
239 "tcp",
240 ports=[parser.port],
241 status="open")
242
243 for item in parser.items:
244 v_id = self.createAndAddVulnWebToService(h_id, s_id, item.name,
245 item.detail, pname=item.param, path=item.url, website=parser.host, severity=item.severity,
246 method=item.method, request=item.req, resolution=item.resolution, ref=item.ref, response=item.resp)
247 del parser
248
249 def resolve(self, host):
250 try:
251 return socket.gethostbyname(host)
252 except:
253 pass
254 return host
255
256 def processCommandString(self, username, current_path, command_string):
257 return None
258
259 def setHost(self):
260 pass
261
262
263 def createPlugin():
264 return W3afPlugin()
265
266 if __name__ == "__main__":
267 import sys
268 import os
269 if len(sys.argv) == 2:
270 report_file = sys.argv[1]
271 if os.path.isfile(report_file):
272 plugin = createPlugin()
273 plugin.processReport(report_file)
274 print(plugin.get_json())
275 else:
276 print(f"Report not found: {report_file}")
277 else:
278 print(f"USAGE {sys.argv[0]} REPORT_FILE")
279
280 # 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 # 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 import re
7 import os
8 import socket
9
10 from urllib.parse import urlparse
11 from faraday_plugins.plugins.plugin import PluginXMLFormat
12 try:
13 import xml.etree.cElementTree as ET
14 import xml.etree.ElementTree as ET_ORIG
15 ETREE_VERSION = ET_ORIG.VERSION
16 except ImportError:
17 import xml.etree.ElementTree as ET
18 ETREE_VERSION = ET.VERSION
19
20 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
21
22 current_path = os.path.abspath(os.getcwd())
23
24 __author__ = "Francisco Amato"
25 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
26 __credits__ = ["Francisco Amato"]
27 __license__ = ""
28 __version__ = "1.0.0"
29 __maintainer__ = "Francisco Amato"
30 __email__ = "[email protected]"
31 __status__ = "Development"
32
33
34 class WapitiXmlParser:
35 """
36 The objective of this class is to parse an xml file generated by the wapiti tool.
37
38 TODO: Handle errors.
39 TODO: Test wapiti output version. Handle what happens if the parser doesn't support it.
40 TODO: Test cases.
41
42 @param wapiti_xml_filepath A proper xml generated by wapiti
43 """
44
45 def __init__(self, xml_output):
46 tree = self.parse_xml(xml_output)
47 if tree:
48 self.items = [data for data in self.get_items(tree)]
49 else:
50 self.items = []
51
52 def parse_xml(self, xml_output):
53 """
54 Open and parse an xml file.
55
56 TODO: Write custom parser to just read the nodes that we need instead of
57 reading the whole file.
58
59 @return xml_tree An xml tree instance. None if error.
60 """
61 try:
62 tree = ET.fromstring(xml_output)
63 except SyntaxError as err:
64 print("SyntaxError: %s. %s" % (err, xml_output))
65 return None
66
67 return tree
68
69 def get_items(self, tree):
70 """
71 @return items A list of Host instances
72 """
73
74 yield Item(tree)
75
76
77
78 def get_attrib_from_subnode(xml_node, subnode_xpath_expr, attrib_name):
79 """
80 Finds a subnode in the item node and the retrieves a value from it
81
82 @return An attribute value
83 """
84 global ETREE_VERSION
85 node = None
86
87 if ETREE_VERSION[0] <= 1 and ETREE_VERSION[1] < 3:
88 match_obj = re.search(
89 "([^\@]+?)\[\@([^=]*?)=\'([^\']*?)\'", subnode_xpath_expr)
90 if match_obj is not None:
91 node_to_find = match_obj.group(1)
92 xpath_attrib = match_obj.group(2)
93 xpath_value = match_obj.group(3)
94 for node_found in xml_node.findall(node_to_find):
95 if node_found.attrib[xpath_attrib] == xpath_value:
96 node = node_found
97 break
98 else:
99 node = xml_node.find(subnode_xpath_expr)
100 else:
101 node = xml_node.find(subnode_xpath_expr)
102 if node is not None:
103 return node.get(attrib_name)
104 return None
105
106
107 class Item:
108 """
109 An abstract representation of a Item
110
111 TODO: Consider evaluating the attributes lazily
112 TODO: Write what's expected to be present in the nodes
113 TODO: Refactor both Host and the Port clases?
114
115 @param item_node A item_node taken from an wapiti xml tree
116 """
117
118 def __init__(self, item_node):
119 self.node = item_node
120 self.url = self.get_url(item_node)
121 self.ip = socket.gethostbyname(self.url.hostname)
122 self.hostname = self.url.hostname
123 self.port = self.get_port(self.url)
124 self.scheme = self.url.scheme
125 self.vulns = self.get_vulns(item_node)
126
127 def do_clean(self, value):
128 myreturn = ""
129 if value is not None:
130 myreturn = re.sub("\n", "", value)
131 return myreturn
132
133 def get_text_from_subnode(self, node, subnode_xpath_expr):
134 """
135 Finds a subnode in the host node and the retrieves a value from it.
136
137 @return An attribute value
138 """
139 sub_node = node.find(subnode_xpath_expr)
140 if sub_node is not None:
141 return sub_node.text.strip()
142
143 return None
144
145 def get_url(self, item_node):
146 target = self.get_info(item_node, 'target')
147 return urlparse(target)
148
149 def get_info(self, item_node,name):
150 path = item_node.findall('report_infos/info')
151
152 for item in path:
153 if item.attrib['name'] == name:
154 return item.text
155
156 def get_port(self, url):
157 if url.port:
158 return url.port
159 else:
160 if url.scheme == "http":
161 return "80"
162 elif url.scheme == "https":
163 return "443"
164
165 def get_vulns(self, item_node):
166 vulns_node = item_node.findall('vulnerabilities/vulnerability')
167 vulns_list = []
168
169 for vuln in vulns_node:
170 vulns_dict = {}
171 vulns_dict['id'] = vuln.attrib['name']
172 vulns_dict['description'] = self.get_text_from_subnode(vuln,'description')
173 vulns_dict['solution'] = self.get_text_from_subnode(vuln,'solution')
174 vulns_dict['references'] = self.get_references(vuln)
175 vulns_dict['entries'] = self.get_entries(vuln)
176 vulns_list.append(vulns_dict)
177
178 return vulns_list
179
180 def get_references(self, node):
181 refs = node.findall('references/reference')
182 references_list = []
183 for ref in refs:
184 references_list.append('Title: ' + self.get_text_from_subnode(ref,'title'))
185 references_list.append('URL: ' + self.get_text_from_subnode(ref,'url'))
186
187 return references_list
188
189 def get_entries(self,node):
190 entries = node.findall('entries/entry')
191 entries_list = []
192 for entry in entries:
193 entries_dict = {}
194 entries_dict['method'] = self.get_text_from_subnode(entry,'method')
195 entries_dict['path'] = self.get_text_from_subnode(entry,'path')
196 entries_dict['level'] = self.severity_format(entry)
197 entries_dict['parameter'] = self.get_text_from_subnode(entry,'parameter')
198 entries_dict['http_request'] = self.get_text_from_subnode(entry,'http_request')
199 entries_dict['curl_command'] = self.get_text_from_subnode(entry,'curl_command')
200 entries_list.append(entries_dict)
201
202 return entries_list
203
204 def severity_format(self, node):
205 """
206 Convert Nexpose severity format into Faraday API severity format
207
208 @return a severity
209 """
210 severity = self.get_text_from_subnode(node, 'level')
211
212 if severity == '1':
213 return 'high'
214 elif severity == '2':
215 return 'medium'
216 elif severity == '3':
217 return 'low'
218
219
220 class WapitiPlugin(PluginXMLFormat):
221 """
222 Example plugin to parse wapiti output.
223 """
224
225 def __init__(self):
226 super().__init__()
227 self.identifier_tag = "report"
228 self.id = "Wapiti"
229 self.name = "Wapiti XML Output Plugin"
230 self.plugin_version = "0.0.1"
231 self.version = "2.2.1"
232 self.options = None
233 self._current_output = None
234 self.protocol = None
235 self.host = None
236 self.port = "80"
237 self.xml_arg_re = re.compile(r"^.*(-oX\s*[^\s]+).*$")
238 self._command_regex = re.compile(
239 r'^(python wapiti|wapiti|sudo wapiti|sudo wapiti\.py|wapiti\.py|python wapiti\.py|\.\/wapiti\.py|wapiti|\.\/wapiti|python wapiti|python \.\/wapiti).*?')
240 self._completition = {
241 "": "python wapiti.py http://server.com/base/url/ [options]",
242 "-s": "&lt;url&gt; ",
243 "--start": "&lt;url&gt; ",
244 "-x": "&lt;url&gt; ",
245 "--exclude": "&lt;url&gt; ",
246 "-p": "&lt;url_proxy&gt; ",
247 "--proxy": "&lt;url_proxy&gt; ",
248 "-c": " -c &lt;cookie_file&gt; ",
249 "--cookie": "&lt;cookie_file&gt; ",
250 "-t": "&lt;timeout&gt; ",
251 "--timeout": "&lt;timeout&gt; ",
252 "-a": "&lt;login%password&gt; ",
253 "--auth": "&lt;login%password&gt; ",
254 "-r": "&lt;parameter_name&gt; ",
255 "--remove": "&lt;parameter_name&gt; ",
256 "-n": "&lt;limit&gt; ",
257 "--nice": "&lt;limit&gt; ",
258 "-m": "&lt;module_options&gt; Set the modules and HTTP methods to use for attacks. Example: -m \"-all,xss:get,exec:post\"",
259 "--module": "&lt;module_options&gt; Set the modules and HTTP methods to use for attacks. Example: -m \"-all,xss:get,exec:post\"",
260 "-u": "Use color to highlight vulnerables parameters in output",
261 "--underline": "Use color to highlight vulnerables parameters in output",
262 "-v": "&lt;level&gt; ",
263 "--verbose": "&lt;level&gt; ",
264 "-b": "&lt;scope&gt;",
265 "--scope": "&lt;scope&gt;",
266 "-f": "&lt;type_file&gt; ",
267 "--reportType": "&lt;type_file&gt; ",
268 "-o": "&lt;output_file&gt; ",
269 "--output": "&lt;output_file&gt; ",
270 "-i": "&lt;file&gt;",
271 "--continue": "&lt;file&gt;",
272 "-k": "&lt;file&gt;",
273 "--attack": "&lt;file&gt;",
274 "-h": "To print this usage message",
275 "--help": "To print this usage message",
276 }
277
278 def report_belongs_to(self, **kwargs):
279 if super().report_belongs_to(**kwargs):
280 report_path = kwargs.get("report_path", "")
281 with open(report_path) as f:
282 output = f.read()
283 return re.search("Wapiti", output) is not None
284 return False
285
286
287 def parseOutputString(self, output):
288 """
289 This method will discard the output the shell sends, it will read it from
290 the xml where it expects it to be present.
291 """
292
293 parser = WapitiXmlParser(output)
294 for item in parser.items:
295 host_id = self.createAndAddHost(item.ip, hostnames=[item.hostname])
296 service_id = self.createAndAddServiceToHost(host_id, item.scheme, protocol='tcp', ports=[item.port])
297 for vuln in item.vulns:
298 for entry in vuln['entries']:
299 vuln_id = self.createAndAddVulnWebToService(host_id,
300 service_id,
301 vuln['id'],
302 desc=vuln['description'],
303 ref=vuln['references'],
304 resolution=vuln['solution'],
305 severity=entry['level'],
306 website=entry['curl_command'],
307 path=entry['path'],
308 request=entry['http_request'],
309 method=entry['method'],
310 params=entry['parameter'])
311
312 def processCommandString(self, username, current_path, command_string):
313 """
314 Adds the -oX parameter to get xml output to the command string that the
315 user has set.
316 """
317 host = re.search(
318 "(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;%\$#\=~_\-]+)).*?$", command_string)
319 self.protocol = host.group(1)
320 self.host = host.group(4)
321 if host.group(11) is not None:
322 self.port = host.group(11)
323 if self.protocol == 'https':
324 self.port = 443
325 self.logger.debug("host = %s, port = %s",self.host, self.port)
326 arg_match = self.xml_arg_re.match(command_string)
327 return "%s -o %s -f xml \n" % (command_string, self._output_file_path)
328
329 def setHost(self):
330 pass
331
332
333 def createPlugin():
334 return WapitiPlugin()
335
336
337 if __name__ == "__main__":
338 import sys
339 import os
340 if len(sys.argv) == 2:
341 report_file = sys.argv[1]
342 if os.path.isfile(report_file):
343 plugin = createPlugin()
344 plugin.processReport(report_file)
345 print(plugin.get_json())
346 else:
347 print(f"Report not found: {report_file}")
348 else:
349 print(f"USAGE {sys.argv[0]} REPORT_FILE")
350 # 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 # 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 from faraday_plugins.plugins.plugin import PluginBase
6 import re
7 import os
8 import sys
9 import random
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
18
19 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
20
21 current_path = os.path.abspath(os.getcwd())
22
23 __author__ = "Morgan Lemarechal"
24 __copyright__ = "Copyright 2014, Faraday Project"
25 __credits__ = ["Morgan Lemarechal"]
26 __license__ = ""
27 __version__ = "1.0.0"
28 __maintainer__ = "Morgan Lemarechal"
29 __email__ = "[email protected]"
30 __status__ = "Development"
31
32
33 class WcscanParser:
34 """
35 The objective of this class is to parse an xml file generated by the wcscan tool.
36 TODO: Handle errors.
37 TODO: Test wcscan output version. Handle what happens if the parser doesn't support it.
38 TODO: Test cases.
39 @param wcscan_filepath A proper simple report generated by wcscan
40 """
41
42 def __init__(self, output):
43 self.scaninfo = {}
44 self.result = {}
45 tree = ET.parse(output)
46 root = tree.getroot()
47 for scan in root.findall(".//scan"):
48 infos = {}
49 for info in scan.attrib:
50 infos[info] = scan.attrib[info]
51 self.scaninfo[scan.attrib['file']] = infos
52
53 item = {}
54 if scan.attrib['type'] == "phpini":
55 for carac in scan:
56 item[carac.tag] = [carac.text, carac.attrib['rec'], ""]
57
58 if scan.attrib['type'] == "webconfig":
59 id = 0
60 for carac in scan:
61 id += 1
62 item[id] = [carac.text, carac.attrib['rec'],
63 carac.attrib['option'], carac.tag]
64
65 self.result[scan.attrib['file']] = item
66
67
68 class WcscanPlugin(PluginBase):
69 """
70 Example plugin to parse wcscan output.
71 """
72
73 def __init__(self):
74 super().__init__()
75 self.id = "Wcscan"
76 self.name = "Wcscan XML Output Plugin"
77 self.plugin_version = "0.0.2"
78 self.version = "0.30"
79 self._completition = {
80 "": "wcscan [-h] [-r] [-host HOST] [-port PORT] [--xml XMLOUTPUT] [--version] files [files ...]",
81 "-h": "show this help message and exit",
82 "-r": "enable the recommendation mode",
83 "--host": "to give the IP address of the conf file owner",
84 "--port": "to give a associated port",
85 "--xml": "enabled the XML output in a specified file",
86 "--version": "Show program's version number and exit",
87 }
88
89 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).*?')
94
95
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):
103 """
104 This method will discard the output the shell sends, it will read it from
105 the xml where it expects it to be present.
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 if debug:
110 parser = WcscanParser(self._output_file_path)
111 else:
112
113 if not os.path.exists(self._output_file_path):
114 return False
115 parser = WcscanParser(self._output_file_path)
116
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)
129
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)
142
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]+).*$")
159
160 def processCommandString(self, username, current_path, command_string):
161 """
162 Adds the parameter to get output to the command string that the
163 user has set.
164 """
165
166 arg_match = self.xml_arg_re.match(command_string)
167
168 if arg_match is None:
169 return "%s --xml %s" % (command_string, self._output_file_path)
170 else:
171 return re.sub(arg_match.group(1),
172 r"-xml %s" % self._output_file_path,
173 command_string)
174
175
176 def createPlugin():
177 return WcscanPlugin()
178
179 if __name__ == "__main__":
180 import sys
181 import os
182 if len(sys.argv) == 2:
183 report_file = sys.argv[1]
184 if os.path.isfile(report_file):
185 plugin = createPlugin()
186 plugin.processReport(report_file)
187 print(plugin.get_json())
188 else:
189 print(f"Report not found: {report_file}")
190 else:
191 print(f"USAGE {sys.argv[0]} REPORT_FILE")
192
193 # 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 # 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 from faraday_plugins.plugins.plugin import PluginBase
7 import re
8 import os
9
10
11
12 current_path = os.path.abspath(os.getcwd())
13
14 __author__ = "Francisco Amato"
15 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
16 __credits__ = ["Francisco Amato"]
17 __license__ = ""
18 __version__ = "1.0.0"
19 __maintainer__ = "Francisco Amato"
20 __email__ = "[email protected]"
21 __status__ = "Development"
22
23
24 class WebfuzzerParser:
25 """
26 The objective of this class is to parse an xml file generated by the webfuzzer tool.
27
28 TODO: Handle errors.
29 TODO: Test webfuzzer output version. Handle what happens if the parser doesn't support it.
30 TODO: Test cases.
31
32 @param webfuzzer_filepath A proper output generated by webfuzzer
33 """
34
35 def __init__(self, webfuzzer_filepath):
36 self.filepath = webfuzzer_filepath
37
38 with open(self.filepath, "r") as f:
39 try:
40 data = f.read()
41 f.close()
42 m = re.search(
43 "Scan of ([\w.]+):([\d]+) \[([/\w]+)\] \(([\w.]+)\)", data)
44 self.hostname = m.group(1)
45 self.port = m.group(2)
46 self.uri = m.group(3)
47 self.ipaddress = m.group(4)
48
49 m = re.search("Server header:\n\n([\w\W]+)\n\n\n", data)
50 self.header = m.group(1)
51
52 self.items = []
53
54 pattern = r'\((POST|GET)\): ([\w\W]*?) \]--'
55
56 for m in re.finditer(pattern, data, re.DOTALL):
57
58 method = m.group(1)
59 info = re.search(
60 "^([\w\W]+)\(([\w\W]+)\)\n--\[ ([\w\W]+)$", m.group(2))
61
62 vuln = {'method': m.group(1), 'desc': info.group(
63 1), 'url': info.group(2), 'resp': info.group(3)}
64 self.items.append(vuln)
65
66 except SyntaxError as err:
67 print("SyntaxError: %s. %s" % (err, self.filepath))
68 return None
69
70
71 class WebfuzzerPlugin(PluginBase):
72 """
73 Example plugin to parse webfuzzer output.
74 """
75
76 def __init__(self):
77 super().__init__()
78 self.id = "Webfuzzer"
79 self.name = "Webfuzzer Output Plugin"
80 self.plugin_version = "0.0.2"
81 self.version = "0.2.0"
82 self.options = None
83 self._current_output = None
84 self.host = None
85 self._command_regex = re.compile(
86 r'^(sudo webfuzzer|webfuzzer|\.\/webfuzzer).*?')
87 self._completition = {'': '__Usage: ./webfuzzer -G|-P URL [OPTIONS]',
88 '-G': '<url> get this as starting url (with parameters)',
89 '-P': '<url> post this as starting url (with parameters)',
90 '-x': 'html output (txt default)',
91 '-c': 'use cookies',
92 '-C': '<cookies> set this cookie(s) **',
93 '-s': 'check for sql, asp, vb, php errors (default)',
94 '-d': 'check for directory traversal *',
95 '-p': 'check for insecure perl open or xss *',
96 '-e': 'check for execution through shell escapes or xss *',
97 '-a': 'set all of the above switches on *',
98 }
99
100 self._output_path = None
101
102 def parseOutputString(self, output, debug=False):
103 """
104 This method will discard the output the shell sends, it will read it from
105 the xml where it expects it to be present.
106
107 NOTE: if 'debug' is true then it is being run from a test case and the
108 output being sent is valid.
109 """
110
111 if self._output_path is None:
112 return False
113 else:
114 if not os.path.exists(self._output_path):
115 return False
116
117 parser = WebfuzzerParser(self._output_path)
118
119 h_id = self.createAndAddHost(parser.ipaddress)
120
121 i_id = self.createAndAddInterface(
122 h_id, parser.ipaddress, ipv4_address=parser.ipaddress, hostname_resolution=[parser.hostname])
123
124 first = True
125 for item in parser.items:
126 if first:
127 s_id = self.createAndAddServiceToInterface(h_id, i_id, parser.port,
128 "tcp",
129 ports=[parser.port])
130 first = False
131
132 v_id = self.createAndAddVulnWebToService(h_id, s_id, name=item['desc'],
133 path=item['url'], response=item[
134 'resp'],
135 method=item['method'], website=parser.hostname)
136
137 del parser
138
139 return True
140
141 def processCommandString(self, username, current_path, command_string):
142 """
143 """
144 host = re.search("\-([G|P]) ([\w\.\-]+)", command_string)
145
146 if host is not None:
147 self.host = host.group(2)
148 self._output_path = current_path + "/" + self.host + ".txt"
149
150
151 def createPlugin():
152 return WebfuzzerPlugin()
153
154 if __name__ == "__main__":
155 import sys
156 import os
157 if len(sys.argv) == 2:
158 report_file = sys.argv[1]
159 if os.path.isfile(report_file):
160 plugin = createPlugin()
161 plugin.processReport(report_file)
162 print(plugin.get_json())
163 else:
164 print(f"Report not found: {report_file}")
165 else:
166 print(f"USAGE {sys.argv[0]} REPORT_FILE")
167
168 # I'm Py3
0 """
1 Faraday Penetration Test IDE
2 Copyright (C) 2018 Infobyte LLC (http://www.infobytesec.com/)
3 See the file 'doc/LICENSE' for the license information
4 """
5 import re
6
7 from faraday_plugins.plugins.plugin import PluginBase
8 from faraday_plugins.plugins.plugins_utils import get_vulnweb_url_fields
9
10 try:
11 import xml.etree.ElementTree as ET
12 except ImportError:
13 import xml.etree.ElementTree as ET
14
15
16 def cleanhtml(raw_html):
17 cleanr = re.compile('<.*?>')
18 cleantext = re.sub(cleanr, '', raw_html)
19 return cleantext
20
21
22 class WebInspectParser():
23
24 def __init__(self, output):
25 self.xml = ET.fromstring(output)
26 self.issues = self.xml.findall("Issues/Issue")
27
28
29 def parse_severity(self, severity):
30
31 severity_dict = {
32 "0": "info",
33 "1": "low",
34 "2": "med",
35 "3": "high",
36 "4": "critical"}
37
38 result = severity_dict.get(severity)
39 if not result:
40 return "info"
41 else:
42 return result
43
44 def return_text(self, tag,element):
45 try:
46 text = element.find(tag).text.encode("ascii", errors="backslashreplace")
47 return text
48 except:
49 return ""
50
51 def parse(self):
52
53 map_objects_fields = {
54 "Name": ["Vuln", "name"],
55 "URL": ["Vuln", "website"],
56 "Scheme": ["Service", "name"],
57 "Host": ["Host", "name"],
58 "Port": ["Service", "port"],
59 "AttackMethod": ["Vuln", "method"],
60 "VulnerableSession": ["Vuln", "request"],
61 "VulnerabilityID": ["Vuln", "reference"],
62 "RawResponse": ["Vuln", "response"],
63 "Summary": ["Vuln", "description"],
64 "Implication": ["Vuln", "data"],
65 "Fix": ["Vuln", "resolution"],
66 "Reference Info": ["Vuln", "reference"],
67 "Severity": ["Vuln", "severity"]
68 }
69
70 result = []
71 for issue in self.issues:
72
73 obj = {
74 "Host" : {},
75 "Service" : {},
76 "Interface" : {},
77 "Vuln": {
78 "reference" : []}
79 }
80
81 for tag, obj_property in map_objects_fields.items():
82
83 value = self.return_text(tag,issue)
84
85 if value is not None:
86
87 faraday_obj_name = obj_property[0]
88 faraday_field = obj_property[1]
89 if faraday_field == "reference":
90 obj[faraday_obj_name].get("reference").append(value)
91 else:
92 obj[faraday_obj_name].update({faraday_field:value})
93
94 # This for loads Summary, Implication, Fix and Reference
95 for section in issue.findall("ReportSection"):
96
97 try:
98 field = section.find("Name").text.encode("ascii", errors="backslashreplace")
99 value = section.find("SectionText").text.encode("ascii", errors="backslashreplace")
100
101 faraday_obj_name = map_objects_fields.get(field)[0]
102 faraday_field = map_objects_fields.get(field)[1]
103 except:
104 continue
105
106 if faraday_field == "reference" and value != "":
107 obj[faraday_obj_name].get("reference").append(cleanhtml(value))
108 else:
109 obj[faraday_obj_name].update({faraday_field:value})
110
111 result.append(obj)
112 return result
113
114
115 class WebInspectPlugin(PluginBase):
116 """
117 This plugin handles WebInspect reports.
118 """
119
120 def __init__(self):
121 super().__init__()
122 self.id = "Webinspect"
123 self.name = "Webinspect"
124 self.plugin_version = "0.0.1"
125 self.version = "1.0.0"
126
127 def parseOutputString(self, output, debug=False):
128
129 parser = WebInspectParser(output)
130 vulns = parser.parse()
131
132 for vuln in vulns:
133
134 host_id = self.createAndAddHost(
135 vuln.get("Host").get("name"))
136
137 interface_id = self.createAndAddInterface(
138 host_id, vuln.get("Host").get("name"))
139
140 service_id = self.createAndAddServiceToInterface(
141 host_id, interface_id,
142 vuln.get("Service").get("name"),
143 protocol=vuln.get("Service").get("name"),
144 ports=[vuln.get("Service").get("port")])
145
146 self.createAndAddVulnWebToService(
147 host_id, service_id,
148 vuln.get("Vuln").get("name"),
149 website=get_vulnweb_url_fields(vuln.get("Vuln").get("website")).get("website"),
150 path=get_vulnweb_url_fields(vuln.get("Vuln").get("website")).get("path"),
151 query=get_vulnweb_url_fields(vuln.get("Vuln").get("website")).get("query"),
152 method=vuln.get("Vuln").get("method"),
153 request=vuln.get("Vuln").get("request"),
154 ref=list(filter(None ,vuln.get("Vuln").get("reference"))),
155 response=vuln.get("Vuln").get("response"),
156 desc=cleanhtml(vuln.get("Vuln").get("description")),
157 resolution=cleanhtml(vuln.get("Vuln").get("resolution")),
158 severity=parser.parse_severity(vuln.get("Vuln").get("severity"))
159 )
160
161 return True
162
163 def processCommandString(self, username, current_path, command_string):
164 return None
165
166
167 def createPlugin():
168 return WebInspectPlugin()
169
170
171 if __name__ == "__main__":
172 import sys
173 import os
174 if len(sys.argv) == 2:
175 report_file = sys.argv[1]
176 if os.path.isfile(report_file):
177 plugin = createPlugin()
178 plugin.processReport(report_file)
179 print(plugin.get_json())
180 else:
181 print(f"Report not found: {report_file}")
182 else:
183 print(f"USAGE {sys.argv[0]} REPORT_FILE")
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 import re
1 import string
2 from urllib.parse import urljoin, urlparse
3
4 from faraday_plugins.plugins.plugin import PluginBase
5
6
7 class WfuzzPlugin(PluginBase):
8
9 def __init__(self):
10 super().__init__()
11 self.id = "Wfuzz"
12 self.name = "Wfuzz Plugin"
13 self.plugin_version = "0.0.1"
14 self.version = "2.2.11"
15 self.options = None
16
17 self.host = None
18 self.port = None
19 self.protocol = None
20 self.fail = None
21 self._command_regex = re.compile(
22 r'^(wfuzz).*?')
23
24 def parseData(self, output):
25
26 data = {
27 'target' : '',
28 'findings' : []
29 }
30 for line in output:
31 # remove stdout hidden chars
32 line = ''.join([char for char in line if char in string.printable])
33 line = line.strip('\r').replace('[0K', '').replace('[0m', '')
34 if line.startswith('Target'):
35 data['target'] = line[8:].rstrip()
36 continue
37 if line.startswith('0'):
38 aux = line.split(' ')
39 res = {}
40 for item in aux:
41 if 'C=' in item:
42 res['response'] = int(item.replace('C=', ''))
43 elif 'L' in item and ' ' in item:
44 res['lines'] = int(item.replace('L', ''))
45 elif 'W' in item and ' ' in item:
46 res['words'] = int(item.replace('W', ''))
47 elif 'Ch' in item and ' ' in item:
48 res['chars'] = int(item.replace('Ch', ''))
49 else:
50 res['request'] = item.rstrip().replace('"', '')
51 data['findings'].append(res)
52
53 return data
54
55 def parseOutputString(self, output, debug=False):
56 output_list = output.split('\n')
57 info = self.parseData(output_list)
58
59 target = info['target']
60 target_url = urlparse(target)
61 port = 80
62
63 if target_url.scheme == 'https':
64 port = 443
65 custom_port = target_url.netloc.split(':')
66 if len(custom_port) > 1:
67 port = custom_port[1]
68
69 host_id = self.createAndAddHost(target)
70
71 service_id = self.createAndAddServiceToHost(host_id,name="http",protocol="tcp", ports=[port] )
72
73 for item in info['findings']:
74 path = item['request']
75 status = item['response']
76 url = urljoin(target, path)
77 lines = item['lines']
78 chars = item['chars']
79 words = item['words']
80 name = "Wfuzz found: {path} with status {status} on url {url}".format(path=path, status=status, url=url)
81 desc = 'Wfuzz found a response with status {status}. Response contains: \n* {words} words \n* {lines} lines \n* {chars} chars'.format(
82 words=words,
83 url=url,
84 lines=lines,
85 chars=chars,
86 status=status
87 )
88 self.createAndAddVulnWebToService(host_id,
89 service_id,
90 name,
91 desc,
92 severity="info",
93 website=target,
94 path=path
95 )
96
97
98 def createPlugin():
99 return WfuzzPlugin()
100
101
102 if __name__ == "__main__":
103 import sys
104 import os
105 if len(sys.argv) == 2:
106 report_file = sys.argv[1]
107 if os.path.isfile(report_file):
108 plugin = createPlugin()
109 plugin.processReport(report_file)
110 print(plugin.get_json())
111 else:
112 print(f"Report not found: {report_file}")
113 else:
114 print(f"USAGE {sys.argv[0]} REPORT_FILE")
115
116
117 # 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 # 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 from faraday_plugins.plugins.plugin import PluginBase
7 import re
8 import os
9 import socket
10 current_path = os.path.abspath(os.getcwd())
11
12 __author__ = "Facundo de Guzmán, Esteban Guillardoy"
13 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
14 __credits__ = ["Facundo de Guzmán", "Esteban Guillardoy"]
15 __license__ = ""
16 __version__ = "1.0.0"
17 __maintainer__ = "Federico Kirschbaum"
18 __email__ = "[email protected]"
19 __status__ = "Development"
20
21
22 class CmdWhoisPlugin(PluginBase):
23 """
24 This plugin handles whois command.
25 Basically detects if user was able to connect to a device
26 """
27
28 def __init__(self):
29 super().__init__()
30 self.id = "whois"
31 self.name = "Whois"
32 self.plugin_version = "0.0.1"
33 self.version = "5.0.20"
34 self.framework_version = "1.0.0"
35 self.options = None
36 self._current_output = None
37 self._command_regex = re.compile(r'^whois.*?')
38 self._host_ip = None
39 self._info = 0
40 self._completition = {
41 "": "whois [OPTION]... OBJECT...",
42 "-l": "one level less specific lookup [RPSL only]",
43 "-L": "find all Less specific matches",
44 "-m": "find first level more specific matches",
45 "-M": "find all More specific matches",
46 "-c": "find the smallest match containing a mnt-irt attribute",
47 "-x": "exact match [RPSL only]",
48 "-d": "return DNS reverse delegation objects too [RPSL only]",
49 "-i": "-i ATTR[,ATTR]... do an inverse lookup for specified ATTRibutes",
50 "-T": "-T TYPE[,TYPE]... only look for objects of TYPE",
51 "-K": "only primary keys are returned [RPSL only]",
52 "-r": "turn off recursive lookups for contact information",
53 "-R": "force to show local copy of the domain object even if it contains referral",
54 "-a": "search all databases",
55 "-s": "-s SOURCE[,SOURCE]... search the database from SOURCE",
56 "-g": "-g SOURCE:FIRST-LAST find updates from SOURCE from serial FIRST to LAST",
57 "-t": "-t TYPE request template for object of TYPE",
58 "-v": "-v TYPE request verbose template for object of TYPE",
59 "-q": "-q [version|sources|types] query specified server info [RPSL only]",
60 "-F": "fast raw output (implies -r)",
61 "-h": "-h HOST connect to server HOST",
62 "-p": "-p PORT connect to PORT",
63 "-H": "hide legal disclaimers",
64 "--verbose": "explain what is being done",
65 "--help": "display this help and exit",
66 "--version": "output version information and exit",
67 }
68
69 global current_path
70
71 def resolve(self, host):
72 try:
73 return socket.gethostbyname(host)
74 except:
75 pass
76 return host
77
78 def parseOutputString(self, output, debug=False):
79 matches = re.findall("Name Server:\s*(.*)\s*", output)
80 for m in matches:
81 m = m.strip()
82 ip = self.resolve(m)
83 h_id = self.createAndAddHost(ip, "os unknown")
84 i_id = self.createAndAddInterface(
85 h_id, ip, "00:00:00:00:00:00", ip, hostname_resolution=[m])
86 return True
87
88 def processCommandString(self, username, current_path, command_string):
89 """
90 """
91 return None
92
93
94 def createPlugin():
95 return CmdWhoisPlugin()
96
97 if __name__ == "__main__":
98 import sys
99 import os
100 if len(sys.argv) == 2:
101 report_file = sys.argv[1]
102 if os.path.isfile(report_file):
103 plugin = createPlugin()
104 plugin.processReport(report_file)
105 print(plugin.get_json())
106 else:
107 print(f"Report not found: {report_file}")
108 else:
109 print(f"USAGE {sys.argv[0]} REPORT_FILE")
110
111 # 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 # 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 """
6 import re
7 import socket
8 import json
9 from faraday_plugins.plugins.plugin import PluginBase
10
11
12 __author__ = "Joaquin L. Pereyra | Federico Fernandez"
13 __copyright__ = "Copyright (c) 2016, Infobyte LLC"
14 __credits__ = ["Joaquin L. Pereyra"]
15 __license__ = ""
16 __version__ = "0.0.1"
17 __maintainer__ = "Joaquin L. Pereyra"
18 __email__ = "[email protected]"
19 __status__ = "Development"
20
21
22 class WPScanPlugin(PluginBase):
23 """ Handle the WPScan tool. Detects the output of the tool
24 and adds the information to Faraday.
25 """
26
27 def __init__(self):
28 """Initalizes the plugin with some basic params.
29 Right now the plugin doesnt support being executed from another folder,
30 like /dir/wpscan.rb
31 """
32 super().__init__()
33 self.id = "wpscan"
34 self.name = "WPscan"
35 self.plugin_version = "0.2"
36 self.version = "3.4.5"
37 self._command_regex = re.compile(
38 r"^((sudo )?(ruby )?(\.\/)?(wpscan)(.rb)?)")
39 self.wpPath = self.get_wpscan_filepath()
40 self.addSetting("WPscan path", str, self.wpPath)
41 self.themes = {}
42 self.plugins = {}
43 self.wpversion = ''
44 self.risks = {'AUTHBYPASS': 'high',
45 'BYPASS': 'med',
46 'CSRF': 'med',
47 'DOS': 'med',
48 'FPD': 'info',
49 'LFI': 'high',
50 'MULTI': 'unclassified',
51 'OBJECTINJECTION': 'med',
52 'PRIVESC': 'high',
53 'RCE': 'critical',
54 'REDIRECT': 'low',
55 'RFI': 'critical',
56 'SQLI': 'high',
57 'SSRF': 'med',
58 'UNKNOWN': 'unclassified',
59 'UPLOAD': 'critical',
60 'XSS': 'high',
61 'XXE': 'high'}
62
63 def get_wpscan_filepath(self):
64 home = os.path.expanduser("~")
65
66 wpscan_path = os.path.join(home, '.wpscan')
67 if os.path.exists(wpscan_path):
68 return wpscan_path
69 else:
70 return None
71
72 def search_file_in_wpscan_folder(self, wp_file):
73 db_path = os.path.join(self.wpPath, 'db', wp_file)
74 data_path = os.path.join(self.wpPath, 'data', wp_file)
75 if os.path.exists(db_path):
76 return db_path
77 elif os.path.exists(data_path):
78 return data_path
79
80 def getPort(self, host, proto):
81 p = re.search(r"\:([0-9]+)\/", host)
82 if p is not None:
83 return p.group(1)
84 elif proto == 'https':
85 return 443
86 else:
87 return 80
88
89 def parseOutputWpscan(self, output):
90 sp = output.split('0m Name:') # cut by name
91 for e in sp:
92 if 'Title:' in e:
93 if 'WordPress version' in e:
94 r = re.search(r'WordPress version (\d.\w)', e) # get wordpress version
95 self.wpversion = r.group(1)
96
97 elif 'wp-content/themes/' in e:
98 name = re.findall(r"Location: .+themes\/(.+)\/", e) # get theme name
99 title = re.findall(r"Title: (.+)", e) # get vulnerabilities title
100 self.themes[name[0]] = title # insert theme in dicc {'themeName' : ['titles', 'titles']}
101
102 else:
103 name = re.findall(r"Location: .+plugins\/(.+)\/", e) # get plugin name
104 title = re.findall(r"Title: (.+)", e) # get vulnerabilities title
105 self.plugins[name[0]] = title # insert plugin in dicc {'plugin' : ['titles', 'titles']}
106
107 def addThemesOrPluginsVulns(self, wpscan_db_filename, dic, host_id, serv_id, domain, wp_url, name):
108 db_file_path = self.search_file_in_wpscan_folder(wpscan_db_filename)
109 with open(db_file_path, "r") as data:
110 j = json.load(data)
111 for p in dic:
112 for title in dic[p]:
113 for vuln in j[p]['vulnerabilities']: # iter vulnerabilities
114 if vuln['title'] == title: # if output title is equal
115 title = vuln['title'] # title
116 risk = self.risks[vuln['vuln_type']] # vuln type (xss,rce,lfi,etc) - risk
117 location = wp_url+'wp-content/'+name+'/'+p+'/'
118 if 'url' in vuln['references']: # if references
119 refs = vuln['references']['url'] #references[]
120 else:
121 refs = [] # references null
122 self.createAndAddVulnWebToService(
123 host_id,
124 serv_id,
125 title,
126 severity=risk,
127 website=domain,
128 ref=refs,
129 path=location)
130
131 def addWPVulns(self, wpscan_db_filename, version, host_id, serv_id, domain):
132 db_file_path = self.search_file_in_wpscan_folder(wpscan_db_filename)
133 with open(db_file_path, "r") as data:
134 j = json.load(data)
135 for vuln in j[version]['vulnerabilities']: # iter vulnerabilities
136 title = vuln['title'] # title
137 risk = self.risks[vuln['vuln_type']] # vuln type (xss,rce,lfi,etc) - risk
138 if 'url' in vuln['references']: # if references
139 refs = vuln['references']['url'] # references[]
140 else:
141 refs = [] # references null
142 self.createAndAddVulnWebToService(
143 host_id,
144 serv_id,
145 title,
146 severity=risk,
147 website=domain,
148 ref=refs)
149
150 def parseOutputString(self, output, debug=False):
151 """Parses the output given as a string by the wpscan tool and creates
152 the appropiate hosts, service and vulnerabilites. Return
153 nothing.
154 """
155 self.parseOutputWpscan(output)
156 wp_url = re.search(r"URL: ((http[s]?)\:\/\/([\w\.]+)[.\S]+)", output)
157 service, base_url = self.__get_service_and_url_from_output(output)
158 if service and base_url:
159 port = self.getPort(wp_url.group(1), service)
160 host_ip = socket.gethostbyname_ex(base_url)[2][0]
161 host_id = self.createAndAddHost(
162 host_ip,
163 hostnames=[base_url])
164
165 service_id = self.createAndAddServiceToHost(
166 host_id,
167 service,
168 "tcp",
169 ports=[port])
170
171 potential_vulns = re.findall(r"(\[\!\].*)", output)
172 for potential_vuln in potential_vulns:
173 vuln_name, severity = self.__get_name_and_severity(potential_vuln)
174 if vuln_name is not None:
175 vuln = potential_vuln # they grow up so fast
176 path = self.__get_path_from_vuln(vuln)
177 self.createAndAddVulnWebToService(
178 host_id,
179 service_id,
180 name=vuln_name,
181 website=base_url,
182 path=path,
183 severity=severity)
184
185 if len(self.plugins) > 0:
186 self.addThemesOrPluginsVulns(
187 'plugins.json',
188 self.plugins,
189 host_id,
190 service_id,
191 base_url,
192 wp_url.group(1),
193 'plugins')
194
195 if len(self.wpversion) > 0:
196 self.addWPVulns(
197 'wordpresses.json',
198 self.wpversion,
199 host_id,
200 service_id,
201 base_url)
202
203 if len(self.themes) > 0:
204 self.addThemesOrPluginsVulns(
205 'themes.json',
206 self.themes,
207 host_id,
208 service_id,
209 base_url,
210 wp_url.group(1),
211 'themes')
212
213 def __get_service_and_url_from_output(self, output):
214 """ Return the service (http or https) and the base URL (URL without
215 protocol) from a given string. In case more than one URL is found,
216 return the service and base_url of the first one, ignore others.
217 """
218 search_url = re.search(r"URL: ((http[s]?)\:\/\/([\w\.]+)[.\S]+)", output)
219 if not search_url:
220 return None, None
221 else:
222 service, base_url = search_url.group(2), search_url.group(3)
223 return service, base_url
224
225 def __get_name_and_severity(self, potential_vuln):
226 """Regex the potential_vuln string against a regex with all
227 the vulnerabilities given by WPscan. Returns a regex match object with
228 the vulnerability's name and severity if the regex found something
229 and (None, None) if the regex found nothing.
230 """
231 critical_search = re.search(r"Website is not fully configured|"
232 "Debug log file found|"
233 "wp-config\.php backup file has been found|"
234 "searchreplacedb2.php has been found",
235 potential_vuln)
236 if critical_search:
237 return critical_search.group(0), "critical"
238
239 info_search = re.search(r"Directory listing is enabled|"
240 "An error_log file has been found|"
241 "file exists exposing a version number|"
242 "Full Path Disclosure|"
243 "Registration is enabled|"
244 "(Upload|Includes) directory has directory listing enabled|"
245 "Default first Wordpress username 'admin' is still used",
246 potential_vuln)
247 if info_search:
248 return info_search.group(0), "info"
249
250 return None, None
251
252 def __get_path_from_vuln(self, vuln):
253 """Given a vuln as string, return the path as a string (empty string
254 for path not found).
255 """
256 path_search = re.search("(?P<url>https?://[^\s]+)", vuln)
257 path = path_search.group('url') if path_search else ""
258 return path
259
260 def processCommandString(self, username, current_path, command_string):
261 return None
262
263
264 def createPlugin():
265 return WPScanPlugin()
266
267 if __name__ == "__main__":
268 import sys
269 import os
270 if len(sys.argv) == 2:
271 report_file = sys.argv[1]
272 if os.path.isfile(report_file):
273 plugin = createPlugin()
274 plugin.processReport(report_file)
275 print(plugin.get_json())
276 else:
277 print(f"Report not found: {report_file}")
278 else:
279 print(f"USAGE {sys.argv[0]} REPORT_FILE")
280
281 # 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 # 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 from faraday_plugins.plugins.plugin import PluginXMLFormat
7 import re
8 import os
9 import sys
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
18
19 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
20
21 current_path = os.path.abspath(os.getcwd())
22
23 __author__ = "Francisco Amato"
24 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
25 __credits__ = ["Francisco Amato"]
26 __license__ = ""
27 __version__ = "1.0.0"
28 __maintainer__ = "Francisco Amato"
29 __email__ = "[email protected]"
30 __status__ = "Development"
31
32
33 class X1XmlParser:
34 """
35 The objective of this class is to parse an xml file generated by the x1 tool.
36
37 TODO: Handle errors.
38 TODO: Test x1 output version. Handle what happens if the parser doesn't support it.
39 TODO: Test cases.
40
41 @param x1_xml_filepath A proper xml generated by x1
42 """
43
44 def __init__(self, xml_output):
45
46 tree = self.parse_xml(xml_output)
47 if tree:
48 self.items = [data for data in self.get_items(tree)]
49 else:
50 self.items = []
51
52 def parse_xml(self, xml_output):
53 """
54 Open and parse an xml file.
55
56 TODO: Write custom parser to just read the nodes that we need instead of
57 reading the whole file.
58
59 @return xml_tree An xml tree instance. None if error.
60 """
61 try:
62 tree = ET.fromstring(xml_output)
63 except SyntaxError as err:
64 print("SyntaxError: %s. %s" % (err, xml_output))
65 return None
66
67 return tree
68
69 def get_items(self, tree):
70 """
71 @return items A list of Host instances
72 """
73
74 for node in tree.findall('results/landscape/system/component'):
75 yield Item(node)
76
77
78 class Item:
79 """
80 An abstract representation of a Item
81
82
83 @param item_node A item_node taken from an x1 xml tree
84 """
85
86 def __init__(self, item_node):
87 self.node = item_node
88
89 self.name = self.get_text_from_subnode('name')
90 self.host = self.get_text_from_subnode('host')
91 self.vclass = self.get_text_from_subnode('class')
92
93 self.connector = self.node.find('connector')
94 self.cname = self.connector.get('name')
95 data = self.cname.split("/")
96 self.port, self.protocol = data[0].split()
97 self.srvname = data[1]
98
99 self.cresults = self.getResults(self.connector)
100 self.results = self.getResults(self.node)
101
102 def getResults(self, tree):
103 """
104 :param tree:
105 """
106 for self.issues in tree.findall('modResults/moduleResult'):
107 yield Results(self.issues)
108
109 def get_text_from_subnode(self, subnode_xpath_expr):
110 """
111 Finds a subnode in the host node and the retrieves a value from it.
112
113 @return An attribute value
114 """
115 sub_node = self.node.find(subnode_xpath_expr)
116 if sub_node is not None:
117 return sub_node.text
118
119 return None
120
121
122 class Results():
123
124 def __init__(self, issue_node):
125 self.node = issue_node
126 self.id = self.get_text_from_subnode('id')
127 self.name = self.get_text_from_subnode('name')
128
129 self.category = self.get_text_from_subnode('category')
130 self.trendingStatus = self.get_text_from_subnode('trendingStatus')
131 self.description = self.get_text_from_subnode('description')
132 self.risk = self.get_text_from_subnode('risk')
133 self.resolution = self.get_text_from_subnode('solution')
134 self.ref = []
135 for r in issue_node.findall('refs/reference'):
136
137 self.ref.append(r.get('type') + "-" + r.get('text'))
138
139 def get_text_from_subnode(self, subnode_xpath_expr):
140 """
141 Finds a subnode in the host node and the retrieves a value from it.
142
143 @return An attribute value
144 """
145 sub_node = self.node.find(subnode_xpath_expr)
146 if sub_node is not None:
147 return sub_node.text
148
149 return None
150
151
152 class X1Plugin(PluginXMLFormat):
153 """
154 Example plugin to parse x1 output.
155 """
156
157 def __init__(self):
158 super().__init__()
159 self.identifier_tag = ["session", "landscapePolicy"]
160 self.id = "X1"
161 self.name = "Onapsis X1 XML Output Plugin"
162 self.plugin_version = "0.0.1"
163 self.version = "Onapsis X1 2.56"
164 self.framework_version = "1.0.0"
165 self.options = None
166 self._current_output = None
167 self._command_regex = re.compile(r'^(sudo x1|\.\/x1).*?')
168
169
170
171 def parseOutputString(self, output, debug=False):
172
173 parser = X1XmlParser(output)
174 for item in parser.items:
175 h_id = self.createAndAddHost(item.host, item.name)
176 i_id = self.createAndAddInterface(
177 h_id, item.host, ipv4_address=item.host, hostname_resolution=[item.vclass])
178 s_id = self.createAndAddServiceToInterface(h_id, i_id, item.srvname,
179 item.protocol,
180 ports=[str(item.port)],
181 status="open")
182 for v in item.results:
183 desc = v.description
184 v_id = self.createAndAddVulnToService(h_id, s_id, v.name, desc=desc,
185 ref=v.ref, severity=v.risk, resolution=v.resolution)
186
187 for v in item.cresults:
188 desc = v.description
189 v_id = self.createAndAddVulnToService(h_id, s_id, v.name, desc=desc,
190 ref=v.ref, severity=v.risk, resolution=v.resolution)
191
192 del parser
193
194 def processCommandString(self, username, current_path, command_string):
195 return None
196
197 def setHost(self):
198 pass
199
200
201 def createPlugin():
202 return X1Plugin()
203
204 if __name__ == "__main__":
205 import sys
206 import os
207 if len(sys.argv) == 2:
208 report_file = sys.argv[1]
209 if os.path.isfile(report_file):
210 plugin = createPlugin()
211 plugin.processReport(report_file)
212 print(plugin.get_json())
213 else:
214 print(f"Report not found: {report_file}")
215 else:
216 print(f"USAGE {sys.argv[0]} REPORT_FILE")
217
218 # 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 # I'm Py3
0 """
1 Faraday Penetration Test IDE
2 Copyright (C) 2017 Infobyte LLC (http://www.infobytesec.com/)
3 See the file 'doc/LICENSE' for the license information
4 """
5 import re
6 import socket
7 from faraday_plugins.plugins.plugin import PluginBase
8
9 __author__ = "Roberto Focke"
10 __copyright__ = "Copyright (c) 2017, Infobyte LLC"
11 __license__ = ""
12 __version__ = "1.0.0"
13
14
15 class xsssniper(PluginBase):
16
17 def __init__(self):
18 super().__init__()
19 self.id = "xsssniper"
20 self.name = "xsssniper"
21 self.plugin_version = "0.0.1"
22 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 xsssniper\.py|.\/xsssniper\.py|python xsssniper\.py)')
25
26 def parseOutputString(self, output, debug=False):
27 parametro = []
28 lineas = output.split("\n")
29 aux = 0
30 for linea in lineas:
31 if not linea:
32 continue
33 linea = linea.lower()
34 if ((linea.find("target:")>0)):
35 url = re.findall('(?:[-\w.]|(?:%[\da-fA-F]{2}))+', linea)
36 host_id = self.createAndAddHost(url[3])
37 address=socket.gethostbyname(url[3])
38 interface_id = self.createAndAddInterface(host_id,address,ipv4_address=address,hostname_resolution=url[3])
39 if ((linea.find("method")>0)):
40 list_a = re.findall("\w+", linea)
41 metodo= list_a[1]
42 if ((linea.find("query string:")>0)):
43 lista_parametros=linea.split('=')
44 aux=len(lista_parametros)
45 if ((linea.find("param:")>0)):
46 list2 = re.findall("\w+",linea)
47 parametro.append(list2[1])
48 service_id = self.createAndAddServiceToInterface(host_id, interface_id, self.protocol, 'tcp',
49 ports=['80'], status='Open', version="", description="")
50 if aux != 0:
51 self.createAndAddVulnWebToService(host_id, service_id, name="xss", desc="XSS", ref='', severity='med',
52 website=url[0], path='', method=metodo, pname='',
53 params=''.join(parametro), request='', response='')
54
55 def processCommandString(self, username, current_path, command_string):
56 return None
57
58
59 def createPlugin():
60 return xsssniper()
61
62 if __name__ == "__main__":
63 import sys
64 import os
65 if len(sys.argv) == 2:
66 report_file = sys.argv[1]
67 if os.path.isfile(report_file):
68 plugin = createPlugin()
69 plugin.processReport(report_file)
70 print(plugin.get_json())
71 else:
72 print(f"Report not found: {report_file}")
73 else:
74 print(f"USAGE {sys.argv[0]} REPORT_FILE")
75
76 # 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 /*
1 * Zed Attack Proxy (ZAP) and its related class files.
2 *
3 * ZAP is an HTTP/HTTPS proxy for assessing web application security.
4 *
5 * Copyright 2018 The ZAP Development Team
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19
20 package org.zaproxy.zap.extension.faraday;
21
22 import org.parosproxy.paros.Constant;
23
24 import javax.swing.*;
25 import java.io.*;
26 import java.util.Properties;
27
28 public class Configuration {
29 private String server;
30 private String user;
31 private String password;
32 private String session;
33 private String workspace;
34 private boolean autoImport;
35 private static Configuration _instance;
36
37 private Configuration() {
38 this.user = "";
39 this.password = "";
40 this.server = "http://127.0.0.1:5985/";
41 this.autoImport = false;
42 }
43
44 public static Configuration getSingleton() {
45 if (_instance == null)
46 _instance = new Configuration();
47 return _instance;
48 }
49
50 public boolean save() throws IOException {
51
52 Properties prop = new Properties();
53 OutputStream output = null;
54
55 String userHome = System.getProperty("user.home");
56 String outputFolder = Constant.getZapHome() + "faraday";
57 File folder = new File(outputFolder);
58 if (!folder.exists()) {
59 folder.mkdir();
60 }
61
62
63 String filePath = outputFolder + File.separator + this.getUser() + ".properties";
64 output = new FileOutputStream(filePath);
65
66 // set the properties value
67 prop.setProperty("fuser", this.getUser());
68 prop.setProperty("fpassword", this.getPassword());
69 prop.setProperty("fserver", this.getServer());
70 prop.setProperty("fworkspace", this.getWorkspace());
71 prop.setProperty("fsession", this.getSession());
72
73 // save properties to project root folder
74 prop.store(output, null);
75
76 if (output != null) {
77 try {
78 output.close();
79 } catch (IOException e) {
80 e.printStackTrace();
81 return false;
82 }
83 }
84
85 return true;
86 }
87
88
89 public void restore(String fUser) throws IOException {
90 Properties prop = new Properties();
91 InputStream input = null;
92
93 String outputFolder = Constant.getZapHome() + "faraday";
94 String filePath = outputFolder + File.separator + fUser + ".properties";
95 input = new FileInputStream(filePath);
96
97 // load a properties file
98 prop.load(input);
99
100 this.setUser(prop.getProperty("fuser"));
101 this.setPassword(prop.getProperty("fpassword"));
102 this.setServer(prop.getProperty("fserver"));
103 this.setWorkspace(prop.getProperty("fworkspace"));
104
105 if (input != null) {
106 try {
107 input.close();
108 } catch (IOException e) {
109 e.printStackTrace();
110 }
111 }
112
113 }
114
115 public String getUser() {
116 return user;
117 }
118
119 public void setUser(String user) {
120 this.user = user;
121 }
122
123 public String getPassword() {
124 return password;
125 }
126
127 public void setPassword(String password) {
128 this.password = password;
129 }
130
131 public String getServer() {
132 return server;
133 }
134
135 public void setServer(String server) {
136 this.server = server;
137 }
138
139 public boolean isAutoImport() {
140 return autoImport;
141 }
142
143 public void setAutoImport(boolean autoImport) {
144 this.autoImport = autoImport;
145 }
146
147 public String getSession() {
148 return session;
149 }
150
151 public void setSession(String session) {
152 this.session = session;
153 }
154
155 public String getWorkspace() {
156 return workspace;
157 }
158
159 public void setWorkspace(String workspace) {
160 this.workspace = workspace;
161 }
162 }
0 package org.zaproxy.zap.extension.faraday;
1
2
3 import org.apache.log4j.Logger;
4 import org.parosproxy.paros.Constant;
5
6 import javax.swing.*;
7 import javax.swing.border.Border;
8 import java.awt.*;
9 import java.awt.event.*;
10 import java.io.*;
11 import java.util.ArrayList;
12 import java.util.Properties;
13 import java.util.ResourceBundle;
14
15 public class ConfigurationDialog extends JFrame {
16 private static final Logger logger = Logger.getLogger(ConfigurationDialog.class);
17 private ResourceBundle messages = null;
18 private FaradayClient faradayClient;
19
20 private static String LOGIN_BUTTON = "Login";
21 private static String LOGOUT_BUTTON = "Logout";
22 private static String WORKSPACES_FIELD = "Select faraday workspace";
23 private static String IMPORT_NEW_VULNS_FIELD = "Import new vulnerabilities";
24 private static String SET_CONFIG_AS_DEFAULT = "Set this configuration as default";
25 private static String IMPORT_BUTTON = "Import vulnerabilities";
26 private static String REFRESH_BUTTON = "Refresh";
27 private static String RESTORE_BUTTON = "Restore";
28 private static String SAVE_BUTTON = "Save";
29
30 private JTabbedPane tabbedPane;
31 private JPanel authPanel;
32 private JPanel configPanel;
33
34 private JTextField fldUser;
35 private JTextField fldPass;
36 private JTextField fldServer;
37
38 private JComboBox cmbWorkspaces;
39 private JCheckBox cboxSetConfigDefault;
40
41
42 private JButton loginButton;
43 private JButton logoutButton;
44 private JButton refreshButton;
45 private JButton restoreButton;
46 private JButton importButton;
47 private JButton saveButton;
48 private JButton closeButton;
49
50
51 public ConfigurationDialog(String s) throws HeadlessException {
52 super(s);
53 }
54
55
56 public void init() {
57 logger.debug("Init Faraday configuration dialog");
58 messages = ResourceBundle.getBundle(
59 this.getClass().getPackage().getName() +
60 ".Messages", Constant.getLocale());
61 // Setup the content-pane of JFrame in BorderLayout
62 Container cp = this.getContentPane();
63 cp.setLayout(new BorderLayout(5, 5));
64 Border padding = BorderFactory.createEmptyBorder(10, 10, 10, 10);
65
66
67 String USERNAME_FIELD = messages.getString("faraday.config.dialog.auth.user");
68 String PASS_FIELD = messages.getString("faraday.config.dialog.auth.pass");
69 String SERVER_FIELD = messages.getString("faraday.config.dialog.server");
70 LOGIN_BUTTON = messages.getString("faraday.config.dialog.auth.login");
71 LOGOUT_BUTTON = messages.getString("faraday.config.dialog.auth.logout");
72 WORKSPACES_FIELD = messages.getString("faraday.config.dialog.workspace");
73 IMPORT_NEW_VULNS_FIELD = messages.getString("faraday.config.dialog.import.new");
74 SET_CONFIG_AS_DEFAULT = messages.getString("faraday.config.dialog.default");
75 IMPORT_BUTTON = messages.getString("faraday.config.dialog.import.new");
76 REFRESH_BUTTON = messages.getString("faraday.config.dialog.refresh");
77 RESTORE_BUTTON = messages.getString("faraday.config.dialog.restore");
78 SAVE_BUTTON = messages.getString("faraday.config.dialog.save");
79 tabbedPane = new JTabbedPane();
80
81 JPanel buttonLoginPanel = new JPanel();
82 buttonLoginPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
83
84 JPanel buttonConfigPanel = new JPanel();
85 buttonConfigPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
86
87 authPanel = new JPanel(new GridLayout(4, 2, 10, 2));
88 authPanel.setBorder(padding);
89 configPanel = new JPanel(new GridLayout(3, 2, 10, 2));
90 configPanel.setBorder(padding);
91
92
93 Configuration configuration = Configuration.getSingleton();
94 faradayClient = new FaradayClient(configuration.getServer());
95
96 authPanel.add(new JLabel(USERNAME_FIELD));
97 fldUser = new JTextField(10);
98 authPanel.add(fldUser);
99
100 authPanel.add(new JLabel(PASS_FIELD));
101 fldPass = new JPasswordField(10);
102 authPanel.add(fldPass);
103
104 authPanel.add(new JLabel(SERVER_FIELD));
105 fldServer = new JTextField(10);
106 fldServer.setText(configuration.getServer());
107 authPanel.add(fldServer);
108
109 configPanel.add(getCBoxSetDefaultConfig());
110
111 buttonConfigPanel.add(getCloseButton());
112 buttonConfigPanel.add(getCloseButton());
113 buttonConfigPanel.add(getRefreshButton());
114 buttonConfigPanel.add(getRestoreButton());
115 buttonConfigPanel.add(getSaveButton());
116 // buttonConfigPanel.add(getImportButton());
117 buttonConfigPanel.add(getLoginButton());
118 buttonConfigPanel.add(getLogoutButton());
119
120
121 authPanel.addComponentListener(new ComponentListener() {
122 @Override
123 public void componentResized(ComponentEvent componentEvent) {
124
125 }
126
127 @Override
128 public void componentMoved(ComponentEvent componentEvent) {
129
130 }
131
132 @Override
133 public void componentShown(ComponentEvent componentEvent) {
134
135 refreshButton.setVisible(false);
136 restoreButton.setVisible(false);
137 // importButton.setVisible(false);
138 saveButton.setVisible(false);
139 }
140
141 @Override
142 public void componentHidden(ComponentEvent componentEvent) {
143 refreshButton.setVisible(true);
144 restoreButton.setVisible(true);
145 // importButton.setVisible(true);
146 saveButton.setVisible(true);
147 }
148 });
149
150 configPanel.addComponentListener(new ComponentListener() {
151 @Override
152 public void componentResized(ComponentEvent componentEvent) {
153
154 }
155
156 @Override
157 public void componentMoved(ComponentEvent componentEvent) {
158
159 }
160
161 @Override
162 public void componentShown(ComponentEvent componentEvent) {
163 loginButton.setVisible(false);
164 logoutButton.setVisible(false);
165 }
166
167 @Override
168 public void componentHidden(ComponentEvent componentEvent) {
169 if (configuration.getSession().equals("")) {
170 loginButton.setVisible(true);
171 } else {
172 logoutButton.setVisible(true);
173 }
174 }
175 });
176
177 tabbedPane.addTab(messages.getString("faraday.config.dialog.tab.auth"), null, authPanel, null);
178 tabbedPane.setMnemonicAt(0, KeyEvent.VK_1);
179
180
181 tabbedPane.addTab(messages.getString("faraday.config.dialog.tabs.conf"), null, configPanel, null);
182 tabbedPane.setMnemonicAt(1, KeyEvent.VK_2);
183
184 tabbedPane.setEnabledAt(1, false);
185
186 cp.add(tabbedPane, BorderLayout.NORTH);
187 cp.add(buttonConfigPanel, BorderLayout.SOUTH);
188
189 if (configuration.getSession() != null && !configuration.getSession().equals("")) {
190 logoutButton.setVisible(true);
191 loginButton.setVisible(false);
192 } else {
193 loginButton.setVisible(true);
194 logoutButton.setVisible(false);
195 }
196
197
198 if (!configuration.getUser().equals("") && !configuration.getPassword().equals("")) {
199 if (faradayClient.Login(configuration.getUser(), configuration.getPassword(), configuration.getServer())) {
200 fldUser.setText(configuration.getUser());
201 fldPass.setText(configuration.getPassword());
202 fldServer.setText(configuration.getServer());
203
204 tabbedPane.setEnabledAt(1, true);
205 tabbedPane.setSelectedIndex(1);
206
207 cboxSetConfigDefault.setSelected(true);
208
209 if (cmbWorkspaces == null) {
210 configPanel.add(new JLabel(WORKSPACES_FIELD));
211 configPanel.add(getWSComboBox());
212 }
213 }
214 }
215
216 this.setSize(550, 300);
217 this.setResizable(false);
218 this.setLocationRelativeTo(null);
219 this.setVisible(true);
220 }
221
222
223 private JButton getLoginButton() {
224 if (this.loginButton == null) {
225 this.loginButton = new JButton();
226 this.loginButton.setText(LOGIN_BUTTON);
227 this.loginButton.addActionListener(new ActionListener() {
228 public void actionPerformed(ActionEvent e) {
229 if (fldUser.getText().equals("") || fldPass.getText().equals("") || fldServer.getText().equals("")) {
230 showMessage(messages.getString("faraday.message.invalid.check.credentials"), messages.getString("faraday.dialog.login.title"), JOptionPane.ERROR_MESSAGE);
231 } else {
232 if (faradayClient.Login(fldUser.getText(), fldPass.getText(), fldServer.getText())) {
233 logoutButton.setVisible(true);
234 loginButton.setVisible(false);
235 if (!tabbedPane.isEnabledAt(1)) {
236 tabbedPane.setEnabledAt(1, true);
237 }
238 tabbedPane.setSelectedIndex(1);
239 if (cmbWorkspaces == null) {
240 configPanel.add(new JLabel(WORKSPACES_FIELD));
241 configPanel.add(getWSComboBox());
242 } else {
243 configPanel.remove(cmbWorkspaces);
244 configPanel.add(getWSComboBox());
245 }
246 } else {
247 showMessage(messages.getString("faraday.message.invalid.credentials"), messages.getString("faraday.dialog.login.title"), JOptionPane.ERROR_MESSAGE);
248 }
249 }
250
251
252 }
253 });
254
255
256 }
257
258 return this.loginButton;
259 }
260
261
262 private JButton getLogoutButton() {
263 if (this.logoutButton == null) {
264 this.logoutButton = new JButton();
265 this.logoutButton.setText(LOGOUT_BUTTON);
266 this.logoutButton.addActionListener(new ActionListener() {
267 public void actionPerformed(ActionEvent e) {
268 Configuration configuration = Configuration.getSingleton();
269 String userTemp = configuration.getUser();
270 if (faradayClient.Logout()) {
271 logoutButton.setVisible(false);
272 loginButton.setVisible(true);
273
274 if (tabbedPane.isEnabledAt(1)) {
275 tabbedPane.setEnabledAt(1, false);
276 }
277 tabbedPane.setSelectedIndex(0);
278
279 Properties prop = new Properties();
280 InputStream input = null;
281 try {
282 String filePath = Constant.getZapHome() + "faraday" + File.separator + "default.properties";
283 input = new FileInputStream(filePath);
284 // load a properties file
285 prop.load(input);
286 // set the properties value
287 String fUser = prop.getProperty("default");
288 if (fUser.equals(userTemp)) {
289 removeDefaultConfig();
290 }
291
292 } catch (IOException io) {
293 System.out.println("We can't found default.properties file");
294 } finally {
295 if (input != null) {
296 try {
297 input.close();
298 } catch (IOException er) {
299 er.printStackTrace();
300 }
301 }
302 }
303
304
305 showMessage(messages.getString("faraday.dialog.logout.success"), messages.getString("faraday.dialog.logout.title"), JOptionPane.INFORMATION_MESSAGE);
306 } else {
307 showMessage(messages.getString("faraday.dialog.logout.error"), messages.getString("faraday.dialog.logout.title"), JOptionPane.ERROR_MESSAGE);
308 }
309 }
310 });
311 }
312
313 return this.logoutButton;
314 }
315
316
317 private JButton getRefreshButton() {
318 if (this.refreshButton == null) {
319 this.refreshButton = new JButton();
320 this.refreshButton.setText(REFRESH_BUTTON);
321 this.refreshButton.addActionListener(new ActionListener() {
322 public void actionPerformed(ActionEvent e) {
323 refreshWorkspaces(true);
324 }
325 });
326 }
327
328 return this.refreshButton;
329 }
330
331
332 private JButton getCloseButton() {
333 if (this.closeButton == null) {
334 this.closeButton = new JButton();
335 this.closeButton.setText(messages.getString("faraday.dialog.button.close"));
336 this.closeButton.addActionListener(new ActionListener() {
337 public void actionPerformed(ActionEvent e) {
338 setVisible(false);
339 dispose();
340 }
341 });
342 }
343
344 return this.closeButton;
345 }
346
347
348 private JButton getRestoreButton() {
349 if (this.restoreButton == null) {
350 this.restoreButton = new JButton();
351 this.restoreButton.setText(RESTORE_BUTTON);
352 this.restoreButton.addActionListener(new ActionListener() {
353 public void actionPerformed(ActionEvent e) {
354 String fUser = JOptionPane.showInputDialog(messages.getString("faraday.config.dialog.restore"), messages.getString("faraday.dialog.enter.user"));
355 if (fUser != null) {
356 restoreConfiguration(fUser);
357 }
358 }
359 });
360 }
361
362 return this.restoreButton;
363 }
364
365
366 private JButton getImportButton() {
367 if (this.importButton == null) {
368 this.importButton = new JButton();
369 this.importButton.setText(IMPORT_BUTTON);
370 this.importButton.addActionListener(new ActionListener() {
371 public void actionPerformed(ActionEvent e) {
372
373 }
374 });
375 }
376
377 return this.importButton;
378 }
379
380
381 private JButton getSaveButton() {
382 if (this.saveButton == null) {
383 this.saveButton = new JButton();
384 this.saveButton.setText(SAVE_BUTTON);
385 this.saveButton.addActionListener(new ActionListener() {
386 public void actionPerformed(ActionEvent e) {
387 saveConfiguration();
388 }
389 });
390 }
391
392 return this.saveButton;
393 }
394
395
396 private JComboBox getWSComboBox() {
397 Configuration configuration = Configuration.getSingleton();
398
399 ArrayList<String> wsList = faradayClient.GetWorkspaces();
400 String[] workspaces = new String[wsList.size()];
401 for (int i = 0; i < wsList.size(); i++) {
402 workspaces[i] = wsList.get(i);
403 }
404 cmbWorkspaces = new JComboBox(workspaces);
405 if (workspaces.length > 0) {
406 if (configuration.getWorkspace() != null) {
407 cmbWorkspaces.setSelectedItem(configuration.getWorkspace());
408 } else {
409 configuration.setWorkspace(workspaces[0]);
410 }
411 }
412 cmbWorkspaces.addActionListener(new ActionListener() {
413 @Override
414 public void actionPerformed(ActionEvent actionEvent) {
415 Configuration.getSingleton().setWorkspace(cmbWorkspaces.getSelectedItem().toString());
416 }
417 });
418
419
420 return cmbWorkspaces;
421 }
422
423
424 private JCheckBox getCBoxSetDefaultConfig() {
425 if (this.cboxSetConfigDefault == null) {
426 cboxSetConfigDefault = new JCheckBox(SET_CONFIG_AS_DEFAULT, false);
427
428 cboxSetConfigDefault.addActionListener(new ActionListener() {
429 @Override
430 public void actionPerformed(ActionEvent actionEvent) {
431 if (cboxSetConfigDefault.isSelected()) {
432 setConfigAsDefault();
433 } else {
434 removeDefaultConfig();
435 }
436 }
437 });
438 }
439
440 return cboxSetConfigDefault;
441 }
442
443
444 private void showMessage(String message, String title, int icon) {
445 JOptionPane.showMessageDialog(
446 this,
447 message,
448 title,
449 icon);
450 }
451
452
453 private void saveConfiguration() {
454 try {
455 if (Configuration.getSingleton().save()) {
456 JOptionPane.showMessageDialog(
457 this,
458 messages.getString("faraday.save.config.success"),
459 messages.getString("faraday.config.dialog.title"),
460 JOptionPane.INFORMATION_MESSAGE);
461 } else {
462 JOptionPane.showMessageDialog(
463 this,
464 messages.getString("faraday.save.config.error"),
465 messages.getString("faraday.config.dialog.title"),
466 JOptionPane.ERROR_MESSAGE);
467
468 }
469 } catch (IOException io) {
470 JOptionPane.showMessageDialog(
471 this,
472 messages.getString("faraday.save.config.error"),
473 messages.getString("faraday.config.dialog.title"),
474 JOptionPane.ERROR_MESSAGE);
475 io.printStackTrace();
476
477 }
478 }
479
480
481 private void restoreConfiguration(String fUser) {
482 try {
483 Configuration configuration = Configuration.getSingleton();
484 configuration.restore(fUser);
485 if (faradayClient.Login(configuration.getUser(), configuration.getPassword(), configuration.getServer())) {
486 fldUser.setText(configuration.getUser());
487 fldPass.setText(configuration.getPassword());
488 fldServer.setText(configuration.getServer());
489
490 tabbedPane.setEnabledAt(1, true);
491 tabbedPane.setSelectedIndex(0);
492
493 cboxSetConfigDefault.setSelected(false);
494 refreshWorkspaces(false);
495 } else {
496 JOptionPane.showMessageDialog(
497 this,
498 messages.getString("faraday.restore.config.error.login"),
499 messages.getString("faraday.config.dialog.title"),
500 JOptionPane.ERROR_MESSAGE);
501 }
502 } catch (IOException ex) {
503 JOptionPane.showMessageDialog(
504 this,
505 messages.getString("faraday.restore.config.error"),
506 messages.getString("faraday.config.dialog.title"),
507 JOptionPane.ERROR_MESSAGE);
508 }
509
510 }
511
512
513 private void setConfigAsDefault() {
514 Configuration configuration = Configuration.getSingleton();
515
516 Properties prop = new Properties();
517 OutputStream output = null;
518
519 try {
520 String outputFolder = Constant.getZapHome() + "faraday";
521 File folder = new File(outputFolder);
522 if (!folder.exists()) {
523 folder.mkdir();
524 }
525
526 String filePath = outputFolder + File.separator + "default.properties";
527 output = new FileOutputStream(filePath);
528
529 // set the properties value
530 prop.setProperty("default", configuration.getUser());
531
532 // save properties to project root folder
533 prop.store(output, null);
534
535 } catch (IOException io) {
536 JOptionPane.showMessageDialog(
537 this,
538 messages.getString("faraday.set.default.config.error"),
539 messages.getString("faraday.config.dialog.title"),
540 JOptionPane.ERROR_MESSAGE);
541 io.printStackTrace();
542 } finally {
543 if (output != null) {
544 try {
545 output.close();
546 } catch (IOException e) {
547 e.printStackTrace();
548 }
549 }
550
551 }
552 }
553
554
555 private void removeDefaultConfig() {
556 try {
557
558 String filePath = Constant.getZapHome() + "faraday" + File.separator + "default.properties";
559 File file = new File(filePath);
560 if (file.delete()) {
561 System.out.println(file.getName() + " is deleted!");
562 } else {
563 System.out.println("Delete operation is failed.");
564 }
565
566 } catch (Exception e) {
567
568 e.printStackTrace();
569
570 }
571 }
572
573
574 private void refreshWorkspaces(boolean canShowAlert) {
575 if (cmbWorkspaces != null) {
576 configPanel.remove(cmbWorkspaces);
577 configPanel.add(getWSComboBox());
578 if (canShowAlert) {
579 JOptionPane.showMessageDialog(
580 this,
581 messages.getString("faraday.refresh.workspace.done"),
582 messages.getString("faraday.config.dialog.title"),
583 JOptionPane.INFORMATION_MESSAGE);
584 }
585 }
586 }
587
588 }
0 package org.zaproxy.zap.extension.faraday;
1
2 import net.sf.json.JSONArray;
3 import net.sf.json.JSONObject;
4 import org.apache.commons.httpclient.URIException;
5 import org.apache.commons.httpclient.methods.PostMethod;
6 import org.apache.http.HttpEntity;
7 import org.apache.http.HttpResponse;
8
9 import org.apache.http.client.ClientProtocolException;
10 import org.apache.http.client.HttpClient;
11 import org.apache.http.entity.StringEntity;
12 import org.apache.http.client.entity.UrlEncodedFormEntity;
13 import org.apache.http.client.methods.HttpGet;
14 import org.apache.http.client.methods.HttpPost;
15 import org.apache.http.impl.client.HttpClients;
16 import org.apache.http.NameValuePair;
17 import org.apache.http.message.BasicNameValuePair;
18 import org.apache.http.util.EntityUtils;
19 import org.parosproxy.paros.Constant;
20 import org.parosproxy.paros.core.scanner.Alert;
21 import org.parosproxy.paros.model.HistoryReference;
22
23 import java.io.*;
24 import java.net.*;
25 import java.nio.charset.StandardCharsets;
26 import java.sql.Time;
27 import java.time.Instant;
28 import java.util.*;
29
30
31 public class FaradayClient {
32
33 private String baseUrl;
34 private ResourceBundle messages = null;
35
36 public FaradayClient(String baseUrl) {
37 this.baseUrl = baseUrl;
38 messages = ResourceBundle.getBundle(
39 this.getClass().getPackage().getName() +
40 ".Messages", Constant.getLocale());
41
42 }
43
44 public boolean Login(String username, String password, String server) {
45 Logout();
46 HttpClient httpClient = HttpClients.createDefault();
47 String LOGIN_URL = "_api/login";
48 HttpPost httpPost = new HttpPost(server + LOGIN_URL);
49
50 // Request parameters and other properties.
51 List<BasicNameValuePair> params = new ArrayList<>(2);
52 params.add(new BasicNameValuePair("email", username));
53 params.add(new BasicNameValuePair("password", password));
54
55 try {
56 httpPost.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
57 HttpResponse response = httpClient.execute(httpPost);
58 if (response.getFirstHeader("Set-Cookie") != null) {
59 Configuration configuration = Configuration.getSingleton();
60 configuration.setSession(response.getFirstHeader("Set-Cookie").getValue());
61 configuration.setUser(username);
62 configuration.setPassword(password);
63 configuration.setServer(server);
64 setBaseUrl(server);
65 return true;
66 } else if (response.getStatusLine().getStatusCode() == 302) {
67 return true;
68 }
69 return false;
70 } catch (UnsupportedEncodingException e) {
71 // writing error to Log
72 e.printStackTrace();
73 return false;
74 } catch (ClientProtocolException e) {
75 e.printStackTrace();
76 return false;
77 } catch (IOException e) {
78 e.printStackTrace();
79 return false;
80 }
81
82 }
83
84 public boolean Logout() {
85 String LOGOUT_URL = "_api/logout";
86 HttpGet httpGet = new HttpGet(this.baseUrl + LOGOUT_URL);
87 Configuration configuration = Configuration.getSingleton();
88
89 if (!Objects.equals(configuration.getSession(), "")) {
90 httpGet.setHeader("Cookie", configuration.getSession());
91
92 //Execute and get the response.
93 HttpResponse response = null;
94
95 try {
96 HttpClient httpClient = HttpClients.createDefault();
97 response = httpClient.execute(httpGet);
98 HttpEntity entity = response.getEntity();
99 if (response.getStatusLine().getStatusCode() == 200) {
100 configuration.setSession("");
101 configuration.setUser("");
102 configuration.setPassword("");
103 return true;
104 }
105 return false;
106 } catch (IOException e) {
107 e.printStackTrace();
108 return false;
109 }
110 }
111 return true;
112 }
113
114 public ArrayList<String> GetWorkspaces() {
115 ArrayList<String> workspaces = new ArrayList<>();
116 String WORKSPACES_URL = "_api/v2/ws/";
117 HttpGet httpGet = new HttpGet(this.baseUrl + WORKSPACES_URL);
118 Configuration configuration = Configuration.getSingleton();
119
120 if (configuration.getSession() != "") {
121 httpGet.setHeader("Cookie", configuration.getSession());
122
123 //Execute and get the response.
124 HttpResponse response = null;
125 InputStream instream = null;
126 try {
127 HttpClient httpClient = HttpClients.createDefault();
128 response = httpClient.execute(httpGet);
129 HttpEntity entity = response.getEntity();
130
131 if (entity != null && response.getStatusLine().getStatusCode() == 200) {
132 instream = entity.getContent();
133
134 BufferedReader br = new BufferedReader(new InputStreamReader(instream));
135 String output;
136 JSONArray jsonArray = new JSONArray();
137 while ((output = br.readLine()) != null) {
138 System.out.println(output);
139 jsonArray = JSONArray.fromObject(output);
140 }
141
142 for (int i = 0; i < jsonArray.size(); i++) {
143 JSONObject jsonObject = jsonArray.getJSONObject(i);
144 workspaces.add(jsonObject.get("name").toString());
145 }
146 }
147 } catch (IOException e) {
148 e.printStackTrace();
149 } finally {
150 try {
151 instream.close();
152 } catch (IOException e) {
153 e.printStackTrace();
154 }
155 }
156 }
157
158 return workspaces;
159 }
160
161
162 private int AddCommand(String commandName, String workspace, String session) {
163 String COMMAND_URL = "_api/v2/ws/" + workspace + "/commands/";
164 HttpClient httpClient = HttpClients.createDefault();
165 HttpPost httpPost = new HttpPost(this.baseUrl + COMMAND_URL);
166
167 try {
168 StringEntity stringEntity = new StringEntity(ConvertCommandToParams(commandName).toString());
169 httpPost.setHeader("Cookie", session);
170 httpPost.setHeader("Content-Type", "application/json");
171 httpPost.setEntity(stringEntity);
172 HttpResponse response = httpClient.execute(httpPost);
173 HttpEntity entity = response.getEntity();
174
175 if (response.getStatusLine().getStatusCode() == 200 || response.getStatusLine().getStatusCode() == 201 || response.getStatusLine().getStatusCode() == 409) {
176 BufferedReader br = new BufferedReader(new InputStreamReader(entity.getContent()));
177 String output;
178 JSONObject json;
179 String commandStr = "-1";
180 while ((output = br.readLine()) != null) {
181 json = JSONObject.fromObject(output);
182 if (response.getStatusLine().getStatusCode() == 409) {
183 JSONObject jsonObject = JSONObject.fromObject(json.get("object"));
184 commandStr = jsonObject.get("_id").toString();
185 } else {
186 commandStr = json.get("_id").toString();
187 }
188 }
189 return Integer.parseInt(commandStr);
190 }
191 return -1;
192 } catch (UnsupportedEncodingException e) {
193 // writing error to Log
194 e.printStackTrace();
195 return -1;
196 } catch (ClientProtocolException e) {
197 e.printStackTrace();
198 return -1;
199 } catch (IOException e) {
200 e.printStackTrace();
201 return -1;
202 }
203 }
204
205 private int AddHost(Alert alert, String workspace, String session) {
206 String VULN_URL = "_api/v2/ws/" + workspace + "/hosts/";
207 HttpClient httpClient = HttpClients.createDefault();
208 HttpPost httpPost = new HttpPost(this.baseUrl + VULN_URL);
209
210 try {
211 StringEntity stringEntity = new StringEntity(ConvertHostToParams(alert).toString());
212 httpPost.setHeader("Cookie", session);
213 httpPost.setHeader("Content-Type", "application/json");
214 httpPost.setEntity(stringEntity);
215 HttpResponse response = httpClient.execute(httpPost);
216 HttpEntity entity = response.getEntity();
217
218 if (response.getStatusLine().getStatusCode() == 200 || response.getStatusLine().getStatusCode() == 201 || response.getStatusLine().getStatusCode() == 409) {
219 BufferedReader br = new BufferedReader(new InputStreamReader(entity.getContent()));
220 String output;
221 JSONObject json;
222 String hostStr = "-1";
223 while ((output = br.readLine()) != null) {
224 json = JSONObject.fromObject(output);
225 if (response.getStatusLine().getStatusCode() == 409) {
226 JSONObject jsonObject = JSONObject.fromObject(json.get("object"));
227 hostStr = jsonObject.get("id").toString();
228 } else {
229 hostStr = json.get("id").toString();
230 }
231 }
232 return Integer.parseInt(hostStr);
233 }
234 return -1;
235 } catch (UnsupportedEncodingException e) {
236 // writing error to Log
237 e.printStackTrace();
238 return -1;
239 } catch (ClientProtocolException e) {
240 e.printStackTrace();
241 return -1;
242 } catch (IOException e) {
243 e.printStackTrace();
244 return -1;
245 }
246 }
247
248 private int AddService(Alert alert, String workspace, String session, int hostId) {
249 String VULN_URL = "_api/v2/ws/" + workspace + "/services/";
250 HttpClient httpClient = HttpClients.createDefault();
251 HttpPost httpPost = new HttpPost(this.baseUrl + VULN_URL);
252
253 try {
254 StringEntity stringEntity = new StringEntity(ConvertServiceToParams(alert, hostId).toString());
255 httpPost.setHeader("Cookie", session);
256 httpPost.setHeader("Content-Type", "application/json");
257 httpPost.setEntity(stringEntity);
258 HttpResponse response = httpClient.execute(httpPost);
259 HttpEntity entity = response.getEntity();
260
261 BufferedReader br = new BufferedReader(new InputStreamReader(entity.getContent()));
262 String output;
263 if (response.getStatusLine().getStatusCode() == 200 || response.getStatusLine().getStatusCode() == 201 || response.getStatusLine().getStatusCode() == 409) {
264 JSONObject json;
265 String serviceStr = "-1";
266 while ((output = br.readLine()) != null) {
267 json = JSONObject.fromObject(output);
268 if (response.getStatusLine().getStatusCode() == 409) {
269 JSONObject jsonObject = JSONObject.fromObject(json.get("object"));
270 serviceStr = jsonObject.get("id").toString();
271 } else {
272 serviceStr = json.get("id").toString();
273 }
274 }
275 return Integer.parseInt(serviceStr);
276 } else {
277 while ((output = br.readLine()) != null) {
278 System.out.println(output);
279 }
280
281 return -1;
282 }
283
284 } catch (UnsupportedEncodingException e) {
285 // writing error to Log
286 e.printStackTrace();
287 return -1;
288 } catch (ClientProtocolException e) {
289 e.printStackTrace();
290 return -1;
291 } catch (IOException e) {
292 e.printStackTrace();
293 return -1;
294 }
295 }
296
297 public int AddVulnerability(Alert alert, String workspace, String session) {
298 int hostId = AddHost(alert, workspace, session);
299 if (hostId == -1) {
300 return 500;
301 }
302
303 String parentType = "Service";
304 int serviceId = AddService(alert, workspace, session, hostId);
305 if (serviceId == -1) {
306 return 500;
307 }
308
309
310 String commandName = messages.getString("faraday.tool.command.name");
311 int commandId = AddCommand(commandName, workspace, session);
312 if (commandId == -1) {
313 return 500;
314 }
315
316
317 String VULN_URL = "_api/v2/ws/" + workspace + "/vulns/?command_id=" + commandId;
318 HttpClient httpClient = HttpClients.createDefault();
319 HttpPost httpPost = new HttpPost(this.baseUrl + VULN_URL);
320 try {
321
322 StringEntity stringEntity = new StringEntity(ConvertAlertToParams(alert, workspace, parentType, serviceId).toString());
323 httpPost.setHeader("Cookie", session);
324 httpPost.setHeader("Content-Type", "application/json");
325 httpPost.setEntity(stringEntity);
326 HttpResponse response = httpClient.execute(httpPost);
327 return response.getStatusLine().getStatusCode();
328
329 } catch (UnsupportedEncodingException e) {
330 // writing error to Log
331 e.printStackTrace();
332 return 402;
333 } catch (ClientProtocolException e) {
334 e.printStackTrace();
335 return 402;
336 } catch (IOException e) {
337 e.printStackTrace();
338 return 402;
339 }
340 }
341
342 private JSONObject ConvertAlertToParams(Alert alert, String workspace, String parentType, int parentId) {
343 // Request parameters and other properties.
344 JSONObject params = new JSONObject();
345
346 params.put("name", alert.getName());
347 params.put("ws", workspace);
348 params.put("request", alert.getMessage().getRequestHeader().toString());
349 params.put("response", alert.getMessage().getResponseHeader().toString());
350 String desc = !alert.getParam().equals("") ? alert.getDescription() + "\nWith parameter: '" + alert.getParam() + "'" :
351 alert.getDescription();
352 params.put("desc", desc);
353 params.put("resolution", alert.getSolution());
354 params.put("type", "VulnerabilityWeb");
355 params.put("data", alert.getPostData());
356 params.put("policyviolations", "[]");
357 params.put("parent_type", parentType);
358 params.put("parent", parentId);
359 params.put("params", alert.getParam());
360
361 JSONObject metadata = new JSONObject();
362 metadata.put("creator", "OWASP");
363 params.put("metadata", metadata);
364
365 String hostname = alert.getMessage().getRequestHeader().getHostName();
366 String IpAddres = GetIPFromHostname(hostname);
367 JSONArray hostNamesArray = new JSONArray();
368 hostNamesArray.add(hostname);
369 hostNamesArray.add(IpAddres);
370 params.put("hostnames", hostNamesArray);
371 params.put("target", IpAddres);
372 params.put("website", hostname);
373
374 JSONArray refsJsonArray = new JSONArray();
375 String[] resfArray = alert.getReference().split("\n");
376 Collections.addAll(refsJsonArray, resfArray);
377 params.put("refs", refsJsonArray);
378
379 try {
380 params.put("path", alert.getMsgUri().getPath());
381 } catch (URIException e) {
382 e.printStackTrace();
383 }
384
385 if (alert.getConfidence() == 4) {
386 params.put("confirmed", true);
387 }
388
389 switch (alert.getRisk()) {
390 case 0:
391 params.put("severity", "informational");
392 break;
393 case 1:
394 params.put("severity", "low");
395 break;
396 case 2:
397 params.put("severity", "medium");
398 break;
399 case 3:
400 params.put("severity", "high");
401 break;
402 }
403 return params;
404
405 }
406
407 private JSONObject ConvertHostToParams(Alert alert) {
408 // Request parameters and other properties.
409 JSONObject params = new JSONObject();
410
411 try {
412 String ipAddress = GetIPFromHostname(alert.getMsgUri().getHost());
413 params.put("ip", ipAddress);
414 params.put("name", alert.getMsgUri().getName());
415 params.put("os", "Unknown");
416 params.put("description", "");
417 JSONObject metadata = new JSONObject();
418 metadata.put("creator", "Zap");
419 params.put("metadata", metadata);
420
421 String hostname = alert.getMessage().getRequestHeader().getHostName();
422 JSONArray hostNamesArray = new JSONArray();
423 hostNamesArray.add(hostname);
424 params.put("hostnames", hostNamesArray);
425
426 } catch (URIException e) {
427 e.printStackTrace();
428 }
429 return params;
430 }
431
432 private JSONObject ConvertServiceToParams(Alert alert, int parentId) {
433 // Request parameters and other properties.
434 JSONObject params = new JSONObject();
435 JSONArray portsJson = new JSONArray();
436 portsJson.add(alert.getMessage().getRequestHeader().getHostPort());
437 params.put("ports", portsJson);
438 params.put("parent", parentId);
439 params.put("status", "open");
440 params.put("type", "Service");
441 params.put("description", "");
442 JSONObject metadata = new JSONObject();
443 metadata.put("creator", "OWASP");
444 params.put("metadata", metadata);
445
446 switch (alert.getMessage().getRequestHeader().getHostPort()) {
447 case 21:
448 params.put("name", "FTP");
449 params.put("protocol", "tcp");
450 break;
451 case 22:
452 params.put("name", "SSH");
453 params.put("protocol", "tcp");
454 break;
455 case 23:
456 params.put("name", "TELNET");
457 params.put("protocol", "tcp");
458 break;
459 case 25:
460 params.put("name", "SMTP");
461 params.put("protocol", "tcp");
462 break;
463 case 80:
464 params.put("name", "HTTP");
465 params.put("protocol", "tcp");
466 break;
467 case 110:
468 params.put("name", "POP");
469 params.put("protocol", "tcp");
470 break;
471 case 443:
472 params.put("name", "SSL");
473 params.put("protocol", "tcp");
474 break;
475 default:
476 params.put("name", "unknown");
477 params.put("protocol", "unknown");
478 break;
479 }
480
481 return params;
482 }
483
484 private JSONObject ConvertCommandToParams(String commandName) {
485 // Request parameters and other properties.
486 JSONObject params = new JSONObject();
487 params.put("itime", Instant.EPOCH.getEpochSecond());
488 params.put("import_source", "shell");
489 params.put("duration", "");
490 params.put("command", "Zap");
491 params.put("tool", commandName);
492 return params;
493 }
494
495 private String GetIPFromHostname(String hostname) {
496 try {
497 InetAddress inetAddr = InetAddress.getByName(hostname);
498 byte[] addr = inetAddr.getAddress();
499
500 // Convert to dot representation
501 String ipAddr = "";
502 for (int i = 0; i < addr.length; i++) {
503 if (i > 0) {
504 ipAddr += ".";
505 }
506
507 ipAddr += addr[i] & 0xFF;
508 }
509
510 System.out.println("IP Address: " + ipAddr);
511 return ipAddr;
512 } catch (UnknownHostException e) {
513 System.out.println("Host not found: " + e.getMessage());
514 return "";
515 }
516 }
517
518 public void setBaseUrl(String baseUrl) {
519 this.baseUrl = baseUrl;
520 }
521 }
522
523
524
0 /*
1 * Zed Attack Proxy (ZAP) and its related class files.
2 *
3 * ZAP is an HTTP/HTTPS proxy for assessing web application security.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.zaproxy.zap.extension.faraday;
18
19 import org.apache.log4j.Logger;
20 import org.parosproxy.paros.Constant;
21 import org.parosproxy.paros.control.Control;
22 import org.parosproxy.paros.extension.ExtensionAdaptor;
23 import org.parosproxy.paros.extension.ExtensionHook;
24 import org.parosproxy.paros.extension.ExtensionPopupMenuItem;
25 import org.zaproxy.zap.view.ZapMenuItem;
26
27 import javax.swing.*;
28 import java.awt.*;
29 import java.awt.event.KeyEvent;
30 import java.io.*;
31 import java.util.Properties;
32 import java.util.ResourceBundle;
33
34 public class FaradayExtension extends ExtensionAdaptor {
35 private static final Logger logger = Logger.getLogger(FaradayExtension.class);
36 private ZapMenuItem menuItemFaradayConfig;
37 private ConfigurationDialog configurationDialog;
38 private PopupMenuItemSendAlert popupMenuItemSendAlert;
39 private PopupMenuItemSendRequest popupMenuItemSendRequest;
40 private ResourceBundle messages = null;
41
42
43
44 public FaradayExtension(String name) {
45 super(name);
46 }
47
48
49 public FaradayExtension() {
50 super();
51 initialize();
52 }
53
54
55 private void initialize() {
56 messages = ResourceBundle.getBundle(
57 this.getClass().getPackage().getName() +
58 ".Messages", Constant.getLocale());
59 this.setName(messages.getString("faraday.extension.name"));
60 this.initConfiguration();
61 }
62
63 @Override
64 public String getAuthor() {
65 return messages.getString("faraday.extension.author");
66 }
67
68 @Override
69 public void hook(ExtensionHook extensionHook) {
70 super.hook(extensionHook);
71
72 if (getView() != null) {
73 extensionHook.getHookMenu().addToolsMenuItem(getMenuItemFaradayConfig());
74 extensionHook.getHookMenu().addPopupMenuItem(this.getPopupMenuItem());
75 extensionHook.getHookMenu().addPopupMenuItem(this.getPopupMenuItemRequest());
76 }
77 }
78
79 @Override
80 public boolean canUnload() {
81 return true;
82 }
83
84 private ZapMenuItem getMenuItemFaradayConfig() {
85 if (menuItemFaradayConfig == null) {
86 menuItemFaradayConfig = new ZapMenuItem(
87 "faraday.menu.tools.label",
88 KeyStroke.getKeyStroke(
89 KeyEvent.VK_F,
90 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() | KeyEvent.ALT_DOWN_MASK,
91 false));
92 menuItemFaradayConfig.setEnabled(Control.getSingleton().getMode() != Control.Mode.safe);
93
94 menuItemFaradayConfig.addActionListener(new java.awt.event.ActionListener() {
95
96 @Override
97 public void actionPerformed(java.awt.event.ActionEvent e) {
98 showConfigurationDialog();
99 }
100 });
101 }
102 return menuItemFaradayConfig;
103 }
104
105
106 private void showConfigurationDialog() {
107 if (configurationDialog == null) {
108 configurationDialog = new ConfigurationDialog(messages.getString("faraday.config.dialog.title"));
109 configurationDialog.init();
110 }
111 configurationDialog.setVisible(true);
112 }
113
114
115 private ExtensionPopupMenuItem getPopupMenuItem() {
116 if (popupMenuItemSendAlert == null) {
117 popupMenuItemSendAlert = new PopupMenuItemSendAlert(messages.getString("faraday.button.send.alert"));
118 }
119
120 return popupMenuItemSendAlert;
121
122 }
123
124
125 private ExtensionPopupMenuItem getPopupMenuItemRequest() {
126 if (popupMenuItemSendRequest == null) {
127 popupMenuItemSendRequest = new PopupMenuItemSendRequest(messages.getString("faraday.button.send.request"));
128 }
129
130 return popupMenuItemSendRequest;
131
132 }
133
134
135 private void initConfiguration() {
136 Configuration configuration = Configuration.getSingleton();
137
138 Properties prop = new Properties();
139 InputStream input = null;
140
141 try {
142 String filePath = Constant.getZapHome() + "faraday" + File.separator + "default.properties";
143 input = new FileInputStream(filePath);
144
145 // load a properties file
146 prop.load(input);
147
148 // set the properties value
149 String fUser = prop.getProperty("default");
150 configuration.restore(fUser);
151
152 } catch (IOException io) {
153 System.out.println("We can't found default.properties file");
154 } finally {
155 if (input != null) {
156 try {
157 input.close();
158 } catch (IOException e) {
159 e.printStackTrace();
160 }
161 }
162
163 }
164 }
165
166 }
0 # An example ZAP extension which adds a top level menu item.
1 #
2 # This file defines the default (English) variants of all of the
3
4 faraday.extension.name = Faraday Extension
5 faraday.extension.author = Jorge Luis Gonzàlez Iznaga
6
7 faraday.menu.tools.label = Faraday configuration options
8 faraday.config.dialog.title = Faraday configuration
9 faraday.config.dialog.tabs.auth = Authorization
10 faraday.config.dialog.tabs.conf = Configuration
11 faraday.config.dialog.auth.user = Faraday user
12 faraday.config.dialog.auth.pass = Faraday password
13 faraday.config.dialog.server = Faraday server
14 faraday.config.dialog.auth.login = Login
15 faraday.config.dialog.auth.logout = Logout
16 faraday.config.dialog.import.current = Import current vulnerabilities
17 faraday.config.dialog.import.new = Import new vulnerabilities
18 faraday.config.dialog.restore = Restore configuration
19 faraday.config.dialog.refresh = Refresh
20 faraday.config.dialog.save = Save configuration
21 faraday.config.dialog.workspace = Select faraday workspace
22 faraday.config.dialog.default = Set this configuration as default
23
24 faraday.config.dialog.tab.auth = Authorization
25 faraday.config.dialog.tab.conf = Configuration
26
27 faraday.dialog.enter.user = Please enter your user
28
29
30 faraday.button.send.alert = Send alert to Faraday
31 faraday.button.send.request = Send request to Faraday
32
33 faraday.message.invalid.check.credentials = Please, check your credentials
34 faraday.message.invalid.credentials = Invalid credentials
35 faraday.dialog.login.title = Faraday login
36 faraday.dialog.logout.title = Faraday logout
37 faraday.dialog.logout.success = You're logout successfully !
38 faraday.dialog.logout.error = We can't complete logout operation
39
40 faraday.dialog.button.close = Close
41
42 faraday.save.config.success = Configuration saved successfully
43 faraday.save.config.error = We can't save your configuration, please check home ZAP directory
44 faraday.restore.config.error = You haven't a configuration saved with this user
45 faraday.refresh.workspace.done = Your workspaces are up to date
46 faraday.restore.config.error.login = Unable to restore this configuration
47 faraday.set.default.config.error = We can't set your configuration
48
49 faraday.send.alert.permissions.error = You should check your current workspace and your permissions
50 faraday.send.alert.conflict = This alert already exists in Faraday
51 faraday.send.request.conflict = This request already exists in Faraday
52 faraday.send.alert.success = Alert added successfully
53 faraday.send.request.success = Request added successfully
54
55 faraday.tool.command.name = OWASP
0 package org.zaproxy.zap.extension.faraday;
1
2 import org.apache.log4j.Logger;
3 import org.parosproxy.paros.Constant;
4 import org.parosproxy.paros.core.scanner.Alert;
5 import org.parosproxy.paros.view.View;
6 import org.zaproxy.zap.extension.alert.AlertNode;
7 import org.zaproxy.zap.extension.alert.PopupMenuItemAlert;
8
9 import javax.swing.*;
10 import java.awt.*;
11 import java.util.ResourceBundle;
12
13 public class PopupMenuItemSendAlert extends PopupMenuItemAlert {
14 private static final Logger logger = Logger.getLogger(PopupMenuItemSendAlert.class);
15 private FaradayClient faradayClient;
16 private ResourceBundle messages = null;
17 private int selectionCount = 0;
18 private int totalSelectionCount = 0;
19 private boolean treeAlertParentSelected = false;
20
21 public PopupMenuItemSendAlert(String label) {
22 super(label, true);
23 Configuration configuration = Configuration.getSingleton();
24 faradayClient = new FaradayClient(configuration.getServer());
25 messages = ResourceBundle.getBundle(
26 this.getClass().getPackage().getName() +
27 ".Messages", Constant.getLocale());
28 }
29
30 @Override
31 protected void performAction(Alert alert) {
32 Configuration configuration = Configuration.getSingleton();
33 String workspace = configuration.getWorkspace();
34 String session = configuration.getSession();
35 if (workspace != null && session != null && !workspace.equals("") && !session.equals("")) {
36 int responseCode = faradayClient.AddVulnerability(alert, configuration.getWorkspace(), session);
37 String message;
38 int iconMessage = 1;
39 switch (responseCode) {
40 case 200:
41 case 201:
42 case 409:
43 message = messages.getString("faraday.send.alert.success");
44 break;
45 case 403:
46 message = messages.getString("faraday.send.alert.permissions.error");
47 iconMessage = JOptionPane.WARNING_MESSAGE;
48 break;
49 // case 409:
50 // message = messages.getString("faraday.send.alert.conflict");
51 // iconMessage = JOptionPane.WARNING_MESSAGE;
52 // break;
53 case 400:
54 case 500:
55 message = "Unable to send " + alert.getName() + " to Faraday";
56 iconMessage = JOptionPane.ERROR_MESSAGE;
57 break;
58
59 default:
60 message = "Unable to send " + alert.getName() + " to Faraday";
61 iconMessage = JOptionPane.ERROR_MESSAGE;
62 break;
63 }
64
65 if (canShowMessageDialog()/*this.selectionCount == 1 && !treeAlertParentSelected*/) {
66 JOptionPane.showMessageDialog(
67 this,
68 message,
69 messages.getString("faraday.button.send.alert"),
70 iconMessage);
71 }
72
73
74 logger.error(message);
75 if (View.isInitialised()) {
76 // Report info to the Output tab
77 View.getSingleton().getOutputPanel().append(message + "\n");
78 }
79
80
81 } else {
82 if (canShowMessageDialog()) {
83 JOptionPane.showMessageDialog(
84 this,
85 messages.getString("faraday.send.alert.permissions.error"),
86 messages.getString("faraday.button.send.alert"),
87 JOptionPane.ERROR_MESSAGE);
88 logger.error(messages.getString("faraday.send.alert.permissions.error"));
89 }
90
91
92 if (View.isInitialised()) {
93 // Report info to the Output tab
94 View.getSingleton().getOutputPanel().append(messages.getString("faraday.send.alert.permissions.error") + "\n");
95 }
96 }
97
98 }
99
100 @Override
101 public boolean isEnableForComponent(Component invoker) {
102 logger.info(invoker.getName());
103 this.totalSelectionCount = 0;
104 try {
105 if (Configuration.getSingleton().getSession() == null || Configuration.getSingleton().getSession().equals("")) {
106 return false;
107 }
108 treeAlertParentSelected = ((JTree) invoker).isRowSelected(0);
109 if (super.isEnableForComponent(invoker) || treeAlertParentSelected) {
110 this.selectionCount = ((JTree) invoker).getSelectionCount();
111 for (int i = 0; i < ((JTree) invoker).getSelectionPaths().length; i++) {
112 AlertNode nodeTemp = (AlertNode) ((JTree) invoker).getSelectionPaths()[i].getLastPathComponent();
113 this.totalSelectionCount += getTotalAlertsToProcess(nodeTemp);
114 }
115
116 setEnabled(true);
117 return true;
118 }
119 return false;
120 } catch (Exception e) {
121 return false;
122 }
123 }
124
125
126 private int getTotalAlertsToProcess(AlertNode node) {
127 if (node.getChildCount() > 0) {
128 int total = 0;
129 for (int i = 0; i < node.getChildCount(); i++) {
130 total += getTotalAlertsToProcess(node.getChildAt(i));
131 }
132 return total;
133 } else {
134 return 1;
135 }
136
137 }
138
139 private boolean canShowMessageDialog() {
140 this.totalSelectionCount--;
141 if (this.treeAlertParentSelected) {
142 this.totalSelectionCount = 1;
143 this.treeAlertParentSelected = false;
144 }
145
146 return this.totalSelectionCount == 0;
147 }
148 }
0 package org.zaproxy.zap.extension.faraday;
1
2 import org.apache.log4j.Logger;
3 import org.parosproxy.paros.Constant;
4 import org.parosproxy.paros.core.scanner.Alert;
5 import org.parosproxy.paros.db.DatabaseException;
6 import org.parosproxy.paros.db.RecordAlert;
7 import org.parosproxy.paros.model.HistoryReference;
8 import org.parosproxy.paros.network.HttpMalformedHeaderException;
9 import org.parosproxy.paros.view.View;
10 import org.zaproxy.zap.extension.alert.PopupMenuAlert;
11 import org.zaproxy.zap.view.messagecontainer.http.HttpMessageContainer;
12 import org.zaproxy.zap.view.popup.PopupMenuItemHistoryReferenceContainer;
13
14 import javax.swing.*;
15 import java.awt.*;
16 import java.util.Iterator;
17 import java.util.List;
18 import java.util.ResourceBundle;
19
20
21 public class PopupMenuItemSendRequest extends PopupMenuItemHistoryReferenceContainer {
22 private FaradayClient faradayClient;
23 private ResourceBundle messages = null;
24 private int selectionCount = 0;
25 private static final Logger logger = Logger.getLogger(PopupMenuItemSendRequest.class);
26
27
28 public PopupMenuItemSendRequest(String label) {
29 super(label, true);
30 Configuration configuration = Configuration.getSingleton();
31 faradayClient = new FaradayClient(configuration.getServer());
32 messages = ResourceBundle.getBundle(
33 this.getClass().getPackage().getName() +
34 ".Messages", Constant.getLocale());
35 }
36
37 @Override
38 public void performAction(HistoryReference href) {
39 try {
40 Alert alert = new Alert(new RecordAlert(), href);
41 alert.setName(href.getSiteNode().getName());
42 alert.setUri(href.getURI().toString());
43 alert.setMessage(href.getHttpMessage());
44 alert.setDescription("");
45 alert.setRiskConfidence(0, 0);
46
47 Configuration configuration = Configuration.getSingleton();
48 String workspace = configuration.getWorkspace();
49 String session = configuration.getSession();
50 if (workspace != null && session != null && !workspace.equals("") && !session.equals("")) {
51 int responseCode = faradayClient.AddVulnerability(alert, configuration.getWorkspace(), session);
52 String message = "";
53 int iconMessage = 1;
54 switch (responseCode) {
55 case 403:
56 message = messages.getString("faraday.send.alert.permissions.error");
57 iconMessage = JOptionPane.WARNING_MESSAGE;
58 break;
59 case 409:
60 message = messages.getString("faraday.send.request.conflict");
61 iconMessage = JOptionPane.WARNING_MESSAGE;
62 break;
63 case 500:
64 message = "Unable to send " + alert.getName() + " to Faraday";
65 iconMessage = JOptionPane.ERROR_MESSAGE;
66 break;
67 case 201:
68 message = messages.getString("faraday.send.request.success");
69 break;
70 }
71
72 if (this.selectionCount == 1) {
73 JOptionPane.showMessageDialog(
74 this,
75 message,
76 messages.getString("faraday.button.send.alert"),
77 iconMessage);
78 }
79
80 logger.error(message);
81 if (View.isInitialised()) {
82 // Report info to the Output tab
83 View.getSingleton().getOutputPanel().append(message + "\n");
84 }
85
86
87 } else {
88 JOptionPane.showMessageDialog(
89 this,
90 messages.getString("faraday.send.alert.permissions.error"),
91 messages.getString("faraday.button.send.request"),
92 JOptionPane.ERROR_MESSAGE);
93
94 logger.error(messages.getString("faraday.send.alert.permissions.error"));
95 if (View.isInitialised()) {
96 // Report info to the Output tab
97 View.getSingleton().getOutputPanel().append(messages.getString("faraday.send.alert.permissions.error") + "\n");
98 }
99 }
100
101
102 } catch (HttpMalformedHeaderException e) {
103 e.printStackTrace();
104 } catch (DatabaseException e) {
105 e.printStackTrace();
106 }
107 }
108
109
110 @Override
111 public void performHistoryReferenceActions(List<HistoryReference> hrefs) {
112 this.selectionCount = hrefs.size();
113
114 for (HistoryReference href : hrefs) {
115 this.performAction(href);
116 }
117 }
118
119 @Override
120 public boolean isEnableForInvoker(Invoker invoker, HttpMessageContainer httpMessageContainer) {
121 if (Configuration.getSingleton().getSession() == null || Configuration.getSingleton().getSession().equals("") ||
122 invoker.name().equals("ALERTS_PANEL")) {
123 return false;
124 }
125 return super.isEnableForInvoker(invoker, httpMessageContainer);
126 }
127
128 @Override
129 public boolean isButtonEnabledForHistoryReference(HistoryReference href) {
130 if (Configuration.getSingleton().getSession() == null || Configuration.getSingleton().getSession().equals("")) {
131 return false;
132 }
133
134 return href.getSiteNode() != null && super.isButtonEnabledForHistoryReference(href);
135 }
136 }
0 <zapaddon>
1 <name>Faraday</name>
2 <version>1</version>
3 <status>release</status>
4 <description>This extension integrates ZAP with the Faraday Integrated Penetration-Test Environment</description>
5 <author>Jorge Luis González Iznaga</author>
6 <extensions>
7 <extension>org.zaproxy.zap.extension.faraday.FaradayExtension</extension>
8 </extensions>
9 <ascanrules/>
10 <pscanrules/>
11 <not-before-version>2.7.0</not-before-version>
12 <not-from-version/>
13 </zapaddon>
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 os
7 import socket
8 from faraday_plugins.plugins.plugin import PluginXMLFormat
9
10
11 try:
12 import xml.etree.cElementTree as ET
13 ETREE_VERSION = ET.VERSION
14 except ImportError:
15 import xml.etree.ElementTree as ET
16 ETREE_VERSION = ET.VERSION
17
18 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
19
20 current_path = os.path.abspath(os.getcwd())
21
22 __author__ = "Francisco Amato"
23 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
24 __credits__ = ["Francisco Amato"]
25 __license__ = ""
26 __version__ = "1.0.0"
27 __maintainer__ = "Francisco Amato"
28 __email__ = "[email protected]"
29 __status__ = "Development"
30
31
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
40
41 class ZapXmlParser:
42 """
43 The objective of this class is to parse an xml
44 file generated by the zap tool.
45
46 TODO: Handle errors.
47 TODO: Test zap output version. Handle what happens
48 if the parser doesn't support it.
49
50 TODO: Test cases.
51
52 @param zap_xml_filepath A proper xml generated by zap
53 """
54
55 def __init__(self, xml_output):
56
57 tree = self.parse_xml(xml_output)
58
59 if tree is not None:
60 self.sites = [data for data in self.get_items(tree)]
61 else:
62 self.sites = []
63
64 def parse_xml(self, xml_output):
65 """
66 Open and parse an xml file.
67
68 TODO: Write custom parser to just read the nodes that we need instead of
69 reading the whole file.
70
71 @return xml_tree An xml tree instance. None if error.
72 """
73 try:
74 parser = ET.XMLParser(target=ET.TreeBuilder())
75 parser.feed(xml_output)
76 tree = parser.close()
77
78 except SyntaxError as err:
79 print("SyntaxError: %s. %s" % (err, xml_output))
80 return None
81
82 return tree
83
84 def get_items(self, tree):
85 """
86 @return items A list of Host instances
87 """
88 for node in tree.findall('site'):
89 yield Site(node)
90
91
92 def get_attrib_from_subnode(xml_node, subnode_xpath_expr, attrib_name):
93 """
94 Finds a subnode in the item node and the retrieves a value from it
95
96 @return An attribute value
97 """
98 global ETREE_VERSION
99 node = None
100
101 if ETREE_VERSION[0] <= 1 and ETREE_VERSION[1] < 3:
102
103 match_obj = re.search(
104 "([^\@]+?)\[\@([^=]*?)=\'([^\']*?)\'",
105 subnode_xpath_expr)
106
107 if match_obj is not None:
108
109 node_to_find = match_obj.group(1)
110 xpath_attrib = match_obj.group(2)
111 xpath_value = match_obj.group(3)
112
113 for node_found in xml_node.findall(node_to_find):
114
115 if node_found.attrib[xpath_attrib] == xpath_value:
116 node = node_found
117 break
118 else:
119 node = xml_node.find(subnode_xpath_expr)
120
121 else:
122 node = xml_node.find(subnode_xpath_expr)
123
124 if node is not None:
125 return node.get(attrib_name)
126
127 return None
128
129
130 class Site:
131
132 def __init__(self, item_node):
133
134 self.node = item_node
135
136 self.host = self.node.get('host')
137 self.ip = self.resolve(self.host)
138 self.port = self.node.get('port')
139
140 self.items = []
141 for alert in self.node.findall('alerts/alertitem'):
142 self.items.append(Item(alert))
143
144 def get_text_from_subnode(self, subnode_xpath_expr):
145 """
146 Finds a subnode in the host node and the retrieves a value from it.
147
148 @return An attribute value
149 """
150 sub_node = self.node.find(subnode_xpath_expr)
151 if sub_node is not None:
152 return sub_node.text
153 return None
154
155 def resolve(self, host):
156
157 try:
158 return socket.gethostbyname(host)
159 except:
160 pass
161
162 return host
163
164
165 class Item:
166 """
167 An abstract representation of a Item
168
169
170 @param item_node A item_node taken from an zap xml tree
171 """
172
173 def __init__(self, item_node):
174
175 self.node = item_node
176 self.id = self.get_text_from_subnode('pluginid')
177 self.name = self.get_text_from_subnode('alert')
178 self.severity = self.get_text_from_subnode('riskcode')
179 self.desc = self.get_text_from_subnode('desc')
180
181 if self.get_text_from_subnode('solution'):
182 self.resolution = self.get_text_from_subnode('solution')
183 else:
184 self.resolution = ''
185
186 if self.get_text_from_subnode('reference'):
187 self.desc += '\nReference: ' + \
188 self.get_text_from_subnode('reference')
189
190 self.ref = []
191 if self.get_text_from_subnode('cweid'):
192 self.ref.append("CWE-" + self.get_text_from_subnode('cweid'))
193
194 self.items = []
195
196 if item_node.find('instances'):
197 arr = item_node.find('instances')
198 else:
199 arr = [item_node]
200
201 for elem in arr:
202 uri = elem.find('uri').text
203 self.parse_uri(uri)
204
205 self.requests = "\n".join([i['uri'] for i in self.items])
206
207 def parse_uri(self, uri):
208 mregex = re.search(
209 "(http|https|ftp)\://([a-zA-Z0-9\.\-]+(\:[a-zA-Z0-9\.&amp"
210 ";%\$\-]+)*@)*((25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]"
211 "{1}|[1-9])\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}"
212 "|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}"
213 "|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|"
214 "[0-9])|localhost|([a-zA-Z0-9\-]+\.)*[a-zA-Z0-9\-]+\.(com|edu|gov|"
215 "int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2"
216 "}))[\:]*([0-9]+)*([/]*($|[a-zA-Z0-9\.\,\?\'\\\+&amp;%\$#\=~_\-]+))"
217 ".*?$",
218 uri)
219
220 protocol = mregex.group(1)
221 host = mregex.group(4)
222 port = 80
223 if protocol == 'https':
224 port = 443
225 if mregex.group(11) is not None:
226 port = mregex.group(11)
227
228 try:
229 params = [i.split('=')[0]
230 for i in uri.split('?')[1].split('&')]
231 except Exception as e:
232 params = ''
233
234 item = {
235 'uri': uri,
236 'params': ', '.join(params),
237 'host': host,
238 'protocol': protocol,
239 'port': port
240 }
241 self.items.append(item)
242
243 def get_text_from_subnode(self, subnode_xpath_expr):
244 """
245 Finds a subnode in the host node and the retrieves a value from it.
246
247 @return An attribute value
248 """
249 sub_node = self.node.find(subnode_xpath_expr)
250 if sub_node is not None:
251 return sub_node.text
252
253 return None
254
255
256 class ZapPlugin(PluginXMLFormat):
257 """
258 Example plugin to parse zap output.
259 """
260
261 def __init__(self):
262 super().__init__()
263 self.identifier_tag = "OWASPZAPReport"
264 self.id = "Zap"
265 self.name = "Zap XML Output Plugin"
266 self.plugin_version = "0.0.3"
267 self.version = "2.4.3"
268 self.framework_version = "1.0.0"
269 self.options = None
270 self._current_output = None
271 self.target = None
272 self._command_regex = re.compile(r'^(zap|sudo zap|\.\/zap).*?')
273
274 def parseOutputString(self, output, debug=False):
275 """
276 This method will discard the output the shell sends, it will read it
277 from the xml where it expects it to be present.
278
279 NOTE: if 'debug' is true then it is being run from a test case and the
280 output being sent is valid.
281 """
282
283 parser = ZapXmlParser(output)
284
285 for site in parser.sites:
286
287 host = []
288 if site.host != site.ip:
289 host = [site.host]
290
291 h_id = self.createAndAddHost(site.ip)
292
293 i_id = self.createAndAddInterface(
294 h_id,
295 site.ip,
296 ipv4_address=site.ip,
297 hostname_resolution=host
298 )
299
300 s_id = self.createAndAddServiceToInterface(
301 h_id,
302 i_id,
303 "http",
304 "tcp",
305 ports=[site.port],
306 status='open'
307 )
308
309 for item in site.items:
310 self.createAndAddVulnWebToService(
311 h_id,
312 s_id,
313 item.name,
314 item.desc,
315 website=site.host,
316 severity=item.severity,
317 path=item.items[0]['uri'],
318 params=item.items[0]['params'],
319 request=item.requests,
320 ref=item.ref,
321 resolution=item.resolution
322 )
323
324 del parser
325
326 def processCommandString(self, username, current_path, command_string):
327 return None
328
329 def setHost(self):
330 pass
331
332
333 def createPlugin():
334 return ZapPlugin()
335
336
337 if __name__ == "__main__":
338 import sys
339 if len(sys.argv) == 2:
340 report_file = sys.argv[1]
341 if os.path.isfile(report_file):
342 plugin = createPlugin()
343 plugin.processReport(report_file)
344 print(plugin.get_json())
345 else:
346 print(f"Report not found: {report_file}")
347 else:
348 print(f"USAGE {sys.argv[0]} REPORT_FILE")
0 <?xml version="1.0" encoding="UTF-8"?><report>
1 Report generated at Tue, 12 Jul 2011 08:32:22.
2 <alertitem>
3 <pluginid>40000</pluginid>
4 <alert>Cookie set without HttpOnly flag</alert>
5 <riskcode>1</riskcode>
6 <reliability>2</reliability>
7 <riskdesc>Low (Warning)</riskdesc>
8 <desc>A cookie has been set without the HttpOnly flag, which means that the cookie can be accessed by JavaScript. If a malicious script can be run on this page then the cookie will be accessible and can be transmitted to another site. If this is a session cookie then session hijacking may be possible.
9 </desc>
10 <uri>http://192.168.1.100/</uri>
11 <param>ASPSESSIONIDQSDRBCRQ=EEFHJOACLHOKLJHFNAFBBECK; path=/</param>
12 <otherinfo/>
13 <uri>http://www.web3.com.ar/ServFotoPorNoticia.asp</uri>
14 <param>ASPSESSIONIDCQATADBB=LMFPHGLBHIIEDFILGFJEJNGE; path=/</param>
15 <otherinfo/>
16 <uri>http://www.web1.com.ar/acceso/include/valida.asp</uri>
17 <param>ASPSESSIONIDSCBABSTB=MNPOKADDPAIDCDNBPGFDHGBF; path=/</param>
18 <otherinfo/>
19 <uri>http://www.web3.com.ar/files/</uri>
20 <param>ASPSESSIONIDCSCTCABB=HFCNOPJBMNJEAHDHMCKAHOBN; path=/</param>
21 <otherinfo/>
22 <uri>http://www.web2.com.ar/acceso/include/valida.asp</uri>
23 <param>ASPSESSIONIDQAASDACB=HADJFCIBOIANGBGNAOIDBGIL; path=/</param>
24 <otherinfo/>
25 <uri>http://www.web3.com.ar/</uri>
26 <param>ASPSESSIONIDSABQACDB=PAJBMJHBLOFELCIKBNLAAKKJ; path=/</param>
27 <otherinfo/>
28 <solution>Ensure that the HttpOnly flag is set for all cookies.
29 </solution>
30 <reference>www.owasp.org/index.php/HttpOnly
31 </reference>
32 </alertitem>
33 <alertitem>
34 <pluginid>40001</pluginid>
35 <alert>Password Autocomplete in browser</alert>
36 <riskcode>1</riskcode>
37 <reliability>2</reliability>
38 <riskdesc>Low (Warning)</riskdesc>
39 <desc>AUTOCOMPLETE attribute is not disabled in HTML FORM/INPUT element containing password type input. Passwords may be stored in browsers and retrieved.
40 </desc>
41 <uri>http://192.168.1.100/</uri>
42 <param>input</param>
43 <otherinfo/>
44 <uri>http://www.web3.com.ar/</uri>
45 <param>input</param>
46 <otherinfo/>
47 <uri>http://www.web3.com.ar/default.asp?errsession=1</uri>
48 <param>input</param>
49 <otherinfo/>
50 <uri>http://www.web3.com.ar/</uri>
51 <param>input</param>
52 <otherinfo/>
53 <uri>http://www.web3.com.ar/</uri>
54 <param>input</param>
55 <otherinfo/>
56 <uri>http://www.web2.com.ar/dealers.htm</uri>
57 <param>input</param>
58 <otherinfo/>
59 <uri>http://www.web3.com.ar/</uri>
60 <param>input</param>
61 <otherinfo/>
62 <solution>Turn off AUTOCOMPLETE attribute in form or individual input elements containing password by using AUTOCOMPLETE='OFF'
63 </solution>
64 <reference>http://msdn.microsoft.com/library/default.asp?url=/workshop/author/forms/autocomplete_ovr.asp
65 </reference>
66 </alertitem>
67 <alertitem>
68 <pluginid>40003</pluginid>
69 <alert>Cross site scripting</alert>
70 <riskcode>3</riskcode>
71 <reliability>2</reliability>
72 <riskdesc>High (Warning)</riskdesc>
73 <desc>Cross-site scripting or HTML injection is possible. Malicious script may be injected into the browser which appeared to be genuine content from the original site. These scripts can be used to execute arbitrary code or steal customer sensitive information such as user password or cookies.
74 Very often this is in the form of a hyperlink with the injected script embeded in the query strings. However, XSS is possible via FORM POST data, cookies, user data sent from another user or shared data retrieved from database.
75 Currently this check does not verify XSS from cookie or database. They should be checked manually if the application retrieve database records from another user's input.
76 </desc>
77 <uri>http://www.web3.com.ar/Mes.asp?hhFrm=frm&amp;hhDia=DiaF&amp;hhMes=MesF&amp;hhAnno=%3CSCRIPT%3Ealert(%22OWASP%20ZAP%22);%3C/SCRIPT%3E</uri>
78 <param>hhAnno=&lt;SCRIPT&gt;alert("OWASP ZAP");&lt;/SCRIPT&gt;</param>
79 <otherinfo/>
80 <uri>http://www.web3.com.ar/Mes.asp?hhFrm=frm&amp;hhDia=DiaF&amp;hhMes=%3CSCRIPT%3Ealert(%22OWASP%20ZAP%22);%3C/SCRIPT%3E&amp;hhAnno=AnnoF</uri>
81 <param>hhMes=&lt;SCRIPT&gt;alert("OWASP ZAP");&lt;/SCRIPT&gt;</param>
82 <otherinfo/>
83 <uri>http://www.web3.com.ar/Mes.asp?hhFrm=frm&amp;hhDia=%3CSCRIPT%3Ealert(%22OWASP%20ZAP%22);%3C/SCRIPT%3E&amp;hhMes=MesF&amp;hhAnno=AnnoF</uri>
84 <param>hhDia=&lt;SCRIPT&gt;alert("OWASP ZAP");&lt;/SCRIPT&gt;</param>
85 <otherinfo/>
86 <uri>http://www.web3.com.ar/Mes.asp?hhFrm=%3CSCRIPT%3Ealert(%22OWASP%20ZAP%22);%3C/SCRIPT%3E&amp;hhDia=DiaF&amp;hhMes=MesF&amp;hhAnno=AnnoF</uri>
87 <param>hhFrm=&lt;SCRIPT&gt;alert("OWASP ZAP");&lt;/SCRIPT&gt;</param>
88 <otherinfo/>
89 <solution>Do not trust client side input even if there is client side validation. Sanitize potentially danger characters in the server side. Very often filtering the &lt;, &gt;, " characters prevented injected script to be executed in most cases. However, sometimes other danger meta-characters such as ' , (, ), /, &amp;, ; etc are also needed.
90 In addition (or if these characters are needed), HTML encode meta-characters in the response. For example, encode &lt; as &amp;lt;
91
92 </solution>
93 <reference>The OWASP guide at http://www.owasp.org/documentation/guide
94 http://www.technicalinfo.net/papers/CSS.html
95 http://www.cgisecurity.org/articles/xss-faq.shtml
96 http://www.cert.org/tech_tips/malicious_code_FAQ.html
97 http://sandsprite.com/Sleuth/papers/RealWorld_XSS_1.html
98
99 </reference>
100 </alertitem>
101 <alertitem>
102 <pluginid>40004</pluginid>
103 <alert>Cross site scripting without brackets</alert>
104 <riskcode>3</riskcode>
105 <reliability>1</reliability>
106 <riskdesc>High (Suspicious)</riskdesc>
107 <desc>Cross-site scripting or HTML injection is possible without '&lt;' and '&gt;'. Malicious script may be injected into the browser which appeared to be genuine content from the original site. These scripts can be used to execute arbitrary code or steal customer sensitive information such as user password or cookies.
108 Very often this is in the form of a hyperlink with the injected script embeded in the query strings. However, XSS is possible via FORM POST data, cookies, user data sent from another user or shared data retrieved from database.
109 Currently this check does not verify XSS from cookie or database. They should be checked manually if the application retrieve database records from another user's input.
110 </desc>
111 <uri>http://www.web3.com.ar/Mes.asp?hhFrm=frm&amp;hhDia=DiaF&amp;hhMes=MesF&amp;hhAnno=paros%22%20style=%22background:url(javascript:alert('OWASP%20ZAP'))</uri>
112 <param>hhAnno=paros" style="background:url(javascript:alert('OWASP ZAP'))</param>
113 <otherinfo/>
114 <uri>http://www.web3.com.ar/Mes.asp?hhFrm=frm&amp;hhDia=DiaF&amp;hhMes=paros%22%20style=%22background:url(javascript:alert('OWASP%20ZAP'))&amp;hhAnno=%3CSCRIPT%3Ealert(%22OWASP%20ZAP%22);%3C/SCRIPT%3E</uri>
115 <param>hhMes=paros" style="background:url(javascript:alert('OWASP ZAP'))</param>
116 <otherinfo/>
117 <uri>http://www.web3.com.ar/Mes.asp?hhFrm=frm&amp;hhDia=paros%22%20style=%22background:url(javascript:alert('OWASP%20ZAP'))&amp;hhMes=MesF&amp;hhAnno=%3CSCRIPT%3Ealert(%22OWASP%20ZAP%22);%3C/SCRIPT%3E</uri>
118 <param>hhDia=paros" style="background:url(javascript:alert('OWASP ZAP'))</param>
119 <otherinfo/>
120 <uri>http://www.web3.com.ar/Mes.asp?hhFrm=paros%22%20style=%22background:url(javascript:alert('OWASP%20ZAP'))&amp;hhDia=DiaF&amp;hhMes=MesF&amp;hhAnno=%3CSCRIPT%3Ealert(%22OWASP%20ZAP%22);%3C/SCRIPT%3E</uri>
121 <param>hhFrm=paros" style="background:url(javascript:alert('OWASP ZAP'))</param>
122 <otherinfo/>
123 <solution>Do not trust client side input even if there is client side validation. Sanitize potentially danger characters in the server side. Very often filtering the &lt;, &gt;, " characters prevented injected script to be executed in most cases. However, sometimes other danger meta-characters such as ' , (, ), /, &amp;, ; etc are also needed.
124 In addition (or if these characters are needed), HTML encode meta-characters in the response. For example, encode &lt; as &amp;lt;
125
126 </solution>
127 <reference>The OWASP guide at http://www.owasp.org/documentation/guide
128 http://www.technicalinfo.net/papers/CSS.html
129 http://www.cgisecurity.org/articles/xss-faq.shtml
130 http://www.cert.org/tech_tips/malicious_code_FAQ.html
131 http://sandsprite.com/Sleuth/papers/RealWorld_XSS_1.html
132
133 </reference>
134 </alertitem>
135 <alertitem>
136 <pluginid>40030</pluginid>
137 <alert>SQL Injection</alert>
138 <riskcode>3</riskcode>
139 <reliability>2</reliability>
140 <riskdesc>High (Warning)</riskdesc>
141 <desc>SQL injection is possible. User parameters submitted will be formulated into a SQL query for database processing. If the query is built by simple 'string concatenation', it is possible to modify the meaning of the query by carefully crafting the parameters. Depending on the access right and type of database used, tampered query can be used to retrieve sensitive information from the database or execute arbitrary code. MS SQL and PostGreSQL, which supports multiple statements, may be exploited if the database access right is more powerful.
142 This can occur in URL query strings, POST paramters or even cookies. Currently check on cookie is not supported by Paros. You should check SQL injection manually as well as some blind SQL injection areas cannot be discovered by this check.
143 </desc>
144 <uri>http://www.web3.com.ar/buscador.asp</uri>
145 <param>hId=&amp;hAreturn=&amp;hAccion=OK&amp;txtBuscar=test&amp;x=0&amp;y=0%27+AND+%271%27%3D%271</param>
146 <otherinfo/>
147 <uri>http://www.web3.com.ar/buscador.asp</uri>
148 <param>hId=&amp;hAreturn=&amp;hAccion=OK%22+OR+%221%22%3D%221&amp;txtBuscar=test&amp;x=0&amp;y=0</param>
149 <otherinfo/>
150 <solution>Do not trust client side input even if there is client side validation. In general, If the input string is numeric, type check it.
151 If the application used JDBC, use PreparedStatement or CallableStatement with parameters passed by '?'
152 If the application used ASP, use ADO Command Objects with strong type checking and parameterized query.
153 If stored procedure or bind variables can be used, use it for parameter passing into query. Do not just concatenate string into query in the stored procedure!
154 Do not create dynamic SQL query by simple string concatentation.
155 Use minimum database user privilege for the application. This does not eliminate SQL injection but minimize its damage. Eg if the application require reading one table only, grant such access to the application. Avoid using 'sa' or 'db-owner'.
156
157 </solution>
158 <reference>The OWASP guide at http://www.owasp.org/documentation/guide
159 http://www.sqlsecurity.com/DesktopDefault.aspx?tabid=23
160 http://www.spidynamics.com/whitepapers/WhitepaperSQLInjection.pdf
161 For Oracle database, refer to http://www.integrigy.com/info/IntegrigyIntrotoSQLInjectionAttacks.pdf
162
163 </reference>
164 </alertitem>
165 </report>
0 from setuptools import setup, find_packages
1 from re import search
2
3 with open('faraday_plugins/__init__.py', 'rt', encoding='utf8') as f:
4 version = search(r'__version__ = \'(.*?)\'', f.read()).group(1)
5
6
7 install_requires = [
8 'Click',
9 'simplejson',
10 'requests',
11 ]
12
13
14 setup(
15 name='faraday-plugins',
16 version=version,
17 packages=find_packages(include=['faraday_plugins', 'faraday_plugins.*']),
18 url='',
19 license='',
20 author='Faradaysec',
21 author_email='',
22 description='',
23 include_package_data=True,
24 install_requires=install_requires,
25 )
0 import os
1
2 import json
3 import pytest
4 from faraday_plugins.plugins.manager import PluginsManager, ReportAnalyzer
5 from faraday_plugins.plugins.plugin import PluginBase
6
7 BLACK_LIST = [
8 'LICENSE',
9 'README.md',
10 '.gitignore',
11 '.gitkeep',
12 ]
13
14 def list_report_files():
15 report_filenames = os.walk('./report-collection')
16
17 for root, directory, filenames in report_filenames:
18 if '.git' in directory:
19 continue
20 for filename in filenames:
21 if filename in BLACK_LIST:
22 continue
23 if '.git' in root:
24 continue
25 yield os.path.join(root, filename)
26
27
28 @pytest.mark.parametrize("report_filename", list_report_files())
29 def test_autodetection_on_all_report_collection(report_filename):
30 plugins_manager = PluginsManager()
31 analyzer = ReportAnalyzer(plugins_manager)
32 plugin: PluginBase = analyzer.get_plugin(report_filename)
33 assert plugin, report_filename
34 plugin.processReport(report_filename)
35 plugin_json = json.loads(plugin.get_json())
36 assert "hosts" in plugin_json
37 assert "command" in plugin_json
38 assert len(plugin_json) == 2