# -*- coding: utf-8 -*- # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301, USA. # # Author: Mauro Soria import threading from lib.connection.RequestException import RequestException from .Path import * from .Scanner import * class Fuzzer(object): def __init__(self, requester, dictionary, testFailPath=None, threads=1, matchCallbacks=[], notFoundCallbacks=[], errorCallbacks=[]): self.requester = requester self.dictionary = dictionary self.testFailPath = testFailPath self.basePath = self.requester.basePath self.threads = [] self.threadsCount = threads if len(self.dictionary) >= threads else len(self.dictionary) self.running = False self.scanners = {} self.defaultScanner = None self.matchCallbacks = matchCallbacks self.notFoundCallbacks = notFoundCallbacks self.errorCallbacks = errorCallbacks self.matches = [] self.errors = [] def wait(self, timeout=None): for thread in self.threads: thread.join(timeout) if timeout is not None and thread.is_alive(): return False return True def setupScanners(self): if len(self.scanners) != 0: self.scanners = {} self.defaultScanner = Scanner(self.requester, self.testFailPath, "") self.scanners['/'] = Scanner(self.requester, self.testFailPath, "/") for extension in self.dictionary.extensions: self.scanners[extension] = Scanner(self.requester, self.testFailPath, "." + extension) def setupThreads(self): if len(self.threads) != 0: self.threads = [] for thread in range(self.threadsCount): newThread = threading.Thread(target=self.thread_proc) newThread.daemon = True self.threads.append(newThread) def getScannerFor(self, path): if path.endswith('/'): return self.scanners['/'] for extension in list(self.scanners.keys()): if path.endswith(extension): return self.scanners[extension] # By default, returns empty tester return self.defaultScanner def start(self): # Setting up testers self.setupScanners() # Setting up threads self.setupThreads() self.index = 0 self.dictionary.reset() self.runningThreadsCount = len(self.threads) self.running = True self.playEvent = threading.Event() self.pausedSemaphore = threading.Semaphore(0) self.playEvent.clear() self.exit = False for thread in self.threads: thread.start() self.play() def play(self): self.playEvent.set() def pause(self): self.playEvent.clear() for thread in self.threads: if thread.is_alive(): self.pausedSemaphore.acquire() def stop(self): self.running = False self.play() def scan(self, path): response = self.requester.request(path) result = None if self.getScannerFor(path).scan(path, response): result = (None if response.status == 404 else response.status) return result, response def isRunning(self): return self.running def finishThreads(self): self.running = False self.finishedEvent.set() def isFinished(self): return self.runningThreadsCount == 0 def stopThread(self): self.runningThreadsCount -= 1 def thread_proc(self): self.playEvent.wait() try: path = next(self.dictionary) while path is not None: try: status, response = self.scan(path) result = Path(path=path, status=status, response=response) if status is not None: self.matches.append(result) for callback in self.matchCallbacks: callback(result) else: for callback in self.notFoundCallbacks: callback(result) del status del response except RequestException as e: for callback in self.errorCallbacks: callback(path, e.args[0]['message']) continue finally: if not self.playEvent.isSet(): self.pausedSemaphore.release() self.playEvent.wait() path = next(self.dictionary) # Raises StopIteration when finishes if not self.running: break except StopIteration: return finally: self.stopThread()