Example #1
0
File: Tool.py Project: u53r55/jok3r
    def getUpdateCmd(self):
        """
		Build update command
		@Returns	Update command-line
		"""
        c = Command(self.tool_dir, self.update, None, self.toolbox_dir, None,
                    None, None, None)
        return c.getParsedInstallCommandLine()
Example #2
0
File: Tool.py Project: u53r55/jok3r
    def getRunCmd(self, target, output_file, output_dir, specific_args):
        """
		Build run command
		"""
        #print self.command
        c = Command(self.tool_dir, self.command, target, self.toolbox_dir,
                    output_file, output_dir, self.service_name, specific_args)
        return c.getParsedRunCommandLine()
Example #3
0
File: Tool.py Project: u53r55/jok3r
    def getInstallCmd(self):
        """
		Build install command
		@Returns	Install command-line
		"""
        c = Command(self.tool_dir, self.install, None, self.toolbox_dir, None,
                    None, None, None)
        return c.getParsedInstallCommandLine()
Example #4
0
    def run(self, target):
        try:
            self.command = Command(self.rawcmd, self.type)
        except CommandException as e:
            logger.error(e)
            return None

        # Build script to run
        if self.type == 'rce-blind':
            logger.warning('WARNING: This attack box must be reachable from the target !')
            logger.info('If target is vulnerable, exploit will try to ping local ' \
                'IP = {localip} from target'.format(
                    localip=NetUtils.get_local_ip_address()))

            print(self.command.get_cmdline(target))

            script = SCRIPT_RCE_BLIND.format(
                exploit_dir=self.directory,
                command=self.command.get_cmdline(target))

        elif self.type == 'rce-standard':
            script = self.command.get_cmdline(target)

        else:
            logger.error('Unsupported exploit type')
            return None

        # Run subprocess
        try:
            logger.info('Exploit will be run from directory: {directory}'.format(
                directory=self.directory))

            proc = subprocess.Popen(script, 
                                    shell=True, 
                                    stdout=subprocess.PIPE, 
                                    stderr=subprocess.STDOUT)

            # Agressivelly get the output
            while True:
                out = proc.stdout.read(1)
                # We put that inside try block to avoid utf8 decoding error
                try:
                    out = out.decode(sys.stdout.encoding)
                    sys.stdout.write(out)
                    self.output += out
                except:
                    pass

                # Break if process has finished
                if out == ''  and proc.poll() != None:
                    break

        except Exception as e:
            logger.error('Error when trying to run command: {exception}'.format(
                exception=e))
            return None

        return self.output
Example #5
0
    def __parse_tool_options(self, section, tool_config):
        """
        Check and parse options from a given tool section
        :param section: Tool section into the toolbox settings file
        :param tool_config: A defaultdict(str) storing tool config which is updated into this method
        :return: Boolean indicating status
        """
        log_prefix = '[{filename}{ext} | Section "{section}"]'.format(
                        filename=TOOLBOX_CONF_FILE, ext=CONF_EXT, section=section)

        optparsed = self.config_parsers[TOOLBOX_CONF_FILE].options(section)
        for opt in TOOL_OPTIONS[MANDATORY]:
            if opt not in optparsed:
                logger.warning('{prefix} Missing mandatory option "{option}", tool is skipped'.format(
                    prefix=log_prefix, option=opt))
                return False

        tool_config['name_clean'] = section
        for opt in optparsed:
            if opt not in TOOL_OPTIONS[MANDATORY]+TOOL_OPTIONS[OPTIONAL]:
                logger.warning('{prefix} Option "{option}" is not supported, it will be ignored'.format(
                    prefix=log_prefix, option=opt))
                continue

            if opt in TOOL_OPTIONS[MANDATORY]:
                val = self.config_parsers[TOOLBOX_CONF_FILE].safe_get(section, opt, '', None)
                if opt == 'name':
                    tool_config[opt]=StringUtils.clean(val, allowed_specials=['-', '_'])
                elif opt == 'description':
                    tool_config[opt] = val
                elif opt == 'target_service':
                    tool_config[opt] = val.lower()
                    if tool_config[opt] not in self.services.list_services(multi=True):
                        logger.warning('{prefix} Service specified in "target_service" is not supported, ' \
                            'tool is skipped'.format(prefix=log_prefix))
                        return False

                if not tool_config[opt]:
                    logger.warning('{prefix} Mandatory option "{option}" is empty, tool is skipped'.format(
                        prefix=log_prefix, option=opt))
                    return False



            elif opt == 'install':
                tool_config[opt] = Command(cmdtype = CMD_INSTALL, 
                                           cmdline = self.config_parsers[TOOLBOX_CONF_FILE].safe_get(section, opt, '', None))

            elif opt == 'update':
                tool_config[opt] = Command(cmdtype = CMD_UPDATE,
                                           cmdline = self.config_parsers[TOOLBOX_CONF_FILE].safe_get(section, opt, '', None))

            elif opt == 'check_command':
                tool_config[opt] = Command(cmdtype = CMD_CHECK,
                                           cmdline = self.config_parsers[TOOLBOX_CONF_FILE].safe_get(section, opt, '', None))

        return True
