Codebase list python-faraday / f78543d
Merge tag 'upstream/1.0.19' into kali/master Upstream version 1.0.19 Sophie Brun 8 years ago
59 changed file(s) with 3690 addition(s) and 2272 deletion(s). Raw diff Collapse all Expand all
2020 * Andres Tarantini
2121 * Martin Tartarelli
2222 * Ronald Iraheta
23 * Thierry Beauquier
66 Designed for simplicity, users should notice no difference between their own terminal application and the one included in Faraday. Developed with a specialized set of functionalities that help users improve their own work. Do you remember yourself programming without an IDE? Well, Faraday does the same as an IDE does for you when programming, but from the perspective of a penetration test.
77
88 Please read the [RELEASE notes](https://github.com/infobyte/faraday/blob/master/RELEASE.md)!
9
10 ![GUI - Web](https://raw.github.com/wiki/infobyte/faraday/images/GUI_Dashboard_new.png)
911
1012 Plugins
1113 ---
3032
3133 This applies only to Debian, Ubuntu, Kali and Backtrack. For the full installation guide [visit our wiki](https://github.com/infobyte/faraday/wiki/Installation).
3234
33 Download the [latest tarball](https://github.com/infobyte/faraday/tarball/master) or clone the [Faraday Git Project](https://github.com/infobyte/faraday repository):
35 Download the [latest tarball](https://github.com/infobyte/faraday/tarball/master) or clone our repo:
3436
3537 ```
3638 $ git clone https://github.com/infobyte/faraday.git faraday-dev
4345 ---
4446 Want to read more about the project? Try our [wiki](https://github.com/infobyte/faraday/wiki).
4547
46 Already a user and have a question or bug report? Please check out our [FAQ](https://github.com/infobyte/faraday/wiki/FAQ). If you're still having troubles you can [open a ticket](https://github.com/infobyte/faraday/issues/new).
48 Already a user and have a question or bug report? Check out our [FAQ](https://github.com/infobyte/faraday/wiki/FAQ) and [troubleshooting](https://github.com/infobyte/faraday/wiki/troubleshooting) pages. If you're still having troubles you can [open a ticket](https://github.com/infobyte/faraday/issues/new).
4749
4850 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)
4951
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)
52 Do you have a question? Troubleshooting? Joing our IRC channel #faraday-dev in [freenode](ircs://irc.freenode.net/faraday-dev) or access directly 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)
5153
88
99 New features in the latest update
1010 =====================================
11
12 Apr 29, 2016:
13 ---
14 * Added Open services count to Hosts list in WEB UI
15 * Improved zsh integration
16 * Added GTK3 interface prototype
17 * Added plugin detection through report name
18 * Fixed an error in wcscan script
19 * Fixed nikto plugin
20 * Fixed openvas plugin
1121
1222 Apr 04, 2016
1323 ---
0 1.0.18
0 1.0.19
1515 from tornado.httpserver import HTTPServer
1616 from tornado.ioloop import IOLoop
1717
18 from plugins.core import PluginControllerForApi
1918 from model.visitor import VulnsLookupVisitor
2019
2120 import utils.logs as logger
4039 _http_server.stop()
4140
4241
43 def startAPIs(plugin_manager, model_controller, mapper_manager, hostname, port):
42 def startAPIs(plugin_controller, model_controller, hostname, port):
4443 global _rest_controllers
4544 global _http_server
46 _rest_controllers = [PluginControllerAPI(plugin_manager, mapper_manager), ModelControllerAPI(model_controller)]
45 _rest_controllers = [PluginControllerAPI(plugin_controller), ModelControllerAPI(model_controller)]
4746
4847 app = Flask('APISController')
4948
9190 def badRequest(self, message):
9291 error = 400
9392 return jsonify(error=error,
94 message=message), error
93 message=message)
9594
9695 def noContent(self, message):
9796 code = 204
9897 return jsonify(code=code,
99 message=message), code
98 message=message)
10099
101100 def ok(self, message):
102101 code = 200
286285
287286
288287 class PluginControllerAPI(RESTApi):
289 def __init__(self, plugin_manager, mapper_manager):
290 self.plugin_controller = PluginControllerForApi(
291 "PluginController",
292 plugin_manager.getPlugins(),
293 mapper_manager)
288 def __init__(self, plugin_controller):
289 self.plugin_controller = plugin_controller
294290
295291 def getRoutes(self):
296292 routes = []
297293 routes.append(Route(path='/cmd/input',
298 view_func=self.postCmdInput,
299 methods=['POST']))
294 view_func=self.postCmdInput,
295 methods=['POST']))
300296 routes.append(Route(path='/cmd/output',
301 view_func=self.postCmdOutput,
302 methods=['POST']))
297 view_func=self.postCmdOutput,
298 methods=['POST']))
303299 routes.append(Route(path='/cmd/active-plugins',
304 view_func=self.clearActivePlugins,
305 methods=['DELETE']))
300 view_func=self.clearActivePlugins,
301 methods=['DELETE']))
306302 return routes
307303
308 def pluginAvailable(self, new_cmd, output_file):
304 def pluginAvailable(self, plugin, cmd):
309305 code = 200
310306 return jsonify(code=code,
311 cmd=new_cmd,
312 custom_output_file=output_file)
307 cmd=cmd,
308 plugin=plugin)
313309
314310 def postCmdInput(self):
315311 json_data = request.get_json()
316312 if 'cmd' in json_data.keys():
317 cmd = json_data.get('cmd')
318 has_plugin, new_cmd, output_file = self.plugin_controller.\
319 processCommandInput(cmd)
320 if has_plugin:
321 return self.pluginAvailable(new_cmd, output_file)
322 return self.noContent("no plugin available for cmd")
323 #cmd not sent, bad request
324 return self.badRequest("cmd parameter not sent")
313 if 'pid' in json_data.keys():
314 if 'pwd' in json_data.keys():
315 try:
316 cmd = base64.b64decode(json_data.get('cmd'))
317 pwd = base64.b64decode(json_data.get('pwd'))
318 except:
319 cmd = ''
320 pwd = ''
321 pid = json_data.get('pid')
322 plugin, new_cmd = self.plugin_controller.\
323 processCommandInput(pid, cmd, pwd)
324 if plugin:
325 return self.pluginAvailable(plugin, new_cmd)
326 else:
327 return self.noContent("no plugin available for cmd")
328 else:
329 return self.badRequest("pwd parameter not sent")
330 else:
331 return self.badRequest("pid parameter not sent")
332 else:
333 return self.badRequest("cmd parameter not sent")
334
335
325336
326337 def postCmdOutput(self):
327338 json_data = request.get_json()
328 if 'cmd' in json_data.keys():
339 if 'pid' in json_data.keys():
329340 if 'output' in json_data.keys():
330 cmd = json_data.get('cmd')
331 output = base64.b64decode(json_data.get('output'))
332 if self.plugin_controller.onCommandFinished(cmd, output):
333 return self.ok("output successfully sent to plugin")
334 return self.badRequest("output received but no active plugin")
341 if 'exit_code' in json_data.keys():
342 pid = json_data.get('pid')
343 output = base64.b64decode(json_data.get('output'))
344 exit_code = json_data.get('exit_code')
345 if self.plugin_controller.onCommandFinished(
346 pid, exit_code, output):
347 return self.ok("output successfully sent to plugin")
348 return self.badRequest(
349 "output received but no active plugin")
350 return self.badRequest("exit_code parameter not sent")
335351 return self.badRequest("output parameter not sent")
336 return self.badRequest("cmd parameter not sent")
352 return self.badRequest("pid parameter not sent")
337353
338354 def clearActivePlugins(self):
339355 self.plugin_controller.clearActivePlugins()
11 <faraday>
22
33 <appname>Faraday - Penetration Test IDE</appname>
4 <version>1.0.18</version>
4 <version>1.0.19</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>
1212 CONST_FARADAY_QTRC_PATH = 'deps/qtrc'
1313 CONST_FARADAY_IMAGES = 'images/'
1414 CONST_FARADAY_LOGS_PATH = 'logs/'
15 CONST_FARADAY_FOLDER_LIST = [ "config", "data", "images",
15 CONST_FARADAY_FOLDER_LIST = [ "config", "data", "images",
1616 "persistence", "plugins",
1717 "report", "temp", "zsh", "logs" ]
1818
2323 CONST_FARADAY_QTRC_BACKUP = '~/.qt/.qtrc_faraday.bak'
2424 CONST_FARADAY_ZSHRC = "zsh/.zshrc"
2525 CONST_FARADAY_ZSH_FARADAY = "zsh/faraday.zsh"
26 CONST_FARADAY_ZSH_PLUGIN = "zsh/plugin_controller_client.py"
26 CONST_FARADAY_ZSH_OUTPUT_PATH = "zsh/output"
2727 CONST_FARADAY_BASE_CFG = "config/default.xml"
2828 CONST_FARADAY_USER_CFG = "config/config.xml"
2929 CONST_FARADAY_LIB_HELPERS = "shell/core/_helpers.so"
5353 FARADAY_USER_ZSHRC = os.path.join(FARADAY_USER_HOME, CONST_FARADAY_ZSHRC)
5454 FARADAY_USER_ZSH_PATH = os.path.join(FARADAY_USER_HOME, CONST_ZSH_PATH)
5555 FARADAY_BASE_ZSH = os.path.join(FARADAY_BASE, CONST_FARADAY_ZSH_FARADAY)
56 FARADAY_BASE_ZSH_PLUGIN = os.path.join(FARADAY_BASE,
57 CONST_FARADAY_ZSH_PLUGIN)
5856
5957 USER_QT = os.path.expanduser(CONST_USER_QT_PATH)
6058 USER_QTRC = os.path.expanduser(CONST_USER_QTRC_PATH)
140138
141139 parser.add_argument('--gui', action="store", dest="gui",
142140 default="qt3",
143 help="Select interface to start faraday. Default = qt3")
141 help="Select interface to start faraday. Supported values are "
142 "qt3 (deprecated), gtk and 'no' (no GUI at all). Defaults to qt3")
144143
145144 parser.add_argument('--cli', action="store_true",
146145 dest="cli",
299298
300299 logger.info("All done. Opening environment.")
301300 #TODO: Handle args in CONF and send only necessary ones.
302 # Force OSX to run no gui
303 if sys.platform == "darwin":
304 args.gui = "no-gui"
305301
306302 main_app = MainApplication(args)
307303
399395 f.seek(0, 0)
400396 f.write('ZDOTDIR=$OLDZDOTDIR' + '\n' + content)
401397 with open(FARADAY_USER_ZSHRC, "a") as f:
402 f.write("source %s" % FARADAY_BASE_ZSH)
398 f.write("source \"%s\"" % FARADAY_BASE_ZSH)
403399 shutil.copy(FARADAY_BASE_ZSH, FARADAY_USER_ZSH_PATH)
404 shutil.copy(FARADAY_BASE_ZSH_PLUGIN, FARADAY_USER_ZSH_PATH)
405
406400
407401 def setupXMLConfig():
408402 """Checks user configuration file status.
(New empty file)
0 #!/usr/bin/python2.7
1 # -*- coding: utf-8 -*-
2 '''
3 Faraday Penetration Test IDE
4 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
5 See the file 'doc/LICENSE' for the license information
6
7 '''
8
9 import os
10 import sys
11
12 try:
13 import gi
14 except ImportError as e:
15 print ("You are missing Gobject Instrospection. Please install "
16 "version 3.14 or above")
17 sys.exit(1)
18
19 try:
20 gi.require_version('Gtk', '3.0')
21 gi.require_version('Vte', '2.91')
22 except ValueError:
23 print ("WARNING: You don't seem to have installed the recommended versions"
24 " of GTK and VTE. Check install of VTE 2.91 and GTK+3")
25
26 try:
27 from gi.repository import Gio, Gtk, GdkPixbuf, Vte, GLib, GObject, Gdk
28 except ImportError as e:
29 print ("You are missing some of the required dependencies. "
30 "Check that you have GTK+3 and Vte installed.")
31 sys.exit(1)
32
33
34 import model.guiapi
35 import model.api
36 import model.log
37
38 from gui.gui_app import FaradayUi
39 from config.configuration import getInstanceConfiguration
40 from utils.logs import getLogger
41 from appwindow import AppWindow
42
43 from dialogs import PreferenceWindowDialog
44 from dialogs import NewWorkspaceDialog
45 from dialogs import PluginOptionsDialog
46 from dialogs import NotificationsDialog
47 from dialogs import aboutDialog
48 from dialogs import helpDialog
49
50 from mainwidgets import Sidebar
51 from mainwidgets import ConsoleLog
52 from mainwidgets import Terminal
53 from mainwidgets import Statusbar
54
55 from gui.loghandler import GUIHandler
56 from utils.logs import addHandler
57
58 CONF = getInstanceConfiguration()
59
60 class GuiApp(Gtk.Application, FaradayUi):
61 """
62 Creates the application and has the necesary callbacks to FaradayUi
63 Right now handles by itself only the menu, everything is else is
64 appWindow's resposibility as far as the initial UI goes.
65 The dialogs are found inside the dialogs module
66 """
67
68 def __init__(self, model_controller, plugin_manager, workspace_manager,
69 plugin_controller):
70
71 FaradayUi.__init__(self,
72 model_controller,
73 plugin_manager,
74 workspace_manager,
75 plugin_controller)
76
77 Gtk.Application.__init__(self, application_id="org.infobyte.faraday",
78 flags=Gio.ApplicationFlags.FLAGS_NONE)
79
80 icons = CONF.getImagePath() + "icons/"
81 self.icon = GdkPixbuf.Pixbuf.new_from_file(icons + "faraday_icon.png")
82 self.window = None
83
84 def getMainWindow(self):
85 """Returns the main window. This is none only at the
86 the startup, the GUI will create one as soon as do_activate() is called
87 """
88 return self.window
89
90 def createWorkspace(self, name, description="", w_type=""):
91 """Pretty much copy/pasted from the QT3 GUI.
92 Uses the instance of workspace manager passed into __init__ to
93 get all the workspaces names and see if they don't clash with
94 the one the user wrote. If everything's fine, it saves the new
95 workspace and returns True. If something went wrong, return False"""
96
97 if name in self.getWorkspaceManager().getWorkspacesNames():
98
99 model.api.log("A workspace with name %s already exists"
100 % name, "ERROR")
101 status = True
102 else:
103 model.api.log("Creating workspace '%s'" % name)
104 model.api.devlog("Looking for the delegation class")
105 manager = self.getWorkspaceManager()
106 try:
107 w = manager.createWorkspace(name, description,
108 manager.namedTypeToDbType(w_type))
109 CONF.setLastWorkspace(w.name)
110 CONF.saveConfig()
111 status = True
112 except Exception as e:
113 status = False
114 model.guiapi.notification_center.showDialog(str(e))
115
116 return status
117
118 def removeWorkspace(self, button, ws_name):
119 """Removes a workspace. If the workspace to be deleted is the one
120 selected, it moves you first to the default. The clears and refreshes
121 sidebar"""
122
123 model.api.log("Removing Workspace: %s" % ws_name)
124 if CONF.getLastWorkspace() == ws_name:
125 self.openDefaultWorkspace()
126 self.getWorkspaceManager().removeWorkspace(ws_name)
127 self.sidebar.clearSidebar()
128 self.sidebar.refreshSidebar()
129
130 def do_startup(self):
131 """
132 GTK calls this method after Gtk.Application.run()
133 Creates instances of the sidebar, terminal, console log and
134 statusbar to be added to the app window.
135 Sets up necesary acttions on menu and toolbar buttons
136 Also reads the .xml file from menubar.xml
137 """
138 Gtk.Application.do_startup(self) # deep GTK magic
139
140 self.sidebar = Sidebar(self.workspace_manager,
141 self.changeWorkspace,
142 self.removeWorkspace,
143 CONF.getLastWorkspace())
144
145 self.terminal = Terminal(CONF)
146 self.console_log = ConsoleLog()
147 self.statusbar = Statusbar(self.on_click_notifications)
148 self.notificationsModel = Gtk.ListStore(str)
149
150 action = Gio.SimpleAction.new("about", None)
151 action.connect("activate", self.on_about)
152 self.add_action(action)
153
154 action = Gio.SimpleAction.new("help", None)
155 action.connect("activate", self.on_help)
156 self.add_action(action)
157
158 action = Gio.SimpleAction.new("quit", None)
159 action.connect("activate", self.on_quit)
160 self.add_action(action)
161
162 action = Gio.SimpleAction.new("preferences", None)
163 action.connect("activate", self.on_preferences)
164 self.add_action(action)
165
166 action = Gio.SimpleAction.new("pluginOptions", None)
167 action.connect("activate", self.on_pluginOptions)
168 self.add_action(action)
169
170 action = Gio.SimpleAction.new("new", None)
171 action.connect("activate", self.on_new_button)
172 self.add_action(action)
173
174 action = Gio.SimpleAction.new("new_terminal") # new terminal = new tab
175 action.connect("activate", self.on_new_terminal_button)
176 self.add_action(action)
177
178 dirname = os.path.dirname(os.path.abspath(__file__))
179 builder = Gtk.Builder.new_from_file(dirname + '/menubar.xml')
180 builder.connect_signals(self)
181 appmenu = builder.get_object('appmenu')
182 self.set_app_menu(appmenu)
183 helpMenu = builder.get_object('Help')
184 self.set_menubar(helpMenu)
185
186 def do_activate(self):
187 """If there's no window, create one and present it (show it to user).
188 If there's a window, just present it"""
189
190 # We only allow a single window and raise any existing ones
191 if not self.window:
192 # Windows are associated with the application
193 # when the last one is closed the application shuts down
194 self.window = AppWindow(self.sidebar,
195 self.terminal,
196 self.console_log,
197 self.statusbar,
198 application=self,
199 title="Faraday")
200
201 self.window.set_icon(self.icon)
202 self.window.present()
203
204 self.loghandler = GUIHandler()
205 model.guiapi.setMainApp(self)
206 addHandler(self.loghandler)
207 self.loghandler.registerGUIOutput(self.window)
208
209 notifier = model.log.getNotifier()
210 notifier.widget = self.window
211 model.guiapi.notification_center.registerWidget(self.window)
212
213 def postEvent(self, receiver, event):
214 if receiver is None:
215 receiver = self.getMainWindow()
216 if event.type() == 3131:
217 receiver.emit("new_log", event.text)
218 if event.type() == 5100:
219 self.notificationsModel.prepend([event.change.getMessage()])
220 receiver.emit("new_notif")
221 if event.type() == 3132:
222 dialog_text = event.text
223 dialog = Gtk.MessageDialog(self.window, 0,
224 Gtk.MessageType.INFO,
225 Gtk.ButtonsType.OK,
226 dialog_text)
227 dialog.run()
228 dialog.destroy()
229
230 def on_about(self, action, param):
231 """ Defines what happens when you press 'about' on the menu"""
232
233 about_dialog = aboutDialog(self.window)
234 about_dialog.run()
235 about_dialog.destroy()
236
237 def on_help(self, action, param):
238 """Defines what happens when user press 'help' on the menu"""
239
240 help_dialog = helpDialog(self.window)
241 help_dialog.run()
242 help_dialog.destroy()
243
244 def on_preferences(self, action, param):
245 """Defines what happens when you press 'preferences' on the menu.
246 Sends as a callback reloadWsManager, so if the user actually
247 changes her Couch URL, the sidebar will reload reflecting the
248 new workspaces available"""
249
250 preference_window = PreferenceWindowDialog(self.reloadWorkspaces,
251 self.window)
252 preference_window.show_all()
253
254 def reloadWorkspaces(self):
255 """Used in conjunction with on_preferences: close workspace,
256 resources the workspaces available, clears the sidebar of the old
257 workspaces and injects all the new ones in there too"""
258 self.workspace_manager.closeWorkspace()
259 self.workspace_manager.resource()
260 self.sidebar.clearSidebar()
261 self.sidebar.refreshSidebar()
262
263 def on_pluginOptions(self, action, param):
264 """Defines what happens when you press "Plugins" on the menu"""
265 pluginsOption_window = PluginOptionsDialog(self.plugin_manager,
266 self.window)
267 pluginsOption_window.show_all()
268
269 def on_new_button(self, action, params):
270 "Defines what happens when you press the 'new' button on the toolbar"
271 new_workspace_dialog = NewWorkspaceDialog(self.createWorkspace,
272 self.workspace_manager,
273 self.sidebar, self.window)
274 new_workspace_dialog.show_all()
275
276 def on_new_terminal_button(self, action, params):
277 """When the user clicks on the new_terminal button, creates a new
278 instance of the Terminal and tells the window to add it as a new tab
279 for the notebook"""
280 new_terminal = Terminal(CONF)
281 the_new_terminal = new_terminal.getTerminal()
282 AppWindow.new_tab(self.window, the_new_terminal)
283
284 def on_click_notifications(self, button):
285 """Defines what happens when the user clicks on the notifications
286 button."""
287
288 notifications_view = Gtk.TreeView(self.notificationsModel)
289 renderer = Gtk.CellRendererText()
290 column = Gtk.TreeViewColumn("Notifications", renderer, text=0)
291 notifications_view.append_column(column)
292 notifications_dialog = NotificationsDialog(notifications_view,
293 self.delete_notifications,
294 self.window)
295 notifications_dialog.show_all()
296
297 def delete_notifications(self):
298 self.notificationsModel.clear()
299 self.window.emit("clear_notifications")
300
301 def changeWorkspace(self, selection=None):
302 """Pretty much copy/pasted from QT3 GUI.
303 Selection is actually used nowhere, but the connect function is
304 Sidebar passes it as an argument so well there it is"""
305
306 tree_model, treeiter = selection.get_selected()
307 workspaceName = tree_model[treeiter][0]
308
309 try:
310 ws = super(GuiApp, self).openWorkspace(workspaceName)
311 except Exception as e:
312 model.guiapi.notification_center.showDialog(str(e))
313 ws = self.openDefaultWorkspace()
314 workspace = ws.name
315 CONF.setLastWorkspace(workspace)
316 CONF.saveConfig()
317 return ws
318
319 def run(self, args):
320 """First method to run, as defined by FaradayUi. This method is
321 mandatory"""
322
323 workspace = args.workspace
324 try:
325 ws = super(GuiApp, self).openWorkspace(workspace)
326 except Exception as e:
327 getLogger(self).error(
328 ("Your last workspace %s is not accessible, "
329 "check configuration") % workspace)
330 getLogger(self).error(str(e))
331 ws = self.openDefaultWorkspace()
332 workspace = ws.name
333
334 CONF.setLastWorkspace(workspace)
335 CONF.saveConfig()
336 Gtk.Application.run(self)
337
338 def on_quit(self, action, param):
339 self.quit()
0 #!/usr/bin/python2.7
1 # -*- coding: utf-8 -*-
2 '''
3 Faraday Penetration Test IDE
4 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
5 See the file 'doc/LICENSE' for the license information
6
7 '''
8 import gi
9
10 from config.configuration import getInstanceConfiguration
11
12 gi.require_version('Gtk', '3.0')
13 gi.require_version('Vte', '2.91')
14
15 from gi.repository import GLib, Gio, Gtk, GObject, Gdk
16
17 CONF = getInstanceConfiguration()
18
19
20 class _IdleObject(GObject.GObject):
21 """
22 Override GObject.GObject to always emit signals in the main thread
23 by emmitting on an idle handler. Deep magic, do not touch unless
24 you know what you are doing.
25 """
26 def __init__(self):
27 GObject.GObject.__init__(self)
28
29 def emit(self, *args):
30 GObject.idle_add(GObject.GObject.emit, self, *args)
31
32
33 class AppWindow(Gtk.ApplicationWindow, _IdleObject):
34 """The main window of the GUI. Draws the toolbar.
35 Positions the terminal, sidebar, consolelog and statusbar received from
36 the app and defined in the mainwidgets module"""
37
38 __gsignals__ = {
39 "new_log": (GObject.SIGNAL_RUN_FIRST, None, (str, )),
40 "new_notif": (GObject.SIGNAL_RUN_FIRST, None, ()),
41 "clear_notifications": (GObject.SIGNAL_RUN_FIRST, None, ())
42 }
43
44 def __init__(self, sidebar, terminal, console_log, statusbar,
45 *args, **kwargs):
46 super(Gtk.ApplicationWindow, self).__init__(*args, **kwargs)
47
48 # This will be in the windows group and have the "win" prefix
49 glib_variant = GLib.Variant.new_boolean(False)
50 max_action = Gio.SimpleAction.new_stateful("maximize", None,
51 glib_variant)
52 max_action.connect("change-state", self.on_maximize_toggle)
53 self.add_action(max_action)
54
55 self.sidebar = sidebar
56 self.terminal = terminal
57 self.log = console_log
58 self.statusbar = statusbar
59
60 self.icons = CONF.getImagePath() + "icons/"
61
62 # sets up the clipboard
63 self.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
64 self.selection_clipboard = Gtk.Clipboard.get(Gdk.SELECTION_PRIMARY)
65
66 # Keep it in sync with the actual state. Deep dark GTK magic
67 self.connect("notify::is-maximized",
68 lambda obj:
69 max_action.set_state(
70 GLib.Variant.new_boolean(obj.props.is_maximized)))
71
72 # TOP BOX: TOOLBAR AND FILTER
73 self.topBox = Gtk.Box()
74 self.topBox.pack_start(self.create_toolbar(), True, True, 0)
75
76 # SIDEBAR BOX
77 self.sidebarBox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
78 self.sidebarBox.pack_start(self.sidebar.scrollableView, True, True, 0)
79 self.sidebarBox.pack_start(self.sidebar.getButton(), False, False, 0)
80
81 # TERMINAL BOX
82 self.firstTerminalBox = self.terminalBox(self.terminal.getTerminal())
83
84 # MIDDLE BOX: NOTEBOOK AND SIDEBAR
85 self.notebook = Gtk.Notebook()
86 self.notebook.set_scrollable(True)
87 self.notebook.append_page(self.firstTerminalBox, Gtk.Label("1"))
88
89 self.middleBox = Gtk.Box()
90 self.middleBox.pack_start(self.notebook, True, True, 0)
91 self.middleBox.pack_start(self.sidebarBox, False, False, 0)
92
93 # LOGGER BOX: THE LOGGER, DUH
94 self.loggerBox = Gtk.Box()
95 self.loggerBox.pack_start(self.log.getLogger(), True, True, 0)
96
97 # NOTIFACTION BOX: THE BUTTON TO ACCESS NOTIFICATION DIALOG
98 self.notificationBox = Gtk.Box()
99 self.notificationBox.pack_start(self.statusbar.button, True, True, 0)
100
101 # MAINBOX: THE BIGGER BOX FOR ALL THE LITTLE BOXES
102 self.mainBox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
103 self.mainBox.pack_start(self.topBox, False, False, 0)
104 self.mainBox.pack_start(self.middleBox, True, True, 0)
105 self.mainBox.pack_start(self.loggerBox, False, False, 0)
106 self.mainBox.pack_end(self.notificationBox, False, False, 0)
107
108 remove_terminal_icon = Gtk.Image.new_from_file(self.icons + "exit.png")
109 remove_terminal_button = Gtk.Button()
110 remove_terminal_button.set_tooltip_text("Delete current tab")
111 remove_terminal_button.connect("clicked", self.delete_tab)
112 remove_terminal_button.set_image(remove_terminal_icon)
113 remove_terminal_button.set_relief(Gtk.ReliefStyle.NONE)
114 remove_terminal_button.show()
115 at_end = Gtk.PackType.END
116
117 self.notebook.set_action_widget(remove_terminal_button, at_end)
118
119 self.add(self.mainBox)
120 self.tab_number = 0 # 0 indexed, even when it shows 1 to the user
121
122 self.show_all()
123
124 def terminalBox(self, terminal):
125 """Given a terminal, creates an EventBox for the Box that has as a
126 children said terminal"""
127 eventTerminalBox = Gtk.EventBox()
128 terminalBox = Gtk.Box()
129 terminalBox.pack_start(terminal, True, True, 0)
130 eventTerminalBox.connect("button_press_event", self.right_click)
131 eventTerminalBox.add(terminalBox)
132 return eventTerminalBox
133
134 def right_click(self, eventbox, event):
135 """Defines the menu created when a user rightclicks on the
136 terminal eventbox"""
137 menu = Gtk.Menu()
138 copy = Gtk.MenuItem("Copy")
139 paste = Gtk.MenuItem("Paste")
140 menu.append(paste)
141 menu.append(copy)
142
143 # TODO: make accelerators for copy paste work. add accel for paste
144 # accelgroup = Gtk.AccelGroup()
145 # self.add_accel_group(accelgroup)
146 # accellabel = Gtk.AccelLabel("Copy/Paste")
147 # accellabel.set_hexpand(True)
148 # copy.add_accelerator("activate",
149 # accelgroup,
150 # Gdk.keyval_from_name("c"),
151 # Gdk.ModifierType.SHIFT_MASK |
152 # Gdk.ModifierType.CONTROL_MASK,
153 # Gtk.AccelFlags.VISIBLE)
154
155 copy.connect("activate", self.copy_text)
156 paste.connect("activate", self.paste_text)
157
158 copy.show()
159 paste.show()
160 menu.popup(None, None, None, None, event.button, event.time)
161
162 def copy_text(self, button):
163 """What happens when the user copies text"""
164 content = self.selection_clipboard.wait_for_text()
165 self.clipboard.set_text(content, -1)
166
167 def paste_text(self, button):
168 """What happens when the user pastes text"""
169 currentTerminal = self.getCurrentFocusedTerminal()
170 currentTerminal.paste_clipboard()
171
172 def getFocusedTab(self):
173 """Return the focused tab"""
174 return self.notebook.get_current_page()
175
176 def getCurrentFocusedTerminal(self):
177 """Returns the current focused terminal"""
178
179 # the focused terminal is the only children of the notebook
180 # thas has only children an event box that has as only children
181 # the terminal. Yeah, I know.
182
183 currentTab = self.getFocusedTab()
184 currentEventBox = self.notebook.get_children()[currentTab]
185 currentBox = currentEventBox.get_children()[0]
186 currentTerminal = currentBox.get_children()[0]
187 return currentTerminal
188
189 def do_new_log(self, text):
190 """To be used on a new_log signal. Calls a method on log to append
191 to it"""
192 self.log.customEvent(text)
193
194 def do_clear_notifications(self):
195 "On clear_notifications signal, it will return the button label to 0"
196 self.statusbar.button.set_label("0")
197
198 def do_new_notif(self):
199 """On a new notification, increment the button label by one"""
200 self.statusbar.inc_button_label()
201
202 def getLogConsole(self):
203 """Returns the LogConsole. Needed by the GUIHandler logger"""
204 return self.log
205
206 def on_maximize_toggle(self, action, value):
207 """Defines what happens when the window gets the signal to maximize"""
208 action.set_state(value)
209 if value.get_boolean():
210 self.maximize()
211 else:
212 self.unmaximize()
213
214 def refreshSidebar(self):
215 """Call the refresh method on sidebar. It will append new workspaces,
216 but it will *NOT* delete workspaces not found anymore in the current
217 ws anymore"""
218 self.sidebar.refresh()
219
220 def create_toolbar(self):
221 """ Creates toolbar with an open and new button, getting the icons
222 from the stock. The method by which it does this is deprecated,
223 this could be improved"""
224
225 toolbar = Gtk.Toolbar()
226 toolbar.set_hexpand(True)
227 toolbar.get_style_context().add_class(Gtk.STYLE_CLASS_PRIMARY_TOOLBAR)
228 icons = self.icons
229
230 # new_from_stock is deprecated, but should work fine for now
231 new_button_icon = Gtk.Image.new_from_file(icons + "sync.png")
232 new_terminal_icon = Gtk.Image.new_from_file(icons + "newshell.png")
233 toggle_log_icon = Gtk.Image.new_from_file(icons + "debug.png")
234
235 new_terminal_button = Gtk.ToolButton.new(new_terminal_icon, None)
236 new_terminal_button.set_tooltip_text("Create a new tab")
237 new_terminal_button.set_action_name('app.new_terminal')
238 toolbar.insert(new_terminal_button, 0)
239
240 new_button = Gtk.ToolButton.new(new_button_icon, None)
241 new_button.set_tooltip_text("Create a new workspace")
242 toolbar.insert(new_button, 1)
243 new_button.set_action_name('app.new')
244
245 toggle_log_button = Gtk.ToggleToolButton.new()
246 toggle_log_button.set_icon_widget(toggle_log_icon)
247 toggle_log_button.set_active(True) # log enabled by default
248 toggle_log_button.set_tooltip_text("Toggle log console")
249 toggle_log_button.connect("clicked", self.toggle_log)
250 toolbar.insert(toggle_log_button, 2)
251
252 return toolbar
253
254 def new_tab(self, new_terminal):
255 """The on_new_terminal_button redirects here. Tells the window
256 to create pretty much a clone of itself when the user wants a new
257 tab"""
258
259 self.tab_number += 1
260 tab_number = self.tab_number
261 pageN = self.terminalBox(new_terminal)
262 self.notebook.append_page(pageN, Gtk.Label(str(tab_number+1)))
263 self.show_all()
264
265 def delete_tab(self, button):
266 """Deletes the current tab"""
267 current_page = self.notebook.get_current_page()
268 self.notebook.remove_page(current_page)
269 self.reorder_tab_names()
270
271 def reorder_tab_names(self):
272 """When a tab is deleted, all other tabs must be renamed to reacomodate
273 the numbers"""
274 # Tabs are zero indexed, but their labels start at one
275
276 number_of_tabs = self.notebook.get_n_pages()
277 for n in range(number_of_tabs):
278 tab = self.notebook.get_nth_page(n)
279 self.notebook.set_tab_label_text(tab, str(n+1))
280 self.tab_number = number_of_tabs-1
281
282 def toggle_log(self, button):
283 """Reverses the visibility status of the loggerbox"""
284 current_state = self.loggerBox.is_visible()
285 self.loggerBox.set_visible(not current_state)
0 #!/usr/bin/python2.7
1 # -*- coding: utf-8 -*-
2 '''
3 Faraday Penetration Test IDE
4 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
5 See the file 'doc/LICENSE' for the license information
6
7 '''
8 import gi
9 import re
10
11 gi.require_version('Gtk', '3.0')
12
13 from gi.repository import Gtk, GdkPixbuf, Gdk
14 from persistence.persistence_managers import CouchDbManager
15 from utils.common import checkSSL
16 from config.configuration import getInstanceConfiguration
17
18
19 CONF = getInstanceConfiguration()
20
21 """This could probably be made much better with just a little effort.
22 It'd be probably a good idea to make a super class Dialog from which
23 all the dialogs inherit from with the common methods used (particularly the
24 OK and Cancel buttons). Good starting point if we continue on with the idea
25 of using GTK.
26
27 Update: so it seems like Gtk actually already provides a Gtk.Dialog class
28 which would seem practical. All dialogs are already made and it is a
29 convenience class only, but if there's need to add more, it's a good
30 thing to know"""
31
32
33 class PreferenceWindowDialog(Gtk.Window):
34 """Sets up a preference dialog with basically nothing more than a
35 label, a text entry to input your CouchDB IP and a couple of buttons.
36 Takes a callback function to the mainapp so that it can refresh the
37 workspace list and information"""
38
39 def __init__(self, callback, parent):
40 Gtk.Window.__init__(self, title="Preferences")
41 self.parent = parent
42 self.set_size_request(400, 100)
43 self.set_type_hint(Gdk.WindowTypeHint.DIALOG)
44 self.set_transient_for(parent)
45 self.timeout_id = None
46 self.reloadWorkspaces = callback
47
48 vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
49 self.add(vbox)
50
51 self.label = Gtk.Label()
52 self.label.set_text("Your Couch IP")
53 vbox.pack_start(self.label, True, False, 0)
54
55 couch_uri = CONF.getCouchURI()
56 self.entry = Gtk.Entry()
57 text = couch_uri if couch_uri else "http://127.0.0.1:5050"
58 self.entry.set_text(text)
59 vbox.pack_start(self.entry, True, False, 0)
60
61 hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
62 vbox.pack_end(hbox, False, True, 0)
63
64 self.OK_button = Gtk.Button.new_with_label("OK")
65 self.OK_button.connect("clicked", self.on_click_OK)
66
67 hbox.pack_start(self.OK_button, False, True, 0)
68
69 self.cancel_button = Gtk.Button.new_with_label("Cancel")
70 self.cancel_button.connect("clicked", self.on_click_cancel)
71 hbox.pack_end(self.cancel_button, False, True, 0)
72
73 def on_click_OK(self, button):
74 """Defines what happens when user clicks OK button"""
75 repourl = self.entry.get_text()
76 if not CouchDbManager.testCouch(repourl):
77 errorDialog(self, "The provided URL is not valid",
78 "Are you sure CouchDB is running?")
79 elif repourl.startswith("https://"):
80 if not checkSSL(repourl):
81 errorDialog("The SSL certificate validation has failed")
82 else:
83 CONF.setCouchUri(repourl)
84 CONF.saveConfig()
85 self.reloadWorkspaces()
86 self.destroy()
87
88 def on_click_cancel(self, button):
89 self.destroy()
90
91
92 class NewWorkspaceDialog(Gtk.Window):
93 """Sets up the New Workspace Dialog, where the user can set a name,
94 a description and a type for a new workspace. Also checks that the
95 those attributes don't correspond to an existing workspace"""
96
97 def __init__(self, callback, workspace_manager, sidebar, parent):
98
99 Gtk.Window.__init__(self, title="Create New Workspace")
100 self.set_type_hint(Gdk.WindowTypeHint.DIALOG)
101 self.set_transient_for(parent)
102 self.set_size_request(200, 200)
103 self.timeout_id = None
104 self.callback = callback
105 self.sidebar = sidebar
106
107 self.mainBox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
108
109 self.nameBox = Gtk.Box(spacing=6)
110 self.name_label = Gtk.Label()
111 self.name_label.set_text("Name: ")
112 self.name_entry = Gtk.Entry()
113 self.nameBox.pack_start(self.name_label, False, False, 10)
114 self.nameBox.pack_end(self.name_entry, True, True, 10)
115 self.nameBox.show()
116
117 self.descrBox = Gtk.Box(spacing=6)
118 self.descr_label = Gtk.Label()
119 self.descr_label.set_text("Description: ")
120 self.descr_entry = Gtk.Entry()
121 self.descrBox.pack_start(self.descr_label, False, False, 10)
122 self.descrBox.pack_end(self.descr_entry, True, True, 10)
123 self.descrBox.show()
124
125 self.typeBox = Gtk.Box(spacing=6)
126 self.type_label = Gtk.Label()
127 self.type_label.set_text("Type: ")
128 self.comboBox = Gtk.ComboBoxText()
129 for w in workspace_manager.getAvailableWorkspaceTypes():
130 self.comboBox.append_text(w)
131 self.typeBox.pack_start(self.type_label, False, False, 10)
132 self.typeBox.pack_end(self.comboBox, True, True, 10)
133 self.typeBox.show()
134
135 self.buttonBox = Gtk.Box(spacing=6)
136 self.OK_button = Gtk.Button.new_with_label("OK")
137 self.OK_button.connect("clicked", self.on_click_OK)
138 self.cancel_button = Gtk.Button.new_with_label("Cancel")
139 self.cancel_button.connect("clicked", self.on_click_cancel)
140 self.buttonBox.pack_start(self.OK_button, False, False, 10)
141 self.buttonBox.pack_end(self.cancel_button, False, False, 10)
142 self.buttonBox.show()
143
144 self.mainBox.pack_start(self.nameBox, False, False, 0)
145 self.mainBox.pack_start(self.descrBox, False, False, 0)
146 self.mainBox.pack_start(self.typeBox, False, False, 0)
147 self.mainBox.pack_end(self.buttonBox, False, False, 0)
148
149 self.mainBox.show()
150 self.add(self.mainBox)
151
152 def on_click_OK(self, button):
153 letters_or_numbers = r"^[a-z][a-z0-9\_\$()\+\-\/]*$"
154 res = re.match(letters_or_numbers, str(self.name_entry.get_text()))
155 if res:
156 if self.callback is not None:
157 self.__name_txt = str(self.name_entry.get_text())
158 self.__desc_txt = str(self.descr_entry.get_text())
159 self.__type_txt = str(self.comboBox.get_active_text())
160 creation_ok = self.callback(self.__name_txt,
161 self.__desc_txt,
162 self.__type_txt)
163 if creation_ok:
164 self.sidebar.addWorkspace(self.__name_txt)
165 self.destroy()
166 else:
167 errorDialog(self, "Invalid workspace name",
168 "A workspace must be named with "
169 "all lowercase letters (a-z), digi"
170 "ts(0-9) or any of the _$()+-/ "
171 "characters. The name has to start"
172 " with a lowercase letter")
173
174 def on_click_cancel(self, button):
175 self.destroy()
176
177
178 class PluginOptionsDialog(Gtk.Window):
179 """The dialog where the user can see details about installed plugins.
180 It is not the prettiest thing in the world but it works.
181 Creating and displaying the models of each plugin settings is specially
182 messy, there's more info in the appropiate methods"""
183 # TODO: probably stop hardcoding the first plugin, right?
184
185 def __init__(self, plugin_manager, parent):
186
187 Gtk.Window.__init__(self, title="Plugins Options")
188 self.set_type_hint(Gdk.WindowTypeHint.DIALOG)
189 self.set_transient_for(parent)
190 self.set_size_request(800, 300)
191
192 if plugin_manager is not None:
193 self.plugin_settings = plugin_manager.getSettings()
194 else:
195 self.plugin_settings = {}
196
197 self.settings_view = None
198 self.id_of_selected = "Acunetix XML Output Plugin" # first one by name
199 self.models = self.createPluginsSettingsModel()
200 self.setSettingsView()
201
202 plugin_info = self.createPluginInfo(plugin_manager)
203 pluginList = self.createPluginListView(plugin_info)
204 scroll_pluginList = Gtk.ScrolledWindow(None, None)
205 scroll_pluginList.add(pluginList)
206 # scroll_pluginList.set_min_content_width(300)
207 pluginListBox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
208 pluginListBox.pack_start(scroll_pluginList, True, True, 0)
209
210 buttonBox = Gtk.Box()
211 OK_button = Gtk.Button.new_with_label("OK")
212 cancel_button = Gtk.Button.new_with_label("Cancel")
213 OK_button.connect("clicked", self.on_click_OK, plugin_manager)
214 cancel_button.connect("clicked", self.on_click_cancel)
215 buttonBox.pack_start(OK_button, True, True, 0)
216 buttonBox.pack_start(cancel_button, True, True, 0)
217 pluginListBox.pack_start(buttonBox, False, False, 0)
218
219 infoBox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
220 nameBox, versionBox, pluginVersionBox = [Gtk.Box() for i in range(3)]
221
222 nameLabel, versionLabel, pluginVersionLabel = [Gtk.Label()
223 for i in range(3)]
224
225 self.nameEntry, self.versionEntry, self.pluginVersionEntry = [
226 Gtk.Label() for i in range(3)]
227
228 nameLabel.set_text("Name: ")
229 versionLabel.set_text("Version: ")
230 pluginVersionLabel.set_text("Plugin version: ")
231
232 nameBox.pack_start(nameLabel, False, False, 5)
233 nameBox.pack_start(self.nameEntry, False, True, 5)
234 versionBox.pack_start(versionLabel, False, False, 5)
235 versionBox.pack_start(self.versionEntry, False, True, 5)
236 pluginVersionBox.pack_start(pluginVersionLabel, False, False, 5)
237 pluginVersionBox.pack_start(self.pluginVersionEntry, False, True, 5)
238
239 infoBox.pack_start(nameBox, False, False, 5)
240 infoBox.pack_start(versionBox, False, False, 5)
241 infoBox.pack_start(pluginVersionBox, False, False, 5)
242
243 self.pluginSpecsBox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
244 self.pluginSpecsBox.pack_start(infoBox, False, False, 5)
245 self.pluginSpecsBox.pack_start(self.settings_view, True, True, 0)
246
247 self.mainBox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
248 self.mainBox.pack_start(pluginListBox, True, True, 5)
249 self.mainBox.pack_end(self.pluginSpecsBox, True, True, 5)
250
251 self.add(self.mainBox)
252
253 def on_click_OK(self, button, plugin_manager):
254 """On click OK button update the plugins settings and then destroy"""
255 if plugin_manager is not None:
256 plugin_manager.updateSettings(self.plugin_settings)
257 self.destroy()
258
259 def on_click_cancel(self, button):
260 """On click cancel button, destroy brutally. No mercy"""
261 self.destroy()
262
263 def createPluginInfo(self, plugin_manager):
264 """Creates and return a TreeStore where the basic information about
265 the plugins: the plugin ID, name, intended version of the tool
266 and plugin version"""
267 plugin_info = Gtk.TreeStore(str, str, str, str)
268
269 for plugin_id, params in self.plugin_settings.iteritems():
270 plugin_info.append(None, [plugin_id,
271 params["name"],
272 params["version"], # tool version
273 params["plugin_version"]])
274
275 # Sort it!
276 sorted_plugin_info = Gtk.TreeModelSort(model=plugin_info)
277 sorted_plugin_info.set_sort_column_id(1, Gtk.SortType.ASCENDING)
278 return sorted_plugin_info
279
280 def createPluginListView(self, plugin_info):
281 """Creates the view for the left-hand side list of the dialog.
282 It uses an instance of the plugin manager to get a list
283 of all available plugins"""
284
285 plugin_list_view = Gtk.TreeView(plugin_info)
286 renderer = Gtk.CellRendererText()
287 column = Gtk.TreeViewColumn("Title", renderer, text=1)
288 column.set_sort_column_id(1)
289 plugin_list_view.append_column(column)
290
291 selection = plugin_list_view.get_selection()
292 selection.connect("changed", self.on_plugin_selection)
293
294 return plugin_list_view
295
296 def createPluginsSettingsModel(self):
297 """Creates a dictionary with
298 {plugin-name : [(setting-name, setting-value)]} structure. This is used
299 to hold all the plugins settings models"""
300
301 models = {}
302
303 for plugin_id in self.plugin_settings.iteritems():
304 # iter through the plugins
305 plugin_info = plugin_id[1] # get dictionary associated to plugin
306 store = Gtk.ListStore(str, str) # create the store for that plugin
307
308 # iter through settings dictionary
309 for setting in plugin_info["settings"].items():
310 setting_name = setting[0]
311 setting_value = setting[1]
312 store.append([setting_name, setting_value])
313
314 models[plugin_id[1]["name"]] = store # populate dict with store
315 return models
316
317 def createAdecuatePluginSettingView(self, store):
318 """Create the adecuate plugin settings view. The first time this is
319 executed, it will be none and it will tell the view which columns
320 to display. After that, it will just change the model displayed"""
321 self.active_store = store
322
323 if self.settings_view is None:
324 self.settings_view = Gtk.TreeView(store)
325 renderer_text = Gtk.CellRendererText()
326 column_text = Gtk.TreeViewColumn("Settings", renderer_text, text=0)
327 self.settings_view.append_column(column_text)
328
329 renderer_editable_text = Gtk.CellRendererText()
330 renderer_editable_text.set_property("editable", True)
331 renderer_editable_text.connect("edited", self.value_changed)
332 column_editabletext = Gtk.TreeViewColumn("Value",
333 renderer_editable_text,
334 text=1)
335
336 self.settings_view.append_column(column_editabletext)
337
338 else:
339 self.settings_view.set_model(store)
340
341 def value_changed(self, widget, path, text):
342 """Save new settings"""
343 self.active_store[path][1] = text
344 setting = self.active_store[path][0]
345 settings = self.plugin_settings[self.name_of_selected]["settings"]
346 settings[setting.strip()] = text.strip()
347
348 def on_plugin_selection(self, selection):
349 """When the user selects a plugin, it will change the text
350 displeyed on the entries to their corresponding values"""
351
352 model, treeiter = selection.get_selected()
353 self.id_of_selected = model[treeiter][1]
354 self.name_of_selected = model[treeiter][0]
355
356 self.setSettingsView()
357
358 self.nameEntry.set_label(model[treeiter][1])
359 self.versionEntry.set_label(model[treeiter][2])
360 self.pluginVersionEntry.set_label(model[treeiter][3])
361
362 def setSettingsView(self):
363 """Makes the window match the selected plugin with the settings
364 displayed"""
365
366 adecuateModel = self.models[self.id_of_selected]
367 self.createAdecuatePluginSettingView(adecuateModel)
368
369
370 class NotificationsDialog(Gtk.Window):
371 """Defines a simple notification dialog. It isn't much, really"""
372
373 def __init__(self, view, callback, parent):
374 Gtk.Window.__init__(self, title="Notifications")
375 self.set_type_hint(Gdk.WindowTypeHint.DIALOG)
376 self.set_transient_for(parent)
377 self.set_size_request(400, 200)
378 self.view = view
379 self.destroy_notifications = callback
380
381 self.button = Gtk.Button()
382 self.button.set_label("OK")
383 self.button.connect("clicked", self.on_click_OK)
384
385 self.mainBox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
386 self.mainBox.pack_start(self.view, True, True, 0)
387 self.mainBox.pack_start(self.button, False, False, 0)
388
389 self.add(self.mainBox)
390
391 def on_click_OK(self, button):
392 self.destroy_notifications()
393 self.destroy()
394
395
396 class aboutDialog(Gtk.AboutDialog):
397 """The simple about dialog displayed when the user clicks on "about"
398 ont the menu. Could be in application.py, but for consistency reasons
399 its here"""
400 def __init__(self, main_window):
401
402 Gtk.AboutDialog.__init__(self, transient_for=main_window, modal=True)
403 icons = CONF.getImagePath() + "icons/"
404 faraday_icon = GdkPixbuf.Pixbuf.new_from_file(icons+"about.png")
405 self.set_logo(faraday_icon)
406 self.set_program_name("Faraday")
407 self.set_comments("Penetration Test IDE -"
408 " Infobyte LLC. - All rights reserved")
409 faraday_website = "http://www.infobytesec.com/faraday.html"
410 self.set_website(faraday_website)
411 self.set_website_label("Learn more about Faraday")
412
413
414 class helpDialog(Gtk.AboutDialog):
415 """Using about dialog 'cause they are very similar, but this will
416 display github page, Wiki, and such"""
417 def __init__(self, main_window):
418 Gtk.AboutDialog.__init__(self, transient_for=main_window, modal=True)
419 icons = CONF.getImagePath() + "icons/"
420 faraday_icon = GdkPixbuf.Pixbuf.new_from_file(icons+"faraday_icon.png")
421 self.set_logo(faraday_icon)
422 self.set_program_name("Faraday")
423 self.set_comments("Farday is a Penetration Test IDE. "
424 "Just use one of the supported tools on Faraday's "
425 " terminal and a plugin will capture the output and "
426 "extract useful information for you.")
427 faraday_website = "https://github.com/infobyte/faraday/wiki"
428 self.set_website(faraday_website)
429 self.set_website_label("Learn more about how to use Faraday")
430
431
432 class errorDialog(Gtk.MessageDialog):
433 """A simple error dialog to show the user where things went wrong.
434 Takes the parent window, (Gtk.Window or Gtk.Dialog, most probably)
435 the error and explanation (strings, nothing fancy) as arguments"""
436
437 def __init__(self, parent_window, error, explanation=None):
438 Gtk.MessageDialog.__init__(self, parent_window, 0,
439 Gtk.MessageType.ERROR,
440 Gtk.ButtonsType.OK,
441 error)
442 if explanation is not None:
443 self.format_secondary_text(explanation)
444 self.run()
445 self.destroy()
0 #!/usr/bin/python2.7
1 # -*- coding: utf-8 -*-
2 '''
3 Faraday Penetration Test IDE
4 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
5 See the file 'doc/LICENSE' for the license information
6
7 '''
8 import gi
9 import os
10 import sys
11
12 gi.require_version('Gtk', '3.0')
13 gi.require_version('Vte', '2.91')
14
15 from gi.repository import Gtk, Vte, GLib
16
17
18 class Terminal(Gtk.Widget):
19 """Defines a simple terminal that will execute faraday-terminal with the
20 corresponding host and port as specified by the CONF"""
21 def __init__(self, CONF):
22 super(Gtk.Widget, self).__init__()
23
24 self.terminal = Vte.Terminal()
25 faraday_directory = os.path.dirname(os.path.realpath(sys.argv[0]))
26 self.terminal.spawn_sync(Vte.PtyFlags.DEFAULT,
27 faraday_directory, ['/bin/zsh'],
28 [],
29 GLib.SpawnFlags.DO_NOT_REAP_CHILD,
30 None,
31 None)
32
33 host, port = CONF.getApiRestfulConInfo()
34 faraday_exec = './faraday-terminal.zsh'
35 self.command = (faraday_exec + " " + host + " " + str(port))
36 self.terminal.feed_child(self.command + '\n', len(self.command)+1)
37
38 def getTerminal(self):
39 return self.terminal
40
41
42 class Sidebar(Gtk.Widget):
43 """Defines the sidebar widget to be used by the AppWindow, passed as an
44 instance by the application. It only handles the view, all the backend
45 word is handled by the application via the callback"""
46
47 def __init__(self, workspace_manager, callback_to_change_workspace,
48 callback_to_remove_workspace, conf):
49 super(Gtk.Widget, self).__init__()
50 self.callback = callback_to_change_workspace
51 self.removeWsCallback = callback_to_remove_workspace
52 self.ws_manager = workspace_manager
53 self.lastWorkspace = conf
54 self.workspaces = self.ws_manager.getWorkspacesNames()
55 self.workspace_list_info = Gtk.ListStore(str)
56
57 self.workspaceModel()
58 self.workspaceView()
59
60 self.sidebar_button = Gtk.Button.new_with_label("Refresh")
61 self.sidebar_button.connect("clicked", self.refreshSidebar)
62
63 self.scrollableView = Gtk.ScrolledWindow.new(None, None)
64 self.scrollableView.set_min_content_width(160)
65 self.scrollableView.add(self.lst)
66
67 def refreshSidebar(self, button=None):
68 """Function called when the user press the refresh button.
69 Gets an updated copy of the workspaces and checks against
70 the model to see which are already there and which arent"""
71 model = self.workspace_list_info
72 self.workspaces = self.ws_manager.getWorkspacesNames()
73 added_workspaces = [added_ws[0] for added_ws in model]
74 for ws in self.workspaces:
75 if ws not in added_workspaces:
76 self.addWorkspace(ws)
77
78 def clearSidebar(self):
79 """Brutaly clear all the information from the model.
80 No one survives"""
81 self.workspace_list_info.clear()
82
83 def createTitle(self):
84 """Return a label with the text "Workspaces"""
85 title = Gtk.Label()
86 title.set_text("Workspaces")
87 return title
88
89 def workspaceModel(self):
90 """Populates the workspace model. Also assigns self.defaultSelection
91 to the treeIter which represents the last active workspace"""
92 for ws in self.workspaces:
93 treeIter = self.workspace_list_info.append([ws])
94 if ws == self.lastWorkspace:
95 self.defaultSelection = treeIter
96
97 def workspaceView(self):
98 """Populate the workspace view. Also selected by default
99 self.defaultSelection (see workspaceModel method). Also connect
100 a selection with the change workspace callback"""
101 self.lst = Gtk.TreeView(self.workspace_list_info)
102 renderer = Gtk.CellRendererText()
103 column = Gtk.TreeViewColumn("Workspaces", renderer, text=0)
104 self.lst.append_column(column)
105
106 # select by default the last active workspace
107 if self.defaultSelection is not None:
108 self.selectDefault = self.lst.get_selection()
109 self.selectDefault.select_iter(self.defaultSelection)
110
111 self.lst.connect("button-press-event", self.on_right_click)
112 selection = self.lst.get_selection()
113 selection.connect("changed", self.callback)
114
115 def on_right_click(self, view, event):
116 """On click, check if it was a right click. If it was,
117 create a menu with the delete option. On click on that option,
118 delete the workspace that occupied the position where the user
119 clicked. Returns True if it was a right click"""
120
121 if event.button == 3: # 3 represents right click
122 menu = Gtk.Menu()
123 delete_item = Gtk.MenuItem("Delete")
124 menu.append(delete_item)
125
126 # underscores mean "i don't 'care 'cause i won't use them
127 # thank haskell for that
128 # get the path of the item where the user clicked
129 # then get its tree_iter. then get its name. then delete
130 # that workspace
131
132 path, _, _, _ = view.get_path_at_pos(int(event.x), int(event.y))
133 tree_iter = self.workspace_list_info.get_iter(path)
134 ws_name = self.workspace_list_info[tree_iter][0]
135
136 delete_item.connect("activate", self.removeWsCallback, ws_name)
137
138 delete_item.show()
139 menu.popup(None, None, None, None, event.button, event.time)
140 return True # prevents the click from selecting a workspace
141
142 def addWorkspace(self, ws):
143 """Append ws workspace to the model"""
144 self.workspace_list_info.append([ws])
145
146 def getSelectedWs(self):
147 """Returns the current selected workspace"""
148 return self.lst.get_selection()
149
150 def selectWs(self, ws):
151 """Selects workspace ws in the list"""
152 self.select = self.lst.get_selection()
153 self.select.select_iter(ws)
154
155 def getButton(self):
156 """Returns the refresh sidebar button"""
157 return self.sidebar_button
158
159
160 class ConsoleLog(Gtk.Widget):
161 """Defines a textView and a textBuffer to be used for displaying
162 and updating logging information in the appwindow."""
163
164 def __init__(self):
165 super(Gtk.Widget, self).__init__()
166
167 self.textBuffer = Gtk.TextBuffer()
168 self.textBuffer.new()
169 self.textBuffer.set_text("LOG. Please run Faraday with the --debug "
170 "flag for more verbose output \n \0", -1)
171
172 self.textView = Gtk.TextView()
173 self.textView.set_editable(False)
174 # TODO: only execute monospace if Gi >= 3.16
175 # self.textView.set_monospace(True)
176 self.textView.set_justification(Gtk.Justification.LEFT)
177
178 self.textView.set_buffer(self.textBuffer)
179
180 self.logger = Gtk.ScrolledWindow.new(None, None)
181 self.logger.set_min_content_height(100)
182 self.logger.set_min_content_width(100)
183 self.logger.add(self.textView)
184
185 def getLogger(self):
186 """Returns the ScrolledWindow used to contain the view"""
187 return self.logger
188
189 def getView(self):
190 """Returns the text view"""
191 return self.textView
192
193 def getBuffer(self):
194 """Returns the buffer"""
195 return self.textBuffer
196
197 def customEvent(self, text):
198 """Filters event so that only those with type 3131 get to the log"""
199 self.update(text)
200
201 def update(self, event):
202 """Updates the textBuffer with the event sent. Also scrolls to last
203 posted automatically"""
204 last_position = self.textBuffer.get_end_iter()
205 self.textBuffer.insert(last_position, event+"\n", len(event + "\n"))
206 insert = self.textBuffer.get_insert()
207 self.textView.scroll_to_mark(insert, 0, False, 1, 1)
208
209
210 class Statusbar(Gtk.Widget):
211 """Defines a statusbar, which is actually more quite like a button.
212 The button has a label that tells how many notifications are there.
213 Takes an on_button_do callback, so it can tell the application what
214 to do when the user presses the button"""
215
216 def __init__(self, on_button_do):
217 super(Gtk.Widget, self).__init__()
218 """Creates a button with zero ("0") as label"""
219 self.callback = on_button_do
220 self.button_label_int = 0
221 self.button = Gtk.Button.new_with_label(str(self.button_label_int))
222 self.button.connect("clicked", self.callback)
223
224 def inc_button_label(self):
225 """increments the label by one"""
226 self.button_label_int += 1
227 self.button.set_label(str(self.button_label_int))
0 <!--
1 Faraday Penetration Test IDE
2 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
3 See the file 'doc/LICENSE' for the license information
4 -->
5
6 <?xml version="1.0"?>
7 <interface>
8 <menu id="appmenu">
9 <section>
10 <item>
11 <attribute name="label" translatable="yes">Preferences</attribute>
12 <attribute name="action">app.preferences</attribute>
13 </item>
14 <item>
15 <attribute name="label" translatable="yes">Plugins</attribute>
16 <attribute name="action">app.pluginOptions</attribute>
17 </item>
18 </section>
19 <section>
20 <item>
21 <attribute name="label" translatable="yes">About</attribute>
22 <attribute name="action">app.about</attribute>
23 </item>
24 <item>
25 <attribute name="label" translatable="yes">Help</attribute>
26 <attribute name="action">app.help</attribute>
27 </item>
28 <item>
29 <attribute name="label" translatable="yes">Quit</attribute>
30 <attribute name="action">app.quit</attribute>
31 <attribute name="accel">&lt;Primary&gt;q</attribute>
32 </item>
33 </section>
34 </menu>
35 </interface>
36
1313
1414 class UiFactory(object):
1515 @staticmethod
16 def create(model_controller, plugin_manager, workspace_manager, gui="gtk"):
16 def create(model_controller, plugin_manager, workspace_manager, plugin_controller, gui="gtk"):
1717 if gui == "gtk":
1818 from gui.gtk.application import GuiApp
1919 elif gui == "qt3":
2121 else:
2222 from gui.nogui.application import GuiApp
2323
24 return GuiApp(model_controller, plugin_manager, workspace_manager)
24 return GuiApp(model_controller, plugin_manager, workspace_manager, plugin_controller)
2525
2626
2727 class FaradayUi(object):
28 def __init__(self, model_controller=None, plugin_manager=None,
29 workspace_manager=None, gui="qt3"):
28 def __init__(self, model_controller, plugin_manager,
29 workspace_manager, plugin_controller, gui="qt3"):
3030 self.model_controller = model_controller
3131 self.plugin_manager = plugin_manager
3232 self.workspace_manager = workspace_manager
33 self.plugin_controller = plugin_controller
3334 self.report_manager = None
3435
3536 def getModelController(self):
7778 try:
7879 ws = self.getWorkspaceManager().openWorkspace(name)
7980 self.report_manager = ReportManager(
80 10, name)
81 10,
82 name,
83 self.plugin_controller
84 )
8185 self.report_manager.start()
8286 except Exception as e:
8387 raise e
96100 self.report_manager.join()
97101 ws = self.getWorkspaceManager().openDefaultWorkspace()
98102 self.report_manager = ReportManager(
99 10, ws.name)
103 10,
104 ws.name,
105 self.plugin_controller
106 )
100107 self.report_manager.start()
101108 return ws
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 import logging
8 import threading
9 import model.guiapi
10 from gui.customevents import LogCustomEvent
11
12 class GUIHandler(logging.Handler):
13 def __init__(self):
14 logging.Handler.__init__(self)
15 self._widgets = []
16 self._widgets_lock = threading.RLock()
17 formatter = logging.Formatter(
18 '%(levelname)s - %(asctime)s - %(name)s - %(message)s')
19 self.setFormatter(formatter)
20
21 def registerGUIOutput(self, widget):
22 self._widgets_lock.acquire()
23 self._widgets.append(widget)
24 self._widgets_lock.release()
25
26 def clearWidgets(self):
27 self._widgets_lock.acquire()
28 self._widgets = []
29 self._widgets_lock.release()
30
31 def emit(self, record):
32 try:
33 msg = self.format(record)
34 self._widgets_lock.acquire()
35 for widget in self._widgets:
36 event = LogCustomEvent(msg)
37 model.guiapi.postCustomEvent(event, widget)
38 self._widgets_lock.release()
39 except:
40 self.handleError(record)
1717
1818
1919 class GuiApp(FaradayUi):
20 def __init__(self, model_controller, plugin_manager, workspace_manager):
20 def __init__(self, model_controller, plugin_manager, workspace_manager, plugin_controller):
2121 FaradayUi.__init__(self,
2222 model_controller,
2323 plugin_manager,
24 workspace_manager)
24 workspace_manager,
25 plugin_controller)
2526 self._stop = False
2627 model.guiapi.setMainApp(self)
2728 self.event_watcher = EventWatcher()
1010
1111
1212 class NotificationCenter():
13 def __init__(self, uiapp=FaradayUi()):
13 def __init__(self, uiapp=FaradayUi(None, None, None, None, None)):
1414 self.uiapp = uiapp
1515 self._consumers = []
1616 self._consumers_lock = threading.RLock()
1717 from gui.gui_app import FaradayUi
1818 from gui.qt3.mainwindow import MainWindow
1919 from gui.qt3.customevents import QtCustomEvent
20 from gui.qt3.logconsole import GUIHandler
20 from gui.loghandler import GUIHandler
2121 from shell.controller.env import ShellEnvironment
2222
2323 import model.guiapi
3131
3232
3333 class GuiApp(qt.QApplication, FaradayUi):
34 def __init__(self, model_controller, plugin_manager, workspace_manager):
34 def __init__(self, model_controller, plugin_manager, workspace_manager, plugin_controller):
3535 FaradayUi.__init__(self,
3636 model_controller,
3737 plugin_manager,
38 workspace_manager)
38 workspace_manager,
39 plugin_controller)
3940 qt.QApplication.__init__(self, [])
4041
4142 self._shell_envs = dict()
33 See the file 'doc/LICENSE' for the license information
44
55 '''
6 import logging
7 import threading
6 import qt
87 import re
9
10 from gui.customevents import LogCustomEvent
11 import model.guiapi
12 import qt
13
14
15 class GUIHandler(logging.Handler):
16 def __init__(self):
17 logging.Handler.__init__(self)
18 self._widgets = []
19 self._widgets_lock = threading.RLock()
20 formatter = logging.Formatter(
21 '%(levelname)s - %(asctime)s - %(name)s - %(message)s')
22 self.setFormatter(formatter)
23
24 def registerGUIOutput(self, widget):
25 self._widgets_lock.acquire()
26 self._widgets.append(widget)
27 self._widgets_lock.release()
28
29 def clearWidgets(self):
30 self._widgets_lock.acquire()
31 self._widgets = []
32 self._widgets_lock.release()
33
34 def emit(self, record):
35 try:
36 msg = self.format(record)
37 self._widgets_lock.acquire()
38 for widget in self._widgets:
39 event = LogCustomEvent(msg)
40 model.guiapi.postCustomEvent(event, widget)
41 self._widgets_lock.release()
42 except:
43 self.handleError(record)
44
458
469 class LogConsole(qt.QVBox):
4710 """
2727 self.connect(self, qt.PYSIGNAL('contextMenu'), self._showContextMenu )
2828 self.contextPopupMenu = qt.QPopupMenu(self)
2929 self._setupContextPopupMenu()
30
30
3131 def addAction(self, name, func):
3232 self._actions[name] = func
3333
3535 """
3636 setups all items in the context menu with all its actions
3737 """
38 #insertItem ( const QString & text,
39 # const QObject * receiver,
40 # const char * member,
41 # const QKeySequence & accel = 0,
42 # int id = -1,
43 # int index = -1 )
4438 self.contextPopupMenu.insertItem("Allow plugins on this shell", self._allowPlugins)
4539 self._actions["new_shell"].addTo(self.contextPopupMenu);
4640 self._actions["close_shell"].addTo(self.contextPopupMenu);
47 #self.contextPopupMenu.insertItem("Close tab", self._actions["close_shell"])
4841
4942 def contextMenuEvent(self, event):
5043 #XXX: emits the signal to show the parent context menu
6255 # e is a qt.QMouseEvent
6356 if "maximize" in self._actions:
6457 self._actions["maximize"]()
65
58
6659 def _setupActions(self):
6760 """
6861 creates some actions needed on some menues and toolbars
6962 """
7063 a = self._actions["close_shell"] = qt.QAction( qt.QIconSet(qt.QPixmap(os.path.join(CONF.getIconsPath(),"newshell.png"))), "&Close Shell", qt.Qt.CTRL + qt.Qt.Key_W, self, "New Shell" )
7164 self.connect(a, qt.SIGNAL('activated()'), self.destroyShellTab)
72
65
7366 a = self._actions["new_shell"] = qt.QAction( qt.QIconSet(qt.QPixmap(os.path.join(CONF.getIconsPath(),"newshell.png"))), "&New Shell", qt.Qt.CTRL + qt.Qt.Key_T, self, "New Shell" )
7467 self.connect(a, qt.SIGNAL('activated()'), self.createShellTab)
7568
7669 def destroyShellTab(self):
7770 getMainWindow().destroyShellTab()
78
71
7972 def createShellTab(self):
8073 getMainWindow().createShellTab()
8174
8679 self.views = []
8780 self.setMargin(10)
8881 self.connect(self, qt.SIGNAL('currentChanged(QWidget*)'), self._setFocus)
89
82
9083 # we replace the tab bar with our own wich handles contextMenu
9184 tabbar = ContextMenuTabBar(self)
9285 self.setTabBar(tabbar)
9386 self._next_id = 0
94
87
9588 def getNextId(self):
9689 self._next_id += 1
9790 return self._next_id
98
91
9992 def addView(self, view):
10093 if view not in self.views:
10194 self.views.append(view)
128121
129122 def _setFocus(self, widget):
130123 # just set focus is set on the widget that is contained in the new selected page.
131 widget.setFocus()
124 widget.setFocus()
44
55 '''
66 # -*- coding: utf-8 -*-
7
8
9
10
11
12
13
14
157
168 from qt import *
179 from qttable import QTable
109101 label_version_font.setBold(1)
110102 self.label_version.setFont(label_version_font)
111103 layout6.addWidget(self.label_version)
112
104
113105 self.le_version = QLineEdit(self.frame3,"le_version")
114106 self.le_version.setMinimumSize(QSize(250,0))
115107 self.le_version.setReadOnly(1)
116108 layout6.addWidget(self.le_version)
117109
118110 frame3Layout.addLayout(layout6,1,0)
119
111
120112 layout7 = QHBoxLayout(None,0,6,"layout7")
121
113
122114 self.label_pversion = QLabel(self.frame3,"label_pversion")
123115 self.label_pversion.setMinimumSize(QSize(67,0))
124116 self.label_pversion.setMaximumSize(QSize(67,32767))
126118 label_pversion_font.setBold(1)
127119 self.label_pversion.setFont(label_pversion_font)
128120 layout7.addWidget(self.label_pversion)
129
121
130122 self.le_pversion = QLineEdit(self.frame3,"le_pversion")
131123 self.le_pversion.setMinimumSize(QSize(250,0))
132124 self.le_pversion.setReadOnly(1)
133125 layout7.addWidget(self.le_pversion)
134
135 frame3Layout.addLayout(layout7,2,0)
126
127 frame3Layout.addLayout(layout7,2,0)
136128
137129 PluginSettingsUiLayout.addWidget(self.frame3,0,1)
138130
33 Copyright (C) 2014 Infobyte LLC (http://www.infobytesec.com/)
44 See the file 'doc/LICENSE' for the license information
55
6 '''
7 '''
86 This script fixes invalid XMLs.
97 '''
108
33 Copyright (C) 2014 Infobyte LLC (http://www.infobytesec.com/)
44 See the file 'doc/LICENSE' for the license information
55
6 '''
7 '''
86 This script either updates or removes Interfaces, Services and Vulnerabilities in case their parent property is null.
97 If the property is null but a parent is found in Couch, the document is updated.
108 If the parent is not found in Couch the document is deleted, since it is an invalid one.
33 Copyright (C) 2014 Infobyte LLC (http://www.infobytesec.com/)
44 See the file 'doc/LICENSE' for the license information
55
6 '''
7 '''
8 This script upload a Vulnerability database to Couch.
6 This script uploads a Vulnerability database to Couch.
97 It takes the content of the DB from data/cwe.csv
108 '''
119 import argparse
1816 def main():
1917
2018 #arguments parser
21 parser = argparse.ArgumentParser(prog='pushExecutiveReports', epilog="Example: ./%(prog)s.py")
19 parser = argparse.ArgumentParser(prog='pushCwe', epilog="Example: ./%(prog)s.py")
2220 parser.add_argument('-c', '--couchdburi', action='store', type=str,
2321 dest='couchdb',default="http://127.0.0.1:5984",
2422 help='Couchdb URL (default http://127.0.0.1:5984)')
33 Copyright (C) 2014 Infobyte LLC (http://www.infobytesec.com/)
44 See the file 'doc/LICENSE' for the license information
55
6 '''
7 '''
8 This script either updates or removes Interfaces, Services and Vulnerabilities in case their parent property is null.
9 If the property is null but a parent is found in Couch, the document is updated.
10 If the parent is not found in Couch the document is deleted, since it is an invalid one.
6 This script removes vulnerabilities from Couch depending on thei severity.
117 '''
128
139 import argparse
22 '''
33 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
44 Author: Ezequiel Tavella
5 '''
65
7 '''
86 This script generate a CSV file with information about the vulndb database.
97 CSV Format:
108 cwe,name,desc_summary,description,resolution,exploitation,references
11
129 '''
1310 from subprocess import call
1411 from os import walk
66 '''
77
88 import os
9 import model.api
9 #import model.api
1010 import threading
1111 import time
1212 import traceback
1313 import re
1414
15 from utils.logs import getLogger
16
1517 try:
1618 import xml.etree.cElementTree as ET
1719
1820 except ImportError:
1921 print "cElementTree could not be imported. Using ElementTree instead"
2022 import xml.etree.ElementTree as ET
21 from apis.rest.client import PluginControllerAPIClient
2223
2324 from config.configuration import getInstanceConfiguration
2425 CONF = getInstanceConfiguration()
2526
2627
2728 class ReportProcessor():
28 def __init__(self):
29 host = CONF.getApiConInfoHost()
30 port_rest = int(CONF.getApiRestfulConInfoPort())
31
32 self.client = PluginControllerAPIClient(host, port_rest)
29 def __init__(self, plugin_controller):
30 self.plugin_controller = plugin_controller
3331
3432 def processReport(self, filename):
3533 """
3634 Process one Report
3735 """
38 model.api.log("Report file is %s" % filename)
36 getLogger(self).debug("Report file is %s" % filename)
3937
4038 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
39
40 if parser.report_type is None:
41 getLogger(self).error(
42 'Plugin not found: automatic and manual try!'
43 )
44 return False
45
46 return self._sendReport(parser.report_type, filename)
47
48 def _sendReport(self, plugin_id, filename):
49 getLogger(self).debug(
50 'The file is %s, %s' % (filename, plugin_id))
51 if not self.plugin_controller.processReport(plugin_id, filename):
52 getLogger(self).error(
53 "Faraday doesn't have a plugin for this tool..."
54 " Processing: ABORT")
55 return False
56 return True
5357
5458 def onlinePlugin(self, cmd):
55 new_cmd, output_file = self.client.send_cmd(cmd)
56 self.client.send_output(cmd)
59
60 _, new_cmd, output_file = self.plugin_controller.processCommandInput(
61 cmd)
62 self.plugin_controller.onCommandFinished(cmd, '')
5763
5864
5965 class ReportManager(threading.Thread):
60 def __init__(self, timer, ws_name):
66 def __init__(self, timer, ws_name, plugin_controller):
6167 threading.Thread.__init__(self)
6268 self.setDaemon(True)
6369 self.timer = timer
6470 self._stop = False
6571 self._report_path = os.path.join(CONF.getReportPath(), ws_name)
6672 self._report_ppath = os.path.join(self._report_path, "process")
67 self.processor = ReportProcessor()
73 self._report_upath = os.path.join(self._report_path, "unprocessed")
74 self.processor = ReportProcessor(plugin_controller)
6875
6976 if not os.path.exists(self._report_path):
7077 os.mkdir(self._report_path)
7178
7279 if not os.path.exists(self._report_ppath):
7380 os.mkdir(self._report_ppath)
81
82 if not os.path.exists(self._report_upath):
83 os.mkdir(self._report_upath)
7484
7585 def run(self):
7686 tmp_timer = 0
8292 try:
8393 self.syncReports()
8494 except Exception:
85 model.api.log("An exception was captured while saving reports\n%s" % traceback.format_exc())
95 getLogger(self).error(
96 "An exception was captured while saving reports\n%s"
97 % traceback.format_exc()
98 )
8699 finally:
87100 tmp_timer = 0
88101
101114 for name in files:
102115 filename = os.path.join(root, name)
103116
104 self.processor.processReport(filename)
105
106 os.rename(filename, os.path.join(self._report_ppath, name))
117 # If plugin not is detected... move to unprocessed
118 if self.processor.processReport(filename) is False:
119
120 os.rename(
121 filename,
122 os.path.join(self._report_upath, name)
123 )
124 else:
125 os.rename(
126 filename,
127 os.path.join(self._report_ppath, name)
128 )
107129
108130 self.onlinePlugins()
109131
132154 """
133155
134156 def __init__(self, report_path):
135 self.report_type = ""
157 self.report_type = None
136158 root_tag, output = self.getRootTag(report_path)
137159
138160 if root_tag:
139161 self.report_type = self.rType(root_tag, output)
162
163 if self.report_type is None:
164
165 getLogger(self).debug(
166 'Automatical detection FAILED... Trying manual...')
167
168 self.report_type = self.getUserPluginName(report_path)
169
170 def getUserPluginName(self, pathFile):
171 rname = pathFile[pathFile.rfind('/') + 1:]
172 ext = rname.rfind('.')
173 if ext < 0:
174 ext = len(rname) + 1
175 rname = rname[0:ext]
176 faraday_index = rname.rfind('_faraday_')
177 if faraday_index > -1:
178 plugin = rname[faraday_index + 9:]
179 return plugin
180 return None
140181
141182 def open_file(self, file_path):
142183 """
154195 f = result = None
155196
156197 signatures = {
157 "\x50\x4B" : "zip" ,
158 "\x3C\x3F\x78\x6D\x6C" : "xml"
198 "\x50\x4B": "zip",
199 "\x3C\x3F\x78\x6D\x6C": "xml"
159200 }
160201
161202 try:
167208 if file_signature.find(key) == 0:
168209
169210 result = signatures[key]
170 model.api.log("Report type detected: %s" %result)
211 getLogger(self).debug("Report type detected: %s" % result)
171212 break
172213
173214 except IOError, err:
174215 self.report_type = None
175 model.api.log("Error while opening file.\n%s. %s" % (err, file_path))
216 getLogger(self).error(
217 "Error while opening file.\n%s. %s" % (err, file_path))
176218
177219 return f, result
178220
182224
183225 f, report_type = self.open_file(file_path)
184226
185 #Check error in open_file()
186 if f == None and report_type == None:
227 # Check error in open_file()
228 if f is None and report_type is None:
187229 self.report_type = None
188230 return None, None
189231
190 #Find root tag based in report_type
232 # Find root tag based in report_type
191233 if report_type == "zip":
192234 result = "maltego"
193235
200242
201243 except SyntaxError, err:
202244 self.report_type = None
203 model.api.log("Not an xml file.\n %s" % (err))
245 getLogger(self).error("Not an xml file.\n %s" % (err))
204246
205247 f.seek(0)
206248 output = f.read()
207 if f: f.close()
249 if f:
250 f.close()
208251
209252 return result, output
210253
215258 :rtype
216259 """
217260 if "nmaprun" == tag:
218 return "nmap"
261 return "Nmap"
219262 elif "w3af-run" == tag:
220 return "w3af"
263 return "W3af"
221264 elif "NessusClientData_v2" == tag:
222 return "nessus"
265 return "Nessus"
223266 elif "report" == tag:
224 if re.search("https://raw.githubusercontent.com/Arachni/arachni/", output) != None:
225 return "arachni_faraday"
226 elif re.search("OpenVAS", output) != None or re.search('<omp><version>', output) != None:
227 return "openvas"
267
268 if re.search(
269 "https://raw.githubusercontent.com/Arachni/arachni/",
270 output
271 ) is not None:
272 return "Arachni"
273
274 elif re.search("OpenVAS", output) is not None or re.search(
275 '<omp><version>',
276 output
277 ) is not None:
278 return "Openvas"
279
228280 else:
229 return "zap"
281 return "Zap"
282
230283 elif "niktoscan" == tag:
231 return "nikto"
284 return "Nikto"
232285 elif "MetasploitV4" == tag:
233 return "metasploit"
286 return "Metasploit"
234287 elif "MetasploitV5" == tag:
235 return "metasploit"
288 return "Metasploit"
236289 elif "issues" == tag:
237 return "burp"
290 return "Burp"
238291 elif "OWASPZAPReport" == tag:
239 return "zap"
292 return "Zap"
240293 elif "ScanGroup" == tag:
241 return "acunetix"
294 return "Acunetix"
242295 elif "session" == tag:
243 return "x1"
296 return "X1"
244297 elif "landscapePolicy" == tag:
245 return "x1"
298 return "X1"
246299 elif "entities" == tag:
247 return "impact"
300 return "Core Impact"
248301 elif "NeXposeSimpleXML" == tag:
249 return "nexpose"
302 return "Nexpose"
250303 elif "NexposeReport" == tag:
251 return "nexpose-full"
304 return "NexposeFull"
252305 elif "ASSET_DATA_REPORT" == tag:
253 return "qualysguard"
306 return "Qualysguard"
254307 elif "scanJob" == tag:
255 return "retina"
308 return "Retina"
256309 elif "netsparker" == tag:
257 return "netsparker"
310 return "Netsparker"
258311 elif "maltego" == tag:
259 return "maltego_faraday"
312 return "Maltego"
260313 else:
261314 return None
1414 from persistence.persistence_managers import DbManager
1515 from controllers.change import ChangeController
1616 from managers.workspace_manager import WorkspaceManager
17 from plugins.controller import PluginControllerForApi
18
1719 import model.api
1820 import model.guiapi
1921 import apis.rest.api as restapi
7880 self._mappers_manager,
7981 self._changes_controller)
8082
83 # Create a PluginController and send this to UI selected.
84 self._plugin_controller = PluginControllerForApi(
85 'PluginController',
86 self._plugin_manager,
87 self._mappers_manager
88 )
89
8190 if self.args.cli:
82 self.app = CliApp(self._workspace_manager)
91 self.app = CliApp(self._workspace_manager, self._plugin_controller)
8392 CONF.setMergeStrategy("new")
8493 else:
8594 self.app = UiFactory.create(self._model_controller,
8695 self._plugin_manager,
8796 self._workspace_manager,
97 self._plugin_controller,
8898 self.args.gui)
8999
90100 self.timer = TimerClass()
117127 self._model_controller.start()
118128 model.api.startAPIServer()
119129 restapi.startAPIs(
120 self._plugin_manager,
130 self._plugin_controller,
121131 self._model_controller,
122 self._mappers_manager,
123132 CONF.getApiConInfoHost(),
124 CONF.getApiRestfulConInfoPort())
133 CONF.getApiRestfulConInfoPort()
134 )
125135
126136 model.api.devlog("Faraday ready...")
127137
99
1010
1111 class CliApp():
12 def __init__(self, workspace_manager):
12 def __init__(self, workspace_manager, plugin_controller):
1313 self.workspace_manager = workspace_manager
14 self.plugin_controller = plugin_controller
1415
1516 def run(self, args):
1617 workspace = args.workspace
2324 getLogger(self).error(str(e))
2425 return -1
2526
26 rp = ReportProcessor()
27 rp = ReportProcessor(self.plugin_controller)
2728 rp.processReport(args.filename)
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 errno
11 from cStringIO import StringIO
12 import multiprocessing
13 import os
14 import Queue
15 import shlex
16 import time
17
18 from plugins.plugin import PluginProcess
19 import model.api
20 from model.commands_history import CommandRunInformation
21 from plugins.modelactions import modelactions
22 from utils.logs import getLogger
23
24 from config.globals import (
25 CONST_FARADAY_HOME_PATH,
26 CONST_FARADAY_ZSH_OUTPUT_PATH)
27
28
29 class PluginControllerBase(object):
30 """
31 TODO: Doc string.
32 """
33 def __init__(self, id, plugin_manager, mapper_manager):
34 self.plugin_manager = plugin_manager
35 self._plugins = plugin_manager.getPlugins()
36 self.id = id
37 self._actionDispatcher = None
38 self._setupActionDispatcher()
39 self._mapper_manager = mapper_manager
40 self.output_path = os.path.join(
41 os.path.expanduser(CONST_FARADAY_HOME_PATH),
42 CONST_FARADAY_ZSH_OUTPUT_PATH)
43
44 def _find_plugin(self, plugin_id):
45 return self._plugins.get(plugin_id, None)
46
47 def _is_command_malformed(self, original_command, modified_command):
48 """
49 Checks if the command to be executed is safe and it's not in the
50 block list defined by the user. Returns False if the modified
51 command is ok, True if otherwise.
52 """
53 block_chars = set(["|", "$", "#"])
54
55 if original_command == modified_command:
56 return False
57
58 orig_cmd_args = shlex.split(original_command)
59
60 if not isinstance(modified_command, basestring):
61 modified_command = ""
62 mod_cmd_args = shlex.split(modified_command)
63
64 block_flag = False
65 orig_args_len = len(orig_cmd_args)
66 for index in xrange(0, len(mod_cmd_args)):
67 if (index < orig_args_len and
68 orig_cmd_args[index] == mod_cmd_args[index]):
69 continue
70
71 for char in block_chars:
72 if char in mod_cmd_args[index]:
73 block_flag = True
74 break
75
76 return block_flag
77
78 def _get_plugins_by_input(self, current_input):
79 """
80 Finds a plugin that can parse the current input and returns
81 the plugin object. Otherwise returns None.
82 """
83 for plugin in self._plugins.itervalues():
84 if plugin.canParseCommandString(current_input):
85 return plugin
86 return None
87
88 def getAvailablePlugins(self):
89 """
90 Return a dictionary with the available plugins.
91 Plugin ID's as keys and plugin instences as values
92 """
93 return self._plugins
94
95 def processOutput(self, plugin, output, isReport=False):
96 output_queue = multiprocessing.JoinableQueue()
97 new_elem_queue = multiprocessing.Queue()
98
99 plugin_process = PluginProcess(
100 plugin, output_queue, new_elem_queue, isReport)
101
102 getLogger(self).debug(
103 "Created plugin_process (%d) for plugin instance (%d)" %
104 (id(plugin_process), id(plugin)))
105
106 plugin_process.start()
107
108 output_queue.put(output)
109 output_queue.put(None)
110 output_queue.join()
111
112 self._processAction(modelactions.PLUGINSTART, [])
113
114 while True:
115 try:
116 current_action = new_elem_queue.get(block=False)
117 if current_action is None:
118 break
119 action = current_action[0]
120 parameters = current_action[1:]
121 getLogger(self).debug(
122 "Core: Processing a new '%s', parameters (%s)\n" %
123 (action, str(parameters)))
124 self._processAction(action, parameters)
125
126 except Queue.Empty:
127 continue
128 except IOError, e:
129 if e.errno == errno.EINTR:
130 continue
131 else:
132 getLogger(self).debug(
133 "new_elem_queue Exception - "
134 "something strange happened... "
135 "unhandled exception?")
136 break
137 except Exception:
138 getLogger(self).debug(
139 "new_elem_queue Exception - "
140 "something strange happened... "
141 "unhandled exception?")
142 break
143 self._processAction(modelactions.PLUGINEND, [])
144
145 def _processAction(self, action, parameters):
146 """
147 decodes and performs the action given
148 It works kind of a dispatcher
149 """
150 getLogger(self).debug(
151 "_processAction - %s - parameters = %s" %
152 (action, str(parameters)))
153 self._actionDispatcher[action](*parameters)
154
155 def _setupActionDispatcher(self):
156 self._actionDispatcher = {
157 modelactions.ADDHOST: model.api.addHost,
158 modelactions.CADDHOST: model.api.createAndAddHost,
159 modelactions.ADDINTERFACE: model.api.addInterface,
160 modelactions.CADDINTERFACE: model.api.createAndAddInterface,
161 modelactions.ADDSERVICEINT: model.api.addServiceToInterface,
162 modelactions.ADDSERVICEAPP: model.api.addServiceToApplication,
163 modelactions.CADDSERVICEINT: model.api.createAndAddServiceToInterface,
164 modelactions.CADDSERVICEAPP: model.api.createAndAddServiceToApplication,
165 modelactions.ADDAPPLICATION: model.api.addApplication,
166 modelactions.CADDAPPLICATION: model.api.createAndAddApplication,
167 modelactions.DELSERVICEINT: model.api.delServiceFromInterface,
168 #Vulnerability
169 modelactions.ADDVULNINT: model.api.addVulnToInterface,
170 modelactions.CADDVULNINT: model.api.createAndAddVulnToInterface,
171 modelactions.ADDVULNAPP: model.api.addVulnToApplication,
172 modelactions.CADDVULNAPP: model.api.createAndAddVulnToApplication,
173 modelactions.ADDVULNHOST: model.api.addVulnToHost,
174 modelactions.CADDVULNHOST: model.api.createAndAddVulnToHost,
175 modelactions.ADDVULNSRV: model.api.addVulnToService,
176 modelactions.CADDVULNSRV: model.api.createAndAddVulnToService,
177 #VulnWeb
178 modelactions.ADDVULNWEBSRV: model.api.addVulnWebToService,
179 modelactions.CADDVULNWEBSRV: model.api.createAndAddVulnWebToService,
180 #Note
181 modelactions.ADDNOTEINT: model.api.addNoteToInterface,
182 modelactions.CADDNOTEINT: model.api.createAndAddNoteToInterface,
183 modelactions.ADDNOTEAPP: model.api.addNoteToApplication,
184 modelactions.CADDNOTEAPP: model.api.createAndAddNoteToApplication,
185 modelactions.ADDNOTEHOST: model.api.addNoteToHost,
186 modelactions.CADDNOTEHOST: model.api.createAndAddNoteToHost,
187 modelactions.ADDNOTESRV: model.api.addNoteToService,
188 modelactions.CADDNOTESRV: model.api.createAndAddNoteToService,
189 modelactions.ADDNOTENOTE: model.api.addNoteToNote,
190 modelactions.CADDNOTENOTE: model.api.createAndAddNoteToNote,
191 #Creds
192 modelactions.CADDCREDSRV: model.api.createAndAddCredToService,
193 modelactions.ADDCREDSRV: model.api.addCredToService,
194 #LOG
195 modelactions.LOG: model.api.log,
196 modelactions.DEVLOG: model.api.devlog,
197 # Plugin state
198 modelactions.PLUGINSTART: model.api.pluginStart,
199 modelactions.PLUGINEND: model.api.pluginEnd
200 }
201
202 def updatePluginSettings(self, plugin_id, new_settings):
203 if plugin_id in self._plugins:
204 self._plugins[plugin_id].updateSettings(new_settings)
205
206
207 class PluginController(PluginControllerBase):
208 """
209 This class is going to be deprecated once we dump qt3
210 """
211 def __init__(self, id, plugin_manager, mapper_manager):
212 PluginControllerBase.__init__(self, id, plugin_manager, mapper_manager)
213 self._active_plugin = None
214 self.last_command_information = None
215 self._buffer = StringIO()
216
217 def setActivePlugin(self, plugin):
218 self._active_plugin = plugin
219
220 def processCommandInput(self, prompt, username, current_path, command_string, interactive):
221 """
222 Receives the prompt that the current session has, the actual command_string that
223 the user typed and if the command is interactive. If it is interactive the
224 plugin controller does not choose a new active plugin but use the one the
225 is already set (if none is set it raises an exeception).
226
227 If always returns an string. It could be modified by the active plugin or, if
228 there is none available, it will return the original command_string.
229 """
230
231 if interactive:
232 return None
233 else:
234 self._disable_active_plugin()
235
236 choosen_plugin = self._get_plugins_by_input(command_string)
237 if choosen_plugin is None:
238 model.api.devlog("There is no active plugin to handle current command/user input")
239 return None
240 self._active_plugin = choosen_plugin
241
242 modified_cmd_string = self._active_plugin.processCommandString(
243 username,
244 current_path,
245 command_string)
246
247 if self._is_command_malformed(command_string, modified_cmd_string):
248 return None
249 else:
250 cmd_info = CommandRunInformation(
251 **{'workspace': model.api.getActiveWorkspace().name,
252 'itime': time.time(),
253 'command': command_string.split()[0],
254 'params': ' '.join(command_string.split()[1:])})
255 self._mapper_manager.save(cmd_info)
256
257 self.last_command_information = cmd_info
258
259 return modified_cmd_string if isinstance(modified_cmd_string, basestring) else None
260
261 def storeCommandOutput(self, output):
262 """
263 Receives and output string and stores it in the buffer. Returns False
264 if the output was not added to the plugin controllers buffer. Returns
265 True otherwise.
266 """
267 if not self.getActivePluginStatus():
268 return False
269 else:
270 self._buffer.write(output)
271 return True
272
273 def getPluginAutocompleteOptions(self, prompt, username, current_path, command_string, interactive):
274 """
275 This method should return a list of possible completitions based on the
276 current output.
277 TODO: We should think how to actually implement this...
278 May be checking which plugin should handle the command in the current input
279 and then passing it to the plugin to return a list of possible values.
280 Each plugin implementation should return possible option according to
281 what was received since it's the plugin the one it knows the command line
282 parameters, etc.
283 """
284 if interactive:
285 return None
286 else:
287 self._disable_active_plugin()
288
289 choosen_plugin = self._get_plugins_by_input(command_string)
290 if choosen_plugin is None:
291 model.api.devlog("There is no active plugin to handle current command/user input")
292 return None
293
294 self._active_plugin = choosen_plugin
295
296 new_options = self._active_plugin.getCompletitionSuggestionsList(command_string)
297 return new_options
298
299 def getActivePluginStatus(self):
300 """
301 Returns true if an active plugin is set, otherwise return False.
302 """
303 return (self._active_plugin is not None)
304
305 def _disable_active_plugin(self):
306 """
307 This method is suppose to disable the active plugin.
308 """
309 model.api.devlog("Disabling active plugin")
310 self._active_plugin = None
311
312 def onCommandFinished(self):
313 """
314 This method is called when the last executed command has finished. It's
315 in charge of giving the plugin the output to be parsed.
316 """
317 cmd_info = self.last_command_information
318 cmd_info.duration = time.time() - cmd_info.itime
319 self._mapper_manager.save(cmd_info)
320
321 if self._active_plugin.has_custom_output():
322 if not os.path.isfile(self._active_plugin.get_custom_file_path()):
323 model.api.devlog("Report file PluginController output file (%s) not created" % self._active_plugin.get_custom_file_path())
324 return False
325 output_file = open(self._active_plugin.get_custom_file_path(), 'r')
326 output = output_file.read()
327 self._buffer.seek(0)
328 self._buffer.truncate()
329 self._buffer.write(output)
330
331 self.processOutput(self._active_plugin, self._buffer.getvalue())
332
333 self._buffer.seek(0)
334 self._buffer.truncate()
335 model.api.devlog("PluginController buffer cleared")
336
337 self._disable_active_plugin()
338
339 return True
340
341
342 class PluginControllerForApi(PluginControllerBase):
343 def __init__(self, id, plugin_manager, mapper_manager):
344 PluginControllerBase.__init__(
345 self, id, plugin_manager, mapper_manager)
346 self._active_plugins = {}
347 self.plugin_sets = {}
348 self.plugin_manager.addController(self, self.id)
349
350 def _get_plugins_by_input(self, cmd, plugin_set):
351 for plugin in plugin_set.itervalues():
352 if plugin.canParseCommandString(cmd):
353 return plugin
354 return None
355
356 def createPluginSet(self, id):
357 self.plugin_sets[id] = self.plugin_manager.getPlugins()
358
359 def processCommandInput(self, pid, cmd, pwd):
360 """
361 This method tries to find a plugin to parse the command sent
362 by the terminal (identiefied by the process id).
363 """
364 if pid not in self.plugin_sets:
365 self.createPluginSet(pid)
366
367 plugin = self._get_plugins_by_input(cmd, self.plugin_sets[pid])
368
369 if plugin:
370 modified_cmd_string = plugin.processCommandString("", pwd, cmd)
371 if not self._is_command_malformed(cmd, modified_cmd_string):
372
373 cmd_info = CommandRunInformation(
374 **{'workspace': model.api.getActiveWorkspace().name,
375 'itime': time.time(),
376 'command': cmd.split()[0],
377 'params': ' '.join(cmd.split()[1:])})
378 self._mapper_manager.save(cmd_info)
379 self._active_plugins[pid] = plugin, cmd_info
380
381 return plugin.id, modified_cmd_string
382
383 return None, None
384
385 def getPluginAutocompleteOptions(self, command_string):
386 """
387 Not implementend right now. Maybe it's better to use
388 zsh autocomplete features
389 """
390 pass
391
392 def onCommandFinished(self, pid, exit_code, term_output):
393
394 if pid not in self._active_plugins.keys():
395 return False
396 if exit_code != 0:
397 del self._active_plugins[pid]
398 return False
399
400 plugin, cmd_info = self._active_plugins.get(pid)
401
402 cmd_info.duration = time.time() - cmd_info.itime
403 self._mapper_manager.save(cmd_info)
404
405 self.processOutput(plugin, term_output)
406 del self._active_plugins[pid]
407 return True
408
409 def processReport(self, plugin, filepath):
410 if plugin in self._plugins:
411 self.processOutput(self._plugins[plugin], filepath, True)
412 return True
413 return False
414
415 def clearActivePlugins(self):
416 self._active_plugins = {}
417
418 def updatePluginSettings(self, plugin_id, new_settings):
419 for plugin_set in self.plugin_sets.values():
420 if plugin_id in plugin_set:
421 plugin_set[plugin_id].updateSettings(new_settings)
422 if plugin_id in self._plugins:
423 self._plugins[plugin_id].updateSettings(new_settings)
77
88 '''
99
10 import multiprocessing
11 import shlex
12 import copy_reg
13 import types
14 import model.api
15 from cStringIO import StringIO
16 import os
17 import re
18 import Queue
19 import traceback
20 import model.common
21 import errno
22 from model.common import (
23 factory, ModelObjectVuln, ModelObjectVulnWeb,
24 ModelObjectNote, ModelObjectCred)
25 from model.hosts import Host, Interface, Service
10 from plugins.plugin import PluginBase as PluginBaseExt
2611
27 from model.commands_history import CommandRunInformation
28 from utils.common import sha1OfStr
29
30 from time import time
31
32 from config.configuration import getInstanceConfiguration
33 CONF = getInstanceConfiguration()
34
35
36 def _pickle_method(method):
37 func_name = method.im_func.__name__
38 obj = method.im_self
39 cls = method.im_class
40 return _unpickle_method, (func_name, obj, cls)
41
42
43 def _unpickle_method(func_name, obj, cls):
44 for cls in cls.mro():
45 try:
46 func = cls.__dict__[func_name]
47 except KeyError:
48 pass
49 else:
50 break
51 return func.__get__(obj, cls)
52
53 copy_reg.pickle(types.MethodType, _pickle_method, _unpickle_method)
54
55
56 class modelactions:
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
97
98 __descriptions = {
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"
128 }
129
130 @staticmethod
131 def getDescription(action):
132 return modelactions.__descriptions.get(action, "")
133
134
135 class PluginControllerBase(object):
136 """
137 TODO: Doc string.
138 """
139 def __init__(self, id, available_plugins, mapper_manager):
140 self._plugins = available_plugins
141 self.id = id
142 self._actionDispatcher = None
143 self._setupActionDispatcher()
144
145 self._mapper_manager = mapper_manager
146
147 def _find_plugin(self, new_plugin_id):
148 try:
149 return self._plugins[new_plugin_id]
150 except KeyError:
151 return None
152
153 def _is_command_malformed(self, original_command, modified_command):
154 """
155 Checks if the command to be executed is safe and it's not in the block list
156 defined by the user. Returns False if the modified command is ok, True if
157 otherwise.
158
159 TODO: Use global command block list.
160 TODO: complete block idioms
161 """
162 block_chars = set(["|", "$", "#"])
163
164 if original_command == modified_command:
165 return False
166
167 orig_cmd_args = shlex.split(original_command)
168
169 if not isinstance(modified_command, basestring):
170 modified_command = ""
171 mod_cmd_args = shlex.split(modified_command)
172
173 block_flag = False
174 orig_args_len = len(orig_cmd_args)
175 for index in xrange(0, len(mod_cmd_args)):
176 if index < orig_args_len and orig_cmd_args[index] == mod_cmd_args[index]:
177 continue
178
179 for char in block_chars:
180 if char in mod_cmd_args[index]:
181 block_flag = True
182 break
183
184 return block_flag
185
186 def _get_plugins_by_input(self, current_input):
187 """
188 Finds a plugin that can parse the current input and returns the plugin
189 object. Otherwise returns None.
190 """
191 for plugin in self._plugins.itervalues():
192 if plugin.canParseCommandString(current_input):
193 return plugin
194 return None
195
196 def getAvailablePlugins(self):
197 """
198 Return a dictionary with the available plugins.
199 Plugin ID's as keys and plugin instences as values
200 """
201 return self._plugins
202
203 def processOutput(self, plugin, output):
204 output_queue = multiprocessing.JoinableQueue()
205 new_elem_queue = multiprocessing.Queue()
206
207 plugin_process = PluginProcess(plugin, output_queue, new_elem_queue)
208 model.api.devlog("PluginController (%d) - Created plugin_process (%d) for plugin instance (%d)" %
209 (id(self), id(plugin_process), id(plugin)))
210
211 plugin_process.start()
212
213 output_queue.put(output)
214 output_queue.put(None)
215 output_queue.join()
216
217 self._processAction(modelactions.PLUGINSTART, [])
218
219 #model.api.devlog("Core: queue size '%s'" % new_elem_queue.qsize())
220 while True:
221 try:
222 current_action = new_elem_queue.get(block=False)
223 if current_action is None:
224 break
225 action = current_action[0]
226 parameters = current_action[1:]
227 model.api.devlog("Core: Processing a new '%s' , parameters (%s) \n" % (action,str(parameters)))
228 self._processAction(action, parameters)
229
230 except Queue.Empty:
231 continue
232 except IOError, e:
233 if e.errno == errno.EINTR:
234 continue
235 else:
236 model.api.devlog("PluginController.onCommandFinished - new_elem_queue Exception- something strange happened... unhandled exception?")
237 model.api.devlog(traceback.format_exc())
238 break
239 except Exception:
240 model.api.devlog("PluginController.onCommandFinished - new_elem_queue Exception- something strange happened... unhandled exception?")
241 model.api.devlog(traceback.format_exc())
242 break
243 self._processAction(modelactions.PLUGINEND, [])
244
245 def _processAction(self, action, parameters):
246 """
247 decodes and performs the action given
248 It works kind of a dispatcher
249 """
250 model.api.devlog("(PluginController) _processAction - %s - parameters = %s" % (action, str(parameters)))
251 res = self._actionDispatcher[action](*parameters)
252
253 def _setupActionDispatcher(self):
254 self._actionDispatcher = {
255 modelactions.ADDHOST : model.api.addHost,
256 modelactions.CADDHOST : model.api.createAndAddHost,
257 modelactions.ADDINTERFACE : model.api.addInterface,
258 modelactions.CADDINTERFACE : model.api.createAndAddInterface,
259 modelactions.ADDSERVICEINT : model.api.addServiceToInterface,
260 modelactions.ADDSERVICEAPP : model.api.addServiceToApplication,
261 modelactions.CADDSERVICEINT : model.api.createAndAddServiceToInterface,
262 modelactions.CADDSERVICEAPP : model.api.createAndAddServiceToApplication,
263 modelactions.ADDAPPLICATION : model.api.addApplication,
264 modelactions.CADDAPPLICATION : model.api.createAndAddApplication,
265 modelactions.DELSERVICEINT : model.api.delServiceFromInterface,
266 #Vulnerability
267 modelactions.ADDVULNINT : model.api.addVulnToInterface,
268 modelactions.CADDVULNINT : model.api.createAndAddVulnToInterface,
269 modelactions.ADDVULNAPP : model.api.addVulnToApplication,
270 modelactions.CADDVULNAPP : model.api.createAndAddVulnToApplication,
271 modelactions.ADDVULNHOST : model.api.addVulnToHost,
272 modelactions.CADDVULNHOST : model.api.createAndAddVulnToHost,
273 modelactions.ADDVULNSRV : model.api.addVulnToService,
274 modelactions.CADDVULNSRV : model.api.createAndAddVulnToService,
275 #VulnWeb
276 modelactions.ADDVULNWEBSRV : model.api.addVulnWebToService,
277 modelactions.CADDVULNWEBSRV : model.api.createAndAddVulnWebToService,
278 #Note
279 modelactions.ADDNOTEINT : model.api.addNoteToInterface,
280 modelactions.CADDNOTEINT : model.api.createAndAddNoteToInterface,
281 modelactions.ADDNOTEAPP : model.api.addNoteToApplication,
282 modelactions.CADDNOTEAPP : model.api.createAndAddNoteToApplication,
283 modelactions.ADDNOTEHOST : model.api.addNoteToHost,
284 modelactions.CADDNOTEHOST : model.api.createAndAddNoteToHost,
285 modelactions.ADDNOTESRV : model.api.addNoteToService,
286 modelactions.CADDNOTESRV : model.api.createAndAddNoteToService,
287 modelactions.ADDNOTENOTE : model.api.addNoteToNote,
288 modelactions.CADDNOTENOTE : model.api.createAndAddNoteToNote,
289 #Creds
290 modelactions.CADDCREDSRV : model.api.createAndAddCredToService,
291 modelactions.ADDCREDSRV : model.api.addCredToService,
292 #modelactions.ADDNOTEVULN : model.api.createAndAddNoteToApplication,
293 #modelactions.CADDNOTEVULN : model.api.createAndAddNoteToApplication,
294 #LOG
295 modelactions.LOG : model.api.log,
296 modelactions.DEVLOG : model.api.devlog,
297 # Plugin state
298 modelactions.PLUGINSTART: model.api.pluginStart,
299 modelactions.PLUGINEND: model.api.pluginEnd
300 }
301
302 def updatePluginSettings(self, plugin_id, new_settings):
303 if plugin_id in self._plugins:
304 self._plugins[plugin_id].updateSettings(new_settings)
305
306
307 class PluginController(PluginControllerBase):
308 """
309 TODO: Doc string.
310 """
311 def __init__(self, id, available_plugins, mapper_manager):
312 PluginControllerBase.__init__(self, id, available_plugins, mapper_manager)
313 self._active_plugin = None
314 self.last_command_information = None
315 self._buffer = StringIO()
316
317 def setActivePlugin(self, plugin):
318 self._active_plugin = plugin
319
320 def processCommandInput(self, prompt, username, current_path, command_string, interactive):
321 """
322 Receives the prompt that the current session has, the actual command_string that
323 the user typed and if the command is interactive. If it is interactive the
324 plugin controller does not choose a new active plugin but use the one the
325 is already set (if none is set it raises an exeception).
326
327 If always returns an string. It could be modified by the active plugin or, if
328 there is none available, it will return the original command_string.
329 """
330
331 if interactive:
332 return None
333 else:
334 self._disable_active_plugin()
335
336 choosen_plugin = self._get_plugins_by_input(command_string)
337 if choosen_plugin is None:
338 model.api.devlog("There is no active plugin to handle current command/user input")
339 return None
340 self._active_plugin = choosen_plugin
341
342 modified_cmd_string = self._active_plugin.processCommandString(
343 username,
344 current_path,
345 command_string)
346
347 if self._is_command_malformed(command_string, modified_cmd_string):
348 return None
349 else:
350 cmd_info = CommandRunInformation(
351 **{'workspace': model.api.getActiveWorkspace().name,
352 'itime': time(),
353 'command': command_string.split()[0],
354 'params': ' '.join(command_string.split()[1:])})
355 self._mapper_manager.save(cmd_info)
356
357 self.last_command_information = cmd_info
358
359 return modified_cmd_string if isinstance(modified_cmd_string, basestring) else None
360
361 def storeCommandOutput(self, output):
362 """
363 Receives and output string and stores it in the buffer. Returns False
364 if the output was not added to the plugin controllers buffer. Returns
365 True otherwise.
366 """
367 if not self.getActivePluginStatus():
368 return False
369 else:
370 self._buffer.write(output)
371 return True
372
373 def getPluginAutocompleteOptions(self, prompt, username, current_path, command_string, interactive):
374 """
375 This method should return a list of possible completitions based on the
376 current output.
377 TODO: We should think how to actually implement this...
378 May be checking which plugin should handle the command in the current input
379 and then passing it to the plugin to return a list of possible values.
380 Each plugin implementation should return possible option according to
381 what was received since it's the plugin the one it knows the command line
382 parameters, etc.
383 """
384 if interactive:
385 return None
386 else:
387 self._disable_active_plugin()
388
389 choosen_plugin = self._get_plugins_by_input(command_string)
390 if choosen_plugin is None:
391 model.api.devlog("There is no active plugin to handle current command/user input")
392 return None
393
394 self._active_plugin = choosen_plugin
395
396 new_options = self._active_plugin.getCompletitionSuggestionsList(command_string)
397 return new_options
398
399 def getActivePluginStatus(self):
400 """
401 Returns true if an active plugin is set, otherwise return False.
402 """
403 return (self._active_plugin is not None)
404
405 def _disable_active_plugin(self):
406 """
407 This method is suppose to disable the active plugin.
408 """
409 model.api.devlog("Disabling active plugin")
410 self._active_plugin = None
411
412 def onCommandFinished(self):
413 """
414 This method is called when the last executed command has finished. It's
415 in charge of giving the plugin the output to be parsed.
416 """
417 cmd_info = self.last_command_information
418 cmd_info.duration = time() - cmd_info.itime
419 self._mapper_manager.save(cmd_info)
420
421 if self._active_plugin.has_custom_output():
422 if not os.path.isfile(self._active_plugin.get_custom_file_path()):
423 model.api.devlog("Report file PluginController output file (%s) not created" % self._active_plugin.get_custom_file_path())
424 return False
425 output_file = open(self._active_plugin.get_custom_file_path(), 'r')
426 output = output_file.read()
427 self._buffer.seek(0)
428 self._buffer.truncate()
429 self._buffer.write(output)
430
431 self.processOutput(self._active_plugin, self._buffer.getvalue())
432
433 self._buffer.seek(0)
434 self._buffer.truncate()
435 model.api.devlog("PluginController buffer cleared")
436
437 self._disable_active_plugin()
438
439 return True
440
441
442 class PluginControllerForApi(PluginControllerBase):
443 def __init__(self, id, available_plugins, mapper_manager):
444 PluginControllerBase.__init__(self, id, available_plugins, mapper_manager)
445 self._active_plugins = {}
446
447 def processCommandInput(self, command_string):
448
449 plugin = self._get_plugins_by_input(command_string)
450
451 if plugin:
452 modified_cmd_string = plugin.processCommandString("", "", command_string)
453 if not self._is_command_malformed(command_string, modified_cmd_string):
454
455 cmd_info = CommandRunInformation(
456 **{'workspace': model.api.getActiveWorkspace().name,
457 'itime': time(),
458 'command': command_string.split()[0],
459 'params': ' '.join(command_string.split()[1:])})
460 self._mapper_manager.save(cmd_info)
461
462 self._active_plugins[command_string] = plugin, cmd_info
463
464 output_file_path = None
465 if plugin.has_custom_output():
466 output_file_path = plugin.get_custom_file_path()
467 return True, modified_cmd_string, output_file_path
468
469 return False, None, None
470
471 def getPluginAutocompleteOptions(self, command_string):
472 # if interactive:
473 # return None
474 # else:
475 # self._disable_active_plugin()
476
477 # choosen_plugin = self._get_plugins_by_input(command_string)
478 # if choosen_plugin is None:
479 # model.api.devlog("There is no active plugin to handle current command/user input")
480 # return None
481
482 # self._active_plugin = choosen_plugin
483
484 # new_options = self._active_plugin.getCompletitionSuggestionsList(command_string)
485 # return new_options
486 pass
487
488 def onCommandFinished(self, cmd, output):
489 if cmd not in self._active_plugins.keys():
490 return False
491
492 plugin, cmd_info = self._active_plugins.get(cmd)
493 cmd_info.duration = time() - cmd_info.itime
494 self._mapper_manager.save(cmd_info)
495
496 self.processOutput(plugin, output)
497
498 del self._active_plugins[cmd]
499 return True
500
501 def clearActivePlugins(self):
502 self._active_plugins = {}
503
504
505 class PluginBase(object):
506 # TODO: Add class generic identifier
507 class_signature = "PluginBase"
508
509 def __init__(self):
510
511 self.data_path = CONF.getDataPath()
512 self.persistence_path = CONF.getPersistencePath()
513 # Must be unique. Check that there is not
514 # an existant plugin with the same id.
515 # TODO: Make script that list current ids.
516 self.id = None
517 self._rid = id(self)
518 self.version = None
519 self.name = None
520 self.description = ""
521 self._command_regex = None
522 self._output_file_path = None
523 self.framework_version = None
524 self._completition = {}
525 self._new_elems = []
526 self._pending_actions = Queue.Queue()
527 self._settings = {}
528
529 def has_custom_output(self):
530 return bool(self._output_file_path)
531
532 def get_custom_file_path(self):
533 return self._output_file_path
534
535 def get_ws(self):
536 return CONF.getLastWorkspace()
537
538 def getSettings(self):
539 for param, (param_type, value) in self._settings.iteritems():
540 yield param, value
541
542 def getSetting(self, name):
543 setting_type, value = self._settings[name]
544 return value
545
546 def addSetting(self, param, param_type, value):
547 self._settings[param] = param_type, value
548
549 def updateSettings(self, new_settings):
550 for name, value in new_settings.iteritems():
551 setting_type, curr_value = self._settings[name]
552 self._settings[name] = setting_type, setting_type(value)
553
554 def canParseCommandString(self, current_input):
555 """
556 This method can be overriden in the plugin implementation
557 if a different kind of check is needed
558 """
559 return self._command_regex is not None and\
560 self._command_regex.match(current_input.strip()) is not None
561
562
563 def getCompletitionSuggestionsList(self, current_input):
564 """
565 This method can be overriden in the plugin implementation
566 if a different kind of check is needed
567 """
568
569 words=current_input.split(" ")
570
571 cword=words[len(words)-1]
572
573
574
575 options={}
576 for k,v in self._completition.iteritems():
577 if re.search(str("^"+cword),k,flags=re.IGNORECASE):
578
579 options[k]=v
580
581 return options
582
583 def parseOutputString(self, output):
584 """
585 This method must be implemented.
586 This method will be called when the command finished executing and
587 the complete output will be received to work with it
588 Using the output the plugin can create and add hosts, interfaces, services, etc.
589 """
590 pass
591
592 def processCommandString(self, username, current_path, command_string):
593 """
594 With this method a plugin can add aditional arguments to the command that
595 it's going to be executed.
596 """
597 return None
598
599 def getParsedElementsDict(self):
600 """
601 This method must be implemented and must return
602 a dictionary with the following form.
603
604 { 'FrameworkVersion': self.framework_version,
605 'HostList': list_of_host_dictionaries,
606 'PortList': list_of_port_dictionaries }
607
608 list_of_host_dictionaries: must be 'None' or a list of
609 dictionaries that have the following form
610
611 { 'HostId': string,
612 'HostAddress': string,
613 ... }
614
615 list_of_port_dictionaries: must be 'None' or a list of
616 dictionaries that have the following form
617
618 { 'PortNumber': integer,
619 'Status': 'OPEN' or 'CLOSED',
620 'Service': string,
621 ... }
622 """
623 pass
624
625 def _set_host(self):
626
627 pass
628
629 def __addPendingAction(self, *args):
630 """
631 Adds a new pending action to the queue
632 Action is build with generic args tuple.
633 The caller of this function has to build the action in the right
634 way since no checks are preformed over args
635 """
636 self._pending_actions.put(args)
637
638 def createAndAddHost(self, name, os = "unknown", category = None, update = False, old_hostname = None):
639 self.__addPendingAction(modelactions.CADDHOST, name, os, category, update, old_hostname)
640 return factory.generateID(Host.class_signature, name=name, os=os)
641
642 def createAndAddInterface(self, host_id, name = "", mac = "00:00:00:00:00:00",
643 ipv4_address = "0.0.0.0", ipv4_mask = "0.0.0.0",
644 ipv4_gateway = "0.0.0.0", ipv4_dns = [],
645 ipv6_address = "0000:0000:0000:0000:0000:0000:0000:0000", ipv6_prefix = "00",
646 ipv6_gateway = "0000:0000:0000:0000:0000:0000:0000:0000", ipv6_dns = [],
647 network_segment = "", hostname_resolution = []):
648 self.__addPendingAction(modelactions.CADDINTERFACE, host_id, name, mac, ipv4_address,
649 ipv4_mask, ipv4_gateway, ipv4_dns, ipv6_address, ipv6_prefix, ipv6_gateway, ipv6_dns,
650 network_segment, hostname_resolution)
651 return factory.generateID(
652 Interface.class_signature, parent_id=host_id, name=name, mac=mac,
653 ipv4_address=ipv4_address, ipv4_mask=ipv4_mask,
654 ipv4_gateway=ipv4_gateway, ipv4_dns=ipv4_dns,
655 ipv6_address=ipv6_address, ipv6_prefix=ipv6_prefix,
656 ipv6_gateway=ipv6_gateway, ipv6_dns=ipv6_dns,
657 network_segment=network_segment,
658 hostname_resolution=hostname_resolution)
659
660 def createAndAddServiceToInterface(self, host_id, interface_id, name, protocol = "tcp?",
661 ports = [], status = "running", version = "unknown", description = ""):
662 self.__addPendingAction(modelactions.CADDSERVICEINT, host_id, interface_id, name, protocol,
663 ports, status, version, description)
664 return factory.generateID(
665 Service.class_signature,
666 name=name, protocol=protocol, ports=ports,
667 status=status, version=version, description=description, parent_id=interface_id)
668
669 def createAndAddVulnToHost(self, host_id, name, desc="", ref=[], severity="", resolution=""):
670 self.__addPendingAction(modelactions.CADDVULNHOST, host_id, name, desc, ref, severity, resolution)
671 return factory.generateID(
672 ModelObjectVuln.class_signature,
673 name=name, desc=desc, ref=ref, severity=severity,
674 resolution=resolution, parent_id=host_id)
675
676 def createAndAddVulnToInterface(self, host_id, interface_id, name, desc="", ref=[], severity="", resolution=""):
677 self.__addPendingAction(modelactions.CADDVULNINT, host_id, interface_id, name, desc, ref, severity, resolution)
678 return factory.generateID(
679 ModelObjectVuln.class_signature,
680 name=name, desc=desc, ref=ref, severity=severity,
681 resolution=resolution, parent_id=interface_id)
682
683 def createAndAddVulnToService(self, host_id, service_id, name, desc="", ref=[], severity="", resolution=""):
684 self.__addPendingAction(modelactions.CADDVULNSRV, host_id, service_id, name, desc, ref, severity, resolution)
685 return factory.generateID(
686 ModelObjectVuln.class_signature,
687 name=name, desc=desc, ref=ref, severity=severity,
688 resolution=resolution, parent_id=service_id)
689
690 def createAndAddVulnWebToService(self, host_id, service_id, name, desc="", ref=[],
691 severity="", resolution="", website="", path="", request="",
692 response="",method="",pname="", params="",query="",category=""):
693 self.__addPendingAction(modelactions.CADDVULNWEBSRV, host_id, service_id, name, desc, ref,
694 severity, resolution, website, path, request, response,
695 method, pname, params, query,category)
696 return factory.generateID(
697 ModelObjectVulnWeb.class_signature,
698 name=name, desc=desc, ref=ref, severity=severity, resolution=resolution,
699 website=website, path=path, request=request, response=response,
700 method=method, pname=pname, params=params, query=query,
701 category=category, parent_id=service_id)
702
703 def createAndAddNoteToHost(self, host_id, name, text):
704 self.__addPendingAction(modelactions.CADDNOTEHOST, host_id, name, text)
705 return factory.generateID(
706 ModelObjectNote.class_signature,
707 name=name, text=text, parent_id=host_id)
708
709 def createAndAddNoteToInterface(self, host_id, interface_id, name, text):
710 self.__addPendingAction(modelactions.CADDNOTEINT, host_id, interface_id, name, text)
711 return factory.generateID(
712 ModelObjectNote.class_signature,
713 name=name, text=text, parent_id=interface_id)
714
715 def createAndAddNoteToService(self, host_id, service_id, name, text):
716 self.__addPendingAction(modelactions.CADDNOTESRV, host_id, service_id, name, text)
717 return factory.generateID(
718 ModelObjectNote.class_signature,
719 name=name, text=text, parent_id=service_id)
720
721 def createAndAddNoteToNote(self, host_id, service_id, note_id, name, text):
722 self.__addPendingAction(modelactions.CADDNOTENOTE, host_id, service_id, note_id, name, text)
723 return factory.generateID(
724 ModelObjectNote.class_signature,
725 name=name, text=text, parent_id=note_id)
726
727 def createAndAddCredToService(self, host_id, service_id, username, password):
728 self.__addPendingAction(modelactions.CADDCREDSRV, host_id, service_id, username, password)
729 return factory.generateID(
730 ModelObjectCred.class_signature,
731 username=username, password=password, parent_id=service_id)
732
733 def addHost(self, host, category=None,update=False, old_hostname=None):
734 self.__addPendingAction(modelactions.ADDHOST, host, category, update, old_hostname)
735
736 def addInterface(self, host_id, interface):
737 self.__addPendingAction(modelactions.ADDINTERFACE, host_id, interface)
738
739 def addApplication(self, host_id, application):
740 self.__addPendingAction(modelactions.ADDAPPLICATION, host_id, application)
741
742 def addServiceToApplication(self, host_id, application_id, service):
743 self.__addPendingAction(modelactions.ADDSERVICEAPP, host_id, application_id, service)
744
745 def addServiceToInterface(self, host_id, interface_id, service):
746 self.__addPendingAction(modelactions.ADDSERVICEINT, host_id, interface_id, service)
747
748 def addVulnToHost(self, host_id, vuln):
749 self.__addPendingAction(modelactions.ADDVULNHOST, host_id, vuln)
750
751 def addVulnToInterface(self, host_id, interface_id, vuln):
752 self.__addPendingAction(modelactions.ADDVULNINT, host_id, interface_id, vuln)
753
754 def addVulnToApplication(self, host_id, application_id, vuln):
755 self.__addPendingAction(modelactions.ADDVULNAPP, host_id, application_id, vuln)
756
757 def addVulnToService(self, host_id, service_id, vuln):
758 self.__addPendingAction(modelactions.ADDVULNSRV, host_id, service_id, vuln)
759
760 def addVulnWebToService(self, host_id, service_id, vuln):
761 self.__addPendingAction(modelactions.ADDVULNWEBSRV, host_id, service_id, vuln)
762
763 def addNoteToHost(self, host_id, note):
764 self.__addPendingAction(modelactions.ADDNOTEHOST, host_id, note)
765
766 def addNoteToInterface(self, host_id, interface_id, note):
767 self.__addPendingAction(modelactions.ADDNOTEINT, host_id, interface_id, note)
768
769 def addNoteToApplication(self, host_id, application_id, note):
770 self.__addPendingAction(modelactions.ADDNOTEAPP, host_id, application_id, note)
771
772 def addNoteToService(self, host_id, service_id, note):
773 self.__addPendingAction(modelactions.ADDNOTESRV, host_id, service_id, note)
774
775 def addNoteToNote(self, host_id, service_id,note_id, note):
776 self.__addPendingAction(modelactions.ADDNOTENOTE, host_id, service_id, note_id, note)
777
778 def addCredToService(self, host_id, service_id, cred):
779 self.__addPendingAction(modelactions.ADDCREDSRV, host_id, service_id, cred)
780
781 def delServiceFromInterface(self, service, hostname,
782 intname, remote = True):
783 self.__addPendingAction(modelactions.DELSERVICEINT,hostname,intname,service,remote)
784
785 def log(self, msg, level='INFO'):
786 self.__addPendingAction(modelactions.LOG,msg,level)
787
788 def devlog(self, msg):
789 self.__addPendingAction(modelactions.DEVLOG,msg)
790
791
792 class PluginProcess(multiprocessing.Process):
793 def __init__(self, plugin_instance, output_queue, new_elem_queue):
794 multiprocessing.Process.__init__(self)
795 self.output_queue = output_queue
796 self.new_elem_queue = new_elem_queue
797 self.plugin = plugin_instance
798
799
800 def run(self):
801 proc_name = self.name
802 plugin = self.plugin
803 model.api.devlog("-" * 40)
804 model.api.devlog("proc_name = %s" % proc_name)
805 model.api.devlog("Starting run method on PluginProcess")
806 model.api.devlog('parent process: %s' % os.getppid())
807 model.api.devlog('process id: %s' % os.getpid())
808 model.api.devlog("-" * 40)
809 done = False
810 while not done:
811 output = self.output_queue.get()
812 if output is not None:
813 model.api.devlog('%s: %s' % (proc_name, "New Output"))
814 try:
815 self.output = output
816 self.plugin.parseOutputString(output)
817 except Exception:
818 model.api.log('Plugin Error: %s, (%s)' % (plugin.id, sha1OfStr(output)),"DEBUG")
819 model.api.devlog("Plugin raised an exception:")
820 model.api.devlog(traceback.format_exc())
821 else:
822 while True:
823 try:
824 self.new_elem_queue.put(self.plugin._pending_actions.get(block=False))
825 except Queue.Empty:
826 model.api.log('Plugin Error: %s, (%s)' % (plugin.id, sha1OfStr(output)),"DEBUG")
827 model.api.devlog("PluginProcess run _pending_actions queue Empty. Breaking loop")
828 break
829 except Exception:
830 model.api.log('Plugin Error: %s, (%s)' % (plugin.id, sha1OfStr(output)),"DEBUG")
831 model.api.devlog("PluginProcess run getting from _pending_action queue - something strange happened... unhandled exception?")
832 model.api.devlog(traceback.format_exc())
833 break
834
835 else:
836
837 done = True
838 model.api.devlog('%s: Exiting' % proc_name)
839 model.api.log('Plugin finished: %s, (%s)' % (plugin.id, sha1OfStr(self.output)))
840
841 self.output_queue.task_done()
842 self.new_elem_queue.put(None)
843 return
844
12 # This class was moved to plugins.plugin so we need a way to
13 # support plugins that are still inheriting from core
14 PluginBase = PluginBaseExt
1313 import sys
1414 import traceback
1515
16 import plugins.core
16 from plugins.controller import PluginController
1717 from config.configuration import getInstanceConfiguration
1818 from utils.logs import getLogger
1919
3434 """
3535 Creates a new plugin controller and adds it into the controllers list.
3636 """
37 plugs = self._instancePlugins()
38 new_controller = plugins.core.PluginController(
39 id, plugs, self._mapper_manager)
37 new_controller = PluginController(
38 id, self, self._mapper_manager)
4039 self._controllers[new_controller.id] = new_controller
4140 self.updateSettings(self._plugin_settings)
4241 return new_controller
42
43 def addController(self, controller, id):
44 self._controllers[id] = controller
4345
4446 def _loadSettings(self):
4547 _plugin_settings = CONF.getPluginSettings()
98100 try:
99101 os.stat(plugin_repo_path)
100102 except OSError:
101
102103 pass
103104
104105 sys.path.append(plugin_repo_path)
121122 pass
122123
123124 def getPlugins(self):
124 return self._instancePlugins()
125 plugins = self._instancePlugins()
126 for id, plugin in plugins.items():
127 if id in self._plugin_settings:
128 plugin.updateSettings(self._plugin_settings[id]["settings"])
129 return plugins
125130
126131 def _updatePluginSettings(self, new_plugin_id):
127132 pass
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
11 class modelactions:
12 ADDHOST = 2000
13 CADDHOST = 2001
14 ADDINTERFACE = 2002
15 CADDINTERFACE = 2003
16 ADDSERVICEINT = 2004
17 ADDSERVICEAPP = 2005
18 CADDSERVICEINT = 2006
19 CADDSERVICEAPP = 2007
20 CADDSERVICEHOST = 2008
21 ADDAPPLICATION = 2009
22 CADDAPPLICATION = 2010
23 ADDVULNINT = 2013
24 CADDVULNINT = 2014
25 ADDVULNAPP = 2015
26 CADDVULNAPP = 2016
27 ADDVULNHOST = 2017
28 CADDVULNHOST = 2018
29 ADDVULNSRV = 2019
30 CADDVULNSRV = 2020
31 ADDNOTEINT = 2021
32 CADDNOTEINT = 2022
33 ADDNOTEAPP = 2023
34 CADDNOTEAPP = 2024
35 ADDNOTEHOST = 2025
36 CADDNOTEHOST = 2026
37 ADDNOTESRV = 2027
38 CADDNOTESRV = 2028
39 CADDNOTEVULN = 2030
40 CADDNOTEVULN = 2031
41 LOG = 2032
42 DEVLOG = 2033
43 DELSERVICEINT = 2034
44 ADDCREDSRV = 2035
45 CADDCREDSRV = 2036
46 ADDVULNWEBSRV = 2037
47 CADDVULNWEBSRV = 2038
48 ADDNOTENOTE = 2039
49 CADDNOTENOTE = 2039
50 PLUGINSTART = 3000
51 PLUGINEND = 3001
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 multiprocessing
11 import os
12 import re
13 import Queue
14 import traceback
15
16 import model.api
17 import model.common
18 from model.common import (
19 factory,
20 ModelObjectVuln,
21 ModelObjectVulnWeb,
22 ModelObjectNote,
23 ModelObjectCred
24 )
25 from model.hosts import Host, Interface, Service
26 from plugins.modelactions import modelactions
27
28 from config.configuration import getInstanceConfiguration
29 CONF = getInstanceConfiguration()
30
31
32 class PluginBase(object):
33 # TODO: Add class generic identifier
34 class_signature = "PluginBase"
35
36 def __init__(self):
37
38 self.data_path = CONF.getDataPath()
39 self.persistence_path = CONF.getPersistencePath()
40 # Must be unique. Check that there is not
41 # an existant plugin with the same id.
42 # TODO: Make script that list current ids.
43 self.id = None
44 self._rid = id(self)
45 self.version = None
46 self.name = None
47 self.description = ""
48 self._command_regex = None
49 self._output_file_path = None
50 self.framework_version = None
51 self._completition = {}
52 self._new_elems = []
53 self._pending_actions = Queue.Queue()
54 self._settings = {}
55
56 def has_custom_output(self):
57 return bool(self._output_file_path)
58
59 def get_custom_file_path(self):
60 return self._output_file_path
61
62 def getSettings(self):
63 for param, (param_type, value) in self._settings.iteritems():
64 yield param, value
65
66 def get_ws(self):
67 return CONF.getLastWorkspace()
68
69 def getSetting(self, name):
70 setting_type, value = self._settings[name]
71 return value
72
73 def addSetting(self, param, param_type, value):
74 self._settings[param] = param_type, value
75
76 def updateSettings(self, new_settings):
77 for name, value in new_settings.iteritems():
78 setting_type, curr_value = self._settings[name]
79 self._settings[name] = setting_type, setting_type(value)
80
81 def canParseCommandString(self, current_input):
82 """
83 This method can be overriden in the plugin implementation
84 if a different kind of check is needed
85 """
86 return (self._command_regex is not None and
87 self._command_regex.match(current_input.strip()) is not None)
88
89 def getCompletitionSuggestionsList(self, current_input):
90 """
91 This method can be overriden in the plugin implementation
92 if a different kind of check is needed
93 """
94 words = current_input.split(" ")
95 cword = words[len(words) - 1]
96
97 options = {}
98 for k, v in self._completition.iteritems():
99 if re.search(str("^" + cword), k, flags=re.IGNORECASE):
100 options[k] = v
101
102 return options
103
104 def processOutput(self, term_output):
105 output = term_output
106 if self.has_custom_output():
107 output = open(self.get_custom_file_path(), 'r').read()
108 self.parseOutputString(output)
109
110 def processReport(self, filepath):
111 output = open(filepath, 'r').read()
112 self.parseOutputString(output)
113
114 def parseOutputString(self, output):
115 """
116 This method must be implemented.
117 This method will be called when the command finished executing and
118 the complete output will be received to work with it
119 Using the output the plugin can create and add hosts, interfaces,
120 services, etc.
121 """
122 pass
123
124 def processCommandString(self, username, current_path, command_string):
125 """
126 With this method a plugin can add aditional arguments to the
127 command that it's going to be executed.
128 """
129 return None
130
131 def __addPendingAction(self, *args):
132 """
133 Adds a new pending action to the queue
134 Action is build with generic args tuple.
135 The caller of this function has to build the action in the right
136 way since no checks are preformed over args
137 """
138 self._pending_actions.put(args)
139
140 def createAndAddHost(self, name, os="unknown", category=None,
141 update=False, old_hostname=None):
142 self.__addPendingAction(modelactions.CADDHOST, name, os, category,
143 update, old_hostname)
144 return factory.generateID(Host.class_signature, name=name, os=os)
145
146 def createAndAddInterface(
147 self, host_id, name="", mac="00:00:00:00:00:00",
148 ipv4_address="0.0.0.0", ipv4_mask="0.0.0.0", ipv4_gateway="0.0.0.0",
149 ipv4_dns=[], ipv6_address="0000:0000:0000:0000:0000:0000:0000:0000",
150 ipv6_prefix="00",
151 ipv6_gateway="0000:0000:0000:0000:0000:0000:0000:0000", ipv6_dns=[],
152 network_segment="", hostname_resolution=[]
153 ):
154 self.__addPendingAction(modelactions.CADDINTERFACE, host_id, name,
155 mac, ipv4_address, ipv4_mask, ipv4_gateway,
156 ipv4_dns, ipv6_address, ipv6_prefix,
157 ipv6_gateway, ipv6_dns,
158 network_segment, hostname_resolution)
159 return factory.generateID(
160 Interface.class_signature, parent_id=host_id, name=name, mac=mac,
161 ipv4_address=ipv4_address, ipv4_mask=ipv4_mask,
162 ipv4_gateway=ipv4_gateway, ipv4_dns=ipv4_dns,
163 ipv6_address=ipv6_address, ipv6_prefix=ipv6_prefix,
164 ipv6_gateway=ipv6_gateway, ipv6_dns=ipv6_dns,
165 network_segment=network_segment,
166 hostname_resolution=hostname_resolution)
167
168 def createAndAddServiceToInterface(self, host_id, interface_id, name,
169 protocol="tcp?", ports=[],
170 status="running", version="unknown",
171 description=""):
172 self.__addPendingAction(modelactions.CADDSERVICEINT, host_id,
173 interface_id, name, protocol, ports, status,
174 version, description)
175 return factory.generateID(
176 Service.class_signature,
177 name=name, protocol=protocol, ports=ports,
178 status=status, version=version, description=description,
179 parent_id=interface_id)
180
181 def createAndAddVulnToHost(self, host_id, name, desc="", ref=[],
182 severity="", resolution=""):
183 self.__addPendingAction(modelactions.CADDVULNHOST, host_id, name,
184 desc, ref, severity, resolution)
185 return factory.generateID(
186 ModelObjectVuln.class_signature,
187 name=name, desc=desc, ref=ref, severity=severity,
188 resolution=resolution, parent_id=host_id)
189
190 def createAndAddVulnToInterface(self, host_id, interface_id, name,
191 desc="", ref=[], severity="",
192 resolution=""):
193 self.__addPendingAction(modelactions.CADDVULNINT, host_id,
194 interface_id, name, desc, ref, severity,
195 resolution)
196 return factory.generateID(
197 ModelObjectVuln.class_signature,
198 name=name, desc=desc, ref=ref, severity=severity,
199 resolution=resolution, parent_id=interface_id)
200
201 def createAndAddVulnToService(self, host_id, service_id, name, desc="",
202 ref=[], severity="", resolution=""):
203 self.__addPendingAction(modelactions.CADDVULNSRV, host_id,
204 service_id, name, desc, ref, severity,
205 resolution)
206 return factory.generateID(
207 ModelObjectVuln.class_signature,
208 name=name, desc=desc, ref=ref, severity=severity,
209 resolution=resolution, parent_id=service_id)
210
211 def createAndAddVulnWebToService(self, host_id, service_id, name, desc="",
212 ref=[], severity="", resolution="",
213 website="", path="", request="",
214 response="", method="", pname="",
215 params="", query="", category=""):
216 self.__addPendingAction(modelactions.CADDVULNWEBSRV, host_id,
217 service_id, name, desc, ref, severity,
218 resolution, website, path, request, response,
219 method, pname, params, query, category)
220 return factory.generateID(
221 ModelObjectVulnWeb.class_signature,
222 name=name, desc=desc, ref=ref, severity=severity,
223 resolution=resolution, website=website, path=path, request=request,
224 response=response, method=method, pname=pname, params=params,
225 query=query, category=category, parent_id=service_id)
226
227 def createAndAddNoteToHost(self, host_id, name, text):
228 self.__addPendingAction(modelactions.CADDNOTEHOST, host_id, name, text)
229 return factory.generateID(
230 ModelObjectNote.class_signature,
231 name=name, text=text, parent_id=host_id)
232
233 def createAndAddNoteToInterface(self, host_id, interface_id, name, text):
234 self.__addPendingAction(modelactions.CADDNOTEINT, host_id, interface_id,
235 name, text)
236 return factory.generateID(
237 ModelObjectNote.class_signature,
238 name=name, text=text, parent_id=interface_id)
239
240 def createAndAddNoteToService(self, host_id, service_id, name, text):
241 self.__addPendingAction(modelactions.CADDNOTESRV, host_id, service_id,
242 name, text)
243 return factory.generateID(
244 ModelObjectNote.class_signature,
245 name=name, text=text, parent_id=service_id)
246
247 def createAndAddNoteToNote(self, host_id, service_id, note_id, name, text):
248 self.__addPendingAction(modelactions.CADDNOTENOTE, host_id, service_id,
249 note_id, name, text)
250 return factory.generateID(
251 ModelObjectNote.class_signature,
252 name=name, text=text, parent_id=note_id)
253
254 def createAndAddCredToService(self, host_id, service_id, username,
255 password):
256 self.__addPendingAction(modelactions.CADDCREDSRV, host_id, service_id,
257 username, password)
258 return factory.generateID(
259 ModelObjectCred.class_signature,
260 username=username, password=password, parent_id=service_id)
261
262 def addHost(self, host, category=None, update=False, old_hostname=None):
263 self.__addPendingAction(modelactions.ADDHOST, host, category, update,
264 old_hostname)
265
266 def addInterface(self, host_id, interface):
267 self.__addPendingAction(modelactions.ADDINTERFACE, host_id, interface)
268
269 def addApplication(self, host_id, application):
270 self.__addPendingAction(modelactions.ADDAPPLICATION, host_id,
271 application)
272
273 def addServiceToApplication(self, host_id, application_id, service):
274 self.__addPendingAction(modelactions.ADDSERVICEAPP, host_id,
275 application_id, service)
276
277 def addServiceToInterface(self, host_id, interface_id, service):
278 self.__addPendingAction(modelactions.ADDSERVICEINT, host_id,
279 interface_id, service)
280
281 def addVulnToHost(self, host_id, vuln):
282 self.__addPendingAction(modelactions.ADDVULNHOST, host_id, vuln)
283
284 def addVulnToInterface(self, host_id, interface_id, vuln):
285 self.__addPendingAction(modelactions.ADDVULNINT, host_id, interface_id,
286 vuln)
287
288 def addVulnToApplication(self, host_id, application_id, vuln):
289 self.__addPendingAction(modelactions.ADDVULNAPP, host_id,
290 application_id, vuln)
291
292 def addVulnToService(self, host_id, service_id, vuln):
293 self.__addPendingAction(modelactions.ADDVULNSRV, host_id, service_id,
294 vuln)
295
296 def addVulnWebToService(self, host_id, service_id, vuln):
297 self.__addPendingAction(modelactions.ADDVULNWEBSRV, host_id, service_id,
298 vuln)
299
300 def addNoteToHost(self, host_id, note):
301 self.__addPendingAction(modelactions.ADDNOTEHOST, host_id, note)
302
303 def addNoteToInterface(self, host_id, interface_id, note):
304 self.__addPendingAction(modelactions.ADDNOTEINT, host_id, interface_id,
305 note)
306
307 def addNoteToApplication(self, host_id, application_id, note):
308 self.__addPendingAction(modelactions.ADDNOTEAPP, host_id,
309 application_id, note)
310
311 def addNoteToService(self, host_id, service_id, note):
312 self.__addPendingAction(modelactions.ADDNOTESRV, host_id, service_id,
313 note)
314
315 def addNoteToNote(self, host_id, service_id, note_id, note):
316 self.__addPendingAction(modelactions.ADDNOTENOTE, host_id, service_id,
317 note_id, note)
318
319 def addCredToService(self, host_id, service_id, cred):
320 self.__addPendingAction(modelactions.ADDCREDSRV, host_id, service_id,
321 cred)
322
323 def delServiceFromInterface(self, service, hostname,
324 intname, remote=True):
325 self.__addPendingAction(modelactions.DELSERVICEINT, hostname, intname,
326 service, remote)
327
328 def log(self, msg, level='INFO'):
329 self.__addPendingAction(modelactions.LOG, msg, level)
330
331 def devlog(self, msg):
332 self.__addPendingAction(modelactions.DEVLOG, msg)
333
334
335 class PluginTerminalOutput(PluginBase):
336 def __init__(self):
337 super(PluginTerminalOutput, self).__init__()
338
339 def processOutput(self, term_output):
340 self.parseOutputString(term_output)
341
342
343 class PluginCustomOutput(PluginBase):
344 def __init__(self):
345 super(PluginCustomOutput, self).__init__()
346
347 def processOutput(self, term_output):
348 # we discard the term_output since it's not necessary
349 # for this type of plugins
350 self.processReport(self._output_file_path)
351
352
353 class PluginProcess(multiprocessing.Process):
354 def __init__(self, plugin_instance, output_queue, new_elem_queue, isReport=False):
355 multiprocessing.Process.__init__(self)
356 self.output_queue = output_queue
357 self.new_elem_queue = new_elem_queue
358 self.plugin = plugin_instance
359 self.isReport = isReport
360
361 def run(self):
362 proc_name = self.name
363 model.api.devlog("-" * 40)
364 model.api.devlog("proc_name = %s" % proc_name)
365 model.api.devlog("Starting run method on PluginProcess")
366 model.api.devlog('parent process: %s' % os.getppid())
367 model.api.devlog('process id: %s' % os.getpid())
368 model.api.devlog("-" * 40)
369 done = False
370 while not done:
371 output = self.output_queue.get()
372 if output is not None:
373 model.api.devlog('%s: %s' % (proc_name, "New Output"))
374 try:
375 if self.isReport:
376 self.plugin.processReport(output)
377 else:
378 self.plugin.processOutput(output)
379 except Exception:
380 model.api.devlog("Plugin raised an exception:")
381 model.api.devlog(traceback.format_exc())
382 else:
383 while True:
384 try:
385 self.new_elem_queue.put(
386 self.plugin._pending_actions.get(block=False))
387 except Queue.Empty:
388 model.api.devlog(
389 ("PluginProcess run _pending_actions"
390 " queue Empty. Breaking loop"))
391 break
392 except Exception:
393 model.api.devlog(
394 ("PluginProcess run getting from "
395 "_pending_action queue - something strange "
396 "happened... unhandled exception?"))
397 model.api.devlog(traceback.format_exc())
398 break
399
400 else:
401
402 done = True
403 model.api.devlog('%s: Exiting' % proc_name)
404
405 self.output_queue.task_done()
406 self.new_elem_queue.put(None)
407 return
88 '''
99 from __future__ import with_statement
1010 from plugins import core
11 from model import api
11 import socket
12 import sys
1213 import re
13 import os, socket
14 import pprint
15 import sys
14 import os
1615
1716 try:
1817 import xml.etree.cElementTree as ET
2120 except ImportError:
2221 import xml.etree.ElementTree as ET
2322 ETREE_VERSION = ET.VERSION
24
23
2524 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
2625
2726 current_path = os.path.abspath(os.getcwd())
2827
29 __author__ = "Francisco Amato"
30 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
31 __credits__ = ["Francisco Amato"]
32 __version__ = "1.0.0"
28 __author__ = "Francisco Amato"
29 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
30 __credits__ = ["Francisco Amato"]
31 __version__ = "1.0.0"
3332 __maintainer__ = "Francisco Amato"
34 __email__ = "[email protected]"
35 __status__ = "Development"
36
37
38
39
33 __email__ = "[email protected]"
34 __status__ = "Development"
35
4036
4137 class AcunetixXmlParser(object):
4238 """
43 The objective of this class is to parse an xml file generated by the acunetix tool.
39 The objective of this class is to parse an xml file generated by
40 the acunetix tool.
4441
4542 TODO: Handle errors.
46 TODO: Test acunetix output version. Handle what happens if the parser doesn't support it.
43 TODO: Test acunetix output version. Handle what happens if
44 the parser doesn't support it.
4745 TODO: Test cases.
4846
4947 @param acunetix_xml_filepath A proper xml generated by acunetix
5048 """
5149 def __init__(self, xml_output):
52 #self.filepath = acunetix_xml_filepath
5350
5451 tree = self.parse_xml(xml_output)
55
52
5653 if tree:
5754 self.sites = [data for data in self.get_items(tree)]
5855 else:
5956 self.sites = []
60
6157
6258 def parse_xml(self, xml_output):
6359 """
6460 Open and parse an xml file.
6561
66 TODO: Write custom parser to just read the nodes that we need instead of
67 reading the whole file.
62 TODO: Write custom parser to just read the nodes that we need instead
63 of reading the whole file.
6864
6965 @return xml_tree An xml tree instance. None if error.
7066 """
8076 """
8177 @return items A list of Host instances
8278 """
83
79
8480 for node in tree.findall('Scan'):
8581 yield Site(node)
8682
8783
88
8984 def get_attrib_from_subnode(xml_node, subnode_xpath_expr, attrib_name):
9085 """
9186 Finds a subnode in the item node and the retrieves a value from it
9489 """
9590 global ETREE_VERSION
9691 node = None
97
92
9893 if ETREE_VERSION[0] <= 1 and ETREE_VERSION[1] < 3:
99
100 match_obj = re.search("([^\@]+?)\[\@([^=]*?)=\'([^\']*?)\'",subnode_xpath_expr)
94
95 match_obj = re.search(
96 "([^\@]+?)\[\@([^=]*?)=\'([^\']*?)\'",
97 subnode_xpath_expr)
98
10199 if match_obj is not None:
102100 node_to_find = match_obj.group(1)
103101 xpath_attrib = match_obj.group(2)
118116 return None
119117
120118
121
122
123119 class Site(object):
124120 def __init__(self, item_node):
125121 self.node = item_node
126
122
127123 self.url = self.get_text_from_subnode('StartURL')
128 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;%\$#\=~_\-]+)).*?$", self.url)
129
124 mregex = re.search(
125 "(http|https|ftp)\://([a-zA-Z0-9\.\-]+(\:[a-zA-Z0-9\.&amp;%\$\-]+)"
126 "*@)*((25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]"
127 ")\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0"
128 ")\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0"
129 ")\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])|"
130 "localhost|([a-zA-Z0-9\-]+\.)*[a-zA-Z0-9\-]+\.(com|edu|gov|int|mil"
131 "|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))[\:"
132 "]*([0-9]+)*([/]*($|[a-zA-Z0-9\.\,\?\'\\\+&amp;%\$#\=~_\-]+)).*?$",
133 self.url)
134
130135 self.protocol = mregex.group(1)
131136 self.host = mregex.group(4)
132 self.port=80
137 self.port = 80
133138 if self.protocol == 'https':
134 self.port=443
139 self.port = 443
135140 if mregex.group(11) is not None:
136141 self.port = mregex.group(11)
137
138
142
139143 self.ip = self.resolve(self.host)
140144 self.os = self.get_text_from_subnode('Os')
141145 self.banner = self.get_text_from_subnode('Banner')
161165 except:
162166 pass
163167 return host
168
164169
165170 class Item(object):
166171 """
179184 self.parameter = self.get_text_from_subnode('Parameter')
180185 self.uri = self.get_text_from_subnode('Affects')
181186 self.desc = self.get_text_from_subnode('Description')
182 self.desc += "\nSolution: " + self.get_text_from_subnode('Recommendation') if self.get_text_from_subnode('Recommendation') else ""
183 self.desc += "\nDetails: " + self.get_text_from_subnode('Details') if self.get_text_from_subnode('reference') else ""
184
185 self.ref=[]
187
188 if self.get_text_from_subnode('Recommendation'):
189 self.desc += "\nSolution: " + self.get_text_from_subnode(
190 'Recommendation')
191 else:
192 self.desc += ""
193
194 if self.get_text_from_subnode('reference'):
195 self.desc += "\nDetails: " + self.get_text_from_subnode('Details')
196 else:
197 self.desc += ""
198
199 self.ref = []
186200 for n in item_node.findall('References/Reference'):
187201 n2 = n.find('URL')
188202 self.ref.append(n2.text)
189
203
190204 def get_text_from_subnode(self, subnode_xpath_expr):
191205 """
192206 Finds a subnode in the host node and the retrieves a value from it.
200214 return None
201215
202216
203
204217 class AcunetixPlugin(core.PluginBase):
205218 """
206219 Example plugin to parse acunetix output.
207220 """
208221 def __init__(self):
209222 core.PluginBase.__init__(self)
210 self.id = "Acunetix"
211 self.name = "Acunetix XML Output Plugin"
212 self.plugin_version = "0.0.1"
213 self.version = "9"
214 self.framework_version = "1.0.0"
215 self.options = None
223 self.id = "Acunetix"
224 self.name = "Acunetix XML Output Plugin"
225 self.plugin_version = "0.0.1"
226 self.version = "9"
227 self.framework_version = "1.0.0"
228 self.options = None
216229 self._current_output = None
217230 self.target = None
218 self._command_regex = re.compile(r'^(acunetix|sudo acunetix|\.\/acunetix).*?')
231 self._command_regex = re.compile(
232 r'^(acunetix|sudo acunetix|\.\/acunetix).*?')
219233
220234 global current_path
221 self._output_file_path = os.path.join(self.data_path,
222 "acunetix_output-%s.xml" % self._rid)
223
224
225 def parseOutputString(self, output, debug = False):
226 """
227 This method will discard the output the shell sends, it will read it from
228 the xml where it expects it to be present.
235 self._output_file_path = os.path.join(
236 self.data_path,
237 "acunetix_output-%s.xml" % self._rid)
238
239 def parseOutputString(self, output, debug=False):
240 """
241 This method will discard the output the shell sends, it will read it
242 from the xml where it expects it to be present.
229243
230244 NOTE: if 'debug' is true then it is being run from a test case and the
231245 output being sent is valid.
237251 host = []
238252 if site.host != site.ip:
239253 host = [site.host]
240 h_id = self.createAndAddHost(site.ip,site.os)
241 i_id = self.createAndAddInterface(h_id, site.ip, ipv4_address=site.ip, hostname_resolution=host)
242 s_id = self.createAndAddServiceToInterface(h_id, i_id, "http", "tcp",
243 ports = [site.port],
244 version= site.banner,
245 status = 'open')
246 n_id = self.createAndAddNoteToService(h_id,s_id,"website","")
247 n2_id = self.createAndAddNoteToNote(h_id,s_id,n_id,site.host,"")
254 h_id = self.createAndAddHost(site.ip, site.os)
255 i_id = self.createAndAddInterface(
256 h_id,
257 site.ip,
258 ipv4_address=site.ip,
259 hostname_resolution=host)
260
261 s_id = self.createAndAddServiceToInterface(
262 h_id,
263 i_id,
264 "http",
265 "tcp",
266 ports=[site.port],
267 version=site.banner,
268 status='open')
269
270 n_id = self.createAndAddNoteToService(h_id, s_id, "website", "")
271 self.createAndAddNoteToNote(h_id, s_id, n_id, site.host, "")
248272
249273 for item in site.items:
250 v_id = self.createAndAddVulnWebToService(h_id, s_id, item.name,
251 item.desc, website=site.host, severity=item.severity,
252 path=item.uri,params=item.parameter,
253 request=item.request,response=item.response,ref=item.ref)
274 self.createAndAddVulnWebToService(
275 h_id,
276 s_id,
277 item.name,
278 item.desc,
279 website=site.host,
280 severity=item.severity,
281 path=item.uri,
282 params=item.parameter,
283 request=item.request,
284 response=item.response,
285 ref=item.ref)
254286
255287 del parser
256
257
258
259
260
261
262
288
263289 def processCommandString(self, username, current_path, command_string):
264290 return None
265
266291
267292 def setHost(self):
268293 pass
44 Faraday Penetration Test IDE
55 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
66 See the file 'doc/LICENSE' for the license information
7 '''
78
8 '''
99 from __future__ import with_statement
1010 from plugins import core
11 from model import api
12 import re
13 import os
1411 import argparse
1512 import shlex
1613 import socket
1714 import random
15 import re
16 import os
17
1818 current_path = os.path.abspath(os.getcwd())
1919
2020
21
22
23
24
25
2621 class AmapPlugin(core.PluginBase):
27 """
28 Example plugin to parse amap output.
29 """
22 """ Example plugin to parse amap output."""
3023 def __init__(self):
3124 core.PluginBase.__init__(self)
32 self.id = "Amap"
33 self.name = "Amap Output Plugin"
34 self.plugin_version = "0.0.3"
35 self.version = "5.4"
36 self.options = None
25 self.id = "Amap"
26 self.name = "Amap Output Plugin"
27 self.plugin_version = "0.0.3"
28 self.version = "5.4"
29 self.options = None
3730 self._current_output = None
38 self._command_regex = re.compile(r'^(amap|sudo amap).*?')
39 self._hosts = []
40 self._completition = {
41 "":"amap [-A|-B|-P|-W] [-1buSRHUdqv] [[-m] -o &lt;file&gt;] [-D &lt;file&gt;] [-t/-T sec] [-c cons] [-C retries] [-p proto] [-i &lt;file&gt;] [target port [port] ...]",
42 "-A":"Map applications: send triggers and analyse responses (default)",
43 "-B":"Just grab banners, do not send triggers",
44 "-P":"No banner or application stuff - be a (full connect) port scanner",
45 "-1":"Only send triggers to a port until 1st identification. Speeeeed!",
46 "-6":"Use IPv6 instead of IPv4",
47 "-b":"Print ascii banner of responses",
48 "-i":"Nmap machine readable outputfile to read ports from",
49 "-u":"Ports specified on commandline are UDP (default is TCP)",
50 "-R":"Do NOT identify RPC / SSL services",
51 "-S":"Do NOT identify RPC / SSL services",
52 "-H":"Do NOT send application triggers marked as potentially harmful",
53 "-U":"Do NOT dump unrecognised responses (better for scripting)",
54 "-d":"Dump all responses",
55 "-v":"Verbose mode, use twice (or more!) for debug (not recommended :-)",
56 "-q":"Do not report closed ports, and do not print them as unidentified",
57 "-o":"FILE [-m] Write output to file FILE, -m creates machine readable output",
58 "-c":"CONS Amount of parallel connections to make (default 32, max 256)",
59 "-C":"RETRIES Number of reconnects on connect timeouts (see -T) (default 3)",
60 "-T":"SEC Connect timeout on connection attempts in seconds (default 5)",
61 "-t":"SEC Response wait timeout in seconds (default 5)",
62 "-p":"PROTO Only send triggers for this protocol (e.g. ftp)",
63 }
31 self._command_regex = re.compile(r'^(amap|sudo amap).*?')
32 self._hosts = []
6433
6534 global current_path
66 self._file_output_path=os.path.join(self.data_path,"amap_output-%s.txt" % random.uniform(1,10))
35 self._file_output_path = os.path.join(
36 self.data_path,
37 "amap_output-%s.txt" % random.uniform(1, 10))
6738
68
69 def parseOutputString(self, output, debug = False):
70
71
39 def parseOutputString(self, output, debug=False):
7240 if not os.path.exists(self._file_output_path):
7341 return False
7442
7543 if not debug:
7644 with open(self._file_output_path) as f:
7745 output = f.read()
78
46
7947 services = {}
8048 for line in output.split('\n'):
8149 if line.startswith('#'):
82
83 continue
84
85
86 fields = self.get_info(line)
87
88
89 if len(fields) < 6:
90
9150 continue
9251
93 address = fields[0]
52 fields = self.get_info(line)
53
54 if len(fields) < 6:
55 continue
56
57 address = fields[0]
9458 h_id = self.createAndAddHost(address)
9559
96 port = fields[1]
97 protocol = fields[2]
98 port_status = fields[3]
99
100 identification = fields[5]
60 port = fields[1]
61 protocol = fields[2]
62 port_status = fields[3]
63
64 identification = fields[5]
10165 printable_banner = fields[6]
102
66
10367 if port in services.keys():
104
10568 if identification != 'unidentified':
106
10769 services[port][5] += ', ' + identification
10870 else:
109 services[port] = [address, port, protocol, port_status, None, identification, printable_banner, None]
110
111
112 args={}
71 services[port] = [
72 address,
73 port,
74 protocol,
75 port_status,
76 None,
77 identification,
78 printable_banner,
79 None]
80
81 args = {}
82
11383 if self.args.__getattribute__("6"):
11484 self.ip = self.get_ip_6(self.args.m)
115 args['ipv6_address']=address
85 args['ipv6_address'] = address
11686 else:
117 self.ip=self.getAddress(self.args.m)
118 args['ipv4_address']=address
119
87 self.ip = self.getAddress(self.args.m)
88 args['ipv4_address'] = address
89
12090 if address != self.args.m:
121 args['hostname_resolution']=self.args.m
122
123 i_id = self.createAndAddInterface(h_id, name=address, **args)
91 args['hostname_resolution'] = self.args.m
92
93 i_id = self.createAndAddInterface(h_id, name=address, **args)
12494
12595 for key in services:
12696 service = services.get(key)
127 s_id = self.createAndAddServiceToInterface(h_id, i_id, service[5], service[2], ports = [service[1]], status = service[3],description = service[6])
128
97 self.createAndAddServiceToInterface(
98 h_id,
99 i_id,
100 service[5],
101 service[2],
102 ports=[service[1]],
103 status=service[3],
104 description=service[6])
105
129106 return True
130107
131108 file_arg_re = re.compile(r"^.*(-o \s*[^\s]+\s+(?:-m|)).*$")
132109
133 def get_info(self,data):
110 def get_info(self, data):
134111 if self.args.__getattribute__("6"):
135 f = re.search(r"^\[(.*)\]:(.*):(.*):(.*):(.*):(.*):(.*):(.*)",data)
136 return [f.group(1),f.group(2),f.group(3),
137 f.group(4),f.group(5),f.group(6),f.group(7),f.group(8)] if f else []
138
112 f = re.search(
113 r"^\[(.*)\]:(.*):(.*):(.*):(.*):(.*):(.*):(.*)",
114 data)
115
116 return [
117 f.group(1),
118 f.group(2),
119 f.group(3),
120 f.group(4),
121 f.group(5),
122 f.group(6),
123 f.group(7),
124 f.group(8)] if f else []
125
139126 else:
140127 return data.split(':')
141
142
143 def get_ip_6(self,host, port=0):
144
145 alladdr = socket.getaddrinfo(host,port)
146 ip6 = filter(
147 lambda x: x[0] == socket.AF_INET6,
148 alladdr
149 )
150
151
152 return list(ip6)[0][4][0]
153
154
155
128
129 def get_ip_6(self, host, port=0):
130 alladdr = socket.getaddrinfo(host, port)
131 ip6 = filter(
132 lambda x: x[0] == socket.AF_INET6,
133 alladdr)
134
135 return list(ip6)[0][4][0]
136
156137 def getAddress(self, hostname):
157138 """
158139 Returns remote IP address from hostname.
160141 try:
161142 return socket.gethostbyname(hostname)
162143 except socket.error, msg:
163
164144 return hostname
165145
166146 def processCommandString(self, username, current_path, command_string):
168148 Adds the -m parameter to get machine readable output.
169149 """
170150 arg_match = self.file_arg_re.match(command_string)
171
151
172152 parser = argparse.ArgumentParser()
173
174 parser.add_argument('-6',action='store_true')
153
154 parser.add_argument('-6', action='store_true')
175155 parser.add_argument('-o')
176156 parser.add_argument('-m')
177
178
179 self._output_file_path = os.path.join(self.data_path,"%s_%s_output-%s.xml" % (self.get_ws(),
180 self.id,
181 random.uniform(1,10)))
182
183 if arg_match is None:
184 final= re.sub(r"(^.*?amap)",
185 r"\1 -o %s -m " % self._file_output_path,
186 command_string)
157
158 self._output_file_path = os.path.join(
159 self.data_path, "%s_%s_output-%s.xml" % (
160 self.get_ws(),
161 self.id,
162 random.uniform(1, 10)))
163
164 if arg_match is None:
165 final = re.sub(
166 r"(^.*?amap)",
167 r"\1 -o %s -m " % self._file_output_path,
168 command_string)
187169 else:
188 final= re.sub(arg_match.group(1),
189 r"-o %s -m " % self._file_output_path,
190 command_string)
191
192
193 cmd=shlex.split(re.sub(r'\-h|\-\-help', r'', final))
170 final = re.sub(
171 arg_match.group(1),
172 r"-o %s -m " % self._file_output_path,
173 command_string)
174
175 cmd = shlex.split(re.sub(r'\-h|\-\-help', r'', final))
194176 if "-6" in cmd:
195177 cmd.remove("-6")
196 cmd.insert(1,"-6")
197
198
199
200 args=None
178 cmd.insert(1, "-6")
179
180 args = None
201181 if len(cmd) > 4:
202182 try:
203183 args, unknown = parser.parse_known_args(cmd)
204184 except SystemExit:
205185 pass
206
207 self.args=args
186
187 self.args = args
208188 return final
209189
210190 def setHost(self):
211191 pass
212192
193
213194 def createPlugin():
214195 return AmapPlugin()
215
366366 self.options = None
367367
368368 self._command_regex = re.compile(
369 r'^(arachni_faraday |\.\/arachni_faraday).*?'
369 r'^(arachni |\.\/arachni).*?'
370370 )
371371
372372 self.protocol = None
44 Faraday Penetration Test IDE
55 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
66 See the file 'doc/LICENSE' for the license information
7 '''
78
8 '''
99 from __future__ import with_statement
1010 from plugins import core
1111 import re
1313 import sys
1414 import random
1515
16
1716 current_path = os.path.abspath(os.getcwd())
1817
19 __author__ = "Francisco Amato"
20 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
21 __credits__ = ["Francisco Amato"]
22 __license__ = ""
23 __version__ = "1.0.0"
18 __author__ = "Francisco Amato"
19 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
20 __credits__ = ["Francisco Amato"]
21 __license__ = ""
22 __version__ = "1.0.0"
2423 __maintainer__ = "Francisco Amato"
25 __email__ = "[email protected]"
26 __status__ = "Development"
27
28
29
30
24 __email__ = "[email protected]"
25 __status__ = "Development"
3126
3227 class DnsmapParser(object):
3328 """
34 The objective of this class is to parse an xml file generated by the dnsmap tool.
29 The objective of this class is to parse an xml file generated by the
30 dnsmap tool.
3531
3632 TODO: Handle errors.
37 TODO: Test dnsmap output version. Handle what happens if the parser doesn't support it.
33 TODO: Test dnsmap output version. Handle what happens if the parser
34 doesn't support it.
3835 TODO: Test cases.
3936
4037 @param dnsmap_filepath A proper simple report generated by dnsmap
4643
4744 for line in lists:
4845 mitem = line.split(',')
49 if mitem.__len__() > 1:
46 if len(mitem) > 1:
5047 item = {'host': mitem[0], 'ip': mitem[1]}
5148 self.items.append(item)
5249
5350
5451 class DnsmapPlugin(core.PluginBase):
55 """
56 Example plugin to parse dnsmap output.
57 """
52 """Example plugin to parse dnsmap output."""
53
5854 def __init__(self):
5955 core.PluginBase.__init__(self)
6056 self.id = "Dnsmap"
6157 self.name = "Dnsmap XML Output Plugin"
6258 self.plugin_version = "0.0.2"
6359 self.version = "0.30"
64 self._completition = {
65 "":"dnsmap &lt;target-domain&gt; [options]",
66 "-w":"-w &lt;wordlist-file&gt;",
67 "-r":"-r &lt;regular-results-file&gt;",
68 "-c":"-c &lt;csv-results-file&gt;",
69 "-d":"-d &lt;delay-millisecs&gt;",
70 "-i":"-i &lt;ips-to-ignore&gt; (useful if you're obtaining false positives)",
71 }
72
7360 self.options = None
7461 self._current_output = None
7562 self.current_path = None
76 self._command_regex = re.compile(r'^(sudo dnsmap|dnsmap|\.\/dnsmap).*?')
63 self._command_regex = re.compile(
64 r'^(sudo dnsmap|dnsmap|\.\/dnsmap).*?')
65
66 self.xml_arg_re = re.compile(r"^.*(-c\s*[^\s]+).*$")
7767
7868 global current_path
79 self._output_file_path = os.path.join(self.data_path,"%s_%s_output-%s.xml" % (self.get_ws(),
80 self.id,
81 random.uniform(1,10)))
69
70 self._output_file_path = os.path.join(
71 self.data_path,
72 "%s_%s_output-%s.xml" % (
73 self.get_ws(),
74 self.id,
75 random.uniform(1, 10)
76 )
77 )
8278
8379 def canParseCommandString(self, current_input):
8480 if self._command_regex.match(current_input.strip()):
8884
8985 def parseOutputString(self, output, debug=False):
9086 """
91 This method will discard the output the shell sends, it will read it from
92 the xml where it expects it to be present.
93
94 NOTE: if 'debug' is true then it is being run from a test case and the
95 output being sent is valid.
87 This method will discard the output the shell sends, it will read it
88 from the xml where it expects it to be present.
9689 """
9790
98 if debug:
99 parser = DnsmapParser(self._output_file_path)
100 else:
91 parser = DnsmapParser(output)
10192
102 if not os.path.exists(self._output_file_path):
103 return False
104
105 parser = DnsmapParser(self._output_file_path)
106
107 for item in parser.items:
108 h_id = self.createAndAddHost(item['ip'])
109 i_id = self.createAndAddInterface(h_id, item['ip'], ipv4_address=item['ip'],hostname_resolution=item['host'])
110
111
112 del parser
93 for item in parser.items:
94 h_id = self.createAndAddHost(item['ip'])
95 self.createAndAddInterface(
96 h_id,
97 item['ip'],
98 ipv4_address=item['ip'],
99 hostname_resolution=item['host'])
113100
114101 return True
115
116 xml_arg_re = re.compile(r"^.*(-c\s*[^\s]+).*$")
117102
118103 def processCommandString(self, username, current_path, command_string):
119104 """
120105 Adds the parameter to get output to the command string that the
121106 user has set.
122107 """
123
124108 arg_match = self.xml_arg_re.match(command_string)
125109
126110 if arg_match is None:
77 '''
88 from __future__ import with_statement
99 from plugins import core
10 from model import api
1110 import re
1211 import os
13 import pprint
1412 import sys
1513 import socket
1614
17
1815 current_path = os.path.abspath(os.getcwd())
1916
20 __author__ = "Francisco Amato"
21 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
22 __credits__ = ["Francisco Amato"]
23 __license__ = ""
24 __version__ = "1.0.0"
17 __author__ = "Francisco Amato"
18 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
19 __credits__ = ["Francisco Amato"]
20 __license__ = ""
21 __version__ = "1.0.0"
2522 __maintainer__ = "Francisco Amato"
26 __email__ = "[email protected]"
27 __status__ = "Development"
23 __email__ = "[email protected]"
24 __status__ = "Development"
2825
29
30
31
3226
3327 class DnswalkParser(object):
3428 """
35 The objective of this class is to parse an xml file generated by the dnswalk tool.
29 The objective of this class is to parse an xml file generated
30 by the dnswalk tool.
3631
3732 TODO: Handle errors.
38 TODO: Test dnswalk output version. Handle what happens if the parser doesn't support it.
33 TODO: Test dnswalk output version. Handle what happens if the parser
34 doesn't support it.
3935 TODO: Test cases.
4036
4137 @param dnswalk_filepath A proper simple report generated by dnswalk
4238 """
4339 def __init__(self, output):
44
40
4541 lists = output.split("\n")
4642 self.items = []
47
48
43
4944 for line in lists:
50 mregex = re.search("WARN: ([\w\.]+) ([\w]+) ([\w\.]+):",line)
45 mregex = re.search("WARN: ([\w\.]+) ([\w]+) ([\w\.]+):", line)
5146 if mregex is not None:
52 print "host %s, ip %s" % (mregex.group(1),mregex.group(3))
53 item = {'host' : mregex.group(1), 'ip' : mregex.group(3), 'type' : mregex.group(2)}
47
48 item = {
49 'host': mregex.group(1),
50 'ip': mregex.group(3),
51 'type': mregex.group(2)}
52
5453 self.items.append(item)
55
56
57 mregex = re.search("Getting zone transfer of ([\w\.]+) from ([\w\.]+)\.\.\.done\.",line)
54
55 mregex = re.search(
56 "Getting zone transfer of ([\w\.]+) from ([\w\.]+)\.\.\.done\.",
57 line)
58
5859 if mregex is not None:
59 ip=self.getAddress(mregex.group(2))
60 item = {'host' : mregex.group(1), 'ip' : ip, 'type' : 'info'}
60 ip = self.getAddress(mregex.group(2))
61 item = {
62 'host': mregex.group(1),
63 'ip': ip,
64 'type': 'info'}
6165 self.items.append(item)
62
66
6367 def getAddress(self, hostname):
64 """
65 Returns remote IP address from hostname.
66 """
68 """Returns remote IP address from hostname."""
6769 try:
6870 return socket.gethostbyname(hostname)
69 except socket.error, msg:
70
71 return hostname
72
73
71 except socket.error:
72 return hostname
7473
7574
7675 class DnswalkPlugin(core.PluginBase):
7978 """
8079 def __init__(self):
8180 core.PluginBase.__init__(self)
82 self.id = "Dnswalk"
83 self.name = "Dnswalk XML Output Plugin"
84 self.plugin_version = "0.0.1"
85 self.version = "2.0.2"
86
87 self.options = None
81 self.id = "Dnswalk"
82 self.name = "Dnswalk XML Output Plugin"
83 self.plugin_version = "0.0.1"
84 self.version = "2.0.2"
85 self.options = None
8886 self._current_output = None
8987 self._current_path = None
90 self._command_regex = re.compile(r'^(sudo dnswalk|dnswalk|\.\/dnswalk).*?')
91 self._completition = {
92 "":"dnswalk domain",
93 "-r":"Recursively descend subdomains of domain",
94 "-i":"Suppress check for invalid characters in a domain name.",
95 "-a":"turn on warning of duplicate A records.",
96 "-d":"Debugging",
97 "-m":"Check only if the domain has been modified. (Useful only if dnswalk has been run previously.)",
98 "-F":"Enable \"facist\" checking. (See man page)",
99 "-l":"Check lame delegations",
100 }
88 self._command_regex = re.compile(
89 r'^(sudo dnswalk|dnswalk|\.\/dnswalk).*?')
10190
10291 global current_path
103
104
10592
10693 def canParseCommandString(self, current_input):
10794 if self._command_regex.match(current_input.strip()):
10996 else:
11097 return False
11198
99 def parseOutputString(self, output, debug=False):
100 """
101 output is the shell output of command Dnswalk.
102 """
103 parser = DnswalkParser(output)
112104
113 def parseOutputString(self, output, debug = False):
114 """
115 This method will discard the output the shell sends, it will read it from
116 the xml where it expects it to be present.
105 for item in parser.items:
117106
118 NOTE: if 'debug' is true then it is being run from a test case and the
119 output being sent is valid.
120 """
121
122
123 if debug:
124 parser = DnswalkParser(output)
125 else:
126
127 parser = DnswalkParser(output)
107 if item['type'] == "A":
128108
129 print parser.items.__len__()
130 for item in parser.items:
131 if item['type'] == "A":
132109 h_id = self.createAndAddHost(item['ip'])
133 i_id = self.createAndAddInterface(h_id, item['ip'], ipv4_address=item['ip'],hostname_resolution=item['host'])
110 i_id = self.createAndAddInterface(
111 h_id,
112 item['ip'],
113 ipv4_address=item['ip'],
114 hostname_resolution=item['host'])
115
134116 elif item['type'] == "info":
117
135118 h_id = self.createAndAddHost(item['ip'])
136 i_id = self.createAndAddInterface(h_id, item['ip'], ipv4_address=item['ip'],hostname_resolution=item['host'])
137 s_id = self.createAndAddServiceToInterface(h_id, i_id, "domain", "tcp", ports=['53'])
138 self.createAndAddVulnToService(h_id, s_id, "Zone transfer", desc="A Dns server allows unrestricted zone transfers",
139 ref=["CVE-1999-0532"])
140119
141 del parser
120 i_id = self.createAndAddInterface(
121 h_id,
122 item['ip'],
123 ipv4_address=item['ip'],
124 hostname_resolution=item['host'])
125
126 s_id = self.createAndAddServiceToInterface(
127 h_id,
128 i_id,
129 "domain",
130 "tcp",
131 ports=['53'])
132
133 self.createAndAddVulnToService(
134 h_id,
135 s_id,
136 "Zone transfer",
137 desc="A Dns server allows unrestricted zone transfers",
138 ref=["CVE-1999-0532"])
139
142140 return True
143141
144142 def processCommandString(self, username, current_path, command_string):
145 """
146 """
147143 return None
144
148145
149146 def createPlugin():
150147 return DnswalkPlugin()
77 '''
88 from __future__ import with_statement
99 from plugins import core
10 from model import api
1110 import socket
1211 import re
1312 import os
14 import pprint
1513 import sys
16
1714
1815 current_path = os.path.abspath(os.getcwd())
1916
20 __author__ = "Francisco Amato"
21 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
22 __credits__ = ["Francisco Amato"]
23 __license__ = ""
24 __version__ = "1.0.0"
17 __author__ = "Francisco Amato"
18 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
19 __credits__ = ["Francisco Amato"]
20 __license__ = ""
21 __version__ = "1.0.0"
2522 __maintainer__ = "Francisco Amato"
26 __email__ = "[email protected]"
27 __status__ = "Development"
28
29
30
31
23 __email__ = "[email protected]"
24 __status__ = "Development"
3225
3326 valid_records = ["NS", "CNAME", "A"]
3427
3528 class FierceParser(object):
3629 """
37 The objective of this class is to parse an xml file generated by the fierce tool.
30 The objective of this class is to parse an shell output generated by
31 the fierce tool.
3832
3933 TODO: Handle errors.
40 TODO: Test fierce output version. Handle what happens if the parser doesn't support it.
34 TODO: Test fierce output version. Handle what happens if the parser
35 doesn't support it.
4136 TODO: Test cases.
4237
4338 @param fierce_filepath A proper simple report generated by fierce
4439 """
4540 def __init__(self, output):
46
47
41
4842 self.target = None
4943 self.items = []
50
51
52
53
54
55
56
57 r = re.search("DNS Servers for ([\w\.-]+):\r\n([^$]+)Trying zone transfer first...",output)
44
45 r = re.search(
46 "DNS Servers for ([\w\.-]+):\r\n([^$]+)Trying zone transfer first...",
47 output)
48
5849 if r is not None:
5950 self.target = r.group(1)
60 mstr = re.sub("\t","",r.group(2))
51 mstr = re.sub("\t", "", r.group(2))
6152 self.dns = mstr.split()
62
63
64
65
66 r = re.search("Now performing [\d]+ test\(s\)...\r\n([^$]+)\x0D\nSubnets found ",output)
53
54 r = re.search(
55 "Now performing [\d]+ test\(s\)...\r\n([^$]+)\x0D\nSubnets found ",
56 output)
57
6758 if r is not None:
6859 list = r.group(1).split("\r\n")
6960 for i in list:
7061 if i != "":
7162 mstr = i.split("\t")
72 item = {'host' : mstr[1], 'type' : "A", 'ip' : mstr[0]}
63 item = {'host': mstr[1], 'type': "A", 'ip': mstr[0]}
7364 self.items.append(item)
74
75
76
77
65
7866 self.isZoneVuln = False
79 r = re.search("Whoah, it worked - misconfigured DNS server found:\r\n([^$]+)There isn't much point continuing, you have everything.",output)
67 r = re.search(
68 "Whoah, it worked - misconfigured DNS server found:\r\n([^$]+)There isn't much point continuing, you have everything.",
69 output)
70
8071 if r is not None:
81
72
8273 self.isZoneVuln = True
8374 list = r.group(1).split("\n")
8475 for i in list:
76
8577 if i != "":
8678 mstr = i.split()
8779 if (mstr and mstr[0] != "" and len(mstr) > 3 and mstr[3] in valid_records):
88 item = {'host' : mstr[0], 'type' : mstr[3], 'ip' : mstr[4]}
89
80 item = {'host': mstr[0], 'type': mstr[3], 'ip': mstr[4]}
9081 self.items.append(item)
82
9183
9284 class FiercePlugin(core.PluginBase):
9385 """
9587 """
9688 def __init__(self):
9789 core.PluginBase.__init__(self)
98 self.id = "Fierce"
99 self.name = "Fierce Output Plugin"
100 self.plugin_version = "0.0.1"
101 self.version = "0.9.9"
102
103 self.options = None
90 self.id = "Fierce"
91 self.name = "Fierce Output Plugin"
92 self.plugin_version = "0.0.1"
93 self.version = "0.9.9"
94 self.options = None
10495 self._current_output = None
10596 self._current_path = None
106 self._command_regex = re.compile(r'^(sudo fierce|fierce|sudo fierce\.pl|fierce\.pl|perl fierce\.pl|\.\/fierce\.pl).*?')
107 self._completition = {
108 "":"perl fierce.pl [-dns example.com] [OPTIONS]",
109 "-connect":"Attempt to make http connections to any non RFC1918 lot of free time on your hands (could take hours-days). ",
110 "-delay":"The number of seconds to wait between lookups.",
111 "-dns":"The domain you would like scanned.",
112 "-dnsfile":"Use DNS servers provided by a file (one per line) for",
113 "-dnsserver":"Use a particular DNS server for reverse lookups ",
114 "-file":"A file you would like to output to be logged to.",
115 "-fulloutput":"When combined with -connect this will output everything",
116 "-help":"This screen.",
117 "-nopattern":"Don't use a search pattern when looking for nearby",
118 "-range":"Scan an internal IP range (must be combined with dnsserver). Note, that this does not support a pattern. perl fierce.pl -range 111.222.333.0-255 -dnsserver ns1.example.co",
119 "-search":" -search Search list. When fierce attempts to traverse up and. perl fierce.pl -dns examplecompany.com -search corpcompany,blahcompany",
120 "-suppress":"Suppress all TTY output (when combined with -file).",
121 "-tcptimeout":"Specify a different timeout (default 10 seconds). You",
122 "-threads":"Specify how many threads to use while scanning (default",
123 "-traverse":"Specify a number of IPs above and below whatever IP you",
124 "-version":"Output the version number.",
125 "-wide":"Scan the entire class C after finding any matching",
126 "-wordlist":"Use a seperate wordlist (one word per line). Usage:",
127 }
128
97 self._command_regex = re.compile(
98 r'^(sudo fierce|fierce|sudo fierce\.pl|fierce\.pl|perl fierce\.pl|\.\/fierce\.pl).*?')
12999 global current_path
130
131
132100
133101 def canParseCommandString(self, current_input):
134102 if self._command_regex.match(current_input.strip()):
155123 pass
156124 return item
157125
158 def parseOutputString(self, output, debug = False):
159 """
160 This method will discard the output the shell sends, it will read it from
161 the xml where it expects it to be present.
126 def parseOutputString(self, output, debug=False):
162127
163 NOTE: if 'debug' is true then it is being run from a test case and the
164 output being sent is valid.
165 """
166
167128 parser = FierceParser(output)
168129 for item in parser.items:
130
169131 item['isResolver'] = False
170132 item['isZoneVuln'] = False
171133 if (item['type'] == "CNAME"):
175137 item['isResolver'] = True
176138 item['isZoneVuln'] = parser.isZoneVuln
177139 for item2 in parser.items:
140
178141 if item['ip'] == item2['ip'] and item != item2:
179142 item2['isResolver'] = item['isResolver']
180143 item2['isZoneVuln'] = item['isZoneVuln']
181
182144 item['ip'] = ''
183145
184
185
186
187
188
146 for item in parser.items:
189147
190
191
192
193
194 for item in parser.items:
195148 if item['ip'] == "127.0.0.1" or item['ip'] == '':
196149 continue
197150 h_id = self.createAndAddHost(item['ip'])
198 i_id = self.createAndAddInterface(h_id, item['ip'], ipv4_address= item['ip'], hostname_resolution = [item['host']])
151 i_id = self.createAndAddInterface(
152 h_id,
153 item['ip'],
154 ipv4_address=item['ip'],
155 hostname_resolution=[item['host']])
156
199157 if item['isResolver']:
200 s_id = self.createAndAddServiceToInterface(h_id, i_id, "domain", "tcp", ports=['53'])
158 s_id = self.createAndAddServiceToInterface(
159 h_id,
160 i_id,
161 "domain",
162 "tcp",
163 ports=['53'])
164
201165 if item['isZoneVuln']:
202 self.createAndAddVulnToService(h_id, s_id, "Zone transfer", desc="A Dns server allows unrestricted zone transfers",
203 ref=["CVE-1999-0532"])
204 del parser
166 self.createAndAddVulnToService(
167 h_id,
168 s_id,
169 "Zone transfer",
170 desc="A Dns server allows unrestricted zone transfers",
171 ref=["CVE-1999-0532"])
205172
206173 def processCommandString(self, username, current_path, command_string):
207 """
208 """
209174 return None
175
210176
211177 def createPlugin():
212178 return FiercePlugin()
44 Faraday Penetration Test IDE
55 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
66 See the file 'doc/LICENSE' for the license information
7 '''
78
8 '''
99 from __future__ import with_statement
1010 from plugins import core
11 from model import api
11 import socket
12 import sys
1213 import re
13 import os, socket
14 import pprint
15 import sys
16
14 import os
1715
1816 current_path = os.path.abspath(os.getcwd())
1917
20 __author__ = "Francisco Amato"
21 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
22 __credits__ = ["Francisco Amato"]
23 __license__ = ""
24 __version__ = "1.0.0"
18 __author__ = "Francisco Amato"
19 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
20 __credits__ = ["Francisco Amato"]
21 __license__ = ""
22 __version__ = "1.0.0"
2523 __maintainer__ = "Francisco Amato"
26 __email__ = "[email protected]"
27 __status__ = "Development"
28
29
30
31
24 __email__ = "[email protected]"
25 __status__ = "Development"
3226
3327 class GoohostParser(object):
3428 """
4438 def __init__(self, goohost_filepath, goohost_scantype):
4539 self.filepath = goohost_filepath
4640 self.scantype = goohost_scantype
47
48
49
50 with open(self.filepath,"r") as f:
51
52 print "estoy por leer el archivo (%s)" % self.filepath
41
42 with open(self.filepath, "r") as f:
43
5344 line = f.readline()
5445 self.items = []
55 print "afuera del while %s" % line
5646 while line:
57 print "estoy adentro del while %s" % line
5847 if self.scantype == 'ip':
5948 minfo = line.split()
60 item = {'host' : minfo[0], 'ip' : minfo[1]}
61 print "El item ip (%s) (%s)" % (minfo[0],minfo[1])
49 item = {'host': minfo[0], 'ip': minfo[1]}
6250 elif self.scantype == 'host':
6351 line = line.strip()
64 item = {'host' : line, 'ip' : self.resolve(line)}
65 print "El item ip (%s) (%s)" % (item['host'],item['ip'])
52 item = {'host': line, 'ip': self.resolve(line)}
6653 else:
67 item = {'data' : line}
68 print "el item host email %s" % line
69
54 item = {'data': line}
55
7056 self.items.append(item)
7157 line = f.readline()
7258
8470 """
8571 def __init__(self):
8672 core.PluginBase.__init__(self)
87 self.id = "Goohost"
88 self.name = "Goohost XML Output Plugin"
89 self.plugin_version = "0.0.1"
90 self.version = "v.0.0.1"
91 self.options = None
73 self.id = "Goohost"
74 self.name = "Goohost XML Output Plugin"
75 self.plugin_version = "0.0.1"
76 self.version = "v.0.0.1"
77 self.options = None
9278 self._current_output = None
9379 self._current_path = None
94 self._command_regex = re.compile(r'^(sudo goohost\.sh|goohost\.sh|sh goohost\.sh|\.\/goohost\.sh).*?')
80 self._command_regex = re.compile(
81 r'^(sudo goohost\.sh|goohost\.sh|sh goohost\.sh|\.\/goohost\.sh).*?')
9582 self.scantype = "host"
9683 self.host = None
97 self._completition = {
98 "":"./goohost.sh -t domain.tld [-m <host|ip|mail> -p <1-20> -v] ",
99 "-t":"target domain. Ex: backtrack.linux.org ",
100 "-m":"method: <ip|host|mail>. Default value is set to host ",
101 "-p":"pages [1-20]. Max number of pages to download from Google. Default 5 ",
102 "-v":"verbosity. Default is set to off ",
103 }
10484
10585 global current_path
10686 self.output_path = None
107
108
10987
110
111 def parseOutputString(self, output, debug = False):
88 def parseOutputString(self, output, debug=False):
11289 """
11390 This method will discard the output the shell sends, it will read it from
11491 the xml where it expects it to be present.
11693 NOTE: if 'debug' is true then it is being run from a test case and the
11794 output being sent is valid.
11895 """
119
120 print "este es el output (%s)" % output
121
96
12297 if self.output_path is None:
123 mypath = re.search("Results saved in file (\S+)",output);
124 print "este es el output %s" % output
98 mypath = re.search("Results saved in file (\S+)", output)
12599 if mypath is not None:
126 print "encontre el archivo '%s' '%s'" % (mypath.group(1), self.scantype)
127
128100 self.output_path = self._current_path + "/" + mypath.group(1)
129
130
131101 else:
132102 return False
133
103
134104 if debug:
135 parser = GoohostParser(output,self.scantype)
105 parser = GoohostParser(output, self.scantype)
136106 else:
137107 if not os.path.exists(self.output_path):
138 print "el archivo no existe '%s'" % self.output_path
139108 return False
140
141 print "estoy a punto de entrar a goohost"
142 parser = GoohostParser(self.output_path,self.scantype)
143109
110 parser = GoohostParser(self.output_path, self.scantype)
144111
145
146
147
148
149 if self.scantype == 'host' or self.scantype == 'ip' :
150
151
112 if self.scantype == 'host' or self.scantype == 'ip':
152113 for item in parser.items:
153114 h_id = self.createAndAddHost(item['ip'])
154 i_id = self.createAndAddInterface(h_id, item['ip'],ipv4_address=item['ip'],hostname_resolution=item['host'])
115 i_id = self.createAndAddInterface(
116 h_id,
117 item['ip'],
118 ipv4_address=item['ip'],
119 hostname_resolution=item['host'])
155120
156121 del parser
157
158
159
160
161122
162123 def processCommandString(self, username, current_path, command_string):
163124 """
164 Adds the -oX parameter to get xml output to the command string that the
165 user has set.
125 Set output path for parser...
166126 """
167
168
169
170
171
172
173
174
175
176
127 self._current_path = current_path
177128
178129 def setHost(self):
179130 pass
2424 __email__ = "[email protected]"
2525 __status__ = "Development"
2626
27
28
29
27
28
29
3030
3131 class ListurlsParser(object):
3232 """
4545 if re.search("Could not reach",output) is not None:
4646 self.fail = True
4747 return
48
48
4949 for line in lists:
5050 if i > 8:
5151 print line
7878 }
7979
8080 global current_path
81 self.output_path = os.path.join(self.data_path,
81 self.output_file_path = os.path.join(self.data_path,
8282 "listurls_output-%s.txt" % self._rid)
83
84
83
84
8585
8686 def canParseCommandString(self, current_input):
8787 if self._command_regex.match(current_input.strip()):
9898 NOTE: if 'debug' is true then it is being run from a test case and the
9999 output being sent is valid.
100100 """
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141101
142
143
144
145102 def processCommandString(self, username, current_path, command_string):
146
103
147104 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;%\$#\=~_\-]+)).*?$", command_string)
148
105
149106 self.protocol = host.group(1)
150107 self.host = host.group(4)
151108 if self.protocol == 'https':
153110 if host.group(11) is not None:
154111 self.port = host.group(11)
155112 return None
156
113
157114 def setHost(self):
158115 pass
159116
88
99 from __future__ import with_statement
1010 from plugins import core
11 from model import api
1211
1312 import zipfile
1413 import sys
2726
2827 current_path = os.path.abspath(os.getcwd())
2928
30 __author__ = "Ezequiel Tavella"
31 __copyright__ = "Copyright (c) 2015, Infobyte LLC"
32 __credits__ = ["Ezequiel Tavella"]
33 __license__ = ""
34 __version__ = "1.0.1"
29 __author__ = "Ezequiel Tavella"
30 __copyright__ = "Copyright (c) 2015, Infobyte LLC"
31 __credits__ = ["Ezequiel Tavella"]
32 __license__ = ""
33 __version__ = "1.0.1"
3534 __maintainer__ = "Ezequiel Tavella"
36 __status__ = "Development"
35 __status__ = "Development"
36
3737
3838 def openMtgx(mtgx_file):
3939
4747
4848 file.close()
4949 return xml
50
5051
5152 class Host():
5253
6061 self.mx_record = ""
6162 self.ns_record = ""
6263
64
6365 class MaltegoMtgxParser():
6466
6567 def __init__(self, xml_file):
6769 self.xml = openMtgx(xml_file)
6870
6971 self.nodes = self.xml.findall(
70 "{http://graphml.graphdrawing.org/xmlns}graph/"
71 "{http://graphml.graphdrawing.org/xmlns}node"
72 "{http://graphml.graphdrawing.org/xmlns}graph/"
73 "{http://graphml.graphdrawing.org/xmlns}node"
7274 )
7375
7476 self.edges = self.xml.findall(
75 "{http://graphml.graphdrawing.org/xmlns}graph/"
76 "{http://graphml.graphdrawing.org/xmlns}edge"
77 "{http://graphml.graphdrawing.org/xmlns}graph/"
78 "{http://graphml.graphdrawing.org/xmlns}edge"
7779 )
7880
7981 self.list_hosts = []
9294 target = edge.get("target")
9395
9496 if source not in self.relations:
95 self.relations.update({source : [target]})
97 self.relations.update({source: [target]})
9698
9799 if target not in self.relations:
98 self.relations.update({target : [source]})
100 self.relations.update({target: [source]})
99101
100102 values = self.relations[source]
101103 values.append(target)
102 self.relations.update({source : values})
104 self.relations.update({source: values})
103105
104106 values = self.relations[target]
105107 values.append(source)
106 self.relations.update({target : values})
108 self.relations.update({target: values})
107109
108110 def getIpAndId(self, node):
109111
110 #Find node ID and maltego entity
112 # Find node ID and maltego entity
111113 node_id = node.get("id")
112114 entity = node.find(
113 "{http://graphml.graphdrawing.org/xmlns}data/"
114 "{http://maltego.paterva.com/xml/mtgx}MaltegoEntity"
115 )
116
117 #Check if is IPv4Address
115 "{http://graphml.graphdrawing.org/xmlns}data/"
116 "{http://maltego.paterva.com/xml/mtgx}MaltegoEntity"
117 )
118
119 # Check if is IPv4Address
118120 if entity.get("type") != "maltego.IPv4Address":
119121 return None
120122
121 #Get IP value
123 # Get IP value
122124 value = entity.find(
123 "{http://maltego.paterva.com/xml/mtgx}Properties/"
124 "{http://maltego.paterva.com/xml/mtgx}Property/"
125 "{http://maltego.paterva.com/xml/mtgx}Value"
126 )
127
128 return {"node_id" : node_id ,"ip" : value.text}
125 "{http://maltego.paterva.com/xml/mtgx}Properties/"
126 "{http://maltego.paterva.com/xml/mtgx}Property/"
127 "{http://maltego.paterva.com/xml/mtgx}Value"
128 )
129
130 return {"node_id": node_id, "ip": value.text}
129131
130132 def getNode(self, node_id):
131133
132 #Get node, filter by id
134 # Get node, filter by id
133135 for node in self.nodes:
134136
135137 if node.get("id") == node_id:
137139
138140 def getType(self, node):
139141
140 #Get type of this node
142 # Get type of this node
141143 entity = node.find(
142 "{http://graphml.graphdrawing.org/xmlns}data/"
143 "{http://maltego.paterva.com/xml/mtgx}MaltegoEntity"
144 "{http://graphml.graphdrawing.org/xmlns}data/"
145 "{http://maltego.paterva.com/xml/mtgx}MaltegoEntity"
144146 )
145147
146148 return entity.get("type")
147149
148150 def getWebsite(self, target_node):
149151
150 #Parse Website Entity
151 result = {"name" : "", "ssl_enabled": "" , "urls": ""}
152 # Parse Website Entity
153 result = {"name": "", "ssl_enabled": "", "urls": ""}
152154
153155 props = target_node.find(
154 "{http://graphml.graphdrawing.org/xmlns}data/"
155 "{http://maltego.paterva.com/xml/mtgx}MaltegoEntity/"
156 "{http://maltego.paterva.com/xml/mtgx}Properties"
156 "{http://graphml.graphdrawing.org/xmlns}data/"
157 "{http://maltego.paterva.com/xml/mtgx}MaltegoEntity/"
158 "{http://maltego.paterva.com/xml/mtgx}Properties"
157159 )
158160
159161 for prop in props:
160162
161163 name_property = prop.get("name")
162 value = prop.find("{http://maltego.paterva.com/xml/mtgx}Value").text
164 value = prop.find(
165 "{http://maltego.paterva.com/xml/mtgx}Value").text
163166
164167 if name_property == "fqdn":
165168 result["name"] = value
172175
173176 def getNetBlock(self, target_node):
174177
175 #Parse Netblock Entity
176 result = {"ipv4_range" : "", "network_owner": "" , "country": ""}
178 # Parse Netblock Entity
179 result = {"ipv4_range": "", "network_owner": "", "country": ""}
177180
178181 props = target_node.find(
179 "{http://graphml.graphdrawing.org/xmlns}data/"
180 "{http://maltego.paterva.com/xml/mtgx}MaltegoEntity/"
181 "{http://maltego.paterva.com/xml/mtgx}Properties"
182 "{http://graphml.graphdrawing.org/xmlns}data/"
183 "{http://maltego.paterva.com/xml/mtgx}MaltegoEntity/"
184 "{http://maltego.paterva.com/xml/mtgx}Properties"
182185 )
183186
184187 for prop in props:
185188
186189 name_property = prop.get("name")
187 value = prop.find("{http://maltego.paterva.com/xml/mtgx}Value").text
190 value = prop.find(
191 "{http://maltego.paterva.com/xml/mtgx}Value").text
188192
189193 if name_property == "ipv4-range":
190194 result["ipv4_range"] = value
197201
198202 def getLocation(self, target_node):
199203
200 #Parse Location Entity
201 result = {"name" : "", "area": "" , "country_code": "",
202 "longitude" : "", "latitude" : "", "area_2" : ""
203 }
204
205 #Get relations with other nodes
204 # Parse Location Entity
205 result = {
206 "name": "",
207 "area": "",
208 "country_code": "",
209 "longitude": "",
210 "latitude": "",
211 "area_2": ""}
212
213 # Get relations with other nodes
206214 node_relations = self.relations[target_node.get("id")]
207215
208 #Find location node based in relation with netblock node.
216 # Find location node based in relation with netblock node.
209217 located = False
210218 for node_id in node_relations:
211219
217225 if not located:
218226 return None
219227
220 #Get properties and update data
228 # Get properties and update data
221229 props = target_node.find(
222 "{http://graphml.graphdrawing.org/xmlns}data/"
223 "{http://maltego.paterva.com/xml/mtgx}MaltegoEntity/"
224 "{http://maltego.paterva.com/xml/mtgx}Properties"
230 "{http://graphml.graphdrawing.org/xmlns}data/"
231 "{http://maltego.paterva.com/xml/mtgx}MaltegoEntity/"
232 "{http://maltego.paterva.com/xml/mtgx}Properties"
225233 )
226234
227235 for prop in props:
228236
229237 name_property = prop.get("name")
230 value = prop.find("{http://maltego.paterva.com/xml/mtgx}Value").text
238 value = prop.find(
239 "{http://maltego.paterva.com/xml/mtgx}Value").text
231240
232241 if name_property == "location.name":
233242 result["name"] = value
246255
247256 def getValue(self, target_node):
248257
249 #Parse Entity
250 result = {"value" : ""}
258 # Parse Entity
259 result = {"value": ""}
251260
252261 value = target_node.find(
253 "{http://graphml.graphdrawing.org/xmlns}data/"
254 "{http://maltego.paterva.com/xml/mtgx}MaltegoEntity/"
255 "{http://maltego.paterva.com/xml/mtgx}Properties/"
256 "{http://maltego.paterva.com/xml/mtgx}Property/"
257 "{http://maltego.paterva.com/xml/mtgx}Value"
262 "{http://graphml.graphdrawing.org/xmlns}data/"
263 "{http://maltego.paterva.com/xml/mtgx}MaltegoEntity/"
264 "{http://maltego.paterva.com/xml/mtgx}Properties/"
265 "{http://maltego.paterva.com/xml/mtgx}Property/"
266 "{http://maltego.paterva.com/xml/mtgx}Value"
258267 )
259268
260269 result["value"] = value.text
266275
267276 for node in self.nodes:
268277
269 #Get IP Address if not continue with other node...
278 # Get IP Address if not continue with other node...
270279 result = self.getIpAndId(node)
271280 if not result:
272281 continue
273282
274 #Create host with values by default
283 # Create host with values by default
275284 host = Host()
276285 host.ip = result["ip"]
277286 host.node_id = result["node_id"]
278287
279 #Get relations with other nodes
288 # Get relations with other nodes
280289 node_relations = self.relations[host.node_id]
281290
282291 for node_id in node_relations:
283292
284 #Get target node and type of node.
293 # Get target node and type of node.
285294 target_node = self.getNode(node_id)
286295 target_type = self.getType(target_node)
287296
288 #Check type of node y add data to host...
297 # Check type of node y add data to host...
289298 if target_type == "maltego.DNSName":
290299 host.dns_name = self.getValue(target_node)
291300 elif target_type == "maltego.Website":
292301 host.website = self.getWebsite(target_node)
293302 elif target_type == "maltego.Netblock":
294303 host.netblock = self.getNetBlock(target_node)
295 #Get location based in relation: netblock -> location
304 # Get location based in relation: netblock -> location
296305 host.location = self.getLocation(target_node)
297306 elif target_type == "maltego.MXRecord":
298307 host.mx_record = self.getValue(target_node)
303312
304313 return self.list_hosts
305314
315
306316 class MaltegoPlugin(core.PluginBase):
307
308 def __init__(self):
309
310 core.PluginBase.__init__(self)
311 self.id = "Maltego"
312 self.name = "Maltego MTGX Output Plugin"
313 self.plugin_version = "1.0.1"
314 self.version = "Maltego 3.6"
315 self.framework_version = "1.0.0"
316 self.current_path = None
317 self.options = None
318 self._current_output = None
319
320 self._command_regex = re.compile(r'^(sudo maltego_faraday|maltego_faraday|\.\/maltego_faraday).*?')
321
322 self.report = None
323 global current_path
324
325 def parseOutputString(self, output, debug = False):
326
327 maltego_parser = MaltegoMtgxParser(self.report)
328 for host in maltego_parser.parse():
329
330 #Create host
331 try:
332 old_hostname = host.dns_name["value"]
333 except:
334 old_hostname = "unknown"
335
336 host_id = self.createAndAddHost(
337 name = host.ip,
338 old_hostname = old_hostname
339 )
340
341 #Create interface
342 try:
343 network_segment = host.netblock["ipv4_range"]
344 hostname_resolution = [host.dns_name["value"]]
345 except:
346 network_segment = "unknown"
347 hostname_resolution = "unknown"
348
349 interface_id = self.createAndAddInterface(
350 host_id = host_id,
351 name = host.ip,
352 ipv4_address = host.ip,
353 network_segment = network_segment ,
354 hostname_resolution = hostname_resolution
355 )
356
357 #Create note with NetBlock information
358 if host.netblock:
359
360 try:
361
362 text = (
363 "Network owner:\n" +
364 host.netblock["network_owner"] or "unknown" +
365 "Country:\n" + host.netblock["country"] or "unknown"
366 )
367 except:
368 text = "unknown"
369
370 self.createAndAddNoteToHost(
371 host_id = host_id,
372 name = "Netblock Information",
373 text = text.encode('ascii', 'ignore')
374 )
375
376 #Create note with host location
377 if host.location:
378
379 try:
380 text = (
381 "Location:\n" +
382 host.location["name"] +
383 "\nArea:\n" +
384 host.location["area"] +
385 "\nArea 2:\n" +
386 host.location["area_2"] +
387 "\nCountry_code:\n" +
388 host.location["country_code"] +
389 "\nLatitude:\n" +
390 host.location["latitude"] +
391 "\nLongitude:\n" +
392 host.location["longitude"]
393 )
394 except:
395 text = "unknown"
396
397 self.createAndAddNoteToHost(
398 host_id = host_id,
399 name = "Location Information",
400 text = text.encode('ascii', 'ignore')
401 )
402
403 #Create service web server
404 if host.website:
405
406 try:
407 description = "SSL Enabled: " + host.website["ssl_enabled"]
408 except:
409 description = "unknown"
410
411 service_id = self.createAndAddServiceToInterface(
412 host_id = host_id,
413 interface_id = interface_id,
414 name = host.website["name"],
415 protocol = "TCP:HTTP",
416 ports = [80],
417 description = description
418 )
419
420 try:
421
422 text =(
423 "Urls:\n" + host.website["urls"]
424 )
425
426 self.createAndAddNoteToService(
427 host_id = host_id,
428 service_id = service_id,
429 name = "URLs",
430 text = text.encode('ascii', 'ignore')
431 )
432
433 except:
434 pass
435
436 if host.mx_record:
437
438 self.createAndAddServiceToInterface(
439 host_id = host_id,
440 interface_id = interface_id,
441 name = host.mx_record["value"],
442 protocol = "SMTP",
443 ports = [25],
444 description = "E-mail Server",
445 )
446
447 if host.ns_record:
448
449 self.createAndAddServiceToInterface(
450 host_id = host_id,
451 interface_id = interface_id,
452 name = host.ns_record["value"],
453 protocol = "DNS",
454 ports = [53],
455 description = "DNS Server",
456 )
457
458 def processCommandString(self, username, current_path, command_string):
459
460 report = command_string.split("maltego_faraday ")[1]
461 self.report = os.path.join(current_path, report)
317 def __init__(self):
318 core.PluginBase.__init__(self)
319 self.id = "Maltego"
320 self.name = "Maltego MTGX Output Plugin"
321 self.plugin_version = "1.0.1"
322 self.version = "Maltego 3.6"
323 self.framework_version = "1.0.0"
324 self.current_path = None
325 self.options = None
326 self._current_output = None
327 self._command_regex = re.compile(
328 r'^(sudo maltego|maltego|\.\/maltego).*?')
329 global current_path
330
331 def parseOutputString(self, filename, debug=False):
332
333 maltego_parser = MaltegoMtgxParser(filename)
334 for host in maltego_parser.parse():
335 # Create host
336 try:
337 old_hostname = host.dns_name["value"]
338 except:
339 old_hostname = "unknown"
340
341 host_id = self.createAndAddHost(
342 name=host.ip,
343 old_hostname=old_hostname)
344
345 # Create interface
346 try:
347 network_segment = host.netblock["ipv4_range"]
348 hostname_resolution = [host.dns_name["value"]]
349 except:
350 network_segment = "unknown"
351 hostname_resolution = "unknown"
352
353 interface_id = self.createAndAddInterface(
354 host_id=host_id,
355 name=host.ip,
356 ipv4_address=host.ip,
357 network_segment=network_segment,
358 hostname_resolution=hostname_resolution)
359
360 # Create note with NetBlock information
361 if host.netblock:
362 try:
363 text = (
364 "Network owner:\n" +
365 host.netblock["network_owner"] or "unknown" +
366 "Country:\n" + host.netblock["country"] or "unknown")
367 except:
368 text = "unknown"
369
370 self.createAndAddNoteToHost(
371 host_id=host_id,
372 name="Netblock Information",
373 text=text.encode('ascii', 'ignore')
374 )
375
376 # Create note with host location
377 if host.location:
378 try:
379 text = (
380 "Location:\n" +
381 host.location["name"] +
382 "\nArea:\n" +
383 host.location["area"] +
384 "\nArea 2:\n" +
385 host.location["area_2"] +
386 "\nCountry_code:\n" +
387 host.location["country_code"] +
388 "\nLatitude:\n" +
389 host.location["latitude"] +
390 "\nLongitude:\n" +
391 host.location["longitude"])
392 except:
393 text = "unknown"
394
395 self.createAndAddNoteToHost(
396 host_id=host_id,
397 name="Location Information",
398 text=text.encode('ascii', 'ignore'))
399
400 # Create service web server
401 if host.website:
402 try:
403 description = "SSL Enabled: " + host.website["ssl_enabled"]
404 except:
405 description = "unknown"
406
407 service_id = self.createAndAddServiceToInterface(
408 host_id=host_id,
409 interface_id=interface_id,
410 name=host.website["name"],
411 protocol="TCP:HTTP",
412 ports=[80],
413 description=description
414 )
415
416 try:
417 text = "Urls:\n" + host.website["urls"]
418
419 self.createAndAddNoteToService(
420 host_id=host_id,
421 service_id=service_id,
422 name="URLs",
423 text=text.encode('ascii', 'ignore'))
424 except:
425 pass
426
427 if host.mx_record:
428
429 self.createAndAddServiceToInterface(
430 host_id=host_id,
431 interface_id=interface_id,
432 name=host.mx_record["value"],
433 protocol="SMTP",
434 ports=[25],
435 description="E-mail Server")
436
437 if host.ns_record:
438
439 self.createAndAddServiceToInterface(
440 host_id=host_id,
441 interface_id=interface_id,
442 name=host.ns_record["value"],
443 protocol="DNS",
444 ports=[53],
445 description="DNS Server")
446
447 def processReport(self, filepath):
448 self.parseOutputString(filepath)
449
450 def processCommandString(self, username, current_path, command_string):
451 pass
452
462453
463454 def createPlugin():
464455 return MaltegoPlugin()
2626 __email__ = "[email protected]"
2727 __status__ = "Development"
2828
29
30
31
29
30
31
3232
3333 class MetagoofilParser(object):
3434 """
4141 @param metagoofil_filepath A proper simple report generated by metagoofil
4242 """
4343 def __init__(self, output):
44
45
44
45
4646 self.items = []
47
47
4848 mfile = open("/root/dev/faraday/trunk/src/del", "r")
4949 output = mfile.read()
5050 mfile.close()
51 print "adentro del init (%s)" % output
52
53
54
55
56
57
58
59
60
61
6251
63
64
65
52
53
54
55
56
57
58
59
60
61
62
63
64
6665 mregex = re.search("\[\+\] List of paths and servers found:[-\s]+([^$]+)\[\+\] List of e-mails found:", output,re.M)
6766 if mregex is None:
68 print "mregex no andubo"
6967 return
70
71
72 print "email (%s) " % (mregex.group(1))
68
69
7370 self.users = mregex.group(1).split("\n")
7471 self.software =mregex.group(2).split("\n")
7572 self.servers=mregex.group(1).strip().split("\n")
76
73
7774 for line in self.servers:
7875 line = line.strip()
7976 item = {'host' : line, 'ip' : self.resolve(line)}
80 print item
8177 self.items.append(item)
8278
8379 def resolve(self, host):
113109 }
114110
115111 global current_path
116
117
112
113
118114
119115 def canParseCommandString(self, current_input):
120116 if self._command_regex.match(current_input.strip()):
131127 NOTE: if 'debug' is true then it is being run from a test case and the
132128 output being sent is valid.
133129 """
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
130
156131 def processCommandString(self, username, current_path, command_string):
157132 """
158133 """
77
88 from __future__ import with_statement
99 from plugins import core
10 from model import api
1110 import re
1211 import os
13 import pprint
1412 import sys
1513 import random
1614 import HTMLParser
7674 """
7775 @return items A list of Host instances
7876 """
79 for host_node in tree.find('niktoscan').findall('scandetails'):
80 yield Host(host_node)
77 if tree.find('niktoscan'):
78 for host_node in tree.find('niktoscan').findall('scandetails'):
79 yield Host(host_node)
80 else:
81 for host_node in tree.findall('scandetails'):
82 yield Host(host_node)
8183
8284
8385 def get_attrib_from_subnode(xml_node, subnode_xpath_expr, attrib_name):
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 OpenvasXmlParser(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 node = tree.findall('report')[0]
9090 node2 = node.findall('results')[0]
91
91
9292 for node in node2.findall('result'):
9393 yield Item(node)
94
95
96
97
94
95
96
97
9898 def get_attrib_from_subnode(xml_node, subnode_xpath_expr, attrib_name):
9999 """
100100 Finds a subnode in the item node and the retrieves a value from it
103103 """
104104 global ETREE_VERSION
105105 node = None
106
106
107107 if ETREE_VERSION[0] <= 1 and ETREE_VERSION[1] < 3:
108
108
109109 match_obj = re.search("([^\@]+?)\[\@([^=]*?)=\'([^\']*?)\'",subnode_xpath_expr)
110110 if match_obj is not None:
111111 node_to_find = match_obj.group(1)
127127 return None
128128
129129
130
130
131131
132132
133133 class Item(object):
153153 self.service=""
154154 self.protocol=""
155155 port = self.get_text_from_subnode('port')
156
156
157157 if (re.search("^general",port) is None):
158158 mregex = re.search("([\w]+) \(([\d]+)\/([\w]+)\)",port)
159159 if mregex is not None:
163163 else:
164164 info = port.split("/")
165165 self.port = info[0]
166 self.protocol = info[1]
166 self.protocol = info[1]
167167 else:
168168 info = port.split("/")
169169 self.service = info[0]
170170 self.protocol = info[1]
171
172
171
172
173173 self.nvt = self.node.findall('nvt')[0]
174 self.node = self.nvt
174 self.node = self.nvt
175175 self.id=self.node.get('oid')
176176 self.name = self.get_text_from_subnode('name')
177177 self.cve = self.get_text_from_subnode('cve') if self.get_text_from_subnode('cve') != "NOCVE" else ""
178178 self.bid = self.get_text_from_subnode('bid') if self.get_text_from_subnode('bid') != "NOBID" else ""
179179 self.xref = self.get_text_from_subnode('xref') if self.get_text_from_subnode('xref') != "NOXREF" else ""
180
180
181181 def do_clean(self,value):
182182 myreturn =""
183183 if value is not None:
184184 myreturn = re.sub("\n","",value)
185185 return myreturn
186
186
187187 def get_text_from_subnode(self, subnode_xpath_expr):
188188 """
189189 Finds a subnode in the host node and the retrieves a value from it.
217217 global current_path
218218 self._output_file_path = os.path.join(self.data_path,
219219 "openvas_output-%s.xml" % self._rid)
220
220
221221
222222 def parseOutputString(self, output, debug = False):
223223 """
226226
227227 NOTE: if 'debug' is true then it is being run from a test case and the
228228 output being sent is valid.
229 """
229 """
230230
231231 parser = OpenvasXmlParser(output)
232232
241241 ref.append(item.bid.encode("utf-8"))
242242 if item.xref:
243243 ref.append(item.xref.encode("utf-8"))
244
244
245245 if ids.has_key(item.subnet):
246246 h_id=ids[item.host]
247247 else:
248248 h_id = self.createAndAddHost(item.subnet)
249249 ids[item.subnet] = h_id
250
250
251251 if item.port == "None":
252252 v_id = self.createAndAddVulnToHost(h_id,item.name.encode("utf-8"),desc=item.description.encode("utf-8"),
253 severity=item.severity.encode("utf-8"),
253254 ref=ref)
254255 else:
255
256
256257 if item.service:
257258 web=True if re.search(r'^(www|http)',item.service) else False
258259 else:
259260 web=True if item.port in ('80','443','8080') else False
260
261
261262 if ids.has_key(item.subnet+"_"+item.subnet):
262263 i_id=ids[item.subnet+"_"+item.subnet]
263264 else:
264265
265
266
266267 if self._isIPV4(item.subnet):
267268 i_id = self.createAndAddInterface(h_id, item.subnet, ipv4_address=item.subnet,hostname_resolution=item.host)
268269 else:
269270 i_id = self.createAndAddInterface(h_id, item.subnet, ipv6_address=item.subnet,hostname_resolution=item.host)
270
271
271272 ids[item.subnet+"_"+item.subnet] = i_id
272
273
273
274
274275 if ids.has_key(item.subnet+"_"+item.port):
275276 s_id=ids[item.subnet+"_"+item.port]
276277 else:
277278 s_id = self.createAndAddServiceToInterface(h_id, i_id, item.service,
278 item.protocol,
279 item.protocol,
279280 ports = [str(item.port)],
280281 status = "open")
281282 ids[item.subnet+"_"+item.port] = s_id
283284 n_id = self.createAndAddNoteToService(h_id,s_id,"website","")
284285 n2_id = self.createAndAddNoteToNote(h_id,s_id,n_id,item.host,"")
285286
286 if item.name:
287 if item.name:
287288 if web:
288289 v_id = self.createAndAddVulnWebToService(h_id, s_id, item.name.encode("utf-8"),
289290 desc=item.description.encode("utf-8"),website=item.host,
293294 desc=item.description.encode("utf-8"),severity=item.severity.encode("utf-8"),ref=ref)
294295
295296 del parser
296
297
298
299
297
298
299
300
300301
301302 def _isIPV4(self, ip):
302303 if len(ip.split(".")) == 4:
304305 else:
305306 return False
306307
307
308
308309 def processCommandString(self, username, current_path, command_string):
309310 return None
310
311
311312
312313 def setHost(self):
313314 pass
88 '''
99 from __future__ import with_statement
1010 from plugins import core
11 from model import api
1211 import re
1312 import os
14 import pprint
1513 import sys
1614 import json
1715 import socket
1816 import random
1917
20
21
2218 current_path = os.path.abspath(os.getcwd())
2319
24 __author__ = "Nicolas Rodriguez"
25 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
26 __credits__ = ["Nicolas Rodriguez"]
27 __license__ = ""
28 __version__ = "1.0.0"
20 __author__ = "Nicolas Rodriguez"
21 __copyright__ = "Copyright (c) 2013, Infobyte LLC"
22 __credits__ = ["Nicolas Rodriguez"]
23 __license__ = ""
24 __version__ = "1.0.0"
2925 __maintainer__ = "Francisco Amato"
30 __email__ = "[email protected]"
31 __status__ = "Development"
26 __email__ = "[email protected]"
27 __status__ = "Development"
3228
3329
3430 class SkipfishParser(object):
3531 """
36 The objective of this class is to parse an xml file generated by the skipfish tool.
32 The objective of this class is to parse an xml file generated by
33 the skipfish tool.
3734
3835 TODO: Handle errors.
39 TODO: Test skipfish output version. Handle what happens if the parser doesn't support it.
36 TODO: Test skipfish output version. Handle what happens if the parser
37 doesn't support it.
4038 TODO: Test cases.
4139
4240 @param skipfish_filepath A proper xml generated by skipfish
4341 """
4442 def __init__(self, skipfish_filepath):
4543 self.filepath = skipfish_filepath
46
44
4745 tmp = open(skipfish_filepath+"/samples.js", "r").read()
48 issues = json.loads(self.extract_data(tmp, "var issue_samples =", "];",
49 lambda x: x.replace("'", '"'), False, False)[1] + "]")
50
46 issues = json.loads(
47 self.extract_data(
48 tmp,
49 "var issue_samples =", "];",
50 lambda x: x.replace("'", '"'),
51 False,
52 False)
53 [1] + "]")
54
5155 tmp = open(skipfish_filepath+"/index.html", "r").read()
52 err_msg = json.loads(self.extract_data(tmp, "var issue_desc=", "};",
53 lambda x: self.convert_quotes(x, "'", '"'), False, False)[1] + "}")
54
55 self.err_msg=err_msg
56 self.issues=issues
57
58 def convert_quotes(self,text, quote="'", inside='"'):
56 err_msg = json.loads(
57 self.extract_data(
58 tmp,
59 "var issue_desc=",
60 "};",
61 lambda x: self.convert_quotes(x, "'", '"'),
62 False,
63 False)
64 [1] + "}")
65
66 self.err_msg = err_msg
67 self.issues = issues
68
69 def convert_quotes(self, text, quote="'", inside='"'):
5970 start = 0
6071 while True:
6172 pos = text.find(quote, start)
62
73
6374 if pos == -1:
6475 break
65
76
6677 ss = text[:pos - 1]
6778 quotes = len(ss) - len(ss.replace(inside, ""))
68
79
6980 if quotes % 2 == 0:
7081 text = text[:pos - 1] + "\\" + quote + text[pos + 1:]
71
82
7283 start = pos + 1
7384 return text
74
85
7586 def extract_data(self, samples, start_tag, end_tag, fn=lambda x: x, include_start_tag=True, include_end_tag=True):
7687 start = samples.find(start_tag)
77
88
7889 if start == -1:
7990 return (-1, None)
80
91
8192 end = samples.find(end_tag, start + 1)
82
93
8394 if end == -1:
8495 return (-2, None)
85
96
8697 data = samples[start:end + len(end_tag)]
8798 data = fn(data)
88
99
89100 if not include_start_tag:
90101 data = data[len(start_tag) + 1:]
91
102
92103 if not include_end_tag:
93104 data = data[:-1 * len(end_tag)]
94
105
95106 return (0, data)
96107
97108
101112 """
102113 def __init__(self):
103114 core.PluginBase.__init__(self)
104 self.id = "Skipfish"
105 self.name = "Skipfish XML Output Plugin"
106 self.plugin_version = "0.0.2"
107 self.version = "2.1.5"
108 self.options = None
115 self.id = "Skipfish"
116 self.name = "Skipfish XML Output Plugin"
117 self.plugin_version = "0.0.2"
118 self.version = "2.1.5"
119 self.options = None
109120 self._current_output = None
110121 self.parent = None
111 self._command_regex = re.compile(r'^(sudo skipfish|skipfish|sudo skipfish\.pl|skipfish\.pl|perl skipfish\.pl|\.\/skipfish\.pl|\.\/skipfish).*?')
112 self._completition = {
113 "":"Usage: skipfish [ options ... ] -W wordlist -o output_dir start_url [ start_url2 ... ]",
114 "-A":"user:pass - use specified HTTP authentication credentials",
115 "-F":"host=IP - pretend that 'host' resolves to 'IP'",
116 "-C":"name=val - append a custom cookie to all requests",
117 "-H":"name=val - append a custom HTTP header to all requests",
118 "-b":"(i|f|p) - use headers consistent with MSIE / Firefox / iPhone",
119 "-N":"- do not accept any new cookies",
120 "--auth-form":"url - form authentication URL",
121 "--auth-user":"user - form authentication user",
122 "--auth-pass":"pass - form authentication password",
123 "--auth-verify-url":" --auth-verify-url - URL for in-session detection",
124 "-d":"max_depth - maximum crawl tree depth (16)",
125 "-c":"max_child - maximum children to index per node (512)",
126 "-x":"max_desc - maximum descendants to index per branch (8192)",
127 "-r":"r_limit - max total number of requests to send (100000000)",
128 "-p":"crawl% - node and link crawl probability (100%)",
129 "-q":"hex - repeat probabilistic scan with given seed",
130 "-I":"string - only follow URLs matching 'string'",
131 "-X":"string - exclude URLs matching 'string'",
132 "-K":"string - do not fuzz parameters named 'string'",
133 "-D":"domain - crawl cross-site links to another domain",
134 "-B":"domain - trust, but do not crawl, another domain",
135 "-Z":"- do not descend into 5xx locations",
136 "-O":"- do not submit any forms",
137 "-P":"- do not parse HTML, etc, to find new links",
138 "-o":"dir - write output to specified directory (required)",
139 "-M":"- log warnings about mixed content / non-SSL passwords",
140 "-E":"- log all HTTP/1.0 / HTTP/1.1 caching intent mismatches",
141 "-U":"- log all external URLs and e-mails seen",
142 "-Q":"- completely suppress duplicate nodes in reports",
143 "-u":"- be quiet, disable realtime progress stats",
144 "-v":"- enable runtime logging (to stderr)",
145 "-W":"wordlist - use a specified read-write wordlist (required)",
146 "-S":"wordlist - load a supplemental read-only wordlist",
147 "-L":"- do not auto-learn new keywords for the site",
148 "-Y":"- do not fuzz extensions in directory brute-force",
149 "-R":"age - purge words hit more than 'age' scans ago",
150 "-T":"name=val - add new form auto-fill rule",
151 "-G":"max_guess - maximum number of keyword guesses to keep (256)",
152 "-z":"sigfile - load signatures from this file",
153 "-g":"max_conn - max simultaneous TCP connections, global (40)",
154 "-m":"host_conn - max simultaneous connections, per target IP (10)",
155 "-f":"max_fail - max number of consecutive HTTP errors (100)",
156 "-t":"req_tmout - total request response timeout (20 s)",
157 "-w":"rw_tmout - individual network I/O timeout (10 s)",
158 "-i":"idle_tmout - timeout on idle HTTP connections (10 s)",
159 "-s":"s_limit - response size limit (400000 B)",
160 "-e":"- do not keep binary responses for reporting",
161 "-l":"max_req - max requests per second (0.000000)",
162 "-k":"duration - stop scanning after the given duration h:m:s",
163 "--config":"- load the specified configuration file",
164
165 }
166
122 self._command_regex = re.compile(
123 r'^(sudo skipfish|skipfish|sudo skipfish\.pl|skipfish\.pl|perl skipfish\.pl|\.\/skipfish\.pl|\.\/skipfish).*?')
167124 global current_path
168125
169
170 def parseOutputString(self, output, debug = False ):
171 """
172 This method will discard the output the shell sends, it will read it from
173 the xml where it expects it to be present.
126 def parseOutputString(self, output, debug=False):
127 """
128 This method will discard the output the shell sends, it will read it
129 from the xml where it expects it to be present.
174130
175131 NOTE: if 'debug' is true then it is being run from a test case and the
176132 output being sent is valid.
177133 """
178
179 # if (re.search("\r\n",output) is None):
180 # self._output_path=output
181
134
182135 if not os.path.exists(self._output_path):
183136 return False
184
137
185138 p = SkipfishParser(self._output_path)
186139
187 hostc={}
188 port=80
140 hostc = {}
141 port = 80
189142 for issue in p.issues:
190 request=""
191 response=""
143 req = ""
144 res = ""
192145 for sample in issue["samples"]:
193146 if not sample["url"] in hostc:
194 reg = 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;%\$#\=~_\-]+)).*?$", sample["url"])
195
147 reg = re.search(
148 "(http|https|ftp)\://([a-zA-Z0-9\.\-]+(\:[a-zA-Z0-9\.&amp;%\$\-]+)*@)*((25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])|localhost|([a-zA-Z0-9\-]+\.)*[a-zA-Z0-9\-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))[\:]*([0-9]+)*([/]*($|[a-zA-Z0-9\.\,\?\'\\\+&amp;%\$#\=~_\-]+)).*?$", sample["url"])
149
196150 protocol = reg.group(1)
197151 host = reg.group(4)
198152 if reg.group(11) is not None:
199153 port = reg.group(11)
200154 else:
201155 port = 443 if protocol == "https" else 80
202
156
203157 ip = self.resolve(host)
204
158
205159 h_id = self.createAndAddHost(ip)
206 i_id = self.createAndAddInterface(h_id, ip, ipv4_address=ip,hostname_resolution=host)
207 s_id = self.createAndAddServiceToInterface(h_id, i_id, "http",
208 "tcp",
209 ports = [port],
210 status = "open")
211
212 n_id = self.createAndAddNoteToService(h_id,s_id,"website","")
213 n2_id = self.createAndAddNoteToNote(h_id,s_id,n_id,host,"")
214
215
216 hostc[sample["url"]]={'h_id':h_id, 'ip':ip,'port':port,'host':host,'protocol':protocol,
217 'i_id':i_id,'s_id':s_id}
218
219
160 i_id = self.createAndAddInterface(
161 h_id,
162 ip,
163 ipv4_address=ip,
164 hostname_resolution=host)
165
166 s_id = self.createAndAddServiceToInterface(
167 h_id,
168 i_id,
169 "http",
170 "tcp",
171 ports=[port],
172 status="open")
173
174 n_id = self.createAndAddNoteToService(
175 h_id,
176 s_id,
177 "website",
178 "")
179
180 self.createAndAddNoteToNote(h_id, s_id, n_id, host, "")
181
182 hostc[sample["url"]] = {
183 'h_id': h_id,
184 'ip': ip,
185 'port': port,
186 'host': host,
187 'protocol': protocol,
188 'i_id': i_id,
189 's_id': s_id}
190
220191 try:
221 request =open("%s/request.dat" % sample["dir"], "r").read()
192 req = open("%s/request.dat" % sample["dir"], "r").read()
222193 except:
223194 pass
224
195
225196 try:
226 response =open("%s/request.dat" % sample["dir"], "r").read()
197 res = open("%s/request.dat" % sample["dir"], "r").read()
227198 except:
228199 pass
229
230 d=hostc[sample["url"]]
231 v_id = self.createAndAddVulnWebToService(d['h_id'], d['s_id'],
232 name=p.err_msg[str(issue["type"])],desc="Extra: " + sample["extra"], website=d['host'],
233 path=sample["url"],severity=issue["severity"])
234
200
201 d = hostc[sample["url"]]
202 self.createAndAddVulnWebToService(
203 d['h_id'],
204 d['s_id'],
205 name=p.err_msg[str(issue["type"])],
206 desc="Extra: " + sample["extra"],
207 website=d['host'],
208 path=sample["url"],
209 severity=issue["severity"])
235210
236211 def resolve(self, host):
237212 try:
249224 """
250225 arg_match = self.xml_arg_re.match(command_string)
251226
252 self._output_path = os.path.join(self.data_path,
253 "skipfish_output-%s" % random.uniform(1,10))
227 self._output_path = os.path.join(
228 self.data_path,
229 "skipfish_output-%s" % random.uniform(1, 10))
254230
255231 if arg_match is None:
256 return re.sub(r"(^.*?skipfish)",
257 r"\1 -o %s" % self._output_path,
258 command_string,1)
232 return re.sub(
233 r"(^.*?skipfish)",
234 r"\1 -o %s" % self._output_path,
235 command_string,
236 1)
259237 else:
260 return re.sub(arg_match.group(1),
261 r"-o %s" % self._output_path,
262 command_string,1)
238 return re.sub(
239 arg_match.group(1),
240 r"-o %s" % self._output_path,
241 command_string,
242 1)
263243
264244 def setHost(self):
265245 pass
1010 import sys
1111 import os
1212
13 from plugins import core
13 from plugins.plugin import PluginTerminalOutput
1414 from model import api
1515 import re
1616 import os
2525 from BaseHTTPServer import BaseHTTPRequestHandler
2626 from StringIO import StringIO
2727
28
28
2929 try:
3030 import xml.etree.cElementTree as ET
3131 import xml.etree.ElementTree as ET_ORIG
3333 except ImportError:
3434 import xml.etree.ElementTree as ET
3535 ETREE_VERSION = ET.VERSION
36
36
3737 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
3838
3939 current_path = os.path.abspath(os.getcwd())
7575 if statement.lstrip().upper().startswith("SELECT"):
7676 return self.cursor.fetchall()
7777
78 class SqlmapPlugin(core.PluginBase):
78 class SqlmapPlugin(PluginTerminalOutput):
7979 """
8080 Example plugin to parse sqlmap output.
8181 """
8282 def __init__(self):
83 core.PluginBase.__init__(self)
83 PluginTerminalOutput.__init__(self)
8484 self.id = "Sqlmap"
8585 self.name = "Sqlmap"
8686 self.plugin_version = "0.0.2"
9696 self.path=""
9797
9898 self.addSetting("Sqlmap path", str, "/root/tools/sqlmap")
99
99
100100 self.db_port = { "MySQL" : 3306, "PostgreSQL":"", "Microsoft SQL Server" : 1433,
101101 "Oracle" : 1521, "Firebird" : 3050,"SAP MaxDB":7210, "Sybase" : 5000,
102102 "IBM DB2" : 50000, "HSQLDB" :9001}
108108 5: "LIKE double quoted string",
109109 }
110110
111 self._command_regex = re.compile(r'^(sudo sqlmap|sqlmap|sudo python sqlmap|python sqlmap|\.\/sqlmap).*?')
111 self._command_regex = re.compile(r'^(python2.7 ./sqlmap.py|sudo sqlmap|sqlmap|sudo python sqlmap|python sqlmap|\.\/sqlmap).*?')
112112
113113 global current_path
114114 self._output_path = ""
277277 "--purge-output":"Safely remove all content from output directory",
278278 "--smart":"Conduct through tests only if positive heuristic(s)",
279279 "--wizard":"Simple wizard interface for beginner users",
280 }
280 }
281281 class HTTPRequest(BaseHTTPRequestHandler):
282282 def __init__(self, request_text):
283283 self.rfile = StringIO(request_text)
284284 self.raw_requestline = self.rfile.readline()
285285 self.error_code = self.error_message = None
286286 self.parse_request()
287
287
288288 def send_error(self, code, message):
289289 self.error_code = code
290290 self.error_message = message
298298 """
299299 Helper function for restoring session data from HashDB
300300 """
301
301
302302 key = "%s%s%s" % (self.url or "%s%s" % (self.hostname, self.port), key, self.HASHDB_MILESTONE_VALUE)
303303 retVal=""
304
304
305305 hash_ = self.hashKey(key)
306306 # print "hash_" + str(hash_) + "key=" + key
307307 if not retVal:
319319 def base64decode(self,value):
320320 """
321321 Decodes string value from Base64 to plain format
322
322
323323 >>> base64decode('Zm9vYmFy')
324324 'foobar'
325325 """
326
326
327327 return value.decode("base64")
328
328
329329 def base64encode(self,value):
330330 """
331331 Encodes string value from plain to Base64 format
332
332
333333 >>> base64encode('foobar')
334334 'Zm9vYmFy'
335335 """
336
336
337337 return value.encode("base64")[:-1].replace("\n", "")
338
338
339339 def base64unpickle(self,value):
340340 """
341341 Decodes value from Base64 to plain format and deserializes (with pickle) its content
342
342
343343 >>> base64unpickle('gAJVBmZvb2JhcnEALg==')
344344 'foobar'
345345 """
346346 if value:
347347 return pickle.loads(self.base64decode(value))
348
349
348
349
350350 def xmlvalue(self,db,name,value="query"):
351
351
352352 filepath = "%s" % os.path.join(current_path, "plugins/repo/sqlmap/queries.xml")
353353 with open(filepath,"r") as f:
354354 try:
356356 except SyntaxError, err:
357357 print "SyntaxError: %s. %s" % (err, filepath)
358358 return None
359
359
360360 for node in tree.findall("dbms[@value='"+db+"']/"+name+""):
361361 return node.attrib[value]
362362
364364 users = re.findall('database management system users \[[\d]+\]:\r\n(.*?)\r\n\r\n',data, re.S)
365365 if users:
366366 return map((lambda x: x.replace("[*] ","")), users[0].split("\r\n"))
367
367
368368 def getdbs(self,data):
369369 dbs = re.findall('available databases \[[\d]+\]:\r\n(.*?)\r\n\r\n',data, re.S)
370370 if dbs:
374374 password = re.findall('database management system users password hashes:\r\n(.*?)\r\n\r\n',data, re.S)
375375 if password:
376376 for p in password[0].split("[*] ")[1::]:
377
377
378378 user=re.findall("^(.*?) \[",p)[0]
379379 mpass=re.findall("password hash: (.*?)$",p, re.S)
380380 mpass=map((lambda x: re.sub(r"[ \r\n]", "", x)), mpass[0].split("password hash: "))
381381 users[user]=mpass
382382 return users
383
383
384384 def getAddress(self, hostname):
385385 """
386386 Returns remote IP address from hostname.
388388 try:
389389 return socket.gethostbyname(hostname)
390390 except socket.error, msg:
391
391
392392 return self.hostname
393
393
394394 def parseOutputString(self, output, debug = False):
395395 """
396396 This method will discard the output the shell sends, it will read it from
399399 NOTE: if 'debug' is true then it is being run from a test case and the
400400 output being sent is valid.
401401 """
402
402
403403 sys.path.append(self.getSetting("Sqlmap path"))
404404
405405 from lib.core.settings import HASHDB_MILESTONE_VALUE
408408 self.HASHDB_MILESTONE_VALUE = HASHDB_MILESTONE_VALUE
409409 self.HASHDB_KEYS = HASHDB_KEYS
410410 self.UNICODE_ENCODING = UNICODE_ENCODING
411
411
412412 password = self.getpassword(output)
413413 webserver = re.search("web application technology: (.*?)\n",output)
414414 if webserver:
416416 users = self.getuser(output)
417417 # print users
418418 dbs = self.getdbs(output)
419
419
420420 # print "webserver = " + webserver
421421 # print "dbs = " + str(dbs)
422422 # print "users = " + str(users)
423423 # print "password = " + str(password)
424
424
425425
426426 db = Database(self._output_path)
427 db.connect()
428
427 db.connect()
428
429429 absFilePaths = self.hashDBRetrieve(self.HASHDB_KEYS.KB_ABS_FILE_PATHS, True, db)
430430 tables = self.hashDBRetrieve(self.HASHDB_KEYS.KB_BRUTE_TABLES, True, db)
431431 columns = self.hashDBRetrieve(self.HASHDB_KEYS.KB_BRUTE_COLUMNS, True, db)
432432 xpCmdshellAvailable = self.hashDBRetrieve(self.HASHDB_KEYS.KB_XP_CMDSHELL_AVAILABLE, True, db)
433 dbms_version = self.hashDBRetrieve(self.HASHDB_KEYS.DBMS, False, db)
434
435 os = self.hashDBRetrieve(self.HASHDB_KEYS.OS, False, db)
436
433 dbms_version = self.hashDBRetrieve(self.HASHDB_KEYS.DBMS, False, db)
434
435 os = self.hashDBRetrieve(self.HASHDB_KEYS.OS, False, db)
436
437437 self.ip=self.getAddress(self.hostname)
438438
439439 dbms=str(dbms_version.split(" ")[0])
447447 version=webserver)
448448 n_id = self.createAndAddNoteToService(h_id,s_id,"website","")
449449 n2_id = self.createAndAddNoteToNote(h_id,s_id,n_id,self.hostname,"")
450
450
451451 db_port=self.db_port[dbms]
452
452
453453 s_id2 = self.createAndAddServiceToInterface(h_id, i_id,
454454 name=dbms ,
455455 protocol="tcp",
460460 if users:
461461 for v in users:
462462 self.createAndAddCredToService(h_id,s_id2,v,"")
463
463
464464 if password:
465465 for k,v in password.iteritems():
466466 for p in v:
467467 self.createAndAddCredToService(h_id,s_id2,k,p)
468
468
469469 if absFilePaths:
470470 n_id2 = self.createAndAddNoteToService(h_id,s_id2,"sqlmap.absFilePaths",str(absFilePaths))
471471 if tables:
474474 n_id2 = self.createAndAddNoteToService(h_id,s_id2,"sqlmap.brutecolumns",str(columns))
475475 if xpCmdshellAvailable:
476476 n_id2 = self.createAndAddNoteToService(h_id,s_id2,"sqlmap.xpCmdshellAvailable",str(xpCmdshellAvailable))
477
477
478478 for inj in self.hashDBRetrieve(self.HASHDB_KEYS.KB_INJECTIONS, True,db) or []:
479479 # print inj
480 # print inj.dbms
481 # print inj.dbms_version
482 # print inj.place
483 # print inj.os
484 # print inj.parameter
485
480 # print inj.dbms
481 # print inj.dbms_version
482 # print inj.place
483 # print inj.os
484 # print inj.parameter
485
486486 dbversion = self.hashDBRetrieve("None"+self.xmlvalue(dbms,"banner"), False, db)
487487 user = self.hashDBRetrieve("None"+self.xmlvalue(dbms,"current_user"), False, db)
488488 dbname = self.hashDBRetrieve("None"+self.xmlvalue(dbms,"current_db"), False, db)
489489 hostname = self.hashDBRetrieve("None"+self.xmlvalue(dbms,"hostname"), False, db)
490490
491491 # print "username = " + user
492
492
493493 if user:
494494 n_id2 = self.createAndAddNoteToService(h_id,s_id2,"db.user",user)
495495 if dbname:
500500 n_id2 = self.createAndAddNoteToService(h_id,s_id2,"db.version",dbversion)
501501 if dbs:
502502 n_id2 = self.createAndAddNoteToService(h_id,s_id2,"db.databases",str(dbs))
503
503
504504 for k,v in inj.data.items():
505505 v_id = self.createAndAddVulnWebToService(h_id, s_id,
506506 website=self.hostname,
508508 desc="Payload:" + str(inj.data[k]['payload'])+
509509 "\nVector:"+ str(inj.data[k]['vector'])+
510510 "\nParam type:" + str(self.ptype[inj.ptype]),
511 ref=[],
511 ref=[],
512512 pname=inj.parameter,
513513 severity="high",
514514 method=inj.place,
515515 params=self.params,
516516 path=self.fullpath)
517
518
519
520
521
517
518
519
520
521
522522
523523 def processCommandString(self, username, current_path, command_string):
524
525
526
524
525
526
527527 parser = argparse.ArgumentParser(conflict_handler='resolve')
528
528
529529 parser.add_argument('-h')
530530 parser.add_argument('-u')
531531 parser.add_argument('-s')
532532 parser.add_argument('-r')
533
534
533
534
535535 try:
536536 args, unknown = parser.parse_known_args(shlex.split(re.sub(r'\-h|\-\-help', r'', command_string)))
537537 except SystemExit:
538 pass
539
540 if args.r:
538 pass
539
540 if args.r:
541541 with open(args.r, 'r') as f:
542542 request = self.HTTPRequest(f.read())
543543 args.u="http://"+request.headers['host']+ request.path
544544 f.close()
545
546 if args.u:
545
546 if args.u:
547547 reg = 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;%\$#\=~_\-]+)).*?$", args.u)
548548 self.protocol = reg.group(1)
549549 self.hostname = reg.group(4)
552552 self.port=443
553553 if reg.group(11) is not None:
554554 self.port = reg.group(11)
555
555
556556 if reg.group(12) is not None:
557557 tmp=re.search("/(.*)\?(.*?$)",reg.group(12))
558558 self.path = "/"+tmp.group(1)
559559 self.params=tmp.group(2)
560
560
561561 self.url=self.protocol+"://"+self.hostname+":"+self.port + self.path
562562 self.fullpath=self.url+"?"+self.params
563563 self._output_path="%s%s" % (os.path.join(self.data_path, "sqlmap_output-"),
564564 re.sub(r'[\n\/]', r'', args.u.encode("base64")[:-1]))
565
565
566566 if not args.s:
567 return "%s -s %s\n" % (command_string,self._output_path)
568
567 return "%s -s %s" % (command_string,self._output_path)
568
569569
570570 def setHost(self):
571571 pass
573573
574574 def createPlugin():
575575 return SqlmapPlugin()
576
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
03 '''
14 Faraday Penetration Test IDE
25 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
36 See the file 'doc/LICENSE' for the license information
47
58 '''
6
7 #!/usr/bin/env python
8 # -*- coding: utf-8 -*-
99
1010 from lxml import etree as ET
1111
6161 self.root = tree.getroot()
6262 self.recommended = "\nRecommended configuration changes in web.config:\n"
6363 self.xml = xml
64
64
6565 def xml_export(self,directive,rec):
66 if self.xml is not None:
66 if self.xml is not None:
6767 newElement = ET.SubElement(self.xml, directive[0])
6868 if len(directive) == 4:
6969 newElement.attrib['name'] = directive[3]
7070 newElement.attrib['option'] = directive[1]
7171 newElement.attrib['rec'] = rec
7272 newElement.text = directive[2]
73
74
73
74
7575 def global_check(self):
7676 print "[+]\033[0;41mVulnerabilites/Informations\033[0m:"
7777 countforms = 0
8181 if element == "forms":
8282 countforms += 1
8383 nameforms.append(tag.attrib['name'])
84 if element == "user":
84 if element == "user":
8585 print " \033[1;30m{}\033[0m {}: {} \033[1;30m({})\033[0m".format(element,
8686 tag.attrib['name'],
8787 rules[element][''][0],
88 option)
88 option)
8989 self.recommended += " Not to store passwords or hashes in web.config\n"
9090 self.xml_export(directive=[element, tag.attrib['name'],'hardcoded'],
9191 rec=rules[element][''][0])
9292 continue
93
94 for option in tag.attrib:
93
94 for option in tag.attrib:
9595 if element == "customErrors" and rules[element].has_key(option) and not tag.attrib[option].lower() in rules[element][option][1]:
9696 print " \033[1;30m{}\033[0m: {} \033[1;30m({})\033[0m".format(element,
9797 rules[element][option][0],
98 option)
98 option)
9999 self.recommended += " <{} {}=\"{}\"/>\n".format(element,option,rules[element][option][1])
100100 self.xml_export(directive=[element,option,'disabled'],
101101 rec=rules[element][option][0])
102102 continue
103
103
104104 elif element == "roleManager" and option == "cookiePath":
105105 print " \033[1;30mroleManager\033[0m: Liberal path defined ('{}') (\033[1;30mcookiePath\033[0m)".format(tag.attrib[option].lower())
106106 self.recommended += " <roleManager cookiePath=\"{abcd1234…}\">\n"
107107 self.xml_export(directive=[element,option,'liberal'],
108108 rec=rules[element][option][0])
109109 continue
110
110
111111 if rules[element].has_key(option) and tag.attrib[option].lower() != rules[element][option][1]:
112112 if element == "forms":
113113 print " \033[1;30m{}\033[0m {}: {} \033[1;30m({})\033[0m".format(element,
114114 tag.attrib['name'],
115115 rules[element][option][0],
116 option)
116 option)
117117 self.xml_export(directive=[element,option,tag.attrib[option],tag.attrib['name']],
118 rec=rules[element][option][0])
119
118 rec=rules[element][option][0])
119
120120 else:
121121 print " \033[1;30m{}\033[0m: {} \033[1;30m({})\033[0m".format(element,
122122 rules[element][option][0],
123123 option)
124
124
125125 self.xml_export(directive=[element,option,tag.attrib[option]],
126 rec=rules[element][option][0])
126 rec=rules[element][option][0])
127127 self.recommended += " <{} {}=\"{}\"/>\n".format(element,option,rules[element][option][1])
128128 continue
129
129
130130 if countforms > 1 and (nameforms.index('.ASPXAUTH') != "-1" or nameforms.index('ASPXAUTH') != "-1"):
131131 print " \033[1;30mforms\033[0m: Non-Unique authentication cookie used\033[1;30m (name)\033[0m"
132132 self.recommended += " <forms name=\"{abcd1234…}\">\n"
133133 self.xml_export(directive=['nameforms','name','false'],
134134 rec="Non-Unique authentication cookie used")
135
135
136136 def scanner(file,recmode,xml):
137137 filetoscan = WebConfigScan(file,xml)
138138 filetoscan.global_check()
139139 if recmode:
140140 print filetoscan.recommended
141
141
0 #!/usr/bin/env python2.7
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 unittest
11 import sys
12 import os
13 sys.path.append(os.path.abspath(os.getcwd()))
14 from plugins.repo.ping.plugin import CmdPingPlugin
15 from model.common import (
16 factory, ModelObjectVuln, ModelObjectCred,
17 ModelObjectVulnWeb, ModelObjectNote
18 )
19 from model.hosts import (
20 Host, Service, Interface
21 )
22 from plugins.modelactions import modelactions
23
24
25 class CmdPingPluginTest(unittest.TestCase):
26 plugin = CmdPingPlugin()
27 outputPingGoogle = ("PING google.com (216.58.222.142) 56(84) bytes of"
28 "data.\n64 bytes from scl03s11-in-f14.1e100.net"
29 "(216.58.222.142): icmp_seq=1 ttl=53 time=28.9 ms")
30 def setUp(self):
31 factory.register(Host)
32 factory.register(Interface)
33 factory.register(Service)
34 factory.register(ModelObjectVuln)
35 factory.register(ModelObjectVulnWeb)
36 factory.register(ModelObjectNote)
37 factory.register(ModelObjectCred)
38
39 def test_Plugin_Calls_createAndAddHost(self):
40 self.plugin.parseOutputString(self.outputPingGoogle)
41 action = self.plugin._pending_actions.get(block=True)
42 self.assertEqual(action[0], modelactions.CADDHOST)
43 self.assertEqual(action[1], "216.58.222.142")
44 action = self.plugin._pending_actions.get(block=True)
45 self.assertEqual(action[0], modelactions.CADDINTERFACE)
46 self.assertEqual(action[2], "216.58.222.142")
47
48 if __name__ == '__main__':
49 unittest.main()
00 #!/usr/bin/python
1 '''
2 Faraday Penetration Test IDE
1 """
2 Faraday Penetration Test IDE.
3
34 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
45 See the file 'doc/LICENSE' for the license information
6 """
57
6 '''
8 import mock
79 import unittest
810 import sys
911 sys.path.append('.')
1012
11 from config.configuration import getInstanceConfiguration
12 from model.workspace import Workspace
13 from managers.reports_managers import ReportManager, NoReportsWatchException
13 from plugins.core import PluginControllerForApi
14 from managers.reports_managers import ReportProcessor
15 from managers.reports_managers import ReportParser
1416
15 from persistence.persistence_managers import DBTYPE
16 from mockito import mock, verify, when, any
17 CONF = getInstanceConfiguration()
17 class UnitTestReportParser(unittest.TestCase):
1818
19 from test_cases import common
19 def testSendReportWithPlugin(self):
2020
21 class UnitTestWorkspaceManager(unittest.TestCase):
22 def testCreateReportManager(self):
23 timer = 10
24 report_manager = ReportManager(timer, mock())
21 plugin_controller = mock.Mock(spec=PluginControllerForApi)
22 plugin_controller.processCommandInput.return_value = (True, None, None)
23 report_processor = ReportProcessor(plugin_controller)
2524
26 self.assertIsNotNone(report_manager)
25 file_mock = mock.MagicMock(spec=file)
26 file_mock.read.return_value = 'Stringreturned'
2727
28 def testWatchReportPath(self):
29 import os.path
30 import os
31 workspace_name = common.new_random_workspace_name()
32 timer = 10
28 with mock.patch('__builtin__.open', create=True) as mock_open:
29 res = report_processor._sendReport("nmap", 'strings')
30 self.assertTrue(res, "The plugin should be executed")
3331
34 report_manager = ReportManager(timer, mock())
35 report_manager.watch(workspace_name)
32 def testSendReportWithoutPlugin(self):
3633
37 self.assertTrue(os.path.exists(os.path.join(CONF.getReportPath(),
38 workspace_name)), 'Report directory not found')
39
40 def testStartReportNoPathRunsException(self):
41 report_manager = ReportManager(0, mock())
42 self.assertRaises(NoReportsWatchException, report_manager.startWatch)
34 plugin_controller = mock.Mock(spec=PluginControllerForApi)
35 plugin_controller.processCommandInput.return_value = (False, None, None)
36 report_processor = ReportProcessor(plugin_controller)
37
38 file_mock = mock.MagicMock(spec=file)
39 file_mock.read.return_value = 'Stringreturned'
40
41 with mock.patch('__builtin__.open', create=True) as mock_open:
42 res = report_processor._sendReport("nmap", 'strings')
43 self.assertFalse(res, "The plugin should not be executed")
4344
4445 if __name__ == '__main__':
4546 unittest.main()
46
22 // See the file 'doc/LICENSE' for the license information
33
44 angular.module('faradayApp')
5 .controller('hostsCtrl',
5 .controller('hostsCtrl',
66 ['$scope', '$cookies', '$filter', '$location', '$route', '$routeParams', '$uibModal', 'hostsManager', 'workspacesFact',
77 function($scope, $cookies, $filter, $location, $route, $routeParams, $uibModal, hostsManager, workspacesFact) {
88
2525 $scope.hosts = hosts;
2626 $scope.loadedVulns = true;
2727 $scope.loadIcons();
28 });
29
30 hostsManager.getAllServicesCount($scope.workspace)
31 .then(function(servicesCount) {
32 $scope.servicesCount = servicesCount;
2833 });
2934
3035 hostsManager.getAllVulnsCount($scope.workspace)
5151 <tr>
5252 <th><input type="checkbox" ng-model="selectall" ng-click="checkAll()"/></th>
5353 <th>
54 <a href="">Services</a>
54 <a href="">Open Services</a>
5555 </th>
5656 <th>
5757 <a href="" ng-click="toggleSort('name')">Name</a>
7777 selection-model-selected-class="multi-selected"
7878 selection-model-on-change="selectedHosts()">
7979 <td><input type="checkbox" name="{{host._id}}"/></td>
80 <td><a href="#/host/ws/{{workspace}}/hid/{{host._id}}">View</a></td>
80 <td><a href="#/host/ws/{{workspace}}/hid/{{host._id}}" ng-bind="servicesCount[host._id] || 'None'"></a></td>
8181 <td>
8282 {{host.name}}
8383 <a href="//www.shodan.io/search?query={{host.name}}" uib-tooltip="Search in Shodan" target="_blank">
5454 var deferred = $q.defer();
5555 var self = this;
5656 this._objects = {};
57
57
5858 $http.get(BASEURL + ws + '/_design/hosts/_view/hosts')
5959 .success(function(hostsArray) {
6060 var hosts = [];
118118 .catch(function() {
119119 deferred.reject("Error creating host");
120120 });
121
121
122122 return deferred.promise;
123123 };
124124
185185 return deferred.promise;
186186 };
187187
188 hostsManager.getAllServicesCount = function(ws) {
189 var deferred = $q.defer();
190
191 var url = BASEURL + ws + '/_design/hosts/_view/byservicecount?group=true';
192
193 $http.get(url)
194 .success(function(allrows) {
195 var services = {};
196
197 allrows.rows.forEach(function(service) {
198 services[service.key] = service.value;
199 });
200
201 deferred.resolve(services);
202 })
203 .error(function() {
204 deferred.reject("Unable to load Services");
205 });
206
207 return deferred.promise;
208 };
209
188210 hostsManager.getInterfaces = function(ws, id) {
189211 var deferred = $q.defer(),
190212 self = this;
6767 })
6868 return deferred.promise;
6969 }
70
70
7171 servicesManager.getServicesByHost = function(ws, host_id) {
7272 var deferred = $q.defer();
7373 var url = BASEURL + "/" + ws + "/_design/services/_view/byhost?key=\"" + host_id + "\"";
122122 })
123123 });
124124 });
125
125
126126 return deferred.promise;
127127 }
128128
6060 <div class="form-horizontal">
6161 <div class="form-group">
6262 <div class="col-md-12">
63 <h5>CWE</h5>
64 <input type="text" ng-model="modal.cwe_selected" class="form-control input-sm" placeholder="Search for CWE" uib-typeahead="cwe as cwe.name for cwe in modal.cweList | filter:{name: $viewValue} | limitTo:10" typeahead-on-select="modal.populate($item, $model, $label)">
63 <h5>Search vulnerability database templates <a href="https://github.com/infobyte/faraday/wiki/vulnerabilities-database" target="_blank"><span class="glyphicon glyphicon-question-sign" title="Read more about vulnerability templates in Faraday Documentation"></span></a></h5>
64 <input type="text" ng-model="modal.cwe_selected" class="form-control input-sm" placeholder="Search for vulnerability templates" uib-typeahead="cwe as cwe.name for cwe in modal.cweList | filter:{name: $viewValue} | limitTo:10" typeahead-on-select="modal.populate($item, $model, $label)">
6565 </div>
6666 </div>
6767 <div class="form-group">
66
77 WORKSPACE=`cat $HOME/.faraday/config/user.xml | grep '<last_workspace' | cut -d '>' -f 2 | cut -d '<' -f 1`
88 STATUS=`curl -s $FARADAY_ZSH_HOST:$FARADAY_ZSH_RPORT/status/check | sed "s/[^0-9]//g" | grep -v '^[[:space:]]*$'`
9 PS1="%{${fg_bold[red]}%}[faraday]($WORKSPACE)%{${reset_color}%} $PS1"
9 USERPS1=$PS1
10 PS1="%{${fg_bold[red]}%}[faraday]($WORKSPACE)%{${reset_color}%} $USERPS1"
11 export FARADAY_OUTPUT=
12 export FARADAY_PLUGIN=
13 alias faraday_b64='base64 -w 0'
14
15 if [[ $(uname) == 'Darwin' ]]; then
16 alias faraday_b64='base64'
17 fi
1018
1119 echo ">>> WELCOME TO FARADAY"
1220 echo "[+] Current Workspace: $WORKSPACE"
1321 if [[ -z $STATUS ]]; then
14 echo "[-] API: Warning API unreachable"
22 echo "[-] API: Warning API unreachable"
1523
16 elif [[ $STATUS == "200" ]]; then
17 echo "[+] API: OK"
18 else
19 echo "[!] API: $STATUS"
24 elif [[ $STATUS == "200" ]]; then
25 echo "[+] API: OK"
26 else
27 echo "[!] API: $STATUS"
2028
2129 fi
2230
2331 setopt multios
2432 setopt histignorespace
2533
26 plugin_controller_client=$HOME/.faraday/zsh/plugin_controller_client.py
2734 old_cmd=
2835
29 add-output() {
36 function add-output() {
3037 old_cmd=$BUFFER
31 new_cmd=`python2 $plugin_controller_client send_cmd $BUFFER`
32 BUFFER=" $new_cmd"
38 FARADAY_PLUGIN=
39 FARADAY_OUTPUT=
40 pwd_actual=$(printf "%s" "$(pwd)"| faraday_b64)
41 cmd_encoded=$(printf "%s" "$BUFFER"| faraday_b64)
42 json_response=`curl -s -X POST -H "Content-Type: application/json" -d "{\"cmd\": \"$cmd_encoded\", \"pid\": $$, \"pwd\": \"$pwd_actual\"}" http://$FARADAY_ZSH_HOST:$FARADAY_ZSH_RPORT/cmd/input`
43 if [[ $? -eq 0 ]]; then
44 code=`echo $json_response|env python2.7 -c "import sys, json;print(json.load(sys.stdin)[\"code\"])"`
45 if [[ "$code" == "200" ]]; then
46 FARADAY_PLUGIN=`echo $json_response | env python2.7 -c "import sys, json; print(json.load(sys.stdin)[\"plugin\"])"`
47 new_cmd=`echo $json_response | env python2.7 -c "import sys, json; print(json.load(sys.stdin)[\"cmd\"])"`
48 if [[ "$new_cmd" != "None" ]]; then
49 BUFFER=" $new_cmd"
50 fi
51 FARADAY_OUTPUT=`mktemp tmp.XXXXXXXXXXXXXXXXXXXXXXXXXXXXX`
52 BUFFER="$BUFFER >&1 >> $FARADAY_OUTPUT"
53 fi
54 fi
3355 zle .accept-line "$@"
3456 }
3557
36 function zshaddhistory() {
58 function send-output() {
59 if [ ! -z "$FARADAY_PLUGIN" ]; then
60 output=`env python2.7 -c "import base64; print(base64.b64encode(open(\"$FARADAY_OUTPUT\",'r').read()))"`
61 temp_file=`mktemp tmp.XXXXXXXXXXXXXXXXXXXXXXXXXXXXX`
62 echo "{\"exit_code\": $?, \"pid\": $$, \"output\": \"$output\" }" >> $temp_file
63 curl=`curl -s -X POST -H "Content-Type: application/json" -d @$temp_file http://$FARADAY_ZSH_HOST:$FARADAY_ZSH_RPORT/cmd/output`
64 rm -f $temp_file
65 fi
66 if [ -f $FARADAY_OUTPUT ];then
67 rm -f $FARADAY_OUTPUT
68 fi
69 FARADAY_OUTPUT=
70 FARADAY_PLUGIN=
71 }
72
73 zshaddhistory() {
3774 emulate -L zsh
3875 print -sr -- "$old_cmd"
3976 fc -p
4077 return 1
4178 }
4279
80 precmd() {
81 send-output
82 WORKSPACE=`cat $HOME/.faraday/config/user.xml | grep '<last_workspace' | cut -d '>' -f 2 | cut -d '<' -f 1`
83 PS1="%{${fg_bold[red]}%}[faraday]($WORKSPACE)%{${reset_color}%} $USERPS1"
84 return 0
85 }
86
87 zshexit() {
88 send-output
89 }
90
4391 zle -N accept-line add-output
0 #!/usr/bin/env python
0 #!/usr/bin/env python2.7
11 '''
22 Faraday Penetration Test IDE
33 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
2828 headers = {'Content-type': 'application/json', 'Accept': 'application/json'}
2929
3030
31 def send_cmd(cmd):
32 data = {"cmd": cmd}
31 def send_cmd(pid, cmd):
32
33 data = {'pid': pid, 'cmd': cmd}
3334 new_cmd = cmd
34 result = False
35 response = ''
36
3537 try:
36 response = requests.post(url_input,
37 data=json.dumps(data),
38 headers=headers)
38 request = requests.post(
39 url_input,
40 data=json.dumps(data),
41 headers=headers)
3942
40 if response.status_code == 200:
41 json_response = response.json()
42 if "cmd" in json_response.keys():
43 if json_response.get("cmd") is not None:
44 new_cmd = json_response.get("cmd")
45 if "custom_output_file" in json_response.keys():
46 output_file = json_response.get("custom_output_file")
47 if output_file is None:
48 output_file = "%s/%s.output" % (output_folder, uuid.uuid4())
49 new_cmd += " >&1 > %s" % output_file
43 if request.status_code == 200:
5044
51 new_cmd += " && python2 %s send_output %s \"%s\"" % (file_path, base64.b64encode(cmd), output_file)
52 result = True
45 response = request.json()
46 if response.get("cmd") is not None:
47 new_cmd = response.get("cmd")
48
49 output_file = "%s/%s%s.output" % (
50 output_folder, data['pid'], uuid.uuid4())
51
52 new_cmd += " >&1 > %s" % output_file
5353 except:
54 new_cmd = cmd
54 response = ''
5555 finally:
56 print new_cmd
57 return result
56 print response
57 return 0
5858
59 def gen_output(pid):
60 print "%s/%s.%s.output" % (output_folder, pid, uuid.uuid4())
61 return 0
5962
60 def send_output(cmd, output_file):
63 def send_output(pid, exit_code, output_file):
6164 output_file = open(output_file)
6265 output = output_file.read()
63 data = {"cmd": base64.b64decode(cmd), "output": base64.b64encode(output)}
66
67 data = {
68 'pid': pid,
69 'exit_code': exit_code,
70 'output': base64.b64encode(output)
71 }
72
6473 response = requests.post(url_output,
6574 data=json.dumps(data),
6675 headers=headers)
6776 if response.status_code != 200:
68 print "something wrong"
6977 print response.json()
70 return True
71 return False
78 return -1
79 return 0
7280
7381
7482 def main(argv):
75 if len(argv) < 3:
83 if len(argv) < 2:
7684 sys.exit(0)
7785
7886 action = argv[1]
7987
80 dispatcher = {'send_cmd': send_cmd, 'send_output': send_output}
88 # dispatcher = {
89 # 'send_cmd': send_cmd,
90 # 'send_output': send_output,
91 # 'gen_output': gen_output}
8192
82 if action in dispatcher.keys():
83 if len(argv[2:]) > 0:
84 dispatcher[action](*argv[2:])
93 if action == 'send_cmd' and len(argv[2:]) == 2:
94 send_cmd(argv[2], argv[3])
95 if action == 'send_output' and len(argv[2:]) == 3:
96 send_cmd(argv[2], argv[3], argv[4])
97 if action == 'gen_output' and len(argv[2:]) == 1:
98 send_cmd(argv[2])
8599
86 #sys.exit(0)
100 # if action in dispatcher.keys():
101 # if len(argv[2:]) > 0:
102 # dispatcher[action](*argv[2:])
103
87104
88105 if __name__ == '__main__':
89106 main(sys.argv)