def request_pool(self): try: clients = {} end = False while not end: try: client = self.client_queue.get(timeout=5) if client == (0,0): end = True else: if client[0] not in clients: clients.update({client[0]: {"timestamp": time.time(), "testcases": []}}) else: clients[client[0]]["timestamp"] = time.time() if len(clients[client[0]]["testcases"]) <= 10: clients[client[0]]["testcases"].append(client[1]) else: clients[client[0]]["testcases"].pop(0) clients[client[0]]["testcases"].append(client[1]) except: pass for c in clients.keys(): if time.time() - clients[c]["timestamp"] >= 30: self.save_testcase(c, clients[c]["testcases"]) del clients[c] except Exception as e: raise PJFBaseException(e.message)
def execute(self, obj): """ Perform the actual external fuzzing, you may replace this method in order to increase performance """ try: if self.config.stdin: self.spawn(self.config.command, stdin_content=obj, stdin=True, timeout=1) else: if "@@" not in self.config.command: raise PJFMissingArgument( "Missing @@ filename indicator while using non-stdin fuzzing method" ) for x in self.config.command: if "@@" in x: self.config.command[self.config.command.index( x)] = x.replace("@@", obj) self.spawn(self.config.command, timeout=2) self.logger.debug( "[{0}] - PJFExternalFuzzer successfully completed".format( time.strftime("%H:%M:%S"))) return self._out except KeyboardInterrupt: return "" except Exception as e: raise PJFBaseException(e.message)
def fuzz_elements(self, element): """ Fuzz all elements inside the object """ try: if type(element) == dict: for key in element: if self.config.parameters: if self.config.exclude_parameters: fuzz = key not in self.config.parameters else: fuzz = key in self.config.parameters else: fuzz = True if fuzz: if type(element[key]) == dict: element[key] = self.fuzz_elements(element[key]) elif type(element[key]) == list: element[key] = self.fuzz_elements(element[key]) else: element[key] = self.mutator.fuzz(element[key]) elif type(element) == list: arr = [] for key in element: if type(key) == dict: arr.append(self.fuzz_elements(key)) elif type(key) == list: arr.append(self.fuzz_elements(key)) else: arr.append(self.mutator.fuzz(key)) element = arr del arr except Exception as e: raise PJFBaseException(e.message) return element
def start_monitor(self, standalone=True): """ Run command in a loop and check exit status plus restart process when needed """ try: self.start() cmdline = shlex.split(self.config.process_to_monitor) if standalone: signal.signal(signal.SIGINT, self.shutdown) self.process = subprocess.Popen(cmdline, stdin=PIPE, stdout=PIPE, stderr=PIPE) while self.process and not self.finished: self.process.wait() if self._is_sigsegv(self.process.returncode): if self.config.debug: print "[\033[92mINFO\033[0m] Process crashed with \033[91mSIGSEGV\033[0m, waiting for testcase..." while not self.got_testcase(): time.sleep(1) self.save_testcase( self.testcase[-10:]) # just take last 10 testcases if self.process: self.process = subprocess.Popen(cmdline, stdin=PIPE, stdout=PIPE, stderr=PIPE) except OSError: self.shutdown() self.process = False self.got_testcase = lambda: True raise PJFProcessExecutionError("Binary <%s> does not exist" % cmdline[0]) except Exception as e: raise PJFBaseException("Unknown error please send log to author")
def web_fuzzer(self): try: from tools import TOOLS_DIR if not self.config.auto: to_fuzz = {'lvl1': {"lvl2": [1, 1.0, "True"]}, "lvl1-1": [{"none": None, "inf": [{"a": {"a": "a"}}]}]} else: to_fuzz = self.config.generate_json(self.config.grammar_path) run = "{0} http://127.0.0.1:8080/fuzzer.html".format(self.config.browser_auto) config = PJFConfiguration(Namespace(json=to_fuzz, html=TOOLS_DIR, ports=self.config.ports, content_type="text/plain", debug=True, nologo=True, level=2, utf8=self.config.utf8, indent=self.config.indent, notify=True, fuzz_web=True, strong_fuzz=self.config.strong_fuzz, process_to_monitor=run, recheck_ports=False)) server = PJFServer(config) server.run() print "[\033[92mINFO\033[0m] Available URLs" for url in self.get_urls(): print "[\033[92m*\033[0m] {0}".format(url) try: while True: time.sleep(1) except KeyboardInterrupt: server.stop() except Exception as e: raise PJFBaseException(e.message)
def browser_autopwn(self): try: from tools import TOOLS_DIR if not self.config.auto: to_fuzz = {'lvl1': {"lvl2": [1, 1.0, "True"]}, "lvl1-1": [{"none": None, "inf": [{"a": {"a": "a"}}]}]} else: to_fuzz = self.config.generate_json(self.config.grammar_path) run = "{0} http://127.0.0.1:8080/fuzzer.html".format(self.config.browser_auto) config = PJFConfiguration(Namespace(json=to_fuzz, html=TOOLS_DIR, ports=self.config.ports, content_type="text/plain", debug=True, nologo=True, level=2, utf8=self.config.utf8, indent=self.config.indent, notify=True, strong_fuzz=self.config.strong_fuzz, process_to_monitor=run, recheck_ports=False)) monitor = PJFProcessMonitor(config) server = PJFServer(config) server.run() try: while True: monitor.start_monitor(standalone=False) except KeyboardInterrupt: monitor.shutdown() server.stop() except Exception as e: raise PJFBaseException(e.message)
def spawn(self, cmd, stdin_content="", stdin=False, shell=False, timeout=2): """ Spawn a new process using subprocess """ try: if type(cmd) != list: raise PJFInvalidType(type(cmd), list) if type(stdin_content) != str: raise PJFInvalidType(type(stdin_content), str) if type(stdin) != bool: raise PJFInvalidType(type(stdin), bool) self._in = stdin_content try: self.process = subprocess.Popen(cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE, shell=shell) self.finish_read(timeout, stdin_content, stdin) if self.process.poll() is not None: self.close() except KeyboardInterrupt: return except OSError: raise PJFProcessExecutionError("Binary <%s> does not exist" % cmd[0]) except Exception as e: raise PJFBaseException(e.message)
def fuzz_stdin(self): try: result = PJFExternalFuzzer(self.config).execute(json_eval.dumps(self.config.json)) if result: sys.stdout.write(result) else: self.fuzz() except Exception as e: raise PJFBaseException(e.message)
def get_urls(self): try: for iface in netifaces.interfaces(): net = netifaces.ifaddresses(iface) if netifaces.AF_INET in net: yield "http://{0}:{1}/fuzzer.html".format(net[netifaces.AF_INET][0]['addr'], self.config.ports["servers"]["HTTP_PORT"]) except Exception as e: raise PJFBaseException(e.message)
def start_http_server(self): try: server = PJFServer(self.config) server.run() try: while True: time.sleep(1) except KeyboardInterrupt: server.stop() except Exception as e: raise PJFBaseException(e.message)
def get_fuzzed(self, indent=False, utf8=False): """ Return the fuzzed object """ try: if "array" in self.json: return self.fuzz_elements(dict(self.json))["array"] else: return self.fuzz_elements(dict(self.json)) except Exception as e: raise PJFBaseException(e.message)
def custom_html(self, filepath): """ Serve custom HTML page """ try: response.headers.append("Access-Control-Allow-Origin", "*") response.headers.append("Accept-Encoding", "identity") response.headers.append("Content-Type", "text/html") return static_file(filepath, root=self.config.html) except Exception as e: raise PJFBaseException(e.message)
def fuzz_command_line(self): try: with tempfile.NamedTemporaryFile(delete=False) as temp_file: temp_file.write(json_eval.dumps(self.config.json)) temp_file.close() setattr(self, "temp_file_name", temp_file.name) if self.config.debug: print "[\033[92mINFO\033[0m] Generated temp file \033[91m%s\033[0m" % self.config.temp_file_name result = PJFExternalFuzzer(self.config).execute(self.config.temp_file_name) with open(self.config.temp_file_name, "wb") as fuzzed: fuzzed.write(result) fuzzed.close() except Exception as e: raise PJFBaseException(e.message)
def fuzz_external(self, stdin_input=False): try: import shlex import os dir_name = "testcase_{0}".format( os.path.basename(shlex.split(self.config.command[0])[0])) try: f = [0] for (_, _, filenames) in os.walk(dir_name): f.extend([ int(t.split("_")[1].split(".")[0]) for t in filenames ]) break last = max(f) + 1 except OSError: last = 0 j = PJFFactory(self.config) j_fuzz = j.fuzzed if not stdin_input: with tempfile.NamedTemporaryFile(delete=False) as temp_file: temp_file.write(j_fuzz) temp_file.close() setattr(self.config, "temp_file_name", temp_file.name) if self.config.debug: print "[\033[92mINFO\033[0m] Generated temp file \033[91m%s\033[0m" % self.config.temp_file_name result = PJFExternalFuzzer(self.config).execute_sigsegv( self.config.temp_file_name) else: setattr(self.config, "temp_file_name", False) result = PJFExternalFuzzer(self.config).execute_sigsegv(j_fuzz) if result: print "[\033[92mINFO\033[0m] Program crashed with \033[91mSIGSEGV\033[0m/\033[91mSIGABRT\033[0m" if self.config.debug: print "[\033[92mINFO\033[0m] Saving testcase..." try: os.mkdir(dir_name) except OSError: pass with open("{0}/testcase_{1}.json".format(dir_name, last), "wb") as t: t.write(j_fuzz) t.close() else: if self.config.temp_file_name: os.unlink(self.config.temp_file_name) print "[\033[92mINFO\033[0m] Program exited normally" except Exception as e: raise PJFBaseException(e.message)
def save_testcase(self, ip, testcases): try: count = 0 dir_name = "testcase_{0}".format(ip) print "[\033[92mINFO\033[0m] Client {0} seems to not respond anymore, saving testcases".format(ip) try: os.mkdir(dir_name) except OSError: pass for test in testcases: with open("{0}/testcase_{1}.json".format(dir_name, count), "wb") as t: t.write(test) t.close() count += 1 except Exception as e: raise PJFBaseException(e.message)
def serve(self): """ Serve fuzzed JSON object """ try: fuzzed = self.json.fuzzed if self.config.fuzz_web: self.client_queue.put((request.environ.get('REMOTE_ADDR'), fuzzed)) response.headers.append("Access-Control-Allow-Origin", "*") response.headers.append("Accept-Encoding", "identity") response.headers.append("Content-Type", self.config.content_type) if self.config.notify: PJFTestcaseServer.send_testcase(fuzzed, '127.0.0.1', self.config.ports["servers"]["TCASE_PORT"]) yield fuzzed except Exception as e: raise PJFBaseException(e.message)
def __contains__(self, items): """ Check if JSON object contains a key """ try: if type(items) != list: raise PJFInvalidType(items, list) ret = 0 for item in items: for key in self.json: if isinstance(self.json[key], PJFFactory): ret += item in self.json[key] elif item == key: ret += 1 return len(items) == ret except Exception as e: raise PJFBaseException(e.message)
def start_file_fuzz(self): try: with open(self.config.json_file, "rb") as json_file: if not self.config.strong_fuzz: setattr(self, "json", literal_eval(json_file.read())) json = PJFFactory(self.config) else: try: setattr(self, "json", literal_eval(json_file.read())) except: json_file.seek(0) setattr(self, "json", json_file.read()) json = PJFFactory(self.config) json_file.close() with open(self.config.json_file, "wb") as json_file: json_file.write(json.fuzzed) except Exception as e: raise PJFBaseException(e.message)
def handle(self, sock): """ Handle the actual TCP connection """ try: size = struct.unpack("<I", sock.recv(4))[0] data = "" while len(data) < size: data += sock.recv(size - len(data)) if len(self.testcase) >= 100: del self.testcase self.testcase = list() self.testcase.append(data) sock.close() except socket.error as e: raise PJFSocketError(e.message) except Exception as e: raise PJFBaseException(e.message)
def shutdown(self, *args): """ Shutdown the running process and the monitor """ try: self._shutdown() if self.process: self.process.wait() self.process.stdout.close() self.process.stdin.close() self.process.stderr.close() self.finished = True self.send_testcase('', '127.0.0.1', self.config.ports["servers"]["TCASE_PORT"]) self.logger.debug( "[{0}] - PJFProcessMonitor successfully completed".format( time.strftime("%H:%M:%S"))) except Exception as e: raise PJFBaseException(e.message)
def send_testcase(json, ip, port): """ Send a raw testcase """ try: json = struct.pack("<I", len(json)) + json try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((ip, int(port))) s.send(json) s.shutdown(socket.SHUT_RDWR) s.close() return True except socket.error: return False except socket.error as e: raise PJFSocketError(e.message) except Exception as e: raise PJFBaseException(e.message)
def fuzzed(self): """ Get a printable fuzzed object """ try: if self.config.strong_fuzz: fuzzer = PJFMutators(self.config) if self.config.url_encode: return urllib.quote(fuzzer.fuzz(json.dumps(self.config.json))) else: if type(self.config.json) in [list, dict]: return fuzzer.fuzz(json.dumps(self.config.json)) else: return fuzzer.fuzz(self.config.json) else: if self.config.url_encode: return urllib.quote(self.get_fuzzed(self.config.indent, self.config.utf8)) else: return self.get_fuzzed(self.config.indent, self.config.utf8) except Exception as e: raise PJFBaseException(e.message)
def save_testcase(self, testcase): """ Save all testcases collected during monitoring """ try: if self.config.debug: print "[\033[92mINFO\033[0m] Saving testcase..." dir_name = "testcase_{0}".format( os.path.basename( shlex.split(self.config.process_to_monitor)[0])) try: os.mkdir(dir_name) except OSError: pass for test in testcase: with open( "{0}/testcase_{1}.json".format(dir_name, self.testcase_count), "wb") as t: t.write(test) t.close() self.testcase_count += 1 except Exception as e: raise PJFBaseException(e.message)
def start_process_monitor(self): try: PJFProcessMonitor(self.config).start_monitor() except Exception as e: raise PJFBaseException(e.message)
def fuzz(self): try: json = PJFFactory(self.config) sys.stdout.write("{0}\n".format(json.fuzzed)) except Exception as e: raise PJFBaseException(e.message)