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 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 []
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
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: print(form) if int(form['obfuscation']): if int(form['obfuscation']) < 0: return 0 elif int(form['obfuscation']) > 3: return 3 else: return int(form['obfuscation']) 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-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 } } User = self.db.Get_UserObject(user) if User.admin == 0: return False, "Insufficient privileges." CampPriv = self.db.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 levels') 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.Add_Implant(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.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)
from flask import Flask, render_template, flash, request, jsonify, g, current_app, url_for, redirect, make_response, send_file, send_from_directory import base64 from uuid import uuid4 from FudgeC2.Implant.Implant import ImplantSingleton from FudgeC2.Data.Database import Database Imp = ImplantSingleton.instance db = Database() app = Flask(str(uuid4())) app.config['SECRET_KEY'] = str(uuid4()) def ImplantManager(a): if "X-Implant" in a: print("Checked in implant is: ", a["X-Implant"]) @app.before_request def before_request(): # TODO: Implement IP whitelist and reject if connection if it is not a valid src IP. return @app.after_request def add_header(r): #r.headers["X-Command"] = a return r # -- TODO: extracted and added into a new stager specific listener(?)
class ListenerManagement(): # TODO: Add checks for failed/failing listeners (i.e. certs not configured & thread dies.) active_listener = 0 listeners = {} db = Database() tls_cert = None tls_key = None def __init__(self, tls_listener_cert, tls_listener_key): # TODO: Implement checks to validate files at this point. self.tls_cert = tls_listener_cert self.tls_key = tls_listener_key def check_tls_certificates(self): cert_result = os.path.isfile(os.getcwd() + "/Storage/" + self.tls_cert) key_result = os.path.isfile(os.getcwd() + "/Storage/" + self.tls_key) if key_result is False or cert_result is False: print( "Warning: ListenerManagement.check_tls_certificates() has failed. Generate certificates." ) return False else: return True 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): # 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, "Implant started sucessfully.") # 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 listener_id in self.listeners.keys(): if self.listeners[listener_id][key] == value: print(value, key) return False return True def __review_listeners(self): for listener_id in self.listeners.keys(): if int(self.listeners[listener_id]['state']) == 2: self.__stop_listener(listener_id) if int(self.listeners[listener_id]['state']) == 3: self.__start_listener(listener_id) def __start_listener(self, 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): 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): flask_listener_object = self.create_app("http") self.listeners[ obj['id']]['listener_thread'] = _thread.start_new_thread( self.start_http_listener_thread, ( obj, flask_listener_object, )) def __start_https_listener(self, obj): flask_listener_object = self.create_app("https") self.listeners[ obj['id']]['listener_thread'] = _thread.start_new_thread( self.start_https_listener_thread, ( obj, flask_listener_object, )) 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 create_app(self, listener_type): import FudgeC2.Listeners.HttpListener del sys.modules["FudgeC2.Listeners.HttpListener"] import FudgeC2.Listeners.HttpListener as HL HL.app.config['listener_type'] = listener_type return HL.app def start_http_listener_thread(self, obj, App): App.run(debug=False, use_reloader=False, host='0.0.0.0', port=obj['port'], threaded=True) def start_https_listener_thread(self, obj, App): path = os.getcwd() + "/Storage/" if self.check_tls_certificates(): App.run(debug=False, use_reloader=False, host='0.0.0.0', port=obj['port'], threaded=True, ssl_context=(path + self.tls_cert, path + self.tls_key)) else: print() # -- User has not set up certificates in the Storage dir return False
def __init__(self): self.db = Database()