Advertisement
AndrewHaxalot

golismero.py

Mar 2nd, 2014
703
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 40.08 KB | None | 0 0
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # PYTHON_ARGCOMPLETE_OK
  4.  
  5. __license__="""
  6. GoLismero 2.0 - The web knife.
  7.  
  8. Authors:
  9.  Daniel Garcia Garcia a.k.a cr0hn | cr0hn<@>cr0hn.com
  10.  Mario Vilas | mvilas<@>gmail.com
  11.  
  12. Golismero project site: https://github.com/golismero
  13. Golismero project mail: golismero.project<@>gmail.com
  14.  
  15. This program is free software; you can redistribute it and/or
  16. modify it under the terms of the GNU General Public License
  17. as published by the Free Software Foundation; either version 2
  18. of the License, or (at your option) any later version.
  19.  
  20. This program is distributed in the hope that it will be useful,
  21. but WITHOUT ANY WARRANTY; without even the implied warranty of
  22. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  23. GNU General Public License for more details.
  24.  
  25. You should have received a copy of the GNU General Public License
  26. along with this program; if not, write to the Free Software
  27. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  28. """
  29.  
  30. __all__ = []
  31.  
  32.  
  33. #------------------------------------------------------------------------------
  34. # Fix the module load path.
  35.  
  36. import sys
  37. from os import path
  38.  
  39. script = __file__
  40. if path.islink(script):
  41.     script = path.realpath(script)
  42. here = path.split(path.abspath(script))[0]
  43. assert here
  44. thirdparty_libs = path.join(here, "thirdparty_libs")
  45. assert path.exists(thirdparty_libs)
  46. has_here = here in sys.path
  47. has_thirdparty_libs = thirdparty_libs in sys.path
  48. if not (has_here and has_thirdparty_libs):
  49.     if has_here:
  50.         sys.path.remove(here)
  51.     if has_thirdparty_libs:
  52.         sys.path.remove(thirdparty_libs)
  53.     sys.path.insert(0, thirdparty_libs)
  54.     sys.path.insert(0, here)
  55.  
  56.  
  57. #------------------------------------------------------------------------------
  58. # Python version check.
  59. # We must do it now before trying to import any more modules.
  60. #
  61. # Note: this is mostly because of argparse, if you install it
  62. #       separately you can try removing this check and seeing
  63. #       what happens (we haven't tested it!).
  64.  
  65. from golismero import show_banner
  66. from sys import version_info, exit
  67. if __name__ == "__main__":
  68.     if version_info < (2, 7) or version_info >= (3, 0):
  69.         show_banner()
  70.         print "[!] You must use Python version 2.7"
  71.         exit(1)
  72.  
  73.     # In OS X, python versions lower than 2.7.6 fails
  74.     import platform
  75.     if (
  76.         platform.system() == "Darwin" and
  77.         (version_info < (2,7,6) or version_info >= (3,0))
  78.     ):
  79.         show_banner()
  80.         print (
  81.             "[!] OS X can experiment some problems with Python versions lower than 2.7.6. It's recommended to upgrade"
  82.             " http://www.python.org/download/releases/2.7.6/"
  83.         )
  84.  
  85.  
  86. #------------------------------------------------------------------------------
  87. # Imported modules
  88.  
  89. import argparse
  90. import os
  91.  
  92. from ConfigParser import RawConfigParser
  93. from getpass import getpass
  94. from glob import glob
  95. from os import getenv, getpid
  96. from thread import get_ident
  97.  
  98.  
  99. #------------------------------------------------------------------------------
  100. # GoLismero modules
  101.  
  102. from golismero.api.config import Config
  103. from golismero.api.external import run_external_tool
  104. from golismero.api.logger import Logger
  105. from golismero.api.plugin import CATEGORIES, STAGES
  106. from golismero.common import OrchestratorConfig, AuditConfig, get_profile, \
  107.      get_available_profiles, get_default_plugins_folder
  108. from golismero.main import launcher
  109. from golismero.main.console import get_terminal_size, colorize, Console
  110. from golismero.main.testing import PluginTester
  111. from golismero.managers.pluginmanager import PluginManager
  112. from golismero.managers.processmanager import PluginContext
  113.  
  114.  
  115. #------------------------------------------------------------------------------
  116. # Custom argparse actions
  117.  
  118. class ArgumentParserWithBanner(argparse.ArgumentParser):
  119.     must_show_banner = True
  120.     def error(self, message):
  121.         if self.must_show_banner:
  122.             self.must_show_banner = False
  123.             show_banner()
  124.         self.usage = None
  125.         message += "\n\nUse -h to see the quick help, or --help to show the full help text."
  126.         return super(ArgumentParserWithBanner, self).error(message)
  127.  
  128. # --enable-plugin
  129. class EnablePluginAction(argparse.Action):
  130.     def __call__(self, parser, namespace, values, option_string=None):
  131.         parsed = [ (True, x.strip()) for x in values.split(",")]
  132.         overrides = getattr(namespace, self.dest, [])
  133.         overrides.extend(parsed)
  134.         setattr(namespace, self.dest, overrides)
  135.  
  136. # --disable-plugin
  137. class DisablePluginAction(argparse.Action):
  138.     def __call__(self, parser, namespace, values, option_string=None):
  139.         parsed = [ (False, x.strip()) for x in values.split(",")]
  140.         overrides = getattr(namespace, self.dest, [])
  141.         overrides.extend(parsed)
  142.         setattr(namespace, self.dest, overrides)
  143.  
  144. # --file
  145. class LoadListFromFileAction(argparse.Action):
  146.     def __call__(self, parser, namespace, values, option_string=None):
  147.         try:
  148.             with open(values, "rU") as f:
  149.                 tokens = []
  150.                 for line in f:
  151.                     line = line.strip()
  152.                     if not line or line[0] == "#":
  153.                         continue
  154.                     tokens.append(tokens)
  155.         except Exception:
  156.             parser.error("Error reading file: %s" % values)
  157.         setattr(namespace, self.dest, tokens)
  158.  
  159. # --cookie-file
  160. class ReadValueFromFileAction(argparse.Action):
  161.     def __call__(self, parser, namespace, values, option_string=None):
  162.         try:
  163.             with open(values, "rU") as f:
  164.                 data = f.read()
  165.         except IOError, e:
  166.             parser.error("Can't read file %r. Error: %s" % (values, str(e)))
  167.         setattr(namespace, self.dest, data)
  168.  
  169. # --plugin-arg
  170. class SetPluginArgumentAction(argparse.Action):
  171.     def __call__(self, parser, namespace, values, option_string=None):
  172.         d = getattr(namespace, self.dest, None)
  173.         if d is None:
  174.             d = []
  175.             setattr(namespace, self.dest, d)
  176.         try:
  177.             plugin_id, token = values.split(":", 1)
  178.             plugin_id  = plugin_id.strip()
  179.             key, value = token.split("=", 1)
  180.             key   = key.strip()
  181.             value = value.strip()
  182.             assert plugin_id
  183.             assert key
  184.             d.append( (plugin_id, key, value) )
  185.         except Exception:
  186.             parser.error("invalid plugin argument: %s" % values)
  187.  
  188. # -h
  189. class QuickHelpAction(argparse._HelpAction):
  190.     def __call__(self, parser, namespace, values, option_string=None):
  191.         if parser.must_show_banner:
  192.             parser.must_show_banner = False
  193.             show_banner()
  194.         parser._print_message(parser.quick_help)
  195.         parser.exit()
  196.  
  197.  
  198. #------------------------------------------------------------------------------
  199. # Command line parser using argparse.
  200.  
  201. COMMANDS = (
  202.  
  203.     # Scanning.
  204.     "SCAN",
  205.     "REPORT",
  206.     "IMPORT",
  207.  
  208.     # Information.
  209.     "PROFILES",
  210.     "PLUGINS",
  211.     "INFO",
  212.  
  213.     # Management.
  214.     "DUMP",
  215.     "UPDATE",
  216. )
  217.  
  218. def cmdline_parser():
  219.  
  220.     # Fix the console width bug in argparse.
  221.     try:
  222.         os.environ["COLUMNS"] = str(get_terminal_size()[0])
  223.     except Exception:
  224.         pass
  225.  
  226.     # Use Bash autocompletion when available.
  227.     try:
  228.         from argcomplete import autocomplete
  229.         from argcomplete.completers import ChoicesCompleter, FilesCompleter
  230.         autocomplete_enabled = True
  231.     except ImportError:
  232.         autocomplete_enabled = False
  233.     if autocomplete_enabled:
  234.         def profiles_completer(prefix, **kwargs):
  235.             return [
  236.                 v for v in get_available_profiles()
  237.                   if v.startswith(prefix)
  238.             ]
  239.         def plugins_completer(prefix, **kwargs):
  240.             if ":" in prefix:
  241.                 return [prefix,]
  242.             names = []
  243.             base = get_default_plugins_folder()
  244.             for cat in CATEGORIES:
  245.                 for (_, _, filenames) in os.walk(path.join(base, cat)):
  246.                     for filename in filenames:
  247.                         if filename.startswith(prefix):
  248.                             name, ext = path.splitext(filename)
  249.                             if ext.lower() == ".golismero":
  250.                                 names.append(name)
  251.             return names
  252.  
  253.     parser = ArgumentParserWithBanner(fromfile_prefix_chars="@", add_help=False)
  254.  
  255.     cmd = parser.add_argument("command", metavar="COMMAND", help="action to perform")
  256.     if autocomplete_enabled:
  257.         cmd.completer = ChoicesCompleter(COMMANDS + tuple(x.lower() for x in COMMANDS))
  258.     parser.add_argument("targets", metavar="TARGET", nargs="*", help="zero or more arguments, meaning depends on command")
  259.  
  260.     parser.add_argument("-h", action=QuickHelpAction, default=argparse.SUPPRESS, help="show this help message and exit")
  261.     parser.add_argument("--help", action='help', default=argparse.SUPPRESS, help="show this help message and exit")
  262.  
  263.     gr_main = parser.add_argument_group("main options")
  264.     cmd = gr_main.add_argument("-f", "--file", metavar="FILE", action=LoadListFromFileAction, help="load a list of targets from a plain text file")
  265.     if autocomplete_enabled:
  266.         cmd.completer = FilesCompleter(directories=False)
  267.     cmd = gr_main.add_argument("--config", metavar="FILE", help="global configuration file")
  268.     if autocomplete_enabled:
  269.         cmd.completer = FilesCompleter(allowednames=(".conf",), directories=False)
  270.     cmd = gr_main.add_argument("--user-config", metavar="FILE", help="per-user configuration file")
  271.     if autocomplete_enabled:
  272.         cmd.completer = FilesCompleter(allowednames=(".conf",), directories=False)
  273.     cmd = gr_main.add_argument("-p", "--profile", metavar="NAME", help="profile to use")
  274.     if autocomplete_enabled:
  275.         cmd.completer = profiles_completer
  276.     cmd = gr_main.add_argument("--ui-mode", metavar="MODE", help="UI mode")
  277.     if autocomplete_enabled:
  278.         cmd.completer = ChoicesCompleter(("console", "disabled")) ##, "web"))
  279.     gr_main.add_argument("-v", "--verbose", action="count", help="increase output verbosity")
  280.     gr_main.add_argument("-q", "--quiet", action="store_const", dest="verbose", const=0, help="suppress text output")
  281.     gr_main.add_argument("--color", action="store_true", default=None, dest="color", help="use colors in console output")
  282.     gr_main.add_argument("--no-color", action="store_false", default=None, dest="color", help="suppress colors in console output")
  283.  
  284.     gr_audit = parser.add_argument_group("audit options")
  285.     gr_audit.add_argument("--audit-name", metavar="NAME", help="customize the audit name")
  286.     cmd = gr_audit.add_argument("-db", "--audit-db", metavar="DATABASE", dest="audit_db", help="specify a database filename")
  287.     if autocomplete_enabled:
  288.         cmd.completer = FilesCompleter(allowednames=(".db",), directories=False)
  289.     gr_audit.add_argument("-nd", "--no-db", dest="audit_db", action="store_const", const=":memory:", help="do not store the results in a database")
  290.     cmd = gr_audit.add_argument("-i", "--input", dest="imports", metavar="FILENAME", action="append", help="read results from external tools right before the audit")
  291.     if autocomplete_enabled:
  292.         cmd.completer = FilesCompleter(allowednames=(".csv", ".xml", ".nessus"), directories=False)
  293.     gr_audit.add_argument("-ni", "--no-input", dest="disable_importing", action="store_true", default=False, help="do not read results from external tools")
  294.     gr_report = parser.add_argument_group("report options")
  295.     cmd = gr_report.add_argument("-o", "--output", dest="reports", metavar="FILENAME", action="append", help="write the results of the audit to this file (use - for stdout)")
  296.     if autocomplete_enabled:
  297.         cmd.completer = FilesCompleter(allowednames=(".html", ".rst", ".txt"), directories=False)
  298.     gr_report.add_argument("-no", "--no-output", dest="disable_reporting", action="store_true", default=False, help="do not output the results")
  299.     gr_report.add_argument("--full", action="store_false", default=None, dest="only_vulns", help="produce fully detailed reports")
  300.     gr_report.add_argument("--brief", action="store_true", dest="only_vulns", help="report only the highlights")
  301.  
  302.     gr_net = parser.add_argument_group("network options")
  303.     gr_net.add_argument("--allow-subdomains", action="store_true", default=None, dest="include_subdomains", help="include subdomains in the target scope")
  304.     gr_net.add_argument("--forbid-subdomains", action="store_false", default=None, dest="include_subdomains", help="do not include subdomains in the target scope")
  305.     gr_net.add_argument("--parent", action="store_true", default=None, dest="allow_parent", help="include parent folders in the target scope")
  306.     gr_net.add_argument("-np", "--no-parent", action="store_false", default=None, dest="allow_parent", help="do not include parent folders in the target scope")
  307.     cmd = gr_net.add_argument("-r", "--depth", help="maximum spidering depth (use \"infinite\" for no limit)")
  308.     if autocomplete_enabled:
  309.         cmd.completer = ChoicesCompleter(("1", "200", "infinite",))
  310.     gr_net.add_argument("--follow-redirects", action="store_true", default=None, dest="follow_redirects", help="follow redirects")
  311.     gr_net.add_argument("--no-follow-redirects", action="store_false", default=None, dest="follow_redirects", help="do not follow redirects")
  312.     gr_net.add_argument("--follow-first", action="store_true", default=None, dest="follow_first_redirect", help="always follow a redirection on the target URL itself")
  313.     gr_net.add_argument("--no-follow-first", action="store_false", default=None, dest="follow_first_redirect", help="don't treat a redirection on a target URL as a special case")
  314.     gr_net.add_argument("--max-connections", help="maximum number of concurrent connections per host")
  315.     gr_net.add_argument("-l", "--max-links", type=int, default=None, help="maximum number of links to analyze (0 => infinite)")
  316.     gr_net.add_argument("-pu","--proxy-user", metavar="USER", help="HTTP proxy username")
  317.     gr_net.add_argument("-pp","--proxy-pass", metavar="PASS", help="HTTP proxy password")
  318.     gr_net.add_argument("-pa","--proxy-addr", metavar="ADDRESS", help="HTTP proxy address")
  319.     gr_net.add_argument("-pn","--proxy-port", metavar="PORT", help="HTTP proxy port number")
  320.     gr_net.add_argument("--cookie", metavar="COOKIE", help="set cookie for requests")
  321.     gr_net.add_argument("--user-agent", metavar="USER_AGENT", help="set a custom user agent or 'random' value")
  322.     cmd = gr_net.add_argument("--cookie-file", metavar="FILE", action=ReadValueFromFileAction, dest="cookie", help="load a cookie from file")
  323.     if autocomplete_enabled:
  324.         cmd.completer = FilesCompleter(directories=False)
  325.     gr_net.add_argument("--persistent-cache", action="store_true", dest="use_cache_db", default=True, help="use a persistent network cache [default]")
  326.     gr_net.add_argument("--volatile-cache", action="store_false", dest="use_cache_db", help="use a volatile network cache")
  327.  
  328.     gr_plugins = parser.add_argument_group("plugin options")
  329.     cmd = gr_plugins.add_argument("-a", "--plugin-arg", metavar="PLUGIN:KEY=VALUE", action=SetPluginArgumentAction, dest="raw_plugin_args", help="pass an argument to a plugin")
  330.     if autocomplete_enabled:
  331.         cmd.completer = plugins_completer
  332.     cmd = gr_plugins.add_argument("-e", "--enable-plugin", metavar="PLUGIN", action=EnablePluginAction, default=[], dest="plugin_load_overrides", help="enable a plugin")
  333.     if autocomplete_enabled:
  334.         cmd.completer = plugins_completer
  335.     cmd = gr_plugins.add_argument("-d", "--disable-plugin", metavar="PLUGIN", action=DisablePluginAction, dest="plugin_load_overrides", help="disable a plugin")
  336.     if autocomplete_enabled:
  337.         cmd.completer = plugins_completer
  338.     gr_plugins.add_argument("--max-concurrent", metavar="N", type=int, default=None, help="maximum number of plugins to run concurrently")
  339.     gr_plugins.add_argument("--plugin-timeout", metavar="N", type=float, default=None, help="timeout in seconds for the execution of a plugin")
  340.     cmd = gr_plugins.add_argument("--plugins-folder", metavar="PATH", help="customize the location of the plugins" )
  341.     if autocomplete_enabled:
  342.         cmd.completer = FilesCompleter(directories=True)
  343.  
  344.     if autocomplete_enabled:
  345.         autocomplete(parser)
  346.  
  347.     quick_help = (
  348.         ################################################################################
  349.         "\n"
  350.         "  SCAN:\n"
  351.         "    Perform a vulnerability scan on the given targets. Optionally import\n"
  352.         "    results from other tools and write a report. The arguments that follow may\n"
  353.         "    be domain names, IP addresses or web pages.\n"
  354.         "\n"
  355.         "  PROFILES:\n"
  356.         "    Show a list of available config profiles. This command takes no arguments.\n"
  357.         "\n"
  358.         "  PLUGINS:\n"
  359.         "    Show a list of available plugins. This command takes no arguments.\n"
  360.         "\n"
  361.         "  INFO:\n"
  362.         "    Show detailed information on a given plugin. The arguments that follow are\n"
  363.         "    the plugin IDs. You can use glob-style wildcards.\n"
  364.         "\n"
  365.         "  REPORT:\n"
  366.         "    Write a report from an earlier scan. This command takes no arguments.\n"
  367.         "    To specify output files use the -o switch.\n"
  368.         "\n"
  369.         "  IMPORT:\n"
  370.         "    Import results from other tools and optionally write a report, but don't\n"
  371.         "    scan the targets. This command takes no arguments. To specify input files\n"
  372.         "    use the -i switch.\n"
  373.         "\n"
  374.         "  DUMP:\n"
  375.         "    Dump the database from an earlier scan in SQL format. This command takes no\n"
  376.         "    arguments. To specify output files use the -o switch.\n"
  377.         "\n"
  378.         "  UPDATE:\n"
  379.         "    Update GoLismero to the latest version. Requires Git to be installed and\n"
  380.         "    available in the PATH. This command takes no arguments.\n"
  381.         "\n"
  382.         "examples:\n"
  383.         "\n"
  384.         "  scan a website and show the results on screen:\n"
  385.         "    %(prog)s scan http://www.example.com\n"
  386.         "\n"
  387.         "  grab Nmap results, scan all hosts found and write an HTML report:\n"
  388.         "    %(prog)s scan -i nmap_output.xml -o report.html\n"
  389.         "\n"
  390.         "  grab results from OpenVAS and show them on screen, but don't scan anything:\n"
  391.         "    %(prog)s import -i openvas_output.xml\n"
  392.         "\n"
  393.         "  show a list of all available configuration profiles:\n"
  394.         "    %(prog)s profiles\n"
  395.         "\n"
  396.         "  show a list of all available plugins:\n"
  397.         "    %(prog)s plugins\n"
  398.         "\n"
  399.         "  show information on all bruteforcer plugins:\n"
  400.         "    %(prog)s info brute_*\n"
  401.         "\n"
  402.         "  dump the database from a previous scan:\n"
  403.         "    %(prog)s dump -db example.db -o dump.sql\n"
  404.         "\n"
  405.         ################################################################################
  406.     )
  407.  
  408.     parser.usage = parser.format_usage()[7:] + \
  409.                    "\navailable commands:\n" + quick_help
  410.     parser.quick_help = (
  411.         "usage: %(prog)s COMMAND [TARGETS...] [--options]\n" \
  412.         + quick_help) % {"prog": parser.prog}
  413.  
  414.     return parser
  415.  
  416.  
  417. #------------------------------------------------------------------------------
  418. def parse_plugin_args(manager, plugin_args):
  419.     """
  420.    Parse a list of tuples with plugin arguments as a dictionary of
  421.    dictionaries, with plugin IDs sanitized.
  422.  
  423.    :param manager: Plugin manager.
  424.    :type manager: PluginManager
  425.  
  426.    :param plugin_args: Arguments as specified in the command line.
  427.    :type plugin_args: list(tuple(str, str, str))
  428.  
  429.    :returns: Sanitized plugin arguments. Dictionary mapping plugin
  430.        names to dictionaries mapping argument names and values.
  431.    :rtype: dict(str -> dict(str -> str))
  432.  
  433.    :raises KeyError: Plugin or argument not found.
  434.    """
  435.     parsed = {}
  436.     for plugin_id, key, value in plugin_args:
  437.         plugin_info = manager.guess_plugin_by_id(plugin_id)
  438.         if not plugin_info:
  439.             raise KeyError("Plugin not found: %s" % plugin_id)
  440.         key = key.lower()
  441.         if key not in plugin_info.plugin_args:
  442.             raise KeyError(
  443.                 "Argument not found: %s:%s" % (plugin_id, key))
  444.         try:
  445.             target = parsed[plugin_info.plugin_id]
  446.         except KeyError:
  447.             parsed[plugin_info.plugin_id] = target = {}
  448.         target[key] = value
  449.     return parsed
  450.  
  451.  
  452. #------------------------------------------------------------------------------
  453. def build_config_from_cmdline():
  454.  
  455.     # Get the command line parser.
  456.     parser = cmdline_parser()
  457.  
  458.     # Parse the command line options.
  459.     try:
  460.         args = sys.argv[1:]
  461.         envcfg = getenv("GOLISMERO_SETTINGS")
  462.         if envcfg:
  463.             args = parser.convert_arg_line_to_args(envcfg) + args
  464.         P, V = parser.parse_known_args(args)
  465.         if P.targets:
  466.             P.targets += V
  467.         else:
  468.             P.targets = V
  469.         P.plugin_args = {}
  470.         command = P.command.upper()
  471.         if command in COMMANDS:
  472.             P.command = command
  473.         else:
  474.             P.targets.insert(0, P.command)
  475.             P.command = "SCAN"
  476.  
  477.         # Load the Orchestrator options.
  478.         cmdParams = OrchestratorConfig()
  479.         cmdParams.command = P.command
  480.         if P.config:
  481.             cmdParams.config_file = path.abspath(P.config)
  482.             if not path.isfile(cmdParams.config_file):
  483.                 raise ValueError("File not found: %s" % cmdParams.config_file)
  484.         if cmdParams.config_file:
  485.             cmdParams.from_config_file(cmdParams.config_file,
  486.                                        allow_profile = True)
  487.         if P.user_config:
  488.             cmdParams.user_config_file = path.abspath(P.user_config)
  489.             if not path.isfile(cmdParams.user_config_file):
  490.                 raise ValueError(
  491.                     "File not found: %s" % cmdParams.user_config_file)
  492.         if cmdParams.user_config_file:
  493.             cmdParams.from_config_file(cmdParams.user_config_file,
  494.                                        allow_profile = True)
  495.         if P.profile:
  496.             cmdParams.profile = P.profile
  497.             cmdParams.profile_file = get_profile(cmdParams.profile)
  498.         if cmdParams.profile_file:
  499.             cmdParams.from_config_file(cmdParams.profile_file)
  500.         cmdParams.from_object(P)
  501.         cmdParams.plugin_load_overrides = P.plugin_load_overrides
  502.  
  503.         # Enable console colors if requested.
  504.         Console.use_colors = cmdParams.color
  505.  
  506.         # Show the program banner.
  507.         parser.must_show_banner = False
  508.         if cmdParams.verbose:
  509.             show_banner()
  510.  
  511.         # Load the target audit options.
  512.         auditParams = AuditConfig()
  513.         auditParams.profile          = cmdParams.profile
  514.         auditParams.profile_file     = cmdParams.profile_file
  515.         auditParams.config_file      = cmdParams.config_file
  516.         auditParams.user_config_file = cmdParams.user_config_file
  517.         if auditParams.config_file:
  518.             auditParams.from_config_file(auditParams.config_file)
  519.         if auditParams.user_config_file:
  520.             auditParams.from_config_file(auditParams.user_config_file)
  521.         if auditParams.profile_file:
  522.             auditParams.from_config_file(auditParams.profile_file)
  523.         auditParams.from_object(P)
  524.         auditParams.plugin_load_overrides = P.plugin_load_overrides
  525.  
  526.         # If importing is turned off, remove the list of imports.
  527.         # FIXME this should be done by argparse in argument order!
  528.         if P.disable_importing:
  529.             auditParams.imports = []
  530.  
  531.         # If reports are turned off, remove the list of reports.
  532.         # Otherwise, if no reports are specified, default to screen report.
  533.         # FIXME this should be done by argparse in argument order!
  534.         if P.disable_reporting:
  535.             auditParams.reports = []
  536.         elif (
  537.             not auditParams.reports and
  538.             (P.command != "REPORT" or not auditParams.targets)
  539.         ):
  540.             auditParams.reports = ["-"]
  541.             if auditParams.only_vulns is None:
  542.                 auditParams.only_vulns = True
  543.  
  544.     # Show exceptions as command line parsing errors.
  545.     except Exception, e:
  546.         ##raise    # XXX DEBUG
  547.         parser.error("arguments error: %s" % str(e))
  548.  
  549.     # Get the plugins folder from the parameters.
  550.     # If no plugins folder is given, use the default.
  551.     plugins_folder = cmdParams.plugins_folder
  552.     if not plugins_folder:
  553.         plugins_folder = path.abspath(script)
  554.         plugins_folder = path.dirname(plugins_folder)
  555.         plugins_folder = path.join(plugins_folder, "plugins")
  556.         if not path.isdir(plugins_folder):
  557.             from golismero import common
  558.             plugins_folder = path.abspath(common.__file__)
  559.             plugins_folder = path.dirname(plugins_folder)
  560.             plugins_folder = path.join(plugins_folder, "plugins")
  561.             if not path.isdir(plugins_folder):
  562.                 parser.error("Default plugins folder not found, aborting!")
  563.         cmdParams.plugins_folder = plugins_folder
  564.  
  565.     # Return the parser, options, and config objects.
  566.     return parser, P, cmdParams, auditParams
  567.  
  568.  
  569. #------------------------------------------------------------------------------
  570. # Start of program.
  571. def main():
  572.  
  573.     # Command implementations.
  574.     command = {
  575.         "PLUGINS":  command_plugins,  # List plugins and quit.
  576.         "INFO":     command_info,     # Display plugin info and quit.
  577.         "PROFILES": command_profiles, # List profiles and quit.
  578.         "DUMP":     command_dump,     # Dump the database and quit.
  579.         "UPDATE":   command_update,   # Update GoLismero and quit.
  580.     }
  581.  
  582.     # Parse the command line.
  583.     parser, P, cmdParams, auditParams = build_config_from_cmdline()
  584.  
  585.     # Get the command implementation.
  586.     implementation = command.get(P.command, command_run)
  587.  
  588.     # Run the command.
  589.     implementation(parser, P, cmdParams, auditParams)
  590.  
  591.  
  592. #------------------------------------------------------------------------------
  593. def command_plugins(parser, P, cmdParams, auditParams):
  594.  
  595.     # Fail if we have arguments.
  596.     if P.targets:
  597.         parser.error("too many arguments")
  598.  
  599.     # Load the plugins list.
  600.     try:
  601.         manager = PluginManager()
  602.         manager.find_plugins(cmdParams)
  603.     except Exception, e:
  604.         parser.error("error loading plugins list: %s" % str(e))
  605.  
  606.     # Show the list of plugins.
  607.     print colorize("-------------", "red")
  608.     print colorize(" Plugin list",  "red")
  609.     print colorize("-------------", "red")
  610.  
  611.     # Import plugins...
  612.     import_plugins = manager.get_plugins("import")
  613.     if import_plugins:
  614.         print
  615.         print colorize("-= Import plugins =-", "yellow")
  616.         for name in sorted(import_plugins.keys()):
  617.             info = import_plugins[name]
  618.             print "\n%s:\n    %s" % \
  619.                   (colorize(name[7:], "cyan"), info.description)
  620.  
  621.     # Testing plugins...
  622.     testing_plugins = manager.get_plugins("testing")
  623.     if testing_plugins:
  624.         names = sorted(testing_plugins.keys())
  625.         names = [x[8:] for x in names]
  626.         stages = [ (v,k) for (k,v) in STAGES.iteritems() ]
  627.         stages.sort()
  628.         for _, stage in stages:
  629.             s = stage + "/"
  630.             p = len(s)
  631.             s_slice = [x[p:] for x in names if x.startswith(s)]
  632.             if s_slice:
  633.                 print
  634.                 print colorize("-= %s plugins =-" % stage.title(), "yellow")
  635.                 for name in s_slice:
  636.                     info = testing_plugins["testing/%s/%s" % (stage, name)]
  637.                     desc = info.description.strip()
  638.                     desc = desc.replace("\n", "\n    ")
  639.                     print "\n%s:\n    %s" % (colorize(name, "cyan"), desc)
  640.  
  641.     # Report plugins...
  642.     report_plugins = manager.get_plugins("report")
  643.     if report_plugins:
  644.         print
  645.         print colorize("-= Report plugins =-", "yellow")
  646.         for name in sorted(report_plugins.keys()):
  647.             info = report_plugins[name]
  648.             desc = info.description.strip()
  649.             desc = desc.replace("\n", "\n    ")
  650.             print "\n%s:\n    %s" % (colorize(name[7:], "cyan"), desc)
  651.  
  652.     # UI plugins...
  653.     ui_plugins = manager.get_plugins("ui")
  654.     if ui_plugins:
  655.         print
  656.         print colorize("-= UI plugins =-", "yellow")
  657.         for name in sorted(ui_plugins.keys()):
  658.             info = ui_plugins[name]
  659.             desc = info.description.strip()
  660.             desc = desc.replace("\n", "\n    ")
  661.             print "\n%s:\n    %s" % (colorize(name[3:], "cyan"), desc)
  662.  
  663.     if path.sep == "/":
  664.         print
  665.     exit(0)
  666.  
  667.  
  668. #------------------------------------------------------------------------------
  669. def command_info(parser, P, cmdParams, auditParams):
  670.  
  671.     # Fail if we don't have arguments.
  672.     if not P.targets:
  673.         parser.error("too few arguments")
  674.  
  675.     # Load the plugins list.
  676.     try:
  677.         manager = PluginManager()
  678.         manager.find_plugins(cmdParams)
  679.     except Exception, e:
  680.         parser.error("error loading plugins list: %s" % str(e))
  681.  
  682.     # Show the plugin information.
  683.     try:
  684.         to_print = []
  685.         plugin_infos = []
  686.         for plugin_id in P.targets:
  687.             m_found = manager.search_plugins_by_mask(plugin_id)
  688.             plugin_infos.extend( m_found.values() )
  689.         if not plugin_infos:
  690.             raise KeyError()
  691.         for info in plugin_infos:
  692.             Config._context = PluginContext( orchestrator_pid = getpid(),
  693.                                              orchestrator_tid = get_ident(),
  694.                                                   plugin_info = info,
  695.                                                     msg_queue = None )
  696.             try:
  697.                 manager.load_plugin_by_id(info.plugin_id)
  698.             except Exception:
  699.                 pass
  700.             m_root = cmdParams.plugins_folder
  701.             m_root = path.abspath(m_root)
  702.             if not m_root.endswith(path.sep):
  703.                 m_root += path.sep
  704.             m_location = info.descriptor_file[len(m_root):]
  705.             a, b = path.split(m_location)
  706.             b = colorize(b, "cyan")
  707.             m_location = path.join(a, b)
  708.             m_src = info.plugin_module[len(m_root):]
  709.             a, b = path.split(m_src)
  710.             b = colorize(b, "cyan")
  711.             m_src = path.join(a, b)
  712.             m_name = info.plugin_id
  713.             p = m_name.rfind("/") + 1
  714.             m_name = m_name[:p] + colorize(m_name[p:], "cyan")
  715.             m_desc = info.description.strip()
  716.             m_desc = m_desc.replace("\n", "\n    ")
  717.             to_print.append("")
  718.             to_print.append("Information for plugin: %s" %
  719.                 colorize(info.display_name, "yellow"))
  720.             to_print.append("-" * len("Information for plugin: %s" %
  721.                 info.display_name))
  722.             to_print.append("%s          %s" %
  723.                 (colorize("ID:", "green"), m_name))
  724.             to_print.append("%s    %s" %
  725.                 (colorize("Location:", "green"), m_location))
  726.             to_print.append("%s %s" %
  727.                 (colorize("Source code:", "green"), m_src))
  728.             if info.plugin_class:
  729.                 to_print.append("%s  %s" %
  730.                     (colorize("Class name:", "green"),
  731.                      colorize(info.plugin_class, "cyan")))
  732.             to_print.append("%s    %s" %
  733.                 (colorize("Category:", "green"), info.category))
  734.             to_print.append("%s       %s" %
  735.                 (colorize("Stage:", "green"), info.stage))
  736.             if info.description != info.display_name:
  737.                 to_print.append("")
  738.                 to_print.append("%s\n    %s" %
  739.                     (colorize("Description:", "green"), m_desc))
  740.             if info.plugin_args:
  741.                 to_print.append("")
  742.                 to_print.append(colorize("Arguments:", "green"))
  743.                 for name, default in sorted(info.plugin_args.iteritems()):
  744.                     if name in info.plugin_passwd_args:
  745.                         default = "****************"
  746.                     to_print.append("\t%s -> %s" %
  747.                         (colorize(name, "cyan"), default))
  748.             to_print.append("")
  749.     except KeyError:
  750.         ##raise # XXX DEBUG
  751.         parser.error("plugin ID not found")
  752.     except ValueError:
  753.         ##raise # XXX DEBUG
  754.         parser.error("plugin ID not found")
  755.     except Exception, e:
  756.         ##raise # XXX DEBUG
  757.         parser.error("error recovering plugin info: %s" % str(e))
  758.  
  759.     for line in to_print:
  760.         print line
  761.     exit(0)
  762.  
  763.  
  764. #------------------------------------------------------------------------------
  765. def command_profiles(parser, P, cmdParams, auditParams):
  766.     if P.targets:
  767.         parser.error("too many arguments")
  768.     profiles = sorted(get_available_profiles())
  769.     if not profiles:
  770.         print "No available profiles!"
  771.     else:
  772.         print "--------------------"
  773.         print " " + colorize("Available profiles", "yellow")
  774.         print "--------------------"
  775.         print
  776.         for name in profiles:
  777.             try:
  778.                 p = RawConfigParser()
  779.                 p.read(get_profile(name))
  780.                 desc = p.get("golismero", "description")
  781.             except Exception:
  782.                 desc = None
  783.             if desc:
  784.                 print "+ %s: %s" % (colorize(name, "cyan"), desc)
  785.             else:
  786.                 print "+ %s" % colorize(name, "cyan")
  787.  
  788.     if path.sep == "/":
  789.         print
  790.     exit(0)
  791.  
  792.  
  793. #------------------------------------------------------------------------------
  794. def command_dump(parser, P, cmdParams, auditParams):
  795.     if auditParams.is_new_audit():
  796.         parser.error("missing audit database")
  797.     if not P.reports:
  798.         parser.error("missing output filename")
  799.     if P.verbose != 0:
  800.         print "Loading database: %s" % \
  801.               colorize(auditParams.audit_db, "yellow")
  802.     with PluginTester(autoinit=False, autodelete=False) as t:
  803.         t.orchestrator_config.verbose = 0
  804.         t.audit_config.audit_name = auditParams.audit_name
  805.         t.audit_config.audit_db   = auditParams.audit_db
  806.         t.init_environment()
  807.         Console.use_colors = cmdParams.color
  808.         for filename in P.reports:
  809.             if P.verbose != 0:
  810.                 print "Dumping to file: %s" % colorize(filename, "cyan")
  811.             t.audit.database.dump(filename)
  812.     exit(0)
  813.  
  814.  
  815. #------------------------------------------------------------------------------
  816. def command_update(parser, P, cmdParams, auditParams):
  817.  
  818.     # Fail if we got any arguments.
  819.     if P.targets:
  820.         parser.error("too many arguments")
  821.  
  822.     # Setup a dummy environment so we can call the API.
  823.     with PluginTester(autoinit=False) as t:
  824.         t.orchestrator_config.ui_mode = "console"
  825.         t.orchestrator_config.verbose = cmdParams.verbose
  826.         t.orchestrator_config.color   = cmdParams.color
  827.         t.init_environment(mock_audit=False)
  828.  
  829.         # Run Git here to download the latest version.
  830.         if cmdParams.verbose:
  831.             Logger.log("Updating GoLismero...")
  832.         run_external_tool("git", ["pull"], cwd = here,
  833.             callback = Logger.log if cmdParams.verbose else lambda x: x)
  834.  
  835.         # Update the TLD names.
  836.         if cmdParams.verbose:
  837.             Logger.log("Updating list of TLD names...")
  838.         import tldextract
  839.         tldextract.TLDExtract().update(True)
  840.  
  841.         # Done!
  842.         if cmdParams.verbose:
  843.             Logger.log("Update complete.")
  844.         exit(0)
  845.  
  846.  
  847. #------------------------------------------------------------------------------
  848. def command_run(parser, P, cmdParams, auditParams):
  849.  
  850.     # For the SCAN command, assume targets are URLs whenever feasible.
  851.     if P.command == "SCAN":
  852.         guessed_urls = []
  853.         for target in auditParams.targets:
  854.             if not "://" in target:
  855.                 guessed_urls.append("http://" + target)
  856.         auditParams.targets.extend(guessed_urls)
  857.  
  858.     # For all other commands, disable the testing plugins.
  859.     else:
  860.         auditParams.plugin_load_overrides.append( (False, "testing") )
  861.  
  862.         # For the IMPORT command, targets are import files.
  863.         if P.command == "IMPORT":
  864.             auditParams.imports = auditParams.targets   # magic
  865.             del auditParams.targets                     # magic
  866.  
  867.         # For the REPORT command, targets are report files.
  868.         elif P.command == "REPORT":
  869.             auditParams.reports = auditParams.targets   # magic
  870.             del auditParams.targets                     # magic
  871.  
  872.         # If we reached this point, we have an internal error!
  873.         else:
  874.             raise RuntimeError("Unsupported command: %s" % P.command)
  875.  
  876.     # Expand wildcards for filenames on Windows.
  877.     # On other platforms this is not needed,
  878.     # as the shell already does it for us.
  879.     if os.path.sep == "\\":
  880.         auditParams._imports = expand_wildcards(auditParams._imports)
  881.         auditParams._reports = expand_wildcards(auditParams._reports)
  882.  
  883.     try:
  884.  
  885.         # Load the plugins.
  886.         manager = PluginManager()
  887.         manager.find_plugins(cmdParams)
  888.  
  889.         # Sanitize the plugin arguments.
  890.         try:
  891.             if P.raw_plugin_args:
  892.                 P.plugin_args = parse_plugin_args(manager, P.raw_plugin_args)
  893.         except KeyError, e:
  894.             ##raise # XXX DEBUG
  895.             parser.error("error parsing plugin arguments: %s" % str(e))
  896.  
  897.         # Prompt for passwords.
  898.         for plugin_id in P.plugin_args.keys():
  899.             plugin_info = manager.get_plugin_by_id(plugin_id)
  900.             target_args = P.plugin_args[plugin_id]
  901.             for key, value in target_args.items():
  902.                 if not value and key in plugin_info.plugin_passwd_args:
  903.                     if len(plugin_info.plugin_passwd_args) > 1:
  904.                         msg = "Enter password for %s (%s): "
  905.                         msg %= (plugin_info.display_name, key)
  906.                     else:
  907.                         msg = "Enter password for %s: "
  908.                         msg %= plugin_info.display_name
  909.                     target_args[key] = getpass(msg)
  910.  
  911.         # Save the plugin arguments for the Orchestrator and the Audit.
  912.         cmdParams.plugin_args   = P.plugin_args
  913.         auditParams.plugin_args = P.plugin_args
  914.  
  915.         # Check the parameters.
  916.         cmdParams.check_params()
  917.         auditParams.check_params()
  918.  
  919.         # Set the plugin arguments before loading the UI plugin.
  920.         for plugin_id, plugin_args in cmdParams.plugin_args.iteritems():
  921.             status = manager.set_plugin_args(plugin_id, plugin_args)
  922.             if status != 0:     # should never happen, but just in case...
  923.                 if status == 1:
  924.                     msg = "Unknown plugin: %s"
  925.                 elif status == 2:
  926.                     msg = "Invalid arguments for plugin: %s"
  927.                 else:
  928.                     msg = "Error setting arguments for plugin: %s"
  929.                 parser.error(msg % plugin_id)
  930.  
  931.         # Load the UI plugin.
  932.         ui_plugin_id = "ui/" + cmdParams.ui_mode
  933.         ui_plugin = manager.load_plugin_by_id(ui_plugin_id)
  934.  
  935.     # Show an error message if something goes wrong.
  936.     except Exception, e:
  937.         ##raise  # XXX DEBUG
  938.         parser.error("error loading plugins: %s" % str(e))
  939.  
  940.     # Check the settings with the UI plugin.
  941.     try:
  942.         ui_plugin.check_params(cmdParams, auditParams)
  943.     except Exception, e:
  944.         ##raise # XXX DEBUG
  945.         msg = str(e)
  946.         if not msg:
  947.             msg = "configuration error!"
  948.         parser.error(msg)
  949.  
  950.     # Launch GoLismero.
  951.     launcher.run(cmdParams, auditParams)
  952.     exit(0)
  953.  
  954.  
  955. #------------------------------------------------------------------------------
  956. def expand_wildcards(filenames):
  957.     expanded = []
  958.     for filename in filenames:
  959.         if "*" in filename or "?" in filename:
  960.             expanded.extend(glob(filename))
  961.         else:
  962.             expanded.append(filename)
  963.     return expanded
  964.  
  965.  
  966. #------------------------------------------------------------------------------
  967. if __name__ == '__main__':
  968.     main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement