eCTF API¶
The eCTF team creates a web API to allow teams to interact with automated tooling and verify that their design meets the Functional Requirements.
This year the interface for the testing service will be through a web API. As part of the eCTF public tools the organizers have provided a python utility which includes a client for interacting with the testing server. This page is for those who want more details on how exactly the testing server works so they can, for example:
Have more familiarity with terms the organizers might use when discussing the testing infrastructure
Directly make http requests to the testing api (we will use
curlas an example) and understand the server’s responsesCreate their own automation for interacting with the testing server
This page assumes some basic familiarity with http and using a web API for information on these subjects please see here.
Tip
The terms Flows and Jobs are both used in this section and for clarity they will be explained.
Flows: These are a collection of testing jobs which represent running a full operation on the testing infrastructure. This could include testing a design, submitting a design, or running the remote scenario.
Jobs: These are the components of a flow which need to be run. They may be reused between flows (e.g. pull-repo will commonly be reused). Within a flow jobs are dependent on other jobs, and a job will only run if the jobs that it is dependent on succeed.
All teams will receive a private authorization token to identify themselves to the server. This token will be used in every request made to the server. If a team believes they have not received such a token they should reach out to the organizers.
General Endpoint Strucutre¶
All endpoints on the server will begin with /api.
The endpoints on the server are devided into 3 main groups.
These groups serve to logically devide up the functionality of endpoints
flag: This group of endpoints all involve a team submitting something to the organizers and potentially recieving a flag in return. These endpoints all have the form
/api/flag/...
Details |
Endpoint |
Method |
Functionality |
|---|---|---|---|
|
POST |
Submit a team photo to the organizers |
|
|
POST |
Submit a design doc to the organizers |
|
|
POST |
Submit a hash for the steal design scenario |
package: This group of endpoints are all related to getting attack packages. These endpoints all have the form
/api/package/...
Details |
Endpoint |
Method |
Functionality |
|---|---|---|---|
|
GET |
Get names of all teams with attack packages |
|
|
GET |
Get the encrypted attack package for given team |
flow: This group of endpoints has all the functionality relating to submitting, viewing the status, and viewing the output of flows. These endpoints all have the form
/api/flow/{flow_name}/...
Details |
Endpoint |
Method |
Functionality |
|---|---|---|---|
|
GET |
Check currently queued flows |
|
|
POST |
Add a flow to queue |
|
|
GET |
Get the results of all jobs for a flow |
|
|
POST |
Cancel the selected running flow |
|
|
GET |
Get the output of a specific job |
|
|
PATCH |
Give input to a job in the “Pending” state |
Submit Team Photo¶
This endpoint is used to submit your team’s photo to the Organizers and recieve the corresponding flag. The submitted image must be of png format.
Inputs:
Header |
Body |
|---|---|
|
|
The private team token given by Organizers |
A png file should be supplied with the post request |
Return Body on Success:
{ "flag_hex" : "str_flag_hex" }
Endpoint’s Return Codes:
Status Code |
Meaning |
|---|---|
|
Success, organizers have recieved team’s photo |
|
The authorization token given is invalid |
|
The submitted file is too large to process |
|
Validation error, either http body is misformed or file is not of type png |
|
Internal error, likely from an aws outage |
Example curl request:
curl --request POST 'https://api.ectf.mitre.org/api/flag/team_photo/' --header 'Authorization: Bearer example-token' -F "team_photo=@/path/to/your/image"
Submit Design Doc¶
This endpoint is used to submit your team’s design document to the Organizers and recieve the corresponding flag. The submitted file must be of pdf format.
Inputs:
Header |
Body |
|---|---|
|
|
The private team token given by Organizers |
A pdf file should be supplied with the post request |
Return Body on Success:
{ "flag_hex" : "str_flag_hex" }
Endpoint’s Return Codes:
Status Code |
Meaning |
|---|---|
|
Success, organizers have recieved team’s design document |
|
The authorization token given is invalid |
|
The submitted file is too large to process |
|
Validation error, either http body is misformed or file is not of type pdf |
|
Internal error, likely from an aws outage |
Example curl request:
curl --request POST 'https://api.ectf.mitre.org/api/flag/design_doc/' --header 'Authorization: Bearer example-token' -F "design_doc=@/path/to/your/image"
Steal Design Flag¶
Submit the hash printed from an HSM by the bootloader for the steal_design flag, only accessible to teams in the attack phase. Will only return a maximum of one flag per request.
Inputs:
Header |
Path |
Json Body |
|---|---|---|
|
|
|
The private team token given by Organizers |
The team who’s flag you wish to take should be supplied in the url path |
A simple string containging the hash you’d like to submit |
Return Body on Success:
{ "flag_hex" : "str_flag_hex" }
Endpoint’s Return Codes:
Status Code |
Meaning |
|---|---|
|
Success, a correct digest has been submitted |
|
Given digest is incorrect OR team being targetted is not in the attack phase |
|
The authorization token given is invalid |
|
This team is not in the attack phase |
|
Team being targetted does not exist |
|
Validation error, http body is misformed |
Example curl request:
curl --request POST 'https://api.ectf.mitre.org/api/flag/steal_design/mitre/' --header 'Authorization: Bearer example-token' --json "example_digest"
Package Names¶
Get the list of all teams who have attack packages stored on the server. Only accessible to teams in the attack phase.
Inputs:
None
Return Body on Success:
[ "mitre", "mitre2", "mitre3" ]
Endpoint’s Return Codes:
Status Code |
Meaning |
|---|---|
|
Success, a correct hash has been submitted |
|
The authorization token given is invalid |
|
This team is not in the attack phase |
Example curl request:
curl --request GET 'https://api.ectf.mitre.org/api/package' --header 'Authorization: Bearer example-token'
Get Package¶
Retrieve the encrypted zip for an attack package. Only accessible to teams in the attack phase.
Inputs:
Header |
Path |
|---|---|
|
|
The private team token given by Organizers |
The team name of the attack package you would like should be provided in the url path |
Return Body on Success:
**file content of encrypted attack package**
Endpoint’s Return Codes:
Status Code |
Meaning |
|---|---|
|
Success, a correct hash has been submitted |
|
The authorization token given is invalid |
|
This team is not in the attack phase |
|
Package name does not exist. |
Example curl request:
#/{package_name}
curl --request GET 'https://api.ectf.mitre.org/api/package/mitre' --header 'Authorization: Bearer example-token'
List All Flows¶
To list all the flows of a specifc type the team has queued use:
Inputs:
Header |
Body |
|---|---|
|
|
The private team token given by Organizers |
An optional query parameter (default 0 when not included) can be provided to select how many flows you’d like to see, 0 returns all flows. |
Return Body on Success:
List[Flow:Schema ]
Endpoint’s Return Codes:
Status Code |
Meaning |
|---|---|
|
Success, a correct hash has been submitted |
|
The authorization token given is invalid |
|
Validation Error, num given is less than 0 |
Example curl request:
curl --request GET 'https://api.ectf.mitre.org/api/flow/test/?num=0' --header 'Authorization: Bearer example-token'
Queue Flow¶
Queue a testing flow of the specified type to the testing server.
Inputs:
Header |
Json Body |
|---|---|
|
|
The private team token given by Organizers |
The parameters being used for the queued flow, has to be of the form above to specify the git repo and branch to use |
Return Body on Success:
UUID of the newly queued flow e.g:
"3fa85f64-5717-4562-b3fc-2c963f66afa6"
Endpoint’s Return Codes:
Status Code |
Meaning |
|---|---|
|
Success/Created, a flow has successfully been queued |
|
The authorization token given is invalid |
|
Conflict, This team already has a flow of this type queued |
|
Validation Error, json body input is misformed |
Example curl request:
curl --request POST 'https://api.ectf.mitre.org/api/test' --header 'Authorization: Bearer example-token' -d '{"git_url" : "example.git", "commit_hash" : "deadbeef"}'
Flow Details¶
Gives all the details from a specific flow, includes info on all the jobs contained within this flow
Inputs:
Header |
Path |
|---|---|
|
|
The private team token given by Organizers |
The desired flow’s flow uuid should be provided in the url path |
Return Body on Success:
This returns a Flow:Schema
Endpoint’s Return Codes:
Status Code |
Meaning |
|---|---|
|
Success, the flow’s details have been provided |
|
The authorization token given is invalid |
|
Not Found, The provided flow id does not lead to a valid flow |
|
Validation Error, the provided flow id was not in the form of a UUID |
# /{flow_id}
curl --request GET 'https://api.ectf.mitre.org/api/flow/test/3fa85f64-5717-4562-b3fc-2c963f66afa6/' --header 'Authorization: Bearer example-token'
Cancel Flow¶
Cancels the currently queued flow for the specified flow type
Inputs:
Header |
Path |
|---|---|
|
|
The private team token given by Organizers |
The cancelled flow’s flow uuid should be provided in the url path |
Return Body on Success:
None
Endpoint’s Return Codes:
Status Code |
Meaning |
|---|---|
|
Success, No content |
|
This flow already completed and can’t be cancelled |
|
The authorization token given is invalid |
|
Not Found, The provided flow id does not lead to a valid flow |
|
Validation Error, the provided flow id was not in the form of a UUID |
# /{flow_id}
curl --request POST 'https://api.ectf.mitre.org/api/flow/test/3fa85f64-5717-4562-b3fc-2c963f66afa6/cancel/' --header 'Authorization: Bearer example-token'
Job Result¶
Gives a zip file containing all the outputs of a job.
Inputs:
Header |
Path |
|---|---|
|
|
The private team token given by Organizers |
The job’s job uuid should be provided in the url path |
Return Body on Success:
**file content of job output zip**
Endpoint’s Return Codes:
Status Code |
Meaning |
|---|---|
|
Success, output has been provided |
|
This job has no output to pull |
|
The authorization token given is invalid |
|
Not Found, The provided job id does not lead to a valid job |
|
Validation Error, the provided job id was not in the form of a UUID |
# /{job_id}
curl --request GET 'https://api.ectf.mitre.org/api/flow/test/job/981b2de7-f03f-4780-9cd1-1b6a893a0221/' -o output.zip --header 'Authorization: Bearer example-token'
Pending Job¶
Provide input to a job currently in the “pending” state. This is for adding user input to a job, where the input the user submits might depend on the result and output of previous jobs. This is mainly used for the Submission process and the Remote Scenario.
Inputs:
Header |
Path |
Json Body |
|---|---|---|
|
|
|
The private team token given by Organizers |
The job’s job uuid should be provided in the url path |
An arbitrary dict of key value pairs used as input for the job |
Return Body on Success:
**file content of job output zip**
Endpoint’s Return Codes:
Status Code |
Meaning |
|---|---|
|
Success, No content |
|
The authorization token given is invalid |
|
Not Found, The provided job id does not lead to a valid job |
|
Validation Error, the provided job id was not in the form of a UUID |
# Note the PATCH method # /{job_id}
curl --request PATCH 'https://api.ectf.mitre.org/api/flow/test/job/a432199e-fb5b-589a-8bcf-cc3a8f3e0722/' --header 'Authorization: Bearer example-token' -d '{"prop_1" : "value_1", "prop_2" : "value_2"}'
As a final note the program used to build the web app also creates autogenerated documentation which can be found at https://api.ectf.mitre.org/docs/. That being said the organizers consider the rules pages to be the official source of documentation on the web API and make no guarantee about the quality or accuracy of the autogenerated documentation.
Flow:Schema¶
id: uuidsubmit_time: datetimename: stringcompleted: booleanparams: dict[str:str]jobs: list[Schema:Job]
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"submit_time": "2026-01-29T14:53:48.335Z",
"name": "test",
"completed": false,
"params": {
"git_url": "example.git",
"commit_hash": "deadbeef"
}
"jobs": [
{
"id": "981b2de7-f03f-4780-9cd1-1b6a893a0221",
"name": "jobA",
"status": "succeeded",
"has_artifacts": true,
"private": false,
},
{
"id": "2d7c01fd-76b6-4221-9c13-0342a2ed902d",
"name": "jobB",
"status": "failed",
"has_artifacts": false,
"private": false,
},
{
"id": "7e782e6c-2e35-4a3e-9702-1f46fed7cd2b",
"name": "jobC",
"status": "queued",
"has_artifacts": false,
"private": false,
}
]
}