| 1 | # Copyright (C) 2008 Adriano Monteiro Marques. |
|---|
| 2 | # |
|---|
| 3 | # Author: Adriano Monteiro Marques <adriano@umitproject.org> |
|---|
| 4 | # Luis A. Bastiao Silva <luis.kop@gmail.com> |
|---|
| 5 | # |
|---|
| 6 | # This program is free software; you can redistribute it and/or modify |
|---|
| 7 | # it under the terms of the GNU General Public License as published by |
|---|
| 8 | # the Free Software Foundation; either version 2 of the License, or |
|---|
| 9 | # (at your option) any later version. |
|---|
| 10 | # |
|---|
| 11 | # This program is distributed in the hope that it will be useful, |
|---|
| 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 14 | # GNU General Public License for more details. |
|---|
| 15 | # |
|---|
| 16 | # You should have received a copy of the GNU General Public License |
|---|
| 17 | # along with this program; if not, write to the Free Software |
|---|
| 18 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|---|
| 19 | |
|---|
| 20 | import re |
|---|
| 21 | import gtk |
|---|
| 22 | import gobject |
|---|
| 23 | |
|---|
| 24 | from higwidgets.higboxes import HIGVBox |
|---|
| 25 | from higwidgets.hignotebooks import HIGNotebook |
|---|
| 26 | from higwidgets.higscrollers import HIGScrolledWindow |
|---|
| 27 | |
|---|
| 28 | from umitCore.UmitConf import CommandProfile, ProfileNotFound, is_maemo |
|---|
| 29 | from umitCore.NmapParser import NmapParser |
|---|
| 30 | |
|---|
| 31 | from umitCore.NmapCommand import NmapCommand |
|---|
| 32 | |
|---|
| 33 | from umitGUI.ScanHostDetailsPage import ScanHostDetailsPage |
|---|
| 34 | from umitGUI.ScanHostsView import ScanHostsView, SCANNING |
|---|
| 35 | from umitGUI.ScanOpenPortsPage import ScanOpenPortsPage |
|---|
| 36 | from umitGUI.ScanRunDetailsPage import ScanRunDetailsPage |
|---|
| 37 | from umitGUI.ScanNmapOutputPage import ScanNmapOutputPage |
|---|
| 38 | |
|---|
| 39 | from umitGUI.ScanToolbar import ScanCommandToolbar, ScanToolbar |
|---|
| 40 | |
|---|
| 41 | from umitGUI.Icons import get_os_icon, get_os_logo, get_vulnerability_logo |
|---|
| 42 | |
|---|
| 43 | |
|---|
| 44 | |
|---|
| 45 | from umitPreferences.conf.ExposeConf import expose_conf |
|---|
| 46 | |
|---|
| 47 | from umitCore.Paths import Path |
|---|
| 48 | from umitCore.UmitLogging import log |
|---|
| 49 | from umitCore.I18N import _ |
|---|
| 50 | |
|---|
| 51 | from types import StringTypes |
|---|
| 52 | |
|---|
| 53 | |
|---|
| 54 | |
|---|
| 55 | icon_dir = Path.pixmaps_dir |
|---|
| 56 | |
|---|
| 57 | |
|---|
| 58 | |
|---|
| 59 | |
|---|
| 60 | |
|---|
| 61 | |
|---|
| 62 | class PageStatus(object): |
|---|
| 63 | """ |
|---|
| 64 | Pages status: |
|---|
| 65 | The page status can be one of the following: |
|---|
| 66 | * saved: there is nothing to be saved in the current scan tab |
|---|
| 67 | * unsaved_unchanged: for recently scanned results that were parsed and |
|---|
| 68 | is unchanged. |
|---|
| 69 | * unsaved_changed: for recently scanned results that were parsed and |
|---|
| 70 | got some changes on its contents (like a comment) |
|---|
| 71 | * loaded_unchanged: for scan results that were loaded from a file and |
|---|
| 72 | got no modifications |
|---|
| 73 | * loaded_changed: for scan results that were loaded from a file and |
|---|
| 74 | got some modifications (like comments) |
|---|
| 75 | * parsing_result: the result is been parsed to be displayed at the tab |
|---|
| 76 | * scanning: there is no parsed result related to this tab, but there |
|---|
| 77 | is a related scan running to show results on that tab |
|---|
| 78 | * empty: there is nothing related to this tab. His widgets are |
|---|
| 79 | disabled and this is the initial state of a new tab |
|---|
| 80 | * unknown: the page status is unknown |
|---|
| 81 | * scan_failed: the scan has failed |
|---|
| 82 | * search_loaded: the scan was loaded from a search result |
|---|
| 83 | """ |
|---|
| 84 | def __init__(self, status=False): |
|---|
| 85 | if status: |
|---|
| 86 | self.status = status |
|---|
| 87 | else: |
|---|
| 88 | self.set_unknown() |
|---|
| 89 | |
|---|
| 90 | def set_empty(self): |
|---|
| 91 | self._status = "empty" |
|---|
| 92 | |
|---|
| 93 | def set_saved(self): |
|---|
| 94 | self._status = "saved" |
|---|
| 95 | |
|---|
| 96 | def set_unsaved_unchanged(self): |
|---|
| 97 | self._status = "unsaved_unchanged" |
|---|
| 98 | |
|---|
| 99 | def set_unsaved_changed(self): |
|---|
| 100 | self._status = "unsaved_changed" |
|---|
| 101 | |
|---|
| 102 | def set_loaded_unchanged(self): |
|---|
| 103 | self._status = "loaded_unchanged" |
|---|
| 104 | |
|---|
| 105 | def set_loaded_changed(self): |
|---|
| 106 | self._status = "loaded_changed" |
|---|
| 107 | |
|---|
| 108 | def set_parsing_result(self): |
|---|
| 109 | self._status = "parsing_result" |
|---|
| 110 | |
|---|
| 111 | def set_scanning(self): |
|---|
| 112 | self._status = "scanning" |
|---|
| 113 | |
|---|
| 114 | def set_unknown(self): |
|---|
| 115 | self._status = "unknown" |
|---|
| 116 | |
|---|
| 117 | def set_scan_failed(self): |
|---|
| 118 | self._status = "scan_failed" |
|---|
| 119 | |
|---|
| 120 | def set_search_loaded(self): |
|---|
| 121 | self._status = "search_loaded" |
|---|
| 122 | |
|---|
| 123 | def get_status(self): |
|---|
| 124 | return self._status |
|---|
| 125 | |
|---|
| 126 | def set_status(self, status): |
|---|
| 127 | if status in self.available_status: |
|---|
| 128 | self._status = status |
|---|
| 129 | else: |
|---|
| 130 | raise Exception("Unknown status!") |
|---|
| 131 | |
|---|
| 132 | def _verify_status(self, status): |
|---|
| 133 | if self._status == status: |
|---|
| 134 | return True |
|---|
| 135 | return False |
|---|
| 136 | |
|---|
| 137 | def get_unsaved_unchanged(self): |
|---|
| 138 | return self._verify_status("unsaved_unchanged") |
|---|
| 139 | |
|---|
| 140 | def get_unsaved_changed(self): |
|---|
| 141 | return self._verify_status("unsaved_changed") |
|---|
| 142 | |
|---|
| 143 | def get_loaded_unchanged(self): |
|---|
| 144 | return self._verify_status("loaded_unchanged") |
|---|
| 145 | |
|---|
| 146 | def get_loaded_changed(self): |
|---|
| 147 | return self._verify_status("loaded_changed") |
|---|
| 148 | |
|---|
| 149 | def get_parsing_result(self): |
|---|
| 150 | return self._verify_status("parsing_result") |
|---|
| 151 | |
|---|
| 152 | def get_scanning(self): |
|---|
| 153 | return self._verify_status("scanning") |
|---|
| 154 | |
|---|
| 155 | def get_empty(self): |
|---|
| 156 | return self._verify_status("empty") |
|---|
| 157 | |
|---|
| 158 | def get_unknown(self): |
|---|
| 159 | return self._verify_status("unknown") |
|---|
| 160 | |
|---|
| 161 | def get_saved(self): |
|---|
| 162 | return self._verify_status("saved") |
|---|
| 163 | |
|---|
| 164 | def get_scan_failed(self): |
|---|
| 165 | return self._verify_status("scan_failed") |
|---|
| 166 | |
|---|
| 167 | def get_search_loaded(self): |
|---|
| 168 | return self._verify_status("search_loaded") |
|---|
| 169 | |
|---|
| 170 | |
|---|
| 171 | status = property(get_status, set_status) |
|---|
| 172 | saved = property(get_saved) |
|---|
| 173 | unsaved_unchanged = property(get_unsaved_unchanged) |
|---|
| 174 | unsaved_changed = property(get_unsaved_changed) |
|---|
| 175 | loaded_unchanged = property(get_loaded_unchanged) |
|---|
| 176 | loaded_changed = property(get_loaded_changed) |
|---|
| 177 | parsing_result = property(get_parsing_result) |
|---|
| 178 | scanning = property(get_scanning) |
|---|
| 179 | empty = property(get_empty) |
|---|
| 180 | unknown = property(get_unknown) |
|---|
| 181 | scan_failed = property(get_scan_failed) |
|---|
| 182 | search_loaded = property(get_search_loaded) |
|---|
| 183 | |
|---|
| 184 | _status = "unknown" |
|---|
| 185 | available_status = ["saved", |
|---|
| 186 | "unsaved_unchanged", |
|---|
| 187 | "unsaved_changed", |
|---|
| 188 | "loaded_unchanged", |
|---|
| 189 | "loaded_changed", |
|---|
| 190 | "parsing_result", |
|---|
| 191 | "scanning", |
|---|
| 192 | "empty", |
|---|
| 193 | "unknown", |
|---|
| 194 | "search_loaded"] |
|---|
| 195 | |
|---|
| 196 | |
|---|
| 197 | class ScanPage(HIGVBox): |
|---|
| 198 | def __init__(self): |
|---|
| 199 | HIGVBox.__init__(self) |
|---|
| 200 | |
|---|
| 201 | # The borders are consuming too much space on Maemo. Setting it to |
|---|
| 202 | # 0 pixels while on Maemo |
|---|
| 203 | if is_maemo(): |
|---|
| 204 | self.set_border_width(0) |
|---|
| 205 | |
|---|
| 206 | self.set_spacing(0) |
|---|
| 207 | self.status = PageStatus() |
|---|
| 208 | self.status.set_empty() |
|---|
| 209 | |
|---|
| 210 | self.changes = False |
|---|
| 211 | self.comments = {} |
|---|
| 212 | |
|---|
| 213 | self.parsed = NmapParser() |
|---|
| 214 | self.top_box = HIGVBox() |
|---|
| 215 | |
|---|
| 216 | self.__create_toolbar() |
|---|
| 217 | self.__create_command_toolbar() |
|---|
| 218 | self.__create_scan_result() |
|---|
| 219 | self.disable_widgets() |
|---|
| 220 | |
|---|
| 221 | self.saved = False |
|---|
| 222 | self.saved_filename = '' |
|---|
| 223 | |
|---|
| 224 | self.top_box.set_border_width(6) |
|---|
| 225 | self.top_box.set_spacing(5) |
|---|
| 226 | |
|---|
| 227 | self.top_box._pack_noexpand_nofill(self.toolbar) |
|---|
| 228 | self.top_box._pack_noexpand_nofill(self.command_toolbar) |
|---|
| 229 | |
|---|
| 230 | self._pack_noexpand_nofill(self.top_box) |
|---|
| 231 | self._pack_expand_fill(self.scan_result) |
|---|
| 232 | |
|---|
| 233 | def target_focus(self): |
|---|
| 234 | self.toolbar.target_entry.child.grab_focus() |
|---|
| 235 | |
|---|
| 236 | def select_first_profile(self): |
|---|
| 237 | model = self.toolbar.profile_entry.get_model() |
|---|
| 238 | if not len(model): # no profiles |
|---|
| 239 | return |
|---|
| 240 | self.toolbar.profile_entry.child.set_text(model[0][0]) |
|---|
| 241 | |
|---|
| 242 | def verify_changes(self): |
|---|
| 243 | return self.__verify_comments_changes() |
|---|
| 244 | |
|---|
| 245 | def go_to_host(self, host): |
|---|
| 246 | """Go to host line on nmap output result""" |
|---|
| 247 | result_nb = self.scan_result.scan_result_notebook |
|---|
| 248 | result_nb.nmap_output.nmap_output.go_to_host(host) |
|---|
| 249 | |
|---|
| 250 | def __create_scan_result(self): |
|---|
| 251 | self.scan_result = ScanResult() |
|---|
| 252 | |
|---|
| 253 | def __create_toolbar(self): |
|---|
| 254 | self.toolbar = ScanToolbar() |
|---|
| 255 | self.toolbar.scan_button.set_sensitive(False) |
|---|
| 256 | self.empty_target = _("<target>") |
|---|
| 257 | |
|---|
| 258 | self.toolbar.target_entry.changed_handler = self.toolbar.target_entry.\ |
|---|
| 259 | connect('changed', self.refresh_command_target) |
|---|
| 260 | self.toolbar.profile_entry.connect('changed', self.refresh_command) |
|---|
| 261 | |
|---|
| 262 | self.toolbar.scan_button.connect('clicked', self.start_scan_cb) |
|---|
| 263 | |
|---|
| 264 | def __create_command_toolbar(self): |
|---|
| 265 | self.command_toolbar = ScanCommandToolbar() |
|---|
| 266 | self.command_toolbar.command_entry.connect('activate', |
|---|
| 267 | lambda x: self.toolbar.scan_button.clicked()) |
|---|
| 268 | |
|---|
| 269 | # This variable says if the command at command entry was edited by user |
|---|
| 270 | self.command_edited = False |
|---|
| 271 | |
|---|
| 272 | |
|---|
| 273 | # When user clicks insite the command entry for edition |
|---|
| 274 | self.command_toolbar.command_entry.connect("focus-in-event", |
|---|
| 275 | self.remember_command) |
|---|
| 276 | |
|---|
| 277 | # When user gets out of the command entry after edition |
|---|
| 278 | self.command_toolbar.command_entry.connect("focus-out-event", |
|---|
| 279 | self.check_command) |
|---|
| 280 | |
|---|
| 281 | def remember_command(self, widget, extra=None): |
|---|
| 282 | # User is inside command entry, probably editing it... |
|---|
| 283 | |
|---|
| 284 | # Target may be empty |
|---|
| 285 | self.old_target = self.toolbar.target_entry.selected_target |
|---|
| 286 | |
|---|
| 287 | if not self.old_target: |
|---|
| 288 | self.old_target = self.empty_target |
|---|
| 289 | |
|---|
| 290 | self.old_full_command = self.command_toolbar.command_entry.get_text() |
|---|
| 291 | self.old_command = self.old_full_command.split(self.old_target)[0] |
|---|
| 292 | |
|---|
| 293 | |
|---|
| 294 | def check_command(self, widget, extra=None): |
|---|
| 295 | # User has left command entry. Verify if something has changed! |
|---|
| 296 | new_command = self.command_toolbar.command |
|---|
| 297 | |
|---|
| 298 | def disable_widgets(self): |
|---|
| 299 | self.scan_result.set_sensitive(False) |
|---|
| 300 | |
|---|
| 301 | def enable_widgets(self): |
|---|
| 302 | self.scan_result.set_sensitive(True) |
|---|
| 303 | |
|---|
| 304 | def refresh_command_target(self, widget): |
|---|
| 305 | log.debug(">>> Refresh Command Target") |
|---|
| 306 | |
|---|
| 307 | profile = self.toolbar.selected_profile |
|---|
| 308 | log.debug(">>> Profile: %s" % profile) |
|---|
| 309 | |
|---|
| 310 | if profile != '': |
|---|
| 311 | target = self.toolbar.selected_target |
|---|
| 312 | if target == '': |
|---|
| 313 | self.toolbar.scan_button.set_sensitive(False) |
|---|
| 314 | else: |
|---|
| 315 | self.toolbar.scan_button.set_sensitive(True) |
|---|
| 316 | |
|---|
| 317 | try: |
|---|
| 318 | cmd_profile = CommandProfile() |
|---|
| 319 | command = cmd_profile.get_command(profile) % target |
|---|
| 320 | del(cmd_profile) |
|---|
| 321 | |
|---|
| 322 | self.command_toolbar.command = command |
|---|
| 323 | except ProfileNotFound: |
|---|
| 324 | pass # Go without a profile |
|---|
| 325 | except TypeError: |
|---|
| 326 | pass # The target is empty... |
|---|
| 327 | #self.profile_not_found_dialog() |
|---|
| 328 | |
|---|
| 329 | def refresh_command(self, widget): |
|---|
| 330 | #log.debug(">>> Refresh Command") |
|---|
| 331 | profile = self.toolbar.selected_profile |
|---|
| 332 | target = self.toolbar.selected_target |
|---|
| 333 | |
|---|
| 334 | #log.debug(">>> Profile: %s" % profile) |
|---|
| 335 | #log.debug(">>> Target: %s" % target) |
|---|
| 336 | |
|---|
| 337 | if target == '': |
|---|
| 338 | target = self.empty_target |
|---|
| 339 | |
|---|
| 340 | try: |
|---|
| 341 | cmd_profile = CommandProfile() |
|---|
| 342 | command = cmd_profile.get_command(profile) % target |
|---|
| 343 | del(cmd_profile) |
|---|
| 344 | |
|---|
| 345 | # scan button must be enable if -iR or -iL options are passed |
|---|
| 346 | if command.find('-iR') != -1 or command.find('-iL') != -1: |
|---|
| 347 | self.toolbar.scan_button.set_sensitive(True) |
|---|
| 348 | |
|---|
| 349 | # For these nmap options, target is unecessary. |
|---|
| 350 | # Removes unnecessary target from the command |
|---|
| 351 | command = command.replace(target,'').strip() |
|---|
| 352 | elif target != self.empty_target: |
|---|
| 353 | self.toolbar.scan_button.set_sensitive(True) |
|---|
| 354 | else: |
|---|
| 355 | self.toolbar.scan_button.set_sensitive(False) |
|---|
| 356 | |
|---|
| 357 | self.command_toolbar.command = command |
|---|
| 358 | except ProfileNotFound: |
|---|
| 359 | pass |
|---|
| 360 | #self.profile_not_found_dialog() |
|---|
| 361 | except TypeError: |
|---|
| 362 | pass # That means that the command string convertion "%" didn't work |
|---|
| 363 | |
|---|
| 364 | def profile_not_found_dialog(self): |
|---|
| 365 | warn_dialog = HIGAlertDialog(message_format=_("Profile not found!"), |
|---|
| 366 | secondary_text=_("The profile name you selected/typed " |
|---|
| 367 | "couldn't be found, and probably doesn't exist. " |
|---|
| 368 | "Please, check the profile name and try again."), |
|---|
| 369 | type=gtk.MESSAGE_QUESTION) |
|---|
| 370 | warn_dialog.run() |
|---|
| 371 | warn_dialog.destroy() |
|---|
| 372 | |
|---|
| 373 | |
|---|
| 374 | def start_scan_cb(self, widget=None): |
|---|
| 375 | if not self.toolbar.scan_button.get_property("sensitive"): |
|---|
| 376 | return |
|---|
| 377 | |
|---|
| 378 | target = self.toolbar.selected_target |
|---|
| 379 | command = self.command_toolbar.command |
|---|
| 380 | profile = self.toolbar.selected_profile |
|---|
| 381 | |
|---|
| 382 | log.debug(">>> Start Scan:") |
|---|
| 383 | log.debug(">>> Target: '%s'" % target) |
|---|
| 384 | log.debug(">>> Profile: '%s'" % profile) |
|---|
| 385 | log.debug(">>> Command: '%s'" % command) |
|---|
| 386 | if expose_conf.page_inside: |
|---|
| 387 | if target and profile: |
|---|
| 388 | self.set_tab_label("%s on %s" %(profile, target)) |
|---|
| 389 | elif target: |
|---|
| 390 | self.set_tab_label("Scan on %s" % target) |
|---|
| 391 | elif profile: |
|---|
| 392 | self.set_tab_label(profile) |
|---|
| 393 | |
|---|
| 394 | if target != '': |
|---|
| 395 | self.toolbar.add_new_target(target) |
|---|
| 396 | |
|---|
| 397 | # TODO: Fix this workarround. The following line will set back the |
|---|
| 398 | # correct command to be executed after the refresh_command_target |
|---|
| 399 | # method that will be called by the targetcombo update method. |
|---|
| 400 | self.command_toolbar.command = command |
|---|
| 401 | |
|---|
| 402 | if (command.find("-iR") == -1 and command.find("-iL") == -1): |
|---|
| 403 | if command.find("<target>") > 0: |
|---|
| 404 | warn_dialog = HIGAlertDialog( |
|---|
| 405 | message_format=_("No Target Host!"), |
|---|
| 406 | secondary_text=_("Target specification is mandatory. " |
|---|
| 407 | "Either by an address in the target input box or " |
|---|
| 408 | "through the '-iR' and '-iL' nmap options. " |
|---|
| 409 | "Aborting scan."), type=gtk.MESSAGE_ERROR) |
|---|
| 410 | warn_dialog.run() |
|---|
| 411 | warn_dialog.destroy() |
|---|
| 412 | return |
|---|
| 413 | |
|---|
| 414 | if command != '': |
|---|
| 415 | # Setting status to scanning |
|---|
| 416 | self.status.set_scanning() |
|---|
| 417 | self.execute_command(command) |
|---|
| 418 | else: |
|---|
| 419 | warn_dialog = HIGAlertDialog( |
|---|
| 420 | message_format=_("Empty Nmap Command!"), |
|---|
| 421 | secondary_text=_("There is no command to execute! " |
|---|
| 422 | "Maybe the selected/typed profile doesn't exist. " |
|---|
| 423 | "Please, check the profile name or type the nmap " |
|---|
| 424 | "command you would like to execute."), |
|---|
| 425 | type=gtk.MESSAGE_ERROR) |
|---|
| 426 | warn_dialog.run() |
|---|
| 427 | warn_dialog.destroy() |
|---|
| 428 | |
|---|
| 429 | def close_tab(self): |
|---|
| 430 | try: |
|---|
| 431 | gobject.source_remove(self.verify_thread_timeout_id) |
|---|
| 432 | except: |
|---|
| 433 | pass |
|---|
| 434 | |
|---|
| 435 | def collect_umit_info(self): |
|---|
| 436 | profile = CommandProfile() |
|---|
| 437 | profile_name = self.toolbar.selected_profile |
|---|
| 438 | |
|---|
| 439 | self.parsed.target = self.toolbar.get_target() |
|---|
| 440 | self.parsed.profile_name = profile_name |
|---|
| 441 | self.parsed.nmap_command = self.command_toolbar.get_command() |
|---|
| 442 | self.parsed.profile = profile.get_command(profile_name) |
|---|
| 443 | self.parsed.profile_hint = profile.get_hint(profile_name) |
|---|
| 444 | self.parsed.profile_description = profile.get_description(profile_name) |
|---|
| 445 | self.parsed.profile_annotation = profile.get_annotation(profile_name) |
|---|
| 446 | self.parsed.profile_options = profile.get_options(profile_name) |
|---|
| 447 | |
|---|
| 448 | del(profile) # XXX not needed, will remove on next commit if no one |
|---|
| 449 | # complains. |
|---|
| 450 | |
|---|
| 451 | if hasattr(self, "command_execution"): |
|---|
| 452 | self.parsed.nmap_output = self.command_execution.get_raw_output() |
|---|
| 453 | elif not self.parsed.nmap_output: |
|---|
| 454 | self.parsed.nmap_output = self.scan_result.get_nmap_output() |
|---|
| 455 | |
|---|
| 456 | def kill_scan(self): |
|---|
| 457 | try: |
|---|
| 458 | self.command_execution.kill() |
|---|
| 459 | except AttributeError: |
|---|
| 460 | pass |
|---|
| 461 | |
|---|
| 462 | self.scan_result.clear_nmap_output() |
|---|
| 463 | self.scan_result.clear_host_view() |
|---|
| 464 | self.status.set_empty() |
|---|
| 465 | self.disable_widgets() |
|---|
| 466 | |
|---|
| 467 | def execute_command(self, command): |
|---|
| 468 | log.critical("execute_command %s" % command) |
|---|
| 469 | try: |
|---|
| 470 | alive = self.command_execution.scan_state() |
|---|
| 471 | if alive: |
|---|
| 472 | warn_dialog = HIGAlertDialog( |
|---|
| 473 | message_format=_("Scan has not finished yet"), |
|---|
| 474 | secondary_text=_("Another scan is running in " |
|---|
| 475 | "the background. To start another scan and kill " |
|---|
| 476 | "the old one, click Ok. To wait for the " |
|---|
| 477 | "conclusion of the old scan, choose Cancel."), |
|---|
| 478 | type=gtk.MESSAGE_QUESTION, buttons=gtk.BUTTONS_OK_CANCEL) |
|---|
| 479 | response = warn_dialog.run() |
|---|
| 480 | warn_dialog.destroy() |
|---|
| 481 | |
|---|
| 482 | if response == gtk.RESPONSE_OK: |
|---|
| 483 | # Kill current scan, and let the another one to be created |
|---|
| 484 | self.kill_scan() |
|---|
| 485 | else: |
|---|
| 486 | return |
|---|
| 487 | except: |
|---|
| 488 | pass |
|---|
| 489 | |
|---|
| 490 | self.command_execution = NmapCommand(command) |
|---|
| 491 | |
|---|
| 492 | try: |
|---|
| 493 | self.command_execution.run_scan() |
|---|
| 494 | except Exception, msg: |
|---|
| 495 | warn_dialog = HIGAlertDialog( |
|---|
| 496 | message_format=_("Command is missing!"), |
|---|
| 497 | secondary_text=_("It seems that your profile's command " |
|---|
| 498 | "is missing or something else went wrong. Please, " |
|---|
| 499 | "try to remove and recreate your profile."), |
|---|
| 500 | type=gtk.MESSAGE_ERROR) |
|---|
| 501 | warn_dialog.run() |
|---|
| 502 | warn_dialog.destroy() |
|---|
| 503 | |
|---|
| 504 | # Ask NmapOutputViewer to show/refresh nmap output from given file |
|---|
| 505 | self.scan_result.show_nmap_output(\ |
|---|
| 506 | self.command_execution.get_output_file()) |
|---|
| 507 | |
|---|
| 508 | # Set a "EXECUTING" icon to host list |
|---|
| 509 | self.scan_result.set_hosts({SCANNING: {'stock': gtk.STOCK_EXECUTE, |
|---|
| 510 | 'action': None}}) |
|---|
| 511 | self.scan_result.set_services({SCANNING:{'action': None}}) |
|---|
| 512 | |
|---|
| 513 | # Clear port list, to remove old information |
|---|
| 514 | self.scan_result.clear_port_list() |
|---|
| 515 | |
|---|
| 516 | # When scan starts, change to nmap output view tab and refresh output |
|---|
| 517 | self.scan_result.change_to_nmap_output_tab() |
|---|
| 518 | self.scan_result.refresh_nmap_output() |
|---|
| 519 | |
|---|
| 520 | self.enable_widgets() |
|---|
| 521 | |
|---|
| 522 | # Add a timeout function |
|---|
| 523 | self.verify_thread_timeout_id = gobject.timeout_add(2000, |
|---|
| 524 | self.verify_execution) |
|---|
| 525 | |
|---|
| 526 | def verify_execution(self): |
|---|
| 527 | # Using new subprocess style |
|---|
| 528 | try: |
|---|
| 529 | alive = self.command_execution.scan_state() |
|---|
| 530 | except: |
|---|
| 531 | self.disable_widgets() |
|---|
| 532 | self.status.set_scan_failed() |
|---|
| 533 | self.scan_result.set_nmap_output(self.command_execution.get_error()) |
|---|
| 534 | return False |
|---|
| 535 | |
|---|
| 536 | # Maybe this automatic refresh should be eliminated |
|---|
| 537 | # to avoid processor burning |
|---|
| 538 | self.scan_result.refresh_nmap_output() |
|---|
| 539 | |
|---|
| 540 | if alive: |
|---|
| 541 | return True |
|---|
| 542 | else: |
|---|
| 543 | self.parse_result(self.command_execution.get_xml_output_file()) |
|---|
| 544 | return False |
|---|
| 545 | |
|---|
| 546 | def load_result(self, file_to_parse): |
|---|
| 547 | #### |
|---|
| 548 | # Setting status to parsing_result |
|---|
| 549 | self.status.set_parsing_result() |
|---|
| 550 | #### |
|---|
| 551 | self._parse(file_to_parse=file_to_parse) |
|---|
| 552 | |
|---|
| 553 | #### |
|---|
| 554 | # Setting status to loaded_unchanged |
|---|
| 555 | self.status.set_loaded_unchanged() |
|---|
| 556 | #### |
|---|
| 557 | |
|---|
| 558 | def parse_result(self, file_to_parse): |
|---|
| 559 | #### |
|---|
| 560 | # Setting status to parsing_result |
|---|
| 561 | self.status.set_parsing_result() |
|---|
| 562 | #### |
|---|
| 563 | self._parse(file_to_parse=file_to_parse) |
|---|
| 564 | |
|---|
| 565 | #### |
|---|
| 566 | # Setting status to unsaved_unchanged |
|---|
| 567 | self.status.set_unsaved_unchanged() |
|---|
| 568 | #### |
|---|
| 569 | |
|---|
| 570 | def load_from_parsed_result(self, parsed_result): |
|---|
| 571 | #### |
|---|
| 572 | # Setting status to parsing_result |
|---|
| 573 | self.status.set_parsing_result() |
|---|
| 574 | #### |
|---|
| 575 | self._parse(parsed_result=parsed_result) |
|---|
| 576 | |
|---|
| 577 | #### |
|---|
| 578 | # Setting status to unsaved_unchanged |
|---|
| 579 | self.status.set_unsaved_unchanged() |
|---|
| 580 | #### |
|---|
| 581 | |
|---|
| 582 | def _parse(self, file_to_parse=None, parsed_result=None): |
|---|
| 583 | """Called when scan is done. Verify if any host were found.""" |
|---|
| 584 | log.debug(">>> XML output file that is going to be " |
|---|
| 585 | "parsed: %s" % file_to_parse) |
|---|
| 586 | |
|---|
| 587 | self.host_view_selection = self.scan_result.get_host_selection() |
|---|
| 588 | self.service_view_selection = self.scan_result.get_service_selection() |
|---|
| 589 | |
|---|
| 590 | # All hosts details pages |
|---|
| 591 | self.host_pages = [] |
|---|
| 592 | self.changes = True |
|---|
| 593 | |
|---|
| 594 | self.host_view_selection.connect('changed', self.update_host_info) |
|---|
| 595 | self.service_view_selection.connect('changed', self.update_service_info) |
|---|
| 596 | self.scan_result.scan_host_view.clear_host_list() |
|---|
| 597 | self.hosts = {} |
|---|
| 598 | self.services = {} |
|---|
| 599 | |
|---|
| 600 | # Removed and created again to avoid host duplication problems when |
|---|
| 601 | # making multiple scans inside the same scan tab |
|---|
| 602 | try: del(self.parsed) |
|---|
| 603 | except: pass |
|---|
| 604 | |
|---|
| 605 | if file_to_parse: |
|---|
| 606 | self.parsed = NmapParser() |
|---|
| 607 | self.parsed.set_xml_file(file_to_parse) |
|---|
| 608 | try: |
|---|
| 609 | log.debug(">>> Start parsing...") |
|---|
| 610 | self.parsed.parse() |
|---|
| 611 | log.debug(">>> Successfully parsed!") |
|---|
| 612 | except: |
|---|
| 613 | log.debug(">>> An exception occourried during xml ouput " |
|---|
| 614 | "parsing") |
|---|
| 615 | try: |
|---|
| 616 | error = self.command_execution.get_error() |
|---|
| 617 | except AttributeError: |
|---|
| 618 | error = _("Couldn't retrieve the error raised by " |
|---|
| 619 | "the command!") |
|---|
| 620 | except: |
|---|
| 621 | error = _("Unknown error!") |
|---|
| 622 | |
|---|
| 623 | log.debug(">>> Error: '%s'" % error) |
|---|
| 624 | |
|---|
| 625 | # Treat root exceptions more carefully! |
|---|
| 626 | if re.findall('[rR][oO0]{2}[tT]', error): |
|---|
| 627 | need_root = HIGAlertDialog( |
|---|
| 628 | message_format=_('Root privileges are needed!'), |
|---|
| 629 | secondary_text=error) |
|---|
| 630 | need_root.run() |
|---|
| 631 | need_root.destroy() |
|---|
| 632 | else: |
|---|
| 633 | unknown_problem = HIGAlertDialog( |
|---|
| 634 | message_format=_('An unexpected error occourried!'), |
|---|
| 635 | secondary_text=error) |
|---|
| 636 | unknown_problem.run() |
|---|
| 637 | unknown_problem.destroy() |
|---|
| 638 | return |
|---|
| 639 | elif parsed_result: |
|---|
| 640 | self.parsed = parsed_result |
|---|
| 641 | |
|---|
| 642 | if int(self.parsed.get_hosts_up()): |
|---|
| 643 | for host in self.parsed.get_hosts(): |
|---|
| 644 | hostname = host.get_hostname() |
|---|
| 645 | host_page = self.set_host_details(host) |
|---|
| 646 | list_states = ["open", "filtered", "open|filtered"] |
|---|
| 647 | |
|---|
| 648 | for service in host.services: |
|---|
| 649 | name = service["service_name"] |
|---|
| 650 | state = service["port_state"] |
|---|
| 651 | |
|---|
| 652 | if state not in list_states: |
|---|
| 653 | continue |
|---|
| 654 | |
|---|
| 655 | if name not in self.services.keys(): |
|---|
| 656 | self.services[name] = {"hosts":[]} |
|---|
| 657 | |
|---|
| 658 | hs = {"host": host, "page": host_page, "hostname": hostname} |
|---|
| 659 | hs.update(service) |
|---|
| 660 | |
|---|
| 661 | self.services[name]["hosts"].append(hs) |
|---|
| 662 | |
|---|
| 663 | self.hosts[hostname] = {'host': host, 'page': host_page} |
|---|
| 664 | |
|---|
| 665 | host_details = self.hosts[hostname]['page'].host_details |
|---|
| 666 | host_info = self.hosts[hostname]['host'] |
|---|
| 667 | |
|---|
| 668 | try: |
|---|
| 669 | host_details.set_os_image( |
|---|
| 670 | get_os_logo(host.get_osmatch()['name'])) |
|---|
| 671 | except: |
|---|
| 672 | host_details.set_os_image(get_os_logo('')) |
|---|
| 673 | |
|---|
| 674 | host_details.set_vulnerability_image(get_vulnerability_logo( |
|---|
| 675 | host_info.get_open_ports())) |
|---|
| 676 | |
|---|
| 677 | icon = None |
|---|
| 678 | try:icon = get_os_icon(host.get_osmatch()['name']) |
|---|
| 679 | except:icon = get_os_icon('') |
|---|
| 680 | |
|---|
| 681 | self.scan_result.scan_host_view.add_host( |
|---|
| 682 | {hostname: {'stock': icon, 'action':None}}) |
|---|
| 683 | |
|---|
| 684 | # Select the first host found |
|---|
| 685 | self.host_view_selection.select_iter( |
|---|
| 686 | self.scan_result.scan_host_view.host_list.get_iter_root()) |
|---|
| 687 | |
|---|
| 688 | self.scan_result.scan_host_view.set_services(self.services.keys()) |
|---|
| 689 | |
|---|
| 690 | try: |
|---|
| 691 | # And them, we update the nmap output! ;) |
|---|
| 692 | self.scan_result.scan_result_notebook.nmap_output.\ |
|---|
| 693 | nmap_output.refresh_output() |
|---|
| 694 | except: |
|---|
| 695 | # Put saved nmap output |
|---|
| 696 | self.scan_result.scan_result_notebook.nmap_output.\ |
|---|
| 697 | nmap_output.text_buffer.\ |
|---|
| 698 | set_text('\n'.join(self.parsed.get_nmap_output().\ |
|---|
| 699 | split('\\n'))) |
|---|
| 700 | |
|---|
| 701 | target = self.parsed.get_target() |
|---|
| 702 | |
|---|
| 703 | if target != '': |
|---|
| 704 | self.toolbar.target_entry.child.set_text(target) |
|---|
| 705 | |
|---|
| 706 | profile_name = self.parsed.profile_name |
|---|
| 707 | |
|---|
| 708 | if profile_name != '': |
|---|
| 709 | profile = CommandProfile() |
|---|
| 710 | profile.add_profile(self.parsed.profile_name, |
|---|
| 711 | command=self.parsed.profile, |
|---|
| 712 | hint=self.parsed.profile_hint, |
|---|
| 713 | options=self.parsed.profile_options, |
|---|
| 714 | description=self.parsed.profile_description, |
|---|
| 715 | annotation=self.parsed.profile_annotation) |
|---|
| 716 | del(profile) |
|---|
| 717 | |
|---|
| 718 | self.toolbar.profile_entry.update() |
|---|
| 719 | |
|---|
| 720 | self.toolbar.selected_profile = profile_name |
|---|
| 721 | else: |
|---|
| 722 | pass |
|---|
| 723 | # The line bellow seens to be useless |
|---|
| 724 | #self.command_toolbar.command = self.parsed.get_nmap_command() |
|---|
| 725 | |
|---|
| 726 | self.collect_umit_info() |
|---|
| 727 | self.switch_scan_details(self.__set_scan_info()) |
|---|
| 728 | self.check_fingerprints() |
|---|
| 729 | |
|---|
| 730 | def check_fingerprints(self): |
|---|
| 731 | re_host = re.compile(r"(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})") |
|---|
| 732 | re_os_fp = re.compile(r"(SInfo.*\);)") |
|---|
| 733 | re_service_fp = re.compile(r"(SF-Port.*\);)") |
|---|
| 734 | re_service_number = re.compile(r"SF-Port(\d+)") |
|---|
| 735 | |
|---|
| 736 | nmap_output = self.parsed.nmap_output.split("\\n\\n") |
|---|
| 737 | fingerprints = {} |
|---|
| 738 | current_ip = None |
|---|
| 739 | |
|---|
| 740 | for line in nmap_output: |
|---|
| 741 | match_host = re_host.search(line) |
|---|
| 742 | match_os = re_os_fp.search(line) |
|---|
| 743 | match_service = re_service_fp.search(line) |
|---|
| 744 | |
|---|
| 745 | if match_host: |
|---|
| 746 | current_ip = match_host.groups()[0] |
|---|
| 747 | if match_os: |
|---|
| 748 | if current_ip not in fingerprints.keys(): |
|---|
| 749 | fingerprints[current_ip] = {} |
|---|
| 750 | |
|---|
| 751 | fingerprints[current_ip]["os"] = match_os.groups()[0] |
|---|
| 752 | if match_service: |
|---|
| 753 | if current_ip not in fingerprints.keys(): |
|---|
| 754 | fingerprints[current_ip] = {} |
|---|
| 755 | |
|---|
| 756 | fp = match_service.groups()[0] |
|---|
| 757 | |
|---|
| 758 | fingerprints[current_ip]["service"] = fp |
|---|
| 759 | |
|---|
| 760 | #port = re_service_port.search(fp).groups()[0] |
|---|
| 761 | #fingerprints[current_ip]["service_port"] = port |
|---|
| 762 | #fingerprints[current_ip]["service_name"] |
|---|
| 763 | |
|---|
| 764 | |
|---|
| 765 | # In the future, umit is going to catch the fingerprint informations |
|---|
| 766 | # and load a umitGUI.OSFingerprintReport or |
|---|
| 767 | # umitGUI.ServiceFingerprintReport window that will let user |
|---|
| 768 | # to directely register a new fingerprint from the interface |
|---|
| 769 | # without worrying about moving his mouse off the interface. |
|---|
| 770 | |
|---|
| 771 | """ |
|---|
| 772 | for fp in fingerprints: |
|---|
| 773 | # We've found a new fp! Please contribute dialog |
|---|
| 774 | # If ok, show the form, sending the ip and fingerprint. |
|---|
| 775 | """ |
|---|
| 776 | |
|---|
| 777 | key_num = len(fingerprints.keys()) |
|---|
| 778 | dialog_text = ("Umit has found that %s. The submission and " |
|---|
| 779 | "registration of fingerprints are very important for you " |
|---|
| 780 | "and the Nmap project! If you would like to contribute " |
|---|
| 781 | "to see your favorite network mapper recognizing those " |
|---|
| 782 | "fingerprints in the future, choose the Ok button, and a " |
|---|
| 783 | "submission page will be open in your default web browser " |
|---|
| 784 | "with instructions about how to proceed on this registration.") |
|---|
| 785 | |
|---|
| 786 | if key_num == 1: |
|---|
| 787 | msg = _("you network scan discovered an unknown fingerprint " |
|---|
| 788 | "sent by the host %s") % fingerprints.keys()[0] |
|---|
| 789 | |
|---|
| 790 | self.show_contribute_dialog(dialog_text % msg) |
|---|
| 791 | |
|---|
| 792 | elif key_num > 1: |
|---|
| 793 | msg = _("your network scan discovered several unknown " |
|---|
| 794 | "fingerprints sent by the follwoing hosts: ") |
|---|
| 795 | for i in fingerprints: |
|---|
| 796 | msg += "%s, " % i |
|---|
| 797 | msg = msg[:-2] |
|---|
| 798 | |
|---|
| 799 | self.show_contribute_dialog(dialog_text % msg) |
|---|
| 800 | |
|---|
| 801 | |
|---|
| 802 | def show_contribute_dialog(self, dialog_text): |
|---|
| 803 | contribute_dialog = HIGAlertDialog( |
|---|
| 804 | message_format=_("Unrecognized Services/OS Fingerprints Found!"), |
|---|
| 805 | secondary_text=dialog_text, type=gtk.MESSAGE_QUESTION, |
|---|
| 806 | buttons=gtk.BUTTONS_OK_CANCEL) |
|---|
| 807 | response = contribute_dialog.run() |
|---|
| 808 | contribute_dialog.destroy() |
|---|
| 809 | |
|---|
| 810 | if response == gtk.RESPONSE_OK: |
|---|
| 811 | webbrowser.open("http://www.insecure.org/nmap/submit/") |
|---|
| 812 | |
|---|
| 813 | |
|---|
| 814 | def __verify_comments_changes(self): |
|---|
| 815 | try: |
|---|
| 816 | for hostname in self.hosts: |
|---|
| 817 | host_details = self.hosts[hostname]['page'].host_details |
|---|
| 818 | if host_details.get_comment() != self.comments[hostname]: |
|---|
| 819 | log.debug("Changes on comments") |
|---|
| 820 | self.changes = True |
|---|
| 821 | return True |
|---|
| 822 | except: |
|---|
| 823 | return False |
|---|
| 824 | |
|---|
| 825 | def __set_scan_info(self): |
|---|
| 826 | self.clean_scan_details() |
|---|
| 827 | run_details = ScanRunDetailsPage() |
|---|
| 828 | |
|---|
| 829 | run_details.set_command_info( |
|---|
| 830 | {'command': self.parsed.get_nmap_command(), |
|---|
| 831 | 'version': self.parsed.get_scanner_version(), |
|---|
| 832 | 'verbose': self.parsed.get_verbose_level(), |
|---|
| 833 | 'debug': self.parsed.get_debugging_level() |
|---|
| 834 | }) |
|---|
| 835 | |
|---|
| 836 | run_details.set_general_info( |
|---|
| 837 | {'start': self.parsed.get_formated_date(), |
|---|
| 838 | 'finish': self.parsed.get_formated_finish_date(), |
|---|
| 839 | 'hosts_up': str(self.parsed.get_hosts_up()), |
|---|
| 840 | 'hosts_down': str(self.parsed.get_hosts_down()), |
|---|
| 841 | 'hosts_scanned': str(self.parsed.get_hosts_scanned()), |
|---|
| 842 | 'open_ports': str(self.parsed.get_open_ports()), |
|---|
| 843 | 'filtered_ports': str(self.parsed.get_filtered_ports()), |
|---|
| 844 | 'closed_ports': str(self.parsed.get_closed_ports()) |
|---|
| 845 | }) |
|---|
| 846 | |
|---|
| 847 | run_details.set_scan_infos(self.parsed.get_scaninfo()) |
|---|
| 848 | |
|---|
| 849 | return run_details |
|---|
| 850 | |
|---|
| 851 | def update_host_info(self, widget): |
|---|
| 852 | self.scan_result.scan_result_notebook.port_mode() |
|---|
| 853 | |
|---|
| 854 | model_host_list, selection = widget.get_selected_rows() |
|---|
| 855 | #host_objs = [self.hosts[model_host_list[i[0]][1]] for i in selection] |
|---|
| 856 | host_objs = [] |
|---|
| 857 | for i in selection: |
|---|
| 858 | key = model_host_list[i[0]][1] |
|---|
| 859 | if self.hosts.has_key(key): |
|---|
| 860 | host_objs.append(self.hosts[key]) |
|---|
| 861 | |
|---|
| 862 | self.clean_host_details() |
|---|
| 863 | |
|---|
| 864 | if len(host_objs) == 1: |
|---|
| 865 | self.set_single_host_port(host_objs[0]['host']) |
|---|
| 866 | self.switch_host_details(host_objs[0]['page']) |
|---|
| 867 | else: |
|---|
| 868 | self.set_multiple_host_port(host_objs) |
|---|
| 869 | self.switch_host_details(self.set_multiple_host_details(host_objs)) |
|---|
| 870 | |
|---|
| 871 | # Switch nmap output to show first host occourrence |
|---|
| 872 | try: |
|---|
| 873 | self.go_to_host(host_objs[0]['host'].get_hostname()) |
|---|
| 874 | except IndexError: |
|---|
| 875 | pass |
|---|
| 876 | |
|---|
| 877 | def update_service_info(self, widget): |
|---|
| 878 | self.scan_result.scan_result_notebook.host_mode() |
|---|
| 879 | |
|---|
| 880 | model_service_list, selection = widget.get_selected_rows() |
|---|
| 881 | serv_objs = [] |
|---|
| 882 | for i in selection: |
|---|
| 883 | key = model_service_list[i[0]][0] |
|---|
| 884 | if self.services.has_key(key): |
|---|
| 885 | serv_objs.append(self.services[key]) |
|---|
| 886 | |
|---|
| 887 | # Removing current widgets from the host details page |
|---|
| 888 | self.clean_host_details() |
|---|
| 889 | |
|---|
| 890 | if len(serv_objs) == 1: |
|---|
| 891 | self.set_single_service_host(serv_objs[0]['hosts']) |
|---|
| 892 | self.switch_host_details([page["page"] \ |
|---|
| 893 | for page in serv_objs[0]['hosts']]) |
|---|
| 894 | else: |
|---|
| 895 | servs = [] |
|---|
| 896 | for s in serv_objs: |
|---|
| 897 | servs.append({"service_name": s["hosts"][0]["service_name"], |
|---|
| 898 | "hosts": s["hosts"]}) |
|---|
| 899 | |
|---|
| 900 | self.set_multiple_service_host(servs) |
|---|
| 901 | |
|---|
| 902 | pages = [] |
|---|
| 903 | for serv in [serv["hosts"] for serv in serv_objs]: |
|---|
| 904 | for h in serv: |
|---|
| 905 | # Prevent from adding a host more then once |
|---|
| 906 | if h["page"] not in pages: |
|---|
| 907 | pages.append(h["page"]) |
|---|
| 908 | |
|---|
| 909 | self.switch_host_details(pages) |
|---|
| 910 | |
|---|
| 911 | # Change scan tab to "Ports/Hosts" |
|---|
| 912 | self.scan_result.scan_result_notebook.set_current_page(0) |
|---|
| 913 | |
|---|
| 914 | def clean_host_details(self): |
|---|
| 915 | parent = self.scan_result.scan_result_notebook.host_details_vbox |
|---|
| 916 | children = parent.get_children() |
|---|
| 917 | |
|---|
| 918 | for child in children: |
|---|
| 919 | parent.remove(child) |
|---|
| 920 | |
|---|
| 921 | def clean_scan_details(self): |
|---|
| 922 | parent = self.scan_result.scan_result_notebook.scan_details_vbox |
|---|
| 923 | children = parent.get_children() |
|---|
| 924 | |
|---|
| 925 | for child in children: |
|---|
| 926 | parent.remove(child) |
|---|
| 927 | |
|---|
| 928 | def switch_host_details(self, page): |
|---|
| 929 | if type(page) == type([]): |
|---|
| 930 | if len(page) > 1: |
|---|
| 931 | for p in page: |
|---|
| 932 | p.hide() |
|---|
| 933 | p.set_expanded(False) |
|---|
| 934 | result_nb = self.scan_result.scan_result_notebook |
|---|
| 935 | result_nb.host_details_vbox._pack_noexpand_nofill(p) |
|---|
| 936 | |
|---|
| 937 | result_nb = self.scan_result.scan_result_notebook |
|---|
| 938 | result_nb.host_details_vbox.show_all() |
|---|
| 939 | return |
|---|
| 940 | elif len(page) == 1: |
|---|
| 941 | page = page[0] |
|---|
| 942 | |
|---|
| 943 | try: |
|---|
| 944 | page.hide() |
|---|
| 945 | except: # XXX except what ? |
|---|
| 946 | pass |
|---|
| 947 | else: |
|---|
| 948 | result_nb = self.scan_result.scan_result_notebook |
|---|
| 949 | result_nb.host_details_vbox._pack_noexpand_nofill(page) |
|---|
| 950 | page.set_expanded(True) |
|---|
| 951 | page.show_all() |
|---|
| 952 | |
|---|
| 953 | def switch_scan_details(self, page): |
|---|
| 954 | # Removing current widget from the host details page |
|---|
| 955 | result_nb = self.scan_result.scan_result_notebook |
|---|
| 956 | result_nb.scan_details_vbox._pack_noexpand_nofill(page) |
|---|
| 957 | page.show_all() |
|---|
| 958 | |
|---|
| 959 | def set_multiple_host_details(self, host_list): |
|---|
| 960 | hosts = [] |
|---|
| 961 | for h in host_list: |
|---|
| 962 | hosts.append(h['page']) |
|---|
| 963 | |
|---|
| 964 | return hosts |
|---|
| 965 | |
|---|
| 966 | def _save_comment(self, widget, extra, host_id): |
|---|
| 967 | if self.status.unsaved_unchanged: |
|---|
| 968 | self.status.set_unsaved_changed() |
|---|
| 969 | elif self.status.loaded_unchanged or self.status.saved: |
|---|
| 970 | self.status.set_loaded_changed() |
|---|
| 971 | |
|---|
| 972 | # Catch a comment and record it to be saved posteriorly |
|---|
| 973 | log.debug(">>> Catching edited comment to be saved posteriorly.") |
|---|
| 974 | buff = widget.get_buffer() |
|---|
| 975 | self.parsed.set_host_comment(host_id, |
|---|
| 976 | buff.get_text(buff.get_start_iter(), buff.get_end_iter())) |
|---|
| 977 | |
|---|
| 978 | def set_host_details(self, host): |
|---|
| 979 | # Start connecting event to automatically update comments, target |
|---|
| 980 | # and profile infos |
|---|
| 981 | host_page = ScanHostDetailsPage(host.get_hostname()) |
|---|
| 982 | host_details = host_page.host_details |
|---|
| 983 | |
|---|
| 984 | log.debug(">>> Setting host details") |
|---|
| 985 | log.debug(">>> Hostname: %s" % host.get_hostname()) |
|---|
| 986 | log.debug(">>> Comment: %s" % self.parsed.get_host_comment(host.id)) |
|---|
| 987 | host_details.set_comment(self.parsed.get_host_comment(host.id)) |
|---|
| 988 | |
|---|
| 989 | # Setting events to automatically record the commentary to be saved |
|---|
| 990 | host_page.host_details.comment_txt_vw.connect("insert-at-cursor", |
|---|
| 991 | self._save_comment, host.id) |
|---|
| 992 | host_page.host_details.comment_txt_vw.connect("focus-out-event", |
|---|
| 993 | self._save_comment, host.id) |
|---|
| 994 | |
|---|
| 995 | |
|---|
| 996 | self.comments[host.get_hostname()] = host.get_comment() |
|---|
| 997 | |
|---|
| 998 | uptime = host.get_uptime() |
|---|
| 999 | |
|---|
| 1000 | host_details.set_host_status({'state':host.get_state(), |
|---|
| 1001 | 'open':str(host.get_open_ports()), |
|---|
| 1002 | 'filtered':str(host.get_filtered_ports()), |
|---|
| 1003 | 'closed':str(host.get_closed_ports()), |
|---|
| 1004 | 'scanned':str(host.get_scanned_ports()), |
|---|
| 1005 | 'uptime':uptime['seconds'], |
|---|
| 1006 | 'lastboot':uptime['lastboot']}) |
|---|
| 1007 | |
|---|
| 1008 | |
|---|
| 1009 | ipv4 = host.get_ip().get('addr', '') |
|---|
| 1010 | ipv6 = host.get_ipv6().get('addr', '') |
|---|
| 1011 | mac = host.get_mac().get('addr', '') |
|---|
| 1012 | |
|---|
| 1013 | host_details.set_addresses({'ipv4': ipv4, 'ipv6': ipv6, 'mac': mac}) |
|---|
| 1014 | host_details.set_hostnames(host.get_hostnames()) |
|---|
| 1015 | |
|---|
| 1016 | os = host.get_osmatch() |
|---|
| 1017 | if os: |
|---|
| 1018 | os['portsused'] = host.get_ports_used() |
|---|
| 1019 | os['osclass'] = host.get_osclasses() |
|---|
| 1020 | |
|---|
| 1021 | host_details.set_os(os) |
|---|
| 1022 | host_details.set_tcpseq(host.get_tcpsequence()) |
|---|
| 1023 | host_details.set_ipseq(host.get_ipidsequence()) |
|---|
| 1024 | host_details.set_tcptsseq(host.get_tcptssequence()) |
|---|
| 1025 | |
|---|
| 1026 | return host_page |
|---|
| 1027 | |
|---|
| 1028 | def set_single_host_port(self, host): |
|---|
| 1029 | host_page = self.scan_result.scan_result_notebook.open_ports.host |
|---|
| 1030 | host_page.switch_port_to_list_store() |
|---|
| 1031 | |
|---|
| 1032 | p = host.get_ports() |
|---|
| 1033 | ports = [] |
|---|
| 1034 | for port in p: |
|---|
| 1035 | ports += port['port'] |
|---|
| 1036 | |
|---|
| 1037 | host_page.clear_port_list() |
|---|
| 1038 | for p in ports: |
|---|
| 1039 | host_page.add_port([self.findout_service_icon(p), |
|---|
| 1040 | int(p.get('portid', '0')), |
|---|
| 1041 | p.get('protocol', ''), |
|---|
| 1042 | p.get('port_state', ''), |
|---|
| 1043 | p.get('service_name', ''), |
|---|
| 1044 | p.get('service_product', '')]) |
|---|
| 1045 | |
|---|
| 1046 | def set_single_service_host(self, service): |
|---|
| 1047 | host_page = self.scan_result.scan_result_notebook.open_ports.host |
|---|
| 1048 | host_page.switch_host_to_list_store() |
|---|
| 1049 | host_page.clear_host_list() |
|---|
| 1050 | |
|---|
| 1051 | for h in service: |
|---|
| 1052 | host_page.add_host([self.findout_service_icon(h), |
|---|
| 1053 | h.get('hostname', ''), |
|---|
| 1054 | int(h.get('portid', '0')), |
|---|
| 1055 | h.get('protocol', ''), |
|---|
| 1056 | h.get('port_state', ''), |
|---|
| 1057 | h.get('service_product', ''), |
|---|
| 1058 | h.get('service_version', '')]) |
|---|
| 1059 | |
|---|
| 1060 | |
|---|
| 1061 | def set_multiple_host_port(self, host_list): |
|---|
| 1062 | host_page = self.scan_result.scan_result_notebook.open_ports.host |
|---|
| 1063 | host_page.switch_port_to_tree_store() |
|---|
| 1064 | host_page.clear_port_tree() |
|---|
| 1065 | |
|---|
| 1066 | for host in host_list: |
|---|
| 1067 | parent = host_page.port_tree.append(None, |
|---|
| 1068 | [host['host'].get_hostname(), '', 0, '', '', '', '']) |
|---|
| 1069 | for port in host['host'].get_ports(): |
|---|
| 1070 | for p in port.get('port', []): |
|---|
| 1071 | host_page.port_tree.append(parent, |
|---|
| 1072 | ['', self.findout_service_icon(p), |
|---|
| 1073 | int(p.get('portid', "0")), |
|---|
| 1074 | p.get('protocol', ''), |
|---|
| 1075 | p.get('port_state', ""), |
|---|
| 1076 | p.get('service_name', _("Unknown")), |
|---|
| 1077 | p.get('service_product', "")]) |
|---|
| 1078 | |
|---|
| 1079 | def set_multiple_service_host(self, service_list): |
|---|
| 1080 | host_page = self.scan_result.scan_result_notebook.open_ports.host |
|---|
| 1081 | host_page.switch_host_to_tree_store() |
|---|
| 1082 | host_page.clear_host_tree() |
|---|
| 1083 | |
|---|
| 1084 | for host in service_list: |
|---|
| 1085 | parent = host_page.host_tree.append(None, [host['service_name'], |
|---|
| 1086 | '','',0,'','', '', '']) |
|---|
| 1087 | for h in host['hosts']: |
|---|
| 1088 | host_page.host_tree.append(parent, |
|---|
| 1089 | ['', self.findout_service_icon(h), h["hostname"], |
|---|
| 1090 | int(h.get('portid', "0")), h.get('protocol', ""), |
|---|
| 1091 | h.get('port_state', _("Unknown")), |
|---|
| 1092 | h.get('service_product', ''), h.get('service_version', |
|---|
| 1093 | _("Unknown"))]) |
|---|
| 1094 | |
|---|
| 1095 | def findout_service_icon(self, port_info): |
|---|
| 1096 | return gtk.STOCK_YES |
|---|
| 1097 | |
|---|
| 1098 | |
|---|
| 1099 | |
|---|
| 1100 | class ScanResult(gtk.HPaned): |
|---|
| 1101 | def __init__(self): |
|---|
| 1102 | gtk.HPaned.__init__(self) |
|---|
| 1103 | |
|---|
| 1104 | self.scan_host_view = ScanHostsView() |
|---|
| 1105 | self.scan_result_notebook = ScanResultNotebook() |
|---|
| 1106 | |
|---|
| 1107 | self.pack1(self.scan_host_view, True, False) |
|---|
| 1108 | self.pack2(self.scan_result_notebook, True, False) |
|---|
| 1109 | |
|---|
| 1110 | def set_nmap_output(self, msg): |
|---|
| 1111 | nmap_output = self.scan_result_notebook.nmap_output.nmap_output |
|---|
| 1112 | nmap_output.text_view.get_buffer().set_text(msg) |
|---|
| 1113 | nmap_output.update_output_colors() |
|---|
| 1114 | |
|---|
| 1115 | def clear_nmap_output(self): |
|---|
| 1116 | nmap_output = self.scan_result_notebook.nmap_output.nmap_output |
|---|
| 1117 | nmap_output.text_view.get_buffer().set_text("") |
|---|
| 1118 | |
|---|
| 1119 | def clear_host_view(self): |
|---|
| 1120 | self.set_hosts({}) |
|---|
| 1121 | |
|---|
| 1122 | def clear_service_view(self): |
|---|
| 1123 | self.set_services({}) |
|---|
| 1124 | |
|---|
| 1125 | def get_host_selection(self): |
|---|
| 1126 | return self.scan_host_view.host_view.get_selection() |
|---|
| 1127 | |
|---|
| 1128 | def get_service_selection(self): |
|---|
| 1129 | return self.scan_host_view.service_view.get_selection() |
|---|
| 1130 | |
|---|
| 1131 | def get_nmap_output(self): |
|---|
| 1132 | return self.scan_result_notebook.nmap_output.get_nmap_output() |
|---|
| 1133 | |
|---|
| 1134 | def show_nmap_output(self, file): |
|---|
| 1135 | """Ask NmapOutputViewer to show/refresh nmap output from given file.""" |
|---|
| 1136 | self.scan_result_notebook.nmap_output.nmap_output.show_nmap_output(file) |
|---|
| 1137 | |
|---|
| 1138 | def set_hosts(self, hosts_dic): |
|---|
| 1139 | """Set hosts at host list.""" |
|---|
| 1140 | self.scan_host_view.set_hosts(hosts_dic) |
|---|
| 1141 | |
|---|
| 1142 | def set_services(self, services_dic): |
|---|
| 1143 | self.scan_host_view.set_services(services_dic) |
|---|
| 1144 | |
|---|
| 1145 | def clear_port_list(self): |
|---|
| 1146 | """Clear Umit's scan result ports list.""" |
|---|
| 1147 | self.scan_result_notebook.open_ports.host.clear_port_list() |
|---|
| 1148 | |
|---|
| 1149 | def change_to_nmap_output_tab(self): |
|---|
| 1150 | """Show the nmap output tab.""" |
|---|
| 1151 | self.scan_result_notebook.set_current_page(1) |
|---|
| 1152 | |
|---|
| 1153 | def refresh_nmap_output(self): |
|---|
| 1154 | """Refresh Nmap output in nmap output tab.""" |
|---|
| 1155 | self.scan_result_notebook.nmap_output.nmap_output.refresh_output() |
|---|
| 1156 | |
|---|
| 1157 | |
|---|
| 1158 | |
|---|
| 1159 | class ScanResultNotebook(HIGNotebook): |
|---|
| 1160 | def __init__(self): |
|---|
| 1161 | HIGNotebook.__init__(self) |
|---|
| 1162 | self.set_scrollable(True) |
|---|
| 1163 | self.set_border_width(5) |
|---|
| 1164 | |
|---|
| 1165 | self.__create_widgets() |
|---|
| 1166 | self.__nmap_output_refreshing() |
|---|
| 1167 | |
|---|
| 1168 | self.append_page(self.open_ports_page, gtk.Label(_('Ports / Hosts'))) |
|---|
| 1169 | self.append_page(self.nmap_output_page, gtk.Label(_('Nmap Output'))) |
|---|
| 1170 | |
|---|
| 1171 | self.append_page(self.host_details_page, gtk.Label(_('Host Details'))) |
|---|
| 1172 | self.append_page(self.scan_details_page, gtk.Label(_('Scan Details'))) |
|---|
| 1173 | |
|---|
| 1174 | def get_nmap_output(self): |
|---|
| 1175 | return self.nmap_output.get_map_output() |
|---|
| 1176 | |
|---|
| 1177 | def host_mode(self): |
|---|
| 1178 | self.open_ports.host.host_mode() |
|---|
| 1179 | |
|---|
| 1180 | def port_mode(self): |
|---|
| 1181 | self.open_ports.host.port_mode() |
|---|
| 1182 | |
|---|
| 1183 | def __create_widgets(self): |
|---|
| 1184 | self.open_ports_page = HIGVBox() |
|---|
| 1185 | self.nmap_output_page = HIGVBox() |
|---|
| 1186 | self.host_details_page = HIGScrolledWindow() |
|---|
| 1187 | self.scan_details_page = HIGScrolledWindow() |
|---|
| 1188 | self.scan_details_vbox = HIGVBox() |
|---|
| 1189 | self.host_details_vbox = HIGVBox() |
|---|
| 1190 | |
|---|
| 1191 | self.open_ports = ScanOpenPortsPage() |
|---|
| 1192 | self.nmap_output = ScanNmapOutputPage() |
|---|
| 1193 | |
|---|
| 1194 | self.no_selected = gtk.Label(_('No host selected!')) |
|---|
| 1195 | self.host_details = self.no_selected |
|---|
| 1196 | |
|---|
| 1197 | self.no_details = gtk.Label(_('Scan is not finished yet!')) |
|---|
| 1198 | self.scan_details = self.no_details |
|---|
| 1199 | |
|---|
| 1200 | self.open_ports_page.add(self.open_ports) |
|---|
| 1201 | self.nmap_output_page.add(self.nmap_output) |
|---|
| 1202 | |
|---|
| 1203 | self.host_details_page.add_with_viewport(self.host_details_vbox) |
|---|
| 1204 | self.host_details_vbox._pack_expand_fill(self.host_details) |
|---|
| 1205 | |
|---|
| 1206 | self.scan_details_page.add_with_viewport(self.scan_details_vbox) |
|---|
| 1207 | self.scan_details_vbox._pack_expand_fill(self.scan_details) |
|---|
| 1208 | |
|---|
| 1209 | def __nmap_output_refreshing(self): |
|---|
| 1210 | self.connect('switch-page', self.refresh_cb) |
|---|
| 1211 | |
|---|
| 1212 | def refresh_cb(self, widget, page=None, page_num=None): |
|---|
| 1213 | if self.nmap_output.nmap_output.thread.isAlive(): |
|---|
| 1214 | if page_num == 2: |
|---|
| 1215 | self.nmap_output.nmap_output.refresh_output(None) |
|---|
| 1216 | |
|---|
| 1217 | |
|---|
| 1218 | if __name__ == "__main__": |
|---|
| 1219 | status = PageStatus("empty") |
|---|
| 1220 | status.set_saved() |
|---|
| 1221 | status.set_unsaved_unchanged() |
|---|
| 1222 | status.set_unsaved_changed() |
|---|
| 1223 | status.set_loaded_unchanged() |
|---|
| 1224 | status.set_loaded_changed() |
|---|
| 1225 | status.set_empty() |
|---|
| 1226 | status.set_scanning() |
|---|
| 1227 | status.set_parsing_result() |
|---|
| 1228 | status.set_unknown() |
|---|
| 1229 | |
|---|
| 1230 | print "Saved:", status.saved |
|---|
| 1231 | print "Unsaved unchanged:", status.unsaved_unchanged |
|---|
| 1232 | print "Unsaved changed:", status.unsaved_changed |
|---|
| 1233 | print "Loaded unchanged:", status.loaded_unchanged |
|---|
| 1234 | print "Loaded changed:", status.loaded_changed |
|---|
| 1235 | print "Empty:", status.empty |
|---|
| 1236 | print "Scanning:", status.scanning |
|---|
| 1237 | print "Parsing result:", status.parsing_result |
|---|
| 1238 | print "Unknown:", status.unknown |
|---|
| 1239 | |
|---|
| 1240 | |
|---|