r8 API Reference¶
-
class
r8.
Challenge
(cid: str)¶ Challenge Display
-
title
: str = "Hello World"¶ The challenge name visible to the user.
Tags for the challenge. This can be used to signal task category of difficulty.
-
flag
: str = "__flag__{...}"¶ If set, a static flag with the given value will be created on startup.
-
points
: Optional[int] = None¶ Number of (hardcoded) points awarded for this challenge. If unset, points are automatically adjusted by the number of solves.
-
await
description
(user: str, solved: bool) → str¶ Challenge description visible to the user. Supports full HTML. There is no additional security layer, XSS is entirely possible.
-
await
visible
(user: str) → bool¶ Determine if the challenge is visible for a given user. Defaults to True.
Challenge Lifecycle
-
active
: bool¶ True if the challenge is currently active, False otherwise (read-only).
-
args
: str¶ The raw string passed to the challenge as an argument between parentheses. For example, given a cid of “Challenge(foo bar)”, this would be “foo bar”.
-
await
start
() → None¶ Called when the challenge is started, can be used to start additional services for example.
Note that challenge instances are always started immediately when running r8, independent of when the challenge will be active. This makes sure that there are no surprising startup errors.
-
await
stop
() → None¶ Called when the challenge is stopped.
Note that challenge instances will not be stopped on the challenge deadline, only flag generation and submission will be halted. This allows in-class demonstrations after the deadline.
Logging and Flag Creation
-
echo
(message: str, err: bool = False) → None¶ Print to console with the challenge’s namespace added in front.
-
log
(ip: THasIP, type: str, data: Optional[str] = None, *, uid: Optional[str] = None) → None¶ Log an event for the current challenge. See
r8.log()
.
-
log_and_create_flag
(ip: THasIP, user: Optional[str] = None, *, max_submissions: int = 1, flag: Optional[str] = None, challenge: Optional[str] = None) → str¶ Create a new flag that can be redeemed for this challenge and log its creation.
If the challenge is currently inactive, __flag__{challenge inactive} will be returned instead.
If flag creation should not be logged (e.g. because it’s done by the challenge automatically on startup), use
r8.util.create_flag()
directly.- Parameters
ip – IP address which caused this flag to be created. Used for logging only.
user – User who caused this flag to be created. Used for logging only.
challenge – If given, override the challenge for which this flag is valid.
HTTP API
Challenges can expose an HTTP API. This is for example used to serve static files (such as challenge icons) that accompany the challenge.
-
static_dir
= "<challenge file directory>/static"¶ Directory that includes static files for the challenge. Will be served from
handle_get_request()
.
-
api_url
(path: str, absolute: bool = False, user: Optional[str] = None) → str¶ Construct a URL pointing to this challenge’s API.
- Parameters
path – The request path relative to the API endpoint.
absolute – If True, an absolute URL is constructed.
user – If given, an authentication token will be included in the URL, making it possible to access the resource without additional authentication.
-
await
handle_get_request
(user: str, request: aiohttp.web_request.Request) → Union[str, aiohttp.web_response.StreamResponse]¶ HTTP GET requests to /api/challenges/cid/* land here. Serves static resources from
static_dir
by default.The request path can be accessed using request.match_info[“path”].
-
await
handle_post_request
(user: str, request: aiohttp.web_request.Request) → Union[str, aiohttp.web_response.StreamResponse]¶ HTTP POST requests to /api/challenges/cid/* land here. Serves 404s by default.
The request path can be accessed using request.match_info[“path”].
Key-Value Storage
Challenges can store additional data in a persistent key value JSON storage in the database.
-
get_data
(key: str, *, cid: Optional[str] = None) → Any¶ Get persistent challenge data for a specific key.
- Parameters
cid – If given, override the challenge for which data should be accessed.
-
set_data
(key: str, value: Any, *, cid: Optional[str] = None)¶ Set persistent challenge data for a specific key.
- Parameters
cid – If given, override the challenge for which data should be modified.
-
Utilities¶
-
r8.util.
get_team
(user: str) → Optional[str]¶ Get a given user’s team.
-
r8.util.
has_solved
(user: str, challenge: str) → bool¶ Check if a user has solved a challenge.
Challenge Description Helpers¶
-
r8.util.
media
(src: Optional[str], desc: str, visible: bool = True)¶ HTML boilerplate for a bootstrap media element. Commonly used to display challenge icons.
- Parameters
src – Path to image.
desc – Media body.
visible – If False, a generic challenge icon will be shown instead.
-
r8.util.
spoiler
(help_text: str, button_text='🕵️ Show Hint') → str¶ HTML boilerplate for spoiler element in challenge descriptions.
-
r8.util.
challenge_form_js
(cid: str) → str¶ JS Boilerplate for simple interactive form submissions in the challenge description.
“Trigger” button for challenges. Clicking it invokes the challenge’s HTTP POST handler.
-
r8.util.
url_for
(path: str, absolute: bool = False, user: Optional[str] = None) → str¶ Construct a URL for the CTF System. If absolute is true, construct an absolute URL including the origin. If user is passed, add an authentication token to the URL.
-
r8.util.
get_host
() → str¶ Return the hostname of the CTF system.
TCP Server Challenge Helpers¶
-
r8.util.
connection_timeout
(f)¶ Decorator to timeout an asyncio TCP connection handler after 60 seconds.
-
r8.util.
tolerate_connection_error
(f)¶ Decorator to silently catch all ConnectionErrors for asyncio TCP connections.
-
r8.util.
format_address
(address: tuple) → str¶ Format an (ip, port) address tuple.
Low-Level Helpers¶
See also
For challenge development, it is recommended to use the equivalent methods
exposed by the challenge class instead:
r8.Challenge.echo()
, r8.Challenge.log()
and r8.Challenge.log_and_create_flag()
.
-
r8.
echo
(namespace: str, message: str, err: bool = False) → None¶ Print to console with a namespace added in front.
- Parameters
namespace – The message ‘category’, e.g. the challenge name.
message – The message.
err – If True, print to stderr.
For quick and dirty challenge development, it is completely okay to just print() instead.
-
r8.
log
(ip: THasIP, type: str, data: Optional[str] = None, *, cid: Optional[str] = None, uid: Optional[str] = None) → int¶ Create a log entry.
- Parameters
ip – IP address which caused this log entry to be created.
type – Event type, for example “submission attempt”
data – Additional event data, for example the actually submitted value.
cid – Challenge this log entry relates to.
uid – User this log entry relates to.
-
r8.util.
create_flag
(challenge: str, max_submissions: int = 1, flag: str = None) → str¶ Create a new flag for an existing challenge. When creating flags from challenges, see also
r8.Challenge.log_and_create_flag()
.- Parameters
challenge – Challenge for which the flag is valid.
max_submissions – Maximum number of times the flag can be redeemed.
flag – If given, use this as the flag string. Otherwise, generate random flag.
-
class
r8.util.
THasIP
¶ An object from which we can derive an IP address,. e.g. a web.Request, an asyncio.StreamWriter, a str or an (ip, port) tuple.