Example #6
0
File: Tool.py Project: u53r55/jok3r
    def checkInstall(self, output):
        """
		Check if the tool is correctly installed.
		Basically, it runs the installed tool without any option
		@Args		output: 	CLIOutput instance
		@Returns	Boolean indicating status
		"""

        # Directory where the tool should be installed
        if not FileUtils.is_dir(self.tool_dir):
            output.printFail(
                'Directory where the tool should be installed (\'{0}\') does not exist !'
                .self.tool_dir)
            return False

        # Try to run the tool
        output.printInfo('Trying to run the tool {0}...'.format(self.name))
        splitted = self.command.strip().split(' ')

        cmd = ''
        if splitted[0].lower() == 'sudo' and len(splitted) > 1:
            cmd = 'sudo '
            splitted = splitted[1:]
        cmd += splitted[0]

        if splitted[0].lower() in ('python', 'python3', 'perl',
                                   'ruby') and len(splitted) > 1:
            if splitted[1] != '-m':
                cmd += ' {0}'.format(splitted[1])
            elif len(splitted) > 2:
                cmd += ' -m {0}'.format(splitted[2])

        elif splitted[0].lower() == 'java' and len(splitted) > 2:
            if splitted[1].lower() == '-jar':
                cmd += ' -jar {0}'.format(splitted[2])

        c = Command(self.tool_dir, cmd, None, self.toolbox_dir, None, None,
                    None, None)
        cmd_check = c.getStandardCommandLine()
        cmd_check_print = cmd_check[cmd_check.index(';') + 1:].strip()

        output.printBeginCmd(cmd_check_print)
        process = ProcessLauncher(cmd_check, output, None)
        process.start()
        output.printEndCmd()

        # Prompt
        output.printPrompt(
            'Does the tool {0} seem to be running correctly ? [Y/n]'.format(
                self.name))
        return CLIUtils.promptYesNo(output, default='Y')
Example #7
0
    def __parse_commands(self, service, section):
        """
        Create Commands object for a given tool.
        Each command is defined in settings file by:
            - command_<command_number> 
            - context_<command_number> (optional)
        :param service: Service name
        :param section: Section name [check_(.+)]
        :return: List of Command instances
        """
        log_prefix = '[{filename}{ext} | Section "{section}"]'.format(
                        filename=service, ext=CONF_EXT, section=section)

        commands = list()
        cmdlines = self.config_parsers[service].safe_get_multi(section, 'command', default=None)
        i = 0
        for cmd in cmdlines:
            context = self.config_parsers[service].safe_get(section, 'context_'+str(i+1), default=None)
            context = self.__parse_context(service, section, i, context)
            if context is None: 
                logger.warning('{prefix} Context is invalid, the check is ignored'.format(prefix=log_prefix))
                return None

            commands.append(Command(cmdtype=CMD_RUN, 
                                    cmdline=cmdlines[i], 
                                    context=context, 
                                    services_config=self.services))
            i += 1
        if not commands:
            logger.warning('{prefix} No command is specified, the check is ignored'.format(prefix=log_prefix))
        return commands
Example #8
0
    def __parse_commands(self, service, section):
        """
        Parse commands for a given tool and create Commands object. 
        Each command is defined in configuration file by:
            - command_<command_number> 
            - context_<command_number> (optional)

        :param str service: Service name
        :param str section: Section name of the check
        :return: Created Command objects
        :rtype: list(Command)
        """
        log_prefix = '[{filename}{ext} | Section "{section}"]'.format(
            filename=service, ext=CONF_EXT, section=section)

        commands = list()

        # Get command lines
        cmdlines = self.config_parsers[service].safe_get_multi(section,
                                                               'command',
                                                               default=None)

        i = 0
        # Loop over command lines
        for cmd in cmdlines:

            # Parse context requirements and create ContextRequirements object
            context = self.config_parsers[service].safe_get(section,
                                                            'context_' +
                                                            str(i + 1),
                                                            default=None)

            context_requirements = self.__parse_context_requirements(
                service, section, i + 1, context)

            if context_requirements is None:
                logger.warning('{prefix} Context requirements are invalid, the check ' \
                    'is ignored'.format(prefix=log_prefix))
                return None

            # Create the Command object
            command = Command(cmdtype=CmdType.RUN,
                              cmdline=cmdlines[i],
                              context_requirements=context_requirements,
                              services_config=self.services)
            commands.append(command)
            i += 1

        if not commands:
            logger.warning('{prefix} No command is specified, the check is ' \
                'ignored'.format(prefix=log_prefix))

        return commands
Example #9
0
File: Tool.py Project: irpr0/jok3r
    def __init__(
            self,
            service_name,
            toolbox_dir,
            tooltype,
            # General tool options
            name,
            tool_ref_name,
            category,
            description,
            command,
            install,
            update,
            installed,
            last_update,
            # Specific tool options
            specific_options):
        """
		Initialize a Tool object

		@Args		service_name:		service targeted by the tool
					toolbox_dir:		toolbox directory
					tooltype: 			ToolType
					name:				tool name as it will appear in the program
					category:			tool category, must be one of the categories in "tools_categories" general setting
					description: 		tool description, it will be diplayed to user before running tool
					raw_command:		command-line used to launched the tool (can use tags)
					install:			command-line for tool install
					update:				command-line for tool update
					installed:			boolean indicating whether tool is installed or not
					last_update:		date of last tool update		
					specific_options:	dictionary of specific tool options
		"""

        self.service_name = service_name
        self.toolbox_dir = toolbox_dir
        self.tooltype = tooltype
        self.name = name
        self.tool_ref_name = tool_ref_name
        self.category = category
        self.description = description
        self.last_update = last_update
        self.installed = installed if isinstance(installed, bool) else False
        self.specific_options = specific_options

        # Directory reserved for the tool
        if self.tooltype == ToolType.USE_MULTI:
            self.tool_dir = self.toolbox_dir + os.sep + Constants.MULTI_SERVICES_TOOLBOX_SUBDIR + os.sep + 'all' + os.sep + self.tool_ref_name
        else:
            self.tool_dir = self.toolbox_dir + os.sep + self.service_name + os.sep + self.category + os.sep + self.name

        # Commands instantiation
        self.command = Command(CommandType.RUN, command, self.tool_dir,
                               self.toolbox_dir) if command else None
        self.install = Command(CommandType.INSTALL, install, self.tool_dir,
                               self.toolbox_dir) if install else None
        self.update = Command(CommandType.UPDATE, update, self.tool_dir,
                              self.toolbox_dir) if update else None
