Skip to content

Commit 84c69e5

Browse files
committed
Update documentation
1 parent d2753f1 commit 84c69e5

File tree

11 files changed

+56
-149
lines changed

11 files changed

+56
-149
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ with `sysctl`. Some programs (e.g., `docker`) will need to be restarted after
7676
adjusting the kernel parameter.
7777

7878
- *CMGR\_ENABLE\_DISK\_QUOTAS*: enables the [disk
79-
quota](examples/markdown_challenges.md#challenge-options) container option when set. Disk quotas
79+
quota](examples/specification.md#challenge-options) container option when set. Disk quotas
8080
are only functional when using the `overlay2` Docker storage driver and
8181
[pquota-enabled](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/storage_administration_guide/xfsquota)
8282
XFS backing storage. Otherwise, the creation of containers with disk quotas will fail at runtime.

examples/README.md

Lines changed: 21 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,128 +1,49 @@
11
# Challenges
22

3-
## Supported Specification Formats
3+
## problem.md
44

5-
As an evolution on the original `hacksport` library, we provide two formats for specifying a challenge: JSON and Markdown.
6-
7-
### problem.json
8-
9-
We attempt to support backwards compatability with the previous `hacksport` JSON format and will use the compatability mode for any `problem.json` file that has a `challenge.py` file in the same directory. However, these challenge definitions are unable to take full advantage of new challenge types and metadata control. So it is preferred to use the newer format if you want to use JSON for specifying the metadata.
10-
11-
### problem.md
12-
13-
In an effort to make it easier to develop content, we've implemented a Markdown specification that imposes some restraints on the document layout but should be far easier to read and maintain than the JSON format. For a detailed breakdown of the new format, see the [specification](markdown_challenges.md).
14-
15-
## Schemas
16-
17-
"Schemas" are a mechanism for declaratively specifying the desired state for a set of builds and instances. Builds and the associated instances that are created by a schema are locked out from manual control and should be the preferred way to manage a large number of builds and instances for events. However, they are still event agnostic and can be used for managing other groupings of resources as appropriate. Two equivalent schema specifications can be found [here](schemas/). It is worth noting that a `-1` for instance count specifies that instances are still manually controlled and allows the CLI or `cmgrd` to dynamically increase or decrease the number of running instances (useful for mapping instances uniquely to end-users without having a large number of unused containers).
5+
This file specifies a challenge's metadata (name, description, etc.) For a detailed breakdown of the format, see the [specification](specification.md).
186

197
## Supported Challenge Types
208

21-
One of the primary goals of _cmgr_ is to make it easier to implement new challenge types. One notable lack of support right now, however, is the inability to mount and manipulate block devices. This is currently a limitation of the underlying container system (i.e. Docker and containerd). Unless otherwise specified, challenge types that expose a port have the associated service running as a non-root user inside the container who does not have permission to read `/challenge` (and hence a known location of the flag).
22-
23-
As a general rule, all challenge types (except "custom" and "hacksport") support two mechanisms for installing dependencies. First, they will look for a `packages.txt` file in the challenge's root directory. This file should have a single "apt" package per line (versioning not supported) and will be installed before any other code is run. Additionally, challenges that require/support Python (e.g. "flask") will also look for the standard `requirements.txt` file and install those dependencies with "pip".
24-
25-
If you are interested in the underlying mechanics for a challenge type, all of the associated Dockerfiles can be found [here](../cmgr/dockerfiles/).
26-
279
### custom
2810

29-
This is by far the most flexible of the challenge types, but also the one with the least amount of structured support. In addition to the challenge specification file (e.g. `problem.md`), the challenge author must supply a complete Dockerfile. The Dockerfile will be supplied with three build arguments when it is first invoked: `FLAG`, `SEED`, and `FLAG_FORMAT`.
11+
In this challenge type, the author must supply a complete Dockerfile. The Dockerfile will be supplied with three build arguments when the challenge is built: `FLAG`, `SEED`, and `FLAG_FORMAT`.
3012

31-
The Dockerfile is responsible for using these inputs to build the templated challenge and format the image appropriately for _cmgr_ to retrieve the artifacts and build metadata. In particular, any artifacts competitors should see **must** be in a GZIP-ed tar archive located at `/challenge/artifacts.tar.gz`. Additionally, there **must** be a `/challenge/metadata.json` file that has a field for the flag (named `flag`) as well as any other lookup values the challenge references in its details and hints. Finally, if the Dockerfile expects any ports to be exposed directly to end-users, then there must be a comment line of the form `# PUBLISH <port number> AS <port name>` in the Dockerfile.
13+
The Dockerfile is responsible for using these inputs to build the templated challenge and format the image appropriately for _cmgr_ to retrieve the artifacts and build metadata. In particular, any artifacts competitors should see **must** be in a GZIP-ed tar archive located at `/challenge/artifacts.tar.gz`. Additionally, there **must** be a `/challenge/metadata.json` file that has a field for the flag (named `flag`) as well as any other lookup values the challenge references in its details and hints. These files must be present in either the final build stage, or in a stage explicitly named `builder`.
3214

3315
You can find an example [here](custom/). The ["multi"](multi/) challenge example demonstrates the full range of customization you can leverage by demonstrating multi-container challenges and custom per-build lookup values.
3416

35-
#### Added Behavior for Docker files
36-
37-
In order to make challenge types as reusable as possible, `cmgr` adds some
38-
additional concepts to a normal `Dockerfile` that need to be considered when
39-
building the `Dockerfile` for a custom challenge.
40-
41-
##### Build stage named 'builder'
17+
#### Publishing ports
4218

43-
If any stage in the Dockerfile is labeled `builder`, then that staged
44-
(rather thand the last stage) must contain the `/challenge/metadata.json`
45-
and, if applicable, `/challenge/artifacts.tar.gz` files.
19+
Docker has a distinction between "exposed" ports and "published" ports. `cmgr` detects which exposed
20+
ports should be published by requiring a comment of the form `# PUBLISH {port} AS {name}` (case
21+
sensitive) to occur in the Dockerfile after `EXPOSE` directives. This allows challenge authors
22+
to bring in base images that already expose ports in Docker (e.g., the PostgreSQL image) without
23+
neccessarily exposing those ports to competitors.
4624

47-
##### Publishing ports
48-
49-
Docker has a distinction between "exposed" ports and "published" ports. To
50-
avoid repetitive boilerplate in `problem.md` files, `cmgr` detects which
51-
exposed ports should be published by requiring a comment of the form
52-
`# PUBLISH {port} AS {name}` (case sensitive) to occur in the Dockerfile after
53-
the `EXPOSE` directive. This allows challenge authors to bring in base images
54-
that already expose ports in Docker (e.g., the PostgreSQL image) without
55-
requiring that the port be directly exposed to the competitor.
56-
57-
##### Launching more than one container
25+
#### Launching more than one container
5826

5927
In order to support challenges that launch multiple containers for a
6028
challenge, `cmgr` introduces a comment of the form `# LAUNCH {build_stage} ...`
6129
which will launch an instance of each listed stage with the stage name as
62-
its Docker DNS name and place them on their own internal network. For a
63-
specific example of this, see the [multi example](../multi). When using
64-
multiple containers, it is important that each `PUBLISH` directive (above)
30+
its Docker DNS name and place them on the same overlay network. For a
31+
specific example of this, see the [multi example](./multi). When using
32+
multiple containers, it is important that each `# PUBLISH` comment (described above)
6533
appears in the same build stage as the `EXPOSE` directive it is referencing.
6634

67-
### flask
68-
69-
This is a simple wrapper around the "flask" web framework designed to require minimal adjustment from standard practice for challenge authors to create new content. An example with a more detailed readme can be found [here](flask/).
70-
71-
### hacksport
72-
73-
This is a shim around the legacy `hacksport` framework. It should "just work" for those challenges, but is also likely to be fragile on more complicated ones. In particular, the "docker" challenges are not supported (but should be easily portable to a new "custom" one) and calls to "mount" are not supported.
74-
75-
### node
76-
77-
This is a simple wrapper around the _Node.js_ framework using their published LTS base image. An example with more details can be found [here](node/). Additionally, these challenges do not need to use _Node.js_ to run the server itself. An example of using Puppeteer (uses _Node_) to enable XSS challenges with a Flask backend can be found [here](puppeteer/).
78-
79-
### php
80-
81-
This is a simple framework for launching a web server/application built using PHP. An example with more details can be found [here](php/).
82-
8335
### Compiled Challenges
8436

85-
Many challenges require compilation as part of their build. To allow maximum flexibility while trying to minimize outside requirements on the build process, we have two different "drivers" for compiled challenges as well as three different styles for how competitors will interact with them.
86-
87-
#### make
88-
89-
The make "driver" is the simplest of the drivers. The build process will call `make main`, `make artifacts.tar.gz`, and `make metadata.json` in that order to build the challenge and necessary components. Additionally, challenges with a network component will have `make run` called to start as the entrypoint.
37+
In the `remote-make` and `static-make` challenge types, the build process will call `make main`, `make artifacts.tar.gz`, and `make metadata.json` in that order to build the challenge and necessary components. Additionally, challenges with a network component will have `make run` called to start as the entrypoint.
9038

91-
#### pybuild
39+
#### remote-make
9240

93-
The `pybuild` driver is an attempt to provide the power of templating (similar to what was available in `hacksport`) while still getting out of the way for the most part. It allows authors to hook the build process at various points by creating a file named `build.py` which defines a class named `Builder`. This class will then be referenced during the build process for the challenge.
41+
The `remote-make` challenge type will take a program that uses stdin/stdout to communicate and connect it to a port so that every new TCP connection gets forked into a new process with stdin/stdout piped to the network.
9442

95-
Probably the most powerful part of this driver is the use of `jinja2` to template the challenge directory into the build image. In particular, any attribute of the `Builder` is directly available for templating. If there are files that should not be templated, you can specify them as a list of strings (full filepath from the challenge root to the file) assigned to `self.dont_template`. Additionally, you can specify files to remove after executing the build (`self.remove`) as well as specify compiler flags for the default target.
43+
#### static-make
9644

97-
There are three functions that can be used to manipulate the build process: `prebuild(self)`, `build(self)`, and `postbuild(self)`. They run in that order and all are completely optional (if `build` is omitted the build step defaults to shelling out to `make {{program_name}}`). Of particular note, `prebuild` is called after `self.flag` and `self.flag_format` have been populated and `random` has been seeded with the appropriate seed for the build. In contrast, `postbuild` is called after `artifacts.tar.gz` has been assembled but before `metadata.json` has been created.
45+
The `static-make` challenge type has no network component and should be solvable solely by using the `artifacts.tar.gz` and `metadata.json` created during the build process.
9846

99-
Pre-defined attributes (all over-ridable)
100-
- `flag`: the auto-generated flag for the problem
101-
- `flag_format`: the requested format for what the flag should look like
102-
- `x86_64`: a boolean indicating whether this should be a 64-bit build (true, and the default) or a 32-bit build.
103-
- `executable_stack`: a boolean indicating whether the stack is executable (default is false)
104-
- `stack_guards`: a boolean indicating whether compiler-injected stack-guards should be used (default is true)
105-
- `strip`: a boolean indicating whether the final binary should be stripped (default is false)
106-
- `debug`: a boolean indicating whether DWARF information should be included with the final binary (default is false)
107-
- `pie`: a boolean indicating whether the final binary should be built as a position-independent executable (default is false)
108-
- `extra_flags`: a list of strings which will be appended to the `CFLAGS`, `CXXFLAGS`, and `ASFLAGS` environment variables (and hence override auto-generated flags)
109-
- `dont_template`: a list of filepaths to skip when applying the templating logic
110-
- `program_name`: **REQUIRED if "build" function not defined**: specifies the name of the binary to build. By default, it will try to use make's implicit build rules to build it by calling `make {{program_name}}` (defaults to "main").
111-
- `exec`: (remote/service only) the command that should be used as the entrypoint for the server (defaults to `./{{program_name}})`
112-
- `artifacts`: The list of files (after "build" step) that should be packaged into `artifacts.tar.gz` (defaults to an empty list)
113-
- `lookups`: a dictionary of string key-value pairs which will be made available to the front-end's templating engine
114-
- `remove`: a list of files to remove prior to starting the server (useful for removing sensitive build files from the build directory)
115-
116-
For debugging purposes, the Python script used to drive this logic is available [here](../support/pybuild.py).
117-
118-
#### remote
119-
120-
The "remote" set of challenge types will take a program that uses stdin/stdout to communicate and connect it to a port so that every new TCP connection gets forked into a new process with stdin/stdout piped to the network.
121-
122-
#### service
123-
124-
The "service" set of challenge types will start the program exactly once and expect it to accept and handle TCP connections completely on its own. The program is expected to read the "PORT" environment variable to ensure it listens on the correct port (but should be safe to hardcode as 5000).
125-
126-
#### static
47+
## Schemas
12748

128-
The "static" set of challenge types have no network component and should be solvable solely by using the `artifacts.tar.gz` and `metadata.json` created during the build process.
49+
"Schemas" are a mechanism for declaratively specifying the desired state for a set of builds and instances. Builds and the associated instances that are created by a schema are locked out from manual control and should be the preferred way to manage a large number of builds and instances for events. However, they are still event agnostic and can be used for managing other groupings of resources as appropriate. An example schema can be found [here](./schema.yaml). It is worth noting that a `-1` for instance count specifies that instances are manually controlled and allows the CLI or `cmgrd` to dynamically increase or decrease the number of running instances (useful for mapping instances uniquely to end-users without having a large number of unused containers).

examples/custom/problem.json

Lines changed: 0 additions & 19 deletions
This file was deleted.

examples/custom/problem.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# custom-socat
2+
3+
- Namespace: cmgr/examples
4+
- Type: custom
5+
- Category: Cryptography
6+
- Points: 50
7+
8+
## Description
9+
10+
We found a file that appears to be encrypted.
11+
12+
## Details
13+
14+
The file is {{url_for('secret.enc', 'here')}}, and someone told us the decryption instructions can be obtained with `nc {{server}} {{port}}`.
15+
16+
## Hints
17+
18+
- It's probably not helpful, but it looks like the build time can be found {{url_for('time.txt', 'here')}}.
19+
20+
## Tags
21+
22+
- crypto
23+
- custom
24+
- example
25+
26+
## Attributes
27+
28+
- organization: ACI
29+
- event: Sample

examples/disks/problem.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
- Type: static-make
55
- Category: Miscellaneous
66
- Points: 150
7-
- Templatable: Yes
8-
- MaxUsers: 0
97

108
## Description
119

examples/multi/problem.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
- Type: custom
55
- Category: Privilege Escalation
66
- Points: 25
7-
- Templatable: yes
8-
- MaxUsers: 1
97

108
## Description
119

examples/remote-make/problem.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
- Type: remote-make
55
- Category: Binary Exploitation
66
- Points: 50
7-
- Templatable: yes
8-
- MaxUsers: 0
97

108
## Description
119

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
name: yaml-example
1+
name: example
22
flag_format: example{%s}
33
challenges:
4-
cmgr/examples/flask--sqlite:
4+
cmgr/examples/custom-socat:
55
seeds: [ 1, 5, 101 ]
66
instance_count: 1
7-
cmgr/examples/php-sqlite:
7+
cmgr/examples/aptitude-and-privileges:
88
seeds: [ 19, 42, 15412 ]
99
instance_count: -1

examples/schemas/basic.json

Lines changed: 0 additions & 14 deletions
This file was deleted.
Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
# Markdown Challenge Specification
1+
# Challenge Specification
22

33
- Namespace: cmgr/examples
4-
- Type: flask
4+
- Type: custom
55
- Category: example
66
- Points: 1
7-
- Templatable: yes
8-
- MaxUsers: 0
97

108
## Description
119

0 commit comments

Comments
 (0)