Imported Upstream version 1.0.18
Sophie Brun
8 years ago
8 | 8 | * Francisco Amato |
9 | 9 | * Franco Linares |
10 | 10 | * Micaela Ranea Sánchez |
11 | * Ezequiel Tavella | |
12 | * Joaquín López Pereyra | |
13 | * Martín Rocha | |
11 | 14 | |
12 | 15 | Project contributors |
13 | 16 | |
15 | 18 | * Juan Urbano |
16 | 19 | * Elian Gidoni |
17 | 20 | * Andres Tarantini |
18 | * Ezequiel Tavella | |
19 | 21 | * Martin Tartarelli |
20 | 22 | * Ronald Iraheta |
45 | 45 | |
46 | 46 | Already a user and have a question or bug report? Please check out our [FAQ](https://github.com/infobyte/faraday/wiki/FAQ). If you're still having troubles you can [open a ticket](https://github.com/infobyte/faraday/issues/new). |
47 | 47 | |
48 | Join our community! Subscribe to our [mailing list](https://groups.google.com/forum/#!forum/faradaysec) or find us on Twitter [@faradaysec] (https://twitter.com/faradaysec) or IRC channel #faraday-dev in [freenode](ircs://irc.freenode.net/faraday-dev). | |
48 | Join our community! Subscribe to our [mailing list](https://groups.google.com/forum/#!forum/faradaysec) or find us on Twitter [@faradaysec] (https://twitter.com/faradaysec) | |
49 | 49 | |
50 | Do you have a question? Troubleshooting? Joing our IRC channel #faraday-dev in [freenode](ircs://irc.freenode.net/faraday-dev) or access directrly from this link: [![Visit our IRC channel](https://kiwiirc.com/buttons/irc.freenode.org/faraday-dev.png)](https://kiwiirc.com/client/irc.freenode.org/?nick=faraday_gi|?#faraday-dev) | |
51 |
8 | 8 | |
9 | 9 | New features in the latest update |
10 | 10 | ===================================== |
11 | ||
12 | Apr 04, 2016 | |
13 | --- | |
14 | * Added cli mode (see wiki for usage instructions) | |
15 | * Support for multiple Faraday instances in the same host | |
16 | * Fixed bug for editing web vulns in bulk | |
17 | * Fixed bug for select all in web UI | |
18 | * Fixed bugs in Qualys, ZAP, nikto, w3af, openVas plugins | |
19 | * Added some new scripts and helpers | |
20 | ||
11 | 21 | |
12 | 22 | Feb 26, 2016: |
13 | 23 | --- |
5 | 5 | |
6 | 6 | ''' |
7 | 7 | |
8 | import socket | |
8 | 9 | import threading |
9 | 10 | import logging |
10 | import requests | |
11 | import json | |
12 | 11 | import base64 |
13 | 12 | |
14 | 13 | from flask import Flask, request, jsonify |
49 | 48 | app = Flask('APISController') |
50 | 49 | |
51 | 50 | _http_server = HTTPServer(WSGIContainer(app)) |
52 | _http_server.listen(port, address=hostname) | |
51 | while True: | |
52 | try: | |
53 | _http_server.listen(port, address=hostname) | |
54 | logger.getLogger().info( | |
55 | "REST API server configured on %s" % str( | |
56 | CONF.getApiRestfulConInfo())) | |
57 | break | |
58 | except socket.error as exception: | |
59 | if exception.errno == 98: | |
60 | # Port already in use | |
61 | # Let's try the next one | |
62 | port += 1 | |
63 | if port > 65535: | |
64 | raise Exception("No ports available!") | |
65 | CONF.setApiRestfulConInfoPort(port) | |
66 | CONF.saveConfig() | |
67 | else: | |
68 | raise exception | |
53 | 69 | |
54 | 70 | routes = [r for c in _rest_controllers for r in c.getRoutes()] |
55 | 71 | |
324 | 340 | return self.ok("active plugins cleared") |
325 | 341 | |
326 | 342 | |
327 | class PluginControllerAPIClient(object): | |
328 | def __init__(self, hostname, port): | |
329 | self.hostname = hostname | |
330 | self.port = port | |
331 | self.url_input = "http://%s:%d/cmd/input" % (self.hostname, self.port) | |
332 | self.url_output = "http://%s:%d/cmd/output" % (self.hostname, self.port) | |
333 | self.url_active_plugins = "http://%s:%d/cmd/active-plugins" % (self.hostname, self.port) | |
334 | self.headers = {'Content-type': 'application/json', 'Accept': 'application/json'} | |
335 | ||
336 | def send_cmd(self, cmd): | |
337 | data = {"cmd": cmd} | |
338 | new_cmd = cmd | |
339 | output_file = None | |
340 | try: | |
341 | response = requests.post(self.url_input, | |
342 | data=json.dumps(data), | |
343 | headers=self.headers) | |
344 | ||
345 | if response.status_code == 200: | |
346 | json_response = response.json() | |
347 | if "cmd" in json_response.keys(): | |
348 | if json_response.get("cmd") is not None: | |
349 | new_cmd = json_response.get("cmd") | |
350 | if "custom_output_file" in json_response.keys(): | |
351 | output_file = json_response.get("custom_output_file") | |
352 | except: | |
353 | new_cmd = cmd | |
354 | finally: | |
355 | return new_cmd, output_file | |
356 | ||
357 | def send_output(self, cmd, output_file): | |
358 | output_file = open(output_file) | |
359 | output = base64.b64encode(output_file.read()) | |
360 | data = {"cmd": cmd, "output": output} | |
361 | response = requests.post(self.url_output, | |
362 | data=json.dumps(data), | |
363 | headers=self.headers) | |
364 | if response.status_code != 200: | |
365 | return False | |
366 | return True | |
367 | ||
368 | ||
369 | 343 | class Route(object): |
370 | 344 | """ Route class, abstracts information about: |
371 | 345 | path, handler and methods """ |
6 | 6 | ''' |
7 | 7 | import requests |
8 | 8 | import json |
9 | import base64 | |
9 | 10 | |
10 | 11 | |
11 | 12 | class RestApiClient(object): |
80 | 81 | def createCred(self, username, password, parent_id): |
81 | 82 | return self._create( |
82 | 83 | "cred", username=username, password=password, parent_id=parent_id) |
84 | ||
85 | ||
86 | class PluginControllerAPIClient(object): | |
87 | def __init__(self, hostname, port): | |
88 | self.hostname = hostname | |
89 | self.port = port | |
90 | self.url_input = "http://%s:%d/cmd/input" % (self.hostname, self.port) | |
91 | self.url_output = "http://%s:%d/cmd/output" % (self.hostname, self.port) | |
92 | self.url_active_plugins = "http://%s:%d/cmd/active-plugins" % (self.hostname, self.port) | |
93 | self.headers = {'Content-type': 'application/json', 'Accept': 'application/json'} | |
94 | ||
95 | def send_cmd(self, cmd): | |
96 | data = {"cmd": cmd} | |
97 | new_cmd = cmd | |
98 | output_file = None | |
99 | try: | |
100 | response = requests.post(self.url_input, | |
101 | data=json.dumps(data), | |
102 | headers=self.headers) | |
103 | ||
104 | if response.status_code == 200: | |
105 | json_response = response.json() | |
106 | if "cmd" in json_response.keys(): | |
107 | if json_response.get("cmd") is not None: | |
108 | new_cmd = json_response.get("cmd") | |
109 | if "custom_output_file" in json_response.keys(): | |
110 | output_file = json_response.get("custom_output_file") | |
111 | except: | |
112 | new_cmd = cmd | |
113 | finally: | |
114 | return new_cmd, output_file | |
115 | ||
116 | def send_output(self, cmd, output_file=None): | |
117 | # output_file could be None, when there is | |
118 | # no output to send | |
119 | output = "" | |
120 | if output_file: | |
121 | output_file = open(output_file) | |
122 | output = base64.b64encode(output_file.read()) | |
123 | data = {"cmd": cmd, "output": output} | |
124 | response = requests.post(self.url_output, | |
125 | data=json.dumps(data), | |
126 | headers=self.headers) | |
127 | if response.status_code != 200: | |
128 | return False | |
129 | return True |
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 | __all__ = [] |
0 | #!/usr/bin/env python | |
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 | ''' | |
7 | ||
8 | import auth.users | |
9 | ||
10 | RolesAdmited = set(["local_admin"]) | |
11 | ||
12 | ||
13 | ||
14 | class Roles(object): | |
15 | """Roles for a user are defined here""" | |
16 | def __init__(self, *args): | |
17 | self.roles = [] | |
18 | if set(args).issubset(RolesAdmited): | |
19 | self.roles.extend(args) | |
20 | ||
21 | def __iter__(self): | |
22 | return iter(self.roles) | |
23 | ||
24 | class codes: | |
25 | """ | |
26 | an enumeration with different result/status codes | |
27 | """ | |
28 | successfulLogin = 0 | |
29 | badUserOrPassword = 1 | |
30 | ||
31 | __descriptions = { | |
32 | successfulLogin :"Successful Logon", | |
33 | badUserOrPassword : "Bad username or password", | |
34 | } | |
35 | ||
36 | @staticmethod | |
37 | def getDescription(code): | |
38 | """ | |
39 | Returns the description for a given code | |
40 | """ | |
41 | return codes.__descriptions.get(code,"code does not exist") | |
42 | ||
43 | ||
44 | class SecurityManager(object): | |
45 | """ | |
46 | Handles the security and authentication in the system. | |
47 | it exposes some methods used to authenticate users and check permissions | |
48 | over different objects on the model. | |
49 | """ | |
50 | ||
51 | ||
52 | ||
53 | ||
54 | ||
55 | ||
56 | ||
57 | ||
58 | def __init__(self): | |
59 | self.current_user = None | |
60 | ||
61 | def authenticateUser(self, username, password): | |
62 | """ | |
63 | Authenticates a user. | |
64 | It returns 2 values: | |
65 | First value: | |
66 | if username and password are correct it returns a User object. | |
67 | It returns None if authentication fails | |
68 | Second value: | |
69 | returns a result code | |
70 | This code can be used later to get a description of the situation. | |
71 | To get the description use | |
72 | """ | |
73 | ||
74 | ||
75 | ||
76 | ||
77 | ||
78 | ||
79 | user = auth.users.User(username,password) | |
80 | self.current_user = user | |
81 | ||
82 | return codes.successfulLogin | |
83 | ||
84 | def getCurrentUser(self): | |
85 | return self.current_user | |
86 | ||
87 | def getUserRoles(self): | |
88 | return Roles("local_admin") | |
89 | ||
90 | def checkPermissions(self, operation): | |
91 | providers = self.getProviders(operation) | |
92 | if any( [ prov.isAllowed(securityManager = self, | |
93 | aUser = self.getCurrentUser(), | |
94 | anOperation = operation) | |
95 | for prov in providers ] ): | |
96 | return True | |
97 | raise SecurityFailException("No permission for anything") | |
98 | ||
99 | def getProviders(self, operation): | |
100 | return [prov() for prov in SecurityProvider.__subclasses__() if prov.handlesOp(operation) ] | |
101 | ||
102 | ||
103 | class SecurityProvider(object): | |
104 | def isAllowed(self, securityManager, aUser, anOperation): | |
105 | raise NotImplementedError("Should not implement abstract") | |
106 | ||
107 | class WorkspacePermisionProvider(SecurityProvider): | |
108 | handles = ["syncActiveWorkspace"] | |
109 | def __init__(self): | |
110 | self.ops_per_roles = {'syncActiveWorkspace' : Roles('pentester', 'admin').roles } | |
111 | ||
112 | @staticmethod | |
113 | def handlesOp(anOperation): | |
114 | return anOperation in WorkspacePermisionProvider.handles | |
115 | ||
116 | def isAllowed(self, securityManager, aUser, anOperation): | |
117 | """ Checks if the user has the role needed to run the operation """ | |
118 | allowd = any(map(lambda x: x in self.ops_per_roles[anOperation], securityManager.getUserRoles())) | |
119 | ||
120 | return allowd | |
121 | ||
122 | ||
123 | class SecurityFailException(Exception): | |
124 | pass |
0 | #!/usr/bin/env python | |
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 | ''' | |
7 | import os | |
8 | import sys | |
9 | import uuid | |
10 | ||
11 | ||
12 | ||
13 | class User(object): | |
14 | """ | |
15 | This represents a user on the system. | |
16 | Users are assigned to work on different workspaces and | |
17 | some permissions are configured. | |
18 | A user can be part of groups. | |
19 | """ | |
20 | def __init__(self, name, passwd = "", display_name = "", groups = [], level = 0): | |
21 | ||
22 | self.name = name | |
23 | self.__id = uuid.uuid4() | |
24 | ||
25 | self.display_name = display_name or name | |
26 | self._groups = [] | |
27 | self._level = level | |
28 | ||
29 | ||
30 | self.__password = passwd | |
31 | ||
32 | ||
33 | ||
34 | self.lastlogon = None | |
35 | ||
36 |
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 | Autor: Ezequiel Tavella | |
9 | ||
10 | This script get all CVEs of vulns in the active workspace and search | |
11 | for exploits in the vFeed database. | |
12 | Support : Exploit-db, Metasploit, Milworm, D2, Saint | |
13 | ||
14 | Thanks ToolsWatch!!! | |
15 | www.toolswatch.org | |
16 | """ | |
17 | ||
18 | import sqlite3 | |
19 | import os | |
20 | ||
21 | DB_PATH = "./data/vfeed.db" | |
22 | URL_DB = "http://www.toolswatch.org/vfeed/vfeed.db.tgz" | |
23 | ||
24 | def getExploits(cve_id, cursor): | |
25 | ||
26 | result = { | |
27 | 'exploit-db' : [], | |
28 | 'metasploit' : [], | |
29 | 'milworm' : [], | |
30 | 'd2' : [], | |
31 | 'saint' : [] | |
32 | } | |
33 | ||
34 | value = (cve_id.upper(), ) | |
35 | ||
36 | #D2 exploits | |
37 | consult = cursor.execute( | |
38 | "SELECT d2_script_file FROM map_cve_d2 WHERE cveid = ?", | |
39 | value | |
40 | ) | |
41 | ||
42 | for row in consult: | |
43 | for i in row: | |
44 | result['d2'].append(i) | |
45 | ||
46 | #Exploit-db exploits | |
47 | consult = cursor.execute( | |
48 | "SELECT exploitdbscript FROM map_cve_exploitdb WHERE cveid = ?", | |
49 | value | |
50 | ) | |
51 | ||
52 | for row in consult: | |
53 | for i in row: | |
54 | result['exploit-db'].append(i) | |
55 | ||
56 | #Metasploit exploits | |
57 | consult = cursor.execute( | |
58 | "SELECT msf_script_file FROM map_cve_msf WHERE cveid = ?", | |
59 | value | |
60 | ) | |
61 | ||
62 | for row in consult: | |
63 | for i in row: | |
64 | result['metasploit'].append(i) | |
65 | ||
66 | #Milworm exploits | |
67 | consult = cursor.execute( | |
68 | "SELECT milw0rmid FROM map_cve_milw0rm WHERE cveid = ?", | |
69 | value | |
70 | ) | |
71 | ||
72 | for row in consult: | |
73 | for i in row: | |
74 | result['milworm'].append(i) | |
75 | ||
76 | #Saint exploits | |
77 | consult = cursor.execute( | |
78 | "SELECT saintexploitlink FROM map_cve_saint WHERE cveid = ?", | |
79 | value | |
80 | ) | |
81 | ||
82 | for row in consult: | |
83 | for i in row: | |
84 | result['saint'].append(i) | |
85 | ||
86 | return result | |
87 | ||
88 | def printExploits(vuln, references, cursor): | |
89 | ||
90 | global getExploits | |
91 | ||
92 | for ref in references: | |
93 | if ref.startswith('CVE') or ref.startswith('cve'): | |
94 | ||
95 | ret = getExploits(ref, cursor) | |
96 | if ret : | |
97 | print '[Exploits ' + vuln + ' ' + ref + ']\n' | |
98 | ||
99 | for tool, info in ret.iteritems(): | |
100 | ||
101 | if not info: | |
102 | continue | |
103 | print '[Tool] ' + tool | |
104 | ||
105 | for path in info: | |
106 | print path | |
107 | print '\n' | |
108 | ||
109 | ||
110 | ||
111 | print '\n[*]Checking DB...' | |
112 | ||
113 | if not os.path.isfile(DB_PATH): | |
114 | ||
115 | print '[!]DB not found: please download the DB from: ' + URL_DB | |
116 | print '[!]Extract this to $FARADAY/data/ and try again!' | |
117 | raise('DB not found','Check if DB exists') | |
118 | ||
119 | print '[*]DB Found!' | |
120 | print '[*]Searching exploits...\n' | |
121 | ||
122 | connection = sqlite3.connect(DB_PATH) | |
123 | cursor = connection.cursor() | |
124 | ||
125 | for host in api.__model_controller.getAllHosts(): | |
126 | for v in host.getVulns(): | |
127 | ||
128 | print '[' + host.name + '] ' + v._name | |
129 | printExploits(v._name, v.getRefs(), cursor) | |
130 | ||
131 | for i in host.getAllInterfaces(): | |
132 | for s in i.getAllServices(): | |
133 | for v in s.getVulns(): | |
134 | ||
135 | print '[' + host.name + '] ' + v._name | |
136 | printExploits(v._name, v.getRefs(), cursor) |
0 | #!/usr/bin/env python | |
1 | # -*- coding: utf-8 -*- | |
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 os | |
8 | ||
9 | def getCweData(): | |
10 | ||
11 | import requests | |
12 | import json | |
13 | ||
14 | #Get elements from cwe DB in couchdb | |
15 | headers = {'Content-Type': 'application/json'} | |
16 | ||
17 | payload = { | |
18 | 'map' : | |
19 | 'function(doc) { if(doc.severity && doc.name){' | |
20 | 'emit(doc.name, doc.severity); }}' | |
21 | } | |
22 | ||
23 | r = requests.post( | |
24 | couchdb + '/cwe/_temp_view', | |
25 | headers = headers, | |
26 | data = json.dumps(payload) | |
27 | ) | |
28 | ||
29 | response_code = r.status_code | |
30 | ||
31 | if response_code == 200: | |
32 | ||
33 | data = r.json()['rows'] | |
34 | dict = {} | |
35 | ||
36 | for item in data: | |
37 | ||
38 | value = item['value'] | |
39 | if value == 'informational': | |
40 | value = 'info' | |
41 | ||
42 | dict.update( {item['key'] : value} ) | |
43 | ||
44 | if dict == {}: | |
45 | return None | |
46 | else: | |
47 | print 'Get CWE data: OK\n' | |
48 | return dict | |
49 | ||
50 | elif response_code == 401: | |
51 | print 'Autorization required, make sure to add user:pwd to Couch URI' | |
52 | else: | |
53 | print 'Error couchDB: ' + str(response_code) + str(r.text) | |
54 | ||
55 | ||
56 | def checkSeverity(vuln, cwe_dict, severity_choose, workspace): | |
57 | ||
58 | import requests | |
59 | import json | |
60 | ||
61 | severity_dict = { | |
62 | 'unclassified' : 0, | |
63 | 'info' : 1, | |
64 | 'low' : 2, | |
65 | 'med' : 3, | |
66 | 'high' : 4, | |
67 | 'critical' : 5, | |
68 | 'all' : 100 | |
69 | } | |
70 | ||
71 | if vuln._name in cwe_dict and severity_dict[vuln.severity] <= severity_dict[severity_choose] : | |
72 | ||
73 | print 'Change: ' + vuln._name + ' to ' + cwe_dict[vuln._name] | |
74 | ||
75 | #Get object Vuln | |
76 | response = requests.get( | |
77 | couchdb + '/' + workspace + '/' + str (vuln._id) | |
78 | ) | |
79 | vulnWeb = response.json() | |
80 | ||
81 | #Change severity | |
82 | vulnWeb['severity'] = cwe_dict[vuln._name] | |
83 | ||
84 | #Put changes... | |
85 | headers = {'Content-Type': 'application/json'} | |
86 | update = requests.put( | |
87 | couchdb + '/' + workspace + '/' + vuln._id, | |
88 | headers = headers, | |
89 | data = json.dumps(vulnWeb) | |
90 | ) | |
91 | ||
92 | if update.status_code == 200 or update.status_code == 201: | |
93 | print 'Change OK\n' | |
94 | else: | |
95 | print 'Error in update Vulnerability, status code: ' + str(update.status_code) | |
96 | print update.text | |
97 | ||
98 | help = ( | |
99 | '\nGet Vulns filtered by Severity and change Severity based in CWE\n' | |
100 | 'Parameters:\n' | |
101 | '1º : Filter by Severity (<=) (unclassified, info, low, med, high, critical, all)\n' | |
102 | '2º : Url to Couchdb\n' | |
103 | '3º : Workspace name\n' | |
104 | 'Try help for this description.\n' | |
105 | 'Example:' | |
106 | './fplugin.py -f getSeverityByCwe.py -p high ' | |
107 | 'http://username:password@localhost:5984/ workspace_test_name' | |
108 | 'Note: In this case, change vulns with severity high, med, low, info and unclassified' | |
109 | ) | |
110 | ||
111 | if script_parameters == 'help' or script_parameters == None or script_parameters == '' : | |
112 | print help | |
113 | raise(Exception('Exit for help')) | |
114 | ||
115 | # Main | |
116 | list_parameters = script_parameters.split(' ') | |
117 | ||
118 | #default value from ENV COUCHDB | |
119 | global couchdb | |
120 | couchdb = os.environ.get('COUCHDB') | |
121 | ||
122 | if not couchdb and list_parameters[1]: | |
123 | couchdb = list_parameters[1] | |
124 | ||
125 | #Default workspace | |
126 | workspace = 'untitled' | |
127 | if list_parameters[2]: | |
128 | workspace = list_parameters[2] | |
129 | ||
130 | #Default severity | |
131 | severity = 'info' | |
132 | if list_parameters[0]: | |
133 | severity = list_parameters[0] | |
134 | ||
135 | ||
136 | cwe = getCweData() | |
137 | ||
138 | if cwe is None: | |
139 | print 'CWE DB not downloaded....EXIT' | |
140 | raise(Exception('Exit CWE DB not found')) | |
141 | ||
142 | for host in api.__model_controller.getAllHosts(): | |
143 | for v in host.getVulns(): | |
144 | checkSeverity(v, cwe, severity, workspace) | |
145 | ||
146 | for i in host.getAllInterfaces(): | |
147 | for s in i.getAllServices(): | |
148 | for v in s.getVulns(): | |
149 | checkSeverity(v, cwe, severity, workspace) |
156 | 156 | self._tkt_api_params = self._getValue(tree, CONST_TKTAPIPARAMS,default ="{}") |
157 | 157 | self._tkt_template = self._getValue(tree, CONST_TKTTEMPLATE,default ="{}") |
158 | 158 | |
159 | ||
159 | self._merge_strategy = None | |
160 | 160 | |
161 | 161 | def getApiConInfo(self): |
162 | 162 | if str(self._api_con_info_host) == "None" or str(self._api_con_info_port) == "None": |
172 | 172 | return self._api_con_info_host |
173 | 173 | |
174 | 174 | def getApiConInfoPort(self): |
175 | return self._api_con_info_port | |
175 | if str(self._api_con_info_port) == "None": | |
176 | return None | |
177 | return int(self._api_con_info_port) | |
176 | 178 | |
177 | 179 | def getApiRestfulConInfoPort(self): |
178 | return self._api_restful_con_info_port | |
180 | if str(self._api_restful_con_info_port) == "None": | |
181 | return None | |
182 | return int(self._api_restful_con_info_port) | |
179 | 183 | |
180 | 184 | def getAppname(self): |
181 | 185 | return self._appname |
279 | 283 | def getTktTemplate(self): |
280 | 284 | return self._tkt_template |
281 | 285 | |
286 | def getMergeStrategy(self): | |
287 | return self._merge_strategy | |
288 | ||
282 | 289 | def setLastWorkspace(self, workspaceName): |
283 | 290 | self._last_workspace = workspaceName |
284 | 291 | |
384 | 391 | |
385 | 392 | def setPluginSettings(self, settings): |
386 | 393 | self._plugin_settings = settings |
394 | ||
395 | def setMergeStrategy(self, strategy): | |
396 | self._merge_strategy = strategy | |
387 | 397 | |
388 | 398 | def indent(self, elem, level=0): |
389 | 399 | """ Indents the tree to make a pretty view of it. """ |
411 | 421 | tree = self._getTree() |
412 | 422 | |
413 | 423 | API_CON_INFO_HOST = Element(CONST_API_CON_INFO_HOST) |
414 | API_CON_INFO_HOST.text = self._getValue(tree, CONST_API_CON_INFO_HOST) | |
415 | # API_CON_INFO_HOST.text = self.getApiConInfoHost() | |
424 | #API_CON_INFO_HOST.text = self._getValue(tree, CONST_API_CON_INFO_HOST) | |
425 | API_CON_INFO_HOST.text = self.getApiConInfoHost() | |
416 | 426 | ROOT.append(API_CON_INFO_HOST) |
417 | 427 | |
418 | 428 | API_CON_INFO_PORT = Element(CONST_API_CON_INFO_PORT) |
419 | API_CON_INFO_PORT.text = self._getValue(tree, CONST_API_CON_INFO_PORT) | |
420 | # API_CON_INFO_PORT.text = str(self.getApiConInfoPort()) | |
429 | #API_CON_INFO_PORT.text = self._getValue(tree, CONST_API_CON_INFO_PORT) | |
430 | API_CON_INFO_PORT.text = str(self.getApiConInfoPort()) | |
421 | 431 | ROOT.append(API_CON_INFO_PORT) |
422 | 432 | |
423 | 433 | API_RESTFUL_CON_INFO_PORT = Element(CONST_API_RESTFUL_CON_INFO_PORT) |
424 | API_RESTFUL_CON_INFO_PORT.text = self._getValue(tree, CONST_API_RESTFUL_CON_INFO_PORT) | |
425 | # API_RESTFUL_CON_INFO_PORT.text = str(self.getApiRestfulConInfoPort()) | |
434 | #API_RESTFUL_CON_INFO_PORT.text = self._getValue(tree, CONST_API_RESTFUL_CON_INFO_PORT) | |
435 | API_RESTFUL_CON_INFO_PORT.text = str(self.getApiRestfulConInfoPort()) | |
426 | 436 | ROOT.append(API_RESTFUL_CON_INFO_PORT) |
427 | 437 | |
428 | 438 | APPNAME = Element(CONST_APPNAME) |
1 | 1 | <faraday> |
2 | 2 | |
3 | 3 | <appname>Faraday - Penetration Test IDE</appname> |
4 | <version>1.0.17</version> | |
4 | <version>1.0.18</version> | |
5 | 5 | <debug_status>0</debug_status> |
6 | 6 | <font>-Misc-Fixed-medium-r-normal-*-12-100-100-100-c-70-iso8859-1</font> |
7 | 7 | <home_path>~/</home_path> |
80 | 80 | |
81 | 81 | parser_connection = parser.add_argument_group('connection') |
82 | 82 | parser_profile = parser.add_argument_group('profiling') |
83 | parser_gui_ex = parser.add_mutually_exclusive_group() | |
84 | 83 | |
85 | 84 | parser_connection.add_argument('-n', '--hostname', action="store", |
86 | 85 | dest="host", |
139 | 138 | default=None, |
140 | 139 | help="Path to the valid CouchDB certificate") |
141 | 140 | |
142 | parser_gui_ex.add_argument('--gui', action="store", dest="gui", | |
141 | parser.add_argument('--gui', action="store", dest="gui", | |
143 | 142 | default="qt3", |
144 | 143 | help="Select interface to start faraday. Default = qt3") |
145 | 144 | |
146 | parser_gui_ex.add_argument('--cli', '--console', action="store_true", | |
145 | parser.add_argument('--cli', action="store_true", | |
147 | 146 | dest="cli", |
148 | 147 | default=False, |
149 | 148 | help="Set this flag to avoid gui and use faraday as a cli.") |
149 | ||
150 | parser.add_argument( | |
151 | '-w', '--workspace', action="store", | |
152 | dest="workspace", | |
153 | default=None, | |
154 | help="Workspace to be opened") | |
155 | ||
156 | parser.add_argument( | |
157 | '-r', '--report', action="store", | |
158 | dest="filename", | |
159 | default=None, | |
160 | help="Report to be parsed by the cli") | |
150 | 161 | |
151 | 162 | #args = parser.parse_args(['@parser_args.cfg']) |
152 | 163 | return parser.parse_args() |
269 | 280 | host = args.host if args.host else host |
270 | 281 | port_xmlrpc = args.port_xmlrpc if args.port_xmlrpc else port_xmlrpc |
271 | 282 | port_rest = args.port_rest if args.port_rest else port_rest |
272 | ||
273 | logger.info("XMLRPC API Server listening on %s:%s" % (host, port_xmlrpc)) | |
274 | logger.info("RESTful API Server listening on %s:%s" % (host, port_rest)) | |
275 | 283 | |
276 | 284 | CONF.setApiConInfoHost(host) |
277 | 285 | CONF.setApiConInfoPort(port_xmlrpc) |
4 | 4 | See the file 'doc/LICENSE' for the license information |
5 | 5 | |
6 | 6 | ''' |
7 | ||
8 | from managers.reports_managers import ReportManager | |
9 | ||
10 | from config.configuration import getInstanceConfiguration | |
11 | CONF = getInstanceConfiguration() | |
7 | 12 | |
8 | 13 | |
9 | 14 | class UiFactory(object): |
22 | 27 | class FaradayUi(object): |
23 | 28 | def __init__(self, model_controller=None, plugin_manager=None, |
24 | 29 | workspace_manager=None, gui="qt3"): |
25 | #self.main_app = main_app | |
26 | 30 | self.model_controller = model_controller |
27 | 31 | self.plugin_manager = plugin_manager |
28 | 32 | self.workspace_manager = workspace_manager |
33 | self.report_manager = None | |
29 | 34 | |
30 | 35 | def getModelController(self): |
31 | 36 | return self.model_controller |
62 | 67 | |
63 | 68 | def createLoggerWidget(self): |
64 | 69 | pass |
70 | ||
71 | def openWorkspace(self, name): | |
72 | # The method openWorkspace can return a workspace or | |
73 | # raise en Exception. For now, just raise it to the caller | |
74 | if self.report_manager: | |
75 | self.report_manager.stop() | |
76 | self.report_manager.join() | |
77 | try: | |
78 | ws = self.getWorkspaceManager().openWorkspace(name) | |
79 | self.report_manager = ReportManager( | |
80 | 10, name) | |
81 | self.report_manager.start() | |
82 | except Exception as e: | |
83 | raise e | |
84 | return ws | |
85 | ||
86 | def openDefaultWorkspace(self): | |
87 | """ | |
88 | Opens the default workspace (called 'untitled'). | |
89 | This method shouldn't fail, since the default workspace | |
90 | should be always available | |
91 | ||
92 | Returns the default workspace | |
93 | """ | |
94 | if self.report_manager: | |
95 | self.report_manager.stop() | |
96 | self.report_manager.join() | |
97 | ws = self.getWorkspaceManager().openDefaultWorkspace() | |
98 | self.report_manager = ReportManager( | |
99 | 10, ws.name) | |
100 | self.report_manager.start() | |
101 | return ws |
10 | 10 | from gui.gui_app import FaradayUi |
11 | 11 | from gui.nogui.eventwatcher import EventWatcher |
12 | 12 | import model.guiapi |
13 | from utils.logs import getLogger | |
14 | ||
15 | from config.configuration import getInstanceConfiguration | |
16 | CONF = getInstanceConfiguration() | |
13 | 17 | |
14 | 18 | |
15 | 19 | class GuiApp(FaradayUi): |
24 | 28 | model.guiapi.notification_center.registerWidget(self.event_watcher) |
25 | 29 | |
26 | 30 | def run(self, args): |
27 | ||
31 | workspace = args.workspace | |
32 | try: | |
33 | ws = super(GuiApp, self).openWorkspace(workspace) | |
34 | except Exception as e: | |
35 | getLogger(self).error( | |
36 | ("Your last workspace %s is not accessible, " | |
37 | "check configuration") % workspace) | |
38 | getLogger(self).error(str(e)) | |
39 | ws = self.openDefaultWorkspace() | |
40 | workspace = ws.name | |
41 | CONF.setLastWorkspace(workspace) | |
42 | CONF.saveConfig() | |
43 | getLogger(self).info("Workspace %s loaded" % workspace) | |
28 | 44 | while True: |
29 | 45 | if self._stop: |
30 | 46 | return |
24 | 24 | import model.api |
25 | 25 | import model.log |
26 | 26 | from utils.logs import addHandler |
27 | from utils.logs import getLogger | |
27 | 28 | |
28 | 29 | from config.configuration import getInstanceConfiguration |
29 | 30 | CONF = getInstanceConfiguration() |
58 | 59 | qt.QPixmap(os.path.join(CONF.getImagePath(), "splash2.png")), |
59 | 60 | qt.Qt.WStyle_StaysOnTop) |
60 | 61 | |
62 | self.startSplashScreen() | |
63 | ||
61 | 64 | def getMainWindow(self): |
62 | 65 | return self._main_window |
63 | 66 | |
64 | 67 | def run(self, args): |
68 | workspace = args.workspace | |
69 | try: | |
70 | ws = super(GuiApp, self).openWorkspace(workspace) | |
71 | except Exception as e: | |
72 | getLogger(self).error( | |
73 | ("Your last workspace %s is not accessible, " | |
74 | "check configuration") % workspace) | |
75 | getLogger(self).error(str(e)) | |
76 | ws = self.openDefaultWorkspace() | |
77 | workspace = ws.name | |
78 | CONF.setLastWorkspace(workspace) | |
79 | CONF.saveConfig() | |
80 | ||
81 | self.loadWorkspaces() | |
65 | 82 | self.createLoggerWidget() |
83 | self.stopSplashScreen() | |
66 | 84 | self._main_window.showAll() |
67 | 85 | couchURL = CONF.getCouchURI() |
68 | 86 | if couchURL: |
74 | 92 | model.api.log("Please configure Couchdb for fancy HTML5 Dashboard (https://github.com/infobyte/faraday/wiki/Couchdb)") |
75 | 93 | exit_code = self.exec_loop() |
76 | 94 | return exit_code |
95 | ||
96 | def openWorkspace(self, name): | |
97 | try: | |
98 | ws = super(GuiApp, self).openWorkspace(name) | |
99 | except Exception as e: | |
100 | model.guiapi.notification_center.showDialog(str(e)) | |
101 | ws = self.openDefaultWorkspace() | |
102 | workspace = ws.name | |
103 | CONF.setLastWorkspace(workspace) | |
104 | CONF.saveConfig() | |
105 | return ws | |
77 | 106 | |
78 | 107 | def createLoggerWidget(self): |
79 | 108 | self.loghandler.registerGUIOutput(self._main_window.getLogConsole()) |
168 | 197 | model.api.devlog("Looking for the delegation class") |
169 | 198 | manager = self.getWorkspaceManager() |
170 | 199 | |
171 | w = manager.createWorkspace(name, description, | |
172 | manager.namedTypeToDbType(w_type)) | |
200 | try: | |
201 | w = manager.createWorkspace( | |
202 | name, description, | |
203 | manager.namedTypeToDbType(w_type)) | |
204 | CONF.setLastWorkspace(w.name) | |
205 | CONF.saveConfig() | |
206 | except Exception as e: | |
207 | model.guiapi.notification_center.showDialog(str(e)) | |
173 | 208 | |
174 | 209 | self.getMainWindow().refreshWorkspaceTreeView() |
175 | 210 | |
176 | 211 | self.getMainWindow().getWorkspaceTreeView().loadAllWorkspaces() |
177 | ||
178 | def openWorkspace(self, name): | |
179 | try: | |
180 | self.getWorkspaceManager().openWorkspace(name) | |
181 | except Exception: | |
182 | model.api.log("An exception was captured while opening \ | |
183 | workspace %s\n%s" % (name, traceback.format_exc()), "ERROR") |
11 | 11 | import model.guiapi as guiapi |
12 | 12 | import re |
13 | 13 | import model.hosts as hosts |
14 | from managers.model_managers import WorkspaceManager | |
15 | 14 | from ui.plugin_settings import * |
16 | 15 | from ui.vulnerabilities import * |
17 | 16 | from ui.preferences import * |
605 | 604 | if url: self._repourl_edit.setText(url) |
606 | 605 | self.layout.addWidget(hbox1) |
607 | 606 | |
608 | hbox2 = qt.QHBox(self) | |
609 | hbox2.setSpacing(5) | |
610 | self._replicate_label = qt.QLabel("Replication enabled", hbox2) | |
611 | self._replicate_edit = qt.QCheckBox(hbox2) | |
612 | self._replicate_edit.setChecked(replication) | |
613 | ||
614 | self.layout.addWidget(hbox2) | |
615 | ||
616 | hbox3 = qt.QHBox(self) | |
617 | hbox3.setSpacing(10) | |
618 | self._replics_label = qt.QLabel("Replics", hbox3) | |
619 | self.__replics_edit = qt.QLineEdit(hbox3) | |
620 | if replics: self.__replics_edit.setText(replics) | |
621 | self.layout.addWidget(hbox3) | |
622 | ||
623 | 607 | self.__repourl_txt = self._repourl_edit.text() |
624 | self.__is_replicated_bool = self._replicate_edit.isChecked() | |
625 | self.__replics_list_txt = self.__replics_edit.text() | |
626 | 608 | |
627 | 609 | |
628 | 610 | self.setupButtons({ "ok" : self.ok_pressed, |
631 | 613 | |
632 | 614 | def getData(self): |
633 | 615 | self.__repourl_txt = self._repourl_edit.text() |
634 | self.__is_replicated_bool = self._replicate_edit.isChecked() | |
635 | self.__replics_list_txt = self.__replics_edit.text() | |
636 | return (self.__repourl_txt.latin1(), | |
637 | self.__is_replicated_bool, | |
638 | self.__replics_list_txt.latin1()) | |
616 | return (self.__repourl_txt.latin1()) | |
639 | 617 | |
640 | 618 | def ok_pressed(self): |
641 | 619 | if self._callback is not None: |
1092 | 1070 | params = self._plugin_settings[plugin_id] |
1093 | 1071 | |
1094 | 1072 | self.le_name.setText(params["name"]) |
1095 | self.le_version.setText(params["version"]) | |
1096 | self.le_pversion.setText(params["plugin_version"]) | |
1073 | self.le_version.setText( | |
1074 | params.get("version") if params.get("version") else "") | |
1075 | self.le_pversion.setText( | |
1076 | params.get("plugin_version") if params.get("plugin_version") else "") | |
1097 | 1077 | |
1098 | 1078 | for setting, value in params["settings"].iteritems(): |
1099 | 1079 | index = self.t_parameters.numRows() |
409 | 409 | shell.myemit('clearSelectionSignal') |
410 | 410 | qt.QApplication.clipboard().setSelectionMode(False) |
411 | 411 | |
412 | def _importWorkspace(self): | |
413 | model.api.showPopup("Be careful that importing could overwrite existing files", level="Warning") | |
414 | wm = self._main_app.getWorkspaceManager() | |
415 | mwin = self._main_app.getMainWindow() | |
416 | ||
417 | filename = QFileDialog.getOpenFileName( | |
418 | "$HOME/.faraday", | |
419 | "Faraday export file (*.faraday)", | |
420 | None, | |
421 | "import file dialog", | |
422 | "Choose a file to import" ); | |
423 | if filename and filename is not None: | |
424 | model.api.log("Import function %s/ %s" % (CONF.getPersistencePath(),filename)) | |
425 | ||
426 | ||
427 | ||
428 | api.importWorskpace("%s/" % CONF.getPersistencePath(), filename) | |
429 | ||
430 | wm.loadWorkspaces() | |
431 | w = wm.getActiveWorkspace() | |
432 | wm.setActiveWorkspace(w) | |
433 | ||
434 | ||
435 | mwin.getWorkspaceTreeView().loadAllWorkspaces() | |
436 | ||
437 | def _exportWorkspace(self): | |
438 | filename = QFileDialog.getSaveFileName( | |
439 | "/tmp", | |
440 | "Faraday export file (*.faraday)", | |
441 | None, | |
442 | "save file dialog", | |
443 | "Choose a file to save the export" ); | |
444 | if filename and filename is not None: | |
445 | model.api.log("Export function %s" % filename) | |
446 | api.exportWorskpace("%s/" % CONF.getPersistencePath(), "%s.faraday" % filename) | |
447 | ||
448 | ||
449 | 412 | def getTabManager(self): |
450 | 413 | return self._tab_manager |
451 | 414 | |
519 | 482 | callback=None) |
520 | 483 | result = repoconfig_dialog.exec_loop() |
521 | 484 | if result == qt.QDialog.Accepted: |
522 | repourl, isReplicated, replics = repoconfig_dialog.getData() | |
485 | repourl = repoconfig_dialog.getData() | |
523 | 486 | api.devlog("repourl = %s" % repourl) |
524 | 487 | wm = self._main_app.getWorkspaceManager() |
525 | 488 | if not CouchDbManager.testCouch(repourl): |
539 | 502 | return |
540 | 503 | |
541 | 504 | CONF.setCouchUri(repourl) |
542 | CONF.setCouchIsReplicated(isReplicated) | |
543 | CONF.setCouchReplics(replics) | |
544 | 505 | CONF.saveConfig() |
545 | 506 | |
546 | 507 | wm.closeWorkspace() |
0 | #!/usr/bin/env python2.7 | |
1 | ||
2 | ''' | |
3 | Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/) | |
4 | Author: Ezequiel Tavella | |
5 | ||
6 | This script generate a CSV file with information about the cfdb database. | |
7 | CSV Format: | |
8 | cwe,name,desc_summary,description,resolution,exploitation,references | |
9 | ''' | |
10 | ||
11 | from subprocess import call | |
12 | from os import walk | |
13 | import csv | |
14 | ||
15 | URL_PROYECT = 'https://github.com/mubix/cfdb' | |
16 | DB_PATH = './cfdb/' | |
17 | ||
18 | class parseFile(): | |
19 | ||
20 | def __init__(self, file_md): | |
21 | ||
22 | self.cwe = '' | |
23 | self.name = None | |
24 | self.desc_summary = None | |
25 | self.description = None | |
26 | self.resolution = None | |
27 | self.explotation = None | |
28 | self.references = None | |
29 | ||
30 | self.file = file_md | |
31 | self.parse() | |
32 | ||
33 | def getContent(self): | |
34 | ||
35 | result = [] | |
36 | while True: | |
37 | ||
38 | subLine = self.file.readline().strip('\n\r') | |
39 | if subLine != '\n': | |
40 | #If EOF -> break | |
41 | if subLine == '' : | |
42 | break | |
43 | ||
44 | if not subLine.startswith('##') : | |
45 | result.append(subLine) | |
46 | else: | |
47 | break | |
48 | ||
49 | return ''.join(result) | |
50 | ||
51 | def parse(self): | |
52 | ||
53 | line = self.file.readline() | |
54 | while line != '': | |
55 | ||
56 | title = line.startswith('Title: ') | |
57 | summary = line.startswith('Description: ') | |
58 | description = line.startswith('## Summary') | |
59 | resolution = line.startswith('## Remediation') | |
60 | references = line.startswith('## References') | |
61 | explotation = line.startswith('## Exploitation') | |
62 | ||
63 | #Slice title and summary... read line and continue with other line | |
64 | if title: | |
65 | ||
66 | self.name = line[title + 6:].strip('\n\r') | |
67 | line = self.file.readline() | |
68 | continue | |
69 | ||
70 | elif summary: | |
71 | self.desc_summary = line[title + 12:].strip('\n\r') | |
72 | line = self.file.readline() | |
73 | continue | |
74 | ||
75 | #Read first line with \n and read the content | |
76 | elif description: | |
77 | line = self.file.readline() | |
78 | self.description = self.getContent() | |
79 | elif resolution: | |
80 | line = self.file.readline() | |
81 | self.resolution = self.getContent() | |
82 | elif references: | |
83 | line = self.file.readline() | |
84 | self.references = self.getContent() | |
85 | elif explotation: | |
86 | line = self.file.readline() | |
87 | self.explotation = self.getContent() | |
88 | #Nothing here...read line | |
89 | else: | |
90 | line = self.file.readline() | |
91 | ||
92 | ||
93 | def main(): | |
94 | ||
95 | #Get DB cfdb | |
96 | print '[*]Execute git clone...' | |
97 | return_code = call(['git', 'clone', URL_PROYECT]) | |
98 | ||
99 | if return_code != 0 and return_code != 128: | |
100 | print '[!]Error:\n Git return code: ' + str(return_code) | |
101 | ||
102 | file_csv = open('cfdb.csv','w') | |
103 | ||
104 | file_csv.write( | |
105 | 'cwe,name,desc_summary,description,resolution,exploitation,references\n' | |
106 | ) | |
107 | ||
108 | #CSV Writer | |
109 | writer = csv.writer( | |
110 | file_csv, | |
111 | quotechar = '"', | |
112 | delimiter = ',', | |
113 | quoting = csv.QUOTE_ALL | |
114 | ) | |
115 | ||
116 | #Get DB names... | |
117 | print '[*]Looking for DBs...' | |
118 | for (root, dirs, files) in walk(DB_PATH): | |
119 | ||
120 | #Jump dirs without info | |
121 | if root.find('.git') < 0 and root.find('.gitignore') < 0: | |
122 | if root != './cfdb/': | |
123 | ||
124 | print '[*]Parsing folder: ' + root | |
125 | for file_db in files: | |
126 | ||
127 | print '[_]File: ' + root + '/' + file_db | |
128 | with open(root + '/' + file_db, 'r') as file_md: | |
129 | ||
130 | csv_content = parseFile(file_md) | |
131 | ||
132 | result = ( | |
133 | csv_content.cwe, | |
134 | csv_content.name, | |
135 | csv_content.desc_summary, | |
136 | csv_content.description, | |
137 | csv_content.resolution, | |
138 | csv_content.explotation, | |
139 | csv_content.references | |
140 | ) | |
141 | ||
142 | writer.writerow(result) | |
143 | ||
144 | print '[*]Parse folder finished...\n' | |
145 | ||
146 | print '[*]All Finished... OK' | |
147 | ||
148 | file_csv.close() | |
149 | ||
150 | if __name__ == '__main__': | |
151 | main() |
0 | #!/usr/bin/env python2.7 | |
1 | ||
2 | ''' | |
3 | Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/) | |
4 | Author: Ezequiel Tavella | |
5 | ''' | |
6 | ||
7 | ''' | |
8 | This script generate a CSV file with information about the vulndb database. | |
9 | CSV Format: | |
10 | cwe,name,desc_summary,description,resolution,exploitation,references | |
11 | ||
12 | ''' | |
13 | from subprocess import call | |
14 | from os import walk | |
15 | import json | |
16 | import csv | |
17 | ||
18 | URL_PROYECT = 'https://github.com/vulndb/data' | |
19 | DB_PATH = './data/db/' | |
20 | ||
21 | ||
22 | class JsonToCsv(): | |
23 | ||
24 | def __init__(self, file): | |
25 | ||
26 | self.cwe = None | |
27 | self.name = None | |
28 | self.description = None | |
29 | self.resolution = None | |
30 | self.references = None | |
31 | self.severity = None | |
32 | ||
33 | self.content = self.getContent(file) | |
34 | self.parse() | |
35 | ||
36 | def getContent(self, file): | |
37 | ||
38 | try: | |
39 | return json.load(file) | |
40 | except: | |
41 | return None | |
42 | ||
43 | def parse(self): | |
44 | ||
45 | """ | |
46 | Available information of vulndb: | |
47 | cwe,name,description,resolution,references | |
48 | """ | |
49 | ||
50 | if not self.content: | |
51 | return | |
52 | ||
53 | self.cwe = self.content.get('cwe') | |
54 | if self.cwe: | |
55 | self.cwe = self.cwe[0] | |
56 | ||
57 | self.name = self.content.get('title') | |
58 | self.severity = self.content.get('severity') | |
59 | ||
60 | self.description = ''.join(self.content.get('description')) | |
61 | self.resolution = ''.join(self.content.get('fix').get('guidance')) | |
62 | ||
63 | try: | |
64 | self.references = [] | |
65 | for reference in self.content.get('references'): | |
66 | ||
67 | self.references.append( | |
68 | reference['title'] + ': ' + reference['url'] | |
69 | ) | |
70 | ||
71 | except: | |
72 | self.references = [] | |
73 | ||
74 | ||
75 | def main(): | |
76 | ||
77 | #Get DB of vulndb | |
78 | print '[*]Execute git clone...' | |
79 | return_code = call(['git', 'clone', URL_PROYECT]) | |
80 | ||
81 | if return_code != 0 and return_code != 128: | |
82 | print '[!]Error:\n Git return code: ' + str(return_code) | |
83 | ||
84 | #Get DB names... | |
85 | print '[*]Looking for DBs...' | |
86 | for (root, dirs, files) in walk(DB_PATH): | |
87 | ||
88 | file_csv = open('vulndb.csv','w') | |
89 | ||
90 | file_csv.write( | |
91 | 'cwe,name,desc_summary,description,resolution,exploitation,references,severity\n' | |
92 | ) | |
93 | ||
94 | writer = csv.writer( | |
95 | file_csv, | |
96 | quotechar = '"', | |
97 | delimiter = ',', | |
98 | quoting = csv.QUOTE_ALL | |
99 | ) | |
100 | ||
101 | for file_db in files: | |
102 | ||
103 | print '[*]Parsing ' + file_db | |
104 | with open(root + file_db, 'r') as file_object: | |
105 | ||
106 | csv_content = JsonToCsv(file_object) | |
107 | ||
108 | result = ( | |
109 | csv_content.cwe, | |
110 | csv_content.name, | |
111 | '', | |
112 | csv_content.description, | |
113 | csv_content.resolution, | |
114 | '', | |
115 | ' '.join(csv_content.references), | |
116 | csv_content.severity | |
117 | ) | |
118 | ||
119 | writer.writerow(result) | |
120 | ||
121 | print '[*]Parse finished...' | |
122 | file_csv.close() | |
123 | ||
124 | if __name__ == '__main__': | |
125 | main() |
6 | 6 | See the file 'doc/LICENSE' for the license information |
7 | 7 | |
8 | 8 | ''' |
9 | from utils.logs import getLogger | |
9 | ||
10 | import os | |
11 | from couchdbkit import designer | |
12 | ||
10 | 13 | from config.configuration import getInstanceConfiguration |
11 | import traceback | |
12 | from couchdbkit import designer | |
13 | import os | |
14 | import sys | |
15 | import re | |
16 | import imp | |
17 | import plugins.core | |
18 | 14 | |
19 | 15 | CONF = getInstanceConfiguration() |
20 | 16 | |
64 | 60 | |
65 | 61 | def get_all_views(self): |
66 | 62 | return self.get_fs_designs() |
67 | ||
68 | class PluginManager(object): | |
69 | def __init__(self, plugin_repo_path, mapper_manager): | |
70 | self._controllers = {} | |
71 | self._plugin_modules = {} | |
72 | self._loadPlugins(plugin_repo_path) | |
73 | self._mapper_manager = mapper_manager | |
74 | ||
75 | self._plugin_settings = {} | |
76 | self._loadSettings() | |
77 | ||
78 | def createController(self, id): | |
79 | """ | |
80 | Creates a new plugin controller and adds it into the controllers list. | |
81 | """ | |
82 | plugs = self._instancePlugins() | |
83 | new_controller = plugins.core.PluginController(id, plugs, self._mapper_manager) | |
84 | self._controllers[new_controller.id] = new_controller | |
85 | self.updateSettings(self._plugin_settings) | |
86 | return new_controller | |
87 | ||
88 | def _loadSettings(self): | |
89 | _plugin_settings=CONF.getPluginSettings() | |
90 | if _plugin_settings: | |
91 | ||
92 | self._plugin_settings=_plugin_settings | |
93 | ||
94 | activep=self._instancePlugins() | |
95 | for plugin_id, plugin in activep.iteritems(): | |
96 | ||
97 | if not plugin_id in _plugin_settings: | |
98 | self._plugin_settings[plugin_id] = { | |
99 | "name": plugin.name, | |
100 | "description": plugin.description, | |
101 | "version": plugin.version, | |
102 | "plugin_version": plugin.plugin_version, | |
103 | "settings": dict(plugin.getSettings()) | |
104 | } | |
105 | ||
106 | dplugins=[] | |
107 | for k,v in self._plugin_settings.iteritems(): | |
108 | if not k in activep: | |
109 | dplugins.append(k) | |
110 | ||
111 | for d in dplugins: | |
112 | del self._plugin_settings[d] | |
113 | ||
114 | ||
115 | CONF.setPluginSettings(self._plugin_settings) | |
116 | CONF.saveConfig() | |
117 | ||
118 | def getSettings(self): | |
119 | return self._plugin_settings | |
120 | ||
121 | def updateSettings(self, settings): | |
122 | self._plugin_settings = settings | |
123 | CONF.setPluginSettings(settings) | |
124 | CONF.saveConfig() | |
125 | for plugin_id, params in settings.iteritems(): | |
126 | new_settings = params["settings"] | |
127 | for c_id, c_instance in self._controllers.iteritems(): | |
128 | c_instance.updatePluginSettings(plugin_id, new_settings) | |
129 | ||
130 | def _instancePlugins(self): | |
131 | plugins = {} | |
132 | for module in self._plugin_modules.itervalues(): | |
133 | new_plugin = module.createPlugin() | |
134 | self._verifyPlugin(new_plugin) | |
135 | plugins[new_plugin.id] = new_plugin | |
136 | return plugins | |
137 | ||
138 | def _loadPlugins(self, plugin_repo_path): | |
139 | """ | |
140 | Finds and load all the plugins that are available in the plugin_repo_path. | |
141 | """ | |
142 | try: | |
143 | os.stat(plugin_repo_path) | |
144 | except OSError: | |
145 | ||
146 | pass | |
147 | ||
148 | sys.path.append(plugin_repo_path) | |
149 | ||
150 | dir_name_regexp = re.compile(r"^[\d\w\-\_]+$") | |
151 | for name in os.listdir(plugin_repo_path): | |
152 | if dir_name_regexp.match(name): | |
153 | try: | |
154 | module_path = os.path.join(plugin_repo_path, name) | |
155 | sys.path.append(module_path) | |
156 | module_filename = os.path.join(module_path, "plugin.py") | |
157 | self._plugin_modules[name] = imp.load_source(name, module_filename) | |
158 | except Exception as e: | |
159 | msg = "An error ocurred while loading plugin %s.\n%s" % (module_filename, traceback.format_exc()) | |
160 | getLogger(self).debug(msg) | |
161 | getLogger(self).warn(e) | |
162 | else: | |
163 | pass | |
164 | ||
165 | def getPlugins(self): | |
166 | return self._instancePlugins() | |
167 | ||
168 | ||
169 | def _updatePluginSettings(self, new_plugin_id): | |
170 | pass | |
171 | ||
172 | def _verifyPlugin(self, new_plugin): | |
173 | """ Generic method that decides is a plugin is valid | |
174 | based on a predefined set of checks. """ | |
175 | try: | |
176 | assert(new_plugin.id is not None) | |
177 | assert(new_plugin.version is not None) | |
178 | assert(new_plugin.name is not None) | |
179 | assert(new_plugin.framework_version is not None) | |
180 | except (AssertionError,KeyError): | |
181 | ||
182 | return False | |
183 | return True |
0 | # -*- coding: utf-8 -*- | |
1 | ||
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 | ''' | |
8 | from utils.logs import getLogger | |
9 | from model.workspace import Workspace | |
10 | from persistence.persistence_managers import DBTYPE | |
11 | ||
12 | from model.guiapi import notification_center | |
13 | ||
14 | from config.configuration import getInstanceConfiguration | |
15 | CONF = getInstanceConfiguration() | |
16 | ||
17 | ||
18 | class WorkspaceManager(object): | |
19 | """Workspace Manager class | |
20 | It's responsabilities goes from: | |
21 | * Workspace creation | |
22 | * Workspace removal | |
23 | * Workspace opening | |
24 | * Active Workspace switching""" | |
25 | ||
26 | def __init__(self, dbManager, mappersManager, changesManager, reportsManager, *args, **kwargs): | |
27 | self.dbManager = dbManager | |
28 | self.mappersManager = mappersManager | |
29 | self.changesManager = changesManager | |
30 | self.reportsManager = reportsManager | |
31 | self.active_workspace = None | |
32 | ||
33 | def getWorkspacesNames(self): | |
34 | return self.dbManager.getAllDbNames() | |
35 | ||
36 | def createWorkspace(self, name, desc, dbtype=DBTYPE.FS): | |
37 | workspace = Workspace(name, desc) | |
38 | dbConnector = self.dbManager.createDb(name, dbtype) | |
39 | if dbConnector: | |
40 | self.closeWorkspace() | |
41 | self.mappersManager.createMappers(dbConnector) | |
42 | self.mappersManager.save(workspace) | |
43 | self.setActiveWorkspace(workspace) | |
44 | CONF.setLastWorkspace(name) | |
45 | CONF.saveConfig() | |
46 | notification_center.workspaceChanged(workspace,self.getWorkspaceType(name)) | |
47 | notification_center.workspaceLoad(workspace.getHosts()) | |
48 | self.changesManager.watch(self.mappersManager, dbConnector) | |
49 | self.reportsManager.watch(name) | |
50 | return workspace | |
51 | return False | |
52 | ||
53 | def openWorkspace(self, name): | |
54 | if name in self.getWorkspacesNames(): | |
55 | self.closeWorkspace() | |
56 | dbConnector = self.dbManager.getConnector(name) | |
57 | self.mappersManager.createMappers(dbConnector) | |
58 | workspace = self.mappersManager.getMapper(Workspace.__name__).find(name) | |
59 | if not workspace: | |
60 | notification_center.showDialog( | |
61 | "Error loading workspace.\nYou should try opening faraday with the '--update' option", | |
62 | level="ERROR") | |
63 | return self.openDefaultWorkspace() | |
64 | self.setActiveWorkspace(workspace) | |
65 | CONF.setLastWorkspace(name) | |
66 | CONF.saveConfig() | |
67 | notification_center.workspaceChanged(workspace,self.getWorkspaceType(name)) | |
68 | notification_center.workspaceLoad(workspace.getHosts()) | |
69 | self.changesManager.watch(self.mappersManager, dbConnector) | |
70 | self.reportsManager.watch(name) | |
71 | return workspace | |
72 | return None | |
73 | ||
74 | def openDefaultWorkspace(self): | |
75 | #This method opens the default workspace called 'untitled' | |
76 | if 'untitled' not in self.getWorkspacesNames(): | |
77 | workspace = Workspace('untitled', 'default workspace') | |
78 | dbConnector = self.dbManager.createDb( | |
79 | workspace.getName(), DBTYPE.FS) | |
80 | if self.active_workspace: | |
81 | self.closeWorkspace() | |
82 | self.mappersManager.createMappers(dbConnector) | |
83 | self.mappersManager.save(workspace) | |
84 | return self.openWorkspace('untitled') | |
85 | ||
86 | ||
87 | def closeWorkspace(self): | |
88 | self.changesManager.unwatch() | |
89 | ||
90 | ||
91 | def removeWorkspace(self, name): | |
92 | if name in self.getWorkspacesNames(): | |
93 | return self.dbManager.removeDb(name) | |
94 | ||
95 | def setActiveWorkspace(self, workspace): | |
96 | self.active_workspace = workspace | |
97 | ||
98 | def getActiveWorkspace(self): | |
99 | return self.active_workspace | |
100 | ||
101 | def workspaceExists(self, name): | |
102 | return self.dbManager.connectorExists(name) | |
103 | ||
104 | def resource(self): | |
105 | self.dbManager.reloadConfig() | |
106 | ||
107 | def isActive(self, name): | |
108 | return self.active_workspace.getName() == name | |
109 | ||
110 | def getWorkspaceType(self, name): | |
111 | return self._dbTypeToNamedType(self.dbManager.getDbType(name)) | |
112 | ||
113 | def _dbTypeToNamedType(self, dbtype): | |
114 | if dbtype == DBTYPE.COUCHDB: | |
115 | return 'CouchDB' | |
116 | if dbtype == DBTYPE.FS: | |
117 | return 'FS' | |
118 | ||
119 | def namedTypeToDbType(self, name): | |
120 | if name =='CouchDB': | |
121 | return DBTYPE.COUCHDB | |
122 | if name == 'FS': | |
123 | return DBTYPE.FS | |
124 | ||
125 | def getAvailableWorkspaceTypes(self): | |
126 | return [self._dbTypeToNamedType(dbtype) for \ | |
127 | dbtype in self.dbManager.getAvailableDBs()] | |
128 |
11 | 11 | import time |
12 | 12 | import traceback |
13 | 13 | import re |
14 | import requests | |
14 | ||
15 | 15 | try: |
16 | 16 | import xml.etree.cElementTree as ET |
17 | 17 | |
18 | 18 | except ImportError: |
19 | 19 | print "cElementTree could not be imported. Using ElementTree instead" |
20 | 20 | import xml.etree.ElementTree as ET |
21 | from apis.rest.api import PluginControllerAPIClient | |
21 | from apis.rest.client import PluginControllerAPIClient | |
22 | 22 | |
23 | 23 | from config.configuration import getInstanceConfiguration |
24 | 24 | CONF = getInstanceConfiguration() |
25 | 25 | |
26 | class NoReportsWatchException(Exception): pass | |
26 | ||
27 | class ReportProcessor(): | |
28 | def __init__(self): | |
29 | host = CONF.getApiConInfoHost() | |
30 | port_rest = int(CONF.getApiRestfulConInfoPort()) | |
31 | ||
32 | self.client = PluginControllerAPIClient(host, port_rest) | |
33 | ||
34 | def processReport(self, filename): | |
35 | """ | |
36 | Process one Report | |
37 | """ | |
38 | model.api.log("Report file is %s" % filename) | |
39 | ||
40 | parser = ReportParser(filename) | |
41 | if (parser.report_type is not None): | |
42 | model.api.log( | |
43 | "The file is %s, %s" % (filename, parser.report_type)) | |
44 | ||
45 | command_string = "./%s %s" % (parser.report_type.lower(), | |
46 | filename) | |
47 | model.api.log("Executing %s" % (command_string)) | |
48 | ||
49 | new_cmd, output_file = self.client.send_cmd(command_string) | |
50 | self.client.send_output(command_string, filename) | |
51 | return True | |
52 | return False | |
53 | ||
54 | def onlinePlugin(self, cmd): | |
55 | new_cmd, output_file = self.client.send_cmd(cmd) | |
56 | self.client.send_output(cmd) | |
57 | ||
27 | 58 | |
28 | 59 | class ReportManager(threading.Thread): |
29 | def __init__(self, timer, plugin_controller, path=None): | |
60 | def __init__(self, timer, ws_name): | |
30 | 61 | threading.Thread.__init__(self) |
31 | 62 | self.setDaemon(True) |
32 | 63 | self.timer = timer |
33 | 64 | self._stop = False |
34 | self.path = path | |
35 | self.plugin_controller = plugin_controller | |
36 | self._report_path = None | |
37 | self._report_ppath = None | |
65 | self._report_path = os.path.join(CONF.getReportPath(), ws_name) | |
66 | self._report_ppath = os.path.join(self._report_path, "process") | |
67 | self.processor = ReportProcessor() | |
68 | ||
69 | if not os.path.exists(self._report_path): | |
70 | os.mkdir(self._report_path) | |
71 | ||
72 | if not os.path.exists(self._report_ppath): | |
73 | os.mkdir(self._report_ppath) | |
38 | 74 | |
39 | 75 | def run(self): |
40 | 76 | tmp_timer = 0 |
53 | 89 | def stop(self): |
54 | 90 | self._stop = True |
55 | 91 | |
56 | def watch(self, name): | |
57 | self._report_path = os.path.join(CONF.getReportPath(), name) | |
58 | self._report_ppath = os.path.join(self._report_path, "process") | |
59 | ||
60 | if not os.path.exists(self._report_path): | |
61 | os.mkdir(self._report_path) | |
62 | ||
63 | if not os.path.exists(self._report_ppath): | |
64 | os.mkdir(self._report_ppath) | |
65 | ||
66 | def startWatch(self): | |
67 | if not self._report_path: | |
68 | raise NoReportsWatchException() | |
69 | self.start() | |
70 | ||
71 | 92 | def syncReports(self): |
72 | 93 | """ |
73 | 94 | Synchronize report directory using the DataManager and Plugins online |
79 | 100 | if root == self._report_path: |
80 | 101 | for name in files: |
81 | 102 | filename = os.path.join(root, name) |
82 | model.api.log( "Report file is %s" % filename) | |
83 | ||
84 | parser = ReportParser(filename) | |
85 | if (parser.report_type is not None): | |
86 | ||
87 | host = CONF.getApiConInfoHost() | |
88 | port_rest = int(CONF.getApiRestfulConInfoPort()) | |
89 | ||
90 | client = PluginControllerAPIClient(host, port_rest) | |
91 | ||
92 | model.api.log("The file is %s, %s" % (filename,parser.report_type)) | |
93 | ||
94 | command_string = "./%s %s" % (parser.report_type.lower(), filename) | |
95 | model.api.log("Executing %s" % (command_string)) | |
96 | ||
97 | new_cmd, output_file = client.send_cmd(command_string) | |
98 | client.send_output(command_string, filename) | |
103 | ||
104 | self.processor.processReport(filename) | |
105 | ||
99 | 106 | os.rename(filename, os.path.join(self._report_ppath, name)) |
100 | 107 | |
101 | 108 | self.onlinePlugins() |
102 | 109 | |
103 | ||
104 | 110 | def onlinePlugins(self): |
105 | 111 | """ |
106 | 112 | Process online plugins |
107 | 113 | """ |
108 | _pluginsOn={"MetasploitOn" : "./metasploiton online",} | |
109 | _pluginsOn.update({"Beef" : "./beef online",}) | |
110 | _psettings=CONF.getPluginSettings() | |
111 | ||
112 | for k,v in _pluginsOn.iteritems(): | |
113 | if k in _psettings: | |
114 | if _psettings[k]['settings']['Enable'] == "1": | |
115 | new_cmd = self.plugin_controller.processCommandInput("", "", | |
116 | "", | |
117 | v, | |
118 | False) | |
119 | ||
120 | self.plugin_controller.storeCommandOutput("") | |
121 | ||
122 | self.plugin_controller.onCommandFinished() | |
123 | ||
114 | pluginsOn = {"MetasploitOn": "./metasploiton online"} | |
115 | pluginsOn.update({"Beef": "./beef online"}) | |
116 | psettings = CONF.getPluginSettings() | |
117 | ||
118 | for name, cmd in pluginsOn.iteritems(): | |
119 | if name in psettings: | |
120 | if psettings[name]['settings']['Enable'] == "1": | |
121 | self.processor.onlinePlugin(cmd) | |
124 | 122 | |
125 | 123 | |
126 | 124 | class ReportParser(object): |
193 | 191 | if report_type == "zip": |
194 | 192 | result = "maltego" |
195 | 193 | |
196 | elif report_type == "xml": | |
194 | else: | |
197 | 195 | |
198 | 196 | try: |
199 | 197 | for event, elem in ET.iterparse(f, ('start', )): |
225 | 223 | elif "report" == tag: |
226 | 224 | if re.search("https://raw.githubusercontent.com/Arachni/arachni/", output) != None: |
227 | 225 | return "arachni_faraday" |
228 | elif re.search("OpenVAS", output) != None: | |
226 | elif re.search("OpenVAS", output) != None or re.search('<omp><version>', output) != None: | |
229 | 227 | return "openvas" |
230 | 228 | else: |
231 | 229 | return "zap" |
251 | 249 | return "nexpose" |
252 | 250 | elif "NexposeReport" == tag: |
253 | 251 | return "nexpose-full" |
254 | elif "SCAN" == tag: | |
252 | elif "ASSET_DATA_REPORT" == tag: | |
255 | 253 | return "qualysguard" |
256 | 254 | elif "scanJob" == tag: |
257 | 255 | return "retina" |
0 | # -*- coding: utf-8 -*- | |
1 | ||
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 | ''' | |
8 | import restkit | |
9 | ||
10 | from model.workspace import Workspace | |
11 | from persistence.persistence_managers import DBTYPE | |
12 | ||
13 | from model.guiapi import notification_center | |
14 | ||
15 | from config.configuration import getInstanceConfiguration | |
16 | CONF = getInstanceConfiguration() | |
17 | ||
18 | ||
19 | class WorkspaceException(Exception): | |
20 | pass | |
21 | ||
22 | ||
23 | class WorkspaceManager(object): | |
24 | """ | |
25 | Workspace Manager class | |
26 | Its responsibilities goes from: | |
27 | * Workspace creation | |
28 | * Workspace removal | |
29 | * Workspace opening | |
30 | * Active Workspace switching | |
31 | """ | |
32 | ||
33 | def __init__(self, dbManager, mappersManager, | |
34 | changesManager, *args, **kwargs): | |
35 | self.dbManager = dbManager | |
36 | self.mappersManager = mappersManager | |
37 | self.changesManager = changesManager | |
38 | self.active_workspace = None | |
39 | ||
40 | def getWorkspacesNames(self): | |
41 | return self.dbManager.getAllDbNames() | |
42 | ||
43 | def createWorkspace(self, name, desc, dbtype=DBTYPE.FS): | |
44 | workspace = Workspace(name, desc) | |
45 | try: | |
46 | dbConnector = self.dbManager.createDb(name, dbtype) | |
47 | except restkit.Unauthorized: | |
48 | raise WorkspaceException( | |
49 | ("You're not authorized to create workspaces\n" | |
50 | "Make sure you're an admin and add your credentials" | |
51 | "to your user configuration " | |
52 | "file in $HOME/.faraday/config/user.xml\n" | |
53 | "For example: " | |
54 | "<couch_uri>http://john:[email protected]:5984</couch_uri>")) | |
55 | except Exception as e: | |
56 | raise WorkspaceException(str(e)) | |
57 | if dbConnector: | |
58 | self.closeWorkspace() | |
59 | self.mappersManager.createMappers(dbConnector) | |
60 | self.mappersManager.save(workspace) | |
61 | self.setActiveWorkspace(workspace) | |
62 | notification_center.workspaceChanged( | |
63 | workspace, self.getWorkspaceType(name)) | |
64 | notification_center.workspaceLoad(workspace.getHosts()) | |
65 | self.changesManager.watch(self.mappersManager, dbConnector) | |
66 | return workspace | |
67 | return False | |
68 | ||
69 | def openWorkspace(self, name): | |
70 | if name not in self.getWorkspacesNames(): | |
71 | raise WorkspaceException( | |
72 | "Workspace %s wasn't found" % name) | |
73 | self.closeWorkspace() | |
74 | try: | |
75 | dbConnector = self.dbManager.getConnector(name) | |
76 | except restkit.Unauthorized: | |
77 | raise WorkspaceException( | |
78 | ("You're not authorized to access this workspace\n" | |
79 | "Add your credentials to your user configuration " | |
80 | "file in $HOME/.faraday/config/user.xml\n" | |
81 | "For example: " | |
82 | "<couch_uri>http://john:[email protected]:5984</couch_uri>")) | |
83 | except Exception as e: | |
84 | raise WorkspaceException(str(e)) | |
85 | self.mappersManager.createMappers(dbConnector) | |
86 | workspace = self.mappersManager.getMapper( | |
87 | Workspace.__name__).find(name) | |
88 | if not workspace: | |
89 | raise WorkspaceException( | |
90 | ("Error loading workspace.\n" | |
91 | "You should try opening faraday " | |
92 | "with the '--update' option")) | |
93 | self.setActiveWorkspace(workspace) | |
94 | notification_center.workspaceChanged( | |
95 | workspace, self.getWorkspaceType(name)) | |
96 | notification_center.workspaceLoad(workspace.getHosts()) | |
97 | self.changesManager.watch(self.mappersManager, dbConnector) | |
98 | return workspace | |
99 | ||
100 | def openDefaultWorkspace(self, name='untitled'): | |
101 | # This method opens the default workspace called 'untitled' | |
102 | if name not in self.getWorkspacesNames(): | |
103 | workspace = Workspace(name, 'default workspace') | |
104 | dbConnector = self.dbManager.createDb( | |
105 | workspace.getName(), DBTYPE.FS) | |
106 | if self.active_workspace: | |
107 | self.closeWorkspace() | |
108 | self.mappersManager.createMappers(dbConnector) | |
109 | self.mappersManager.save(workspace) | |
110 | return self.openWorkspace(name) | |
111 | ||
112 | def closeWorkspace(self): | |
113 | self.changesManager.unwatch() | |
114 | ||
115 | def removeWorkspace(self, name): | |
116 | if name in self.getWorkspacesNames(): | |
117 | return self.dbManager.removeDb(name) | |
118 | ||
119 | def setActiveWorkspace(self, workspace): | |
120 | self.active_workspace = workspace | |
121 | ||
122 | def getActiveWorkspace(self): | |
123 | return self.active_workspace | |
124 | ||
125 | def workspaceExists(self, name): | |
126 | return self.dbManager.connectorExists(name) | |
127 | ||
128 | def resource(self): | |
129 | self.dbManager.reloadConfig() | |
130 | ||
131 | def isActive(self, name): | |
132 | return self.active_workspace.getName() == name | |
133 | ||
134 | def getWorkspaceType(self, name): | |
135 | return self._dbTypeToNamedType(self.dbManager.getDbType(name)) | |
136 | ||
137 | def _dbTypeToNamedType(self, dbtype): | |
138 | if dbtype == DBTYPE.COUCHDB: | |
139 | return 'CouchDB' | |
140 | if dbtype == DBTYPE.FS: | |
141 | return 'FS' | |
142 | ||
143 | def namedTypeToDbType(self, name): | |
144 | if name == 'CouchDB': | |
145 | return DBTYPE.COUCHDB | |
146 | if name == 'FS': | |
147 | return DBTYPE.FS | |
148 | ||
149 | def getAvailableWorkspaceTypes(self): | |
150 | return [self._dbTypeToNamedType(dbtype) for | |
151 | dbtype in self.dbManager.getAvailableDBs()] |
6 | 6 | ''' |
7 | 7 | |
8 | 8 | import os |
9 | import socket | |
9 | 10 | import zipfile |
10 | 11 | import logging |
11 | 12 | |
75 | 76 | if CONF.getApiConInfo() is None: |
76 | 77 | CONF.setApiConInfo(hostname, port) |
77 | 78 | devlog("starting XMLRPCServer with api_conn_info = %s" % str(CONF.getApiConInfo())) |
78 | try: | |
79 | _xmlrpc_api_server = model.common.XMLRPCServer(CONF.getApiConInfo()) | |
80 | # Registers the XML-RPC introspection functions system.listMethods, system.methodHelp and system.methodSignature. | |
81 | _xmlrpc_api_server.register_introspection_functions() | |
82 | ||
83 | # register a function to nicely stop server | |
84 | _xmlrpc_api_server.register_function(_xmlrpc_api_server.stop_server) | |
85 | ||
86 | # register all the api functions to be exposed by the server | |
87 | _xmlrpc_api_server.register_function(createAndAddHost) | |
88 | _xmlrpc_api_server.register_function(createAndAddInterface) | |
89 | _xmlrpc_api_server.register_function(createAndAddServiceToApplication) | |
90 | _xmlrpc_api_server.register_function(createAndAddServiceToInterface) | |
91 | _xmlrpc_api_server.register_function(createAndAddApplication) | |
92 | _xmlrpc_api_server.register_function(createAndAddNoteToService) | |
93 | _xmlrpc_api_server.register_function(createAndAddNoteToHost) | |
94 | _xmlrpc_api_server.register_function(createAndAddNoteToNote) | |
95 | _xmlrpc_api_server.register_function(createAndAddVulnWebToService) | |
96 | _xmlrpc_api_server.register_function(createAndAddVulnToService) | |
97 | _xmlrpc_api_server.register_function(createAndAddVulnToHost) | |
98 | _xmlrpc_api_server.register_function(addHost) | |
99 | _xmlrpc_api_server.register_function(addInterface) | |
100 | _xmlrpc_api_server.register_function(addServiceToApplication) | |
101 | _xmlrpc_api_server.register_function(addServiceToInterface) | |
102 | _xmlrpc_api_server.register_function(addApplication) | |
103 | _xmlrpc_api_server.register_function(newHost) | |
104 | _xmlrpc_api_server.register_function(newInterface) | |
105 | _xmlrpc_api_server.register_function(newService) | |
106 | _xmlrpc_api_server.register_function(newApplication) | |
107 | _xmlrpc_api_server.register_function(devlog) | |
108 | ||
109 | #TODO: check if all necessary APIs are registered here!! | |
110 | ||
111 | devlog("XMLRPC API server configured...") | |
112 | except Exception, e: | |
113 | msg = "There was an error creating the XMLRPC API Server:\n%s" % str(e) | |
114 | log(msg) | |
115 | devlog("[ERROR] - %s" % msg) | |
79 | ||
80 | while True: | |
81 | ||
82 | try: | |
83 | _xmlrpc_api_server = model.common.XMLRPCServer(CONF.getApiConInfo()) | |
84 | # Registers the XML-RPC introspection functions system.listMethods, system.methodHelp and system.methodSignature. | |
85 | _xmlrpc_api_server.register_introspection_functions() | |
86 | ||
87 | # register a function to nicely stop server | |
88 | _xmlrpc_api_server.register_function(_xmlrpc_api_server.stop_server) | |
89 | ||
90 | # register all the api functions to be exposed by the server | |
91 | _xmlrpc_api_server.register_function(createAndAddHost) | |
92 | _xmlrpc_api_server.register_function(createAndAddInterface) | |
93 | _xmlrpc_api_server.register_function(createAndAddServiceToApplication) | |
94 | _xmlrpc_api_server.register_function(createAndAddServiceToInterface) | |
95 | _xmlrpc_api_server.register_function(createAndAddApplication) | |
96 | _xmlrpc_api_server.register_function(createAndAddNoteToService) | |
97 | _xmlrpc_api_server.register_function(createAndAddNoteToHost) | |
98 | _xmlrpc_api_server.register_function(createAndAddNoteToNote) | |
99 | _xmlrpc_api_server.register_function(createAndAddVulnWebToService) | |
100 | _xmlrpc_api_server.register_function(createAndAddVulnToService) | |
101 | _xmlrpc_api_server.register_function(createAndAddVulnToHost) | |
102 | _xmlrpc_api_server.register_function(addHost) | |
103 | _xmlrpc_api_server.register_function(addInterface) | |
104 | _xmlrpc_api_server.register_function(addServiceToApplication) | |
105 | _xmlrpc_api_server.register_function(addServiceToInterface) | |
106 | _xmlrpc_api_server.register_function(addApplication) | |
107 | _xmlrpc_api_server.register_function(newHost) | |
108 | _xmlrpc_api_server.register_function(newInterface) | |
109 | _xmlrpc_api_server.register_function(newService) | |
110 | _xmlrpc_api_server.register_function(newApplication) | |
111 | _xmlrpc_api_server.register_function(devlog) | |
112 | ||
113 | #TODO: check if all necessary APIs are registered here!! | |
114 | ||
115 | getLogger().info( | |
116 | "XMLRPC API server configured on %s" % str( | |
117 | CONF.getApiConInfo())) | |
118 | break | |
119 | except socket.error as exception: | |
120 | if exception.errno == 98: | |
121 | # Port already in use | |
122 | # Let's try the next one | |
123 | port += 1 | |
124 | if port > 65535: | |
125 | raise Exception("No ports available!") | |
126 | CONF.setApiConInfo(hostname, port) | |
127 | CONF.saveConfig() | |
128 | else: | |
129 | raise exception | |
130 | except Exception as e: | |
131 | msg = "There was an error creating the XMLRPC API Server:\n%s" % str(e) | |
132 | log(msg) | |
133 | devlog("[ERROR] - %s" % msg) | |
116 | 134 | |
117 | 135 | |
118 | 136 | #------------------------------------------------------------------------------- |
520 | 538 | #getConflicts: get the current conflicts |
521 | 539 | def getConflicts(): |
522 | 540 | return __model_controller.getConflicts() |
523 | ||
524 | #------------------------------------------------------------------------------- | |
525 | ||
526 | #exportWorskpace | |
527 | ||
528 | def exportWorskpace(workspace_path, export_path): | |
529 | """ | |
530 | This api will create a zip file for the persistence directory | |
531 | """ | |
532 | zip = zipfile.ZipFile(export_path, 'w', compression=zipfile.ZIP_DEFLATED) | |
533 | root_len = len(os.path.abspath(workspace_path)) | |
534 | for root, dirs, files in os.walk(workspace_path): | |
535 | if ".svn" not in root: | |
536 | archive_root = os.path.abspath(root)[root_len:] | |
537 | if files is not ".svn": | |
538 | for f in files: | |
539 | fullpath = os.path.join(root, f) | |
540 | archive_name = os.path.join(archive_root, f) | |
541 | # print f | |
542 | zip.write(fullpath, archive_name, zipfile.ZIP_DEFLATED) | |
543 | zip.close() | |
544 | ||
545 | ||
546 | def importWorskpace(workspace_path, file_path): | |
547 | """ | |
548 | This api will import a zip file of the persistence directory. | |
549 | WARNING: this will overwrite any existing files! | |
550 | """ | |
551 | ||
552 | archive = zipfile.ZipFile(str(file_path), "r", zipfile.ZIP_DEFLATED) | |
553 | names = archive.namelist() | |
554 | ||
555 | for name in names: | |
556 | filename = os.path.join(workspace_path, name) | |
557 | if not os.path.exists(os.path.dirname(filename)): | |
558 | os.mkdir(os.path.dirname(filename)) | |
559 | # create the output file. This will overwrite any existing file with the same name | |
560 | temp = open(filename, "wb") | |
561 | data = archive.read(name) # read data from zip archive | |
562 | temp.write(data) | |
563 | temp.close() | |
564 | ||
565 | archive.close() | |
566 | 541 | |
567 | 542 | #------------------------------------------------------------------------------- |
568 | 543 | # EVIDENCE |
672 | 647 | def showPopup(msg, level="Information"): |
673 | 648 | return model.log.getNotifier().showPopup(msg, level) |
674 | 649 | |
650 | ||
651 | # Plugin status | |
652 | ||
653 | def pluginStart(): | |
654 | __model_controller.addPluginStart() | |
655 | ||
656 | ||
657 | def pluginEnd(): | |
658 | __model_controller.addPluginEnd() | |
659 | ||
675 | 660 | #------------------------------------------------------------------------------- |
676 | 661 | def getLoggedUser(): |
677 | 662 | """ |
10 | 10 | import threading |
11 | 11 | import requests |
12 | 12 | |
13 | # TODO: no seria mejor activar todo ? | |
14 | # XXX: something strange happens if we import | |
15 | # this module at the bottom of the list.... | |
16 | from auth.manager import SecurityManager | |
17 | from auth.manager import codes | |
18 | 13 | from model.controller import ModelController |
19 | 14 | from persistence.persistence_managers import DbManager |
20 | 15 | from controllers.change import ChangeController |
21 | from managers.model_managers import WorkspaceManager | |
16 | from managers.workspace_manager import WorkspaceManager | |
22 | 17 | import model.api |
23 | 18 | import model.guiapi |
24 | 19 | import apis.rest.api as restapi |
25 | 20 | import model.log |
26 | 21 | from utils.logs import getLogger |
27 | 22 | import traceback |
28 | from managers.all import PluginManager | |
23 | from plugins.manager import PluginManager | |
29 | 24 | from managers.mapper_manager import MapperManager |
30 | from managers.reports_managers import ReportManager | |
31 | ||
32 | 25 | from utils.error_report import exception_handler |
33 | 26 | from utils.error_report import installThreadExcepthook |
34 | 27 | |
35 | 28 | from gui.gui_app import UiFactory |
29 | from model.cli_app import CliApp | |
36 | 30 | |
37 | 31 | from config.configuration import getInstanceConfiguration |
38 | 32 | CONF = getInstanceConfiguration() |
67 | 61 | def __init__(self, args): |
68 | 62 | self._original_excepthook = sys.excepthook |
69 | 63 | |
70 | self._configuration = CONF | |
64 | self.args = args | |
71 | 65 | |
72 | self._security_manager = SecurityManager() | |
73 | 66 | self._mappers_manager = MapperManager() |
74 | 67 | self._changes_controller = ChangeController() |
75 | 68 | self._db_manager = DbManager() |
76 | 69 | |
77 | self._model_controller = ModelController( | |
78 | self._security_manager, | |
79 | self._mappers_manager) | |
70 | self._model_controller = ModelController(self._mappers_manager) | |
80 | 71 | |
81 | 72 | self._plugin_manager = PluginManager( |
82 | 73 | os.path.join(CONF.getConfigPath(), "plugins"), |
83 | 74 | self._mappers_manager) |
84 | 75 | |
85 | self._reports_manager = ReportManager(10, self._plugin_manager.createController("ReportManager")) | |
86 | ||
87 | 76 | self._workspace_manager = WorkspaceManager( |
88 | 77 | self._db_manager, |
89 | 78 | self._mappers_manager, |
90 | self._changes_controller, | |
91 | self._reports_manager) | |
79 | self._changes_controller) | |
92 | 80 | |
93 | self.gui_app = UiFactory.create(self._model_controller, | |
81 | if self.args.cli: | |
82 | self.app = CliApp(self._workspace_manager) | |
83 | CONF.setMergeStrategy("new") | |
84 | else: | |
85 | self.app = UiFactory.create(self._model_controller, | |
94 | 86 | self._plugin_manager, |
95 | 87 | self._workspace_manager, |
96 | args.gui) | |
97 | ||
98 | self.gui_app.setSplashImage(os.path.join( | |
99 | CONF.getImagePath(), "splash2.png")) | |
88 | self.args.gui) | |
100 | 89 | |
101 | 90 | self.timer = TimerClass() |
102 | 91 | self.timer.start() |
105 | 94 | sys.excepthook = exception_handler |
106 | 95 | installThreadExcepthook() |
107 | 96 | |
108 | def disableLogin(self): | |
109 | CONF.setAuth(sys.disablelogin) | |
110 | ||
111 | 97 | def start(self): |
112 | 98 | try: |
113 | ||
114 | self.gui_app.startSplashScreen() | |
115 | self.gui_app.splashMessage("Starting Faraday") | |
116 | ||
117 | 99 | signal.signal(signal.SIGINT, self.ctrlC) |
118 | 100 | |
119 | logged = True | |
101 | model.api.devlog("Starting application...") | |
102 | model.api.devlog("Setting up remote API's...") | |
120 | 103 | |
121 | while True: | |
104 | if not self.args.workspace: | |
105 | workspace = CONF.getLastWorkspace() | |
106 | self.args.workspace = workspace | |
122 | 107 | |
123 | username, password = "usuario", "password" | |
108 | model.api.setUpAPIs( | |
109 | self._model_controller, | |
110 | self._workspace_manager, | |
111 | CONF.getApiConInfoHost(), | |
112 | CONF.getApiConInfoPort()) | |
113 | model.guiapi.setUpGUIAPIs(self._model_controller) | |
124 | 114 | |
125 | if username is None and password is None: | |
126 | break | |
127 | result = self._security_manager.authenticateUser(username, password) | |
128 | if result == codes.successfulLogin: | |
129 | logged = True | |
130 | break | |
115 | model.api.devlog("Starting model controller daemon...") | |
131 | 116 | |
132 | if logged: | |
133 | model.api.devlog("Starting application...") | |
134 | model.api.devlog("Setting up remote API's...") | |
135 | # We need to create the last used workspace (or the default | |
136 | # workspace) before we start the model controller and the | |
137 | # report manager | |
117 | self._model_controller.start() | |
118 | model.api.startAPIServer() | |
119 | restapi.startAPIs( | |
120 | self._plugin_manager, | |
121 | self._model_controller, | |
122 | self._mappers_manager, | |
123 | CONF.getApiConInfoHost(), | |
124 | CONF.getApiRestfulConInfoPort()) | |
138 | 125 | |
139 | last_workspace = CONF.getLastWorkspace() | |
140 | try: | |
141 | if not self._workspace_manager.workspaceExists(last_workspace): | |
142 | getLogger(self).info("Your last workspace ("+str(last_workspace)+") wasn't accessible, check configuration...") | |
143 | self._workspace_manager.openDefaultWorkspace() | |
144 | #self._workspace_manager.createWorkspace(last_workspace, 'default workspace, probably created already in couchb') | |
145 | else: | |
146 | self._workspace_manager.openWorkspace(last_workspace) | |
147 | except restkit.errors.Unauthorized: | |
148 | print "You are trying to enter CouchDB with authentication" | |
149 | print "Add your credentials to your user configuration file in $HOME/.faraday/config/user.xml" | |
150 | print "For example: <couch_uri>http://john:[email protected]:5984</couch_uri>" | |
151 | return | |
126 | model.api.devlog("Faraday ready...") | |
152 | 127 | |
153 | model.api.setUpAPIs( | |
154 | self._model_controller, | |
155 | self._workspace_manager, | |
156 | CONF.getApiConInfoHost(), | |
157 | CONF.getApiConInfoPort()) | |
158 | model.guiapi.setUpGUIAPIs(self._model_controller) | |
159 | ||
160 | model.api.devlog("Starting model controller daemon...") | |
161 | ||
162 | self._model_controller.start() | |
163 | model.api.startAPIServer() | |
164 | restapi.startAPIs( | |
165 | self._plugin_manager, | |
166 | self._model_controller, | |
167 | self._mappers_manager, | |
168 | CONF.getApiConInfoHost(), | |
169 | CONF.getApiRestfulConInfoPort()) | |
170 | # Start report manager here | |
171 | getLogger(self).debug("Starting Reports Manager Thread") | |
172 | self._reports_manager.startWatch() | |
173 | ||
174 | model.api.devlog("Faraday ready...") | |
175 | model.api.__current_logged_user = username | |
176 | ||
177 | self.gui_app.splashMessage("Loading workspace... Please wait.") | |
178 | ||
179 | self.gui_app.loadWorkspaces() | |
180 | ||
181 | self.gui_app.stopSplashScreen() | |
128 | exit_code = self.app.run(self.args) | |
182 | 129 | |
183 | 130 | except Exception: |
184 | 131 | print "There was an error while starting Faraday" |
185 | 132 | print "-" * 50 |
186 | 133 | traceback.print_exc() |
187 | 134 | print "-" * 50 |
188 | self.__exit(-1) | |
189 | ||
190 | if logged: | |
191 | exit_code = self.gui_app.run([]) | |
192 | #exit_code = self.app.exec_loop() | |
193 | else: | |
194 | 135 | exit_code = -1 |
195 | 136 | |
196 | return self.__exit(exit_code) | |
137 | finally: | |
138 | return self.__exit(exit_code) | |
197 | 139 | |
198 | 140 | def __exit(self, exit_code=0): |
199 | 141 | """ |
200 | 142 | Exits the application with the provided code. |
201 | 143 | It also waits until all app threads end. |
202 | 144 | """ |
203 | model.api.devlog("Closing Faraday...") | |
145 | model.api.log("Closing Faraday...") | |
204 | 146 | model.api.devlog("stopping model controller thread...") |
205 | 147 | model.api.stopAPIServer() |
206 | 148 | restapi.stopServer() |
207 | self._reports_manager.stop() | |
208 | 149 | self._changes_controller.stop() |
209 | 150 | self._model_controller.stop() |
210 | 151 | self._model_controller.join() |
211 | self.gui_app.quit() | |
212 | 152 | self.timer.stop() |
213 | 153 | model.api.devlog("Waiting for controller threads to end...") |
214 | 154 | return exit_code |
217 | 157 | """ |
218 | 158 | Redefined quit handler to nicely end up things |
219 | 159 | """ |
220 | self.gui_app.quit() | |
160 | self.app.quit() | |
221 | 161 | |
222 | 162 | def ctrlC(self, signal, frame): |
223 | 163 | getLogger(self).info("Exiting...") |
224 | self.__exit(exit_code=0) | |
225 | ||
226 | def getWorkspaceManager(self): | |
227 | return self._workspace_manager | |
164 | self.app.quit() |
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 utils.logs import getLogger | |
8 | from managers.reports_managers import ReportProcessor | |
9 | ||
10 | ||
11 | class CliApp(): | |
12 | def __init__(self, workspace_manager): | |
13 | self.workspace_manager = workspace_manager | |
14 | ||
15 | def run(self, args): | |
16 | workspace = args.workspace | |
17 | try: | |
18 | self.workspace_manager.openWorkspace(workspace) | |
19 | except Exception as e: | |
20 | getLogger(self).error( | |
21 | ("The workspace %s is not accessible, " | |
22 | "check configuration") % workspace) | |
23 | getLogger(self).error(str(e)) | |
24 | return -1 | |
25 | ||
26 | rp = ReportProcessor() | |
27 | rp.processReport(args.filename) |
3 | 3 | See the file 'doc/LICENSE' for the license information |
4 | 4 | |
5 | 5 | ''' |
6 | """ | |
7 | Contains base classes used to represent the application model | |
8 | and some other common objects and functions used in the model | |
9 | """ | |
10 | 6 | import sys |
11 | 7 | import os |
12 | 8 | import traceback |
17 | 13 | import json |
18 | 14 | import model |
19 | 15 | from conflict import ConflictUpdate |
20 | from model.diff import ModelObjectDiff | |
16 | from model.diff import ModelObjectDiff, MergeSolver | |
21 | 17 | |
22 | 18 | try: |
23 | 19 | import model.api as api |
30 | 26 | from time import time |
31 | 27 | import cPickle as pickle |
32 | 28 | from config.configuration import getInstanceConfiguration |
29 | CONF = getInstanceConfiguration() | |
30 | ||
31 | """ | |
32 | Contains base classes used to represent the application model | |
33 | and some other common objects and functions used in the model | |
34 | """ | |
35 | ||
33 | 36 | |
34 | 37 | class MetadataUpdateActions(object): |
35 | 38 | """Constants for the actions made on the update""" |
36 | 39 | UNDEFINED = -1 |
37 | 40 | CREATE = 0 |
38 | 41 | UPDATE = 1 |
42 | ||
39 | 43 | |
40 | 44 | class Metadata(object): |
41 | 45 | """To save information about the modification of ModelObjects. |
149 | 153 | if prop1 in self.defaultValues(): return prop2 |
150 | 154 | elif prop2 in self.defaultValues(): return prop1 |
151 | 155 | elif self.tieBreakable(key): return self.tieBreak(key, prop1, prop2) |
152 | else: return (prop2, prop1) | |
156 | else: return (prop1, prop2) | |
153 | 157 | |
154 | 158 | def tieBreakable(self, key): |
155 | 159 | return False |
163 | 167 | for k, v in diff.getPropertiesDiff().items(): |
164 | 168 | attribute = self.__getAttribute(k) |
165 | 169 | prop_update = self.propertyTieBreaker(attribute, *v) |
166 | if isinstance(prop_update, tuple): | |
170 | ||
171 | if (not isinstance(prop_update, tuple) or | |
172 | CONF.getMergeStrategy()): | |
173 | # if there's a strategy set by the user, apply it | |
174 | if isinstance(prop_update, tuple): | |
175 | prop_update = MergeSolver( | |
176 | CONF.getMergeStrategy() | |
177 | ).solve(prop_update[0], prop_update[1]) | |
178 | ||
179 | setattr(self, attribute, prop_update) | |
180 | else: | |
167 | 181 | conflict = True |
168 | else: | |
169 | setattr(self, attribute, prop_update) | |
170 | 182 | if conflict: |
171 | 183 | self.updates.append(ConflictUpdate(self, newModelObject)) |
172 | 184 | return conflict |
74 | 74 | EDITCRED = 2048 |
75 | 75 | ADDCRED = 2049 |
76 | 76 | DELCRED = 2050 |
77 | PLUGINSTART = 3000 | |
78 | PLUGINEND = 3001 | |
77 | 79 | |
78 | 80 | __descriptions = { |
79 | 81 | ADDHOST: "ADDHOST", |
114 | 116 | ADDVULN: "ADDVULN", |
115 | 117 | DELVULN: "DELVULN", |
116 | 118 | ADDCRED: "ADDCRED", |
117 | DELCRED: "DELCRED" | |
119 | DELCRED: "DELCRED", | |
120 | PLUGINSTART: "PLUGINSTART", | |
121 | PLUGINEND: "PLUGINEND" | |
118 | 122 | } |
119 | 123 | |
120 | 124 | @staticmethod |
124 | 128 | |
125 | 129 | class ModelController(threading.Thread): |
126 | 130 | |
127 | def __init__(self, security_manager, mappers_manager): | |
131 | def __init__(self, mappers_manager): | |
128 | 132 | threading.Thread.__init__(self) |
129 | 133 | |
130 | self.__sec = security_manager | |
131 | 134 | self.mappers_manager = mappers_manager |
132 | 135 | |
133 | 136 | # set as daemon |
134 | 137 | self.setDaemon(True) |
135 | ||
136 | #TODO: think of a good way to handle cross reference between hosts and | |
137 | #categories | |
138 | self._categories = {} | |
139 | self._categories[CONF.getDefaultCategory()] = [] | |
140 | ||
141 | # dictionary with host ids as key | |
142 | self._hosts = None | |
143 | 138 | |
144 | 139 | # flag to stop daemon thread |
145 | 140 | self._stop = False |
146 | 141 | # locks needed to make model thread-safe |
147 | 142 | self._hosts_lock = threading.RLock() |
143 | ||
144 | # count of plugins sending actions | |
145 | self.active_plugins_count = 0 | |
146 | self.active_plugins_count_lock = threading.RLock() | |
148 | 147 | |
149 | 148 | #TODO: check if it is better using collections.deque |
150 | 149 | # a performance analysis should be done |
241 | 240 | modelactions.EDITNOTE: self.__edit, |
242 | 241 | modelactions.EDITCRED: self.__edit, |
243 | 242 | modelactions.ADDCRED: self.__add, |
244 | modelactions.DELCRED: self.__del | |
243 | modelactions.DELCRED: self.__del, | |
244 | # Plugin states | |
245 | modelactions.PLUGINSTART: self._pluginStart, | |
246 | modelactions.PLUGINEND: self._pluginEnd | |
245 | 247 | } |
246 | 248 | |
247 | 249 | def run(self): |
349 | 351 | """ |
350 | 352 | while True: |
351 | 353 | # check if thread must finish |
352 | if self._stop: | |
353 | return | |
354 | # no plugin should be active to stop the controller | |
355 | if self._stop and self.active_plugins_count == 0: | |
356 | break | |
354 | 357 | # first we check if there is a sync api request |
355 | 358 | # or if the model is being saved/sync'ed |
356 | 359 | # or if we have pending duplicated hosts that need to be |
384 | 387 | # because if we don't do it, the daemon will be blocked forever |
385 | 388 | pass |
386 | 389 | except Exception: |
387 | getLogger(self).devlog("something strange happened... unhandled exception?") | |
388 | getLogger(self).devlog(traceback.format_exc()) | |
390 | getLogger(self).debug("something strange happened... unhandled exception?") | |
391 | getLogger(self).debug(traceback.format_exc()) | |
389 | 392 | |
390 | 393 | def sync_lock(self): |
391 | 394 | self._sync_api_request = True |
652 | 655 | res = True |
653 | 656 | return res |
654 | 657 | |
658 | def addPluginStart(self): | |
659 | self.__addPendingAction(modelactions.PLUGINSTART) | |
660 | ||
661 | def addPluginEnd(self): | |
662 | self.__addPendingAction(modelactions.PLUGINEND) | |
663 | ||
664 | def _pluginStart(self): | |
665 | self.active_plugins_count_lock.acquire() | |
666 | getLogger(self).info("Plugin Started") | |
667 | self.active_plugins_count += 1 | |
668 | self.active_plugins_count_lock.release() | |
669 | ||
670 | def _pluginEnd(self): | |
671 | self.active_plugins_count_lock.acquire() | |
672 | getLogger(self).info("Plugin Ended") | |
673 | self.active_plugins_count -= 1 | |
674 | self.active_plugins_count_lock.release() | |
675 | ||
655 | 676 | def addVulnToInterfaceASYNC(self, host, intId, newVuln): |
656 | 677 | self.__addPendingAction(modelactions.ADDVULNINT, newVuln, intId) |
657 | 678 | |
887 | 908 | hosts_mapper = self.mappers_manager.getMapper(model.hosts.Host.__name__) |
888 | 909 | return hosts_mapper.find(name) |
889 | 910 | |
890 | def getHostsCount(self): | |
891 | return len(self._hosts) | |
892 | ||
893 | 911 | def getAllHosts(self): |
894 | 912 | hosts = self.mappers_manager.getMapper( |
895 | 913 | model.hosts.Host.__name__).getAll() |
916 | 934 | |
917 | 935 | for hostname in intr.getHostnames(): |
918 | 936 | self.treeWordsTries.addWord(hostname) |
919 | ||
920 | def checkPermissions(self, op): | |
921 | ## In order to use the decorator passPermissionsOrRaise | |
922 | ## The client should implement checkPermissions method. | |
923 | self.__sec.checkPermissions(op) |
3 | 3 | See the file 'doc/LICENSE' for the license information |
4 | 4 | |
5 | 5 | ''' |
6 | ||
7 | ||
6 | 8 | class ModelObjectDiff(object): |
7 | 9 | def __init__(self, objLeft, objRight): |
8 | 10 | if not isinstance(objLeft, objRight.__class__): |
11 | 13 | self.obj1, self.obj2 = objLeft, objRight |
12 | 14 | |
13 | 15 | self.conflicting = [] |
14 | self.conflicting.extend(self.getPropertiesDiff()) | |
15 | ||
16 | self.conflicting.extend(self.getPropertiesDiff()) | |
17 | ||
16 | 18 | self.only_in_obj1 = {} |
17 | 19 | self.only_in_obj2 = {} |
18 | 20 | |
19 | 21 | def existDiff(self): |
20 | 22 | return bool(self.conflicting) or bool(self.only_in_obj1) or bool(self.only_in_obj2) |
21 | 23 | |
22 | ||
23 | def getPropertiesDiff(self): | |
24 | def getPropertiesDiff(self): | |
24 | 25 | prop_diff = {} |
25 | 26 | for attrdesc, attrname in self.obj1.publicattrsrefs.items(): |
26 | 27 | info = lambda attr_ref: attr_ref() if callable(attr_ref) else attr_ref |
27 | prop1 = info(self.obj1.__getattribute__(attrname)) | |
28 | prop1 = info(self.obj1.__getattribute__(attrname)) | |
28 | 29 | prop2 = info(self.obj2.__getattribute__(attrname)) |
29 | 30 | if prop1 != prop2: |
30 | 31 | prop_diff[attrdesc] = (prop1, prop2) |
35 | 36 | """ Polymorphic method to get the differences between the list of objects on a ModelObject. |
36 | 37 | Pass the ObjectDiff class, the unbound method to get all the objects and the one to get one by ID""" |
37 | 38 | |
38 | ||
39 | only_in_obj1 = [ i for i in getAllFunc(self.obj1) if not i in getAllFunc(self.obj2) ] | |
40 | only_in_obj2 = [ i for i in getAllFunc(self.obj2) if not i in getAllFunc(self.obj1) ] | |
41 | ||
42 | ||
43 | ||
44 | ||
45 | ||
39 | only_in_obj1 = [i for i in getAllFunc(self.obj1) if not i in getAllFunc(self.obj2)] | |
40 | only_in_obj2 = [i for i in getAllFunc(self.obj2) if not i in getAllFunc(self.obj1)] | |
46 | 41 | |
47 | 42 | return (only_in_obj1, only_in_obj2) |
48 | 43 | |
49 | 44 | def getDifferencesIn(self, getAllFunc): |
50 | 45 | """ Polymorphic method to get the differences between the list of objects on a ModelObject. |
51 | 46 | Pass the ObjectDiff class, the unbound method to get all the objects and the one to get one by ID""" |
52 | ||
53 | ||
54 | only_in_obj1 = [ i for i in getAllFunc(self.obj1) if not i in getAllFunc(self.obj2) ] | |
55 | only_in_obj2 = [ i for i in getAllFunc(self.obj2) if not i in getAllFunc(self.obj1) ] | |
47 | only_in_obj1 = [i for i in getAllFunc(self.obj1) if not i in getAllFunc(self.obj2)] | |
48 | only_in_obj2 = [i for i in getAllFunc(self.obj2) if not i in getAllFunc(self.obj1)] | |
56 | 49 | |
57 | 50 | return only_in_obj1, only_in_obj2 |
58 | 51 | |
59 | class HostDiff(ModelObjectDiff): | |
60 | """A container for all the differences between two hosts""" | |
61 | def __init__(self, h1, h2): | |
62 | super(HostDiff, self).__init__(h1, h2) | |
63 | 52 | |
64 | obj1_only, obj2_only = self.getDifferencesIn(h1.__class__.getAllInterfaces) | |
65 | if len(obj1_only): | |
66 | self.only_in_obj1.update({"Interfaces": obj1_only}) | |
67 | if len(obj2_only): | |
68 | self.only_in_obj2.update({"Interfaces": obj2_only}) | |
53 | class MergeStrategy(object): | |
54 | @staticmethod | |
55 | def solve(old, new): | |
56 | raise NotImplemented("This is an abstract class") | |
69 | 57 | |
70 | obj1_only, obj2_only = self.getDifferencesIn(h1.__class__.getAllServices) | |
71 | if len(obj1_only): | |
72 | self.only_in_obj1.update({"Services": obj1_only}) | |
73 | if len(obj2_only): | |
74 | self.only_in_obj2.update({"Services": obj2_only}) | |
75 | 58 | |
76 | obj1_only, obj2_only = self.getDifferencesIn(h1.__class__.getVulns) | |
77 | if len(obj1_only): | |
78 | self.only_in_obj1.update({"Vulns": obj1_only}) | |
79 | if len(obj2_only): | |
80 | self.only_in_obj2.update({"Vulns": obj2_only}) | |
59 | class MergeKeepNew(MergeStrategy): | |
60 | @staticmethod | |
61 | def solve(old, new): | |
62 | return new | |
81 | 63 | |
82 | obj1_only, obj2_only = self.getDifferencesIn(h1.__class__.getAllApplications) | |
83 | if len(obj1_only): | |
84 | self.only_in_obj1.update({"Apps": obj1_only}) | |
85 | if len(obj2_only): | |
86 | self.only_in_obj2.update({"Apps": obj2_only}) | |
87 | 64 | |
88 | class InterfaceDiff(ModelObjectDiff): | |
89 | pass | |
65 | class MergeKeepOld(MergeStrategy): | |
66 | @staticmethod | |
67 | def solve(old, new): | |
68 | return old | |
90 | 69 | |
70 | ||
71 | class MergeSolver(object): | |
72 | def __init__(self, strategy): | |
73 | if strategy == "new": | |
74 | self.strategy = MergeKeepNew | |
75 | elif strategy == "old": | |
76 | self.strategy = MergeKeepOld | |
77 | else: | |
78 | raise Exception("Invalid strategy to resolve merges") | |
79 | ||
80 | def solve(self, old, new): | |
81 | return self.strategy.solve(old, new) |
710 | 710 | it returns None otherwise |
711 | 711 | """ |
712 | 712 | return self._getValueByID("_services", name) |
713 | ||
714 | ||
715 | def __checkDiffObjectClasses(objLeft, objRight, checkClass): | |
716 | ||
717 | if not isinstance(objLeft, checkClass) or not isinstance(objRight, checkClass) : | |
718 | raise Exception("Cannot diff objects. Class mismatch!. objLeft class (%s) - objRight class (%s)" | |
719 | % (objLeft.__class__.__name__, objRight.__class__.__name__)) | |
720 | ||
721 | def HostDiff(objLeft, objRight): | |
722 | """ | |
723 | ||
724 | """ | |
725 | __checkDiffObjectClasses(objLeft, objRight, Host) | |
726 | ||
727 | ||
728 | def InterfaceDiff(objLeft, objRight): | |
729 | __checkDiffObjectClasses(objLeft, objRight, Interface) | |
730 | pass | |
731 | ||
732 | def ServiceDiff(objLeft, objRight): | |
733 | __checkDiffObjectClasses(objLeft, objRight, Service) | |
734 | pass | |
735 | ||
736 | def HostApplicationDiff(objLeft, objRight): | |
737 | __checkDiffObjectClasses(objLeft, objRight, HostApplication) | |
738 | pass | |
739 | ||
740 | __diff_dispatch_table = { | |
741 | Host: HostDiff, | |
742 | HostApplication: HostApplicationDiff, | |
743 | Interface: InterfaceDiff, | |
744 | Service: ServiceDiff | |
745 | } | |
746 | ||
747 | ||
748 | def ModelObjectDiff(objLeft, objRight): | |
749 | """ | |
750 | This is like a dispatcher. Based on the object class it call the corresponding | |
751 | diff function. If a diff function for the class is not pressent it raises a | |
752 | NotImplemented Exception | |
753 | """ | |
754 | ||
755 | if not isinstance(objLeft, objRight.__class__): | |
756 | raise Exception("Cannot compare objects of different classes. objLeft (%s) vs objRight (%s)" | |
757 | % (objLeft.__class__.__name__, objRight.__class__.__name__)) | |
758 | ||
759 | global __diff_dispatch_table | |
760 | diff_func = __diff_dispatch_table.get(objLeft.__class__, None) | |
761 | ||
762 | if diff_func is None: | |
763 | raise NotImplemented("Diff function for %s does not exist" % objLeft.__class__.__name__) | |
764 | ||
765 | return diff_func(objLeft, objRight) |
8 | 8 | import os |
9 | 9 | import shutil |
10 | 10 | import mockito |
11 | import restkit | |
11 | 12 | import threading |
12 | 13 | from urlparse import urlparse |
13 | 14 | import traceback |
223 | 224 | super(CouchDbConnector, self).__init__(type=DBTYPE.COUCHDB) |
224 | 225 | self.db = db |
225 | 226 | self.saves_counter = 0 |
226 | #self.seq_num = seq_num | |
227 | 227 | self.mutex = threading.Lock() |
228 | vmanager = ViewsManager() | |
229 | vmanager.addViews(self.db) | |
230 | 228 | self._docs = {} |
231 | self._compactDatabase() | |
229 | try: | |
230 | vmanager = ViewsManager() | |
231 | vmanager.addViews(self.db) | |
232 | self._compactDatabase() | |
233 | except restkit.Unauthorized: | |
234 | getLogger(self).warn( | |
235 | "You're not authorized to upload views to this database") | |
232 | 236 | self.seq_num = self.db.info()['update_seq'] |
233 | # self._tree = self._createTree(self.getDocs()) | |
234 | 237 | |
235 | 238 | def getDocs(self): |
236 | 239 | if len(self._docs.keys()) == 0: |
377 | 380 | |
378 | 381 | #@trap_timeout |
379 | 382 | def _compactDatabase(self): |
380 | self.db.compact() | |
383 | try: | |
384 | self.db.compact() | |
385 | except: | |
386 | getLogger(self).warn( | |
387 | "You're not authorized to compact this database") | |
381 | 388 | |
382 | 389 | |
383 | 390 | class AbstractPersistenceManager(object): |
540 | 547 | def pushReports(self): |
541 | 548 | vmanager = ViewsManager() |
542 | 549 | reports = os.path.join(os.getcwd(), "views", "reports") |
543 | workspace = self.__serv.get_or_create_db("reports") | |
544 | vmanager.addView(reports, workspace) | |
550 | try: | |
551 | workspace = self.__serv.get_or_create_db("reports") | |
552 | vmanager.addView(reports, workspace) | |
553 | except: | |
554 | getLogger(self).warn( | |
555 | "Reports database couldn't be uploaded. You need to be an admin to do it") | |
545 | 556 | return self.__uri + "/reports/_design/reports/index.html" |
546 | 557 | |
547 | 558 | def lostConnectionResolv(self): |
19 | 19 | import traceback |
20 | 20 | import model.common |
21 | 21 | import errno |
22 | from model.common import factory, ModelObjectVuln, ModelObjectVulnWeb, ModelObjectNote, ModelObjectCred | |
22 | from model.common import ( | |
23 | factory, ModelObjectVuln, ModelObjectVulnWeb, | |
24 | ModelObjectNote, ModelObjectCred) | |
23 | 25 | from model.hosts import Host, Interface, Service |
24 | 26 | |
25 | 27 | from model.commands_history import CommandRunInformation |
52 | 54 | |
53 | 55 | |
54 | 56 | class modelactions: |
55 | ADDHOST = 2000 | |
56 | CADDHOST = 2001 | |
57 | ADDINTERFACE = 2002 | |
58 | CADDINTERFACE = 2003 | |
59 | ADDSERVICEINT = 2004 | |
60 | ADDSERVICEAPP = 2005 | |
61 | CADDSERVICEINT = 2006 | |
62 | CADDSERVICEAPP = 2007 | |
63 | CADDSERVICEHOST = 2008 | |
64 | ADDAPPLICATION = 2009 | |
65 | CADDAPPLICATION = 2010 | |
66 | ADDVULNINT = 2013 | |
67 | CADDVULNINT = 2014 | |
68 | ADDVULNAPP = 2015 | |
69 | CADDVULNAPP = 2016 | |
70 | ADDVULNHOST = 2017 | |
71 | CADDVULNHOST = 2018 | |
72 | ADDVULNSRV = 2019 | |
73 | CADDVULNSRV = 2020 | |
74 | ADDNOTEINT = 2021 | |
75 | CADDNOTEINT = 2022 | |
76 | ADDNOTEAPP = 2023 | |
77 | CADDNOTEAPP = 2024 | |
78 | ADDNOTEHOST = 2025 | |
79 | CADDNOTEHOST = 2026 | |
80 | ADDNOTESRV = 2027 | |
81 | CADDNOTESRV = 2028 | |
82 | CADDNOTEVULN = 2030 | |
83 | CADDNOTEVULN = 2031 | |
84 | LOG = 2032 | |
85 | DEVLOG = 2033 | |
86 | DELSERVICEINT = 2034 | |
87 | ADDCREDSRV = 2035 | |
88 | CADDCREDSRV = 2036 | |
89 | ADDVULNWEBSRV = 2037 | |
90 | CADDVULNWEBSRV = 2038 | |
91 | ADDNOTENOTE = 2039 | |
92 | CADDNOTENOTE = 2039 | |
57 | ADDHOST = 2000 | |
58 | CADDHOST = 2001 | |
59 | ADDINTERFACE = 2002 | |
60 | CADDINTERFACE = 2003 | |
61 | ADDSERVICEINT = 2004 | |
62 | ADDSERVICEAPP = 2005 | |
63 | CADDSERVICEINT = 2006 | |
64 | CADDSERVICEAPP = 2007 | |
65 | CADDSERVICEHOST = 2008 | |
66 | ADDAPPLICATION = 2009 | |
67 | CADDAPPLICATION = 2010 | |
68 | ADDVULNINT = 2013 | |
69 | CADDVULNINT = 2014 | |
70 | ADDVULNAPP = 2015 | |
71 | CADDVULNAPP = 2016 | |
72 | ADDVULNHOST = 2017 | |
73 | CADDVULNHOST = 2018 | |
74 | ADDVULNSRV = 2019 | |
75 | CADDVULNSRV = 2020 | |
76 | ADDNOTEINT = 2021 | |
77 | CADDNOTEINT = 2022 | |
78 | ADDNOTEAPP = 2023 | |
79 | CADDNOTEAPP = 2024 | |
80 | ADDNOTEHOST = 2025 | |
81 | CADDNOTEHOST = 2026 | |
82 | ADDNOTESRV = 2027 | |
83 | CADDNOTESRV = 2028 | |
84 | CADDNOTEVULN = 2030 | |
85 | CADDNOTEVULN = 2031 | |
86 | LOG = 2032 | |
87 | DEVLOG = 2033 | |
88 | DELSERVICEINT = 2034 | |
89 | ADDCREDSRV = 2035 | |
90 | CADDCREDSRV = 2036 | |
91 | ADDVULNWEBSRV = 2037 | |
92 | CADDVULNWEBSRV = 2038 | |
93 | ADDNOTENOTE = 2039 | |
94 | CADDNOTENOTE = 2039 | |
95 | PLUGINSTART = 3000 | |
96 | PLUGINEND = 3001 | |
93 | 97 | |
94 | 98 | __descriptions = { |
95 | ADDHOST : "ADDHOST", | |
96 | CADDHOST : "CADDHOST", | |
97 | ADDINTERFACE : "ADDINTERFACE", | |
98 | CADDINTERFACE : "CADDINTERFACE", | |
99 | ADDSERVICEINT : "ADDSERVICEINT", | |
100 | ADDSERVICEAPP : "ADDSERVICEAPP", | |
101 | CADDSERVICEINT : "CADDSERVICEINT", | |
102 | CADDSERVICEAPP : "CADDSERVICEAPP", | |
103 | CADDSERVICEHOST : "CADDSERVICEHOST", | |
104 | ADDAPPLICATION : "ADDAPPLICATION", | |
105 | CADDAPPLICATION : "CADDAPPLICATION", | |
106 | ADDVULNINT : "ADDVULNINT", | |
107 | CADDVULNINT : "CADDVULNINT", | |
108 | ADDVULNAPP : "ADDVULNAPP", | |
109 | CADDVULNAPP : "CADDVULNAPP", | |
110 | ADDVULNHOST : "ADDVULNHOST", | |
111 | CADDVULNHOST : "CADDVULNHOST", | |
112 | ADDVULNSRV : "ADDVULNSRV", | |
113 | CADDVULNSRV : "CADDVULNSRV", | |
114 | LOG : "LOG", | |
115 | DEVLOG : "DEVLOG", | |
116 | DELSERVICEINT : "DELSERVICEINT", | |
117 | ADDCREDSRV : "ADDCREDINT", | |
118 | ADDVULNWEBSRV : "ADDVULNWEBSRV", | |
119 | CADDVULNWEBSRV : "CADDVULNWEBSRV", | |
120 | ADDNOTENOTE : "ADDNOTENOTE", | |
121 | CADDNOTENOTE : "CADDNOTENOTE", | |
99 | ADDHOST: "ADDHOST", | |
100 | CADDHOST: "CADDHOST", | |
101 | ADDINTERFACE: "ADDINTERFACE", | |
102 | CADDINTERFACE: "CADDINTERFACE", | |
103 | ADDSERVICEINT: "ADDSERVICEINT", | |
104 | ADDSERVICEAPP: "ADDSERVICEAPP", | |
105 | CADDSERVICEINT: "CADDSERVICEINT", | |
106 | CADDSERVICEAPP: "CADDSERVICEAPP", | |
107 | CADDSERVICEHOST: "CADDSERVICEHOST", | |
108 | ADDAPPLICATION: "ADDAPPLICATION", | |
109 | CADDAPPLICATION: "CADDAPPLICATION", | |
110 | ADDVULNINT: "ADDVULNINT", | |
111 | CADDVULNINT: "CADDVULNINT", | |
112 | ADDVULNAPP: "ADDVULNAPP", | |
113 | CADDVULNAPP: "CADDVULNAPP", | |
114 | ADDVULNHOST: "ADDVULNHOST", | |
115 | CADDVULNHOST: "CADDVULNHOST", | |
116 | ADDVULNSRV: "ADDVULNSRV", | |
117 | CADDVULNSRV: "CADDVULNSRV", | |
118 | LOG: "LOG", | |
119 | DEVLOG: "DEVLOG", | |
120 | DELSERVICEINT: "DELSERVICEINT", | |
121 | ADDCREDSRV: "ADDCREDINT", | |
122 | ADDVULNWEBSRV: "ADDVULNWEBSRV", | |
123 | CADDVULNWEBSRV: "CADDVULNWEBSRV", | |
124 | ADDNOTENOTE: "ADDNOTENOTE", | |
125 | CADDNOTENOTE: "CADDNOTENOTE", | |
126 | PLUGINSTART: "PLUGINSTART", | |
127 | PLUGINEND: "PLUGINEND" | |
122 | 128 | } |
123 | 129 | |
124 | 130 | @staticmethod |
207 | 213 | output_queue.put(output) |
208 | 214 | output_queue.put(None) |
209 | 215 | output_queue.join() |
216 | ||
217 | self._processAction(modelactions.PLUGINSTART, []) | |
210 | 218 | |
211 | 219 | #model.api.devlog("Core: queue size '%s'" % new_elem_queue.qsize()) |
212 | 220 | while True: |
232 | 240 | model.api.devlog("PluginController.onCommandFinished - new_elem_queue Exception- something strange happened... unhandled exception?") |
233 | 241 | model.api.devlog(traceback.format_exc()) |
234 | 242 | break |
243 | self._processAction(modelactions.PLUGINEND, []) | |
235 | 244 | |
236 | 245 | def _processAction(self, action, parameters): |
237 | 246 | """ |
285 | 294 | #LOG |
286 | 295 | modelactions.LOG : model.api.log, |
287 | 296 | modelactions.DEVLOG : model.api.devlog, |
297 | # Plugin state | |
298 | modelactions.PLUGINSTART: model.api.pluginStart, | |
299 | modelactions.PLUGINEND: model.api.pluginEnd | |
288 | 300 | } |
289 | 301 | |
290 | 302 | def updatePluginSettings(self, plugin_id, new_settings): |
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 | ||
10 | import imp | |
11 | import os | |
12 | import re | |
13 | import sys | |
14 | import traceback | |
15 | ||
16 | import plugins.core | |
17 | from config.configuration import getInstanceConfiguration | |
18 | from utils.logs import getLogger | |
19 | ||
20 | CONF = getInstanceConfiguration() | |
21 | ||
22 | ||
23 | class PluginManager(object): | |
24 | def __init__(self, plugin_repo_path, mapper_manager): | |
25 | self._controllers = {} | |
26 | self._plugin_modules = {} | |
27 | self._loadPlugins(plugin_repo_path) | |
28 | self._mapper_manager = mapper_manager | |
29 | ||
30 | self._plugin_settings = {} | |
31 | self._loadSettings() | |
32 | ||
33 | def createController(self, id): | |
34 | """ | |
35 | Creates a new plugin controller and adds it into the controllers list. | |
36 | """ | |
37 | plugs = self._instancePlugins() | |
38 | new_controller = plugins.core.PluginController( | |
39 | id, plugs, self._mapper_manager) | |
40 | self._controllers[new_controller.id] = new_controller | |
41 | self.updateSettings(self._plugin_settings) | |
42 | return new_controller | |
43 | ||
44 | def _loadSettings(self): | |
45 | _plugin_settings = CONF.getPluginSettings() | |
46 | if _plugin_settings: | |
47 | ||
48 | self._plugin_settings = _plugin_settings | |
49 | ||
50 | activep = self._instancePlugins() | |
51 | for plugin_id, plugin in activep.iteritems(): | |
52 | ||
53 | if plugin_id not in _plugin_settings: | |
54 | self._plugin_settings[plugin_id] = { | |
55 | "name": plugin.name, | |
56 | "description": plugin.description, | |
57 | "version": plugin.version, | |
58 | "plugin_version": plugin.plugin_version, | |
59 | "settings": dict(plugin.getSettings()) | |
60 | } | |
61 | ||
62 | dplugins = [] | |
63 | for k, v in self._plugin_settings.iteritems(): | |
64 | if k not in activep: | |
65 | dplugins.append(k) | |
66 | ||
67 | for d in dplugins: | |
68 | del self._plugin_settings[d] | |
69 | ||
70 | CONF.setPluginSettings(self._plugin_settings) | |
71 | CONF.saveConfig() | |
72 | ||
73 | def getSettings(self): | |
74 | return self._plugin_settings | |
75 | ||
76 | def updateSettings(self, settings): | |
77 | self._plugin_settings = settings | |
78 | CONF.setPluginSettings(settings) | |
79 | CONF.saveConfig() | |
80 | for plugin_id, params in settings.iteritems(): | |
81 | new_settings = params["settings"] | |
82 | for c_id, c_instance in self._controllers.iteritems(): | |
83 | c_instance.updatePluginSettings(plugin_id, new_settings) | |
84 | ||
85 | def _instancePlugins(self): | |
86 | plugins = {} | |
87 | for module in self._plugin_modules.itervalues(): | |
88 | new_plugin = module.createPlugin() | |
89 | self._verifyPlugin(new_plugin) | |
90 | plugins[new_plugin.id] = new_plugin | |
91 | return plugins | |
92 | ||
93 | def _loadPlugins(self, plugin_repo_path): | |
94 | """ | |
95 | Finds and load all the plugins that are | |
96 | available in the plugin_repo_path. | |
97 | """ | |
98 | try: | |
99 | os.stat(plugin_repo_path) | |
100 | except OSError: | |
101 | ||
102 | pass | |
103 | ||
104 | sys.path.append(plugin_repo_path) | |
105 | ||
106 | dir_name_regexp = re.compile(r"^[\d\w\-\_]+$") | |
107 | for name in os.listdir(plugin_repo_path): | |
108 | if dir_name_regexp.match(name): | |
109 | try: | |
110 | module_path = os.path.join(plugin_repo_path, name) | |
111 | sys.path.append(module_path) | |
112 | module_filename = os.path.join(module_path, "plugin.py") | |
113 | self._plugin_modules[name] = imp.load_source( | |
114 | name, module_filename) | |
115 | except Exception as e: | |
116 | msg = "An error ocurred while loading plugin %s.\n%s" % ( | |
117 | module_filename, traceback.format_exc()) | |
118 | getLogger(self).debug(msg) | |
119 | getLogger(self).warn(e) | |
120 | else: | |
121 | pass | |
122 | ||
123 | def getPlugins(self): | |
124 | return self._instancePlugins() | |
125 | ||
126 | def _updatePluginSettings(self, new_plugin_id): | |
127 | pass | |
128 | ||
129 | def _verifyPlugin(self, new_plugin): | |
130 | """ | |
131 | Generic method that decides is a plugin is valid | |
132 | based on a predefined set of checks. | |
133 | """ | |
134 | try: | |
135 | assert(new_plugin.id is not None) | |
136 | assert(new_plugin.version is not None) | |
137 | assert(new_plugin.name is not None) | |
138 | assert(new_plugin.framework_version is not None) | |
139 | except (AssertionError, KeyError): | |
140 | ||
141 | return False | |
142 | return True |
40 | 40 | "-e":"execute model directly", |
41 | 41 | "-o":"output command", |
42 | 42 | } |
43 | ||
43 | ||
44 | 44 | |
45 | 45 | def parseOutputString(self, output, debug = False): |
46 | 46 | pass |
47 | ||
48 | ||
47 | ||
48 | ||
49 | 49 | file_arg_re = re.compile(r"^.*(-o\s*[^\s]+).*$") |
50 | 50 | def processCommandString(self, username, current_path, command_string): |
51 | 51 | """ |
55 | 55 | self._file_output_path=os.path.join(self.data_path,"f_output-%s.txt" % random.uniform(1,10)) |
56 | 56 | |
57 | 57 | parser = argparse.ArgumentParser() |
58 | ||
58 | ||
59 | 59 | parser.add_argument('-e') |
60 | 60 | parser.add_argument('-f') |
61 | 61 | parser.add_argument('-o') |
62 | ||
63 | if arg_match is None: | |
64 | final= re.sub(r"(^.*?fplugin)", | |
62 | ||
63 | #NO support -h --help style parameters. | |
64 | #Need "" in all parameter. Example script.py -p "parameter1 parameter2" | |
65 | parser.add_argument('-p') | |
66 | ||
67 | if arg_match is None: | |
68 | final = re.sub(r"(^.*?fplugin)", | |
65 | 69 | r"\1 -o %s" % self._file_output_path, |
66 | command_string) | |
70 | command_string) | |
67 | 71 | else: |
68 | final= re.sub(arg_match.group(1), | |
72 | final = re.sub(arg_match.group(1), | |
69 | 73 | r"-o %s" % self._file_output_path, |
70 | 74 | command_string) |
71 | ||
72 | ||
73 | cmd=shlex.split(re.sub(r'\-h|\-\-help', r'', final)) | |
75 | ||
76 | ||
77 | cmd = shlex.split(re.sub(r'\-h|\-\-help', r'', final)) | |
74 | 78 | try: |
75 | 79 | self.args, unknown = parser.parse_known_args(cmd) |
76 | 80 | except SystemExit: |
77 | 81 | pass |
78 | ||
79 | codeEx="" | |
82 | ||
83 | codeEx = "" | |
80 | 84 | if self.args.e: |
81 | codeEx=self.args.e | |
85 | codeEx = self.args.e | |
82 | 86 | elif self.args.f: |
83 | 87 | with open(current_path + "/" + self.args.f) as f: |
84 | 88 | codeEx = f.read() |
87 | 91 | if codeEx: |
88 | 92 | buffer = StringIO() |
89 | 93 | sys.stdout = buffer |
90 | ||
94 | ||
91 | 95 | try: |
92 | exec(codeEx) | |
96 | locales = locals() | |
97 | locales.update({'script_parameters' : self.args.p}) | |
98 | exec(codeEx, globals(), locales) | |
99 | ||
93 | 100 | except Exception: |
94 | 101 | api.devlog("[Error] - Faraday plugin") |
95 | 102 | api.devlog(traceback.format_exc()) |
96 | ||
103 | ||
97 | 104 | sys.stdout = sys.__stdout__ |
98 | 105 | |
99 | 106 | try: |
102 | 109 | f.close() |
103 | 110 | except: |
104 | 111 | api.devlog ("[Faraday] Can't save faraday plugin output file") |
105 | return | |
112 | return | |
106 | 113 | |
107 | 114 | print buffer.getvalue() |
108 | 115 | |
115 | 122 | |
116 | 123 | def createPlugin(): |
117 | 124 | return FPlugin() |
118 |
0 | 0 | #!/usr/bin/env python |
1 | 1 | # -*- coding: utf-8 -*- |
2 | ||
3 | 2 | ''' |
4 | 3 | Faraday Penetration Test IDE |
5 | 4 | Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) |
6 | 5 | See the file 'doc/LICENSE' for the license information |
7 | ||
8 | 6 | ''' |
7 | ||
9 | 8 | from __future__ import with_statement |
10 | 9 | from plugins import core |
11 | 10 | from model import api |
14 | 13 | import pprint |
15 | 14 | import sys |
16 | 15 | import random |
16 | import HTMLParser | |
17 | 17 | |
18 | 18 | try: |
19 | 19 | import xml.etree.cElementTree as ET |
22 | 22 | except ImportError: |
23 | 23 | import xml.etree.ElementTree as ET |
24 | 24 | ETREE_VERSION = ET.VERSION |
25 | ||
25 | ||
26 | 26 | ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")] |
27 | 27 | |
28 | 28 | current_path = os.path.abspath(os.getcwd()) |
36 | 36 | __email__ = "[email protected]" |
37 | 37 | __status__ = "Development" |
38 | 38 | |
39 | ||
40 | ||
41 | ||
42 | ||
43 | 39 | class NiktoXmlParser(object): |
44 | 40 | """ |
45 | 41 | The objective of this class is to parse an xml file generated by the nikto tool. |
51 | 47 | @param nikto_xml_filepath A proper xml generated by nikto |
52 | 48 | """ |
53 | 49 | def __init__(self, xml_output): |
50 | ||
54 | 51 | tree = self.parse_xml(xml_output) |
55 | ||
52 | ||
56 | 53 | if tree: |
57 | 54 | self.hosts = [host for host in self.get_hosts(tree)] |
58 | 55 | else: |
59 | 56 | self.hosts = [] |
60 | ||
61 | 57 | |
62 | 58 | def parse_xml(self, xml_output): |
63 | 59 | """ |
80 | 76 | """ |
81 | 77 | @return items A list of Host instances |
82 | 78 | """ |
83 | for host_node in tree.findall('scandetails'): | |
79 | for host_node in tree.find('niktoscan').findall('scandetails'): | |
84 | 80 | yield Host(host_node) |
85 | ||
86 | ||
87 | ||
88 | ||
81 | ||
82 | ||
89 | 83 | def get_attrib_from_subnode(xml_node, subnode_xpath_expr, attrib_name): |
90 | 84 | """ |
91 | 85 | Finds a subnode in the item node and the retrieves a value from it |
94 | 88 | """ |
95 | 89 | global ETREE_VERSION |
96 | 90 | node = None |
97 | ||
91 | ||
98 | 92 | if ETREE_VERSION[0] <= 1 and ETREE_VERSION[1] < 3: |
99 | ||
93 | ||
100 | 94 | match_obj = re.search("([^\@]+?)\[\@([^=]*?)=\'([^\']*?)\'",subnode_xpath_expr) |
101 | 95 | if match_obj is not None: |
96 | ||
102 | 97 | node_to_find = match_obj.group(1) |
103 | 98 | xpath_attrib = match_obj.group(2) |
104 | 99 | xpath_value = match_obj.group(3) |
105 | 100 | for node_found in xml_node.findall(node_to_find): |
101 | ||
106 | 102 | if node_found.attrib[xpath_attrib] == xpath_value: |
107 | 103 | node = node_found |
108 | 104 | break |
118 | 114 | return None |
119 | 115 | |
120 | 116 | |
121 | ||
122 | ||
123 | ||
124 | 117 | class Item(object): |
125 | 118 | """ |
126 | 119 | An abstract representation of a Item |
132 | 125 | @param item_node A item_node taken from an nikto xml tree |
133 | 126 | """ |
134 | 127 | def __init__(self, item_node): |
128 | ||
135 | 129 | self.node = item_node |
136 | 130 | |
131 | self.osvdbid = ["OSVDB-ID: " + self.node.get('osvdbid')] if self.node.get('osvdbid') != "0" else [] | |
132 | ||
133 | self.namelink = self.get_text_from_subnode('namelink') | |
134 | self.iplink = self.get_text_from_subnode('iplink') | |
135 | ||
137 | 136 | self.id_nikto = self.node.get('id') |
138 | ||
139 | self.osvdbid = ["BID-"+self.node.get('osvdbid')] if self.node.get('osvdbid') != "0" else [] | |
140 | 137 | self.osvdblink = self.node.get('osvdbidlink') |
141 | 138 | self.method = self.node.get('method') |
142 | self.desc = self.get_text_from_subnode('description') | |
143 | self.uri = self.get_text_from_subnode('uri') | |
144 | self.namelink = self.get_text_from_subnode('namelink') | |
145 | self.iplink = self.get_text_from_subnode('iplink') | |
146 | ||
147 | ||
139 | ||
140 | self.uri = self.get_uri() | |
141 | self.desc = self.get_desc() | |
142 | self.params = self.get_params(self.uri) | |
143 | ||
144 | def get_uri(self): | |
145 | ||
146 | try: | |
147 | ||
148 | uri = self.get_text_from_subnode('uri') | |
149 | h = HTMLParser.HTMLParser() | |
150 | return h.unescape(uri) | |
151 | ||
152 | except Exception as e: | |
153 | return uri | |
154 | ||
155 | def get_desc(self): | |
156 | ||
157 | desc = self.get_text_from_subnode('description') | |
158 | ||
159 | try: | |
160 | ||
161 | uri_present = desc.split(': ', 1)[0] | |
162 | h = HTMLParser.HTMLParser() | |
163 | if uri_present == h.unescape(self.uri): | |
164 | ||
165 | name = desc.split(': ', 1)[1] | |
166 | if name is not None and name != '': | |
167 | return name | |
168 | ||
169 | return desc | |
170 | ||
171 | except Exception as e: | |
172 | return desc | |
173 | ||
174 | def get_params(self, uri): | |
175 | ||
176 | try: | |
177 | params = [i.split("=")[0] for i in uri.split('?')[1].split('&')] | |
178 | except Exception as e: | |
179 | params = '' | |
180 | ||
181 | return params | |
182 | ||
148 | 183 | def get_text_from_subnode(self, subnode_xpath_expr): |
149 | 184 | """ |
150 | 185 | Finds a subnode in the host node and the retrieves a value from it. |
174 | 209 | @param host_node A host_node taken from an nmap xml tree |
175 | 210 | """ |
176 | 211 | def __init__(self, host_node): |
212 | ||
177 | 213 | self.node = host_node |
178 | 214 | self.targetip = self.node.get('targetip') |
179 | 215 | self.targethostname = self.node.get('targethostname') |
183 | 219 | self.sitename = self.node.get('sitename') |
184 | 220 | self.siteip = self.node.get('hostheader') |
185 | 221 | self.items = [item for item in self.get_items()] |
186 | ||
222 | ||
187 | 223 | def get_items(self): |
188 | 224 | """ |
189 | 225 | @return items A list of Host instances |
207 | 243 | Example plugin to parse nikto output. |
208 | 244 | """ |
209 | 245 | def __init__(self): |
246 | ||
210 | 247 | core.PluginBase.__init__(self) |
211 | 248 | self.id = "Nikto" |
212 | 249 | self.name = "Nikto XML Output Plugin" |
259 | 296 | global current_path |
260 | 297 | self._output_file_path = os.path.join(self.data_path, |
261 | 298 | "nikto_output-%s.xml" % self._rid) |
262 | ||
263 | ||
264 | 299 | |
265 | 300 | def parseOutputString(self, output, debug = False ): |
266 | 301 | """ |
274 | 309 | parser = NiktoXmlParser(output) |
275 | 310 | |
276 | 311 | for host in parser.hosts: |
277 | ||
278 | ||
279 | ||
312 | ||
280 | 313 | h_id = self.createAndAddHost(host.targetip) |
281 | ||
282 | ||
283 | i_id = self.createAndAddInterface(h_id, host.targetip, ipv4_address=host.targetip,hostname_resolution=host.targethostname) | |
284 | s_id = self.createAndAddServiceToInterface(h_id, i_id, "http", | |
285 | "tcp", | |
286 | ports = [host.port], | |
287 | status = "open") | |
288 | ||
314 | ||
315 | i_id = self.createAndAddInterface( | |
316 | h_id, | |
317 | host.targetip, | |
318 | ipv4_address = host.targetip, | |
319 | hostname_resolution = host.targethostname | |
320 | ) | |
321 | ||
322 | s_id = self.createAndAddServiceToInterface( | |
323 | h_id, | |
324 | i_id, | |
325 | "http", | |
326 | "tcp", | |
327 | ports = [host.port], | |
328 | status = "open" | |
329 | ) | |
330 | ||
289 | 331 | n_id = self.createAndAddNoteToService(h_id,s_id,"website","") |
290 | n2_id = self.createAndAddNoteToNote(h_id,s_id,n_id,host.targethostname,"") | |
291 | ||
332 | ||
333 | n2_id = self.createAndAddNoteToNote( | |
334 | h_id, | |
335 | s_id, | |
336 | n_id, | |
337 | host.targethostname, | |
338 | "" | |
339 | ) | |
340 | ||
292 | 341 | for item in host.items: |
293 | v_id = self.createAndAddVulnWebToService(h_id, s_id, | |
294 | name=item.desc, ref=item.osvdbid, website=host.targethostname, | |
295 | method=item.method, path=item.namelink,query=item.uri) | |
342 | ||
343 | v_id = self.createAndAddVulnWebToService( | |
344 | h_id, | |
345 | s_id, | |
346 | name = item.desc, | |
347 | ref = item.osvdbid, | |
348 | website = host.targethostname, | |
349 | method = item.method, | |
350 | path = item.namelink, | |
351 | query = item.uri, | |
352 | params = ', '.join(item.params) | |
353 | ) | |
354 | ||
296 | 355 | del parser |
297 | 356 | |
298 | 357 | xml_arg_re = re.compile(r"^.*(-output\s*[^\s]+).*$") |
302 | 361 | Adds the -oX parameter to get xml output to the command string that the |
303 | 362 | user has set. |
304 | 363 | """ |
305 | self._output_file_path = os.path.join(self.data_path,"%s_%s_output-%s.xml" % (self.get_ws(), | |
306 | self.id, | |
307 | random.uniform(1,10))) | |
364 | self._output_file_path = os.path.join( | |
365 | self.data_path, | |
366 | "%s_%s_output-%s.xml" % ( | |
367 | self.get_ws(), | |
368 | self.id, | |
369 | random.uniform(1,10) | |
370 | ) | |
371 | ) | |
372 | ||
308 | 373 | arg_match = self.xml_arg_re.match(command_string) |
309 | ||
310 | 374 | |
311 | 375 | if arg_match is None: |
312 | 376 | return re.sub(r"(^.*?nikto(\.pl)?)", |
0 | 0 | #!/usr/bin/env python |
1 | 1 | # -*- coding: utf-8 -*- |
2 | ||
3 | 2 | ''' |
4 | 3 | Faraday Penetration Test IDE |
5 | 4 | Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) |
6 | 5 | See the file 'doc/LICENSE' for the license information |
7 | ||
8 | 6 | ''' |
7 | ||
9 | 8 | from __future__ import with_statement |
10 | 9 | from plugins import core |
11 | 10 | from model import api |
14 | 13 | import sys |
15 | 14 | |
16 | 15 | try: |
16 | ||
17 | 17 | import xml.etree.cElementTree as ET |
18 | 18 | import xml.etree.ElementTree as ET_ORIG |
19 | 19 | ETREE_VERSION = ET_ORIG.VERSION |
21 | 21 | import xml.etree.ElementTree as ET |
22 | 22 | ETREE_VERSION = ET.VERSION |
23 | 23 | |
24 | ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")] | |
24 | ETREE_VERSION = [int(i) for i in ETREE_VERSION.split('.')] | |
25 | 25 | |
26 | 26 | current_path = os.path.abspath(os.getcwd()) |
27 | 27 | |
28 | __author__ = "Francisco Amato" | |
29 | __copyright__ = "Copyright (c) 2013, Infobyte LLC" | |
30 | __credits__ = ["Francisco Amato"] | |
31 | __license__ = "" | |
32 | __version__ = "1.0.0" | |
33 | __maintainer__ = "Francisco Amato" | |
34 | __email__ = "[email protected]" | |
35 | __status__ = "Development" | |
36 | ||
28 | __author__ = 'Francisco Amato' | |
29 | __copyright__ = 'Copyright (c) 2013, Infobyte LLC' | |
30 | __credits__ = ['Francisco Amato'] | |
31 | __license__ = '' | |
32 | __version__ = '1.0.0' | |
33 | __maintainer__ = 'Francisco Amato' | |
34 | __email__ = '[email protected]' | |
35 | __status__ = 'Development' | |
36 | ||
37 | ||
38 | def cleaner_unicode(string): | |
39 | if string is not None: | |
40 | return string.encode('ascii', errors='backslashreplace') | |
41 | else: | |
42 | return string | |
37 | 43 | |
38 | 44 | class QualysguardXmlParser(object): |
39 | 45 | """ |
46 | 52 | @param qualysguard_xml_filepath A proper xml generated by qualysguard |
47 | 53 | """ |
48 | 54 | def __init__(self, xml_output): |
55 | ||
49 | 56 | tree = self.parse_xml(xml_output) |
50 | 57 | if tree: |
51 | 58 | self.items = [data for data in self.get_items(tree)] |
52 | 59 | else: |
53 | 60 | self.items = [] |
54 | ||
55 | 61 | |
56 | 62 | def parse_xml(self, xml_output): |
57 | 63 | """ |
65 | 71 | try: |
66 | 72 | tree = ET.fromstring(xml_output) |
67 | 73 | except SyntaxError, err: |
68 | self.devlog("SyntaxError: %s. %s" % (err, xml_output)) | |
74 | self.devlog('SyntaxError: %s. %s' % (err, xml_output)) | |
69 | 75 | return None |
70 | 76 | |
71 | 77 | return tree |
73 | 79 | def get_items(self, tree): |
74 | 80 | """ |
75 | 81 | @return items A list of Host instances |
76 | """ | |
77 | for node in tree.findall("IP"): | |
78 | yield Item(node) | |
79 | ||
80 | ||
82 | """ | |
83 | for node in tree.find('HOST_LIST').findall('HOST'): | |
84 | yield Item(node, tree) | |
81 | 85 | |
82 | 86 | |
83 | 87 | class Item(object): |
84 | 88 | """ |
85 | An abstract representation of a Item | |
86 | ||
87 | ||
89 | An abstract representation of a Item (HOST) | |
88 | 90 | @param item_node A item_node taken from an qualysguard xml tree |
89 | 91 | """ |
90 | def __init__(self, item_node): | |
92 | def __init__(self, item_node, tree): | |
93 | ||
91 | 94 | self.node = item_node |
92 | self.ip = item_node.get('value') | |
93 | self.os = self.get_text_from_subnode("OS") | |
94 | ||
95 | self.vulns=self.getResults(item_node) | |
96 | ||
95 | self.ip = self.get_text_from_subnode('IP') | |
96 | ||
97 | self.os = self.get_text_from_subnode('OPERATING_SYSTEM') | |
98 | self.vulns = self.getResults(tree) | |
99 | ||
97 | 100 | def getResults(self, tree): |
98 | """ | |
99 | :param tree: | |
100 | """ | |
101 | for self.issues in tree.findall("VULNS/CAT"): | |
102 | for v in self.issues.findall("VULN"): | |
103 | yield Results(v,self.issues) | |
104 | ||
101 | ||
102 | glossary = tree.find('GLOSSARY/VULN_DETAILS_LIST') | |
103 | ||
104 | for self.issue in self.node.find('VULN_INFO_LIST'): | |
105 | yield Results(self.issue, glossary) | |
106 | ||
105 | 107 | def get_text_from_subnode(self, subnode_xpath_expr): |
106 | 108 | """ |
107 | 109 | Finds a subnode in the host node and the retrieves a value from it. |
114 | 116 | |
115 | 117 | return None |
116 | 118 | |
119 | ||
117 | 120 | class Results(): |
118 | def __init__(self, issue_node,parent): | |
121 | ||
122 | def __init__(self, issue_node, glossary): | |
123 | ||
124 | #VULN_INFO ElementTree | |
119 | 125 | self.node = issue_node |
120 | self.port=parent.get('port') | |
121 | self.protocol=parent.get('protocol') | |
122 | self.name=self.node.get('number') | |
123 | self.severity=self.node.get('severity') | |
124 | self.title=self.get_text_from_subnode("TITLE") | |
125 | self.cvss=self.get_text_from_subnode("CVSS_BASE") | |
126 | self.pci=self.get_text_from_subnode("PCI_FLAG") | |
127 | self.diagnosis=self.get_text_from_subnode("DIAGNOSIS") | |
128 | self.solution=self.get_text_from_subnode("SOLUTION") | |
129 | self.result=self.get_text_from_subnode("RESULT") | |
130 | self.desc = self.diagnosis | |
131 | self.desc += "\nResult: " +self.result if self.result else "" | |
132 | ||
133 | self.ref=[] | |
134 | for r in issue_node.findall("CVE_ID_LIST/CVE_ID"): | |
135 | self.node=r | |
136 | self.ref.append(self.get_text_from_subnode("ID")) | |
137 | for r in issue_node.findall("BUGTRAQ_ID_LIST/BUGTRAQ_ID"): | |
138 | self.node=r | |
139 | self.ref.append("bid-"+self.get_text_from_subnode("ID")) | |
140 | ||
141 | ||
142 | ||
143 | def get_text_from_subnode(self, subnode_xpath_expr): | |
144 | """ | |
145 | Finds a subnode in the host node and the retrieves a value from it. | |
126 | self.port = self.get_text_from_subnode(self.node, 'PORT') | |
127 | self.protocol = self.get_text_from_subnode(self.node, 'PROTOCOL') | |
128 | self.name = self.get_text_from_subnode(self.node, 'QID') | |
129 | self.result = self.get_text_from_subnode(self.node, 'RESULT') | |
130 | ||
131 | self.severity_dict = { | |
132 | '1': 'info', | |
133 | '2': 'low', | |
134 | '3': 'med', | |
135 | '4': 'high', | |
136 | '5': 'critical' | |
137 | } | |
138 | ||
139 | #GLOSSARY TAG | |
140 | self.glossary = glossary | |
141 | self.severity = self.severity_dict.get(self.get_text_from_glossary('SEVERITY'), 'info') | |
142 | self.title = self.get_text_from_glossary('TITLE') | |
143 | self.cvss = self.get_text_from_glossary('CVSS_SCORE/CVSS_BASE') | |
144 | self.pci = self.get_text_from_glossary('PCI_FLAG') | |
145 | self.solution = self.get_text_from_glossary('SOLUTION') | |
146 | self.impact = self.get_text_from_glossary('IMPACT') | |
147 | ||
148 | #Description | |
149 | self.desc = self.cleaner_results(self.get_text_from_glossary('THREAT')) | |
150 | if not self.desc: | |
151 | self.desc = '' | |
152 | if self.result: | |
153 | self.desc += '\n\nResult: ' + self.cleaner_results(self.result) | |
154 | if self.impact: | |
155 | self.desc += '\n\nImpact: ' +self.cleaner_results(self.impact) | |
156 | if self.result: | |
157 | self.desc += '\n\nSolution: ' + self.cleaner_results(self.solution) | |
158 | ||
159 | #References | |
160 | self.ref = [] | |
161 | self.ref.append( self.get_text_from_glossary('CVE_ID_LIST/CVE_ID/ID') ) | |
162 | ||
163 | if self.cvss: | |
164 | self.ref.append('CVSS SCORE: ' + self.cvss) | |
165 | ||
166 | if self.pci: | |
167 | self.ref.append('PCI: ' + self.pci) | |
168 | ||
169 | def cleaner_results(self, string): | |
170 | ||
171 | try: | |
172 | return string.replace('<P>', '').replace('<UL>','').replace('<LI>','').replace('<BR>', '') | |
173 | except: | |
174 | return '' | |
175 | ||
176 | def get_text_from_glossary(self, tag): | |
177 | """ | |
178 | Finds a subnode in the glossary and retrieves a value of this. | |
179 | Filter by QualysId. | |
146 | 180 | |
147 | 181 | @return An attribute value |
148 | 182 | """ |
149 | sub_node = self.node.find(subnode_xpath_expr) | |
183 | ||
184 | for vuln_detail in self.glossary: | |
185 | ||
186 | id_act = vuln_detail.get('id').strip('qid_') | |
187 | if id_act == self.name: | |
188 | ||
189 | text = vuln_detail.find(tag) | |
190 | if text is not None: | |
191 | return cleaner_unicode(text.text) | |
192 | else: | |
193 | return None | |
194 | ||
195 | def get_text_from_subnode(self, node, subnode_xpath_expr): | |
196 | """ | |
197 | Finds a subnode in the node and the retrieves a value from it. | |
198 | ||
199 | @return An attribute value | |
200 | """ | |
201 | sub_node = node.find(subnode_xpath_expr) | |
150 | 202 | if sub_node is not None: |
151 | return sub_node.text | |
203 | return cleaner_unicode(sub_node.text) | |
152 | 204 | |
153 | 205 | return None |
154 | 206 | |
157 | 209 | Example plugin to parse qualysguard output. |
158 | 210 | """ |
159 | 211 | def __init__(self): |
212 | ||
160 | 213 | core.PluginBase.__init__(self) |
161 | self.id = "Qualysguard" | |
162 | self.name = "Qualysguard XML Output Plugin" | |
163 | self.plugin_version = "0.0.1" | |
164 | self.version = "Qualysguard" | |
165 | self.framework_version = "1.0.0" | |
214 | self.id = 'Qualysguard' | |
215 | self.name = 'Qualysguard XML Output Plugin' | |
216 | self.plugin_version = '0.0.2' | |
217 | self.version = 'Qualysguard 2016 March ' | |
218 | self.framework_version = '1.0.0' | |
166 | 219 | self.options = None |
167 | 220 | self._current_output = None |
168 | 221 | self._command_regex = re.compile(r'^(sudo qualysguard|\.\/qualysguard).*?') |
169 | 222 | |
170 | 223 | global current_path |
171 | 224 | self._output_file_path = os.path.join(self.data_path, |
172 | "qualysguard_output-%s.xml" % self._rid) | |
225 | 'qualysguard_output-%s.xml' % self._rid) | |
173 | 226 | |
174 | 227 | def parseOutputString(self, output, debug = False): |
175 | ||
176 | ||
228 | ||
177 | 229 | parser = QualysguardXmlParser(output) |
230 | ||
178 | 231 | for item in parser.items: |
179 | h_id = self.createAndAddHost(item.ip,item.os) | |
180 | i_id = self.createAndAddInterface(h_id, item.ip,ipv4_address=item.ip, hostname_resolution=item.ip) | |
181 | ||
232 | ||
233 | h_id = self.createAndAddHost( | |
234 | item.ip, | |
235 | item.os | |
236 | ) | |
237 | ||
238 | i_id = self.createAndAddInterface( | |
239 | h_id, | |
240 | item.ip, | |
241 | ipv4_address = item.ip, | |
242 | hostname_resolution = item.ip | |
243 | ) | |
244 | ||
182 | 245 | for v in item.vulns: |
246 | ||
183 | 247 | if v.port is None: |
184 | v_id=self.createAndAddVulnToHost(h_id,v.title,ref=v.ref,severity=v.severity,resolution=v.solution,desc=v.desc) | |
248 | ||
249 | v_id = self.createAndAddVulnToHost( | |
250 | h_id, | |
251 | v.title if v.title else v.name, | |
252 | ref = v.ref, | |
253 | severity = v.severity, | |
254 | resolution = v.solution if v.solution else '', | |
255 | desc = v.desc | |
256 | ) | |
257 | ||
185 | 258 | else: |
186 | web=False | |
187 | s_id = self.createAndAddServiceToInterface(h_id, i_id, v.port, | |
188 | v.protocol, | |
189 | ports = [str(v.port)], | |
190 | status = "open") | |
191 | ||
192 | if v.port in ['80','443'] or re.search("ssl|http",v.name): | |
193 | web=True | |
259 | ||
260 | web = False | |
261 | s_id = self.createAndAddServiceToInterface( | |
262 | h_id, | |
263 | i_id, | |
264 | v.port, | |
265 | v.protocol, | |
266 | ports = [ str(v.port) ], | |
267 | status = 'open' | |
268 | ) | |
269 | ||
270 | if v.port in ['80','443'] or re.search('ssl|http', v.name): | |
271 | web = True | |
194 | 272 | else: |
195 | web=False | |
196 | ||
273 | web = False | |
274 | ||
197 | 275 | if web: |
198 | v_id=self.createAndAddVulnWebToService(h_id, s_id,v.title,ref=v.ref,website=item.ip,severity=v.severity,desc=v.desc,resolution=v.solution) | |
199 | n_id = self.createAndAddNoteToService(h_id,s_id,"website","") | |
200 | n2_id = self.createAndAddNoteToNote(h_id,s_id,n_id,item.ip,"") | |
276 | v_id = self.createAndAddVulnWebToService( | |
277 | h_id, | |
278 | s_id, | |
279 | v.title if v.title else v.name, | |
280 | ref = v.ref, | |
281 | website = item.ip, | |
282 | severity = v.severity, | |
283 | desc = v.desc, | |
284 | resolution = v.solution if v.solution else '' | |
285 | ) | |
286 | ||
287 | n_id = self.createAndAddNoteToService( | |
288 | h_id, | |
289 | s_id, | |
290 | 'website', | |
291 | '' | |
292 | ) | |
293 | ||
294 | n2_id = self.createAndAddNoteToNote( | |
295 | h_id, | |
296 | s_id, | |
297 | n_id, | |
298 | item.ip, | |
299 | '' | |
300 | ) | |
301 | ||
201 | 302 | else: |
202 | v_id=self.createAndAddVulnToService(h_id, s_id,v.title,ref=v.ref,severity=v.severity,desc=v.desc,resolution=v.solution) | |
203 | ||
204 | ||
205 | ||
206 | ||
207 | ||
208 | ||
209 | ||
303 | v_id = self.createAndAddVulnToService( | |
304 | h_id, | |
305 | s_id, | |
306 | v.title if v.title else v.name, | |
307 | ref = v.ref, | |
308 | severity = v.severity, | |
309 | desc = v.desc, | |
310 | resolution = v.solution if v.solution else '' | |
311 | ) | |
312 | ||
210 | 313 | del parser |
211 | ||
314 | ||
212 | 315 | def processCommandString(self, username, current_path, command_string): |
213 | 316 | return None |
214 | ||
215 | 317 | |
216 | 318 | def setHost(self): |
217 | 319 | pass |
218 | ||
219 | 320 | |
220 | 321 | def createPlugin(): |
221 | 322 | return QualysguardPlugin() |
21 | 21 | except ImportError: |
22 | 22 | import xml.etree.ElementTree as ET |
23 | 23 | ETREE_VERSION = ET.VERSION |
24 | ||
24 | ||
25 | 25 | ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")] |
26 | 26 | |
27 | 27 | current_path = os.path.abspath(os.getcwd()) |
35 | 35 | __email__ = "[email protected]" |
36 | 36 | __status__ = "Development" |
37 | 37 | |
38 | ||
39 | ||
40 | ||
38 | ||
39 | ||
40 | ||
41 | 41 | |
42 | 42 | class W3afXmlParser(object): |
43 | 43 | """ |
55 | 55 | self.host = None |
56 | 56 | |
57 | 57 | tree = self.parse_xml(xml_output) |
58 | ||
58 | ||
59 | 59 | if tree: |
60 | 60 | self.items = [data for data in self.get_items(tree)] |
61 | 61 | else: |
62 | 62 | self.items = [] |
63 | ||
63 | ||
64 | 64 | |
65 | 65 | def parse_xml(self, xml_output): |
66 | 66 | """ |
84 | 84 | @return items A list of Host instances |
85 | 85 | """ |
86 | 86 | bugtype="" |
87 | ||
88 | ||
87 | ||
88 | ||
89 | 89 | scaninfo = tree.findall('scan-info')[0] |
90 | 90 | self.target = scaninfo.get('target') |
91 | 91 | host = re.search("(http|https|ftp)\://([a-zA-Z0-9\.\-]+(\:[a-zA-Z0-9\.&%\$\-]+)*@)*((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\.\,\?\'\\\+&%\$#\=~_\-]+)).*?$", self.target) |
92 | ||
92 | ||
93 | 93 | self.protocol = host.group(1) |
94 | 94 | self.host = host.group(4) |
95 | 95 | if self.protocol == 'https': |
96 | 96 | self.port=443 |
97 | 97 | if host.group(11) is not None: |
98 | 98 | self.port = host.group(11) |
99 | ||
99 | ||
100 | 100 | for node in tree.findall('vulnerability'): |
101 | 101 | yield Item(node) |
102 | 102 | for node in tree.findall('information'): |
103 | 103 | yield Item(node) |
104 | ||
105 | ||
106 | ||
107 | ||
104 | ||
105 | ||
106 | ||
107 | ||
108 | 108 | def get_attrib_from_subnode(xml_node, subnode_xpath_expr, attrib_name): |
109 | 109 | """ |
110 | 110 | Finds a subnode in the item node and the retrieves a value from it |
113 | 113 | """ |
114 | 114 | global ETREE_VERSION |
115 | 115 | node = None |
116 | ||
116 | ||
117 | 117 | if ETREE_VERSION[0] <= 1 and ETREE_VERSION[1] < 3: |
118 | ||
118 | ||
119 | 119 | match_obj = re.search("([^\@]+?)\[\@([^=]*?)=\'([^\']*?)\'",subnode_xpath_expr) |
120 | 120 | if match_obj is not None: |
121 | 121 | node_to_find = match_obj.group(1) |
137 | 137 | return None |
138 | 138 | |
139 | 139 | |
140 | ||
140 | ||
141 | 141 | |
142 | 142 | |
143 | 143 | class Item(object): |
175 | 175 | self.resp = tx.find('http-response/status').text |
176 | 176 | for h in tx.findall('http-response/headers/header'): |
177 | 177 | self.resp += "\n%s: %s" % (h.get('field'),h.get('content')) |
178 | self.resp += "\n%s" % tx.find('http-response/body').text | |
179 | ||
180 | ||
178 | ||
179 | if tx.find('http-response/body'): | |
180 | self.resp += "\n%s" % tx.find('http-response/body').text | |
181 | ||
182 | ||
181 | 183 | def do_clean(self,value): |
182 | 184 | myreturn ="" |
183 | 185 | if value is not None: |
184 | 186 | myreturn = re.sub("\n","",value) |
185 | 187 | return myreturn |
186 | ||
188 | ||
187 | 189 | def get_text_from_subnode(self, subnode_xpath_expr): |
188 | 190 | """ |
189 | 191 | Finds a subnode in the host node and the retrieves a value from it. |
195 | 197 | return sub_node.text |
196 | 198 | |
197 | 199 | return None |
198 | ||
199 | 200 | |
200 | 201 | |
201 | 202 | class W3afPlugin(core.PluginBase): |
221 | 222 | global current_path |
222 | 223 | self._output_file_path = os.path.join(self.data_path, |
223 | 224 | "w3af_output-%s.xml" % self._rid) |
224 | ||
225 | ||
225 | 226 | |
226 | 227 | def parseOutputString(self, output, debug = False): |
227 | 228 | |
243 | 244 | item.detail, pname=item.param, path=item.url, website=parser.host, severity=item.severity, |
244 | 245 | method=item.method, request=item.req, resolution=item.resolution, ref=item.ref, response=item.resp) |
245 | 246 | del parser |
246 | ||
247 | ||
248 | ||
249 | ||
247 | ||
248 | ||
249 | ||
250 | ||
250 | 251 | |
251 | 252 | |
252 | 253 | def resolve(self, host): |
258 | 259 | |
259 | 260 | def processCommandString(self, username, current_path, command_string): |
260 | 261 | return None |
261 | ||
262 | ||
262 | 263 | |
263 | 264 | def setHost(self): |
264 | 265 | pass |
0 | 0 | #!/usr/bin/env python |
1 | 1 | # -*- coding: utf-8 -*- |
2 | ||
3 | 2 | ''' |
4 | 3 | Faraday Penetration Test IDE |
5 | 4 | Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) |
6 | 5 | See the file 'doc/LICENSE' for the license information |
7 | ||
8 | 6 | ''' |
7 | ||
9 | 8 | from __future__ import with_statement |
10 | 9 | from plugins import core |
11 | 10 | from model import api |
18 | 17 | import xml.etree.cElementTree as ET |
19 | 18 | import xml.etree.ElementTree as ET_ORIG |
20 | 19 | ETREE_VERSION = ET_ORIG.VERSION |
20 | ||
21 | 21 | except ImportError: |
22 | 22 | import xml.etree.ElementTree as ET |
23 | 23 | ETREE_VERSION = ET.VERSION |
24 | ||
24 | ||
25 | 25 | ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")] |
26 | 26 | |
27 | 27 | current_path = os.path.abspath(os.getcwd()) |
35 | 35 | __email__ = "[email protected]" |
36 | 36 | __status__ = "Development" |
37 | 37 | |
38 | ||
39 | ||
40 | ||
38 | class ParserEtToAscii(ET_ORIG.TreeBuilder): | |
39 | def data(self, data): | |
40 | self._data.append(data.encode("ascii", errors = "backslashreplace")) | |
41 | ||
41 | 42 | |
42 | 43 | class ZapXmlParser(object): |
43 | 44 | """ |
44 | The objective of this class is to parse an xml file generated by the zap tool. | |
45 | The objective of this class is to parse an xml | |
46 | file generated by the zap tool. | |
45 | 47 | |
46 | 48 | TODO: Handle errors. |
47 | TODO: Test zap output version. Handle what happens if the parser doesn't support it. | |
49 | TODO: Test zap output version. Handle what happens | |
50 | if the parser doesn't support it. | |
51 | ||
48 | 52 | TODO: Test cases. |
49 | 53 | |
50 | 54 | @param zap_xml_filepath A proper xml generated by zap |
51 | 55 | """ |
52 | 56 | def __init__(self, xml_output): |
57 | ||
53 | 58 | tree = self.parse_xml(xml_output) |
54 | ||
55 | if tree: | |
59 | ||
60 | if tree is not None: | |
56 | 61 | self.sites = [data for data in self.get_items(tree)] |
57 | 62 | else: |
58 | 63 | self.sites = [] |
59 | ||
60 | 64 | |
61 | 65 | def parse_xml(self, xml_output): |
62 | 66 | """ |
68 | 72 | @return xml_tree An xml tree instance. None if error. |
69 | 73 | """ |
70 | 74 | try: |
71 | tree = ET.fromstring(xml_output) | |
75 | parser = ET_ORIG.XMLParser(target = ParserEtToAscii()) | |
76 | parser.feed(xml_output) | |
77 | tree = parser.close() | |
78 | ||
72 | 79 | except SyntaxError, err: |
73 | 80 | print "SyntaxError: %s. %s" % (err, xml_output) |
74 | 81 | return None |
79 | 86 | """ |
80 | 87 | @return items A list of Host instances |
81 | 88 | """ |
82 | ||
83 | 89 | for node in tree.findall('site'): |
84 | 90 | yield Site(node) |
85 | 91 | |
86 | 92 | |
87 | ||
88 | 93 | def get_attrib_from_subnode(xml_node, subnode_xpath_expr, attrib_name): |
89 | 94 | """ |
90 | 95 | Finds a subnode in the item node and the retrieves a value from it |
93 | 98 | """ |
94 | 99 | global ETREE_VERSION |
95 | 100 | node = None |
96 | ||
101 | ||
97 | 102 | if ETREE_VERSION[0] <= 1 and ETREE_VERSION[1] < 3: |
98 | ||
99 | match_obj = re.search("([^\@]+?)\[\@([^=]*?)=\'([^\']*?)\'",subnode_xpath_expr) | |
103 | ||
104 | match_obj = re.search( | |
105 | "([^\@]+?)\[\@([^=]*?)=\'([^\']*?)\'", | |
106 | subnode_xpath_expr) | |
107 | ||
100 | 108 | if match_obj is not None: |
109 | ||
101 | 110 | node_to_find = match_obj.group(1) |
102 | 111 | xpath_attrib = match_obj.group(2) |
103 | 112 | xpath_value = match_obj.group(3) |
113 | ||
104 | 114 | for node_found in xml_node.findall(node_to_find): |
115 | ||
105 | 116 | if node_found.attrib[xpath_attrib] == xpath_value: |
106 | 117 | node = node_found |
107 | 118 | break |
117 | 128 | return None |
118 | 129 | |
119 | 130 | |
120 | ||
121 | ||
122 | 131 | class Site(object): |
132 | ||
123 | 133 | def __init__(self, item_node): |
134 | ||
124 | 135 | self.node = item_node |
136 | ||
125 | 137 | self.host = self.node.get('host') |
126 | 138 | self.ip = self.resolve(self.host) |
127 | 139 | self.port = self.node.get('port') |
140 | ||
128 | 141 | self.items = [] |
129 | 142 | for alert in self.node.findall('alerts/alertitem'): |
130 | 143 | self.items.append(Item(alert)) |
138 | 151 | sub_node = self.node.find(subnode_xpath_expr) |
139 | 152 | if sub_node is not None: |
140 | 153 | return sub_node.text |
141 | ||
142 | 154 | return None |
143 | 155 | |
144 | 156 | def resolve(self, host): |
157 | ||
145 | 158 | try: |
146 | 159 | return socket.gethostbyname(host) |
147 | 160 | except: |
148 | 161 | pass |
162 | ||
149 | 163 | return host |
150 | 164 | |
151 | 165 | class Item(object): |
156 | 170 | @param item_node A item_node taken from an zap xml tree |
157 | 171 | """ |
158 | 172 | def __init__(self, item_node): |
173 | ||
159 | 174 | self.node = item_node |
160 | ||
161 | 175 | self.id=self.get_text_from_subnode('pluginid') |
162 | 176 | self.name = self.get_text_from_subnode('alert') |
163 | ||
164 | ||
165 | 177 | self.severity = self.get_text_from_subnode('riskcode') |
166 | 178 | self.desc = self.get_text_from_subnode('desc') |
167 | self.resolution = self.get_text_from_subnode('solution') if self.get_text_from_subnode('solution') else "" | |
168 | self.desc += "\nReference: " + self.get_text_from_subnode('reference') if self.get_text_from_subnode('reference') else "" | |
179 | ||
180 | if self.get_text_from_subnode('solution'): | |
181 | self.resolution = self.get_text_from_subnode('solution') | |
182 | else: | |
183 | self.resolution = '' | |
184 | ||
185 | if self.get_text_from_subnode('reference'): | |
186 | self.desc += '\nReference: '+self.get_text_from_subnode('reference') | |
187 | ||
169 | 188 | self.ref=[] |
170 | 189 | if self.get_text_from_subnode('cweid'): |
171 | self.ref.append("CWE-"+self.get_text_from_subnode('cweid')) | |
172 | ||
173 | ||
190 | self.ref.append("CWE-"+self.get_text_from_subnode('cweid')) | |
191 | ||
174 | 192 | self.items =[] |
175 | i=0 | |
176 | for n in item_node.findall('uri'): | |
177 | n2 = item_node.findall('param')[i] | |
178 | ||
179 | ||
180 | mregex = re.search("(http|https|ftp)\://([a-zA-Z0-9\.\-]+(\:[a-zA-Z0-9\.&%\$\-]+)*@)*((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\.\,\?\'\\\+&%\$#\=~_\-]+)).*?$", n.text) | |
181 | ||
193 | ||
194 | for instance in item_node.find('instances'): | |
195 | ||
196 | uri = instance.find('uri').text | |
197 | ||
198 | mregex = re.search( | |
199 | "(http|https|ftp)\://([a-zA-Z0-9\.\-]+(\:[a-zA-Z0-9\.&" | |
200 | ";%\$\-]+)*@)*((25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]" | |
201 | "{1}|[1-9])\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}" | |
202 | "|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}" | |
203 | "|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|" | |
204 | "[0-9])|localhost|([a-zA-Z0-9\-]+\.)*[a-zA-Z0-9\-]+\.(com|edu|gov|" | |
205 | "int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2" | |
206 | "}))[\:]*([0-9]+)*([/]*($|[a-zA-Z0-9\.\,\?\'\\\+&%\$#\=~_\-]+))" | |
207 | ".*?$", | |
208 | uri) | |
209 | ||
182 | 210 | protocol = mregex.group(1) |
183 | 211 | host = mregex.group(4) |
184 | port =80 | |
212 | port = 80 | |
185 | 213 | if protocol == 'https': |
186 | port=443 | |
214 | port = 443 | |
187 | 215 | if mregex.group(11) is not None: |
188 | 216 | port = mregex.group(11) |
189 | ||
190 | item = {'uri' : n.text, 'param' : n2.text, 'host' : host, 'protocol' : protocol, 'port' : port} | |
217 | ||
218 | try: | |
219 | params = [i.split('=')[0] for i in uri.split('?')[1].split('&')] | |
220 | except Exception as e: | |
221 | params = '' | |
222 | ||
223 | item = { | |
224 | 'uri' : uri, | |
225 | 'params' : ', '.join(params), | |
226 | 'host' : host, | |
227 | 'protocol' : protocol, | |
228 | 'port' : port | |
229 | } | |
191 | 230 | self.items.append(item) |
192 | i=i+1 | |
193 | self.requests = "\n".join([i['uri'] for i in self.items]) | |
194 | ||
231 | ||
232 | self.requests = "\n".join( [i['uri'] for i in self.items] ) | |
233 | ||
195 | 234 | def get_text_from_subnode(self, subnode_xpath_expr): |
196 | 235 | """ |
197 | 236 | Finds a subnode in the host node and the retrieves a value from it. |
205 | 244 | return None |
206 | 245 | |
207 | 246 | |
208 | ||
209 | 247 | class ZapPlugin(core.PluginBase): |
210 | 248 | """ |
211 | 249 | Example plugin to parse zap output. |
212 | 250 | """ |
213 | 251 | def __init__(self): |
252 | ||
214 | 253 | core.PluginBase.__init__(self) |
215 | 254 | self.id = "Zap" |
216 | 255 | self.name = "Zap XML Output Plugin" |
217 | self.plugin_version = "0.0.2" | |
218 | self.version = "2.2.2" | |
256 | self.plugin_version = "0.0.3" | |
257 | self.version = "2.4.3" | |
219 | 258 | self.framework_version = "1.0.0" |
220 | 259 | self.options = None |
221 | 260 | self._current_output = None |
223 | 262 | self._command_regex = re.compile(r'^(zap|sudo zap|\.\/zap).*?') |
224 | 263 | |
225 | 264 | global current_path |
226 | self._output_file_path = os.path.join(self.data_path, | |
227 | "zap_output-%s.xml" % self._rid) | |
228 | ||
265 | ||
266 | self._output_file_path = os.path.join( | |
267 | self.data_path, | |
268 | "zap_output-%s.xml" % self._rid | |
269 | ) | |
229 | 270 | |
230 | 271 | def parseOutputString(self, output, debug = False): |
231 | 272 | """ |
232 | This method will discard the output the shell sends, it will read it from | |
233 | the xml where it expects it to be present. | |
273 | This method will discard the output the shell sends, it will read it | |
274 | from the xml where it expects it to be present. | |
234 | 275 | |
235 | 276 | NOTE: if 'debug' is true then it is being run from a test case and the |
236 | 277 | output being sent is valid. |
237 | 278 | """ |
238 | 279 | |
239 | 280 | parser = ZapXmlParser(output) |
281 | ||
240 | 282 | for site in parser.sites: |
283 | ||
241 | 284 | host = [] |
242 | 285 | if site.host != site.ip: |
243 | 286 | host = [site.host] |
287 | ||
244 | 288 | h_id = self.createAndAddHost(site.ip) |
245 | i_id = self.createAndAddInterface(h_id, site.ip, ipv4_address=site.ip, hostname_resolution=host) | |
246 | s_id = self.createAndAddServiceToInterface(h_id, i_id, "http", "tcp", | |
247 | ports = [site.port], | |
248 | status = 'open') | |
249 | n_id = self.createAndAddNoteToService(h_id,s_id,"website","") | |
250 | n2_id = self.createAndAddNoteToNote(h_id,s_id,n_id,site.host,"") | |
289 | ||
290 | i_id = self.createAndAddInterface( | |
291 | h_id, | |
292 | site.ip, | |
293 | ipv4_address = site.ip, | |
294 | hostname_resolution = host | |
295 | ) | |
296 | ||
297 | s_id = self.createAndAddServiceToInterface( | |
298 | h_id, | |
299 | i_id, | |
300 | "http", | |
301 | "tcp", | |
302 | ports = [site.port], | |
303 | status = 'open' | |
304 | ) | |
305 | ||
306 | n_id = self.createAndAddNoteToService(h_id, s_id, "website", "") | |
307 | n2_id = self.createAndAddNoteToNote(h_id, s_id, n_id, site.host, "") | |
251 | 308 | |
252 | 309 | for item in site.items: |
253 | v_id = self.createAndAddVulnWebToService(h_id, s_id, item.name, | |
254 | item.desc, website=site.host, severity=item.severity, | |
255 | path=item.items[0]['uri'],params=item.items[0]['param'], | |
256 | request=item.requests,ref=item.ref, resolution=item.resolution) | |
310 | v_id = 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 | ) | |
257 | 323 | |
258 | 324 | del parser |
259 | ||
260 | ||
261 | ||
262 | ||
263 | ||
264 | ||
265 | ||
325 | ||
266 | 326 | def processCommandString(self, username, current_path, command_string): |
267 | 327 | return None |
268 | ||
269 | 328 | |
270 | 329 | def setHost(self): |
271 | 330 | pass |
62 | 62 | "update_controller_action": "ModelControler.newVuln", |
63 | 63 | "owner": "john" |
64 | 64 | }, |
65 | "impact": { | |
66 | accountability: false, | |
67 | availability: false, | |
68 | confidentiality: false, | |
69 | integrity: false | |
70 | }, | |
65 | 71 | "owned": false, |
66 | 72 | "severity": "med", |
67 | 73 | "type": "Vulnerability", |
68 | 74 | "owner": "john", |
69 | 75 | "desc": "I'm scared!", |
70 | 76 | "data": "", |
77 | "easeofresolution": "simple", | |
71 | 78 | "description": "I'm scared!" |
72 | 79 | }; |
73 | 80 | vuln2 = { |
90 | 97 | "update_controller_action": "ModelControler.newVuln", |
91 | 98 | "owner": "john" |
92 | 99 | }, |
100 | "impact": { | |
101 | accountability: false, | |
102 | availability: false, | |
103 | confidentiality: false, | |
104 | integrity: false | |
105 | }, | |
93 | 106 | "owned": false, |
94 | 107 | "severity": "med", |
95 | 108 | "type": "Vulnerability", |
96 | 109 | "owner": "john", |
97 | 110 | "desc": "I'm scared!", |
98 | 111 | "data": "", |
112 | "easeofresolution": "trivial", | |
99 | 113 | "description": "I'm scared!" |
100 | 114 | }; |
101 | 115 | vuln3 = { |
111 | 125 | "update_controller_action": "No model controller call", |
112 | 126 | "owner": "" |
113 | 127 | }, |
128 | "easeofresolution": "simple", | |
114 | 129 | "name": "Service Detection", |
115 | 130 | "obj_id": "008cba9b11897f2d52c53dd953d75fa233a7fffe", |
116 | 131 | "owned": false, |
118 | 133 | "parent": "6.7.8", |
119 | 134 | "refs": [ |
120 | 135 | ], |
136 | "impact": { | |
137 | accountability: false, | |
138 | availability: false, | |
139 | confidentiality: false, | |
140 | integrity: false | |
141 | }, | |
121 | 142 | "severity": "low", |
122 | 143 | "type": "VulnerabilityWeb", |
123 | 144 | "method": "", |
258 | 279 | }); |
259 | 280 | |
260 | 281 | describe('Status report vuln deletion - delete method (modal)', function() { |
261 | it('call delete by property with no vulns selected', function() { | |
282 | it('call delete with no vulns selected', function() { | |
262 | 283 | // we need $scope.gridOptions.data to have all the vulns before calling |
263 | 284 | // the delete method |
264 | $scope.$apply(); | |
265 | $scope.deleteVuln(vuln1); | |
266 | fakeModal.close(); | |
285 | $scope.getCurrentSelection = function() { return []; } | |
286 | $scope.$apply(); | |
287 | $scope.delete(); | |
267 | 288 | $scope.$apply(); |
268 | 289 | |
269 | 290 | expect($scope.gridOptions.data.length).toEqual(3); |
274 | 295 | it('call delete with a valid vuln (1.2.3.4) selected and accept modal', function() { |
275 | 296 | // we need $scope.gridOptions.data to have all the vulns before calling |
276 | 297 | // the delete method |
277 | vuln1.selected_statusreport_controller = true; | |
278 | $scope.$apply(); | |
279 | $scope.deleteVuln(); | |
298 | $scope.getCurrentSelection = function() { return [vuln1]; } | |
299 | $scope.$apply(); | |
300 | $scope.delete(); | |
280 | 301 | fakeModal.close(); |
281 | 302 | $scope.$apply(); |
282 | 303 | |
288 | 309 | it('call delete with a valid vuln (1.2.3.4) selected and cancel modal', function() { |
289 | 310 | // we need $scope.gridOptions.data to have all the vulns before calling |
290 | 311 | // the delete method |
291 | vuln1.selected_statusreport_controller = true; | |
312 | $scope.getCurrentSelection = function() { return [vuln1]; } | |
292 | 313 | $scope.$apply(); |
293 | 314 | $scope.delete(); |
294 | 315 | fakeModal.dismiss(); |
300 | 321 | expect($scope.gridOptions.data).toContain(vuln3); |
301 | 322 | }); |
302 | 323 | it('call delete with valid vulns selected and accept modal', function() { |
303 | vuln1.selected_statusreport_controller = true; | |
304 | vuln2.selected_statusreport_controller = true; | |
324 | $scope.getCurrentSelection = function() { return [vuln1, vuln2]; } | |
305 | 325 | $scope.$apply(); |
306 | 326 | $scope.delete(); |
307 | 327 | fakeModal.close(); |
452 | 472 | }); |
453 | 473 | |
454 | 474 | describe('Status report vuln edition - update method', function() { |
455 | //TODO: test each editable property | |
475 | it('edit many vulns by property', function() { | |
476 | $scope.getCurrentSelection = function() { return [vuln1, vuln2, vuln3]; }; | |
477 | var impact = { | |
478 | accountability: true, | |
479 | availability: true, | |
480 | confidentiality: true, | |
481 | integrity: false | |
482 | }; | |
483 | ||
484 | $scope.$apply(); | |
485 | // String properties | |
486 | $scope.editString('name'); | |
487 | fakeModal.close('Changed name'); | |
488 | // Text properties | |
489 | $scope.editText('desc'); | |
490 | fakeModal.close('Changed description'); | |
491 | // Severity property | |
492 | $scope.editSeverity(); | |
493 | fakeModal.close('high'); | |
494 | // Ease of resolution property(obj) | |
495 | $scope.editEaseofresolution(); | |
496 | fakeModal.close('difficult'); | |
497 | // References property | |
498 | $scope.editReferences(); | |
499 | fakeModal.close(['CVE-new-ref','OSVDB:new-ref']); | |
500 | // Impact property(obj) | |
501 | $scope.editImpact(); | |
502 | fakeModal.close(impact); | |
503 | // Comfirm property | |
504 | $scope.editConfirm(); | |
505 | fakeModal.close('Confirm'); | |
506 | ||
507 | $scope.gridOptions.data.forEach(function(v) { | |
508 | expect(v.name).toEqual("Changed name"); | |
509 | expect(v.desc).toEqual("Changed description"); | |
510 | expect(v.severity).toEqual("high"); | |
511 | expect(v.easeofresolution).toEqual("difficult"); | |
512 | expect(v.refs).toContain('CVE-new-ref', 'OSVDB:new-ref'); | |
513 | expect(v.impact).toEqual(impact); | |
514 | expect(v.confirmed).toEqual(true); | |
515 | }); | |
516 | }); | |
517 | it('edit many vulns by property but cancel the modal', function() { | |
518 | $scope.getCurrentSelection = function() { return [vuln1, vuln2, vuln3]; }; | |
519 | var impact = { | |
520 | accountability: true, | |
521 | availability: true, | |
522 | confidentiality: true, | |
523 | integrity: false | |
524 | }; | |
525 | ||
526 | $scope.$apply(); | |
527 | // String properties | |
528 | $scope.editString('name'); | |
529 | fakeModal.dismiss(); | |
530 | // Text properties | |
531 | $scope.editText('desc'); | |
532 | fakeModal.dismiss(); | |
533 | // Severity property | |
534 | $scope.editSeverity(); | |
535 | fakeModal.dismiss(); | |
536 | // Ease of resolution property(obj) | |
537 | $scope.editEaseofresolution(); | |
538 | fakeModal.dismiss(); | |
539 | // References property | |
540 | $scope.editReferences(); | |
541 | fakeModal.dismiss(); | |
542 | // Impact property(obj) | |
543 | $scope.editImpact(); | |
544 | fakeModal.dismiss(); | |
545 | // Comfirm property | |
546 | $scope.editConfirm(); | |
547 | fakeModal.dismiss(); | |
548 | ||
549 | $scope.gridOptions.data.forEach(function(v) { | |
550 | expect(v.name).not.toEqual("Changed name"); | |
551 | expect(v.desc).not.toEqual("Changed description"); | |
552 | expect(v.severity).not.toEqual("high"); | |
553 | expect(v.easeofresolution).not.toEqual("difficult"); | |
554 | expect(v.refs).not.toContain('CVE-new-ref', 'OSVDB:new-ref'); | |
555 | expect(v.impact).not.toEqual(impact); | |
556 | expect(v.confirmed).not.toEqual(true); | |
557 | }); | |
558 | }); | |
559 | it('edit many vulns from CWE', function() { | |
560 | $scope.getCurrentSelection = function() { return [vuln1, vuln2, vuln3]; }; | |
561 | var CWE_obj = { | |
562 | name: "ES-Cisco ASA Error", | |
563 | desc: "Summary: El cisco ASA es vulnerable", | |
564 | refs: ['CVE-new-ref'], | |
565 | resolution: "Actualizar la ultima version" | |
566 | }; | |
567 | ||
568 | $scope.$apply(); | |
569 | $scope.editCWE(); | |
570 | fakeModal.close(CWE_obj); | |
571 | ||
572 | $scope.gridOptions.data.forEach(function(v) { | |
573 | expect(v.name).toEqual("ES-Cisco ASA Error"); | |
574 | expect(v.desc).toEqual("Summary: El cisco ASA es vulnerable"); | |
575 | expect(v.refs).toContain('CVE-new-ref'); | |
576 | expect(v.resolution).toEqual('Actualizar la ultima version'); | |
577 | }); | |
578 | }); | |
579 | it('edit many vulns from CWE but cancel the modal', function() { | |
580 | $scope.getCurrentSelection = function() { return [vuln1, vuln2, vuln3]; }; | |
581 | var CWE_obj = { | |
582 | name: "ES-Cisco ASA Error", | |
583 | desc: "Summary: El cisco ASA es vulnerable", | |
584 | refs: ['CVE-new-ref'], | |
585 | resolution: "Actualizar la ultima version" | |
586 | }; | |
587 | ||
588 | $scope.$apply(); | |
589 | $scope.editCWE(); | |
590 | fakeModal.dismiss(); | |
591 | $scope.$apply(); | |
592 | ||
593 | $scope.gridOptions.data.forEach(function(v) { | |
594 | expect(v.name).not.toEqual("ES-Cisco ASA Error"); | |
595 | expect(v.desc).not.toEqual("Summary: El cisco ASA es vulnerable"); | |
596 | expect(v.refs).not.toContain('CVE-new-ref'); | |
597 | expect(v.resolution).not.toEqual('Actualizar la ultima version'); | |
598 | }); | |
599 | }); | |
456 | 600 | }); |
457 | 601 | |
458 | 602 | describe('Status report vuln edition - edit method (modal)', function() { |
468 | 612 | "severity": "high" |
469 | 613 | }; |
470 | 614 | |
471 | vuln1.selected_statusreport_controller = true; | |
472 | ||
473 | $scope.getCurrentSelection = function() { | |
474 | return [vuln1]; | |
475 | }; | |
615 | $scope.getCurrentSelection = function() { return [vuln1]; }; | |
476 | 616 | |
477 | 617 | $scope.$apply(); |
478 | 618 | $scope.edit(); |
503 | 643 | "owned": true, |
504 | 644 | "severity": "high" |
505 | 645 | }; |
506 | vuln1.selected_statusreport_controller = true; | |
507 | $scope.$apply(); | |
508 | //$scope.edit(); | |
509 | //fakeModal.dismiss(); | |
510 | //$scope.$apply(); | |
646 | $scope.getCurrentSelection = function() { return [vuln1]; }; | |
647 | $scope.$apply(); | |
648 | $scope.edit(); | |
649 | fakeModal.dismiss(); | |
650 | $scope.$apply(); | |
511 | 651 | |
512 | 652 | expect($scope.gridOptions.data.length).toEqual(3); |
513 | 653 | $scope.gridOptions.data.forEach(function(vuln) { |
30 | 30 | }); |
31 | 31 | |
32 | 32 | it('Tests if existence is well asked', function() { |
33 | $httpBackend.when('HEAD', 'http://localhost:9876/tuvieja') | |
33 | $httpBackend.when('HEAD', 'http://localhost:9876/test_workspace') | |
34 | 34 | .respond(200, ''); |
35 | 35 | |
36 | $httpBackend.expectHEAD('http://localhost:9876/tuvieja'); | |
36 | $httpBackend.expectHEAD('http://localhost:9876/test_workspace'); | |
37 | 37 | fact = createFactory(); |
38 | fact.exists('tuvieja').then(function(exist){ | |
38 | fact.exists('test_workspace').then(function(exist){ | |
39 | 39 | expect(exist).toBe(true); |
40 | 40 | }); |
41 | 41 | $httpBackend.flush(); |
54 | 54 | "description": "" |
55 | 55 | }; |
56 | 56 | |
57 | var object = { | |
58 | _attachments: | |
59 | { views: | |
60 | {"content_type": "application/javascript"} | |
61 | } | |
62 | }; | |
63 | ||
57 | 64 | $httpBackend.expectPUT('http://localhost:9876/test_workspace', |
58 | 65 | workspace).respond(200, {"ok": true}); |
59 | 66 | |
60 | 67 | $httpBackend.expectPUT('http://localhost:9876/test_workspace/test_workspace', |
61 | 68 | workspace).respond(200, {"ok": true}); |
62 | 69 | |
70 | $httpBackend.expectGET('http://localhost:9876/reports/_design/reports').respond(200, object); | |
71 | ||
72 | $httpBackend.expectPOST('http://localhost:9876/test_workspace/_bulk_docs', | |
73 | {'docs': []}).respond(200, {"ok": true}); | |
74 | ||
75 | $httpBackend.when('HEAD', 'http://localhost:9876/test_workspace') | |
76 | .respond(200, ''); | |
77 | ||
63 | 78 | fact = createFactory(); |
64 | 79 | |
65 | 80 | fact.put(workspace); |
81 | fact.exists('test_workspace').then(function(exist){ | |
82 | expect(exist).toBe(true); | |
83 | }); | |
66 | 84 | $httpBackend.flush(); |
67 | expect(workspace_exists).toBe(true); | |
68 | 85 | }); |
69 | 86 | |
70 | 87 | it('Tests if OK Delete are well done', function() { |
6 | 6 | |
7 | 7 | import restkit.errors |
8 | 8 | import model |
9 | ||
9 | 10 | |
10 | 11 | def simple_decorator(decorator): |
11 | 12 | '''this decorator can be used to turn simple functions |
23 | 24 | g.__doc__ = f.__doc__ |
24 | 25 | g.__dict__.update(f.__dict__) |
25 | 26 | return g |
26 | ||
27 | ||
28 | 27 | new_decorator.__name__ = decorator.__name__ |
29 | 28 | new_decorator.__doc__ = decorator.__doc__ |
30 | 29 | new_decorator.__dict__.update(decorator.__dict__) |
31 | 30 | return new_decorator |
32 | ||
33 | ||
34 | @simple_decorator | |
35 | def modify_class_field(func): | |
36 | def wrapper(self, *args, **kwargs): | |
37 | self.cuca = "eehh" | |
38 | return func(self, *args, **kwargs) | |
39 | return wrapper | |
40 | 31 | |
41 | 32 | |
42 | 33 | @simple_decorator |
43 | 34 | def updateLocalMetadata(func): |
44 | 35 | def wrapper(self, *args, **kwargs): |
45 | 36 | self.updateMetadata() |
46 | return func(self, *args, **kwargs) | |
47 | return wrapper | |
48 | ||
49 | @simple_decorator | |
50 | def passPermissionsOrRaise(func): | |
51 | def wrapper(self, *args, **kwargs): | |
52 | self.checkPermissions(op = func.func_name) | |
53 | 37 | return func(self, *args, **kwargs) |
54 | 38 | return wrapper |
55 | 39 | |
70 | 54 | model.api.devlog("Operation [%s] timeout" % func.__name__) |
71 | 55 | return func(self, *args, **kwargs) |
72 | 56 | return wrapper |
73 | ||
74 |
749 | 749 | /* End of media */ |
750 | 750 | /* Styles for WS list */ |
751 | 751 | .ws-name { |
752 | font: 250%/3 'Ubuntu',Helvetica,Arial,sans-serif !important; | |
752 | font: 120%/3 'Ubuntu',Helvetica,Arial,sans-serif !important; | |
753 | 753 | font-size: 100%; |
754 | 754 | } |
755 | 755 | .ws-link { |
7 | 7 | function($scope, $cookies, $filter, $location, $route, $routeParams, $uibModal, hostsManager, workspacesFact, dashboardSrv, servicesManager) { |
8 | 8 | |
9 | 9 | init = function() { |
10 | $scope.selectall = false; | |
10 | $scope.selectall_service = false; | |
11 | 11 | // current workspace |
12 | 12 | $scope.workspace = $routeParams.wsId; |
13 | 13 | //ID of current host |
27 | 27 | $scope.services = []; |
28 | 28 | dashboardSrv.getServicesByHost($scope.workspace, hostId) |
29 | 29 | .then(function(services) { |
30 | if (services.length > 0) $scope.loadedServices = true; | |
30 | 31 | services.forEach(function(service) { |
31 | 32 | servicesManager.getService(service.id, $scope.workspace, true) |
32 | 33 | .then(function(s) { |
49 | 50 | $scope.pageSize = 10; |
50 | 51 | $scope.currentPage = 0; |
51 | 52 | $scope.newCurrentPage = 0; |
52 | ||
53 | ||
53 | 54 | if(!isNaN(parseInt($cookies.pageSize))) $scope.pageSize = parseInt($cookies.pageSize); |
54 | 55 | $scope.newPageSize = $scope.pageSize; |
55 | 56 | |
87 | 88 | |
88 | 89 | $location.path(url); |
89 | 90 | }; |
90 | ||
91 | ||
91 | 92 | $scope.go = function() { |
92 | 93 | $scope.pageSize = $scope.newPageSize; |
93 | 94 | $cookies.pageSize = $scope.pageSize; |
205 | 206 | }; |
206 | 207 | |
207 | 208 | $scope.update = function(services, data) { |
208 | services.forEach(function(service){ | |
209 | services.forEach(function(service){ | |
209 | 210 | delete service.selected; |
210 | 211 | servicesManager.updateService(service, data, $scope.workspace).then(function(s) { |
211 | 212 | }, function(message){ |
307 | 308 | }; |
308 | 309 | |
309 | 310 | $scope.checkAllServices = function() { |
310 | $scope.selectall = !$scope.selectall; | |
311 | $scope.selectall_service = !$scope.selectall_service; | |
311 | 312 | |
312 | 313 | tmp_services = filter($scope.services); |
313 | 314 | tmp_services.forEach(function(service) { |
314 | service.selected = $scope.selectall; | |
315 | service.selected = $scope.selectall_service; | |
315 | 316 | }); |
316 | 317 | }; |
317 | 318 | |
332 | 333 | } |
333 | 334 | |
334 | 335 | filter = function(data) { |
335 | var tmp_data = $filter('orderObjectBy')(data, $scope.sortField, $scope.reverse); | |
336 | var tmp_data = $filter('orderBy')(data, $scope.sortField, $scope.reverse); | |
336 | 337 | tmp_data = $filter('filter')(tmp_data, $scope.expression); |
337 | 338 | tmp_data = tmp_data.splice($scope.pageSize * $scope.currentPage, $scope.pageSize); |
338 | 339 |
7 | 7 | function($scope, $cookies, $filter, $location, $route, $routeParams, $uibModal, hostsManager, workspacesFact) { |
8 | 8 | |
9 | 9 | init = function() { |
10 | $scope.selectall = false; | |
10 | $scope.selectall_hosts = false; | |
11 | 11 | // hosts list |
12 | 12 | $scope.hosts = []; |
13 | 13 | // current workspace |
23 | 23 | hostsManager.getHosts($scope.workspace) |
24 | 24 | .then(function(hosts) { |
25 | 25 | $scope.hosts = hosts; |
26 | $scope.loadedVulns = true; | |
26 | 27 | $scope.loadIcons(); |
27 | 28 | }); |
28 | 29 | |
45 | 46 | $scope.pageSize = 10; |
46 | 47 | $scope.currentPage = 0; |
47 | 48 | $scope.newCurrentPage = 0; |
48 | ||
49 | ||
49 | 50 | if(!isNaN(parseInt($cookies.pageSize))) $scope.pageSize = parseInt($cookies.pageSize); |
50 | 51 | $scope.newPageSize = $scope.pageSize; |
51 | 52 | |
90 | 91 | |
91 | 92 | $location.path(url); |
92 | 93 | }; |
93 | ||
94 | ||
94 | 95 | $scope.go = function() { |
95 | 96 | $scope.pageSize = $scope.newPageSize; |
96 | 97 | $cookies.pageSize = $scope.pageSize; |
364 | 365 | }; |
365 | 366 | |
366 | 367 | $scope.checkAll = function() { |
367 | $scope.selectall = !$scope.selectall; | |
368 | $scope.selectall_hosts = !$scope.selectall_hosts; | |
368 | 369 | |
369 | 370 | tmp_hosts = filter($scope.hosts); |
370 | 371 | tmp_hosts.forEach(function(host) { |
371 | host.selected = $scope.selectall; | |
372 | host.selected = $scope.selectall_hosts; | |
372 | 373 | }); |
373 | 374 | }; |
374 | 375 | |
389 | 390 | } |
390 | 391 | |
391 | 392 | filter = function(data) { |
392 | var tmp_data = $filter('orderObjectBy')(data, $scope.sortField, $scope.reverse); | |
393 | var tmp_data = $filter('orderBy')(data, $scope.sortField, $scope.reverse); | |
393 | 394 | tmp_data = $filter('filter')(tmp_data, $scope.expression); |
394 | 395 | tmp_data = tmp_data.splice($scope.pageSize * $scope.currentPage, $scope.pageSize); |
395 | 396 | |
396 | 397 | return tmp_data; |
397 | 398 | }; |
398 | ||
399 | ||
399 | 400 | init(); |
400 | 401 | }]); |
36 | 36 | <div class="form-group"> |
37 | 37 | <div class="input-group input-group-sm"> |
38 | 38 | <span class="input-group-addon glyphicon-btn glyphicon glyphicon-remove" ng-click="searchFor(false, '')" ng-if="search"></span> |
39 | <input type="text" class="form-control" id="filter-by" | |
39 | <input type="text" class="form-control" id="filter-by" | |
40 | 40 | placeholder="enter keywords" ng-change="currentPage = 0" ng-model="searchParams" /> |
41 | 41 | <span class="input-group-addon glyphicon-btn" ng-click="searchFor(true, searchParams)"> |
42 | <i class="fa fa-search" ng-if="hosts"></i> | |
43 | <i class="fa fa-refresh fa-spin" ng-if="hosts.length == 0"></i> | |
42 | <i class="fa fa-search" ng-if="loadedVulns"></i> | |
43 | <i class="fa fa-refresh fa-spin" ng-if="!loadedVulns"></i> | |
44 | 44 | </span> |
45 | 45 | </div> |
46 | 46 | </div> |
47 | </form> | |
47 | </form> | |
48 | 48 | </div> |
49 | 49 | <table class="status-report hosts-list table table-responsive"> |
50 | 50 | <thead> |
72 | 72 | </thead> |
73 | 73 | <tbody> |
74 | 74 | <tr ng-repeat="host in filtered = (hosts | filter:expression) | orderBy:sortField:reverse | startFrom:currentPage*pageSize | limitTo:pageSize" |
75 | selection-model selection-model-type="checkbox" | |
76 | selection-model-mode="multiple-additive" | |
75 | selection-model selection-model-type="checkbox" | |
76 | selection-model-mode="multiple-additive" | |
77 | 77 | selection-model-selected-class="multi-selected" |
78 | 78 | selection-model-on-change="selectedHosts()"> |
79 | 79 | <td><input type="checkbox" name="{{host._id}}"/></td> |
62 | 62 | <input type="text" class="form-control" id="filter-by" |
63 | 63 | placeholder="enter keywords" ng-change="currentPage = 0" ng-model="searchParams" /> |
64 | 64 | <span class="input-group-addon glyphicon-btn" ng-click="searchFor(true, searchParams)"> |
65 | <i class="fa fa-search" ng-if="services.length > 0"></i> | |
66 | <i class="fa fa-refresh fa-spin" ng-if="services.length == 0"></i> | |
65 | <i class="fa fa-search" ng-if="services.length > 0 || !loadedServices"></i> | |
66 | <i class="fa fa-refresh fa-spin" ng-if="services.length == 0 && loadedServices"></i> | |
67 | 67 | </span> |
68 | 68 | </div> |
69 | 69 | </div> |
70 | </form> | |
70 | </form> | |
71 | 71 | </div> |
72 | 72 | <table class="status-report hosts-list table table-responsive"> |
73 | 73 | <thead> |
63 | 63 | //set gridApi on scope |
64 | 64 | $scope.gridApi = gridApi; |
65 | 65 | $scope.gridApi.selection.on.rowSelectionChanged( $scope, function ( rowChanged ) { |
66 | $scope.selectionChange(); | |
66 | 67 | if ( typeof(rowChanged.treeLevel) !== 'undefined' && rowChanged.treeLevel > -1 ) { |
67 | 68 | // this is a group header |
68 | 69 | children = $scope.gridApi.treeBase.getRowChildren( rowChanged ); |
720 | 721 | } |
721 | 722 | }); |
722 | 723 | modal.result.then(function(data) { |
723 | $scope.selectedVulns().forEach(function(vuln) { | |
724 | $scope.getCurrentSelection().forEach(function(vuln) { | |
724 | 725 | var references = vuln.refs.concat([]); |
725 | 726 | data.refs.forEach(function(ref) { |
726 | 727 | if(vuln.refs.indexOf(ref) == -1){ |
898 | 899 | } |
899 | 900 | }; |
900 | 901 | |
902 | $scope.selectionChange = function() { | |
903 | $scope.vulnWebSelected = $scope.getCurrentSelection().some(function(v) { | |
904 | return v.type === "VulnerabilityWeb" | |
905 | }); | |
906 | }; | |
907 | ||
901 | 908 | init(); |
902 | 909 | }]); |
92 | 92 | <input type="text" class="form-control" id="filter-by" |
93 | 93 | placeholder="enter keywords" ng-change="currentPage = 0" ng-model="searchParams" /> |
94 | 94 | <span class="input-group-addon glyphicon-btn" ng-click="searchFor(true, searchParams)"> |
95 | <i class="fa fa-search" ng-if="gridOptions.data.length > 0"></i> | |
96 | <i class="fa fa-refresh fa-spin" ng-if="gridOptions.data.length == 0"></i> | |
95 | <i class="fa fa-search" ng-if="gridOptions.data.length > 0 || gridOptions.total >= 0"></i> | |
96 | <i class="fa fa-refresh fa-spin" ng-if="gridOptions.total === undefined"></i> | |
97 | 97 | </span> |
98 | 98 | </div> |
99 | 99 | </div> |
184 | 184 | loadAtt, |
185 | 185 | self = this, |
186 | 186 | url = BASEURL + self.ws + "/" + self._id; |
187 | ||
187 | ||
188 | 188 | self.populate().then(function(resp) { |
189 | 189 | $http.put(url, resp) |
190 | 190 | .success(function(data) { |
2 | 2 | // See the file 'doc/LICENSE' for the license information |
3 | 3 | |
4 | 4 | angular.module('faradayApp') |
5 | .factory('vulnsManager', | |
5 | .factory('vulnsManager', | |
6 | 6 | ['Vuln', 'WebVuln', 'BASEURL', '$filter', '$http', '$q', 'attachmentsFact', 'hostsManager', 'servicesManager', |
7 | 7 | function(Vuln, WebVuln, BASEURL, $filter, $http, $q, attachmentsFact, hostsManager, servicesManager) { |
8 | 8 | var vulnsManager = {}; |
9 | ||
9 | ||
10 | 10 | vulnsManager.vulns = []; |
11 | 11 | vulnsManager.vulns_indexes = {}; |
12 | 12 | |
76 | 76 | deferred.reject(err); |
77 | 77 | }); |
78 | 78 | } catch(e) { |
79 | console.log(e.stack); | |
80 | 79 | deferred.reject(e.name + ": " + e.message); |
81 | 80 | } |
82 | 81 | |
83 | 82 | return deferred.promise; |
84 | 83 | }; |
85 | ||
84 | ||
86 | 85 | vulnsManager.deleteVuln = function(vuln) { |
87 | 86 | var deferred = $q.defer(), |
88 | 87 | self = this; |