def insertQuestions_Answers(self): db = Database() f = open("Preprocessing/Question_Answers.txt", 'r') i = 0 while True: Question = Question = DataPreprocessing.removeSinqleQuotes( f.readline().rstrip()) if Question == "": print("------Finished reading------") break A1 = DataPreprocessing.removeSinqleQuotes(f.readline().rstrip()) A2 = DataPreprocessing.removeSinqleQuotes(f.readline().rstrip()) A3 = DataPreprocessing.removeSinqleQuotes(f.readline().rstrip()) CA_ID = f.readline().rstrip() cols = [ "Question", "Answer_1", "Answer_2", "Answer_3", "Correct_AnswerID" ] values = [ "'" + Question + "'", "'" + A1 + "'", "'" + A2 + "'", "'" + A3 + "'", "'" + str(CA_ID) + "'" ] conflict_fields = [ "Question", "Answer_1", "Answer_2", "Answer_3", "Correct_AnswerID" ] i += 1 print("-----Row " + (str)(i) + " -----") db.insert("Questions_Answers", cols, values, conflict_fields, '') print("Inserted Questions_Answers rows")
class CampaignExportManager: db = Database() def export_campaign_database(self, username, cid): # check user is admin # check campaign exists # check user has read access to campaign return def generate_database_(self): # get information (func) # check database export directory exists # check file doesn't exist # create campaign_name_unixtime # create database # encrypt # return file, return password return def get_information(self): return def database_file_storage_check(self): return
class StagerGeneration: # TODO: This needs cleaned up to ensure expandability with database changes. db = Database() NetProfMan = NetworkProfileManager() def generate_static_stagers(self, cid, user): ret_data = {} if self.db.campaign.Verify_UserCanAccessCampaign(user, cid): implant_info = self.db.implant.Get_AllImplantBaseFromCid(cid) if implant_info is not False: for implant in implant_info: ret_data[implant['title']] = { "description": implant['description'], "url": implant['callback_url'], "kill_date":implant['kill_date'], "encryption":implant['encryption'], "powershell_stager": self._generate_powershell_stager_string(implant), "docm_macro_stager": self._generate_docx_stager_string(implant), "stager_key": implant['stager_key']} return ret_data else: return ret_data def GenerateSingleStagerFile(self, cid, user, stager_type): # TODO: Create docx file download from template. if self.db.campaign.Verify_UserCanAccessCampaign(user, cid): if stager_type == "docx": return self._generate_docx_stager_file() return else: return False def _generate_docx_stager_string(self, implant_data): stager_list = [] if 'network_profiles' in implant_data: for element in implant_data['network_profiles']: raw = self.NetProfMan.get_docm_implant_stager(element, implant_data) if raw is not None: stager_list.append(raw) return stager_list else: return stager_list def _generate_powershell_stager_string(self, implant_data): # Calls the Network Profile Manager to see if a powershell stager exists, if not the network profile will return # a None value. Care should be taken to avoid comms which do not have any stager options to generate active # payloads. stager_list = [] if 'network_profiles' in implant_data: for element in implant_data['network_profiles']: raw = self.NetProfMan.get_powershell_implant_stager(element, implant_data) if raw is not None: stager_list.append(raw) return stager_list else: return stager_list
class AppManager: db = None def __init__(self): self.db = Database() @staticmethod def check_software_version(): # Returns "True" if the software is behind GitHubs master version file. url = "https://raw.githubusercontent.com/Ziconius/FudgeC2/master/version.txt" try: request_result = requests.get(url, timeout=0.5) master = request_result.content.decode() with open("../version.txt", 'r') as v_file: local_version_number = str(v_file.read()) if LooseVersion(master) > LooseVersion(local_version_number): return True else: return False except Exception as exception_text: print(exception_text) return False @staticmethod def get_software_verision_number(): try: with open("../version.txt", 'r') as v_file: local_version_number = str(v_file.read()) return local_version_number except Exception as exception_text: print(exception_text) return "0.0.0" def campaign_create_campaign(self, user, form): # Responsible for validating admin account, and campaign title exists. if self.db.user.User_IsUserAdminAccount(user) is True: if 'title' in form and 'description' in form: if form['title'].strip() != "": if self.db.campaign.create_campaign(user, form['title'].strip(), form['description'].strip()) is True: return True, "Campaign created successfully." else: return False, "Unknown error." else: return False, "You must supply both title and description values." else: return False, "You must supply both title and description values." else: return False, "You do not have admin permissions to create a campaign." def campaign_get_campaign_name_from_cid(self, cid): return self.db.campaign.Get_CampaignNameFromCID(cid) # TODO: Implement returning app logs to web app. def get_application_logs(self, username): # is user admin if not return false. if self.db.user.User_IsUserAdminAccount(username): return self.db.get_application_logs() else: return []
def insertNotifications(): db = Database() db_access = DataAccess() f = open("Preprocessing/Notifications.txt", 'r') i = 0 while True: Notification = DataPreprocessing.removeSinqleQuotes( f.readline().rstrip()) if Notification == "": print("------Finished reading------") break cols = [] values = [] noAttachment = True if "Attachment" in Notification: content = Notification.split(" Attachment: ") Notification = content[0] content = content[1].split("-") if content[1] == "GIF": rows = db_access.select("Tag", ["Tag"], ["Tag"], ["'" + content[0] + "'"], "") row = rows[0][0] if row is not None: # Check the existence of the tag used as an attachment. cols = ["Message", "Attachment", "Type"] values = [ "'" + Notification + "'", "'" + content[0] + "'", "'GIF'" ] noAttachment = False elif content[1] == "Button": cols = ["Message", "Attachment", "Type"] values = [ "'" + Notification + "'", "'" + content[0] + "'", "'Button'" ] noAttachment = False if noAttachment == True: cols = ["Message"] values = ["'" + Notification + "'"] conflict_fields = ["Message"] i += 1 print("-----Notification number :: " + (str)(i) + " -----") db.insert("Notification", cols, values, conflict_fields, "") print("Inserted Notification messages")
def __run__(self): d = Database() d.__deleteTables__() d.__createTables__() self.insertAnswers_and_keywords() self.insertQuestions_Answers() self.insertGifs() self.insertNotifications() print("Data preprocessing done!")
class ImplantResponseProcessor: db = Database() def _system_info(self, data): # Username: Kris # Hostname: DESKTOP - SUMPM3F # Domain: WORKGROUP # Local IP: 192.168.2.130 return None def process_command_response(self, command_id, command_response): a = self.db.implant.get_registered_implant_commands_by_command_id( command_id) b = ast.literal_eval(a['log_entry']) return command_response, None
def process_command_response(self, command_entry, raw_command_result): # Takes a module type value, and the result and passes the raw result to the module process_implant_response db = Database() a = db.implant.get_registered_implant_commands_by_command_id( command_entry) command_entry = a['log_entry'] host_data = None response_string = raw_command_result implant_module = self._get_module_object_by_type_( command_entry['type']) if implant_module is not None: response_string, host_data = implant_module.process_implant_response( raw_command_result, command_entry['args']) # failure checks required. return response_string, host_data
def insertGifs(): db = Database() file = open("Preprocessing/gifs.txt", "r") while True: line = file.readline() if (line == ''): print("EOF!") break arr = line.split(" ") name = "'" + arr[0].strip("\n") + "'" url = "'" + arr[1].strip("\n") + "'" tag = "'" + arr[2].strip("\n") + "'" db.insert("Tag", ["tag"], [tag], ["tag"], "") db.insert("Gifs", ["name", "url", "gif_tag"], [name, url, tag], "", "") print("Inserted Tags & Gifs")
class CampaignExportManager: export_db = None db = Database() file_dir = "Storage/ExportedCampaigns/" def test(self, filename, file_dir): # check file name for uniqueness. self.export_db = DbCreator(filename) a = self.export_db.Session.query(ExportedCampaign).all() return a def test_put(self, a, b, c, d): logs = ExportedCampaign(user=str(a), time=str(b), log_type=str(c), entry=str(d)) self.export_db.Session.add(logs) self.export_db.Session.commit() def _validate_user_(self, username, cid): if self.db.user.User_IsUserAdminAccount(username) is not False: if self.db.campaign.Verify_UserCanReadCampaign(username, cid) is not False: return True return False def get_encrypted_file(self, username, cid, filename): if self._validate_user_(username, cid) is False: return False a = os.listdir(self.file_dir) if filename in a: return filename else: return False def export_campaign_database(self, username, cid): if self._validate_user_(username, cid) is False: return False db = self._generate_database_(cid) if db is False: return False # db contains(filename, file_path, password) result = self.encrypt_file(db[0], db[1], db[2]) if result is False: return return result[0], result[1] def _generate_database_(self, cid): # DONE get information (func) # check database export directory exists # DONE check file doesn't exist # DONE create campaign_name_unixtime # DONE create database # DONE encrypt # DONE return file, return password password = str(''.join( random.choices(string.ascii_lowercase + string.ascii_uppercase + string.digits, k=12))) raw_logs = self.db.Log_GetCampaignActions(cid) campaign_name = self.db.campaign.Get_CampaignNameFromCID(cid) file_name = "{}_{}".format(campaign_name.replace(" ", "_"), int(time.time())) file_dir = "Storage/ExportedCampaigns/" a = os.listdir(file_dir) database = file_dir + file_name if file_name in a: return False b = self.test(file_name, file_dir) for x in raw_logs: self.test_put(raw_logs[x]['user'], raw_logs[x]['time'], raw_logs[x]['log_type'], raw_logs[x]['entry']) return file_name, file_dir, password def get_information(self): return def database_file_storage_check(self): return def encrypt_file(self, filename, file_path, password): password_provided = password # This is input in the form of a string password = password_provided.encode() # Convert to type bytes salt = b'salt_' # CHANGE THIS - recommend using a key from os.urandom(16), must be of type bytes kdf = PBKDF2HMAC(algorithm=hashes.SHA256(), length=32, salt=salt, iterations=100000, backend=default_backend()) key = base64.urlsafe_b64encode(kdf.derive(password)) from cryptography.fernet import Fernet # key = b'' # Use one of the methods to get a key (it must be the same when decrypting) input_file = file_path + filename output_file = file_path + filename + ".encrypted" with open(input_file, 'rb') as f: data = f.read() fernet = Fernet(key) encrypted = fernet.encrypt(data) with open(output_file, 'wb') as f: f.write(encrypted) return filename + ".encrypted", password
class UserManagementController: db = Database() def add_new_user(self, formdata=None, submitting_user=None): # -- Refacteror/Clean Add failure checks # -- Check for the keys in formdata, if none then return an error. # -- UserName/is_admin Result_Dict = { "action":"Add New User", "result":None, "reason":None } # TODO: Review if minimum lenght usernames should be permitted. if len(formdata['UserName']) < 3: Result_Dict['result'] = False Result_Dict['reason'] = "Username too short" return Result_Dict U = self.db.user.Get_UserObject(submitting_user) if U.admin == 1: G = self.db.user.Get_UserObject(formdata['UserName']) admin = False if 'is_admin' in formdata: admin = True if G == None: N=8 pw=''.join(random.choices(string.ascii_uppercase + string.digits, k=N)) self.db.user.add_new_user(formdata['UserName'],pw,admin) Result_Dict['result']=True Result_Dict['reason']=str(formdata['UserName']+" now created. Password is: "+pw+" <br> Take note of this, it will not be visable again.") else: Result_Dict['result'] = False Result_Dict['reason'] = "User already exists." # -- Validate return Result_Dict def AddUserToCampaign(self, submitter, Users, Campaign, Rights=0): # -- Refactor with Try/Catch validating the Rights values. ''' :param submitter: string :param Users: Request.form (dict) :param Campaign: int :param Rights: int :return: bool ''' # Remove Right kawgs. # Improve variable names # -- current_user_settings = self.db.campaign.get_campaign_user_settings(Campaign) if len(Users) < 1: return False S = self.db.user.Get_UserObject(submitter) if S.admin: for user in Users: for current_user in current_user_settings: if user == current_user['user']: if int(Users[user]) != int(current_user['permissions']): self.db.campaign.User_SetCampaignAccessRights(user, current_user['uid'], Campaign, Users[user]) return True else: return False # -- New methods added in Tauren Herbalist to abstract functionality from the web application. # -- This improves maintainability between frontend <-> Database changes. def user_login(self, user, password): # Returns False or user database object. return self.db.user.user_login(user, password) def get_first_logon_guid(self, user): return self.db.user.Get_UserFirstLogonGuid(user) def get_user_object(self, user): return self.db.user.Get_UserObject(user) def change_password_first_logon(self, form): pw_1 = form['password_one'] pw_2 = form['password_two'] pw_c = form['current_password'] guid = form['id'] if pw_1 == pw_2: user_object = self.db.user.User_ChangePasswordOnFirstLogon(guid, pw_c, pw_1) return user_object else: return False def get_current_campaign_users_settings_list(self, user, cid): # Returns a list of user dictionaries. Remove the current user so that a user cannot attempt to # update, or remove their own configurations. list_of_user_settings = self.db.campaign.get_campaign_user_settings(cid) for x in list_of_user_settings: if x['user'] == user: list_of_user_settings.remove(x) return list_of_user_settings def campaign_get_user_access_right_cid(self, user, cid): # Return a boolean. return self.db.campaign.Verify_UserCanAccessCampaign(user, cid) def campaign_get_user_campaign_list(self, user): return self.db.campaign.get_all_user_campaigns(user) def campaign_get_all_implant_base_from_cid(self, user, cid): if self.db.campaign.Verify_UserCanReadCampaign(user, cid) is True: return self.db.campaign.get_all_campaign_implant_templates_from_cid(cid) return False
class UserManagementController: db = Database() def process_new_user_account(self, formdata=None, submitting_user=None): # -- Refactor/Clean Add failure checks # -- Check for the keys in formdata, if none then return an error. # -- UserName/is_admin # Process - authorise user; Return False, "Insufficient permissions" # Parse input; Return False; "Input error" # Submit form; Return False; "Entry already exists" # Submit form; Check email; Send email; Return True; "User created, email notification" # Submit form; Check email; Send email; Return True; "User created, password is X" # Configuration vars generated_password_lenght = 12 if self.db.user.User_IsUserAdminAccount(submitting_user) is not True: return False, "Insufficient permissions" name = formdata.get("name", None) username = formdata.get("username", None) user_email = formdata.get("user_email", None) # DEV: This will be refactored when granular permissions are implemented. admin = formdata.get("admin", False) password = ''.join(random.choices(string.ascii_uppercase + string.ascii_lowercase + string.digits, k=generated_password_lenght)) # Check if the input matches minimum: if len(username) < 3: return False, "Minimum username is 3 characters." # Assuming all checks passed, attempt to create the user in the database: state, msg = self.db.user.add_new_user(name, username, user_email, password, admin) if state is False: return False, msg else: if email_notification.email_notification_configuration(): if email_notification.send_email_new_user_account(name, user_email, password): return True, f"{username} account created. Login information has been emailed to: {user_email}" else: return False, f"SMTP failed. New user account email notification failed. The temporary password for this account is: {password}<br>" \ f"Please take note of this as it will not be visible again." else: return True, f"{username} account created. The temporary password for this account is: {password}<br>" \ f"Please take note of this as it will not be visible again." def AddUserToCampaign(self, submitter, Users, Campaign, Rights=0): # -- Refactor with Try/Catch validating the Rights values. ''' :param submitter: string :param Users: Request.form (dict) :param Campaign: int :param Rights: int :return: bool ''' # Remove Right kawgs. # Improve variable names # -- current_user_settings = self.db.campaign.get_campaign_user_settings(Campaign) if len(Users) < 1: return False S = self.db.user.Get_UserObject(submitter) if S.admin: for user in Users: for current_user in current_user_settings: if user == current_user['user']: if int(Users[user]) != int(current_user['permissions']): self.db.campaign.User_SetCampaignAccessRights(user, current_user['uid'], Campaign, Users[user]) return True else: return False # -- New methods added in Tauren Herbalist to abstract functionality from the web application. # -- This improves maintainability between frontend <-> Database changes. def user_login(self, user, password): # Returns False or user database object. return self.db.user.user_login(user, password) def get_first_logon_guid(self, user): return self.db.user.Get_UserFirstLogonGuid(user) def get_user_object(self, user): return self.db.user.Get_UserObject(user) def get_users_state(self, user): if self.db.user.User_IsUserAdminAccount(user): return self.db.user.get_user_state_list() return [] def update_active_account_state(self, user, form): if self.db.user.User_IsUserAdminAccount(user): target_user = form['user'] target_state = form['to_state'] if self.db.user.change_account_active_state(target_user, target_state): return True return False else: return False def change_password_first_logon(self, form): pw_1 = form['password_one'] pw_2 = form['password_two'] pw_c = form['current_password'] guid = form['id'] if pw_1 == pw_2: user_object = self.db.user.User_ChangePasswordOnFirstLogon(guid, pw_c, pw_1) return user_object else: return False def get_current_campaign_users_settings_list(self, user, cid): # Returns a list of user dictionaries. Remove the current user so that a user cannot attempt to # update, or remove their own configurations. list_of_user_settings = self.db.campaign.get_campaign_user_settings(cid) for x in list_of_user_settings: if x['user'] == user: list_of_user_settings.remove(x) return list_of_user_settings def campaign_get_user_access_right_cid(self, user, cid): # Return a boolean. return self.db.campaign.Verify_UserCanAccessCampaign(user, cid) def campaign_get_user_campaign_list(self, user): return self.db.campaign.get_all_user_campaigns(user) def campaign_get_all_implant_base_from_cid(self, user, cid): if self.db.campaign.Verify_UserCanReadCampaign(user, cid) is True: return self.db.campaign.get_all_campaign_implant_templates_from_cid(cid) return False
class UserManagementController(): db = Database() def AddUser(self, formdata=None, submitting_user=None): # -- Refacteror/Clean Add failure checks # -- Check for the keys in formdata, if none then return an error. # -- UserName/is_admin Result_Dict = { "action": "Add New User", "result": None, "reason": None } # TODO: Review if minimum lenght usernames should be permitted. if len(formdata['UserName']) < 3: Result_Dict['result'] = False Result_Dict['reason'] = "Username too short" return Result_Dict U = self.db.Get_UserObject(submitting_user) if U.admin: G = self.db.Get_UserObject(formdata['UserName']) admin = False if 'is_admin' in formdata: admin = True if G == None: N = 8 pw = ''.join( random.choices(string.ascii_uppercase + string.digits, k=N)) self.db.Add_User(formdata['UserName'], pw, admin) Result_Dict['result'] = True Result_Dict['reason'] = str( formdata['UserName'] + " now created. Password is: " + pw + " <br> Take note of this, it will not be visable again.") else: Result_Dict['result'] = False Result_Dict['reason'] = "User already exists." # -- Validate return Result_Dict def AddUserToCampaign(self, Submitter, Users, Campaign, Rights=0): # -- Refactor with Try/Catch validating the Rights values. ''' :param Submitter: string :param Users: Request.form (dict) :param Campaign: int :param Rights: int :return: bool ''' # Remove Right kawgs. # Improve variable names # -- if len(Users) < 1: return False if Users: for User in Users: S = self.db.Get_UserObject(Submitter) if S.admin: U = self.db.Get_UserObject(User) if U: C = self.db.Verify_UserCanAccessCampaign( S.user_email, Campaign) if C: self.db.User_SetCampaignAccessRights( U.uid, Campaign, Users[User]) else: return False return True
def insertAnswers_and_keywords(): db = Database() db_access = DataAccess() parser = TextParser() recognizer = WordRecognizer() file = open("Preprocessing/essay_questions.txt", "r") while True: line = file.readline() if (line == ''): print("no question!") break question = DataPreprocessing.removeSinqleQuotes(line.strip('-')) line = file.readline() if (line == ''): print("no answer!") break answer = DataPreprocessing.removeSinqleQuotes(line.strip('\n')) cols = ["answer"] values = ["'" + answer + "'"] db.insert("Answers", cols, values, "", "") cols = ["id"] parameters = ["answer"] values = ["'" + answer + "'"] answer_id = db_access.select("Answers", cols, parameters, values, "") tokens = parser.tokenize(question) keywords = parser.removeStopWords(tokens) keywords_id = [] for k in keywords: cols = ["id"] parameters = ["keyword"] values = ["'" + k + "'"] keyword_id = db_access.select("Keywords", cols, parameters, values, "") if len(keyword_id) == 0: cols = ["keyword"] values = ["'" + k + "'"] conflict_fields = ["keyword"] db.insert("Keywords", cols, values, conflict_fields, "") cols = ["id"] parameters = ["keyword"] values = ["'" + k + "'"] id = db_access.select("Keywords", cols, parameters, values, "") keywords_id.append(id[0][0]) synonyms = recognizer.getSynonym(k) for s in synonyms: cols = ["key_id", "synonym"] values = [str(id[0][0]) + ", '" + s + "'"] db.insert("Synonyms", cols, values, "", "") else: keywords_id.append(keyword_id[0][0]) for i in keywords_id: cols = ["answer_id", "keyword_id"] values = [str(answer_id[0][0]) + "," + str(i)] conflict_fields = ["answer_id", "keyword_id"] db.insert("Answers_Keywords", cols, values, conflict_fields, "") print("Inserted Answers_Keywords rows")
class ImplantManagement: # -- The implant management class is responsible for performing pre-checks and validation before sending data # -- to the Implant class db = Database() Imp = ImplantSingleton.instance ImpFunc = ImplantFunctionality() NetProMan = NetworkProfileManager() def _form_validated_obfucation_level_(self, form): # Checking if obfuscation if an integer between 0-4, if not return None to raise an error. try: obfuscation_value = int(form['obfuscation']) if obfuscation_value < 0: obfuscation_value = 0 elif obfuscation_value > 4: obfuscation_value = 4 return obfuscation_value except: return None def _validate_command(self, command): # Validate the command is one of 2 thing either a Powershell direct execution or a # builtin command using :: notation. # Once validate this processes the command into a "type" and "arg", both strings. command_listing = self.ImpFunc.command_listing() # Process command output into: # :: load_module powerup if command.lstrip()[0:2] == "::": preprocessed_command = command.lstrip()[2:].lower().strip() for x in command_listing: if x['input'] in preprocessed_command: a = preprocessed_command.partition(x['input']) r_command = {"type": x['type'], "args": a[2].strip()} return r_command, True return command, "Unknown inbuilt command (::). See help page for more info." elif command.lstrip()[0:1] == ":": preprocessed_command = command.lstrip()[1:].lower().strip() for x in command_listing: if x['input'] in preprocessed_command: return command, ( f"Potential typo found in command. A single colon was found, did you mean :" f"{command}. If not please raise a GitHub ticket with the submitted command." ) else: r_command = {"type": "CM", "args": command} return r_command, True def _validate_template_kill_date(self, form): if 'kill_date' in form: try: # Checking to ensure a the time is not before current time. # This time must match the webapp submission format. user_time = datetime.datetime.strptime(form['kill_date'], '%d/%m/%Y, %H:%M') current_time = datetime.datetime.now() if user_time < current_time: return None else: # Reformatting the datetime to match implant datetime format string return datetime.datetime.strftime(user_time, '%Y-%m-%d %H:%M:%S') except Exception as E: return None def get_network_profile_options(self): return self.NetProMan.get_implant_template_code() def implant_command_registration(self, cid, username, form): result_msg = "Unknown error." try: User = self.db.campaign.Verify_UserCanWriteCampaign(username, cid) if User is False: result_msg = "You are not authorised to register commands in this campaign." raise ValueError if "cmd" not in form and "ImplantSelect" not in form: result_msg = f"Malformed request: {form}" raise ValueError if len(form['cmd']) == 0: result_msg = "No command submitted." raise ValueError processed_command, validated_command = self._validate_command( form['cmd']) if validated_command is not True: result_msg = validated_command raise ValueError result = self.ImpFunc.validate_pre_registered_command( processed_command) if result is not True: result_msg = result raise ValueError if form['ImplantSelect'] == "ALL": list_of_implants = self.db.implant.Get_AllGeneratedImplantsFromCID( cid) else: list_of_implants = self.db.implant.Get_AllImplantIDFromTitle( form['ImplantSelect']) # Check if any implants have been returned against the user submitted values. if len(list_of_implants) == 0: result_msg = "No implants listed." raise ValueError # Assuming all checks have passed no Exceptions will have been raised and we can register commands. for implant in list_of_implants: self.Imp.add_implant_command_to_server( username, cid, implant['unique_implant_id'], processed_command) return {"result": True, "reason": "Command registered"} except: return {"result": False, "reason": result_msg} def _verify_network_profile_(self, form): network_protocols = {} for key in form: a = self.NetProMan.validate_web_form(key, form[key]) if a is not None: if a != False: network_protocols.update(a) return network_protocols def create_new_implant(self, cid, form, user): # TODO: Create checks for conflicting ports. implant_configuration = { "title": None, "description": None, "url": None, "beacon": None, "inital_delay": None, "obfuscation_level": None, "protocol": {}, "kill_date": None } try: User = self.db.user.Get_UserObject(user) if User.admin == 0: return False, "Insufficient privileges." campaign_priv = self.db.campaign.Verify_UserCanWriteCampaign( user, cid) if campaign_priv is False: raise ValueError('User cannot write to this campaign.') if "CreateImplant" in form: obfuscation_level = self._form_validated_obfucation_level_( form) implant_configuration[ 'kill_date'] = self._validate_template_kill_date(form) if obfuscation_level is None: raise ValueError('Missing, or invalid obfuscation level.') else: implant_configuration[ 'obfuscation_level'] = obfuscation_level # -- Test for initial callback delay if 'initial_delay' in form: if int(form['initial_delay']) and int( form['initial_delay']) >= 0: implant_configuration['initial_delay'] = form[ 'initial_delay'] else: raise ValueError( "Initial delay must be positive integer.") else: raise ValueError("Initial delay not submitted.") # -- Test for beacon delay if 'beacon_delay' in form: if int(form['beacon_delay']) >= 1: implant_configuration['beacon'] = form['beacon_delay'] else: raise ValueError( "Beacon delay must an integer greater than 1 second." ) else: raise ValueError("No beacon delay submitted.") if form['title'] == "" or form['url'] == "" or form[ 'description'] == "": raise ValueError('Mandatory values left blank') else: implant_configuration['title'] = form['title'] implant_configuration['url'] = form['url'] implant_configuration['description'] = form['description'] implant_configuration['beacon'] = form['beacon_delay'] # Verify the input against all loaded network profiles. validated_network_protocols = self._verify_network_profile_( form) if len(validated_network_protocols) != 0: implant_configuration['protocol'].update( validated_network_protocols) else: raise ValueError( "Error: No valid network profiles submitted.") implant_creation = self.db.implant.create_new_implant_template( user, cid, implant_configuration) if implant_creation is True: return True, "Implant created." else: raise ValueError(f"Error: {implant_creation}") except Exception as E: print(E) return False, E def Get_RegisteredImplantCommands(self, username, cid=0): # -- Return list of dictionaries, not SQLAlchemy Objects. if self.db.campaign.Verify_UserCanAccessCampaign(username, cid): commands = self.db.implant.Get_RegisteredImplantCommandsFromCID( cid) to_dict = [] for x in commands: a = x.__dict__ if '_sa_instance_state' in a: del a['_sa_instance_state'] to_dict.append(a) return to_dict else: return False def Get_CampaignLogs(self, username, cid): User = self.db.campaign.Verify_UserCanReadCampaign(username, cid) if User is False: return { "cmd_reg": { "result": False, "reason": "You are not authorised to view commands in this campaign." } } return self.db.Log_GetCampaignActions(cid) def get_active_campaign_implants(self, user, campaign_id): if self.db.campaign.Verify_UserCanAccessCampaign(user, campaign_id): raw = self.db.implant.Get_AllGeneratedImplantsFromCID(campaign_id) # Removing the SQLAlchemy object. tr = [] for num, item in enumerate(raw): del item['_sa_instance_state'] tr.append(item) return tr else: return False
class ListenerManagement(): active_listener = 0 listeners = {} db = Database() def create_listener(self, listener_name, listener_type, port=None, auto_start=False, url=None): # Listener States: # 0 : Stopped # 1 : Running # 2 : Awaiting Stop # 3 : Awaiting Start supported_listeners = ["http", "https"] a = self.__check_for_listener_duplicate_element( listener_name, "common_name") if a == False: return (False, "Existing listener name found.") a = self.__check_for_listener_duplicate_element(port, "port") if a == False: return (False, "Existing port found.") if listener_type in supported_listeners: if int(port): #print("Creating: ", listener_type, port) # TODO: Likely to contain bugs if deletion is implemented? id = "0000" + str(len(self.listeners)) id = id[-4:] # -- listener = { "type": listener_type, "port": port, "state": int(0), "id": id, "common_name": listener_name } if auto_start == True: listener["state"] = int(3) self.__validate_listener(listener) self.listeners[id] = listener if auto_start == True: self.__review_listeners() else: return (False, "Please submit the port as an integer.") return (False, "Invalid listener configuration.") return (True, "Success") def listener_form_submission(self, user, form): # This will process the values submitted and decided what to call next # these will require further management if not self.db.User_IsUserAdminAccount(user): return (False, "Insufficient privileges") # else: # return (True, "Temp success value.") if 'state_change' in form: print("State change requested") if self.listeners[form['state_change']]['state'] == 0: print(form['state_change']) self.update_listener(form['state_change'], "start") elif self.listeners[form['state_change']]['state'] == 1: print(form['state_change']) self.update_listener(form['state_change'], "stop") elif 'listener_name' in form and 'listener_protocol' in form and 'listener_port' in form: # print("Adding new listener") if 'auto_start' in form: auto_start = True else: auto_start = False a = self.create_listener(form['listener_name'], form['listener_protocol'].lower(), form['listener_port'], auto_start) return a return (True, "Placeholder content") # User action def update_listener(self, id, action): print(id, action) if action == "stop": self.listeners[id]["state"] = 2 if action == "start": self.listeners[id]["state"] = 3 self.__review_listeners() return # Review Data def get_active_listeners(self, type=None): return self.listeners def __check_for_listener_duplicate_element(self, value, key): for x in self.listeners.keys(): #print(self.listeners[x][key], "==",value) if self.listeners[x][key] == value: print(value, key) return False return True def __review_listeners(self): for l in self.listeners.keys(): #print("+",self.listeners[l]) if int(self.listeners[l]['state']) == 2: #print("state 2 found for ID:",l) self.__stop_listener(l) if int(self.listeners[l]['state']) == 3: self.__start_listener(l) def __start_listener(self, id): #print("Starting: ", id) self.listeners[id]['state'] = 1 if self.listeners[id]['type'] == "http": self.__start_http_listener(self.listeners[id]) elif self.listeners[id]['type'] == "https": self.__start_https_listener(self.listeners[id]) elif self.listeners[id]['type'] == "dns": self.__start_dns_listener(self.listeners[id]) return def __stop_listener(self, id): #print("Stopping: ", id) self.listeners[id]['state'] = 0 return def __validate_listener(self, listener): # This will check for any conflicting arguments i.e. conflicting ports. return True # TODO: Refactor this code to remove as much code duplication. def __start_http_listener(self, obj): #print("Pre-Thread",obj) self.listeners[ obj['id']]['listener_thread'] = _thread.start_new_thread( self.start_http_listener_thread, (obj, )) def __start_https_listener(self, obj): #print("Pre-Thread",obj) _thread.start_new_thread(self.start_https_listener_thread, (obj, )) def __start_dns_listener(self, obj): # -- This listener is not implemented yet. return def __start_binary_listener(self, obj): # -- This listener is not implemented yet. return def start_http_listener_thread(self, obj): App = HttpListener.app App.config['listener_type'] = "http" App.run(debug=False, use_reloader=False, host='0.0.0.0', port=obj['port'], threaded=True) def start_https_listener_thread(self, obj): AppSsl = HttpListener.app AppSsl.config['listener_type'] = "https" path = os.getcwd() + "/Storage/" #print(path) AppSsl.run(debug=False, use_reloader=False, host='0.0.0.0', port=obj['port'], threaded=True, ssl_context=(path + 'server.crt', path + 'server.key'))
class ImplantManagement: # -- The implant management class is responsible for performing pre-checks and validation before sending data # -- to the Implant class db = Database() Imp = ImplantSingleton.instance def _form_validated_obfucation_level_(self, form): if "obfuscation" in form: try: obfuscation_value = int(form['obfuscation']) if obfuscation_value < 0: return 0 elif obfuscation_value > 3: return 3 else: return obfuscation_value except: print("None integer submitted.") return None return None def _validate_command(self, command): # -- TODO: Check if type needs to be enforced. special_cmd = ["sys_info", "enable_persistence"] if command[0:2] == "::": preprocessed_command = command[2:].lower().strip() if preprocessed_command in special_cmd: postprocessed_command = ":: " + preprocessed_command return postprocessed_command, True return command, { "cmd_reg": { "result": False, "reason": "Unknown inbuilt command, i.e. '::'" } } return command, True def ImplantCommandRegistration(self, cid, username, form): # -- This should be refactored at a later date to support read/write changes to # -- granular controls on templates, and later specific implants # print("CID: ",cid,"\nUSR: "******"\nCMD: ",form) User = self.db.campaign.Verify_UserCanWriteCampaign(username, cid) if User is False: return { "cmd_reg": { "result": False, "reason": "You are not authorised to register commands in this campaign." } } # -- Get All implants or implants by name then send to 'implant.py' # -- email, unique implant key, cmd if "cmd" in form and "ImplantSelect" in form: # -- before checking the database assess the cmd that was input. if len(form['cmd']) == 0: return { "cmd_reg": { "result": False, "reason": "No command submitted." } } processed_command, validated_command = self._validate_command( form['cmd']) if validated_command is not True: return validated_command # -- If validated_command is True then continue as it IS a valid command. N.b it may not be a legitimate command, but it is considered valid here. if form['ImplantSelect'] == "ALL": list_of_implants = self.db.implant.Get_AllGeneratedImplantsFromCID( cid) else: list_of_implants = self.db.implant.Get_AllImplantIDFromTitle( form['ImplantSelect']) # -- Access if this can fail. If empty return error. if len(list_of_implants) == 0: return { "cmd_reg": { "result": False, "reason": "No implants listed." } } for implant in list_of_implants: # -- Create return from the Implant.AddCommand() method. self.Imp.AddCommand(username, cid, implant['unique_implant_id'], processed_command) return { "cmd_reg": { "result": True, "reason": "Command registered" } } return { "cmd_reg": { "result": False, "reason": "Incorrect implant given, or non-existent active implant." } } def CreateNewImplant(self, cid, form, user): # TODO: Create checks for conflicting ports. implant_configuration = { "title": None, "description": None, "url": None, "beacon": None, "inital_delay": None, "obfuscation_level": None, "protocol": { "comms_http": None, "comms_https": None, "comms_binary": None, "comms_dns": None } } print(form) User = self.db.user.Get_UserObject(user) if User.admin == 0: return False, "Insufficient privileges." CampPriv = self.db.campaign.Verify_UserCanWriteCampaign(user, cid) if CampPriv is False: return False, "User cannot write to this campaign." try: if "CreateImplant" in form: obfuscation_level = self._form_validated_obfucation_level_( form) if obfuscation_level is None: raise ValueError('Missing, or invalid obfuscation level.') else: implant_configuration[ 'obfuscation_level'] = obfuscation_level # -- Test for initial callback delay if 'initial_delay' in form: if int(form['initial_delay']) and int( form['initial_delay']) >= 0: implant_configuration['initial_delay'] = form[ 'initial_delay'] else: raise ValueError( "Initial delay must be positive integer.") else: raise ValueError("Initial delay not submitted.") # -- Test for beacon delay if 'beacon_delay' in form: if int(form['beacon_delay']) >= 1: implant_configuration['beacon'] = form['beacon_delay'] else: raise ValueError( "Beacon delay must an integer greater than 1 second." ) else: raise ValueError("No beacon delay submitted.") if form['title'] == "" or form['url'] == "" or form[ 'description'] == "": raise ValueError('Mandatory values left blank') else: implant_configuration['title'] = form['title'] implant_configuration['url'] = form['url'] implant_configuration['description'] = form['description'] implant_configuration['beacon'] = form['beacon_delay'] a = { "comms_http": "http-port", "comms_https": "https-port", "comms_dns": "dns-port", "comms_binary": "binary-port" } for element in a.keys(): print("::", element) if element in form: # print("@@", form[a[element]]) if int(form[a[element]]): if int(form[a[element]]) > 0 or int( form[a[element]]) < 65536: # print(int(form[a[element]])) implant_configuration["protocol"][ element] = int(form[a[element]]) else: raise ValueError( "Submitted port for {} is out of range". format(a[element])) else: # print(form[element]) raise ValueError( "Ports must be submitted as an integer") protocol_set = False for proto in implant_configuration['protocol'].keys(): if implant_configuration['protocol'][proto] is None: protocol_set = True if protocol_set is False: raise ValueError( 'No protocol selected, ensure a protocol and port are selected.' ) # print("Final configuration:") # for element in implant_configuration.keys(): # print(element,": ",implant_configuration[element]) a = self.db.implant.create_new_implant_template( user, cid, implant_configuration) if a is True: return True, "Implant created." else: raise ValueError( "Error creating entry. Ensure implant title is unique." ) except Exception as E: return False, E return def Get_RegisteredImplantCommands(self, username, cid=0): # -- Return list of dictionaries, not SQLAlchemy Objects. if self.db.campaign.Verify_UserCanAccessCampaign(username, cid): Commands = self.db.implant.Get_RegisteredImplantCommandsFromCID( cid) toDict = [] for x in Commands: a = x.__dict__ if '_sa_instance_state' in a: del a['_sa_instance_state'] toDict.append(a) return toDict else: return False def Get_CampaignLogs(self, username, cid): User = self.db.campaign.Verify_UserCanReadCampaign(username, cid) if User is False: return { "cmd_reg": { "result": False, "reason": "You are not authorised to view commands in this campaign." } } return self.db.Log_GetCampaignActions(cid) def get_active_campaign_implants(self, user, campaign_id): if self.db.campaign.Verify_UserCanAccessCampaign(user, campaign_id): return self.db.implant.Get_AllGeneratedImplantsFromCID(campaign_id) else: return False
class ImplantManagement(): # -- The implant management class is responsible for performing pre-checks and validation before sending data # -- to the Implant class db = Database() Imp = ImplantSingleton.instance def _form_validated_obfucation_level_(self, form): for x in form: if "obfus" in x: a = x.split("-") # -- returning first value, we should only receive a single entry. try: return int(a[1]) except: return None return None def _validate_command(self, command): # -- TODO: Check if type needs to be enforced. special_cmd = ["sys_info"] if command[0:2] == "::": preprocessed_command = command[2:].lower().strip() if preprocessed_command in special_cmd: postprocessed_command = ":: " + preprocessed_command return postprocessed_command, True return command, { "cmd_reg": { "result": False, "reason": "Unknown inbuilt command, i.e. '::'" } } return command, True def ImplantCommandRegistration(self, cid, username, form): # -- This should be refactored at a later date to support read/write changes to # -- granular controls on templates, and later specific implants #print("CID: ",cid,"\nUSR: "******"\nCMD: ",form) User = self.db.Verify_UserCanWriteCampaign(username, cid) if User == False: return { "cmd_reg": { "result": False, "reason": "You are not authorised to register commands in this campaign." } } # -- Get All implants or implants by name then send to 'implant.py' # -- email, unique implant key, cmd if "cmd" in form and "ImplantSelect" in form: # -- before checking the database assess the cmd that was input. processed_command, validated_command = self._validate_command( form['cmd']) if validated_command != True: return validated_command # -- If validated_command is True then continue as it IS a valid command. N.b it may not be a legitimate command, but it is considered valid here. if form['ImplantSelect'] == "ALL": ListOfImplants = self.db.Get_AllGeneratedImplantsFromCID(cid) else: ListOfImplants = self.db.Get_AllImplantIDFromTitle( form['ImplantSelect']) # -- Access if this can fail. If empty return error. if len(ListOfImplants) == 0: return { "cmd_reg": { "result": False, "reason": "No implants listed." } } for implant in ListOfImplants: # -- Create return from the Implant.AddCommand() method. self.Imp.AddCommand(username, cid, implant['unique_implant_id'], processed_command) return { "cmd_reg": { "result": True, "reason": "Command registered" } } return { "cmd_reg": { "result": False, "reason": "Incorrect implant given, or non-existant active implant." } } def CreateNewImplant(self, cid, form, user): # -- This is creating a new Implant Template User = self.db.Get_UserObject(user) if User.admin == 0: return False, "Insufficient Priviledges" CampPriv = self.db.Verify_UserCanWriteCampaign(user, cid) if CampPriv == False: return False, "User cannot write to this campaign" # -- From here we know the user is able to write to the Campaign and an admin. try: if "CreateImplant" in form: obfuscation_level = self._form_validated_obfucation_level_( form) if obfuscation_level == None: print("OL", obfuscation_level) raise ValueError('Missing, or invalid obfuscation levels') if form['title'] == "" or form['url'] == "" or form[ 'description'] == "": raise ValueError('Mandatory values left blank') title = form['title'] url = form['url'] port = form['port'] description = form['description'] beacon = form['beacon_delay'] initial_delay = form['initial_delay'] comms_http = 0 comms_https = 0 comms_dns = 0 comms_binary = 0 try: port = int(port) except: if type(port) != int: raise ValueError('Port is required as integer') # -- Comms check --# if "comms_http" in form: comms_http = 1 if "comms_https" in form: comms_https = 1 if "comms_dns" in form: comms_dns = 1 if "comms_binary" in form: comms_binary = 1 if comms_binary == 0 and comms_dns == 0 and comms_http == 0 and comms_https == 0: raise ValueError('No communication channel selected.') if comms_http == 1 and comms_https == 1: raise ValueError( "Please select either HTTP or HTTPS. Multi-protocol is not yet supported." ) a = self.db.Add_Implant(cid, title, url, port, beacon, initial_delay, comms_http, comms_https, comms_dns, comms_binary, description, obfuscation_level) if a == True: return True, "Implant created." else: raise ValueError( "Error creating entry. Ensure implant title is unique") except Exception as e: # -- returning page with generic Error --# return False, e def Get_RegisteredImplantCommands(self, username, cid=0): # -- Return list of dictionaries, not SQLAlchemy Objects. if self.db.Verify_UserCanAccessCampaign(username, cid): Commands = self.db.Get_RegisteredImplantCommandsFromCID(cid) toDict = [] for x in Commands: a = x.__dict__ if '_sa_instance_state' in a: del a['_sa_instance_state'] toDict.append(a) return toDict else: return False def Get_CampaignLogs(self, username, cid): User = self.db.Verify_UserCanReadCampaign(username, cid) if User == False: return { "cmd_reg": { "result": False, "reason": "You are not authorised to view commands in this campaign." } } return self.db.Log_GetCampaignActions(cid)
class __OnlyOne: # Dev Work: # Improve logging on listener events # Improve race-condition checks listeners = [] db = Database() npm = NetworkProfileManager() def create_new_listener(self, username, common_name, profile_tag, args, auto_start): # First check the profile tag is valid, and then ensure adding the listener to the DB is successful # before taking action. if self.db.user.User_IsUserAdminAccount(username): listener_class = self.npm.get_listener_object(profile_tag) listener_interface = self.npm.get_listener_interface( profile_tag) if listener_class is not None and listener_interface is not None: if self.db.listener.create_new_listener_record( common_name, args, profile_tag, auto_start): listener = self.db.listener.get_listener_by_common_name( common_name) if listener is not False: # already instantiated interface listener['interface'] = listener_interface listener['interface'].configure( listener_class, args) self.listeners.append(listener) self.listener_state_change(username, listener['name'], listener['auto_run']) return True return False def get_all_listeners(self): # Create copys of listeners to avoid the interface being removed. to_return = [] for listener in self.listeners: to_return.append(copy.copy(listener)) return to_return def get_listener_state(self, common_name): for listener in self.listeners: if listener['common_name'] == common_name: return listener['object'].query_state() def startup_auto_run_listeners(self): tmp_listeners = self.db.listener.get_all_listeners() for listener in tmp_listeners: listener_class = self.npm.get_listener_object( listener['protocol']) listener_interface = self.npm.get_listener_interface( listener['protocol']) if listener_class is not None and listener_interface is not None: # Already instantiated interface listener['interface'] = listener_interface listener['interface'].configure(listener_class, listener['port']) self.listeners.append(listener) self.listener_state_change(True, listener['name'], listener['auto_run']) def listener_state_change(self, username, common_name, state): state_change_message = "Insufficient privileges" if self.db.user.User_IsUserAdminAccount( username) or username is True: for listener in self.listeners: if listener['name'] == common_name: if state == 0 and listener['state'] == 1: listener['state'] = 0 listener['interface'].stop_listener() state_change_message = f"Successfully stopped {common_name}" elif state == 1: listener['state'] = 1 listener['interface'].start_listener() state_change_message = f"Successfully started {common_name}" return state_change_message # Undecided if I want to check for server certificates at a global level - this should be down to # specific network profiles? # Note: This is still called when the C2 server starts for now so automatically return True. @staticmethod def check_tls_certificates(): return True
class ListenerManagement: listeners = {} db = Database() def __init__(self, a, b): pass def _check_if_listener_is_unique(self, name, port, protocol, reboot): # Bypass the unique check when rebooting. if reboot: return True a = self.db.listener.get_all_listeners() for x in a: if x.name == name: return False return True def _create_listener(self, name, raw_protocol, port, auto_start=False, reboot=False): protocol = raw_protocol.lower() if self._check_if_listener_is_unique(name, port, protocol, reboot): if protocol.lower() == "http" or protocol.lower() == "https": self.listeners[name] = HttpListener(name, port, protocol) elif protocol == "binary": self.listeners['name'] = BinaryListener(name, port, protocol) else: return False if reboot is not True: if self.db.listener.create_new_listener_record( name, port, protocol, auto_start) is False: return False if auto_start is True or auto_start == 1: self.listeners[name].start_listener() else: return False return True def _update_listener_state(self, listener, state): if listener in self.listeners.keys(): if state == "off": self.listeners[listener].stop_listener() elif state == "on": self.listeners[listener].start_listener() return def check_tls_certificates(self): return True def get_active_listeners(self): blah = {} for listener in self.listeners: blah[self.listeners[listener].name] = { "type": self.listeners[listener].type, "port": self.listeners[listener].port, "state": self.listeners[listener].query_state(), "id": "who knows", "common_name": self.listeners[listener].name } return blah def update_listener_state(self, username, form): if self.db.user.User_IsUserAdminAccount(username) is False: return False, "You are not an admin." if "state_change" in form: if form['state_change'] in self.listeners.keys(): current_state = self.listeners[ form['state_change']].query_state() if current_state is True: self._update_listener_state(form['state_change'], "off") else: self._update_listener_state(form['state_change'], "on") return True, "" else: return False, "" def create_new_listener(self, username, form): if self.db.user.User_IsUserAdminAccount(username) is False: return False, "You are not an admin." if "listener_name" in form: auto_start = False if "auto_start" in form: auto_start = True listener_created = self._create_listener(form['listener_name'], form['listener_protocol'], form['listener_port'], auto_start) if listener_created is True: return True, "Listener created" else: return False, "Error in _create_listener()" else: return False, "" def start_auto_run_listeners_at_boot(self): auto_run_listeners = self.db.listener.get_all_listeners() for listener in auto_run_listeners: self._create_listener(listener.name, listener.protocol, listener.port, listener.auto_run, True)
def __init__(self): self.db = Database()
# -- May need to move authentication to this level. return db.implant.Get_CampaignImplantResponses(cid) # -- Used by Implant stagers to create a suitable implant based on implant template configuration def GeneratePayload(self, NewSplicedImplantData): # -- Refactor code: variable names + checks on types. ImpGen = ImplantGenerator() if len(NewSplicedImplantData) == 1: NewSplicedImplantData = NewSplicedImplantData[0] rendered_implant = ImpGen.generate_implant_from_template(NewSplicedImplantData) db.implant.Set_GeneratedImplantCopy(NewSplicedImplantData, rendered_implant) return rendered_implant # TODO: # create functions for all listener/webapp/stager actions to avoid direct DB queries from ImplantManager, HttpListener/HttpsListener etc # Register implant submit stager key ( implant template returns a unique implant id aka unique implant key ) # Implant check in: UII/UIK + protocol checkin occured over instance = None def __init__(self): if not ImplantSingleton.instance: ImplantSingleton.instance = ImplantSingleton.__OnlyOne() else: ImplantSingleton.instance.val = arg db = Database() ImplantSingleton() # Create Singleton
class ImplantManagement: # -- The implant management class is responsible for performing pre-checks and validation before sending data # -- to the Implant class db = Database() Imp = ImplantSingleton.instance def _form_validated_obfucation_level_(self, form): if "obfuscation" in form: try: obfuscation_value = int(form['obfuscation']) if obfuscation_value < 0: return 0 elif obfuscation_value > 3: return 3 else: return obfuscation_value except: return None return None def _validate_command(self, command): command_listing = [{ "type": "FU", "args": "base64-file::filelocation", "input": "upload_file" }, { "type": "FD", "args": "base64 target file", "input": "download_file" }, { "type": "PS", "args": "sound file location (on Fudge)", "input": "play_audio" }, { "type": "EP", "args": None, "input": "enable_persistence" }, { "type": "SI", "args": None, "input": "sys_info" }, { "type": "EC", "args": None, "input": "export_clipboard" }, { "type": "LM", "args": None, "input": "load_module" }, { "type": "IM", "args": None, "input": "exec_module" }, { "type": "ML", "args": None, "input": "list_modules" }] # Process command output into: if command.lstrip()[0:2] == "::": preprocessed_command = command.lstrip()[2:].lower().strip() for x in command_listing: if x['input'] in preprocessed_command: a = preprocessed_command.partition(x['input']) r_command = {"type": x['type'], "args": a[2].strip()} return r_command, True return command, { "cmd_reg": { "result": False, "reason": "Unknown inbuilt command, i.e. '::'" } } elif command.lstrip()[0:1] == ":": preprocessed_command = command.lstrip()[1:].lower().strip() for x in command_listing: if x['input'] in preprocessed_command: return command, { "cmd_reg": { "result": False, "reason": f"Potential typo found in \ command.A single colon was found, did you mean: :{command}. If not please submit a GitHub ticket with the \ submitted command." } } else: r_command = {"type": "CM", "args": command} return r_command, True def ImplantCommandRegistration(self, cid, username, form): # -- This should be refactored at a later date to support read/write changes to # -- granular controls on templates, and later specific implants User = self.db.campaign.Verify_UserCanWriteCampaign(username, cid) if User is False: return { "cmd_reg": { "result": False, "reason": "You are not authorised to register commands in this campaign." } } # -- Get All implants or implants by name then send to 'implant.py' # -- email, unique implant key, cmd if "cmd" in form and "ImplantSelect" in form: # -- before checking the database assess the cmd that was input. if len(form['cmd']) == 0: return { "cmd_reg": { "result": False, "reason": "No command submitted." } } processed_command, validated_command = self._validate_command( form['cmd']) if validated_command is not True: return validated_command # -- If validated_command is True then continue as it IS a valid command. N.b it may not be a legitimate command, but it is considered valid here. if form['ImplantSelect'] == "ALL": list_of_implants = self.db.implant.Get_AllGeneratedImplantsFromCID( cid) else: list_of_implants = self.db.implant.Get_AllImplantIDFromTitle( form['ImplantSelect']) # -- Access if this can fail. If empty return error. if len(list_of_implants) == 0: return { "cmd_reg": { "result": False, "reason": "No implants listed." } } for implant in list_of_implants: # -- Create return from the Implant.AddCommand() method. self.Imp.AddCommand(username, cid, implant['unique_implant_id'], processed_command) return { "cmd_reg": { "result": True, "reason": "Command registered" } } return { "cmd_reg": { "result": False, "reason": "Incorrect implant given, or non-existent active implant." } } def CreateNewImplant(self, cid, form, user): # TODO: Create checks for conflicting ports. implant_configuration = { "title": None, "description": None, "url": None, "beacon": None, "inital_delay": None, "obfuscation_level": None, "protocol": { "comms_http": None, "comms_https": None, "comms_binary": None, "comms_dns": None } } try: User = self.db.user.Get_UserObject(user) if User.admin == 0: return False, "Insufficient privileges." campaign_priv = self.db.campaign.Verify_UserCanWriteCampaign( user, cid) if campaign_priv is False: raise ValueError('User cannot write to this campaign.') if "CreateImplant" in form: obfuscation_level = self._form_validated_obfucation_level_( form) if obfuscation_level is None: raise ValueError('Missing, or invalid obfuscation level.') else: implant_configuration[ 'obfuscation_level'] = obfuscation_level # -- Test for initial callback delay if 'initial_delay' in form: if int(form['initial_delay']) and int( form['initial_delay']) >= 0: implant_configuration['initial_delay'] = form[ 'initial_delay'] else: raise ValueError( "Initial delay must be positive integer.") else: raise ValueError("Initial delay not submitted.") # -- Test for beacon delay if 'beacon_delay' in form: if int(form['beacon_delay']) >= 1: implant_configuration['beacon'] = form['beacon_delay'] else: raise ValueError( "Beacon delay must an integer greater than 1 second." ) else: raise ValueError("No beacon delay submitted.") if form['title'] == "" or form['url'] == "" or form[ 'description'] == "": raise ValueError('Mandatory values left blank') else: implant_configuration['title'] = form['title'] implant_configuration['url'] = form['url'] implant_configuration['description'] = form['description'] implant_configuration['beacon'] = form['beacon_delay'] a = { "comms_http": "http-port", "comms_https": "https-port", "comms_dns": "dns-port", "comms_binary": "binary-port" } for element in a.keys(): if element in form: if int(form[a[element]]): if int(form[a[element]]) > 0 or int( form[a[element]]) < 65536: implant_configuration["protocol"][ element] = int(form[a[element]]) else: raise ValueError( f"Submitted port for {a[element]} is out of range" ) else: raise ValueError( "Ports must be submitted as an integer") protocol_set = False for proto in implant_configuration['protocol'].keys(): if implant_configuration['protocol'][proto] is None: protocol_set = True if protocol_set is False: raise ValueError( 'No protocol selected, ensure a protocol and port are selected.' ) a = self.db.implant.create_new_implant_template( user, cid, implant_configuration) if a is True: return True, "Implant created." else: raise ValueError( "Error creating entry. Ensure implant title is unique." ) except Exception as E: return False, E def Get_RegisteredImplantCommands(self, username, cid=0): # -- Return list of dictionaries, not SQLAlchemy Objects. if self.db.campaign.Verify_UserCanAccessCampaign(username, cid): commands = self.db.implant.Get_RegisteredImplantCommandsFromCID( cid) to_dict = [] for x in commands: a = x.__dict__ if '_sa_instance_state' in a: del a['_sa_instance_state'] to_dict.append(a) return to_dict else: return False def Get_CampaignLogs(self, username, cid): User = self.db.campaign.Verify_UserCanReadCampaign(username, cid) if User is False: return { "cmd_reg": { "result": False, "reason": "You are not authorised to view commands in this campaign." } } return self.db.Log_GetCampaignActions(cid) def get_active_campaign_implants(self, user, campaign_id): if self.db.campaign.Verify_UserCanAccessCampaign(user, campaign_id): raw = self.db.implant.Get_AllGeneratedImplantsFromCID(campaign_id) # Removing the SQLAlchemy object. tr = [] for num, item in enumerate(raw): del item['_sa_instance_state'] tr.append(item) return tr else: return False