Example #10
0
class Exploit:

    def __init__(self, name, description, type_, rawcmd, success=''):
        self.name = name
        self.description = description
        self.type = type_
        self.rawcmd = rawcmd
        self.success = success
        self.directory = TOOL_BASEPATH + '/exploits/' + self.name.lower()
        self.output = ''


    def run(self, target):
        try:
            self.command = Command(self.rawcmd, self.type)
        except CommandException as e:
            logger.error(e)
            return None

        # Build script to run
        if self.type == 'rce-blind':
            logger.warning('WARNING: This attack box must be reachable from the target !')
            logger.info('If target is vulnerable, exploit will try to ping local ' \
                'IP = {localip} from target'.format(
                    localip=NetUtils.get_local_ip_address()))

            print(self.command.get_cmdline(target))

            script = SCRIPT_RCE_BLIND.format(
                exploit_dir=self.directory,
                command=self.command.get_cmdline(target))

        elif self.type == 'rce-standard':
            script = self.command.get_cmdline(target)

        else:
            logger.error('Unsupported exploit type')
            return None

        # Run subprocess
        try:
            logger.info('Exploit will be run from directory: {directory}'.format(
                directory=self.directory))

            proc = subprocess.Popen(script, 
                                    shell=True, 
                                    stdout=subprocess.PIPE, 
                                    stderr=subprocess.STDOUT)

            # Agressivelly get the output
            while True:
                out = proc.stdout.read(1)
                # We put that inside try block to avoid utf8 decoding error
                try:
                    out = out.decode(sys.stdout.encoding)
                    sys.stdout.write(out)
                    self.output += out
                except:
                    pass

                # Break if process has finished
                if out == ''  and proc.poll() != None:
                    break

        except Exception as e:
            logger.error('Error when trying to run command: {exception}'.format(
                exception=e))
            return None

        return self.output


    def check_success(self):
        if self.type == 'rce-blind':
            m = re.search(MATCHING_PATTERN_RCE_BLIND, self.output, re.IGNORECASE)

        elif self.type == 'rce-standard':
            m = re.search(self.success, self.output, re.IGNORECASE)

        return (m is not None)
            
Example #11
0
    def __parse_tool_options(self, section, tool_config):
        """
        Check and parse options from a given tool section.

        :param str section: Section name corresponding to the tool in toolbox.conf
        :param defaultdict(str) tool_config: Tool configuration updated in this method
        :return: Status of parsing
        :rtype: bool
        """
        log_prefix = '[{filename}{ext} | Section "{section}"]'.format(
            filename=TOOLBOX_CONF_FILE, ext=CONF_EXT, section=section)

        # Check presence of mandatory options
        optparsed = self.config_parsers[TOOLBOX_CONF_FILE].options(section)
        for opt in TOOL_OPTIONS[MANDATORY]:
            if opt not in optparsed:
                logger.warning('{prefix} Missing mandatory option "{option}", ' \
                    'tool is skipped'.format(prefix=log_prefix, option=opt))
                return False

        # Loop over options
        for opt in optparsed:

            # Check for unsupported options
            if opt not in TOOL_OPTIONS[MANDATORY] + TOOL_OPTIONS[OPTIONAL]:
                logger.warning('{prefix} Option "{option}" is not supported, ' \
                    'it will be ignored'.format(prefix=log_prefix, option=opt))
                continue

            # Add value
            val = self.config_parsers[TOOLBOX_CONF_FILE].safe_get(
                section, opt, '', None)

            if opt == 'name':
                tool_config[opt] = StringUtils.clean(
                    val, allowed_specials=['-', '_'])

            elif opt == 'description':
                tool_config[opt] = val

            elif opt == 'target_service':
                tool_config[opt] = val.lower()

                if tool_config[opt] not in self.services.list_services(
                        multi=True):
                    logger.warning('{prefix} Service specified in "target_service" is ' \
                        'not supported, tool is skipped'.format(prefix=log_prefix))
                    return False

            elif opt == 'install':
                tool_config[opt] = Command(cmdtype=CmdType.INSTALL,
                                           cmdline=val)

            elif opt == 'update':
                tool_config[opt] = Command(cmdtype=CmdType.UPDATE, cmdline=val)

            elif opt == 'check_command':
                tool_config[opt] = Command(cmdtype=CmdType.CHECK, cmdline=val)

            # Check for empty mandatory option
            if opt in TOOL_OPTIONS[MANDATORY] and not tool_config[opt]:
                logger.warning('{prefix} Mandatory option "{option}" is empty, tool ' \
                    'is skipped'.format(prefix=log_prefix, option=opt))
                return False

        return True
