def __replace_tokens_from_matchobj(self, string, match): """ Replace tokens $1, $2 ... with the corresponding value of matching group. E.g. : $1 <-> (?P<m1>...) This method is used when the matching method "finditer" is used (default) :param str string: String that may contain some tokens ($1, $2 ...) :param _sre.SRE_Match match: Match object resulting from re.search() :return: String with tokens replaced with correct values (or None in case of error) :rtype: str|None """ output = string for i in range(1, 10): token = '${}'.format(i) if token in string: group = 'm{}'.format(i) if group in match.groupdict(): # Replace token by value of matching group # If value is None, replace by empty string output = output.replace(token, match.group(group) or '') else: logger.smarterror('Invalid matchstring for service={service}, ' \ 'tool={tool}'.format( service=self.service.name, tool=self.tool_name)) return None # else: # # Token must be sequentials ($1, $2...) # break return output
def __replace_tokens_from_captdict(self, string, captdict, index): """ Replace tokens $1, $2 ... with the corresponding value of matching group. E.g. : $1 <-> (?P<m1>...) This method is used when the matching method "search" is used :param str string: String that may contain some tokens ($1, $2 ...) :param dict captdict: Captures dict resulting from regex.search().capturesdict() :return: String with tokens replaced with correct values (or None in case of error) :rtype: str|None """ output = string for i in range(1, 10): token = '${}'.format(i) if token in string: group = 'm{}'.format(i) if group in captdict and index < len(captdict[group]): output = output.replace(token, captdict[group][index]) else: logger.smarterror('Invalid matchstring for service={service}, ' \ 'tool={tool}'.format( service=self.service.name, tool=self.tool_name)) return None # else: # # Token must be sequentials ($1, $2...) # break return output
def __detect_specific_options(self): """Detect specific option update from command output""" if self.service.name in options_match.keys(): if self.tool_name in options_match[self.service.name].keys(): p = options_match[self.service.name][self.tool_name] for pattern in p.keys(): logger.debug('Search for option pattern: {pattern}'.format( pattern=pattern)) try: m = re.search(pattern, self.cmd_output, re.IGNORECASE|re.MULTILINE) except Exception as e: logger.warning('Error with matchstring [{pattern}], you should '\ 'review it. Exception: {exception}'.format( pattern=pattern, exception=e)) break # If pattern matches cmd output, update specific option if m: logger.debug('Option pattern matches') if 'name' in p[pattern]: name = self.__replace_tokens_from_matchobj( p[pattern]['name'], m) if name is None: continue else: logger.smarterror('Invalid matchstring for ' \ 'service={service}, tool={tool}: Missing ' \ '"name" key'.format( service=self.service.name, tool=self.tool_name)) continue if 'value' in p[pattern]: value = self.__replace_tokens_from_matchobj( p[pattern]['value'], m) if value is None: continue else: logger.smarterror('Invalid matchstring for ' \ 'service={service}, tool={tool}: Missing ' \ '"value" key'.format( service=self.service.name, tool=self.tool_name)) continue # Add specific option to context self.cu.add_option(name, value)
def __detect_credentials(self): """ Detect usernames/credentials from command output Important: A command output might contain several usernames/passwords with the same pattern. Example method "search": >>> text = " ... Prefix ... Found credentials: ... admin:pass ... toto:pwd ... lorem ipsum ... lorem ipsum" >>> import regex >>> m = regex.search('Pre[\s\S]*?Found credentials:(\s*(?P<m1>\S+):(?P<m2>\S+)\s*\n)+', text) >>> matchs = m.capturesdict() >>> matchs {'m1': ['admin', 'toto'], 'm2': ['pass', 'pwd']} >>> m = regex.search('(\[v\] Trying Credentials:\s*(?P<user>\S+)\s*(?P<password>\S+)\s*\n)+', text) >>> m.capturesdict() {'user': ['Miniwick', 'Miniwick', 'Miniwick', 'Miniwick', 'Miniwick'], 'password': ['password', 'admin', '123456', 'Password1', 'Miniwick']} >>> m = regex.search('WordPress[\s\S]*?(\[v\] Trying Credentials:\s*(?P<user>\S+)\s*(?P<password>\S+)\s*\n)+', text) >>> m.capturesdict() {'user': ['Miniwick', 'Miniwick', 'Miniwick', 'Miniwick', 'Miniwick'], 'password': ['password', 'admin', '123456', 'Password1', 'Miniwick']} """ if self.service.name in creds_match.keys(): if self.tool_name in creds_match[self.service.name].keys(): p = creds_match[self.service.name][self.tool_name] for pattern in p.keys(): # Important: Multiple search/match #m = re.search(pattern, self.cmd_output, re.IGNORECASE|re.DOTALL) logger.debug('Search for creds pattern: {pattern}'.format( pattern=pattern)) if 'user' not in p[pattern]: logger.smarterror('Invalid matchstring for service={service}, ' \ ' tool={tool}: Missing "user" key'.format( service=self.service.name, tool=self.tool_name)) continue # Matching method if 'meth' in p[pattern] \ and p[pattern]['meth'] in ('finditer', 'search'): method = p[pattern]['meth'] else: method = 'finditer' # Perform regexp matching try: if method == 'finditer': m = re.finditer(pattern, self.cmd_output, re.IGNORECASE) else: m = regex.search(pattern, self.cmd_output, regex.IGNORECASE) except Exception as e: logger.warning('Error with matchstring [{pattern}], you should ' \ 'review it. Exception: {exception}'.format( pattern=pattern, exception=e)) break if not m: continue pattern_match = False # Method "finditer" if method == 'finditer': for match in m: pattern_match = True cred = dict() # Replace tokens in user, pass, type cred['user'] = self.__replace_tokens_from_matchobj( p[pattern]['user'], match) if cred['user'] is None: continue if 'pass' in p[pattern]: cred[ 'pass'] = self.__replace_tokens_from_matchobj( p[pattern]['pass'], match) if cred['pass'] is None: continue if 'type' in p[pattern]: cred[ 'type'] = self.__replace_tokens_from_matchobj( p[pattern]['type'], match) if cred['type'] is None: continue # Add username/cred to context if 'pass' in cred: self.cu.add_credentials( username=cred.get('user'), password=cred.get('pass'), auth_type=cred.get('type')) elif 'user' in cred: self.cu.add_username( username=cred.get('user'), auth_type=cred.get('type')) # Method "search" else: pattern_match = True matchs = m.capturesdict() if 'm1' not in matchs: logger.smarterror('Invalid matchstring for ' \ 'service={service}, tool={tool}: Missing match ' \ 'group'.format( service=self.service.name, tool=self.tool_name)) return nb_groups = len(matchs['m1']) for i in range(nb_groups): cred = dict() # Replace tokens in user, pass, type cred['user'] = self.__replace_tokens_from_captdict( p[pattern]['user'], matchs, i) if cred['user'] is None: continue if 'pass' in p[pattern]: cred[ 'pass'] = self.__replace_tokens_from_captdict( p[pattern]['pass'], matchs, i) if cred['pass'] is None: continue if 'type' in p[pattern]: cred[ 'type'] = self.__replace_tokens_from_captdict( p[pattern]['type'], matchs, i) if cred['type'] is None: continue # Add username/cred to context if 'pass' in cred: self.cu.add_credentials( username=cred.get('user'), password=cred.get('pass'), auth_type=cred.get('type')) elif 'user' in cred: self.cu.add_username( username=cred.get('user'), auth_type=cred.get('type')) # If a pattern has matched, skip the next patterns if pattern_match: logger.debug('Creds pattern matches (user only)') return