def create_system_user(system_username=None, system_password=None): if not system_username: config = get_config() system_username = config['SYSTEM_USERNAME'] if not system_password: config = get_config() system_password = config['SYSTEM_PASSWORD'] if not len(system_username) > 0 or not len(system_password) > 0: error_out( "System Username and/or System Password not found in config. Run `setup` to initialize Faction or `new " "config` to create a new config") create_user(system_username, system_password, "system")
def create_admin_user(admin_username=None, admin_password=None): if not admin_username: config = get_config() admin_username = config['ADMIN_USERNAME'] if not admin_password: config = get_config() admin_password = config['ADMIN_PASSWORD'] if not len(admin_username) > 0 or not len(admin_password) > 0: error_out( "Admin Username and/or Admin Password not found in config. Run `setup` to initialize faction or `new " "config` to create a new config") create_user(admin_username, admin_password, "admin")
def remove_builds(): print_output("Removing agent build files..") config = get_config() build_path = os.path.join(config["FACTION_PATH"], "agents/build") command = "sudo su -c 'rm -rf {0}*'".format(build_path) log.debug("Running: '{0}' from {1}".format(command, os.path.join(config["FACTION_PATH"]))) call(command, cwd=os.path.join(config["FACTION_PATH"]), shell=True)
def remove_modules(): print_output("Removing modules..") config = get_config() modules_path = os.path.join(config["FACTION_PATH"], "modules/") command = "sudo su -c 'rm -rf {0}*'".format(modules_path) log.debug("Running: '{0}' from {1}".format(command, os.path.join(config["FACTION_PATH"]))) call(command, cwd=os.path.join(config["FACTION_PATH"]), shell=True)
def stop_faction(): print_output("Stopping Faction..") config = get_config() install_path = os.path.join(config["FACTION_PATH"], "install/") log.debug("Running: 'docker-compose -p faction stop' from {0}".format( install_path)) call("docker-compose -p faction stop", cwd=install_path, shell=True)
def write_dev_compose_file(): config = get_config() docker_compose_file_path = os.path.join(config["FACTION_PATH"], "install/docker-compose.yml") docker_compose_file_contents = """ version: "3" services: db: image: postgres:latest ports: - "5432:5432" volumes: - {0}/data:/var/lib/postgresql/data environment: - POSTGRES_DB={6} - POSTGRES_USERNAME={1} - POSTGRES_PASSWORD={2} mq: image: rabbitmq:3-management ports: - "5672:5672" - "8080:15672" environment: - RABBITMQ_DEFAULT_USER={3} - RABBITMQ_DEFAULT_PASS={4} """.format(config["FACTION_PATH"], config["POSTGRES_USERNAME"], config["POSTGRES_PASSWORD"], config["RABBIT_USERNAME"], config["RABBIT_PASSWORD"], config["POSTGRES_HOST"], config["POSTGRES_DATABASE"]) with open(docker_compose_file_path, "w+") as compose_file: compose_file.write(docker_compose_file_contents)
def remove_database_files(): print_output("Removing database files..") config = get_config() install_path = os.path.join(config["FACTION_PATH"], "install/") data_path = os.path.join(config["FACTION_PATH"], "data/") command = "sudo su -c 'rm -rf {0}*'".format(data_path) log.debug("Running: '{0}' from {1}".format(command, install_path)) call(command, cwd=install_path, shell=True)
def clean_faction(): print_output("Removing Faction Docker Containers and Images..") config = get_config() install_path = os.path.join(config["FACTION_PATH"], "install/") log.debug( "Running: 'docker-compose -p faction down --remove-orphans --rmi all' from {0}" .format(install_path)) call("docker-compose -p faction down --remove-orphans --rmi all", cwd=install_path, shell=True)
def remove_uploads(): print_output("Removing uploaded files..") config = get_config() paths = [] uploads_path = os.path.join(config["FACTION_PATH"], "uploads/") paths.append(os.path.join(uploads_path, "files/")) paths.append(os.path.join(uploads_path, "payloads/")) for path in paths: command = "sudo su -c 'rm -rf {0}*'".format(path) log.debug("Running: '{0}' from {1}".format(command, uploads_path)) call(command, cwd=uploads_path, shell=True)
def create_direct_transport(name="DIRECT Transport", transport_type="DIRECT", guid="0000-0000-0000-0000-0000", api_key=None): config = get_config() if not api_key: error_out("No API Key included in request") configuration = '{"TransportId": 1, "ApiUrl":"' + config[ 'EXTERNAL_ADDRESS'] + '","ApiKeyName":"' + api_key[ 'Name'] + '","ApiSecret":"' + api_key['Token'] + '"}' create_transport(name, transport_type, guid, api_key["Id"], configuration)
def take_action(self, parsed_args): config = get_config() containers = config["CONTAINERS"] results = [] for container in containers: status = get_container_status(container) if status: results.append((status.name, status.status, status.ip_address, status.created)) else: error_out( "Container {0} not found. Faction won't work with out this." .format(container)) return (("Container Name", "Status", "IP Address", "Created"), results)
def get_logs(container_name=None, follow=False): config = get_config() install_path = os.path.join(config["FACTION_PATH"], "install/") if follow == True: compose_command = 'docker-compose -p faction logs --follow {0}'.format( container_name) else: compose_command = 'docker-compose -p faction logs {0}'.format( container_name) log.debug(compose_command=" from {0}".format(install_path)) call("docker-compose -p faction logs", cwd=install_path, shell=True)
def build_faction(): print_output("Building Faction containers..") try: config = get_config() install_path = os.path.join(config["FACTION_PATH"], "install/") log.debug( "Running: 'docker-compose -p faction up -d --force-recreate --build' from {0}" .format(install_path)) ret = call("docker-compose -p faction up -d --force-recreate --build", cwd=install_path, shell=True) if ret == 0: print_output("Faction has been built") else: error_out("Failed to build Faction.") except Exception as e: error_out("Building Faction failed. Error: {0}".format(str(e)))
def __init__(self): CONFIG = get_config() db_params = { 'drivername': 'postgres', 'username': CONFIG['POSTGRES_USERNAME'], 'password': CONFIG['POSTGRES_PASSWORD'], 'host': get_container_ip_address("faction_db_1"), 'database': CONFIG['POSTGRES_DATABASE'] } # Resolves an issue with backrefs, thanks to this stackoverflow # user for answering their own question: https://stackoverflow.com/a/49515079 def _gen_relationship(base, direction, return_fn, attrname, local_cls, referred_cls, **kw): return generate_relationship(base, direction, return_fn, attrname + '_ref', local_cls, referred_cls, **kw) # this person seems to know what they're talking about: https://stackoverflow.com/a/48288656 def _name_for_collection_relationship(base, local_cls, referred_cls, constraint): if constraint.name: return constraint.name.lower() # if this didn't work, revert to the default behavior return name_for_collection_relationship(base, local_cls, referred_cls, constraint) db_url = URL(**db_params) self.engine = create_engine(db_url) self.base = automap_base() self.base.prepare( self.engine, reflect=True, generate_relationship=_gen_relationship, name_for_collection_relationship=_name_for_collection_relationship) self.session = Session(self.engine) self.User = self.base.classes.User self.UserRole = self.base.classes.UserRole self.Agent = self.base.classes.Agent self.Transport = self.base.classes.Transport self.ApiKey = self.base.classes.ApiKey
def write_dev_compose_file(): config = get_config() docker_compose_file_path = os.path.join(config["FACTION_PATH"], "install/docker-compose.yml") docker_compose_file_contents = """ version: "3.4" x-logging: &default-logging driver: local options: max-size: "{7}" max-file: "{8}" services: db: image: postgres:latest ports: - "5432:5432" volumes: - {0}/data:/var/lib/postgresql/data environment: - POSTGRES_DB={6} - POSTGRES_USERNAME={1} - POSTGRES_PASSWORD={2} logging: *default-logging mq: image: rabbitmq:3-management ports: - "5672:5672" - "8080:15672" environment: - RABBITMQ_DEFAULT_USER={3} - RABBITMQ_DEFAULT_PASS={4} logging: *default-logging """.format(config["FACTION_PATH"], config["POSTGRES_USERNAME"], config["POSTGRES_PASSWORD"], config["RABBIT_USERNAME"], config["RABBIT_PASSWORD"], config["POSTGRES_HOST"], config["POSTGRES_DATABASE"], config["LOG_FILE_SIZE"], config["LOG_FILE_NUMBER"]) with open(docker_compose_file_path, "w+") as compose_file: compose_file.write(docker_compose_file_contents)
def write_build_compose_file(): config = get_config() docker_compose_file_path = os.path.join(config["FACTION_PATH"], "install/docker-compose.yml") docker_compose_file_contents = """ version: "3" services: db: image: postgres:latest ports: - "5432:5432" volumes: - {0}/data:/var/lib/postgresql/data environment: - POSTGRES_DB={6} - POSTGRES_USERNAME={1} - POSTGRES_PASSWORD={2} mq: image: rabbitmq:3-management ports: - "5672:5672" - "8080:15672" environment: - RABBITMQ_DEFAULT_USER={3} - RABBITMQ_DEFAULT_PASS={4} console: build: ../services/console ports: - "{8}:443" depends_on: - api volumes: - {0}/certs:/opt/faction/certs api: build: ../source/api depends_on: - mq - db ports: - "5000:5000" volumes: - {10}:{10} environment: - API_UPLOAD_DIR={10} - FLASK_SECRET={9} - POSTGRES_HOST={5} - POSTGRES_DATABASE={6} - POSTGRES_USERNAME={1} - POSTGRES_PASSWORD={2} - RABBIT_HOST={7} - RABBIT_USERNAME={3} - RABBIT_PASSWORD={4} core: build: ../source/core depends_on: - mq - db - console environment: - POSTGRES_HOST={5} - POSTGRES_DATABASE={6} - POSTGRES_USERNAME={1} - POSTGRES_PASSWORD={2} - RABBIT_HOST={7} - RABBIT_USERNAME={3} - RABBIT_PASSWORD={4} - SYSTEM_USERNAME={11} - SYSTEM_PASSWORD={12} build-server-dotnet: build: ../source/build-server-dotnet depends_on: - core - api volumes: - {0}:{0} environment: - POSTGRES_HOST={5} - POSTGRES_DATABASE={6} - POSTGRES_USERNAME={1} - POSTGRES_PASSWORD={2} - RABBIT_HOST={7} - RABBIT_USERNAME={3} - RABBIT_PASSWORD={4} """.format(config["FACTION_PATH"], config["POSTGRES_USERNAME"], config["POSTGRES_PASSWORD"], config["RABBIT_USERNAME"], config["RABBIT_PASSWORD"], config["POSTGRES_HOST"], config["POSTGRES_DATABASE"], config["RABBIT_HOST"], config["CONSOLE_PORT"], config["FLASK_SECRET"], config["API_UPLOAD_DIR"], config["SYSTEM_USERNAME"], config["SYSTEM_PASSWORD"]) with open(docker_compose_file_path, "w+") as compose_file: compose_file.write(docker_compose_file_contents)
def write_hub_compose_file(docker_tag): config = get_config() docker_compose_file_path = os.path.join(config["FACTION_PATH"], "install/docker-compose.yml") docker_compose_file_contents = """ version: "3.4" x-logging: &default-logging driver: local options: max-size: "{13}" max-file: "{14}" services: db: image: postgres:latest ports: - "5432:5432" volumes: - {0}/data:/var/lib/postgresql/data environment: - POSTGRES_DB={6} - POSTGRES_USERNAME={1} - POSTGRES_PASSWORD={2} logging: *default-logging mq: image: rabbitmq:3-management ports: - "5672:5672" - "8080:15672" environment: - RABBITMQ_DEFAULT_USER={3} - RABBITMQ_DEFAULT_PASS={4} logging: *default-logging console: image: faction/console:{15} ports: - "{8}:443" depends_on: - api volumes: - {0}/certs:/opt/faction/certs logging: *default-logging api: image: faction/api:{15} depends_on: - mq - db ports: - "5000:5000" volumes: - {10}:{10} environment: - API_UPLOAD_DIR={10} - FLASK_SECRET={9} - POSTGRES_HOST={5} - POSTGRES_DATABASE={6} - POSTGRES_USERNAME={1} - POSTGRES_PASSWORD={2} - RABBIT_HOST={7} - RABBIT_USERNAME={3} - RABBIT_PASSWORD={4} logging: *default-logging core: image: faction/core:{15} depends_on: - mq - db - console environment: - POSTGRES_HOST={5} - POSTGRES_DATABASE={6} - POSTGRES_USERNAME={1} - POSTGRES_PASSWORD={2} - RABBIT_HOST={7} - RABBIT_USERNAME={3} - RABBIT_PASSWORD={4} - SYSTEM_USERNAME={11} - SYSTEM_PASSWORD={12} logging: *default-logging build-dotnet: image: faction/build-dotnet:{15} depends_on: - core - api volumes: - {0}:{0} environment: - POSTGRES_HOST={5} - POSTGRES_DATABASE={6} - POSTGRES_USERNAME={1} - POSTGRES_PASSWORD={2} - RABBIT_HOST={7} - RABBIT_USERNAME={3} - RABBIT_PASSWORD={4} logging: *default-logging """.format(config["FACTION_PATH"], config["POSTGRES_USERNAME"], config["POSTGRES_PASSWORD"], config["RABBIT_USERNAME"], config["RABBIT_PASSWORD"], config["POSTGRES_HOST"], config["POSTGRES_DATABASE"], config["RABBIT_HOST"], config["CONSOLE_PORT"], config["FLASK_SECRET"], config["API_UPLOAD_DIR"], config["SYSTEM_USERNAME"], config["SYSTEM_PASSWORD"], config["LOG_FILE_SIZE"], config["LOG_FILE_NUMBER"], docker_tag) with open(docker_compose_file_path, "w+") as compose_file: compose_file.write(docker_compose_file_contents)
def take_action(self, parsed_args): print_output("Setup started..") generate_config_file( admin_username=parsed_args.admin_username, admin_password=parsed_args.admin_password, api_upload_dir=parsed_args.api_upload_dir, build=parsed_args.build, console_port=parsed_args.console_port, containers=parsed_args.container_names, docker_network_name=parsed_args.docker_network_name, external_address=parsed_args.external_address, faction_path=parsed_args.faction_path, flask_secret=parsed_args.flask_secret, postgres_host=parsed_args.postgres_host, postgres_database=parsed_args.postgres_database, postgres_username=parsed_args.postgres_username, postgres_password=parsed_args.postgres_password, rabbit_host=parsed_args.rabbit_host, rabbit_username=parsed_args.rabbit_username, rabbit_password=parsed_args.rabbit_password, system_username=parsed_args.system_username, system_password=parsed_args.system_password) if parsed_args.external_address: if not parsed_args.external_address.startswtih( 'http://') or not parsed_args.external_address.startswtih( 'https://'): error_out( 'Setup failed. --external-address argument must begin with http:// or https://' ) if parsed_args.build: for component in parsed_args.components: download_github_repo( "FactionC2/{0}".format(component), "{0}/source/{1}".format(parsed_args.faction_path, component), parsed_args.github_pat) write_build_compose_file() elif parsed_args.dev: write_dev_compose_file() else: write_hub_compose_file() clone_github_repo( "FactionC2/Modules-Dotnet", "{0}/modules/dotnet".format(parsed_args.faction_path)) clone_github_repo( "maraudershell/Marauder", "{0}/agents/Marauder".format(parsed_args.faction_path)) build_faction() if parsed_args.dev: print_output("Pausing setup, you need to do stuff.") print("Add the following to your hosts file: ") print("127.0.0.1 api") print("127.0.0.1 db") print("127.0.0.1 mq\n") print( "Run the following commands from the Faction Core directory: ") print( "1. dotnet ef migration add 'Initial' (You only have to do this once, unless you change the db schema)" ) print("2. dotnet ef database update\n") input("Press enter to continue setup..") else: print_output("Waiting 30 seconds for Core to come up..") core_down = True sleep(30) while core_down: status = get_container_status('faction_core_1') self.log.debug("Got status: {0}".format(status)) if status: if status.status.lower() == 'running': print_output("Core is up, continuing..") core_down = False else: print_output( "Core is not up yet. Waiting 15 more seconds..") sleep(15) create_database_migration("Initial") update_database() create_faction_roles() create_system_user() create_admin_user() system_id = get_user_id('system') api_key = create_api_key(user_id=system_id, owner_id=system_id, type="Transport") create_direct_transport(api_key=api_key) if parsed_args.dev == None or parsed_args.dev == False: print_output("Restarting Core for database changes..") core = get_container("faction_core_1") restart_container(core) config = get_config() print_output( "Setup complete! Get to hacking!!\n\nURL: {0}\nUsername: {1}\nPassword: {2}" .format(config["EXTERNAL_ADDRESS"], config["ADMIN_USERNAME"], config["ADMIN_PASSWORD"]))
def take_action(self, parsed_args): print_output("Setup started..") if parsed_args.external_address: if not (parsed_args.external_address.startswith("http://") or parsed_args.external_address.startswith("https://")): error_out( "Setup failed. --external-address argument must begin with http:// or https://" ) else: ip_options = get_ip_addresses() while True: print_output("Available NICs : IP Addresses") for key, value in ip_options.items(): print(key, " : ", value) selection = input( "Please select a NIC that corresponds to the ip address you wish to use: " ) if selection in ip_options: break parsed_args.external_address = "https://" + ip_options[selection] generate_config_file( admin_username=parsed_args.admin_username, admin_password=parsed_args.admin_password, api_upload_dir=parsed_args.api_upload_dir, build=parsed_args.build_from_source, console_port=parsed_args.console_port, containers=parsed_args.container_names, docker_network_name=parsed_args.docker_network_name, external_address=parsed_args.external_address, faction_path=parsed_args.faction_path, flask_secret=parsed_args.flask_secret, postgres_host=parsed_args.postgres_host, postgres_database=parsed_args.postgres_database, postgres_username=parsed_args.postgres_username, postgres_password=parsed_args.postgres_password, rabbit_host=parsed_args.rabbit_host, rabbit_username=parsed_args.rabbit_username, rabbit_password=parsed_args.rabbit_password, system_username=parsed_args.system_username, system_password=parsed_args.system_password, log_file_size=parsed_args.log_file_size, log_file_number=parsed_args.log_file_number) docker_tag = "latest" github_repo = "master" if parsed_args.release == "development": docker_tag = "dev" github_repo = "development" if parsed_args.build_from_source: for component in parsed_args.components: download_github_repo( "FactionC2/{0}".format(component), "{0}/source/{1}".format(parsed_args.faction_path, component), component, parsed_args.github_pat) write_build_compose_file() elif parsed_args.build_for_dev_environment: write_dev_compose_file() else: write_hub_compose_file(docker_tag) clone_github_repo( github_repo, "FactionC2/Modules-Dotnet", "{0}/modules/dotnet".format(parsed_args.faction_path)) clone_github_repo( github_repo, "maraudershell/Marauder", "{0}/agents/Marauder".format(parsed_args.faction_path)) build_faction() if parsed_args.build_for_dev_environment: print_output("Pausing setup, you need to do stuff.") print("Add the following to your hosts file: ") print("127.0.0.1 api") print("127.0.0.1 db") print("127.0.0.1 mq\n") print( "Run the following commands from the Faction Core directory: ") print( "1. dotnet ef migration add 'Initial' (You only have to do this once, unless you change the db schema)" ) print("2. dotnet ef database update\n") input("Press enter to continue setup..") else: print_output("Waiting 30 seconds for Core to come up..") core_down = True sleep(30) while core_down: status = get_container_status('faction_core_1') self.log.debug("Got status: {0}".format(status)) if status: if status.status.lower() == 'running': print_output("Core is up, continuing..") core_down = False else: print_output( "Core is not up yet. Waiting 15 more seconds..") sleep(15) create_database_migration("Initial") update_database() # Now that the environment is up, we can import common lib from factionpy.processing.user import get_user_id from factionpy.processing.api_key import new_api_key from factioncli.processing.setup.transport import create_direct_transport from factioncli.processing.setup.user_role import create_faction_roles from factioncli.processing.setup.user import create_admin_user, create_system_user create_faction_roles() create_system_user() create_admin_user() print_output("Creating API Key for Direct Transport") system_id = get_user_id('system') api_key = new_api_key(api_key_type="Transport", user_id=system_id, owner_id=system_id) create_direct_transport(api_key=api_key) if parsed_args.build_for_dev_environment is None or parsed_args.build_for_dev_environment is False: print_output("Restarting Core for database changes..") core = get_container("faction_core_1") restart_container(core) config = get_config() print_output( "Setup complete! Happy hacking!!\n\nURL: {0}\nUsername: {1}\nPassword: {2}" .format(config["EXTERNAL_ADDRESS"], config["ADMIN_USERNAME"], config["ADMIN_PASSWORD"]))