Example #12
0
    def __parse_tool_options(self, section, tool_config):
        """
        Check and parse options from a given tool section.

        :param str section: Section name corresponding to the tool in toolbox.conf
        :param defaultdict(str) tool_config: Tool configuration updated in this method
        :return: Status of parsing
        :rtype: bool
        """
        log_prefix = '[{filename}{ext} | Section "{section}"]'.format(
            filename=TOOLBOX_CONF_FILE, ext=CONF_EXT, section=section)

        # Check presence of mandatory options
        optparsed = self.config_parsers[TOOLBOX_CONF_FILE].options(section)
        for opt in TOOL_OPTIONS[MANDATORY]:
            if opt not in optparsed:
                logger.warning('{prefix} Missing mandatory option "{option}", ' \
                    'tool is skipped'.format(prefix=log_prefix, option=opt))
                return False

        # Loop over options
        for opt in optparsed:

            # Check for unsupported options
            if opt not in TOOL_OPTIONS[MANDATORY] + TOOL_OPTIONS[OPTIONAL]:
                logger.warning('{prefix} Option "{option}" is not supported, ' \
                    'it will be ignored'.format(prefix=log_prefix, option=opt))
                continue

            # Add value
            val = self.config_parsers[TOOLBOX_CONF_FILE].safe_get(
                section, opt, '', None)

            if opt == 'name':
                tool_config[opt] = StringUtils.clean(
                    val, allowed_specials=['-', '_'])

            elif opt == 'description':
                tool_config[opt] = val

            elif opt == 'target_service':
                tool_config[opt] = val.lower()

                if tool_config[opt] not in self.services.list_services(
                        multi=True):
                    logger.warning('{prefix} Service specified in "target_service" is ' \
                        'not supported, tool is skipped'.format(prefix=log_prefix))
                    return False

            elif opt == 'virtualenv':
                tool_config[opt] = val.lower()

                # For Python, format must be "python<version>"
                if tool_config[opt].startswith('python'):
                    m = re.match('python(?P<version>[0-9](\.[0-9])*)',
                                 tool_config[opt])
                    if not m:
                        logger.warning('{prefix} Invalid Python virtualenv, must be: ' \
                            'virtualenv = python<version>. Tool is skipped'.format(
                                prefix=log_prefix))
                        return False

                # For Ruby, make sure to have a format like "ruby-<version>"
                # Format "ruby<version>" is accepted and turned into "ruby-<version>"
                if tool_config[opt].startswith('ruby'):
                    m1 = re.match('ruby(?P<version>[0-9](\.[0-9])*)',
                                  tool_config[opt])
                    m2 = re.match('ruby-(?P<version>[0-9](\.[0-9])*)',
                                  tool_config[opt])
                    if m1:
                        tool_config[opt] = 'ruby-{version}'.format(
                            version=m.group('version'))
                    elif not m2:
                        logger.warning('{prefix} Invalid Ruby virtualenv, must be: ' \
                            'virtualenv = ruby-<version>. Tool is skipped'.format(
                                prefix=log_prefix))
                        return False

            elif opt == 'install':
                tool_config[opt] = Command(cmdtype=CmdType.INSTALL,
                                           cmdline=val)

            elif opt == 'update':
                tool_config[opt] = Command(cmdtype=CmdType.UPDATE, cmdline=val)

            elif opt == 'check_command':
                tool_config[opt] = Command(cmdtype=CmdType.CHECK, cmdline=val)

            # Check for empty mandatory option
            if opt in TOOL_OPTIONS[MANDATORY] and not tool_config[opt]:
                logger.warning('{prefix} Mandatory option "{option}" is empty, tool ' \
                    'is skipped'.format(prefix=log_prefix, option=opt))
                return False

        return True
Example #13
0
    def run(self, target, mode, rce_command=''):
        """
        :param Target targer: Target instance
        :param str mode: mode can be either "detect" or "exploit"
        :param str rce_command: RCE command to run when running exploit (requires mode=exploit)
        """
        try:
            if mode == 'detect':
                self.command = Command(self.detection_rawcmd, self.type)
            elif mode == 'exploit':
                self.command = Command(self.exploit_rawcmd, self.type, self.exploit_rce_output)
        except CommandException as e:
            logger.error(e)
            return None

        # Build script to run
        if mode == 'exploit':
            if self.type == 'rce':
                if not self.exploit_rce_output:
                    logger.warning('The exploit will attempt to execute command on remote system but no '
                        'output will be available !')
                    # For RCE exploit without command output in test mode (no rce_command provided):
                    # Use script template that check for reverse connection with ICMP ping and HTTP requests
                    if len(rce_command) == 0:
                        logger.warning('WARNING: This attack box must be reachable from the target !')
                        logger.info('No command supplied to run through RCE, automatic exploit test will be started...')
                        logger.info('If target is vulnerable, exploit will try to ping (ICMP Echo request) and '
                            'to send HTTP request to local IP = {localip} from target'.format(
                                localip=NetUtils.get_local_ip_address()))

                        cmdline = self.command.get_cmdline(target)
                        print(cmdline)

                        script = SCRIPT_RCE_BLIND.format(
                            exploit_dir=self.directory,
                            command=cmdline)
                    else:
                        cmdline = self.command.get_cmdline(target, rce_command)
                        print(cmdline)
                        script  = 'cd {exploit_dir}; '.format(exploit_dir=self.directory)
                        script += cmdline
                else:
                    if len(rce_command) == 0:
                        logger.info('No command supplied to run through RCE, automatic exploit test will be started...')
                        logger.info('If target is vulnerable, exploit will try to run an echo command on target')
                        cmdline = self.command.get_cmdline(target)
                        print(cmdline)
                        script  = 'cd {exploit_dir}; '.format(exploit_dir=self.directory)
                        script += cmdline
                    else:
                        cmdline = self.command.get_cmdline(target, rce_command)
                        print(cmdline)
                        script  = 'cd {exploit_dir}; '.format(exploit_dir=self.directory)
                        script += cmdline                        

            else:
                cmdline = self.command.get_cmdline(target)
                print(cmdline)
                script  = 'cd {exploit_dir}; '.format(exploit_dir=self.directory)
                script += cmdline
        else:
            logger.warning('The script will attempt to detect if remote system is vulnerable without '
                'actually exploiting the vulnerability.')
            logger.warning('WARNING: False Positive is possible !')
            cmdline = self.command.get_cmdline(target)
            script  = 'cd {exploit_dir}; '.format(exploit_dir=self.directory)
            script += cmdline            

        # Run subprocess
        try:
            logger.info('{script} will be run from directory: {directory}'.format(
                script='Exploit' if mode == 'exploit' else 'Detection script',
                directory=self.directory))

            proc = subprocess.Popen(script, 
                                    shell=True, 
                                    executable='/bin/bash',
                                    stdout=subprocess.PIPE, 
                                    stderr=subprocess.STDOUT)

            # Agressivelly get the output
            while True:
                out = proc.stdout.read(1)
                # We put that inside try block to avoid utf8 decoding error
                try:
                    out = out.decode(sys.stdout.encoding)
                    sys.stdout.write(out)
                    self.output += out
                except:
                    pass

                # Break if process has finished
                if out == ''  and proc.poll() != None:
                    break

        except Exception as e:
            logger.error('Error when trying to run command: {exception}'.format(
                exception=e))
            return None

        return self.output
