def create(): # Don't deploy the template. Delete this line when you implement your level. exit('This is a template file. It is not meant to be deployed.') # ---------Level Initialization--------- # Put code here that generates anything passed to the configuration template, # sets up the project before deployment, or does anything else that happens # before the deployment gets inserted. # -------------------------------------- # ---------Deployment Insertion--------- # Insert the deployment, filling out labels, config template arguments, # and the deployment manager template imports. config_template_args = {} labels = {} template_files = [] deployments.insert(LEVEL_PATH, config_template_args=config_template_args, labels=labels, template_files=template_files) # -------------------------------------- # --------------Level Setup------------- # Put code here that does anything that needs to happen after the deployment. # This includes usings APIs to modify deployed resources or anything else. # Print complete message and print/save start info print(f'Level creation complete for: {LEVEL_PATH}\n' f'Instruction for the level can be accessed at ' f'thunder-ctf.cloud/levels/{LEVEL_PATH}.html') start_message = '--Put the start message here.--' levels.write_start_info(LEVEL_PATH, start_message)
def create(): # ---------Level Initialization--------- # Create randomized bucket name to avoid namespace conflict nonce = str(random.randint(100000000000, 999999999999)) bucket_name = f'{RESOURCE_PREFIX}-bucket-{nonce}' # -------------------------------------- # ---------Deployment Insertion--------- # Insert deployment config_template_args = {'nonce': nonce} template_files = ['core/framework/templates/bucket_acl.jinja'] deployments.insert(LEVEL_PATH, template_files=template_files, config_template_args=config_template_args) # -------------------------------------- # --------------Level Setup------------- print("Level setup started for: " + LEVEL_PATH) # Insert secret into bucket storage_client = storage.Client() bucket = storage_client.get_bucket(bucket_name) secret_blob = storage.Blob('secret.txt', bucket) secret = levels.make_secret(LEVEL_PATH) secret_blob.upload_from_string(secret) # Print complete message and print/save start info print( f'Level creation complete for: {LEVEL_PATH}\n' f'Instruction for the level can be accessed at thunder-ctf.cloud/thunder/{LEVEL_PATH}.html' ) start_message = f'The secret for this level can be found in the Google Cloud Storage (GCS) bucket {bucket_name}' levels.write_start_info(LEVEL_PATH, start_message)
def create(): print("Level initialization started for: " + LEVEL_PATH) # Create randomized nonce name to avoid namespace conflicts nonce = str(random.randint(100000000000, 999999999999)) bucket_name = f'{RESOURCE_PREFIX}-bucket-{nonce}' # Create ssh key ssh_private_key, ssh_public_key = ssh_keys.generate_ssh_keypair() ssh_username = "******" try: # Construct git repo repo_path = os.path.dirname(os.getcwd()) + "/temp-repository-" + nonce create_repo_files(repo_path, ssh_private_key) print("Level initialization finished for: " + LEVEL_PATH) # Insert deployment config_template_args = { 'nonce': nonce, 'ssh_public_key': ssh_public_key, 'ssh_username': ssh_username } template_files = [ 'core/framework/templates/bucket_acl.jinja', 'core/framework/templates/ubuntu_vm.jinja', 'core/framework/templates/service_account.jinja', 'core/framework/templates/iam_policy.jinja' ] deployments.insert(LEVEL_PATH, template_files=template_files, config_template_args=config_template_args) print("Level setup started for: " + LEVEL_PATH) # Upload repository to bucket gcstorage.upload_directory_recursive(repo_path, bucket_name) # Create logs secret_name = create_logs() # Create service account key file sa_key = iam.generate_service_account_key(f'{RESOURCE_PREFIX}-access') print(f'Level creation complete for: {LEVEL_PATH}') start_message = ( f'Use the compromised service account credentials stored in {RESOURCE_PREFIX}-access.json to find the credit card number of {secret_name}, ' 'which is hidden somewhere in the GCP project') levels.write_start_info(LEVEL_PATH, start_message, file_name=f'{RESOURCE_PREFIX}-access.json', file_content=sa_key) print( f'Instruction for the level can be accessed at thunder-ctf.cloud/thunder/{LEVEL_PATH}.html' ) finally: # If there is an error, make sure to delete the temporary repository before exiting if os.path.exists(repo_path): shutil.rmtree(repo_path)
def create(second_deploy=True): # Create randomized bucket name to avoid namespace conflict nonce = str(random.randint(100000000000, 999999999999)) bucket_name = f'{RESOURCE_PREFIX}-bucket-{nonce}' # Set role of default cloud function account credentials, project_id = google.auth.default() func_upload_url = cloudfunctions.upload_cloud_function( f'core/levels/{LEVEL_PATH}/function', FUNCTION_LOCATION) print("Level initialization finished for: " + LEVEL_PATH) # Insert deployment config_template_args = {'nonce': nonce, 'func_upload_url': func_upload_url} template_files = [ 'core/framework/templates/service_account.jinja', 'core/framework/templates/cloud_function.jinja', 'core/framework/templates/iam_policy.jinja', 'core/framework/templates/bucket_acl.jinja'] if second_deploy: deployments.insert(LEVEL_PATH, template_files=template_files, config_template_args=config_template_args, second_deploy=True) else: deployments.insert(LEVEL_PATH, template_files=template_files, config_template_args=config_template_args) try: print("Level setup started for: " + LEVEL_PATH) # Allow player to use cloud function's service account iam_api = discovery.build('iam', 'v1', credentials=credentials) policy_body = {"policy": { "bindings": [{ "members": [f"serviceAccount:a5-access@{project_id}.iam.gserviceaccount.com"], "role": "roles/iam.serviceAccountUser"}]}} iam_api.projects().serviceAccounts().setIamPolicy( resource=f'projects/{project_id}/serviceAccounts/a5-func-{nonce}-sa@{project_id}.iam.gserviceaccount.com', body=policy_body).execute() # Insert secret into bucket storage_client = storage.Client() bucket = storage_client.get_bucket(bucket_name) secret_blob = storage.Blob('secret.txt', bucket) secret = levels.make_secret(LEVEL_PATH) secret_blob.upload_from_string(secret) # Create service account key file sa_key = iam.generate_service_account_key(f'{RESOURCE_PREFIX}-access') print(f'Level creation complete for: {LEVEL_PATH}') start_message = ( f'Use the compromised service account credentials stored in {RESOURCE_PREFIX}-access.json to find the secret, ' 'which is located in a file called secret.txt in a private bucket on the project.') levels.write_start_info( LEVEL_PATH, start_message, file_name=f'{RESOURCE_PREFIX}-access.json', file_content=sa_key) print( f'Instruction for the level can be accessed at thunder-ctf.cloud/thunder/{LEVEL_PATH}.html') except Exception as e: exit()
def create(): print("Level initialization started for: " + LEVEL_PATH) # Create randomized nonce name to avoid namespace conflicts nonce = str(random.randint(100000000000, 999999999999)) bucket_name = f'{RESOURCE_PREFIX}-bucket-{nonce}' print("Level initialization finished for: " + LEVEL_PATH) # Insert deployment config_template_args = {'nonce': nonce} template_files = [ 'core/framework/templates/bucket_acl.jinja', 'core/framework/templates/service_account.jinja', 'core/framework/templates/iam_policy.jinja', 'core/framework/templates/container_vm.jinja' ] deployments.insert(LEVEL_PATH, template_files=template_files, config_template_args=config_template_args) print("Level setup started for: " + LEVEL_PATH) # Insert secret into bucket storage_client = storage.Client() bucket = storage_client.get_bucket(bucket_name) secret_blob = storage.Blob('secret.txt', bucket) secret = levels.make_secret(LEVEL_PATH) secret_blob.upload_from_string(secret) # Create service account key file sa_key = iam.generate_service_account_key(f'{RESOURCE_PREFIX}-access') print(f'Level creation complete for: {LEVEL_PATH}') start_message = ( f'Use the compromised service account credentials stored in {RESOURCE_PREFIX}-access.json to find the secret, ' 'which is located in a file called secret.txt in a private bucket on the project.' ) levels.write_start_info(LEVEL_PATH, start_message, file_name=f'{RESOURCE_PREFIX}-access.json', file_content=sa_key) print( f'Instruction for the level can be accessed at thunder-ctf.cloud/thunder/{LEVEL_PATH}.html' )
def create(second_deploy=True): # Create randomized bucket name to avoid namespace conflict nonce = str(random.randint(100000000000, 999999999999)) nonce_file = f'core/levels/{LEVEL_PATH}/nonce.txt' #write key file in function directory with open(nonce_file, 'w') as f: f.write(nonce) os.chmod(nonce_file, 0o700) print(f'Nonce {nonce} has been written to {nonce_file}') # Set role of default cloud function account credentials, project_id = google.auth.default() print("Level initialization finished for: " + LEVEL_PATH) # Insert deployment config_template_args = {'nonce': nonce} template_files = [ 'core/framework/templates/service_account.jinja', 'core/framework/templates/iam_policy.jinja', 'core/framework/templates/bucket_acl.jinja', 'core/framework/templates/ubuntu_vm.jinja', 'core/framework/templates/cloud_function.jinja' ] print("Level setup started for: " + LEVEL_PATH) start_message = ' Use function entrypoints below to access levels \n\n' for RESOURCE_PREFIX in LEVEL_NAMES: LEVEL_NAME = LEVEL_NAMES[RESOURCE_PREFIX] fvar = FARS[RESOURCE_PREFIX] func_patha = f'core/levels/{LEVEL_PATH}/{RESOURCE_PREFIX}/functionaccess' func_pathc = f'core/levels/{LEVEL_PATH}/{RESOURCE_PREFIX}/functioncheck' #Generate function urls func_template_argc = {'fvar': fvar} func_upload_urla = cloudfunctions.upload_cloud_function( func_patha, FUNCTION_LOCATION) func_upload_urlc = cloudfunctions.upload_cloud_function( func_pathc, FUNCTION_LOCATION, template_args=func_template_argc) #Update deployment with functions config_template_args_patch = { f'funca_upload_url_{RESOURCE_PREFIX}': func_upload_urla, f'funcc_upload_url_{RESOURCE_PREFIX}': func_upload_urlc, f'level_name_{RESOURCE_PREFIX}': LEVEL_NAME, f'resource_prefix_{RESOURCE_PREFIX}': RESOURCE_PREFIX } config_template_args.update(config_template_args_patch) msg = f'https://{FUNCTION_LOCATION}-{project_id}.cloudfunctions.net/{RESOURCE_PREFIX}-f-access-{nonce} {LEVEL_NAMES[RESOURCE_PREFIX]}' start_message += msg + '\n' # scores funciton func_pathsc = f'core/levels/{LEVEL_PATH}/scores' #Generate scores function urls func_template_arg = {'anws': FARS, 'level_names': LEVEL_NAMES} func_upload_urlsc = cloudfunctions.upload_cloud_function( func_pathsc, FUNCTION_LOCATION, template_args=func_template_arg) login_user = os.environ.get('USER', 'USER is not set.') #Update deployment with functions config_template_args_patch = { 'funcc_upload_url_scores': func_upload_urlsc, 'login_user': login_user } config_template_args.update(config_template_args_patch) msg = f'https://{FUNCTION_LOCATION}-{project_id}.cloudfunctions.net/scores-f-{nonce}' start_message += '\n Or access levels through Score Board: \n' + msg + '\n' if second_deploy: deployments.insert(LEVEL_PATH, template_files=template_files, config_template_args=config_template_args, second_deploy=True) else: deployments.insert(LEVEL_PATH, template_files=template_files, config_template_args=config_template_args) try: # Insert secret into bucket storage_client = storage.Client() for b in BUCKETS: bucket_name = f'{b}-bucket-{nonce}' secret = levels.make_secret(LEVEL_PATH) bucket = storage_client.get_bucket(bucket_name) secret_blob = storage.Blob(f'secret_{b}.txt', bucket) secret_blob.upload_from_string(secret) # Create and insert data in datastore for k in KINDS: entities = [{ 'name': f'admin-{k}', 'password': '******', 'active': True }, { 'name': f'editor-{k}', 'password': '******', 'active': True }] kind = f'{k}-{nonce}-{project_id}' client = datastore.Client(project_id) for entity in entities: entity_key = client.key(kind) task = datastore.Entity(key=entity_key) task.update(entity) client.put(task) #print(f'Datastore {kind} created') levels.write_start_info(LEVEL_PATH, start_message) except Exception as e: exit()
def create(second_deploy=True): print("Level initialization started for: " + LEVEL_PATH) # Create randomized nonce name to avoid namespace conflicts nonce = str(random.randint(100000000000, 999999999999)) bucket_name = f'{RESOURCE_PREFIX}-bucket-{nonce}' # Create random function password func_xor_password = str(random.randint(100000000000, 999999999999)) xor_factor = str(random.randint(100000000000, 999999999999)) func_template_args = {'bucket_name': bucket_name, 'xor_factor': xor_factor} # Upload function and get upload url func_upload_url = cloudfunctions.upload_cloud_function( f'core/levels/{LEVEL_PATH}/function', FUNCTION_LOCATION, template_args=func_template_args) print("Level initialization finished for: " + LEVEL_PATH) # Insert deployment config_template_args = { 'nonce': nonce, 'func_xor_password': func_xor_password, 'func_upload_url': func_upload_url } template_files = [ 'core/framework/templates/bucket_acl.jinja', 'core/framework/templates/cloud_function.jinja', 'core/framework/templates/service_account.jinja', 'core/framework/templates/iam_policy.jinja' ] if second_deploy: deployments.insert(LEVEL_PATH, template_files=template_files, config_template_args=config_template_args, second_deploy=True) else: deployments.insert(LEVEL_PATH, template_files=template_files, config_template_args=config_template_args) try: print("Level setup started for: " + LEVEL_PATH) # Insert secret into bucket storage_client = storage.Client() bucket = storage_client.get_bucket(bucket_name) secret_blob = storage.Blob('secret.txt', bucket) secret = levels.make_secret(LEVEL_PATH) secret_blob.upload_from_string(secret) # Create service account key file sa_key = iam.generate_service_account_key(f'{RESOURCE_PREFIX}-access') print(f'Level creation complete for: {LEVEL_PATH}') start_message = ( f'Use the given compromised credentials to find the secret hidden in the level.' ) levels.write_start_info(LEVEL_PATH, start_message, file_name=f'{RESOURCE_PREFIX}-access.json', file_content=sa_key) print( f'Instruction for the level can be accessed at thunder-ctf.cloud/thunder/{LEVEL_PATH}.html' ) except Exception as e: exit()
def create(second_deploy=True): print("Level initialization started for: " + LEVEL_PATH) # Create randomized nonce name to avoid namespace conflicts nonce = str(random.randint(100000000000, 999999999999)) bucket_name = f'{RESOURCE_PREFIX}-bucket-{nonce}' func_template_args = {'bucket_name': bucket_name} # Upload function and get upload url func_upload_url = cloudfunctions.upload_cloud_function( f'core/levels/{LEVEL_PATH}/function', FUNCTION_LOCATION, template_args=func_template_args) print("Level initialization finished for: " + LEVEL_PATH) secret = levels.make_secret(LEVEL_PATH) # Insert deployment config_template_args = { 'nonce': nonce, 'secret': secret, 'func_upload_url': func_upload_url } template_files = [ 'core/framework/templates/bucket_acl.jinja', 'core/framework/templates/cloud_function.jinja', 'core/framework/templates/service_account.jinja', 'core/framework/templates/iam_policy.jinja', 'core/framework/templates/ubuntu_vm.jinja' ] if second_deploy: deployments.insert(LEVEL_PATH, template_files=template_files, config_template_args=config_template_args, second_deploy=True) else: deployments.insert(LEVEL_PATH, template_files=template_files, config_template_args=config_template_args) try: print("Level setup started for: " + LEVEL_PATH) # Insert dummy files into bucket gcstorage.upload_directory_recursive( f'core/levels/{LEVEL_PATH}/bucket', bucket_name) # Delete startup script that contains secret from instance metadata credentials, project_id = google.auth.default() compute_api = discovery.build('compute', 'v1', credentials=credentials) instance_info = compute_api.instances().get( project=project_id, zone=INSTANCE_ZONE, instance=f'{RESOURCE_PREFIX}-instance').execute() metadata_fingerprint = instance_info['metadata']['fingerprint'] set_metadata_body = {'fingerprint': metadata_fingerprint, 'items': []} compute_api.instances().setMetadata( project=project_id, zone=INSTANCE_ZONE, instance=f'{RESOURCE_PREFIX}-instance', body=set_metadata_body).execute() # Create service account key file sa_key = iam.generate_service_account_key(f'{RESOURCE_PREFIX}-access') print(f'Level creation complete for: {LEVEL_PATH}') start_message = ( f'In this level, look for a file named "secret.txt," which is owned by "secretuser." ' 'Use the given compromised credentials to find it.') levels.write_start_info(LEVEL_PATH, start_message, file_name=f'{RESOURCE_PREFIX}-access.json', file_content=sa_key) print( f'Instruction for the level can be accessed at thunder-ctf.cloud/thunder/{LEVEL_PATH}.html' ) except Exception as e: exit()
def create(second_deploy=True): bar = ProgBar() print("\nLevel initialization started for: " + LEVEL_PATH) nonce = str(random.randint(100000000000, 999999999999)) credentials, project_id = google.auth.default() #the cloud function may need to know information about the vm in order to hit our API. put that info here. func_template_args = {} func_upload_url = cloudfunctions.upload_cloud_function( f'core/levels/{LEVEL_PATH}/resources/rmUser', FUNCTION_LOCATION, template_args=func_template_args) # Create database password value db_secret_value = '' for _ in range(0, 64): db_secret_value += random.choice(string.ascii_letters + string.digits) create_secret(DB_SECRET_ID, db_secret_value) config_template_args = { 'nonce': nonce, 'root_password': db_secret_value, 'func_upload_url': func_upload_url } template_files = [ 'core/framework/templates/service_account.jinja', 'core/framework/templates/iam_policy.jinja', 'core/framework/templates/sql_db.jinja', 'core/framework/templates/container_vm.jinja', 'core/framework/templates/bucket_acl.jinja', 'core/framework/templates/cloud_function.jinja' ] if second_deploy: deployments.insert(LEVEL_PATH, template_files=template_files, config_template_args=config_template_args, second_deploy=True) else: deployments.insert(LEVEL_PATH, template_files=template_files, config_template_args=config_template_args) print("\nLevel setup started for: " + LEVEL_PATH) bar.tick('Creating database tables') create_tables(db_secret_value) dev_key = iam.generate_service_account_key('dev-account') dev_sa = service_account.Credentials.from_service_account_info( json.loads(dev_key)) compute_admin_key = iam.generate_service_account_key('compute-admin') logging_key = iam.generate_service_account_key('log-viewer') # add vm files to bucket bar.tick('Uploading container source to bucket') storage_client = storage.Client() vm_image_bucket = storage_client.get_bucket(f'vm-image-bucket-{nonce}') gcstorage.upload_directory_recursive( f'core/levels/{LEVEL_PATH}/resources/api-engine', f'vm-image-bucket-{nonce}') storage_blob = storage.Blob('compute-admin.json', vm_image_bucket) storage_blob.upload_from_string(compute_admin_key) bar.tick('Creating developer logs') #os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = 'start/dev-account.json' url = "http://us-central1-" + project_id + ".cloudfunctions.net/rm-user-" + nonce req = google.auth.transport.requests.Request() id_token = google.oauth2.id_token.fetch_id_token(req, url) headers = {'Authorization': f"Bearer {id_token}"} data = {'name': 'Robert Caldwell', 'authentication': dev_key} resp = req(url, method='POST', body=data, headers=headers) bar.tick('Starting exploit script') hostname = exploit(nonce, logging_key, bar) hack(hostname) bar.tick('Exploit complete') print(f'\nLevel creation complete for: {LEVEL_PATH}') start_message = ( 'Nefarious statuses are being posted by accounts without the owner\'s knowledge. Find out how this is happening. \nInstructions for each level can be found at: https://asokamoto.github.io/CloudAuditCTFs/' ) levels.write_start_info(LEVEL_PATH, start_message)