diff --git a/challenges/misc/a-true-connection/README.md b/challenges/misc/a-true-connection/README.md new file mode 100644 index 0000000..683f786 --- /dev/null +++ b/challenges/misc/a-true-connection/README.md @@ -0,0 +1,3 @@ +# A true connection + +An example challenge to demonstrate an instanced TCP challenge. diff --git a/challenges/misc/a-true-connection/challenge.yml b/challenges/misc/a-true-connection/challenge.yml new file mode 100644 index 0000000..45ceb59 --- /dev/null +++ b/challenges/misc/a-true-connection/challenge.yml @@ -0,0 +1,31 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/ctfpilot/challenge-schema/refs/heads/main/schema.json + +enabled: true +name: A true connection +slug: a-true-connection +author: The Mikkel +category: misc +difficulty: beginner +tags: [ + Instanced, + TCP, + Example +] +type: instanced +instanced_type: tcp +instanced_name: null +instanced_subdomains: [] +connection: null +flag: +- flag: ctfpilot{what-a-connection-you-have-made!} + case_sensitive: false +points: 1000 +decay: 75 +min_points: 100 +description_location: description.md +handout_dir: handout +dockerfile_locations: +- location: src/Dockerfile + context: src/ + identifier: null + diff --git a/challenges/misc/a-true-connection/description.md b/challenges/misc/a-true-connection/description.md new file mode 100644 index 0000000..ba24afa --- /dev/null +++ b/challenges/misc/a-true-connection/description.md @@ -0,0 +1,6 @@ +# A true connection + +**Difficulty:** Beginner +**Author:** The Mikkel + +What a connection you can make, if you can connect to the endpoint below! diff --git a/challenges/misc/a-true-connection/handout/.gitkeep b/challenges/misc/a-true-connection/handout/.gitkeep new file mode 100644 index 0000000..0f065bc --- /dev/null +++ b/challenges/misc/a-true-connection/handout/.gitkeep @@ -0,0 +1,2 @@ +# This file is used to keep the directory in the repository. +# This directory is used to store files that are handed out, for the challenge. The files are automatically zipped and copied to the files directory. \ No newline at end of file diff --git a/challenges/misc/a-true-connection/k8s/challenge/k8s.yml b/challenges/misc/a-true-connection/k8s/challenge/k8s.yml new file mode 100644 index 0000000..7520395 --- /dev/null +++ b/challenges/misc/a-true-connection/k8s/challenge/k8s.yml @@ -0,0 +1,130 @@ +apiVersion: kube-ctf.ctfpilot.com/v1 +kind: instancedChallenge +metadata: + name: "a-true-connection" + labels: + challenges.ctfpilot.com/type: "tcp" + challenges.ctfpilot.com/name: "a-true-connection" + challenges.ctfpilot.com/category: "misc" + ctfpilot.com/component: "instanced-challenge" +spec: + expires: 3600 + available_at: 0 + type: tcp + template: | + apiVersion: apps/v1 + kind: Deployment + metadata: + name: "ctf-{{ deployment_id }}" + namespace: ctfpilot-challenges-instanced + labels: + challenges.ctfpilot.com/type: "tcp" + challenges.ctfpilot.com/name: "a-true-connection" + challenges.ctfpilot.com/category: "misc" + instanced.challenges.ctfpilot.com/deployment: "{{ deployment_id }}" + instanced.challenges.ctfpilot.com/owner: "{{ owner_id }}" + ctfpilot.com/component: "instanced-challenge" + annotations: + janitor/expires: "{{ expires }}" + spec: + replicas: 1 + selector: + matchLabels: + instanced.challenges.ctfpilot.com/deployment: "{{ deployment_id }}" + instanced.challenges.ctfpilot.com/owner: "{{ owner_id }}" + template: + metadata: + labels: + role: "tcp" + challenges.ctfpilot.com/type: "tcp" + challenges.ctfpilot.com/name: "a-true-connection" + challenges.ctfpilot.com/category: "misc" + instanced.challenges.ctfpilot.com/deployment: "{{ deployment_id }}" + instanced.challenges.ctfpilot.com/owner: "{{ owner_id }}" + ctfpilot.com/component: "instanced-challenge" + spec: + enableServiceLinks: false + automountServiceAccountToken: false + imagePullSecrets: + - name: dockerconfigjson-github-com + dnsPolicy: None + dnsConfig: + nameservers: + - 1.1.1.1 + - 8.8.8.8 + tolerations: + - key: "cluster.ctfpilot.com/node" + value: "scaler" + effect: "PreferNoSchedule" + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: "cluster.ctfpilot.com/node" + operator: In + values: + - scaler + containers: + - name: tcp + image: ghcr.io/ctfpilot/challenges-example-misc-a-true-connection:3 + imagePullPolicy: IfNotPresent + resources: + limits: + cpu: 200m + memory: 256Mi + requests: + cpu: 10m + memory: 32Mi + ports: + - containerPort: 8080 + name: tcp + --- + apiVersion: v1 + kind: Service + metadata: + name: "ctf-{{ deployment_id }}" + namespace: ctfpilot-challenges-instanced + labels: + challenges.ctfpilot.com/type: "tcp" + challenges.ctfpilot.com/name: "a-true-connection" + challenges.ctfpilot.com/category: "misc" + instanced.challenges.ctfpilot.com/deployment: "{{ deployment_id }}" + instanced.challenges.ctfpilot.com/owner: "{{ owner_id }}" + ctfpilot.com/component: "instanced-challenge" + annotations: + janitor/expires: "{{ expires }}" + spec: + selector: + role: "tcp" + instanced.challenges.ctfpilot.com/deployment: "{{ deployment_id }}" + ports: + - port: 8080 + name: tcp + targetPort: tcp + --- + apiVersion: traefik.io/v1alpha1 + kind: IngressRouteTCP + metadata: + name: "ingress-ctf-{{ deployment_id }}" + namespace: ctfpilot-challenges-instanced + labels: + challenges.ctfpilot.com/type: "tcp" + challenges.ctfpilot.com/name: "a-true-connection" + challenges.ctfpilot.com/category: "misc" + instanced.challenges.ctfpilot.com/deployment: "{{ deployment_id }}" + instanced.challenges.ctfpilot.com/owner: "{{ owner_id }}" + ctfpilot.com/component: "instanced-challenge" + annotations: + janitor/expires: "{{ expires }}" + traefik.ingress.kubernetes.io/router.priority: "100" + spec: + entryPoints: + - websecure + routes: + - match: HostSNI(`{{ deployment_id }}.{{ domain }}`) + services: + - name: "ctf-{{ deployment_id }}" + port: 8080 + tls: + secretName: kubectf-cert-challs diff --git a/challenges/misc/a-true-connection/k8s/config/Chart.yaml b/challenges/misc/a-true-connection/k8s/config/Chart.yaml new file mode 100644 index 0000000..7ab72d4 --- /dev/null +++ b/challenges/misc/a-true-connection/k8s/config/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: configmap-a-true-connection +version: 1.3.0 +description: Challenge configmap for a-true-connection in category misc +appVersion: "1.3.0" +type: application diff --git a/challenges/misc/a-true-connection/k8s/config/templates/k8s.yml b/challenges/misc/a-true-connection/k8s/config/templates/k8s.yml new file mode 100644 index 0000000..8e2417a --- /dev/null +++ b/challenges/misc/a-true-connection/k8s/config/templates/k8s.yml @@ -0,0 +1,64 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: "challenge-misc-a-true-connection" + labels: + challenges.ctfpilot.com/type: "tcp" + challenges.ctfpilot.com/name: "a-true-connection" + challenges.ctfpilot.com/category: "misc" + challenges.ctfpilot.com/version: "3" + challenges.ctfpilot.com/configmap: "challenge-config" + challenges.ctfpilot.com/enabled: "true" + ctfpilot.com/component: "challenge-config" +data: + name: "a-true-connection" + path: "challenges/misc/a-true-connection" + repository: "ctfpilot/challenges-example" + generated_at: "2025-12-20 19:25:35" + challenge: | + { + "$schema": "https://raw.githubusercontent.com/ctfpilot/challenge-schema/refs/heads/main/schema.json", + "enabled": true, + "name": "A true connection", + "slug": "a-true-connection", + "author": "The Mikkel", + "category": "misc", + "difficulty": "beginner", + "tags": [ + "Instanced", + "TCP", + "Example" + ], + "type": "instanced", + "instanced_type": "tcp", + "instanced_name": null, + "instanced_subdomains": [], + "connection": null, + "flag": [ + { + "flag": "ctfpilot{what-a-connection-you-have-made!}", + "case_sensitive": false + } + ], + "points": 1000, + "decay": 75, + "min_points": 100, + "description_location": "description.md", + "handout_dir": "handout", + "dockerfile_locations": [ + { + "location": "src/Dockerfile", + "context": "src/", + "identifier": null + } + ] + } + + description: | + # A true connection + + **Difficulty:** Beginner + **Author:** The Mikkel + + What a connection you can make, if you can connect to the endpoint below! + diff --git a/challenges/misc/a-true-connection/k8s/config/values.yaml b/challenges/misc/a-true-connection/k8s/config/values.yaml new file mode 100644 index 0000000..d6782a9 --- /dev/null +++ b/challenges/misc/a-true-connection/k8s/config/values.yaml @@ -0,0 +1,11 @@ +challenge: + enabled: true + name: a-true-connection + category: misc + type: tcp + version: 3 + path: challenges/misc/a-true-connection +kubectf: + expires: 3600 + availableAt: 0 + host: example.com diff --git a/challenges/misc/a-true-connection/k8s/files/.gitkeep b/challenges/misc/a-true-connection/k8s/files/.gitkeep new file mode 100644 index 0000000..ef3682f --- /dev/null +++ b/challenges/misc/a-true-connection/k8s/files/.gitkeep @@ -0,0 +1 @@ +# This file is to keep the directory in git. diff --git a/challenges/misc/a-true-connection/solution/README.md b/challenges/misc/a-true-connection/solution/README.md new file mode 100644 index 0000000..7fcdc60 --- /dev/null +++ b/challenges/misc/a-true-connection/solution/README.md @@ -0,0 +1,4 @@ +# Solution + +Connect to the service using the provided connection details. +From here, the user has to send a message to the server, which will respond with the flag. diff --git a/challenges/misc/a-true-connection/src/.gitkeep b/challenges/misc/a-true-connection/src/.gitkeep new file mode 100644 index 0000000..76376a2 --- /dev/null +++ b/challenges/misc/a-true-connection/src/.gitkeep @@ -0,0 +1,2 @@ +# This file is used to keep the directory in the repository. +# This directory is used to store source files for the challenge. \ No newline at end of file diff --git a/challenges/misc/a-true-connection/src/Dockerfile b/challenges/misc/a-true-connection/src/Dockerfile new file mode 100644 index 0000000..726f8c2 --- /dev/null +++ b/challenges/misc/a-true-connection/src/Dockerfile @@ -0,0 +1,4 @@ +# Dockerfile for misc - A true connection +FROM istio/tcp-echo-server:1.1 + +CMD [ "8080", "FLAG: ctfpilot{what-a-connection-you-have-made!} | Your message: " ] diff --git a/challenges/misc/a-true-connection/src/docker-compose.yml b/challenges/misc/a-true-connection/src/docker-compose.yml new file mode 100644 index 0000000..d840188 --- /dev/null +++ b/challenges/misc/a-true-connection/src/docker-compose.yml @@ -0,0 +1,5 @@ +services: + a-true-connection: + build: . + ports: + - "8080:8080/tcp" diff --git a/challenges/misc/a-true-connection/template/.gitkeep b/challenges/misc/a-true-connection/template/.gitkeep new file mode 100644 index 0000000..709b816 --- /dev/null +++ b/challenges/misc/a-true-connection/template/.gitkeep @@ -0,0 +1,2 @@ +# This file is used to keep the directory in the repository. +# This directory is used to store templates for the challenge deployment. \ No newline at end of file diff --git a/challenges/misc/a-true-connection/template/k8s.yml b/challenges/misc/a-true-connection/template/k8s.yml new file mode 100644 index 0000000..f55c8c6 --- /dev/null +++ b/challenges/misc/a-true-connection/template/k8s.yml @@ -0,0 +1,116 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: "ctf-{{ deployment_id }}" + namespace: ctfpilot-challenges-instanced + labels: + challenges.ctfpilot.com/type: "{{ CHALLENGE_TYPE }}" + challenges.ctfpilot.com/name: "{{ CHALLENGE_NAME }}" + challenges.ctfpilot.com/category: "{{ CHALLENGE_CATEGORY }}" + instanced.challenges.ctfpilot.com/deployment: "{{ deployment_id }}" + instanced.challenges.ctfpilot.com/owner: "{{ owner_id }}" + ctfpilot.com/component: "instanced-challenge" + annotations: + janitor/expires: "{{ expires }}" +spec: + replicas: 1 + selector: + matchLabels: + instanced.challenges.ctfpilot.com/deployment: "{{ deployment_id }}" + instanced.challenges.ctfpilot.com/owner: "{{ owner_id }}" + template: + metadata: + labels: + role: "{{ CHALLENGE_TYPE }}" + challenges.ctfpilot.com/type: "{{ CHALLENGE_TYPE }}" + challenges.ctfpilot.com/name: "{{ CHALLENGE_NAME }}" + challenges.ctfpilot.com/category: "{{ CHALLENGE_CATEGORY }}" + instanced.challenges.ctfpilot.com/deployment: "{{ deployment_id }}" + instanced.challenges.ctfpilot.com/owner: "{{ owner_id }}" + ctfpilot.com/component: "instanced-challenge" + spec: + enableServiceLinks: false + automountServiceAccountToken: false + imagePullSecrets: + - name: dockerconfigjson-github-com + dnsPolicy: None + dnsConfig: + nameservers: + - 1.1.1.1 + - 8.8.8.8 + tolerations: + - key: "cluster.ctfpilot.com/node" + value: "scaler" + effect: "PreferNoSchedule" + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: "cluster.ctfpilot.com/node" + operator: In + values: + - scaler + containers: + - name: tcp + image: ghcr.io/{{ CHALLENGE_REPO }}-{{ DOCKER_IMAGE }}:{{ CHALLENGE_VERSION }} + imagePullPolicy: IfNotPresent + resources: + limits: + cpu: 200m + memory: 256Mi + requests: + cpu: 10m + memory: 32Mi + ports: + - containerPort: 8080 + name: tcp +--- +apiVersion: v1 +kind: Service +metadata: + name: "ctf-{{ deployment_id }}" + namespace: ctfpilot-challenges-instanced + labels: + challenges.ctfpilot.com/type: "{{ CHALLENGE_TYPE }}" + challenges.ctfpilot.com/name: "{{ CHALLENGE_NAME }}" + challenges.ctfpilot.com/category: "{{ CHALLENGE_CATEGORY }}" + instanced.challenges.ctfpilot.com/deployment: "{{ deployment_id }}" + instanced.challenges.ctfpilot.com/owner: "{{ owner_id }}" + ctfpilot.com/component: "instanced-challenge" + annotations: + janitor/expires: "{{ expires }}" +spec: + selector: + role: "{{ CHALLENGE_TYPE }}" + instanced.challenges.ctfpilot.com/deployment: "{{ deployment_id }}" + ports: + - port: 8080 + name: tcp + targetPort: tcp +--- +apiVersion: traefik.io/v1alpha1 +kind: IngressRouteTCP +metadata: + name: "ingress-ctf-{{ deployment_id }}" + namespace: ctfpilot-challenges-instanced + labels: + challenges.ctfpilot.com/type: "{{ CHALLENGE_TYPE }}" + challenges.ctfpilot.com/name: "{{ CHALLENGE_NAME }}" + challenges.ctfpilot.com/category: "{{ CHALLENGE_CATEGORY }}" + instanced.challenges.ctfpilot.com/deployment: "{{ deployment_id }}" + instanced.challenges.ctfpilot.com/owner: "{{ owner_id }}" + ctfpilot.com/component: "instanced-challenge" + annotations: + janitor/expires: "{{ expires }}" + traefik.ingress.kubernetes.io/router.priority: "100" +spec: + entryPoints: + - websecure + routes: + - match: HostSNI(`{{ deployment_id }}.{{ domain }}`) + services: + - name: "ctf-{{ deployment_id }}" + port: 8080 + tls: + secretName: kubectf-cert-challs diff --git a/challenges/misc/a-true-connection/version b/challenges/misc/a-true-connection/version new file mode 100644 index 0000000..e440e5c --- /dev/null +++ b/challenges/misc/a-true-connection/version @@ -0,0 +1 @@ +3 \ No newline at end of file