Example #14
0
class Exploit:

    def __init__(self, 
                 name, 
                 product,
                 description, 
                 type_, 
                 detection_rawcmd, 
                 detection_success,
                 exploit_rawcmd,
                 exploit_rce_output,
                 exploit_success):
        self.name = name
        self.product = product
        self.description = description
        self.type = type_
        self.detection_rawcmd = detection_rawcmd
        self.detection_success = detection_success
        self.exploit_rawcmd = exploit_rawcmd
        self.exploit_rce_output = exploit_rce_output
        self.exploit_success = exploit_success
        self.directory = TOOL_BASEPATH + '/exploits/' + self.name.lower()
        self.output = ''


    def is_mode_supported(self, mode):
        """
        Check is specified mode is supported (either "detect" or "exploit")
        :param str mode: Requested mode
        """
        if mode == 'detect':
            return (self.detection_rawcmd is not None and len(self.detection_rawcmd) > 0)
        elif mode == 'exploit':
            return (self.exploit_rawcmd is not None and len(self.exploit_rawcmd) > 0)
        else:
            return False


    def run(self, target, mode, rce_command=''):
        """
        :param Target targer: Target instance
        :param str mode: mode can be either "detect" or "exploit"
        :param str rce_command: RCE command to run when running exploit (requires mode=exploit)
        """
        try:
            if mode == 'detect':
                self.command = Command(self.detection_rawcmd, self.type)
            elif mode == 'exploit':
                self.command = Command(self.exploit_rawcmd, self.type, self.exploit_rce_output)
        except CommandException as e:
            logger.error(e)
            return None

        # Build script to run
        if mode == 'exploit':
            if self.type == 'rce':
                if not self.exploit_rce_output:
                    logger.warning('The exploit will attempt to execute command on remote system but no '
                        'output will be available !')
                    # For RCE exploit without command output in test mode (no rce_command provided):
                    # Use script template that check for reverse connection with ICMP ping and HTTP requests
                    if len(rce_command) == 0:
                        logger.warning('WARNING: This attack box must be reachable from the target !')
                        logger.info('No command supplied to run through RCE, automatic exploit test will be started...')
                        logger.info('If target is vulnerable, exploit will try to ping (ICMP Echo request) and '
                            'to send HTTP request to local IP = {localip} from target'.format(
                                localip=NetUtils.get_local_ip_address()))

                        cmdline = self.command.get_cmdline(target)
                        print(cmdline)

                        script = SCRIPT_RCE_BLIND.format(
                            exploit_dir=self.directory,
                            command=cmdline)
                    else:
                        cmdline = self.command.get_cmdline(target, rce_command)
                        print(cmdline)
                        script  = 'cd {exploit_dir}; '.format(exploit_dir=self.directory)
                        script += cmdline
                else:
                    if len(rce_command) == 0:
                        logger.info('No command supplied to run through RCE, automatic exploit test will be started...')
                        logger.info('If target is vulnerable, exploit will try to run an echo command on target')
                        cmdline = self.command.get_cmdline(target)
                        print(cmdline)
                        script  = 'cd {exploit_dir}; '.format(exploit_dir=self.directory)
                        script += cmdline
                    else:
                        cmdline = self.command.get_cmdline(target, rce_command)
                        print(cmdline)
                        script  = 'cd {exploit_dir}; '.format(exploit_dir=self.directory)
                        script += cmdline                        

            else:
                cmdline = self.command.get_cmdline(target)
                print(cmdline)
                script  = 'cd {exploit_dir}; '.format(exploit_dir=self.directory)
                script += cmdline
        else:
            logger.warning('The script will attempt to detect if remote system is vulnerable without '
                'actually exploiting the vulnerability.')
            logger.warning('WARNING: False Positive is possible !')
            cmdline = self.command.get_cmdline(target)
            script  = 'cd {exploit_dir}; '.format(exploit_dir=self.directory)
            script += cmdline            

        # Run subprocess
        try:
            logger.info('{script} will be run from directory: {directory}'.format(
                script='Exploit' if mode == 'exploit' else 'Detection script',
                directory=self.directory))

            proc = subprocess.Popen(script, 
                                    shell=True, 
                                    executable='/bin/bash',
                                    stdout=subprocess.PIPE, 
                                    stderr=subprocess.STDOUT)

            # Agressivelly get the output
            while True:
                out = proc.stdout.read(1)
                # We put that inside try block to avoid utf8 decoding error
                try:
                    out = out.decode(sys.stdout.encoding)
                    sys.stdout.write(out)
                    self.output += out
                except:
                    pass

                # Break if process has finished
                if out == ''  and proc.poll() != None:
                    break

        except Exception as e:
            logger.error('Error when trying to run command: {exception}'.format(
                exception=e))
            return None

        return self.output


    def check_success(self, mode):
        """
        Check vuln detection success or exploit success when run in automatic test (i.e. 
        no command provided via --cmd)

        :param str mode: mode can be either "detect" or "exploit"
        """
        m = None
        if mode == 'detect':
            m = re.search(self.detection_success, self.output, re.IGNORECASE)
            if not m:
                if self.detection_success.lower() in self.output.lower():
                    m = self.detection_success
        elif mode == 'exploit':
            if self.type == 'rce':
                if self.exploit_rce_output:
                    # RCE with command output: use success match string provided in settings
                    m = re.search(self.exploit_success, self.output, re.IGNORECASE)
                    if not m:
                        if self.exploit_success.lower() in self.output.lower():
                            m = self.exploit_success
                else:
                    # RCE without command output: use built-in match strings to detect either
                    # ICMP echo reply or received HTTP request
                    m = re.search(MATCHING_PATTERN_RCE_BLIND_ICMP, self.output, re.IGNORECASE)
                    if not m:
                        m = re.search(MATCHING_PATTERN_RCE_BLIND_HTTP, self.output, re.IGNORECASE)
            else:
                # Other exploit type (e.g. sqli)
                m = re.search(self.exploit_success, self.output, re.IGNORECASE)
                if not m:
                    if self.exploit_success.lower() in self.output.lower():
                        m = self.exploit_success

        return (m is not None)
            
