-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Antreas Pogiatzis
committed
Jun 18, 2024
1 parent
300d74d
commit d5039ce
Showing
11 changed files
with
475 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
name: 'Discover CTF challenges in a directory' | ||
description: 'Finds CTF challenge directories that contain a challenge.yml file' | ||
|
||
inputs: | ||
base-dir: | ||
description: 'Directory to discover challenges from' | ||
required: true | ||
default: '.' | ||
|
||
outputs: | ||
dirs: | ||
description: "Challenge directories" | ||
value: ${{ steps.find-challenge-dirs.outputs.dirs }} | ||
|
||
runs: | ||
using: "composite" | ||
steps: | ||
- name: Find directories containing challenge.yml | ||
id: find-challenge-dirs | ||
run: | | ||
dirs=$(find ${{ inputs.base_dir }} -name 'challenge.yml' -printf '%h\n' | sort -u) | ||
echo dirs="${dirs//$'\n'/ }" >> "$GITHUB_OUTPUT" | ||
shell: bash | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
FROM python:3.8-slim-buster | ||
|
||
COPY entrypoint.py /entrypoint.py | ||
COPY README.jinja /README.jinja | ||
COPY challenge_README.jinja /challenge_README.jinja | ||
|
||
# Python dependencies | ||
RUN pip install pyyaml jinja2 | ||
|
||
# File to execute when the docker container starts up (`entrypoint.sh`) | ||
ENTRYPOINT ["python", "/entrypoint.py"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
![Grant thornton Beginner Quest](_assets/gtbq.png) | ||
# Grant Thornton Beginner Quest 2024 | ||
|
||
**Dates:** 05/07/2024 - 14/07/2024 | ||
|
||
## Repository Structure | ||
|
||
This is the official repository with the challenges published in Grant Thornton Beginner Quest (GTBQ) CTF 2024. Each challenge has a public, solution and setup folder (if applicable) and is accompanied with a short description. The setup folder contains all the files required to build and host the challenge and usually contains the flag and a proof of concept solution as well. The public folder contains the files that are released to the participant during the competition. | ||
|
||
## Dependencies | ||
|
||
Although some of the challenges may run as is, it is recommended that you have docker and docker-compose installed and use the provided scripts to run the challenges to ensure isolation and therefore proper environment setup. | ||
|
||
## Challenges | ||
|
||
{% for category, challenges in challenge_categories.items() %} | ||
### {{ category }} | ||
|
||
| Name | Author | | ||
| ---- | ------ | | ||
{% for challenge in challenges %}| [{{ challenge.name }}]({{ challenge.dir }}) | {{ challenge.author }} | | ||
{% endfor %} | ||
|
||
{% endfor %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
name: 'Generate Challenge README' | ||
description: 'Generates a README file with a table of challenges from provided directories' | ||
|
||
inputs: | ||
directories: | ||
description: 'Comma separated list of directories that contain a challenge.yml file' | ||
required: true | ||
|
||
runs: | ||
using: 'docker' | ||
image: 'Dockerfile' | ||
args: | ||
- ${{ inputs.directories }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
# {{ challenge.name }} | ||
{% if challenge.type == "dynamic_docker" %} | ||
[![Try in PWD](https://raw.githubusercontent.com/play-with-docker/stacks/master/assets/images/button.png)](https://labs.play-with-docker.com/?stack={{ challenge.docker_compose_url }}) | ||
{% endif %} | ||
|
||
**Category**: {{ challenge.category }} | ||
|
||
**Author**: {{ challenge.author }} | ||
|
||
## Description | ||
|
||
{{ challenge.description }} | ||
|
||
{% if challenge.type == "dynamic_docker" %} | ||
## Run locally | ||
|
||
Launch challenge: | ||
``` | ||
curl -sSL {{ challenge.docker_compose_url }} | docker compose -f - up -d | ||
``` | ||
|
||
Shutdown challenge: | ||
``` | ||
curl -sSL {{ challenge.docker_compose_url }} | docker compose -f - down | ||
``` | ||
{% endif %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import os | ||
import yaml | ||
import sys | ||
from jinja2 import Environment, FileSystemLoader | ||
from urllib.parse import urljoin | ||
|
||
|
||
class IgnoreSpecificConstructorLoader(yaml.SafeLoader): | ||
def ignore_constructor(self, node): | ||
return None | ||
|
||
|
||
IgnoreSpecificConstructorLoader.add_constructor( | ||
"!filecontents", IgnoreSpecificConstructorLoader.ignore_constructor | ||
) | ||
|
||
|
||
def parse_challenge(directory): | ||
path = os.path.join(directory, "challenge.yml") | ||
print(path) | ||
with open(path, "r") as file: | ||
return yaml.load(file, Loader=IgnoreSpecificConstructorLoader) | ||
|
||
|
||
def main(): | ||
directories = sys.argv[1].split(" ") | ||
challenge_categories = {} | ||
|
||
file_loader = FileSystemLoader("/") | ||
env = Environment(loader=file_loader) | ||
challenge_readme_tmpl = env.get_template("challenge_README.jinja") | ||
|
||
for directory in directories: | ||
challenge = parse_challenge(directory) | ||
category = challenge["category"] | ||
challenge["dir"] = directory | ||
challenge["docker_compose_url"] = urljoin("https://raw.githubusercontent.com/cybermouflons/gt-beginner-quest-2024/master/", f"{directory}/docker-compose.yml") | ||
if category not in challenge_categories: | ||
challenge_categories[category] = [] | ||
challenge_categories[category].append(challenge) | ||
|
||
chall_readme = challenge_readme_tmpl.render(challenge=challenge) | ||
with open(os.path.join(directory, "README.md"), "w") as f: | ||
f.write(chall_readme) | ||
|
||
readme_tmpl = env.get_template("README.jinja") | ||
output = readme_tmpl.render(challenge_categories=challenge_categories) | ||
|
||
with open("README.md", "w") as file: | ||
file.write(output) | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
name: Build ans Push Challenges | ||
|
||
on: [workflow_call] # allow this workflow to be called from other workflows | ||
|
||
env: | ||
REGISTRY: ghcr.io | ||
|
||
jobs: | ||
run: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Checkout repository | ||
uses: actions/checkout@v3 | ||
with: | ||
lfs: 'false' | ||
|
||
- name: Challenge discovery | ||
id: challenge-discovery | ||
uses: ./.github/actions/discover-challenges | ||
with: | ||
base-dir: '.' | ||
|
||
- name: Docker challenge discovery | ||
id: docker-chall-discovery | ||
run: | | ||
IFS=' ' read -r -a chall_dirs <<< "${{ steps.challenge-discovery.outputs.dirs }}" | ||
for dir in "${chall_dirs[@]}"; do | ||
if [[ -f "${dir}/docker-compose.yml" ]]; then | ||
dirs+=("${dir}") | ||
fi | ||
done | ||
echo "dirs=${dirs[*]}" >> "$GITHUB_OUTPUT" | ||
- name: Docker challenges list | ||
run: echo "${{ steps.docker-chall-discovery.outputs.dirs }}" | ||
|
||
- uses: actions/cache/restore@v3 | ||
id: challenges-hashes-cache | ||
with: | ||
path: .cache/last_hashes | ||
key: last-hashes | ||
|
||
- name: Create folder challenge hashes | ||
id: challenge-hashes | ||
run: | | ||
if [ -d .cache ]; then | ||
rm -f .cache/hashes | ||
fi | ||
mkdir -p .cache | ||
touch .cache/hashes | ||
IFS=' ' read -r -a challenge_dirs <<< "${{ steps.docker-chall-discovery.outputs.dirs }}" | ||
for dir in "${challenge_dirs[@]}"; do | ||
echo "$(find $dir -type f -print0 | sort -z | xargs -0 sha1sum | sha1sum | cut -d " " -f 1) $dir" >> .cache/hashes | ||
done | ||
sort .cache/hashes -o .cache/hashes | ||
- name: Find modified challenges | ||
id: modified-chalenges | ||
run: | | ||
COMMIT_MESSAGE=$(git log --format=%B -n 1) | ||
if [[ "$COMMIT_MESSAGE" == *"[no-cache]"* ]]; then | ||
rm -f .cache/last_hashes | ||
fi | ||
touch .cache/last_hashes | ||
changes=$(diff .cache/hashes .cache/last_hashes) || true | ||
if [[ -n "$changes" ]]; then | ||
changed_dirs=$(echo "$changes" | grep '<' | cut -d ' ' -f 3-) | ||
echo "$changed_dirs" | ||
fi | ||
mv .cache/hashes .cache/last_hashes | ||
echo dirs="${changed_dirs//$'\n'/ }" >> "$GITHUB_OUTPUT" | ||
- name: Changed challenges list | ||
run: echo "${{ steps.modified-chalenges.outputs.dirs }}" | ||
|
||
- name: Log in to the Container registry | ||
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1 | ||
with: | ||
registry: ${{ env.REGISTRY }} | ||
username: ${{ github.actor }} | ||
password: ${{ secrets.GITHUB_TOKEN }} | ||
|
||
- name: Set up Docker Buildx | ||
uses: docker/setup-buildx-action@v1 | ||
|
||
- name: Build challenges | ||
run: | | ||
IFS=' ' read -r -a challenge_dirs <<< "${{ steps.modified-chalenges.outputs.dirs }}" | ||
for challenge_dir in "${challenge_dirs[@]}"; do | ||
docker compose -f $challenge_dir/docker-compose.yml build | ||
done | ||
- name: Push challenges | ||
run: | | ||
IFS=' ' read -r -a challenge_dirs <<< "${{ steps.modified-chalenges.outputs.dirs }}" | ||
for challenge_dir in "${challenge_dirs[@]}"; do | ||
docker compose -f $challenge_dir/docker-compose.yml push | ||
done | ||
- name: Clear cache | ||
uses: actions/github-script@v6 | ||
with: | ||
script: | | ||
console.log("About to clear") | ||
const cachesToDelete = ["last-hashes"]; | ||
const caches = await github.rest.actions.getActionsCacheList({ | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
}) | ||
for (const cache of caches.data.actions_caches) { | ||
if (cachesToDelete.includes(cache.key)) { | ||
console.log(cache) | ||
github.rest.actions.deleteActionsCacheById({ | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
cache_id: cache.id, | ||
}) | ||
} | ||
} | ||
console.log("Clear completed") | ||
- name: Save folder challenge hashes | ||
id: challenge-hashes-save | ||
uses: actions/cache/save@v3 | ||
with: | ||
path: .cache/last_hashes | ||
key: last-hashes |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
name: Sync CTFd | ||
|
||
on: [workflow_call] # allow this workflow to be called from other workflows | ||
|
||
jobs: | ||
sync: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Checkout repository | ||
uses: actions/checkout@v3 | ||
|
||
- name: Create .ctf/config file | ||
run: | | ||
mkdir -p .ctf | ||
echo "${{ secrets.CTF_CLI_CONFIG }}" > .ctf/config | ||
- name: Install ctfcli | ||
run: | | ||
python -m pip install --upgrade pip | ||
pip install git+https://github.com/apogiatzis/ctfcli | ||
- name: Challenge discovery | ||
id: challenge-discovery | ||
uses: ./.github/actions/discover-challenges | ||
with: | ||
base-dir: '.' | ||
|
||
- uses: actions/cache/restore@v3 | ||
id: challenges-hashes-cache | ||
with: | ||
path: .cache/last_hashes | ||
key: ctfd-pipeline-last-hashes | ||
|
||
- name: Create folder challenge hashes | ||
id: challenge-hashes | ||
run: | | ||
if [ -d .cache ]; then | ||
rm -f .cache/hashes | ||
fi | ||
mkdir -p .cache | ||
touch .cache/hashes | ||
IFS=' ' read -r -a challenge_dirs <<< "${{ steps.challenge-discovery.outputs.dirs }}" | ||
for dir in "${challenge_dirs[@]}"; do | ||
echo "$(find $dir -type f -print0 | sort -z | xargs -0 sha1sum | sha1sum | cut -d " " -f 1) $dir" >> .cache/hashes | ||
done | ||
sort .cache/hashes -o .cache/hashes | ||
- name: Find modified challenges | ||
id: modified-chalenges | ||
run: | | ||
COMMIT_MESSAGE=$(git log --format=%B -n 1) | ||
if [[ "$COMMIT_MESSAGE" == *"[no-cache]"* ]]; then | ||
rm -f .cache/last_hashes | ||
fi | ||
touch .cache/last_hashes | ||
changes=$(diff .cache/hashes .cache/last_hashes) || true | ||
if [[ -n "$changes" ]]; then | ||
changed_dirs=$(echo "$changes" | grep '<' | cut -d ' ' -f 3-) | ||
echo "$changed_dirs" | ||
fi | ||
mv .cache/hashes .cache/last_hashes | ||
echo dirs="${changed_dirs//$'\n'/ }" >> "$GITHUB_OUTPUT" | ||
- name: Changed challenges list | ||
run: echo "${{ steps.modified-chalenges.outputs.dirs }}" | ||
|
||
- name: Challenge Sync | ||
id: challenge-sync | ||
run: | | ||
IFS=' ' read -r -a chall_dirs <<< "${{ steps.modified-chalenges.outputs.dirs }}" | ||
for dir in "${chall_dirs[@]}"; do | ||
ctf challenge install ${dir} | ||
ctf challenge sync ${dir} | ||
done | ||
echo "dirs=${dirs[*]}" >> "$GITHUB_OUTPUT" | ||
- name: Clear cache | ||
uses: actions/github-script@v6 | ||
with: | ||
script: | | ||
console.log("About to clear") | ||
const cachesToDelete = ["ctfd-pipeline-last-hashes"]; | ||
const caches = await github.rest.actions.getActionsCacheList({ | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
}) | ||
for (const cache of caches.data.actions_caches) { | ||
if (cachesToDelete.includes(cache.key)) { | ||
console.log(cache) | ||
github.rest.actions.deleteActionsCacheById({ | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
cache_id: cache.id, | ||
}) | ||
} | ||
} | ||
console.log("Clear completed") | ||
- name: Save folder challenge hashes | ||
id: challenge-hashes-save | ||
uses: actions/cache/save@v3 | ||
with: | ||
path: .cache/last_hashes | ||
key: ctfd-pipeline-last-hashes |
Oops, something went wrong.