Skip to content

Writing a checker

Roman Nikitin edited this page Jan 23, 2021 · 1 revision

Checker is an app that checks whether the team's task is running normally, puts flags and then checks them after a few rounds.

Actions and arguments are passed to checker as command-line arguments, the first one is always command type, the second is team host.

Checker should terminate with one of the five return codes:

  • 101: OK code, everything works
  • 102: CORRUPT, service's working correctly, but didn't return flags from previous rounds (returned by GET only)
  • 103: MUMBLE, service's not working correctly
  • 104: DOWN, could not connect normally
  • 110: CHECKER_ERROR, unexpected error in checker

All other return codes are considered to be CHECKER_ERROR.

In case of unsuccessful invocation stdout output will be shown on scoreboard, stderr output is considered to be the debug info and is stored in database. Also, in case of CHECKER_ERROR celery container prints warning to console with detailed logs.

Checker must implement three main actions:


  • CHECK: checks that team's service is running normally. Visits some pages, checks registration, login, etc...

Example invocation: /checkers/task/check.py check 127.0.0.1


  • PUT: puts a flag to the team's service.

Example invocation: /checkers/task/check.py put 127.0.0.1 <flag_id> <flag> <vuln_number>

If the checker returns flag_id (see checker config), it should write some data which helps to access flag later (username, password, etc) to stdout (that data will be the flag_id passed to GET action). Otherwise, it ought to use flag_id as some "seed" to generate such data (on the next invocation flag_id will be the same if checker_type is set to hackerdom_nfr).

PUT is run even if CHECK failed


  • GET: fetches one random old flag from last flag_lifetime rounds.

Example invocation: /checkers/task/check.py get 127.0.0.1 <flag_id> <flag> <vuln_number>

This action should check if the flag can be acquired correctly.

GET is not run if CHECK (or previous GETs if gets > 1) fail.

A simple example of checker can be found here.


Be aware that to test task locally, LAN IP (not 127.0.0.1) needs to be specified for the team.

See this link to read more about writing checkers for Hackerdom checksystem. Vulns' frequencies (e.g. put 1 flag for the first vuln for each 3 flags of the second) are not supported (and won't be), but can be easily emulated with task place count and checker. For example, for the above configuration (1:3) specify 4 places for the task, and then in checker PUT flag to the first vuln if the supplied vuln is 1 and to the second vuln otherwise (vuln is 2, 3 or 4):

# How to store 3 times more flags for the second vuln
if int(vuln) == 1:  # first vuln
    pass
elif int(vuln) in (2, 3, 4):  # second vuln
    pass

Local files

If checker is using local files, it should access them by the relative to its directory path, as checkers are run using absolute paths inside Docker. For example, the following is incorrect: open('local_file.txt') as in Docker it'll try to open /local_file.txt instead of /checkers/task/local_file.txt. Correct usage would be

from pathlib import Path

BASE_DIR = Path(__file__).absolute().resolve().parent
local_path = BASE_DIR / 'local_file.txt'

Modifying checker container

As checkers run in celery container, open docker_config/celery/Dockerfile.fast (or docker_config/celery/Dockerfile if you're not using the fast build) and install all necessary packages to the image. Any modification can be made in CUSTOMIZE block. With enough confidence, even the base image of celery container could be changed (python3.7 needs to be installed anyway).

Clone this wiki locally