Example #15
0
class Tool(object):

	def __init__(self, 
				 service_name,
				 toolbox_dir,
				 tooltype,
				 # General tool options
				 name,
				 tool_ref_name,
				 category,
				 description,
				 command,
				 install,
				 update,
				 installed,
				 last_update,
				 # Specific tool options
				 specific_options):
		"""
		Initialize a Tool object

		@Args		service_name:		service targeted by the tool
					toolbox_dir:		toolbox directory
					tooltype: 			ToolType
					name:				tool name as it will appear in the program
					category:			tool category, must be one of the categories in "tools_categories" general setting
					description: 		tool description, it will be diplayed to user before running tool
					raw_command:		command-line used to launched the tool (can use tags)
					install:			command-line for tool install
					update:				command-line for tool update
					installed:			boolean indicating whether tool is installed or not
					last_update:		date of last tool update		
					specific_options:	dictionary of specific tool options
		"""

		self.service_name	 	= service_name
		self.toolbox_dir	 	= toolbox_dir
		self.tooltype 			= tooltype
		self.name 			 	= name
		self.tool_ref_name		= tool_ref_name
		self.category 		 	= category
		self.description 	 	= description
		self.last_update 	 	= last_update
		self.installed 		 	= installed if isinstance(installed, bool) else False
		self.specific_options 	= specific_options		

		# Directory reserved for the tool
		if self.tooltype == ToolType.USE_MULTI:
			self.tool_dir 	= self.toolbox_dir + os.sep + Constants.MULTI_SERVICES_TOOLBOX_SUBDIR + os.sep + 'all' + os.sep + self.tool_ref_name
		else:
			self.tool_dir 	= self.toolbox_dir + os.sep + self.service_name + os.sep + self.category + os.sep + self.name

		# Commands instantiation
		self.command 	= Command(CommandType.RUN, 		command, 	self.tool_dir, self.toolbox_dir) if command else None
		self.install 	= Command(CommandType.INSTALL, 	install, 	self.tool_dir, self.toolbox_dir) if install else None
		self.update 	= Command(CommandType.UPDATE, 	update, 	self.tool_dir, self.toolbox_dir) if update  else None


	def printToolSummary(self, output):
		"""
		Print tool info nicely

		@Args		output:	CLIOutput instance
		"""

		output.printTitle1('   {0} {1}'.format(self.name, '[-> {0}]'.format(self.tool_ref_name) if self.tool_ref_name else ''))
		output.printNewLine('     Description : {0}'.format(self.description))
		#if self.command: 		output.printNewLine('     Command     : {0}'.format(self.command.cmdline))
		output.printRaw('     Installed   : ')   
		last_update = self.last_update if self.last_update else 'Unknown'
		output.printGreen('Yes [last update: {0}]\n'.format(last_update)) if self.installed else output.printRed('No\n')
		if self.installed: 	output.printNewLine('     Location    : {0}'.format(self.tool_dir))
		if self.specific_options:
			specific = ''
			for option in self.specific_options:
				type_opt  = self.specific_options[option][0]
				value_opt = self.specific_options[option][1]
				if type_opt == bool and value_opt == True:
					specific += '         - {0}: {1}\n'.format(option, 'True')
				elif type_opt == list and value_opt:
					specific += '         - {0}: {1}\n'.format(option, ', '.join(value_opt))
			if specific:
				output.printRaw('     Specific    :\n{0}'.format(specific))


	def printToolSummaryBrief(self, output):
		"""
		Print tool name + install status on one line

		@Args 		output: CLIOutput instance
		"""
		txt = '   - {0}{1}\n'.format(self.name, ' [-> {0}]'.format(self.tool_ref_name) if self.tool_ref_name else '')
		output.printGreen(txt) if self.installed else output.printRed(txt)


	def createToolDirectory(self, output):
		"""
		Create the tool directory if necessary

		@Args		output: 	CLIOutput instance
		@Returns	Boolean indicating operation status
		"""
		if FileUtils.is_dir(self.tool_dir):
			output.printInfo('Directory "{0}" already exists'.format(self.tool_dir))
			return True

		try:
			FileUtils.create_directory(self.tool_dir)
		except Exception as e:
			output.printError('Unable to create new directory "{0}": {1}'.format(self.tool_dir, e))
			return False
		output.printInfo('New directory "{0}" created'.format(self.tool_dir))
		return True


	def runInstall(self, settings, output, fast_mode=False, referencing_tool=None):
		"""
		Install the tool

		@Args		settings: 	Settings instance
					output: 	CLIOutput instance
					fast_mode:	Boolean. If True, do not prompt confirm before install and do not check install after
		@Returns	Boolean indicating status
		"""

		# Check for cases where no install will be run
		if self.installed:
			if self.tooltype == ToolType.USE_MULTI:
				output.printInfo('This is a reference to the tool "{0}" which is already installed, skipped.'.format(self.tool_ref_name))
			else:
				output.printInfo('{0} is already installed (according to config), skipped.'.format(self.name))
			print
			return False

		elif self.tooltype == ToolType.USE_MULTI:
			output.printInfo('This is a reference to the tool "{0}", which is not specific to the service {1}'.format(self.tool_ref_name, self.service_name))
			ref_tool = settings.toolbox.searchInToolboxForService(self.tool_ref_name, Constants.MULTI_SERVICES_CONF_FILE)
			if ref_tool:
				return ref_tool.runInstall(settings, output, fast_mode=fast_mode, referencing_tool=self)
			else:
				output.printFail('The tool "{0}" has not been found inside the conf file "{1}{2}"'.format(self.tool_ref_name, \
					Constants.MULTI_SERVICES_CONF_FILE, Constants.CONF_EXT))
				return False

		elif not self.install:
			output.printWarning('No tool install command specified in config file, skipped')
			if not fast_mode: output.printPrompt('Do you want to mark this tool as installed ? [Y/n]')
			if fast_mode or CLIUtils.promptYesNo(output, default='Y'):
				try:
					if settings.changeInstalledOption(self.service_name, self.name, True):
						output.printSuccess('Tool {0} has been marked as installed. '.format(self.name))
					else:
						output.printError('Error when saving "{0}{1}" configuration file'.format(Constants.INSTALL_STATUS_CONF_FILE, Constants.CONF_EXT))
				except Exception as e:
					output.printError('An unexpected error occured when trying to mark the tool as installed')
					self.removeTool(settings, output)
			else:
				output.printInfo('Tool is still not marked as installed')
			print
			return False

		# Create directory for the tool if necessary
		if not self.createToolDirectory(output):
			output.printFail('Tool install skipped.')
			print
			return False

		# Print basic info and prompt confirmation
		cmd, cmd_short = self.install.getParsedCmdline()
		output.printInfo('Description     : {0}'.format(self.description))
		output.printInfo('Install command : {0}'.format(cmd_short))
		if not fast_mode: output.printPrompt('Install ? [Y/n]')

		# Run install command if wanted
		if fast_mode or CLIUtils.promptYesNo(output, default='Y'):
			output.printBeginCmd(cmd_short)
			process = ProcessLauncher(cmd, output, None)
			process.start()
			output.printEndCmd()
			output.printSuccess('Tool installation has finished')

			# Check install ?
			install_ok = True
			if not (self.tooltype == ToolType.MULTI_SERVICES and not referencing_tool) and not fast_mode:
				output.printInfo('Now, checking if {0} has been installed correctly. Hit any key to run test...'.format(self.name))
				CLIUtils.getch()
				try:
					install_ok = self.checkInstall(output, referencing_tool=referencing_tool)
				except Exception as e:
					install_ok = False
					output.printError('An unexpected error occured when checking install: {0}'.format(e))

			# Change install status in configuration file
			if install_ok:
				try:
					if settings.changeInstalledOption(self.service_name, self.name, True):
						output.printSuccess('Tool {0} has been marked as installed. '.format(self.name))
					else:
						output.printError('Error when saving "{0}{1}" configuration file'.format(Constants.INSTALL_STATUS_CONF_FILE, Constants.CONF_EXT))
				except Exception as e:
					output.printError('An unexpected error occured when trying to mark the tool as installed: {0}'.format(e))
					self.removeTool(settings, output)
			else:
				output.printFail('Tool {0} has not been marked as installed'.format(self.name))
				self.removeTool(settings, output)
		else:
			output.printFail('Tool has not been installed')
		print


	def checkInstall(self, output, referencing_tool=None):
		"""
		Check if the tool is correctly installed.
		Basically, it runs the installed tool without any option

		@Args		output: 	CLIOutput instance
		@Returns	Boolean indicating status
		"""

		output.printInfo('Trying to run the tool {0} with no option...'.format(self.name))
		if referencing_tool:
			cmd, cmd_short = referencing_tool.command.getParsedCmdline(remove_args=True)
		elif self.command:
			cmd, cmd_short = self.command.getParsedCmdline(remove_args=True)
		else:
			raise Exception

		output.printBeginCmd(cmd_short)
		process = ProcessLauncher(cmd, output, None)
		process.start()
		output.printEndCmd()

		output.printPrompt('Does the tool {0} seem to be running correctly ? [Y/n]'.format(self.name))
		return CLIUtils.promptYesNo(output, default='Y')


	def runUpdate(self, settings, output, fast_mode=False, referencing_tool=None):
		"""
		Run the update for the tool 

		@Args		settings: 	Settings instance
					output: 	CLIOutput instance
		@Returns	Boolean indicating status
		"""

		# Check for cases where no update will be run
		if not self.installed:
			output.printInfo('{0} is not installed yet (according to config), skipped.'.format(self.name))
			print
			return False

		elif self.tooltype == ToolType.USE_MULTI:
			output.printInfo('This is a reference to the tool "{0}", which is not specific to the service {1}'.format(self.tool_ref_name, self.service_name))
			ref_tool = settings.toolbox.searchInToolboxForService(self.tool_ref_name, Constants.MULTI_SERVICES_CONF_FILE)
			if ref_tool:
				return ref_tool.runUpdate(settings, output, fast_mode=fast_mode, referencing_tool=self)
			else:
				output.printFail('The tool "{0}" has not been found inside the conf file "{1}{2}"'.format(self.tool_ref_name, \
					Constants.MULTI_SERVICES_CONF_FILE, Constants.CONF_EXT))
				return False

		elif not self.update:
			output.printWarning('No tool update command specified in config file, skipped.')
			print
			return False

		# Create directory for the tool if necessary (should not be necessary because only update)
		if not FileUtils.is_dir(self.tool_dir):
			output.printFail('Tool directory does not exist but tool marked as installed. Trying to re-install it...')
			return self.runInstall(settings, output, fast_mode)

		# Print basic info and prompt confirmation
		cmd, cmd_short = self.update.getParsedCmdline()
		output.printInfo('Description     : {0}'.format(self.description))
		output.printInfo('Install command : {0}'.format(cmd_short))
		if not fast_mode: output.printPrompt('Update ? [Y/n]')

		# Run update command if wanted
		if fast_mode or CLIUtils.promptYesNo(output, default='Y'):
			output.printBeginCmd(cmd_short)
			process = ProcessLauncher(cmd, output, None)
			process.start()
			output.printEndCmd()
			output.printSuccess('Tool update has finished')

			# Check install ?
			update_ok = True
			if not (self.tooltype == ToolType.MULTI_SERVICES and not referencing_tool) and not fast_mode:
				output.printInfo('Now, checking if {0} has been updateed correctly. Hit any key to run test...'.format(self.name))
				CLIUtils.getch()
				try:
					update_ok = self.checkInstall(output, referencing_tool=referencing_tool)
				except Exception as e:
					update_ok = False
					output.printError('An unexpected error occured when checking install: {0}'.format(e))

			# Change install status in configuration file
			if update_ok:
				try:
					if settings.changeInstalledOption(self.service_name, self.name, True):
						output.printSuccess('Tool {0} has been marked as successfully updated'.format(self.name))
					else:
						output.printError('Error when saving "{0}{1}" configuration file'.format(Constants.INSTALL_STATUS_CONF_FILE, Constants.CONF_EXT))
				except Exception as e:
					output.printError('An unexpected error occured when trying to change the last update date: {0}'.format(e))
					#self.removeTool(settings, output)
			else:
				output.printFail('Tool {0} has not been marked as updated'.format(self.name))
				#self.removeTool(settings, output)
				output.printPrompt('Do you want to try to re-install {0} ? [Y/n]'.format(self.name))
				if CLIUtils.promptYesNo(output, default='Y'):
					self.reinstallTool(settings, output, referencing_tool=referencing_tool)
		else:
			output.printFail('Tool has not been updated')
		print


	def runTool(self, 
				settings, 
				output, 
				output_dir, 
				target, 
				specific_args, 
				ignore_specific=False,
				auto_yes=False):
		"""
		Run the tool

		@Args		settings: 		instance of Settings
					output: 		instance of CLIOutput
					output_dir:		directory where tool execution output will be saved
					target:			instance of Target
					specific_args:	specific arguments
					always_run: 	boolean indicating if tool should be always run (ignoring context specific options)
					auto_yes: 		boolean indicating if prompt should be displayed or not before running
		@Returns	Boolean indicating status
		"""
		# Tool not installed yet
		if not self.installed:
			output.printInfo('{0} is not installed yet (according to config), skipped.'.format(self.name))
			return False

		# If context specific
		if self.specific_options and not ignore_specific:
			for opt in self.specific_options.keys():
				# Boolean option
				if self.specific_options[opt][0] == bool:
					if self.specific_options[opt][1] == True and (opt not in specific_args.keys() or specific_args[opt] == False):
						output.printInfo('Tool skipped. Specific to: {0} = True'.format(opt))
						return False

				# List option
				elif self.specific_options[opt][0] == list and self.specific_options[opt][1]: 
					if opt not in specific_args.keys() or \
					   specific_args[opt] != 'all' and specific_args[opt] not in self.specific_options[opt][1]:
						output.printInfo('Tool skipped. Specific to: {0} = {1}'.format(opt, ', '.join(self.specific_options[opt][1])))
						return False

		# Print basic info and prompt confirmation
		cmd, cmd_short = self.command.getParsedCmdline(output_dir=output_dir, 
													   output_filename=self.name+'.txt',
													   target=target,
													   specific_args=specific_args)
		output.printInfo('Description : {0}'.format(self.description))
		output.printInfo('Run command : {0}'.format(cmd_short))
		if not auto_yes: 
			output.printPrompt('Run tool ? [Y/n/t/w/q]'.format(self.category, self.name))
			to_run = CLIUtils.promptRunMode(output, default='Y')
		else:
			to_run = 'Yes'

		# Run command if wanted
		if to_run == 'Quit':
			print
			output.printWarning('Exit !')
			sys.exit(0)
		elif to_run != 'No':
			output.printBeginCmd(cmd_short)
			process = ProcessLauncher(cmd, output, None)			
			# Normal running
			if auto_yes or to_run == 'Yes':
				process.start()

			# Start in new tab
			elif to_run == 'Tab':
				# TODO
				output.printInfo('Not yet implemented')

			# Start in new window
			elif to_run == 'Window':
				process.startInNewWindow()
				print
				output.printInfo('Started in another window')
			output.printEndCmd()
		print	


	def removeTool(self, settings, output):
		"""
		Remove the tool:
			- Remove tool directory into toolbox
			- Change install status to false
		
		@Args		settings: 	Settings instance
					output: 	CLIOutput instance
		@Returns	Boolean indicating operation status
		"""

		if self.tooltype == ToolType.USE_MULTI:
			output.printInfo('"{0}" is a reference to the tool "{1}" used for multi services. Not deleted'.format(\
				self.name, self.tool_ref_name))
			return False

		if not FileUtils.is_dir(self.tool_dir):
			output.printInfo('Directory "{0}" does not exist'.format(self.tool_dir))
		else:
			if not FileUtils.remove_directory(self.tool_dir):
				output.printFail('Unable to delete directory "{0}". Check permissions and/or re-run with sudo'.format(self.tool_dir))
				return False
			else:
				output.printInfo('Directory "{0}" deleted'.format(self.tool_dir))

		# Make sure "installed" option in config file is set to False
		if not settings.changeInstalledOption(self.service_name, self.name, False):
			output.printError('An unexpected error occured when trying to mark the tool as uninstalled !')
		self.installed = False
		return True


	def reinstallTool(self, settings, output, referencing_tool=None):
		"""
		Try a tool re-install, i.e. remove and install

		@Args		settings: 	Settings instance
					output: 	CLIOutput instance
		@Returns	Boolean indicating operation status
		"""
		output.printInfo('First, the tool directory will be removed...')
		if not self.removeTool(settings, output):
			return False
		output.printInfo('Now, running a new install for {0}...'.format(self.name))
		return self.runInstall(settings, output, referencing_tool=referencing_tool)