The build script looks for a config.yml in the root of the ctf directory. All configuration options within this file will be submitted to CTFd.
---
config:
ctf_theme: core-beta
user_mode: users
account_visibility: private
score_visibility: private
registration_visibility: private
registration_code: private by design
paused: 1
ctf_name: "{{ CONFIG_ACCOUNT.title() }} CTF"
ctf_description: "Capture the Flag event hosted by Lacework for {{ CONFIG_ACCOUNT }} on the {{ CONFIG_SUBACCOUNT }} subaccount. Scope includes the following AWS accounts: {{ CONFIG_AWS_ACCOUNTS }}"
Every directory within this ctf directory will become a category. Prefix with a number_ for sorting purposes. i.e. 1_getting to know you
Inside of each category directory create a challenges.yml with the challenge structure. The field structure follows what the CTFd API expects.
---
challenges:
- name: compliance
value: 5
type: standard
state: visible
description: |
description text
flags:
- type: static
data: case_insensitive
content: private by design
- name: oh really?
value: 100
type: standard
connection_info: 'https://memecentral.org/wp-content/uploads/2019/08/really-seriously-meme.jpg'
description: |
Do you believe the user account **{{ CONFIG_AWS_USER }}** is problematic? Why or why not?
flags:
- type: regex
content: .*
Any variable passed in via a config.json will be made available in all YAML files. The variable will be uppercased and then prefixed with CONFIG_ Examples below.
aws_account becomes CONFIG_AWS_ACCOUNT
account becomes CONFIG_ACCOUNT
subaccount becomes CONFIG_SUBACCOUNT
To make the challenge descriptions and flags dynamic you can add a parse_challenge function into a __init__.py file in the category directory. Each challenge for that category will be passed through this function. Example function signature below. schema argument is an import of schema as a module. challenge argument is the JSON of the challenge object. config is the passed in config.json
def parse_challenge(schema, challenge, config):
# modify challenge details
# add flag(s) to challenge
challenge['flags'] = []
flag = { 'type': 'static', 'data': 'case_insensitive', 'content': 'flag goes here' }
challenge['flags'].append(flag)
return challenge