class Scenario(object): def __init__(self, scenario_name): self.file_manager = FileManager() #Variables self.scenario_name = scenario_name self.scenario_id = get_unique_id(length=8) now = datetime.now() self.creation_date = now.strftime("%d/%m/%Y %H:%M:%S") self.last_accessed = self.creation_date[:] self.exploit_info = ExploitInfo("", "", "") self.vulnerability_info = VulnerabilityInfo("", "", "", "") self.machines = dict() def setExploitInfo(self, exploit_info): """ Sets the exploit info for this scenario :param exploit_info: Object which carries the exploit info """ self.exploit_info = exploit_info def setVulnerabilityInfo(self, vulnerability_info): """ Sets the vulnerability info for this scenario :param vulnerability_info: Object which carries the vulnerability info """ self.vulnerability_info = vulnerability_info def addVM(self, vm): """ Adds a new virtual machine to this scenario :param vm: Object which carries the virtual machine data """ self.machines[vm.name] = vm def dictionary(self): """ Generates a dictionary for the Scenario object :return: A dictionary with Scenario data """ scenario_dict = dict() scenario_dict["scenario_name"] = self.scenario_name scenario_dict["scenario_id"] = self.scenario_id scenario_dict["creation_date"] = self.creation_date scenario_dict["last_accessed"] = self.last_accessed scenario_dict["exploit_info"] = self.exploit_info.dictionary( ) if self.exploit_info else dict() scenario_dict[ "vulnerability_info"] = self.vulnerability_info.dictionary( ) if self.vulnerability_info else dict() scenario_dict["machines"] = dict() for name in self.machines: scenario_dict["machines"][name] = self.machines[name].dictionary() return scenario_dict def generateScenario(self, scenario_name): """ Generates a scenario JSON file :param scenario_name: String with the scenario name :return: JSON file containing all the scenario data """ json_dict = self.dictionary() json_name = self.scenario_name + ".json" with open( self.file_manager.getScenariosPath() / scenario_name / "JSON" / json_name, 'w') as outfile: json.dump(json_dict, outfile) json_string = json.dumps(json_dict, indent=2) print(json_string) return json_string
class VagrantManager(object): def __init__(self): self.file_manager = FileManager() self.vagrant_file = VagrantFile() self.scenario_manager = ScenarioManager() def createVagrantFiles(self, scenario_name): """ Creates a vagrant file per machine in a scenario :param scenario_json: String with the scenario name :return: True if vagrant files were successfully created """ self.file_manager.createMachineFolders(scenario_name) scenario_json = self.scenario_manager.getScenario(scenario_name) for machine_name in scenario_json["machines"]: machine = scenario_json["machines"][machine_name] machine_path = self.file_manager.getScenariosPath( ) / scenario_name / "Machines" / machine_name print( self.vagrant_file.vagrantFilePerMachine(machine, machine_path)) result = {"result": True} return result def runVagrantUp(self, scenario_name): """ Executes the vagrant up command for each machine in the scenario :param scenario_name: String with the scenario name :return: True if the vagrant up commands were successfully executed """ self.createVagrantFiles(scenario_name) scenario_json = self.scenario_manager.getScenario(scenario_name) for machine_name in scenario_json["machines"]: machine_path = self.file_manager.getScenariosPath( ) / scenario_name / "Machines" / machine_name if not os.path.exists(machine_path): # Proceed if path exists return os.chdir(machine_path) process = subprocess.Popen(['vagrant', 'up'], stdout=subprocess.PIPE, universal_newlines=True) while True: output = process.stdout.readline() if output == '' and process.poll() is not None: break if output: print(output.strip()) result = {"result": True} return result def sendCommand(self, scenario_name, machine_name, command, default_timeout=5, show_output=True): #First we need to move to the directory of the given machine machine_path = self.file_manager.getScenariosPath( ) / scenario_name / "Machines" / machine_name #using "vagrant ssh -c 'command' <machine>" will only try to execute that command and return, CHANGE THIS connect_command = "vagrant ssh -c '{}' {}".format( command, machine_name) sshProcess = subprocess.Popen(connect_command, cwd=machine_path, stdin=subprocess.PIPE, stdout=subprocess.PIPE, universal_newlines=True, shell=True, bufsize=0) #wait for the execution to finish, process running on different shell sshProcess.wait() sshProcess.stdin.close() return_code = sshProcess.returncode if show_output: for line in sshProcess.stdout: if line == "END\n": break print(line, end="") for line in sshProcess.stdout: if line == "END\n": break print(line, end="") return return_code def restartVM(self, machine_name): pass def haltVM(self, machine_name): pass def testNetworkPing(self, scenario_name, machine_name, destination_machine_name, count=1): if self.scenario_manager.scenarioExists(scenario_name): scenario_data = self.scenario_manager.getScenario(scenario_name) try: machines = scenario_data['machines'] machine_to_ping = machines[destination_machine_name] machine_to_ping_network_settings = machine_to_ping[ 'network_settings'] destination_ip = machine_to_ping_network_settings['ip_address'] ping_command = "ping -c {} {}".format(count, destination_ip) return_code = self.sendCommand(scenario_name, machine_name, ping_command) if return_code == 0: print("Ping Succesful") return True elif return_code == 1: print("No answer from %s" % destination_machine_name) return False else: print("Another error as ocurred") return False except KeyError: print("Machines not defined for this Scenario") return False else: print("Scenario %s not found" % scenario_name) return False def getAvailableBoxes(self): """ Gets the available boxes in the Vagrant context :return: A list of string with the available boxes """ # Variables boxes = {} boxNum = 0 boxlist = subprocess.check_output("vagrant box list", shell=True) boxlist = str(boxlist) boxlist = re.sub(r"(^[b']|'|\s(.*?)\\n)", " ", boxlist) boxlist = boxlist.split(" ") boxlist = filter(None, boxlist) print("Loading available Vanilla VMs") for boxName in boxlist: boxNum = boxNum + 1 boxes[boxNum] = boxName print("[ " + str(boxNum) + " ]" + boxName) return boxes
class ScenarioManager(object): def __init__(self): self.file_manager = FileManager() def createScenario(self, scenario_name): """ Creates a new scenario which includes the folders and the scenario JSON file :param scenario_name: String with the scenario name :return: True if the new scenario was successfully created """ #Folder creation moved to FileManager self.file_manager.createScenarioFolders(scenario_name) scenario = Scenario(scenario_name) scenario.generateScenario(scenario_name) result = {"result": True} return result def getScenarios(self): """ Gets the available scenarios :return: A list of strings with the available scenarios """ # Variables scenarios = os.listdir(self.file_manager.getScenariosPath()) scenarios_dict = {"scenarios": scenarios} return scenarios_dict def scenarioExists(self, scenario_name): """ Check if a scenario exists :param scenario_name: String with the scenario name :return: False if the scenario JSON file does not exist and the path to the JSON file if it exist """ scenario_dir_path = self.file_manager.getScenariosPath( ) / scenario_name / "JSON" if not os.path.isdir(scenario_dir_path): print("Scenario %s directory not found" % scenario_name) return False else: scenario_json_path = scenario_dir_path / ''.join( [scenario_name, ".json"]) if not os.path.exists(scenario_json_path): print("Scenario %s json not found" % scenario_name) return None else: return scenario_json_path def getScenario(self, scenario_name): """ Gets the scenario as a JSON file :param scenario_name: String with the scenario name :return: JSON file with the scenario info """ scenario_json_path = self.scenarioExists(scenario_name) if scenario_json_path: try: with open(scenario_json_path) as json_file: scenario_json = json.load(json_file) return scenario_json except: print("Something went wrong while retrieving Scenario JSON") def editScenario(self, scenario_json): """ Edits a current scenario with a JSON file :param scenario_name: String with the scenario name :param scenario_json: JSON file with the new scenario :return: True if the scenario has been successfully edited, otherwise False """ scenario_name = scenario_json["scenario_name"] scenario_json_path = self.scenarioExists(scenario_name) if scenario_json_path: with open(scenario_json_path, 'w+') as outfile: outfile.write(json.dumps(scenario_json, indent=2)) outfile.close() #THIS IS A PLACEHOLDER #It will try to create the folders every time the scenario is edited #new_scenario_dict = json.loads(new_scenario) return True else: return False
class ScenarioManager(): def __init__(self, db_manager=DatabaseManager()): self.file_manager = FileManager() self.db_manager = db_manager self.scenarios_dict = self._initializeFromDatabase() def _initializeFromDirectory(self): """ Initializes the scenario's runtime objects using data from the host folders. :return: Dictionary containing scenario's data """ # Variables scenarios_dict = dict() scenarios = os.listdir(self.file_manager.getScenariosPath()) for scenario_name in scenarios: json_name = ''.join([scenario_name, ".json"]) with open( self.file_manager.getScenarioJSONPath(scenario_name) / json_name) as outfile: scenario_dict = json.load(outfile) scenario = Scenario(scenario_name).objectFromDictionary( scenario_dict) scenarios_dict[scenario_name] = scenario return scenarios_dict def _initializeFromDatabase(self): """ Pre-populates the database with scenarios. :return: Dictionary containing scenario's data """ # Variables scenarios_dict = dict() scenarios = self.db_manager.getScenarios() for raw_scenario in scenarios: del raw_scenario["_id"] scenario_name = raw_scenario["scenario_name"] scenario = Scenario(scenario_name).objectFromDictionary( raw_scenario) scenarios_dict[scenario_name] = scenario return scenarios_dict def newEmpty(self, scenario_name): """ Creates a new scenario which includes the folders and the scenario JSON file :param scenario_name: String with the scenario name :return: Response object containing the status of the request """ #Folder creation moved to FileManager response = Response() if scenario_name not in self.scenarios_dict: #self.file_manager.createScenarioFolders(scenario_name) scenario = Scenario(scenario_name) self.scenarios_dict[scenario_name] = scenario #self._saveScenarioAsJSON(scenario) self.db_manager.insertScenario(scenario.dictionary().copy()) response.setResponse(True) response.setBody(scenario.dictionary()) else: response.setResponse(False) response.setReason('Scenario already exist') response.setBody(dict()) return response.dictionary() def getAll(self): """ Gets the available scenarios :return: Response object containing the status of the request """ # Variables scenarios_dict = { "scenarios": [ self.scenarios_dict[s].scenario_name for s in self.scenarios_dict ] } response = Response() response.setResponse(True) response.setBody(scenarios_dict) return response.dictionary() def getOne(self, scenario_name): """ Gets the scenario as a JSON file :param scenario_name: String with the scenario name :return: Response object containing the status of the request """ response = Response() if scenario_name in self.scenarios_dict: response.setResponse(True) response.setBody(self.scenarios_dict[scenario_name].dictionary()) else: response.setResponse(False) response.setReason('Scenario doesn\'t exist') response.setBody(dict()) return response.dictionary() def editOne(self, scenario_json): """ Edits a current scenario with a JSON file :param scenario_json: JSON file with the new scenario :return: Response object containing the status of the request """ response = Response() print(scenario_json) scenario_name = scenario_json["scenario_name"] if scenario_name in self.scenarios_dict: if "machines" in scenario_json: for machine in scenario_json["machines"]: if scenario_json["machines"][machine]["uuid"] == "": new_uuid = uuid.uuid4() new_uuid = str(new_uuid).replace('-', '') print("Unique id: ", new_uuid) scenario_json['machines'][machine]['uuid'] = new_uuid scenario_json = Scenario(scenario_name).objectFromDictionary( scenario_json) self.scenarios_dict[scenario_name] = scenario_json #self._saveScenarioAsJSON(new_scenario) self.db_manager.editScenario(scenario_json.dictionary().copy()) response.setResponse(True) response.setBody(self.scenarios_dict[scenario_name].dictionary()) else: response.setReason('Scenario doesn\'t exist') response.setResponse(False) response.setBody(dict()) return response.dictionary() def deleteOne(self, scenario_name): """ Deletes one scenario from the database. :param scenario_name: Scenario's name string :return: Response object containing the status of the request """ response = Response() if scenario_name in self.scenarios_dict: deleted_scenario = self.scenarios_dict.pop(scenario_name) #self.file_manager.deleteScenariosFolder(scenario_name) self.db_manager.deleteScenario(scenario_name) response.setResponse(True) response.setBody(deleted_scenario.dictionary()) else: response.setResponse(False) response.setReason('Scenario doesn\'t exist') response.setBody(dict()) return response.dictionary() def scenarioExists(self, scenario_name): """ Check if a scenario exists. :param scenario_name: String with the scenario name :return: False if the scenario JSON file does not exist and the path to the JSON file if it exist """ scenario_dir_path = self.file_manager.getScenariosPath( ) / scenario_name / "JSON" if not os.path.isdir(scenario_dir_path): print("Scenario %s directory not found" % scenario_name) return False else: scenario_json_path = scenario_dir_path / ''.join( [scenario_name, ".json"]) if not os.path.exists(scenario_json_path): print("Scenario %s json not found" % scenario_name) return None else: return scenario_json_path def _saveScenarioAsJSON(self, scenario): """ Saves a scenario as a JSON file :param scenario: Scenario's name string :return: None """ scenario_json_path = self.file_manager.getScenarioJSONPath( scenario.scenario_name) / ''.join( [scenario.scenario_name, ".json"]) if scenario_json_path: with open(scenario_json_path, 'w+') as outfile: outfile.write(json.dumps(scenario.dictionary(), indent=2)) outfile.close() return
class DatabaseManager(): def __init__(self): self.file_manager = FileManager() self.url = ConfigManager().mongoURL() self.db_name = "soft_prac" self.scenarios_col_name = 'scenarios' self.exploits_col_name = 'exploits' self.vulnerabilities_col_name = 'vulnerabilities' self.client = pymongo.MongoClient(self.url) self.db = self.client[self.db_name] self.scenarios_col = self.db[self.scenarios_col_name] self.exploits_col = self.db[self.exploits_col_name] self.vulnerabilities_col = self.db[self.vulnerabilities_col_name] self.addScenariosToDB() self.addExploitsToDB() self.addVulnerabilitiesToDB() def _initializeScenariosFromDirectory(self): """ Initializes the scenario's runtime objects using data from the host folders. :return: Dictionary containing scenario's data """ # Variables scenarios_dict = dict() scenarios = os.listdir(self.file_manager.getScenariosPath()) for scenario_name in scenarios: json_name = ''.join([scenario_name, ".json"]) with open( self.file_manager.getScenarioJSONPath(scenario_name) / json_name) as outfile: scenario_dict = json.load(outfile) scenario = Scenario(scenario_name).objectFromDictionary( scenario_dict) scenarios_dict[scenario_name] = scenario return scenarios_dict def addScenariosToDB(self): """ Pre-populates the database with scenarios. :return: None """ scenarios_to_add = ['Scenario_1', 'Scenario_3'] currentScenarios = self.getScenarios() scenarios_list = [ scenario['scenario_name'] for scenario in currentScenarios ] scenarios_set = set(scenarios_list) for scenario_name in scenarios_to_add: if scenario_name not in scenarios_set: json_name = ''.join([scenario_name, ".json"]) with open( self.file_manager.getScenarioJSONPath(scenario_name) / json_name) as outfile: scenario_dict = json.load(outfile) scenario = Scenario(scenario_name).objectFromDictionary( scenario_dict) self.insertScenario(scenario.dictionary().copy()) return def addExploitsToDB(self): """ Pre-populates the database with exploits. :return: None """ exploits_to_add = [ 'Django_3_0_Cross-Site_Request_Forgery_Token_Bypass' ] currentExploits = self.getExploits() exploits_list = [exploit['name'] for exploit in currentExploits] exploits_set = set(exploits_list) for exploit_name in exploits_to_add: if exploit_name not in exploits_set: json_name = ''.join([exploit_name, ".json"]) with open( self.file_manager.getExploitJSONPath(exploit_name) / json_name) as outfile: exploit_dict = json.load(outfile) exploit = Exploit().objectFromDictionary(exploit_dict) self.insertExploit(exploit.dictionary().copy()) return def addVulnerabilitiesToDB(self): """ Pre-populates the database with vulnerabilities. :return: None """ vulnerabilities_to_add = ['rConfig_3_9_searchColumn_SQL_Injection'] currentVulnerabilities = self.getVulnerabilities() vulnerabilities_list = [ vulnerability['name'] for vulnerability in currentVulnerabilities ] vulnerabilities_set = set(vulnerabilities_list) for vulnerability_name in vulnerabilities_to_add: if vulnerability_name not in vulnerabilities_set: json_name = ''.join([vulnerability_name, ".json"]) with open( self.file_manager.getVulnerabilityJSONPath( vulnerability_name) / json_name) as outfile: vulnerability_dict = json.load(outfile) vulnerability = Vulnerability().objectFromDictionary( vulnerability_dict) self.insertVulnerability(vulnerability.dictionary().copy()) return #CRUD: CREATE, READ, UPDATE and DELETE #Scenarios def insertScenario(self, scenario_json): """ Inserts a scenario into the database. :param scenario_json: Scenario's JSON file to be inserted :return: Inserted document's id """ doc = self.scenarios_col.insert_one(scenario_json) return doc.inserted_id def getScenarioNames(self): """ Gets the scenario's names. :return: A list containing the scenarios names """ return [doc['scenario_name'] for doc in self.scenarios_col.find()] def getScenarios(self): """ Gets the scenarios from the databases. :return: A list containing scenarios in the database """ return [doc for doc in self.scenarios_col.find()] def getScenario(self, scenario_name): """ Gets a specific scenario from the database. :param scenario_name: Scenario's name string :return: A list containing the scenario retrieved from the database """ query = {'scenario_name': scenario_name} return [doc for doc in self.scenarios_col.find(query)] def editScenario(self, scenario_json): """ Edits a scenario in the database. :param scenario_json: JSON file containing scenario's data :return: Modified document's id """ query = {'scenario_name': scenario_json['scenario_name']} new_doc = {"$set": scenario_json} doc = self.scenarios_col.update_one(query, new_doc) return doc.modified_count def deleteScenario(self, scenario_name): """ Deletes a scenario from the database. :param scenario_name: Scenario's name string :return: Deleted document's id """ query = {'scenario_name': scenario_name} doc = self.scenarios_col.delete_one(query) return doc.deleted_count #Exploits def insertExploit(self, exploit_json): """ Inserts a scenario in the database. :param exploit_json: JSON file containing the exploit's data :return: Inserted document's id """ doc = self.exploits_col.insert_one(exploit_json) return doc.inserted_id def getExploitNames(self): """ Gets the exploits names from the database. :return: A list containing the exploits' names """ return [doc['name'] for doc in self.exploits_col.find()] def getExploits(self): """ Gets the exploits stored in the database. :return: A list containing the stored exploits' data """ return [doc for doc in self.exploits_col.find()] def getExploit(self, exploit_name): """ Gets an exploit from the database. :param exploit_name: Exploit's name string :return: A list containing the exploit data """ query = {'name': exploit_name} return [doc for doc in self.exploits_col.find(query)] def editExploit(self, exploit_json): """ Edits an exploit in the database. :param exploit_json: JSON file containing the exploit's data :return: Modified document's id """ query = {'name': exploit_json['name']} new_doc = {"$set": exploit_json} doc = self.exploits_col.update_one(query, new_doc) return doc.modified_count def deleteExploit(self, exploit_name): """ Deletes a exploit from the database. :param exploit_name: Exploit's name string :return: Deleted document's id """ query = {'name': exploit_name} doc = self.exploits_col.delete_one(query) return doc.deleted_count #Vulnerabilities def insertVulnerability(self, vulnerability_json): """ Inserts a vulnerability in the database. :param vulnerability_json: JSON file containing the vulnerability's data :return: Inserted document's id """ doc = self.vulnerabilities_col.insert_one(vulnerability_json) return doc.inserted_id def getVulnerabilityNames(self): """ Gets the vulnerabilities names from the database. :return: A list containing the vulnerability's names """ return [doc['name'] for doc in self.vulnerabilities_col.find()] def getVulnerabilities(self): """ Gets the vulnerabilities stored in the database. :return: A list containing the stored vulnerabilities' data """ return [doc for doc in self.vulnerabilities_col.find()] def getVulnerability(self, vulnerability_name): """ Gets a vulnerability from the database. :param vulnerability_name: Vulnerability's name string :return: A list containing the vulnerability's data """ query = {'name': vulnerability_name} return [doc for doc in self.vulnerabilities_col.find(query)] def editVulnerability(self, vulnerability_json): """ Edits a vulnerability in the database. :param vulnerability_json: JSON file containing the vulnerability's data :return: Modified document's id """ query = {'name': vulnerability_json['name']} new_doc = {"$set": vulnerability_json} doc = self.vulnerabilities_col.update_one(query, new_doc) return doc.modified_count def deleteVulnerability(self, vulnerability_name): """ Deletes a vulnerability from the database. :param vulnerability_name: Vulnerability's name string :return: Deleted document's id """ query = {'name': vulnerability_name} doc = self.vulnerabilities_col.delete_one(query) return doc.deleted_count