def check_session(self): """ """ valid = False try: if config.MONITOR_DIR and os.path.isdir(config.MONITOR_DIR): if config.SESSION: # validate session if config.SESSION not in os.listdir(config.MONITOR_DIR): utility.msg("Session %s not found in %s" % (config.SESSION, config.MONITOR_DIR)) else: valid = True else: # fetch latest version tmp = os.listdir(config.MONITOR_DIR) if len(tmp) <= 0: utility.msg("No running sessions found", ERROR) valid = False else: config.SESSION = tmp[-1] utility.msg("Setting session to %s" % config.SESSION, LOG) valid = True else: utility.msg("Directory '%s' not found" % config.MONITOR_DIR, ERROR) valid = False except Exception, e: utility.msg("Error checking session: %s" % e, ERROR) valid = False
def check_session(self): """ """ valid = False try: if config.MONITOR_DIR and os.path.isdir(config.MONITOR_DIR): valid = True except Exception, e: utility.msg("Error checking session: %s" % e, ERROR) valid = False
def check_node(): """ Validate this node by ensuring it's properly authd to the remote server. We'll also perform a few checks to ensure the session information is available to us. """ valid = False if config.MONITOR_DIR and (config.NODE_ID or nr.register_node()): valid = True # pry open MONITOR_DIR and check for SESSION or grab latest if config.MONITOR_DIR and os.path.isdir(config.MONITOR_DIR): if config.SESSION: # validate session exists if config.SESSION not in os.listdir(config.MONITOR_DIR): utility.msg("Session %s not found at %s" % (config.SESSION, config.MONITOR_DIR)) else: valid = True else: # grab latest session tmp = os.listdir(config.MONITOR_DIR) if len(tmp) <= 0: utility.msg("No running sessions found", ERROR) valid = False else: config.SESSION = tmp[-1] utility.msg("Setting session to %s" % config.SESSION, LOG) valid = True else: utility.msg("Directory '%s' not found" % config.MONITOR_DIR, ERROR) valid = False return valid
def check_server(): """ Simple check server """ url = 'http://{0}:{1}'.format(config.SERVER_IP, config.SERVER_PORT) try: response = requests.get(url) if response.status_code is 200: utility.msg("Server appears active.") return except: pass utility.msg("Could not reach server.", ERROR)
def get_status(self): """ """ try: status = None data = None base = config.MONITOR_DIR + "/fuzzer_stats" with open(base) as f: data = f.read().split("\n") # afl runs indefinitely, so we'll just grab the total cycles completed if len(data) > 0: status = "%s cycles" % data[0].split(":")[1].lstrip() except Exception, e: utility.msg("Failed to parse status update: %s" % e, ERROR) status = "Error"
def get_status(self): """ """ try: status = None data = None base = config.MONITOR_DIR + '/fuzzer_stats' with open(base) as f: data = f.read().split('\n') # afl runs indefinitely, so we'll just grab the total cycles completed if len(data) > 0: status = "%s cycles" % data[0].split(':')[1].lstrip() except Exception, e: utility.msg("Failed to parse status update: %s" % e, ERROR) status = "Error"
def load_fuzzer(): """ Dynamically load the specified fuzzer """ load = importlib.import_module("src.fuzzers") modules = list(pkgutil.iter_modules(load.__path__)) for mod in modules: # pull up the module and iterate over its components, # stop once we find the correct class to invoke. dp = mod[0].find_module(mod[1]).load_module(mod[1]) for e in dir(dp): x = getattr(dp, e) if e and e != "Fuzzer" and config.SESSION_FUZZER in e and\ issubclass(x, Fuzzer): utility.msg("Loaded fuzzer %s" % e, LOG) return x() return None
def monitor(): """ """ # known crashes; key is idx (top level folder before crash info); value is known_crashes = {} try: utility.msg("Initializing monitor for node %s..." % config.NODE_ID) # check monitor dir if not os.path.isdir(config.MONITOR_DIR): utility.msg("Directory %s not found" % config.MONITOR_DIR, ERROR) return # # prior to monitor loop, lets ensure we're synced with upstream by providing # current status # # update status nr.send_status_update() # grab a listing of all current crashes; set of "knowns" known_crashes = fetch_crashes() # register crashes nr.register_crash(known_crashes) # enter the mainline monitor loop log_hash = nr.log_hash() while True: # we only push when the hash of the status file changes, or a # new fault is detected tmp_hsh = nr.log_hash() if tmp_hsh != log_hash: # status update nr.send_status_update() log_hash = tmp_hsh # pull crashes and diff for new ones temporal_crash = fetch_crashes() if len(temporal_crash) != len(known_crashes): # we have new crashes; ship them over temporal_crash = list(set(temporal_crash) - set(known_crashes)) nr.register_crash(temporal_crash) known_crashes += temporal_crash # sleep, now sleep(config.CHECK_INTERVAL) except KeyboardInterrupt: pass except Exception, e: utility.msg("Error during monitor: %s" % e, ERROR)
def register_crash(crash): """ Accepts a dictionary of {crash_idx : crash_file_path} and attempts to POST them to the remote server. If the server already has the crash data, it will not be sent. """ base = 'http://{0}:{1}'.format(config.SERVER_IP, config.SERVER_PORT) uri = '/node/%d/?crash={0}' % config.NODE_ID for (cidx, cpath) in crash.iteritems(): try: response = requests.get(base + uri.format(cidx)) if response.status_code is 200 and "Invalid crash" in response.content: # crash does not exist on server, POST it data = { "node_id": config.NODE_ID, "skey": sha256(config.SERVER_KEY).hexdigest(), "crash_idx": str(cidx), "crash": open(cpath).read() } response = requests.post(base + "/crash/", data=json.dumps(data)) if response.status_code is 200 and "Node updated" in response.content: utility.msg("Crash %s posted successfully" % cidx, LOG) else: utility.msg("Failed to post crash %s: %s" % (cidx, response.content), ERROR) except Exception, e: utility.msg("Error contacting remote server: %s" % e, ERROR)
def register_node(): """ Register the local node """ if not config.SERVER_IP: utility.msg("Server IP not configured", ERROR) return False data = { "skey": sha256(config.SERVER_KEY).hexdigest(), } # # POST the skey to the server; we should get back our assigned node ID # url = "http://{0}:{1}/register/".format(config.SERVER_IP, config.SERVER_PORT) try: response = requests.post(url, data=data) except Exception: return False if response.status_code is 200 and "Active" in response.content: node_id = findall("Node (.*?) Active", response.content)[0] _write_id(node_id) utility.msg("Node %s now assigned and active" % config.NODE_ID) return True else: utility.msg("Could not register node: %s" % response.content, ERROR) return False
def get_status(self): """ Parse the status file and pull the latest iteration update """ try: data = None spath = config.MONITOR_DIR + '/' + config.SESSION + '/' + 'status.txt' with open(spath) as f: data = f.read().split('\n') # chop it up status = None data = [x for x in data if len(x) > 0] if 'Test finished' in data[:-1]: status = 'Completed' else: (cidx, total) = findall("Iteration (.*?) of (.*?) :", data[-1])[0] status = '%s/%s' % (cidx, total) except Exception, e: utility.msg("Failed to parse status update: %s" % e, ERROR) status = "Error"
def monitor(fuzzy): """ """ # known crashes; key is idx (top level folder before crash info); value is known_crashes = {} try: utility.msg("Initializing monitor for node %s..." % config.NODE_ID) # check monitor dir if not os.path.isdir(config.MONITOR_DIR): utility.msg("Directory %s not found" % config.MONITOR_DIR, ERROR) return # # prior to monitor loop, lets ensure we're synced with upstream by providing # current set of crashes; dupes will be thrown out # # register crashes current_crashes = fuzzy.fetch_crashes() nr.register_crash(current_crashes) # baseline fuzzy.crashes = current_crashes while True: # status update nr.send_status_update(fuzzy.get_status()) # check for any new crashes temporal_crash = fuzzy.check_new_crashes() if temporal_crash: # we have new crashes; ship them over nr.register_crash(temporal_crash) # sleep, now sleep(config.CHECK_INTERVAL) except KeyboardInterrupt: pass except Exception, e: utility.msg("Error during monitor: %s" % e, ERROR)
def send_status_update(status): """ Update the server with a fuzzer's status """ try: data = { "node_id" : config.NODE_ID, "skey" : sha256(config.SERVER_KEY).hexdigest(), "state" : status } url = "http://{0}:{1}/status/".format(config.SERVER_IP, config.SERVER_PORT) response = requests.post(url, data=json.dumps(data)) if response.status_code is 200 and "Node Updated" in response.content: utility.msg("Status '%s' updated" % status, LOG) else: utility.msg("Failed to submit status: %s" % response.content, ERROR) except Exception, e: utility.msg("Failed to send status update: %s" % e, ERROR)
def send_status_update(status): """ Update the server with a fuzzer's status """ try: data = { "node_id": config.NODE_ID, "skey": sha256(config.SERVER_KEY).hexdigest(), "state": status } url = "http://{0}:{1}/status/".format(config.SERVER_IP, config.SERVER_PORT) response = requests.post(url, data=json.dumps(data)) if response.status_code is 200 and "Node Updated" in response.content: utility.msg("Status '%s' updated" % status, LOG) else: utility.msg("Failed to submit status: %s" % response.content, ERROR) except Exception, e: utility.msg("Failed to send status update: %s" % e, ERROR)
def send_status_update(): """ Pull the current iteration from the status file and post it to the remote server """ try: data = None spath = config.MONITOR_DIR + '/' + config.SESSION + '/' + 'status.txt' with open(spath) as f: data = f.read().split('\n') # chop it up status = None data = [x for x in data if len(x) > 0] if 'Test finished' in data[-1]: status = 'Completed' else: (cidx, total) = findall("Iteration (.*?) of (.*?) :", data[-1])[0] status = '%s/%s' % (cidx, total) # prepare to send data = { "node_id": config.NODE_ID, "skey": sha256(config.SERVER_KEY).hexdigest(), "iteration": status } url = 'http://{0}:{1}/status/'.format(config.SERVER_IP, config.SERVER_PORT) response = requests.post(url, data=json.dumps(data)) if response.status_code is 200 and "Node Updated" in response.content: utility.msg("Status '%s' updated" % status, LOG) else: utility.msg("Failed to submit status: %s" % response.content, ERROR) except Exception, e: utility.msg("Failed to send status update: %s" % e, ERROR)
def register_node(): """ Register the local node """ if not config.SERVER_IP: utility.msg("Server IP not configured", ERROR) return False data = { "skey": sha256(config.SERVER_KEY).hexdigest(), "fuzzer": config.SESSION_FUZZER, "session": config.SESSION_NAME } # # POST the skey to the server; we should get back our assigned node ID # url = "http://{0}:{1}/register/".format(config.SERVER_IP, config.SERVER_PORT) try: response = requests.post(url, data=data) except Exception: return False if response.status_code is 200 and "Active" in response.content: node_id = findall("Node (.*?) Active", response.content)[0] _write_id(node_id) utility.msg("Node %s now assigned and active" % config.NODE_ID) return True else: utility.msg("Could not register node: %s" % response.content, ERROR) return False
def register_crash(crash): """ Accepts a dictionary of {crash_idx : crash_file_path} and attempts to POST them to the remote server. If the server already has the crash data, it will not be sent. """ if len(crash.keys()) <= 0: return base = 'http://{0}:{1}'.format(config.SERVER_IP, config.SERVER_PORT) uri = '/node/%d/?crash={0}' % config.NODE_ID for (cidx, cpath) in crash.iteritems(): try: response = requests.get(base + uri.format(cidx)) if response.status_code is 200 and "Invalid crash" in response.content: # crash does not exist on server, POST it data = { "node_id": config.NODE_ID, "skey": sha256(config.SERVER_KEY).hexdigest(), "crash_idx": str(cidx), "crash": open(cpath).read() } response = requests.post(base + "/crash/", data=json.dumps(data)) if response.status_code is 200 and "Node updated" in response.content: utility.msg("Crash %s posted successfully" % cidx, LOG) else: utility.msg( "Failed to post crash %s: %s" % (cidx, response.content), ERROR) except Exception, e: utility.msg("Error contacting remote server: %s" % e, ERROR)