def _run_thread(self, path): """ makes a HTTP GET request to check if a file exists. to be used as a thread. :param directory: the directory to search for files in :param word: the file name to search for :return (list): a list of found files """ found_files = [] # construct the url to be used in the GET request url = f'{self.main.get_host_url_base()}/' # make the GET request for the file resp = http_get_request(url + f'{path}', self.main.cookies) # check if the response code is a success code if (resp.status_code in self.main.success_codes): if self.main.options['verbose']: success(path, prepend=' ') found_files.append(path) # only return a list if files were actually found if found_files: return found_files
def run_module(self): """ the entrypoint into the module. takes any found webpages and uses threads to extract params from the html. """ info('Parsing HTML...') # get the list of found pages found_pages = self._get_previous_results('FileScanner') # if there are no found pages, theres no need to run this module if not found_pages: return # pass the found pages to threads with concurrent.futures.ProcessPoolExecutor() as executor: results = list(executor.map(self._run_thread, found_pages)) # clean up the results from the threads final = [] _ = [final.extend(p) for p in results if p] final = list(filter(None, final)) # remove duplicate found parameters final = [i for n, i in enumerate(final) if i not in final[n + 1:]] if self.main.options['verbose']: for params in final: success(f'Found params: {params["action"]} ' f'({" ".join(params["params"])})', prepend=' ') self._save_scan_results(final, update_count=False)
def _run_thread(self, form): """ search through form parameters to find anti-csrf tokens """ if len(form) != 3: warning('Internal error, not enough form elements in CSRF') exit() # give the form data human friendly names method = form['method'] page = form['action'] params = form['params'] # were only concerned with POST requests for CSRF if method == 'POST': # check if param names contain any anti-csrf token params if not any(csrf_name in params for csrf_name in self.csrf_fields): success(f'No anti-csrf tokens for: {page}/{",".join(params)}', prepend=' ') return { 'method': method, 'page': page, 'parameter': params, 'payload': None }
def reset_wordlist(self): conn = self.get_connection(self.db_paths['wordlist']) sql_statement = 'UPDATE wordlist SET count = 0 WHERE count > 0' self.execute_statement(conn, sql_statement) success('wordlist database has been reset successfully', prepend=' ')
def show_previous_scans(self): """ Displays a list of past scans and allows user to select one. This method prints out a list of all the past scans saved in the 'scans' database, along with their scan IDs. It prompts the user to enter the scan ID of the scan to be selected. Args: None Returns: the scan ID of the selected scan """ info('Previous scans:') # store an instance of the 'scans' database scans_table = self.db.get_scan_db().table('scans') # get all the saved scans scans = scans_table.all() # loop through each scan for scan in scans: # print the scan details to stdout success( f'ID: {scan.doc_id},' f'Host: {scan["host"]}', f'Port: {scan["port"]}', f'Time: {scan["timestamp"]}', prepend=' ') # prompt the user to enter the scan ID of the scan to be selected # it will reject scan IDs which dont exist, and any input which is # not an int check = False while not check: try: choice = int(input('> ')) if not self.db.scan_exists(choice): warning(f'Scan ID: {choice} does not exist') else: check = True except (ValueError): pass return choice
def auto_crawl(self): """ recursively crawls all the links in a web application. Args: None Returns: None """ # loop through all pages found so far loop_pages = self.found_pages for page in loop_pages: for link in self._parse_links(page): if link not in loop_pages: success(f'Found page: {link}', prepend=' ') loop_pages.append(link) self._save_scan_results(loop_pages, update_count=False)
def _check_page_content(self, method, injection, param, page, page_text): assert(hasattr(self, "re_search_strings")) search_strings = self.re_search_strings if any([s in page_text for s in search_strings]): if not (page, param) in self.injectable_params: if self.main.options['verbose']: success(f'Vulnerable parameter: {page} - {param} ({injection})', prepend=' ') # self.injectable_params.append((page, param, injection)) self.injectable_params.append({'method': method, 'page': page, 'parameter': param, 'payload': injection}) return True return False
def _run_thread(self, word): """ makes a HTTP GET request to check if a directory exists. to be used as a thread. :param word: the directory to scan for :return (string): the directory if found """ # check for restricted paths if self.main.restrict_paths and word in self.main.restrict_paths: return None # GET request to the directory url = f'{self.main.get_host_url_base()}/{word}/' resp = http_get_request(url, self.main.cookies) # check if the response code is a success code if (resp.status_code in self.main.success_codes): if self.main.options['verbose']: success(word, prepend=' ') return word
def _parse_robots(self): # construct url for robots.txt url = f'{self.main.get_host_url_base()}/robots.txt' resp = http_get_request(url, self.main.cookies) dir_paths = [] file_paths = [] # checking is robots.txt exists if resp.status_code == 200: success('robots.txt found', prepend=' ') info('parsing robots.txt', prepend=' ') lines = resp.text.split('\n') # if there are no lines then theres nothing to do if not lines: return # loop through every line in robots.txt for line in lines: if line.startswith('Allow:') or line.startswith('Disallow:'): path = line.split(': ')[1] success(f'Found path: {path}', prepend=' ') if not path: continue if path[:-1] == '/': dir_paths.append(path) else: file_paths.append(path) if dir_paths: table = self.main.db.get_scan_db().table('directories_discovered') table.insert({"scan_id": self.main.id, "results": dir_paths}) if file_paths: table = self.main.db.get_scan_db().table('files_discovered') table.insert({"scan_id": self.main.id, "results": file_paths})
def proxy_response_handle(self, resp, path): """ Parses a response from the proxy. Args: resp: the response from proxy. from 'requests' module path: the path to the webpage for the request Returns: None """ if self.main.base_dir in path: path = path.replace(self.main.base_dir, '/') if resp.status_code in self.main.success_codes: # get rid of any GET parameters in the path if '?' in path: path = path.split('?')[0] # we dont want to find files in restricted paths if path not in self.main.restrict_paths: # we dont want files which weve already found if path not in self.manual_found_pages: # get the filename file = os.path.normpath(path).split(os.path.sep)[-1] if '.' in file: # get the file extension ext = file.split('.')[-1] # we dont want file types not in the extension list if f'.{ext}' in self.main.file_extensions: self.manual_found_pages.append(path) success(f'Found new page: {path}', prepend=' ') else: self.manual_found_pages.append(path) success(f'Found new page: {path}', prepend=' ')
def reset_scans(self): scans = self.get_scan_db() scans.purge_tables() success('scans database has been reset successfully', prepend=' ')
def _run_thread(self, param): """ Checks for blind SQL injections by injecting payloads into parameters and checking the timing of the response from the server. Args: param: the parameter to inject payloads into Returns: dict containing information about vulnerable parameter, or None if not vulnerable """ method = param['method'] page = param['action'] self.injections = [] self.injectable_params = [] inject_params = param['params'] assert (hasattr(self, "attack_strings")) attack_strings = self.attack_strings if method == 'GET': url = self._construct_get_url(page, inject_params) for p in inject_params: for injection in attack_strings: resp = http_get_request(url, self.main.cookies) normal_time = resp.elapsed final_url = url.replace(f'{p}=test', f'{p}={injection}') resp = http_get_request(final_url, self.main.cookies) test_time = resp.elapsed check_time = test_time - normal_time if check_time.total_seconds() >= 5.0: if self.main.options['verbose']: success( f'Vulnerable parameter: {page} - {p} ({injection})', prepend=' ') # self.injectable_params.append((page, param, injection)) self.injectable_params.append({ 'method': method, 'page': page, 'parameter': p, 'payload': injection }) break elif method == 'POST': # construct the url to make the request to url = f'{self.main.get_host_url_base()}/{page}' for p in inject_params: params = self._construct_post_params(inject_params) for injection in attack_strings: resp = http_post_request(url, params, self.main.cookies) normal_time = resp.elapsed params[p] = injection resp = http_post_request(url, params, self.main.cookies) test_time = resp.elapsed check_time = test_time - normal_time if check_time.total_seconds() >= 5.0: if self.main.options['verbose']: success( f'Vulnerable parameter: {page} - {p} ({injection})', prepend=' ') # self.injectable_params.append((page, param, injection)) self.injectable_params.append({ 'method': method, 'page': page, 'parameter': p, 'payload': injection }) break return self.injectable_params