The checker protocol is the HTTP-based protocol used by the engine to communicate with the checkers.
The checker is expected to respond with an HTTP status code 200 to the following requests:
Response:
interface CheckerInfoMessage {
serviceName: string;
flagVariants: number;
noiseVariants: number;
havocVariants: number;
exploitVariants: number;
}
The name of the service as reported by the checker.
The number of different flag variants supported and requested by the checker/service. Each variant MUST correspond to a different location in the service where the flag is stored (flag store).
The number of different noise variants supported and request by the checker/service. Different variants do not necessarily need to store the noise in different locations. Unlike a flag, the noise does not need to remain secret and is only used during a getnoise
to check whether a noise is still present in the service, even if it is stored in a publicly accessible location.
The number of different havoc variants supported and requested by the checker/service. Each havoc is an independent task, meaning there is no concept of two or more related tasks like a putflag
or getflag
.
A havoc can be used to test any functionality in the service which is not covered by putflag
/getflag
and putnoise
/getnoise
. While a havoc MAY use information from other tasks, it MUST NOT fail if such information is missing from either the checker database or the service. For example, it might try to reuse an account created during putflag
, but is NOT allowed to fail when this account is missing, unlike the corresponding getflag
, which is expected to fail when it can no longer login. Note that havoc
may run at any time during a round, even before putflag
or putnoise
.
The number of different exploits. This MUST be greater than or equal to the number of flagVariants
.
Parameter:
interface CheckerTaskMessage {
taskId: number;
method: "putflag" | "getflag" | "putnoise" | "getnoise" | "havoc" | "exploit";
address: string;
teamId: number;
teamName: string;
currentRoundId: number;
relatedRoundId: number;
flag?: string | null;
variantId: number;
timeout: number;
roundLength: number;
taskChainId: string;
flagRegex?: string | null;
flagHash?: string | null;
attackInfo?: string | null;
}
Response:
interface CheckerResultMessage {
result: "INTERNAL_ERROR" | "OK" | "MUMBLE" | "OFFLINE";
message: string | null;
attackInfo?: string | null;
flag?: string | null;
}
This ID MUST be unique for each task during a CTF. It is the responsibility of the caller (usually the Engine) to ensure this criterion is met.
The method to be executed in this task.
putflag
:
- A
putflag
MUST store the flag (see the flag parameter below) in the service in such a way that it can be retrieved by agetflag
at a later point in time from a correctly working service. - The flag MUST be stored in a "secret" location in the service, meaning either some form of credentials only known by the checker or an exploit are required to get the flag.
- The
putflag
MUST store any credentials which are required by thegetflag
to retrieve the flag (e.g. username and password used for registering an account) in the checker's database. - The
putflag
MUST use thetaskChainId
(see below) as identifier in the database to store any credentials required by a latergetflag
, as this is guaranteed to be identical for any subsequentgetflag
requests for the same flag. ThetaskChainId
is also guaranteed to be different for tasks in the same round for different teams and tasks for the same team in different rounds. - The caller MUST NOT call
putflag
with the sameflag
ortaskChainId
twice during a CTF. (Note that supporting such behavior in the checker might still be useful for testing purposes.) - A
putflag
MAY return a value forattackInfo
providing some identifying information the attackers may use to identify where the flag is stored in the service, such as a username in a service that doesn't provide other user enumeration capabilities.
getflag
:
- A
getflag
request MUST check whether the flag (see the flag parameter below) placed during the previousputflag
with the sametaskChainId
is still present in the service. - A checker MUST NOT return
"OK"
(see CheckerResultMessage below) unless the flag was found in the service. It MUST return"MUMBLE"
if the flag was not found or"OFFLINE"
if the service could not be reached. - A checker MUST use the
taskChainId
(see below) as identifier to retrieve any credentials stored by the relatedputflag
, as this value is guaranteed to be identical to thetaskChainId
during theputflag
. - A
getflag
MUST NOT return an"INTERNAL_ERROR"
(see CheckerResultMessage below), even if the correspondingputflag
did not return"OK"
. It could be that the flag was stored successfully but theputflag
did not return"OK"
due to some other reason, for example because it timed out before any subsequent actions were completed. - If required credentials, which should have been stored during the
putflag
, are missing from the database, the checker MUST return"MUMBLE"
(see CheckerResultMessage below) immediately without performing any subsequent checks. - A checker MUST be able to handle multiple subsequent
getflag
calls for the sameputflag
/taskChainId
.
putnoise
:
- A
putnoise
MUST store some noise generated by the checker to the service in such a way that it can be retrievd by a subsequentputnoise
with the sametaskChainId
at a later point in time from a correctly working service. - A checker MUST generate sufficiently unique noise to avoid false positives. Checker implementations might either generate something randomly and store it in the database using the
taskChainId
as identifier, or use thetaskChainId
as a seed to feed a deterministic RNG to be able to generate the same noise during a subsequentgetnoise
. - The noise MAY be stored in a "public" location in the service, meaning it might be visible to all normal users of the service without any secret credentials or an exploit.
- THe noise MUST NOT be stored in a location in the service where other teams can legally remove it, i.e., without an exploit.
- The checker MUST use the
taskChainId
as the identifier in case any credentials or the noise itself need to be stored in the database for a subsequentgetnoise
.
getnoise
:
- A
getnoise
MUST check whether the noise placed during a previousputnoise
is still present in the service. - The checker MUST NOT return
"OK"
unless the noise was found in the service. - If required credentials, which sould have been stored during the
putnoise
, are missing from the database, the checker MUST return"MUMBLE"
immediately without performing any subsequent checks. - A checker MUST be able to handle multiple subsequent
getnoise
calls for the sameputnoise
/taskChainId
.
havoc
:
- A
havoc
MUST NOT depend on any tasks from previous rounds or the same round, including credentials that should have been created during one of these task. - A
havoc
MAY use e.g. credentials from other tasks, provided it does not fail if these credentials are missing.
exploit
:
- The
exploit
method is only used during testing and MUST NOT be called during a CTF. - Each flag store MUST be exploitable by at least one
exploit
variant. - A flag store MAY be exploitable by more than one
exploit
variant. - An
exploit
variant MAY be able to retrieve flags from multiple flag stores. - Each flag store SHOULD have an
exploit
variant that is only able to retrieve flags from that flag store. - The
exploit
method checks whether it is able to retrieve a flag matching theflagHash
. If yes, it MUST returnOK
and the found flag as part of the result. If not, it MUST returnMUMBLE
. It is the caller's responsibility to ensure the flag is stored in the expected flag store. - An
exploit
MAY need to be connected to the service beforeputflag
is called. In that case it is the caller's responsibility to ensureputflag
is called while theexploit
method is running.
General requirements:
- A checker MUST NOT make any assumptions about the order of the execution of any tasks, except the following:
- a
getflag
with the sametaskChainId
as the relatedputflag
is always executed after theputflag
is completed - a
getnoise
with the sametaskChainId
as the releatedputnoise
is always executed after theputnoise
is completed
- a
The address of the target team's vulnbox. Can be either an IP address or a valid hostname.
The id of the target team. Primarily used for logging purposes.
The name of the target team. Primarily used for logging purposes.
The ID of the current round.
When the method is getflag
or getnoise
, the related round ID refers to the round in which the putflag
or putnoise
with the same taskChainId
was executed. For all other methods, it is identical to the currentRoundId
.
A checker MUST NOT use the relatedRoundId
as the identifier to store and retrieve data for later rounds or from previous rounds for related putflag
/getflag
or putnoise
/getnoise
tasks, instead it MUST use the taskChainId
as the identifier.
For putflag
and getflag
, this is the flag which is supposed to be stored or retrieved by the task. For all other methods, the flag is not set or null
.
The variant id for the method, used to support different flag, noise and havoc methods. This id will be a value between 0 and flagVariants - 1
(see CheckerInfoMessage above) for putflag
/getflag
, the same for noise and havoc with noiseVariants
and havocVariants
respectively.
A checker MUST return an "INTERNAL_ERROR"
when called with a variantId
it does not support. (This is required for testing automatically whether the specified number of variants matches the implementations in the methods.)
The timeout for the task in milliseconds. If a checker does not return a response within the timeout, the caller assumes that the result is "OFFLINE"
.
A checker or checker library SHOULD enforce the timeout internally to avoid consuming computational resources after the caller has cancelled the request.
The length of a round in milliseconds.
The unique identifier of a chain of tasks (i.e. putflag
and getflag
s or putnoise
and getnoise
for the same flag/noise share an Id, each havoc has its own Id). It is up to the caller to ensure the aforementioned criteria are met, the Engine achieves this by composing it the following way: "{flag|noise|havoc}_s{serviceId}_r{relatedRoundId}_t{teamId}_i{uniqueVariantIndex}"
.
The taskChainId
MUST be used as the identifier in the database in case e.g. credentials created during putflag
need to be stored and are required in a subsequent getflag
.
A checker MUST support being called multiple times with the same method
, serviceId
, roundId
, teamId
and variantId
, in which case the uniqueVariantIndex
can be used to distinguish the taskChains.
For the exploit
method, this is a regular expression that the flag to be exploited must match. For all other methods, the flagRegex
is not set or null
.
For the exploit
method, this is the hex-encoded SHA256 hash of the flag to be searched by the exploit. For all other methods, the flagHash
is not set or null
.
For the exploit
method, this is the attackInfo
that was returned by the putflag
method for the flag to be found, if any was returned, and not set or null
otherwise. For all other methods, the flagHash
is not set or null
.
The result of the task. For the purpose of scoring, there is no difference between "MUMBLE"
and "OFFLINE"
and the distinction is only made for informational purposes.
In general, "OFFLINE"
should be returned when an issue with the connection to the service occurs, "MUMBLE"
should be returned in all other failure cases, for example when the service sends an unexpected response. An "INTERNAL_ERROR"
MUST NOT be returned when the checker is called within the specification, most notably when all of the aforementioned criteria are met.
When the service behaves as expected, the checker MUST return "OK"
.
A message describing the error that occured, this is displayed on the public scoreboard if it is not null.
The message MUST be null
when the result
is "OK"
.
The message SHOULD provide additional information on failure cases.
The checker MUST NOT include internal details in this message, most notably it MUST NOT include the flag or other secret information.
When the result
is "INTERNAL_ERROR"
, the message MUST NOT be displayed publicly to participants and MAY contain secret information.
For results from putflag
, this is an arbitrary string that will be publicly displayed for each team and round if it is not null
. For all other methods, this field must be unset or null
.
It SHOULD provide attackers with otherwise unavailable information required to mount an exploit retrieving this flag, such as a username.
For results from exploit
, if the result is "OK"
, this MUST be the flag matching the flagHash
. For other methods or when the result is not "OK"
, this must be unset or null
.