Codebase list python-faraday / upstream/1.0.18
Imported Upstream version 1.0.18 Sophie Brun 8 years ago
53 changed file(s) with 2180 addition(s) and 1352 deletion(s). Raw diff Collapse all Expand all
88 * Francisco Amato
99 * Franco Linares
1010 * Micaela Ranea Sánchez
11 * Ezequiel Tavella
12 * Joaquín López Pereyra
13 * Martín Rocha
1114
1215 Project contributors
1316
1518 * Juan Urbano
1619 * Elian Gidoni
1720 * Andres Tarantini
18 * Ezequiel Tavella
1921 * Martin Tartarelli
2022 * Ronald Iraheta
4545
4646 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).
4747
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)
4949
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
88
99 New features in the latest update
1010 =====================================
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
1121
1222 Feb 26, 2016:
1323 ---
0 1.0.17
0 1.0.18
55
66 '''
77
8 import socket
89 import threading
910 import logging
10 import requests
11 import json
1211 import base64
1312
1413 from flask import Flask, request, jsonify
4948 app = Flask('APISController')
5049
5150 _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
5369
5470 routes = [r for c in _rest_controllers for r in c.getRoutes()]
5571
324340 return self.ok("active plugins cleared")
325341
326342
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
369343 class Route(object):
370344 """ Route class, abstracts information about:
371345 path, handler and methods """
66 '''
77 import requests
88 import json
9 import base64
910
1011
1112 class RestApiClient(object):
8081 def createCred(self, username, password, parent_id):
8182 return self._create(
8283 "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
-7
auth/__init__.py less more
0 '''
1 Faraday Penetration Test IDE
2 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
3 See the file 'doc/LICENSE' for the license information
4
5 '''
6 __all__ = []
+0
-125
auth/manager.py less more
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
-37
auth/users.py less more
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
0 #!/usr/bin/env python2.7
11 # -*- coding: utf-8 -*-
22
33 '''
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)
156156 self._tkt_api_params = self._getValue(tree, CONST_TKTAPIPARAMS,default ="{}")
157157 self._tkt_template = self._getValue(tree, CONST_TKTTEMPLATE,default ="{}")
158158
159
159 self._merge_strategy = None
160160
161161 def getApiConInfo(self):
162162 if str(self._api_con_info_host) == "None" or str(self._api_con_info_port) == "None":
172172 return self._api_con_info_host
173173
174174 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)
176178
177179 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)
179183
180184 def getAppname(self):
181185 return self._appname
279283 def getTktTemplate(self):
280284 return self._tkt_template
281285
286 def getMergeStrategy(self):
287 return self._merge_strategy
288
282289 def setLastWorkspace(self, workspaceName):
283290 self._last_workspace = workspaceName
284291
384391
385392 def setPluginSettings(self, settings):
386393 self._plugin_settings = settings
394
395 def setMergeStrategy(self, strategy):
396 self._merge_strategy = strategy
387397
388398 def indent(self, elem, level=0):
389399 """ Indents the tree to make a pretty view of it. """
411421 tree = self._getTree()
412422
413423 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()
416426 ROOT.append(API_CON_INFO_HOST)
417427
418428 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())
421431 ROOT.append(API_CON_INFO_PORT)
422432
423433 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())
426436 ROOT.append(API_RESTFUL_CON_INFO_PORT)
427437
428438 APPNAME = Element(CONST_APPNAME)
11 <faraday>
22
33 <appname>Faraday - Penetration Test IDE</appname>
4 <version>1.0.17</version>
4 <version>1.0.18</version>
55 <debug_status>0</debug_status>
66 <font>-Misc-Fixed-medium-r-normal-*-12-100-100-100-c-70-iso8859-1</font>
77 <home_path>~/</home_path>
8080
8181 parser_connection = parser.add_argument_group('connection')
8282 parser_profile = parser.add_argument_group('profiling')
83 parser_gui_ex = parser.add_mutually_exclusive_group()
8483
8584 parser_connection.add_argument('-n', '--hostname', action="store",
8685 dest="host",
139138 default=None,
140139 help="Path to the valid CouchDB certificate")
141140
142 parser_gui_ex.add_argument('--gui', action="store", dest="gui",
141 parser.add_argument('--gui', action="store", dest="gui",
143142 default="qt3",
144143 help="Select interface to start faraday. Default = qt3")
145144
146 parser_gui_ex.add_argument('--cli', '--console', action="store_true",
145 parser.add_argument('--cli', action="store_true",
147146 dest="cli",
148147 default=False,
149148 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")
150161
151162 #args = parser.parse_args(['@parser_args.cfg'])
152163 return parser.parse_args()
269280 host = args.host if args.host else host
270281 port_xmlrpc = args.port_xmlrpc if args.port_xmlrpc else port_xmlrpc
271282 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))
275283
276284 CONF.setApiConInfoHost(host)
277285 CONF.setApiConInfoPort(port_xmlrpc)
44 See the file 'doc/LICENSE' for the license information
55
66 '''
7
8 from managers.reports_managers import ReportManager
9
10 from config.configuration import getInstanceConfiguration
11 CONF = getInstanceConfiguration()
712
813
914 class UiFactory(object):
2227 class FaradayUi(object):
2328 def __init__(self, model_controller=None, plugin_manager=None,
2429 workspace_manager=None, gui="qt3"):
25 #self.main_app = main_app
2630 self.model_controller = model_controller
2731 self.plugin_manager = plugin_manager
2832 self.workspace_manager = workspace_manager
33 self.report_manager = None
2934
3035 def getModelController(self):
3136 return self.model_controller
6267
6368 def createLoggerWidget(self):
6469 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
1010 from gui.gui_app import FaradayUi
1111 from gui.nogui.eventwatcher import EventWatcher
1212 import model.guiapi
13 from utils.logs import getLogger
14
15 from config.configuration import getInstanceConfiguration
16 CONF = getInstanceConfiguration()
1317
1418
1519 class GuiApp(FaradayUi):
2428 model.guiapi.notification_center.registerWidget(self.event_watcher)
2529
2630 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)
2844 while True:
2945 if self._stop:
3046 return
2424 import model.api
2525 import model.log
2626 from utils.logs import addHandler
27 from utils.logs import getLogger
2728
2829 from config.configuration import getInstanceConfiguration
2930 CONF = getInstanceConfiguration()
5859 qt.QPixmap(os.path.join(CONF.getImagePath(), "splash2.png")),
5960 qt.Qt.WStyle_StaysOnTop)
6061
62 self.startSplashScreen()
63
6164 def getMainWindow(self):
6265 return self._main_window
6366
6467 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()
6582 self.createLoggerWidget()
83 self.stopSplashScreen()
6684 self._main_window.showAll()
6785 couchURL = CONF.getCouchURI()
6886 if couchURL:
7492 model.api.log("Please configure Couchdb for fancy HTML5 Dashboard (https://github.com/infobyte/faraday/wiki/Couchdb)")
7593 exit_code = self.exec_loop()
7694 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
77106
78107 def createLoggerWidget(self):
79108 self.loghandler.registerGUIOutput(self._main_window.getLogConsole())
168197 model.api.devlog("Looking for the delegation class")
169198 manager = self.getWorkspaceManager()
170199
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))
173208
174209 self.getMainWindow().refreshWorkspaceTreeView()
175210
176211 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")
1111 import model.guiapi as guiapi
1212 import re
1313 import model.hosts as hosts
14 from managers.model_managers import WorkspaceManager
1514 from ui.plugin_settings import *
1615 from ui.vulnerabilities import *
1716 from ui.preferences import *
605604 if url: self._repourl_edit.setText(url)
606605 self.layout.addWidget(hbox1)
607606
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
623607 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()
626608
627609
628610 self.setupButtons({ "ok" : self.ok_pressed,
631613
632614 def getData(self):
633615 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())
639617
640618 def ok_pressed(self):
641619 if self._callback is not None:
10921070 params = self._plugin_settings[plugin_id]
10931071
10941072 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 "")
10971077
10981078 for setting, value in params["settings"].iteritems():
10991079 index = self.t_parameters.numRows()
409409 shell.myemit('clearSelectionSignal')
410410 qt.QApplication.clipboard().setSelectionMode(False)
411411
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
449412 def getTabManager(self):
450413 return self._tab_manager
451414
519482 callback=None)
520483 result = repoconfig_dialog.exec_loop()
521484 if result == qt.QDialog.Accepted:
522 repourl, isReplicated, replics = repoconfig_dialog.getData()
485 repourl = repoconfig_dialog.getData()
523486 api.devlog("repourl = %s" % repourl)
524487 wm = self._main_app.getWorkspaceManager()
525488 if not CouchDbManager.testCouch(repourl):
539502 return
540503
541504 CONF.setCouchUri(repourl)
542 CONF.setCouchIsReplicated(isReplicated)
543 CONF.setCouchReplics(replics)
544505 CONF.saveConfig()
545506
546507 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()
66 See the file 'doc/LICENSE' for the license information
77
88 '''
9 from utils.logs import getLogger
9
10 import os
11 from couchdbkit import designer
12
1013 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
1814
1915 CONF = getInstanceConfiguration()
2016
6460
6561 def get_all_views(self):
6662 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
-129
managers/model_managers.py less more
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
1111 import time
1212 import traceback
1313 import re
14 import requests
14
1515 try:
1616 import xml.etree.cElementTree as ET
1717
1818 except ImportError:
1919 print "cElementTree could not be imported. Using ElementTree instead"
2020 import xml.etree.ElementTree as ET
21 from apis.rest.api import PluginControllerAPIClient
21 from apis.rest.client import PluginControllerAPIClient
2222
2323 from config.configuration import getInstanceConfiguration
2424 CONF = getInstanceConfiguration()
2525
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
2758
2859 class ReportManager(threading.Thread):
29 def __init__(self, timer, plugin_controller, path=None):
60 def __init__(self, timer, ws_name):
3061 threading.Thread.__init__(self)
3162 self.setDaemon(True)
3263 self.timer = timer
3364 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)
3874
3975 def run(self):
4076 tmp_timer = 0
5389 def stop(self):
5490 self._stop = True
5591
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
7192 def syncReports(self):
7293 """
7394 Synchronize report directory using the DataManager and Plugins online
79100 if root == self._report_path:
80101 for name in files:
81102 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
99106 os.rename(filename, os.path.join(self._report_ppath, name))
100107
101108 self.onlinePlugins()
102109
103
104110 def onlinePlugins(self):
105111 """
106112 Process online plugins
107113 """
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)
124122
125123
126124 class ReportParser(object):
193191 if report_type == "zip":
194192 result = "maltego"
195193
196 elif report_type == "xml":
194 else:
197195
198196 try:
199197 for event, elem in ET.iterparse(f, ('start', )):
225223 elif "report" == tag:
226224 if re.search("https://raw.githubusercontent.com/Arachni/arachni/", output) != None:
227225 return "arachni_faraday"
228 elif re.search("OpenVAS", output) != None:
226 elif re.search("OpenVAS", output) != None or re.search('<omp><version>', output) != None:
229227 return "openvas"
230228 else:
231229 return "zap"
251249 return "nexpose"
252250 elif "NexposeReport" == tag:
253251 return "nexpose-full"
254 elif "SCAN" == tag:
252 elif "ASSET_DATA_REPORT" == tag:
255253 return "qualysguard"
256254 elif "scanJob" == tag:
257255 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()]
66 '''
77
88 import os
9 import socket
910 import zipfile
1011 import logging
1112
7576 if CONF.getApiConInfo() is None:
7677 CONF.setApiConInfo(hostname, port)
7778 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)
116134
117135
118136 #-------------------------------------------------------------------------------
520538 #getConflicts: get the current conflicts
521539 def getConflicts():
522540 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()
566541
567542 #-------------------------------------------------------------------------------
568543 # EVIDENCE
672647 def showPopup(msg, level="Information"):
673648 return model.log.getNotifier().showPopup(msg, level)
674649
650
651 # Plugin status
652
653 def pluginStart():
654 __model_controller.addPluginStart()
655
656
657 def pluginEnd():
658 __model_controller.addPluginEnd()
659
675660 #-------------------------------------------------------------------------------
676661 def getLoggedUser():
677662 """
1010 import threading
1111 import requests
1212
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
1813 from model.controller import ModelController
1914 from persistence.persistence_managers import DbManager
2015 from controllers.change import ChangeController
21 from managers.model_managers import WorkspaceManager
16 from managers.workspace_manager import WorkspaceManager
2217 import model.api
2318 import model.guiapi
2419 import apis.rest.api as restapi
2520 import model.log
2621 from utils.logs import getLogger
2722 import traceback
28 from managers.all import PluginManager
23 from plugins.manager import PluginManager
2924 from managers.mapper_manager import MapperManager
30 from managers.reports_managers import ReportManager
31
3225 from utils.error_report import exception_handler
3326 from utils.error_report import installThreadExcepthook
3427
3528 from gui.gui_app import UiFactory
29 from model.cli_app import CliApp
3630
3731 from config.configuration import getInstanceConfiguration
3832 CONF = getInstanceConfiguration()
6761 def __init__(self, args):
6862 self._original_excepthook = sys.excepthook
6963
70 self._configuration = CONF
64 self.args = args
7165
72 self._security_manager = SecurityManager()
7366 self._mappers_manager = MapperManager()
7467 self._changes_controller = ChangeController()
7568 self._db_manager = DbManager()
7669
77 self._model_controller = ModelController(
78 self._security_manager,
79 self._mappers_manager)
70 self._model_controller = ModelController(self._mappers_manager)
8071
8172 self._plugin_manager = PluginManager(
8273 os.path.join(CONF.getConfigPath(), "plugins"),
8374 self._mappers_manager)
8475
85 self._reports_manager = ReportManager(10, self._plugin_manager.createController("ReportManager"))
86
8776 self._workspace_manager = WorkspaceManager(
8877 self._db_manager,
8978 self._mappers_manager,
90 self._changes_controller,
91 self._reports_manager)
79 self._changes_controller)
9280
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,
9486 self._plugin_manager,
9587 self._workspace_manager,
96 args.gui)
97
98 self.gui_app.setSplashImage(os.path.join(
99 CONF.getImagePath(), "splash2.png"))
88 self.args.gui)
10089
10190 self.timer = TimerClass()
10291 self.timer.start()
10594 sys.excepthook = exception_handler
10695 installThreadExcepthook()
10796
108 def disableLogin(self):
109 CONF.setAuth(sys.disablelogin)
110
11197 def start(self):
11298 try:
113
114 self.gui_app.startSplashScreen()
115 self.gui_app.splashMessage("Starting Faraday")
116
11799 signal.signal(signal.SIGINT, self.ctrlC)
118100
119 logged = True
101 model.api.devlog("Starting application...")
102 model.api.devlog("Setting up remote API's...")
120103
121 while True:
104 if not self.args.workspace:
105 workspace = CONF.getLastWorkspace()
106 self.args.workspace = workspace
122107
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)
124114
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...")
131116
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())
138125
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...")
152127
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)
182129
183130 except Exception:
184131 print "There was an error while starting Faraday"
185132 print "-" * 50
186133 traceback.print_exc()
187134 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:
194135 exit_code = -1
195136
196 return self.__exit(exit_code)
137 finally:
138 return self.__exit(exit_code)
197139
198140 def __exit(self, exit_code=0):
199141 """
200142 Exits the application with the provided code.
201143 It also waits until all app threads end.
202144 """
203 model.api.devlog("Closing Faraday...")
145 model.api.log("Closing Faraday...")
204146 model.api.devlog("stopping model controller thread...")
205147 model.api.stopAPIServer()
206148 restapi.stopServer()
207 self._reports_manager.stop()
208149 self._changes_controller.stop()
209150 self._model_controller.stop()
210151 self._model_controller.join()
211 self.gui_app.quit()
212152 self.timer.stop()
213153 model.api.devlog("Waiting for controller threads to end...")
214154 return exit_code
217157 """
218158 Redefined quit handler to nicely end up things
219159 """
220 self.gui_app.quit()
160 self.app.quit()
221161
222162 def ctrlC(self, signal, frame):
223163 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)
33 See the file 'doc/LICENSE' for the license information
44
55 '''
6 """
7 Contains base classes used to represent the application model
8 and some other common objects and functions used in the model
9 """
106 import sys
117 import os
128 import traceback
1713 import json
1814 import model
1915 from conflict import ConflictUpdate
20 from model.diff import ModelObjectDiff
16 from model.diff import ModelObjectDiff, MergeSolver
2117
2218 try:
2319 import model.api as api
3026 from time import time
3127 import cPickle as pickle
3228 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
3336
3437 class MetadataUpdateActions(object):
3538 """Constants for the actions made on the update"""
3639 UNDEFINED = -1
3740 CREATE = 0
3841 UPDATE = 1
42
3943
4044 class Metadata(object):
4145 """To save information about the modification of ModelObjects.
149153 if prop1 in self.defaultValues(): return prop2
150154 elif prop2 in self.defaultValues(): return prop1
151155 elif self.tieBreakable(key): return self.tieBreak(key, prop1, prop2)
152 else: return (prop2, prop1)
156 else: return (prop1, prop2)
153157
154158 def tieBreakable(self, key):
155159 return False
163167 for k, v in diff.getPropertiesDiff().items():
164168 attribute = self.__getAttribute(k)
165169 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:
167181 conflict = True
168 else:
169 setattr(self, attribute, prop_update)
170182 if conflict:
171183 self.updates.append(ConflictUpdate(self, newModelObject))
172184 return conflict
7474 EDITCRED = 2048
7575 ADDCRED = 2049
7676 DELCRED = 2050
77 PLUGINSTART = 3000
78 PLUGINEND = 3001
7779
7880 __descriptions = {
7981 ADDHOST: "ADDHOST",
114116 ADDVULN: "ADDVULN",
115117 DELVULN: "DELVULN",
116118 ADDCRED: "ADDCRED",
117 DELCRED: "DELCRED"
119 DELCRED: "DELCRED",
120 PLUGINSTART: "PLUGINSTART",
121 PLUGINEND: "PLUGINEND"
118122 }
119123
120124 @staticmethod
124128
125129 class ModelController(threading.Thread):
126130
127 def __init__(self, security_manager, mappers_manager):
131 def __init__(self, mappers_manager):
128132 threading.Thread.__init__(self)
129133
130 self.__sec = security_manager
131134 self.mappers_manager = mappers_manager
132135
133136 # set as daemon
134137 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
143138
144139 # flag to stop daemon thread
145140 self._stop = False
146141 # locks needed to make model thread-safe
147142 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()
148147
149148 #TODO: check if it is better using collections.deque
150149 # a performance analysis should be done
241240 modelactions.EDITNOTE: self.__edit,
242241 modelactions.EDITCRED: self.__edit,
243242 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
245247 }
246248
247249 def run(self):
349351 """
350352 while True:
351353 # 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
354357 # first we check if there is a sync api request
355358 # or if the model is being saved/sync'ed
356359 # or if we have pending duplicated hosts that need to be
384387 # because if we don't do it, the daemon will be blocked forever
385388 pass
386389 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())
389392
390393 def sync_lock(self):
391394 self._sync_api_request = True
652655 res = True
653656 return res
654657
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
655676 def addVulnToInterfaceASYNC(self, host, intId, newVuln):
656677 self.__addPendingAction(modelactions.ADDVULNINT, newVuln, intId)
657678
887908 hosts_mapper = self.mappers_manager.getMapper(model.hosts.Host.__name__)
888909 return hosts_mapper.find(name)
889910
890 def getHostsCount(self):
891 return len(self._hosts)
892
893911 def getAllHosts(self):
894912 hosts = self.mappers_manager.getMapper(
895913 model.hosts.Host.__name__).getAll()
916934
917935 for hostname in intr.getHostnames():
918936 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)
33 See the file 'doc/LICENSE' for the license information
44
55 '''
6
7
68 class ModelObjectDiff(object):
79 def __init__(self, objLeft, objRight):
810 if not isinstance(objLeft, objRight.__class__):
1113 self.obj1, self.obj2 = objLeft, objRight
1214
1315 self.conflicting = []
14 self.conflicting.extend(self.getPropertiesDiff())
15
16 self.conflicting.extend(self.getPropertiesDiff())
17
1618 self.only_in_obj1 = {}
1719 self.only_in_obj2 = {}
1820
1921 def existDiff(self):
2022 return bool(self.conflicting) or bool(self.only_in_obj1) or bool(self.only_in_obj2)
2123
22
23 def getPropertiesDiff(self):
24 def getPropertiesDiff(self):
2425 prop_diff = {}
2526 for attrdesc, attrname in self.obj1.publicattrsrefs.items():
2627 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))
2829 prop2 = info(self.obj2.__getattribute__(attrname))
2930 if prop1 != prop2:
3031 prop_diff[attrdesc] = (prop1, prop2)
3536 """ Polymorphic method to get the differences between the list of objects on a ModelObject.
3637 Pass the ObjectDiff class, the unbound method to get all the objects and the one to get one by ID"""
3738
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)]
4641
4742 return (only_in_obj1, only_in_obj2)
4843
4944 def getDifferencesIn(self, getAllFunc):
5045 """ Polymorphic method to get the differences between the list of objects on a ModelObject.
5146 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)]
5649
5750 return only_in_obj1, only_in_obj2
5851
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)
6352
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")
6957
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})
7558
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
8163
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})
8764
88 class InterfaceDiff(ModelObjectDiff):
89 pass
65 class MergeKeepOld(MergeStrategy):
66 @staticmethod
67 def solve(old, new):
68 return old
9069
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)
710710 it returns None otherwise
711711 """
712712 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)
88 import os
99 import shutil
1010 import mockito
11 import restkit
1112 import threading
1213 from urlparse import urlparse
1314 import traceback
223224 super(CouchDbConnector, self).__init__(type=DBTYPE.COUCHDB)
224225 self.db = db
225226 self.saves_counter = 0
226 #self.seq_num = seq_num
227227 self.mutex = threading.Lock()
228 vmanager = ViewsManager()
229 vmanager.addViews(self.db)
230228 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")
232236 self.seq_num = self.db.info()['update_seq']
233 # self._tree = self._createTree(self.getDocs())
234237
235238 def getDocs(self):
236239 if len(self._docs.keys()) == 0:
377380
378381 #@trap_timeout
379382 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")
381388
382389
383390 class AbstractPersistenceManager(object):
540547 def pushReports(self):
541548 vmanager = ViewsManager()
542549 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")
545556 return self.__uri + "/reports/_design/reports/index.html"
546557
547558 def lostConnectionResolv(self):
1919 import traceback
2020 import model.common
2121 import errno
22 from model.common import factory, ModelObjectVuln, ModelObjectVulnWeb, ModelObjectNote, ModelObjectCred
22 from model.common import (
23 factory, ModelObjectVuln, ModelObjectVulnWeb,
24 ModelObjectNote, ModelObjectCred)
2325 from model.hosts import Host, Interface, Service
2426
2527 from model.commands_history import CommandRunInformation
5254
5355
5456 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
9397
9498 __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"
122128 }
123129
124130 @staticmethod
207213 output_queue.put(output)
208214 output_queue.put(None)
209215 output_queue.join()
216
217 self._processAction(modelactions.PLUGINSTART, [])
210218
211219 #model.api.devlog("Core: queue size '%s'" % new_elem_queue.qsize())
212220 while True:
232240 model.api.devlog("PluginController.onCommandFinished - new_elem_queue Exception- something strange happened... unhandled exception?")
233241 model.api.devlog(traceback.format_exc())
234242 break
243 self._processAction(modelactions.PLUGINEND, [])
235244
236245 def _processAction(self, action, parameters):
237246 """
285294 #LOG
286295 modelactions.LOG : model.api.log,
287296 modelactions.DEVLOG : model.api.devlog,
297 # Plugin state
298 modelactions.PLUGINSTART: model.api.pluginStart,
299 modelactions.PLUGINEND: model.api.pluginEnd
288300 }
289301
290302 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
4040 "-e":"execute model directly",
4141 "-o":"output command",
4242 }
43
43
4444
4545 def parseOutputString(self, output, debug = False):
4646 pass
47
48
47
48
4949 file_arg_re = re.compile(r"^.*(-o\s*[^\s]+).*$")
5050 def processCommandString(self, username, current_path, command_string):
5151 """
5555 self._file_output_path=os.path.join(self.data_path,"f_output-%s.txt" % random.uniform(1,10))
5656
5757 parser = argparse.ArgumentParser()
58
58
5959 parser.add_argument('-e')
6060 parser.add_argument('-f')
6161 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)",
6569 r"\1 -o %s" % self._file_output_path,
66 command_string)
70 command_string)
6771 else:
68 final= re.sub(arg_match.group(1),
72 final = re.sub(arg_match.group(1),
6973 r"-o %s" % self._file_output_path,
7074 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))
7478 try:
7579 self.args, unknown = parser.parse_known_args(cmd)
7680 except SystemExit:
7781 pass
78
79 codeEx=""
82
83 codeEx = ""
8084 if self.args.e:
81 codeEx=self.args.e
85 codeEx = self.args.e
8286 elif self.args.f:
8387 with open(current_path + "/" + self.args.f) as f:
8488 codeEx = f.read()
8791 if codeEx:
8892 buffer = StringIO()
8993 sys.stdout = buffer
90
94
9195 try:
92 exec(codeEx)
96 locales = locals()
97 locales.update({'script_parameters' : self.args.p})
98 exec(codeEx, globals(), locales)
99
93100 except Exception:
94101 api.devlog("[Error] - Faraday plugin")
95102 api.devlog(traceback.format_exc())
96
103
97104 sys.stdout = sys.__stdout__
98105
99106 try:
102109 f.close()
103110 except:
104111 api.devlog ("[Faraday] Can't save faraday plugin output file")
105 return
112 return
106113
107114 print buffer.getvalue()
108115
115122
116123 def createPlugin():
117124 return FPlugin()
118
00 #!/usr/bin/env python
11 # -*- coding: utf-8 -*-
2
32 '''
43 Faraday Penetration Test IDE
54 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
65 See the file 'doc/LICENSE' for the license information
7
86 '''
7
98 from __future__ import with_statement
109 from plugins import core
1110 from model import api
1413 import pprint
1514 import sys
1615 import random
16 import HTMLParser
1717
1818 try:
1919 import xml.etree.cElementTree as ET
2222 except ImportError:
2323 import xml.etree.ElementTree as ET
2424 ETREE_VERSION = ET.VERSION
25
25
2626 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
2727
2828 current_path = os.path.abspath(os.getcwd())
3636 __email__ = "[email protected]"
3737 __status__ = "Development"
3838
39
40
41
42
4339 class NiktoXmlParser(object):
4440 """
4541 The objective of this class is to parse an xml file generated by the nikto tool.
5147 @param nikto_xml_filepath A proper xml generated by nikto
5248 """
5349 def __init__(self, xml_output):
50
5451 tree = self.parse_xml(xml_output)
55
52
5653 if tree:
5754 self.hosts = [host for host in self.get_hosts(tree)]
5855 else:
5956 self.hosts = []
60
6157
6258 def parse_xml(self, xml_output):
6359 """
8076 """
8177 @return items A list of Host instances
8278 """
83 for host_node in tree.findall('scandetails'):
79 for host_node in tree.find('niktoscan').findall('scandetails'):
8480 yield Host(host_node)
85
86
87
88
81
82
8983 def get_attrib_from_subnode(xml_node, subnode_xpath_expr, attrib_name):
9084 """
9185 Finds a subnode in the item node and the retrieves a value from it
9488 """
9589 global ETREE_VERSION
9690 node = None
97
91
9892 if ETREE_VERSION[0] <= 1 and ETREE_VERSION[1] < 3:
99
93
10094 match_obj = re.search("([^\@]+?)\[\@([^=]*?)=\'([^\']*?)\'",subnode_xpath_expr)
10195 if match_obj is not None:
96
10297 node_to_find = match_obj.group(1)
10398 xpath_attrib = match_obj.group(2)
10499 xpath_value = match_obj.group(3)
105100 for node_found in xml_node.findall(node_to_find):
101
106102 if node_found.attrib[xpath_attrib] == xpath_value:
107103 node = node_found
108104 break
118114 return None
119115
120116
121
122
123
124117 class Item(object):
125118 """
126119 An abstract representation of a Item
132125 @param item_node A item_node taken from an nikto xml tree
133126 """
134127 def __init__(self, item_node):
128
135129 self.node = item_node
136130
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
137136 self.id_nikto = self.node.get('id')
138
139 self.osvdbid = ["BID-"+self.node.get('osvdbid')] if self.node.get('osvdbid') != "0" else []
140137 self.osvdblink = self.node.get('osvdbidlink')
141138 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
148183 def get_text_from_subnode(self, subnode_xpath_expr):
149184 """
150185 Finds a subnode in the host node and the retrieves a value from it.
174209 @param host_node A host_node taken from an nmap xml tree
175210 """
176211 def __init__(self, host_node):
212
177213 self.node = host_node
178214 self.targetip = self.node.get('targetip')
179215 self.targethostname = self.node.get('targethostname')
183219 self.sitename = self.node.get('sitename')
184220 self.siteip = self.node.get('hostheader')
185221 self.items = [item for item in self.get_items()]
186
222
187223 def get_items(self):
188224 """
189225 @return items A list of Host instances
207243 Example plugin to parse nikto output.
208244 """
209245 def __init__(self):
246
210247 core.PluginBase.__init__(self)
211248 self.id = "Nikto"
212249 self.name = "Nikto XML Output Plugin"
259296 global current_path
260297 self._output_file_path = os.path.join(self.data_path,
261298 "nikto_output-%s.xml" % self._rid)
262
263
264299
265300 def parseOutputString(self, output, debug = False ):
266301 """
274309 parser = NiktoXmlParser(output)
275310
276311 for host in parser.hosts:
277
278
279
312
280313 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
289331 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
292341 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
296355 del parser
297356
298357 xml_arg_re = re.compile(r"^.*(-output\s*[^\s]+).*$")
302361 Adds the -oX parameter to get xml output to the command string that the
303362 user has set.
304363 """
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
308373 arg_match = self.xml_arg_re.match(command_string)
309
310374
311375 if arg_match is None:
312376 return re.sub(r"(^.*?nikto(\.pl)?)",
00 #!/usr/bin/env python
11 # -*- coding: utf-8 -*-
2
32 '''
43 Faraday Penetration Test IDE
54 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
65 See the file 'doc/LICENSE' for the license information
7
86 '''
7
98 from __future__ import with_statement
109 from plugins import core
1110 from model import api
1413 import sys
1514
1615 try:
16
1717 import xml.etree.cElementTree as ET
1818 import xml.etree.ElementTree as ET_ORIG
1919 ETREE_VERSION = ET_ORIG.VERSION
2121 import xml.etree.ElementTree as ET
2222 ETREE_VERSION = ET.VERSION
2323
24 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
24 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split('.')]
2525
2626 current_path = os.path.abspath(os.getcwd())
2727
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
3743
3844 class QualysguardXmlParser(object):
3945 """
4652 @param qualysguard_xml_filepath A proper xml generated by qualysguard
4753 """
4854 def __init__(self, xml_output):
55
4956 tree = self.parse_xml(xml_output)
5057 if tree:
5158 self.items = [data for data in self.get_items(tree)]
5259 else:
5360 self.items = []
54
5561
5662 def parse_xml(self, xml_output):
5763 """
6571 try:
6672 tree = ET.fromstring(xml_output)
6773 except SyntaxError, err:
68 self.devlog("SyntaxError: %s. %s" % (err, xml_output))
74 self.devlog('SyntaxError: %s. %s' % (err, xml_output))
6975 return None
7076
7177 return tree
7379 def get_items(self, tree):
7480 """
7581 @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)
8185
8286
8387 class Item(object):
8488 """
85 An abstract representation of a Item
86
87
89 An abstract representation of a Item (HOST)
8890 @param item_node A item_node taken from an qualysguard xml tree
8991 """
90 def __init__(self, item_node):
92 def __init__(self, item_node, tree):
93
9194 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
97100 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
105107 def get_text_from_subnode(self, subnode_xpath_expr):
106108 """
107109 Finds a subnode in the host node and the retrieves a value from it.
114116
115117 return None
116118
119
117120 class Results():
118 def __init__(self, issue_node,parent):
121
122 def __init__(self, issue_node, glossary):
123
124 #VULN_INFO ElementTree
119125 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.
146180
147181 @return An attribute value
148182 """
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)
150202 if sub_node is not None:
151 return sub_node.text
203 return cleaner_unicode(sub_node.text)
152204
153205 return None
154206
157209 Example plugin to parse qualysguard output.
158210 """
159211 def __init__(self):
212
160213 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'
166219 self.options = None
167220 self._current_output = None
168221 self._command_regex = re.compile(r'^(sudo qualysguard|\.\/qualysguard).*?')
169222
170223 global current_path
171224 self._output_file_path = os.path.join(self.data_path,
172 "qualysguard_output-%s.xml" % self._rid)
225 'qualysguard_output-%s.xml' % self._rid)
173226
174227 def parseOutputString(self, output, debug = False):
175
176
228
177229 parser = QualysguardXmlParser(output)
230
178231 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
182245 for v in item.vulns:
246
183247 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
185258 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
194272 else:
195 web=False
196
273 web = False
274
197275 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
201302 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
210313 del parser
211
314
212315 def processCommandString(self, username, current_path, command_string):
213316 return None
214
215317
216318 def setHost(self):
217319 pass
218
219320
220321 def createPlugin():
221322 return QualysguardPlugin()
2121 except ImportError:
2222 import xml.etree.ElementTree as ET
2323 ETREE_VERSION = ET.VERSION
24
24
2525 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
2626
2727 current_path = os.path.abspath(os.getcwd())
3535 __email__ = "[email protected]"
3636 __status__ = "Development"
3737
38
39
40
38
39
40
4141
4242 class W3afXmlParser(object):
4343 """
5555 self.host = None
5656
5757 tree = self.parse_xml(xml_output)
58
58
5959 if tree:
6060 self.items = [data for data in self.get_items(tree)]
6161 else:
6262 self.items = []
63
63
6464
6565 def parse_xml(self, xml_output):
6666 """
8484 @return items A list of Host instances
8585 """
8686 bugtype=""
87
88
87
88
8989 scaninfo = tree.findall('scan-info')[0]
9090 self.target = scaninfo.get('target')
9191 host = re.search("(http|https|ftp)\://([a-zA-Z0-9\.\-]+(\:[a-zA-Z0-9\.&amp;%\$\-]+)*@)*((25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])|localhost|([a-zA-Z0-9\-]+\.)*[a-zA-Z0-9\-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))[\:]*([0-9]+)*([/]*($|[a-zA-Z0-9\.\,\?\'\\\+&amp;%\$#\=~_\-]+)).*?$", self.target)
92
92
9393 self.protocol = host.group(1)
9494 self.host = host.group(4)
9595 if self.protocol == 'https':
9696 self.port=443
9797 if host.group(11) is not None:
9898 self.port = host.group(11)
99
99
100100 for node in tree.findall('vulnerability'):
101101 yield Item(node)
102102 for node in tree.findall('information'):
103103 yield Item(node)
104
105
106
107
104
105
106
107
108108 def get_attrib_from_subnode(xml_node, subnode_xpath_expr, attrib_name):
109109 """
110110 Finds a subnode in the item node and the retrieves a value from it
113113 """
114114 global ETREE_VERSION
115115 node = None
116
116
117117 if ETREE_VERSION[0] <= 1 and ETREE_VERSION[1] < 3:
118
118
119119 match_obj = re.search("([^\@]+?)\[\@([^=]*?)=\'([^\']*?)\'",subnode_xpath_expr)
120120 if match_obj is not None:
121121 node_to_find = match_obj.group(1)
137137 return None
138138
139139
140
140
141141
142142
143143 class Item(object):
175175 self.resp = tx.find('http-response/status').text
176176 for h in tx.findall('http-response/headers/header'):
177177 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
181183 def do_clean(self,value):
182184 myreturn =""
183185 if value is not None:
184186 myreturn = re.sub("\n","",value)
185187 return myreturn
186
188
187189 def get_text_from_subnode(self, subnode_xpath_expr):
188190 """
189191 Finds a subnode in the host node and the retrieves a value from it.
195197 return sub_node.text
196198
197199 return None
198
199200
200201
201202 class W3afPlugin(core.PluginBase):
221222 global current_path
222223 self._output_file_path = os.path.join(self.data_path,
223224 "w3af_output-%s.xml" % self._rid)
224
225
225226
226227 def parseOutputString(self, output, debug = False):
227228
243244 item.detail, pname=item.param, path=item.url, website=parser.host, severity=item.severity,
244245 method=item.method, request=item.req, resolution=item.resolution, ref=item.ref, response=item.resp)
245246 del parser
246
247
248
249
247
248
249
250
250251
251252
252253 def resolve(self, host):
258259
259260 def processCommandString(self, username, current_path, command_string):
260261 return None
261
262
262263
263264 def setHost(self):
264265 pass
00 #!/usr/bin/env python
11 # -*- coding: utf-8 -*-
2
32 '''
43 Faraday Penetration Test IDE
54 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
65 See the file 'doc/LICENSE' for the license information
7
86 '''
7
98 from __future__ import with_statement
109 from plugins import core
1110 from model import api
1817 import xml.etree.cElementTree as ET
1918 import xml.etree.ElementTree as ET_ORIG
2019 ETREE_VERSION = ET_ORIG.VERSION
20
2121 except ImportError:
2222 import xml.etree.ElementTree as ET
2323 ETREE_VERSION = ET.VERSION
24
24
2525 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
2626
2727 current_path = os.path.abspath(os.getcwd())
3535 __email__ = "[email protected]"
3636 __status__ = "Development"
3737
38
39
40
38 class ParserEtToAscii(ET_ORIG.TreeBuilder):
39 def data(self, data):
40 self._data.append(data.encode("ascii", errors = "backslashreplace"))
41
4142
4243 class ZapXmlParser(object):
4344 """
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.
4547
4648 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
4852 TODO: Test cases.
4953
5054 @param zap_xml_filepath A proper xml generated by zap
5155 """
5256 def __init__(self, xml_output):
57
5358 tree = self.parse_xml(xml_output)
54
55 if tree:
59
60 if tree is not None:
5661 self.sites = [data for data in self.get_items(tree)]
5762 else:
5863 self.sites = []
59
6064
6165 def parse_xml(self, xml_output):
6266 """
6872 @return xml_tree An xml tree instance. None if error.
6973 """
7074 try:
71 tree = ET.fromstring(xml_output)
75 parser = ET_ORIG.XMLParser(target = ParserEtToAscii())
76 parser.feed(xml_output)
77 tree = parser.close()
78
7279 except SyntaxError, err:
7380 print "SyntaxError: %s. %s" % (err, xml_output)
7481 return None
7986 """
8087 @return items A list of Host instances
8188 """
82
8389 for node in tree.findall('site'):
8490 yield Site(node)
8591
8692
87
8893 def get_attrib_from_subnode(xml_node, subnode_xpath_expr, attrib_name):
8994 """
9095 Finds a subnode in the item node and the retrieves a value from it
9398 """
9499 global ETREE_VERSION
95100 node = None
96
101
97102 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
100108 if match_obj is not None:
109
101110 node_to_find = match_obj.group(1)
102111 xpath_attrib = match_obj.group(2)
103112 xpath_value = match_obj.group(3)
113
104114 for node_found in xml_node.findall(node_to_find):
115
105116 if node_found.attrib[xpath_attrib] == xpath_value:
106117 node = node_found
107118 break
117128 return None
118129
119130
120
121
122131 class Site(object):
132
123133 def __init__(self, item_node):
134
124135 self.node = item_node
136
125137 self.host = self.node.get('host')
126138 self.ip = self.resolve(self.host)
127139 self.port = self.node.get('port')
140
128141 self.items = []
129142 for alert in self.node.findall('alerts/alertitem'):
130143 self.items.append(Item(alert))
138151 sub_node = self.node.find(subnode_xpath_expr)
139152 if sub_node is not None:
140153 return sub_node.text
141
142154 return None
143155
144156 def resolve(self, host):
157
145158 try:
146159 return socket.gethostbyname(host)
147160 except:
148161 pass
162
149163 return host
150164
151165 class Item(object):
156170 @param item_node A item_node taken from an zap xml tree
157171 """
158172 def __init__(self, item_node):
173
159174 self.node = item_node
160
161175 self.id=self.get_text_from_subnode('pluginid')
162176 self.name = self.get_text_from_subnode('alert')
163
164
165177 self.severity = self.get_text_from_subnode('riskcode')
166178 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
169188 self.ref=[]
170189 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
174192 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\.&amp;%\$\-]+)*@)*((25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])|localhost|([a-zA-Z0-9\-]+\.)*[a-zA-Z0-9\-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))[\:]*([0-9]+)*([/]*($|[a-zA-Z0-9\.\,\?\'\\\+&amp;%\$#\=~_\-]+)).*?$", 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\.&amp"
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\.\,\?\'\\\+&amp;%\$#\=~_\-]+))"
207 ".*?$",
208 uri)
209
182210 protocol = mregex.group(1)
183211 host = mregex.group(4)
184 port =80
212 port = 80
185213 if protocol == 'https':
186 port=443
214 port = 443
187215 if mregex.group(11) is not None:
188216 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 }
191230 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
195234 def get_text_from_subnode(self, subnode_xpath_expr):
196235 """
197236 Finds a subnode in the host node and the retrieves a value from it.
205244 return None
206245
207246
208
209247 class ZapPlugin(core.PluginBase):
210248 """
211249 Example plugin to parse zap output.
212250 """
213251 def __init__(self):
252
214253 core.PluginBase.__init__(self)
215254 self.id = "Zap"
216255 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"
219258 self.framework_version = "1.0.0"
220259 self.options = None
221260 self._current_output = None
223262 self._command_regex = re.compile(r'^(zap|sudo zap|\.\/zap).*?')
224263
225264 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 )
229270
230271 def parseOutputString(self, output, debug = False):
231272 """
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.
234275
235276 NOTE: if 'debug' is true then it is being run from a test case and the
236277 output being sent is valid.
237278 """
238279
239280 parser = ZapXmlParser(output)
281
240282 for site in parser.sites:
283
241284 host = []
242285 if site.host != site.ip:
243286 host = [site.host]
287
244288 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, "")
251308
252309 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 )
257323
258324 del parser
259
260
261
262
263
264
265
325
266326 def processCommandString(self, username, current_path, command_string):
267327 return None
268
269328
270329 def setHost(self):
271330 pass
6262 "update_controller_action": "ModelControler.newVuln",
6363 "owner": "john"
6464 },
65 "impact": {
66 accountability: false,
67 availability: false,
68 confidentiality: false,
69 integrity: false
70 },
6571 "owned": false,
6672 "severity": "med",
6773 "type": "Vulnerability",
6874 "owner": "john",
6975 "desc": "I'm scared!",
7076 "data": "",
77 "easeofresolution": "simple",
7178 "description": "I'm scared!"
7279 };
7380 vuln2 = {
9097 "update_controller_action": "ModelControler.newVuln",
9198 "owner": "john"
9299 },
100 "impact": {
101 accountability: false,
102 availability: false,
103 confidentiality: false,
104 integrity: false
105 },
93106 "owned": false,
94107 "severity": "med",
95108 "type": "Vulnerability",
96109 "owner": "john",
97110 "desc": "I'm scared!",
98111 "data": "",
112 "easeofresolution": "trivial",
99113 "description": "I'm scared!"
100114 };
101115 vuln3 = {
111125 "update_controller_action": "No model controller call",
112126 "owner": ""
113127 },
128 "easeofresolution": "simple",
114129 "name": "Service Detection",
115130 "obj_id": "008cba9b11897f2d52c53dd953d75fa233a7fffe",
116131 "owned": false,
118133 "parent": "6.7.8",
119134 "refs": [
120135 ],
136 "impact": {
137 accountability: false,
138 availability: false,
139 confidentiality: false,
140 integrity: false
141 },
121142 "severity": "low",
122143 "type": "VulnerabilityWeb",
123144 "method": "",
258279 });
259280
260281 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() {
262283 // we need $scope.gridOptions.data to have all the vulns before calling
263284 // the delete method
264 $scope.$apply();
265 $scope.deleteVuln(vuln1);
266 fakeModal.close();
285 $scope.getCurrentSelection = function() { return []; }
286 $scope.$apply();
287 $scope.delete();
267288 $scope.$apply();
268289
269290 expect($scope.gridOptions.data.length).toEqual(3);
274295 it('call delete with a valid vuln (1.2.3.4) selected and accept modal', function() {
275296 // we need $scope.gridOptions.data to have all the vulns before calling
276297 // 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();
280301 fakeModal.close();
281302 $scope.$apply();
282303
288309 it('call delete with a valid vuln (1.2.3.4) selected and cancel modal', function() {
289310 // we need $scope.gridOptions.data to have all the vulns before calling
290311 // the delete method
291 vuln1.selected_statusreport_controller = true;
312 $scope.getCurrentSelection = function() { return [vuln1]; }
292313 $scope.$apply();
293314 $scope.delete();
294315 fakeModal.dismiss();
300321 expect($scope.gridOptions.data).toContain(vuln3);
301322 });
302323 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]; }
305325 $scope.$apply();
306326 $scope.delete();
307327 fakeModal.close();
452472 });
453473
454474 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 });
456600 });
457601
458602 describe('Status report vuln edition - edit method (modal)', function() {
468612 "severity": "high"
469613 };
470614
471 vuln1.selected_statusreport_controller = true;
472
473 $scope.getCurrentSelection = function() {
474 return [vuln1];
475 };
615 $scope.getCurrentSelection = function() { return [vuln1]; };
476616
477617 $scope.$apply();
478618 $scope.edit();
503643 "owned": true,
504644 "severity": "high"
505645 };
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();
511651
512652 expect($scope.gridOptions.data.length).toEqual(3);
513653 $scope.gridOptions.data.forEach(function(vuln) {
3030 });
3131
3232 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')
3434 .respond(200, '');
3535
36 $httpBackend.expectHEAD('http://localhost:9876/tuvieja');
36 $httpBackend.expectHEAD('http://localhost:9876/test_workspace');
3737 fact = createFactory();
38 fact.exists('tuvieja').then(function(exist){
38 fact.exists('test_workspace').then(function(exist){
3939 expect(exist).toBe(true);
4040 });
4141 $httpBackend.flush();
5454 "description": ""
5555 };
5656
57 var object = {
58 _attachments:
59 { views:
60 {"content_type": "application/javascript"}
61 }
62 };
63
5764 $httpBackend.expectPUT('http://localhost:9876/test_workspace',
5865 workspace).respond(200, {"ok": true});
5966
6067 $httpBackend.expectPUT('http://localhost:9876/test_workspace/test_workspace',
6168 workspace).respond(200, {"ok": true});
6269
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
6378 fact = createFactory();
6479
6580 fact.put(workspace);
81 fact.exists('test_workspace').then(function(exist){
82 expect(exist).toBe(true);
83 });
6684 $httpBackend.flush();
67 expect(workspace_exists).toBe(true);
6885 });
6986
7087 it('Tests if OK Delete are well done', function() {
66
77 import restkit.errors
88 import model
9
910
1011 def simple_decorator(decorator):
1112 '''this decorator can be used to turn simple functions
2324 g.__doc__ = f.__doc__
2425 g.__dict__.update(f.__dict__)
2526 return g
26
27
2827 new_decorator.__name__ = decorator.__name__
2928 new_decorator.__doc__ = decorator.__doc__
3029 new_decorator.__dict__.update(decorator.__dict__)
3130 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
4031
4132
4233 @simple_decorator
4334 def updateLocalMetadata(func):
4435 def wrapper(self, *args, **kwargs):
4536 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)
5337 return func(self, *args, **kwargs)
5438 return wrapper
5539
7054 model.api.devlog("Operation [%s] timeout" % func.__name__)
7155 return func(self, *args, **kwargs)
7256 return wrapper
73
74
749749 /* End of media */
750750 /* Styles for WS list */
751751 .ws-name {
752 font: 250%/3 'Ubuntu',Helvetica,Arial,sans-serif !important;
752 font: 120%/3 'Ubuntu',Helvetica,Arial,sans-serif !important;
753753 font-size: 100%;
754754 }
755755 .ws-link {
77 function($scope, $cookies, $filter, $location, $route, $routeParams, $uibModal, hostsManager, workspacesFact, dashboardSrv, servicesManager) {
88
99 init = function() {
10 $scope.selectall = false;
10 $scope.selectall_service = false;
1111 // current workspace
1212 $scope.workspace = $routeParams.wsId;
1313 //ID of current host
2727 $scope.services = [];
2828 dashboardSrv.getServicesByHost($scope.workspace, hostId)
2929 .then(function(services) {
30 if (services.length > 0) $scope.loadedServices = true;
3031 services.forEach(function(service) {
3132 servicesManager.getService(service.id, $scope.workspace, true)
3233 .then(function(s) {
4950 $scope.pageSize = 10;
5051 $scope.currentPage = 0;
5152 $scope.newCurrentPage = 0;
52
53
5354 if(!isNaN(parseInt($cookies.pageSize))) $scope.pageSize = parseInt($cookies.pageSize);
5455 $scope.newPageSize = $scope.pageSize;
5556
8788
8889 $location.path(url);
8990 };
90
91
9192 $scope.go = function() {
9293 $scope.pageSize = $scope.newPageSize;
9394 $cookies.pageSize = $scope.pageSize;
205206 };
206207
207208 $scope.update = function(services, data) {
208 services.forEach(function(service){
209 services.forEach(function(service){
209210 delete service.selected;
210211 servicesManager.updateService(service, data, $scope.workspace).then(function(s) {
211212 }, function(message){
307308 };
308309
309310 $scope.checkAllServices = function() {
310 $scope.selectall = !$scope.selectall;
311 $scope.selectall_service = !$scope.selectall_service;
311312
312313 tmp_services = filter($scope.services);
313314 tmp_services.forEach(function(service) {
314 service.selected = $scope.selectall;
315 service.selected = $scope.selectall_service;
315316 });
316317 };
317318
332333 }
333334
334335 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);
336337 tmp_data = $filter('filter')(tmp_data, $scope.expression);
337338 tmp_data = tmp_data.splice($scope.pageSize * $scope.currentPage, $scope.pageSize);
338339
77 function($scope, $cookies, $filter, $location, $route, $routeParams, $uibModal, hostsManager, workspacesFact) {
88
99 init = function() {
10 $scope.selectall = false;
10 $scope.selectall_hosts = false;
1111 // hosts list
1212 $scope.hosts = [];
1313 // current workspace
2323 hostsManager.getHosts($scope.workspace)
2424 .then(function(hosts) {
2525 $scope.hosts = hosts;
26 $scope.loadedVulns = true;
2627 $scope.loadIcons();
2728 });
2829
4546 $scope.pageSize = 10;
4647 $scope.currentPage = 0;
4748 $scope.newCurrentPage = 0;
48
49
4950 if(!isNaN(parseInt($cookies.pageSize))) $scope.pageSize = parseInt($cookies.pageSize);
5051 $scope.newPageSize = $scope.pageSize;
5152
9091
9192 $location.path(url);
9293 };
93
94
9495 $scope.go = function() {
9596 $scope.pageSize = $scope.newPageSize;
9697 $cookies.pageSize = $scope.pageSize;
364365 };
365366
366367 $scope.checkAll = function() {
367 $scope.selectall = !$scope.selectall;
368 $scope.selectall_hosts = !$scope.selectall_hosts;
368369
369370 tmp_hosts = filter($scope.hosts);
370371 tmp_hosts.forEach(function(host) {
371 host.selected = $scope.selectall;
372 host.selected = $scope.selectall_hosts;
372373 });
373374 };
374375
389390 }
390391
391392 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);
393394 tmp_data = $filter('filter')(tmp_data, $scope.expression);
394395 tmp_data = tmp_data.splice($scope.pageSize * $scope.currentPage, $scope.pageSize);
395396
396397 return tmp_data;
397398 };
398
399
399400 init();
400401 }]);
3636 <div class="form-group">
3737 <div class="input-group input-group-sm">
3838 <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"
4040 placeholder="enter keywords" ng-change="currentPage = 0" ng-model="searchParams" />
4141 <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>
4444 </span>
4545 </div>
4646 </div>
47 </form>
47 </form>
4848 </div>
4949 <table class="status-report hosts-list table table-responsive">
5050 <thead>
7272 </thead>
7373 <tbody>
7474 <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"
7777 selection-model-selected-class="multi-selected"
7878 selection-model-on-change="selectedHosts()">
7979 <td><input type="checkbox" name="{{host._id}}"/></td>
6262 <input type="text" class="form-control" id="filter-by"
6363 placeholder="enter keywords" ng-change="currentPage = 0" ng-model="searchParams" />
6464 <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>
6767 </span>
6868 </div>
6969 </div>
70 </form>
70 </form>
7171 </div>
7272 <table class="status-report hosts-list table table-responsive">
7373 <thead>
6363 //set gridApi on scope
6464 $scope.gridApi = gridApi;
6565 $scope.gridApi.selection.on.rowSelectionChanged( $scope, function ( rowChanged ) {
66 $scope.selectionChange();
6667 if ( typeof(rowChanged.treeLevel) !== 'undefined' && rowChanged.treeLevel > -1 ) {
6768 // this is a group header
6869 children = $scope.gridApi.treeBase.getRowChildren( rowChanged );
720721 }
721722 });
722723 modal.result.then(function(data) {
723 $scope.selectedVulns().forEach(function(vuln) {
724 $scope.getCurrentSelection().forEach(function(vuln) {
724725 var references = vuln.refs.concat([]);
725726 data.refs.forEach(function(ref) {
726727 if(vuln.refs.indexOf(ref) == -1){
898899 }
899900 };
900901
902 $scope.selectionChange = function() {
903 $scope.vulnWebSelected = $scope.getCurrentSelection().some(function(v) {
904 return v.type === "VulnerabilityWeb"
905 });
906 };
907
901908 init();
902909 }]);
9292 <input type="text" class="form-control" id="filter-by"
9393 placeholder="enter keywords" ng-change="currentPage = 0" ng-model="searchParams" />
9494 <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>
9797 </span>
9898 </div>
9999 </div>
184184 loadAtt,
185185 self = this,
186186 url = BASEURL + self.ws + "/" + self._id;
187
187
188188 self.populate().then(function(resp) {
189189 $http.put(url, resp)
190190 .success(function(data) {
22 // See the file 'doc/LICENSE' for the license information
33
44 angular.module('faradayApp')
5 .factory('vulnsManager',
5 .factory('vulnsManager',
66 ['Vuln', 'WebVuln', 'BASEURL', '$filter', '$http', '$q', 'attachmentsFact', 'hostsManager', 'servicesManager',
77 function(Vuln, WebVuln, BASEURL, $filter, $http, $q, attachmentsFact, hostsManager, servicesManager) {
88 var vulnsManager = {};
9
9
1010 vulnsManager.vulns = [];
1111 vulnsManager.vulns_indexes = {};
1212
7676 deferred.reject(err);
7777 });
7878 } catch(e) {
79 console.log(e.stack);
8079 deferred.reject(e.name + ": " + e.message);
8180 }
8281
8382 return deferred.promise;
8483 };
85
84
8685 vulnsManager.deleteVuln = function(vuln) {
8786 var deferred = $q.defer(),
8887 self = this;