Skip to content

Commit 4249df6

Browse files
docs: multi-distro install script and practice-run instructions
- Add root-level setup.sh that detects the distro, installs build deps, and installs a static qo binary with qo-check/qo-setup/qo-reset aliases. - Add GIST.md with step-by-step instructions for students: install, download the test archive, start the practice session, and run the test levels. - Document the pristine-staging workflow in the threat model and README.
1 parent 65fe822 commit 4249df6

5 files changed

Lines changed: 236 additions & 4 deletions

File tree

GIST.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Linux evaluation '26 — Setup & Test Instructions
2+
3+
> **Important:** run this on a Linux machine (Arch, Debian/Ubuntu, Fedora, OpenSUSE, Alpine all work). You need `sudo` access.
4+
5+
## 1. Install `qo`
6+
7+
Open a terminal and run:
8+
9+
```bash
10+
curl -fsSL https://raw.githubusercontent.com/Open-Source-Community/qo2/ctf-improvements/setup.sh | bash
11+
```
12+
13+
The script detects your distro, installs the required tools (Go, tar, gzip), builds `qo`, and installs it to `/usr/local/bin/qo`. Verify:
14+
15+
```bash
16+
qo --help
17+
```
18+
19+
## 2. Download the test archive
20+
21+
Get the practice archive from the drive:
22+
23+
**https://drive.google.com/drive/folders/1nhKUefmOJ8FNPSUXz4sXmCMmDvnXtsc3?usp=sharing**
24+
25+
Download `test.enc` and save it in your home folder.
26+
27+
## 3. Start the practice session
28+
29+
```bash
30+
cd ~
31+
sudo qo start -m test -i <YOUR_STUDENT_ID> -a ~/test.enc -p osc2026 -k testkey -d 90m
32+
```
33+
34+
- `-i` — your student ID (the one you registered with, e.g. `2023001`)
35+
- `-a` — path to `test.enc`
36+
- `-p` — password (given above)
37+
- `-k` — starter key (given above)
38+
- `-d` — session duration in minutes
39+
40+
You will land inside the sandbox shell (`ahmed@sandbox:~$`).
41+
42+
## 4. Inside the sandbox
43+
44+
```bash
45+
qo-setup # create all 10 test levels
46+
cd ~/challenges/test1
47+
bash tools-test.sh # sanity check — expect: Total tests passed: 43/43
48+
cat README.md # read the task
49+
# solve the task, then:
50+
./check.sh # get your flag for this level
51+
```
52+
53+
Repeat for the other levels (`test2``test10`) — each has its own README.
54+
55+
Useful commands inside the sandbox:
56+
57+
```bash
58+
qo-setup test2 # set up one level
59+
qo-reset test2 # reset a level to its original files
60+
ls ~/challenges # list all levels
61+
```
62+
63+
## 5. Leaderboard
64+
65+
Live rankings will be shown at: **https://<leaderboard-url>/** _(placeholder — link will be announced)_
66+
67+
During practice, your test submissions appear on the admin dashboard under the **Test run** section, so the organizers can see you are comfortable with the tool before the real event.
68+
69+
## 6. Tips
70+
71+
- `exit` — leave the sandbox (your session ends).
72+
- The real event will use a different archive and password, announced later.
73+
- If anything is broken or a tool is missing in the sandbox, tell the organizers — this practice round exists to catch those issues.

README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,28 @@ diagnostic output — exactly like the historical tool. On success the parent
143143
replaces the hardcoded `key="..."` with a per-student flag derived via HMAC over
144144
`(base_flag, student_id)`, so every student gets their own flag.
145145

146+
### Working in the sandbox (home-based challenges)
147+
148+
Students never touch `/tmp`. On first login they set up their challenge folders
149+
from the root-only pristine staging area:
150+
151+
```bash
152+
qo-setup # copies every level into ~/challenges/
153+
qo-setup level1 # or just one level
154+
cd ~/challenges/level1
155+
cat README.md
156+
./check.sh # verified from home
157+
```
158+
159+
A level whose files were corrupted can be restored at any time:
160+
161+
```bash
162+
qo-reset level1 # wipe ~/challenges/level1 and re-copy pristine files
163+
```
164+
165+
Both commands relay over the same socket as `qo-check`; the parent copies only
166+
non-secret data and never exposes the real `check.sh` or the base flag.
167+
146168
## Customizing the Sandbox
147169

148170
### Adding System Binaries

docs/threat-model.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,14 @@ flow is out of scope.
4949
next `ExtractRootfs`).
5050
5. **Level keys are validated.** The stub's level identity is checked against a
5151
path-containment rule before the parent touches `ChallengesDir`; `..`/
52-
absolute/odd characters are rejected.
52+
absolute/odd characters are rejected (applies to the `check`, `setup`, and
53+
`reset` socket verbs alike).
54+
6. **The student never sees `/tmp`.** Non-secret level data is extracted to the
55+
root-only pristine staging tree (`/tmp/rootfs_pristine`, mode `0700`) and
56+
shipped to `~/challenges/<level>` only via the `qo-setup`/`qo-reset` socket
57+
verbs. The student works exclusively from their home directory; a corrupted
58+
working copy is restorable at any time by re-running `qo-setup`/`qo-reset`,
59+
which wipes and re-copies from pristine.
5360

5461
## Attack surface and responses
5562

@@ -61,9 +68,14 @@ flow is out of scope.
6168
leaves the parent (property 3).
6269
- **Student replays/brute-forces the socket**: the socket only runs the real
6370
check for the requested level and returns output; no new capability. `exit 0`
64-
is still gated on the check passing.
71+
is still gated on the check passing. The `setup`/`reset` verbs only copy
72+
non-secret pristine data into the requester's own home; they cannot reach
73+
`ChallengesDir`.
6574
- **Student alters the stub**: the stub carries no secrets; tampering only
6675
breaks their own `./check.sh`.
76+
- **Student corrupts their working copy**: `qo-setup`/`qo-reset` restore the
77+
level from pristine; the pristine tree is root-only and re-extracted fresh on
78+
every `qo start`.
6779
- **Student reads `/proc` of host processes**: possible (no PID namespace);
6880
accepted risk. Avoid placing session secrets in host process env/cmdlines.
6981
- **Network exfiltration**: sandbox shares the host network. The base flag never

scripts/test-e2e.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ chmod +x "$CHALLENGE_DIR/level3/check.sh"
9090

9191
# 2. Build qo binary and encrypt archive
9292
echo -e "${BLUE}[2/4] Building binary & encrypting challenge archive...${NC}"
93-
go build -o "$WORK_DIR/qo" main.go
93+
CGO_ENABLED=0 go build -o "$WORK_DIR/qo" main.go
9494

9595
"$WORK_DIR/qo" build -f "$CHALLENGE_DIR" -p "$PASS" -k "$KEY" -u "2020-01-01 00:00" -o "$ARCHIVE_PATH"
9696

@@ -103,7 +103,7 @@ echo -e "${GREEN}Encrypted archive built successfully.${NC}"
103103
# 3. Run the secrecy & archive round-trip tests (non-root)
104104
echo -e "${BLUE}[3/4] Running secrecy & archive round-trip tests...${NC}"
105105
go test -v ./pkg/archive/... -run 'TestArchiveRoundTripSecrecy|TestIsValidFolderStructure'
106-
go test -v ./pkg/sandbox/... -run 'TestWriteCheckStubsSecrecy|TestLoadBaseFlag|TestPresentCheckSuccess|TestValidateLevelKey|TestGenerateUniqueFlag'
106+
go test -v ./pkg/sandbox/... -run 'TestWriteCheckStubsSecrecy|TestLoadBaseFlag|TestPresentCheckSuccess|TestValidateLevelKey|TestGenerateUniqueFlag|TestRunSetup|TestSetupSocketRoundTrip'
107107

108108
# 4. Run the root-required check-execution tests (real chroot + socket + HMAC)
109109
echo -e "${BLUE}[4/4] Running root-required check-execution tests...${NC}"

setup.sh

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
#!/usr/bin/env bash
2+
#
3+
# setup.sh — install qo on Linux (multi-distro)
4+
#
5+
# Builds the static qo binary and installs it to /usr/local/bin, along with the
6+
# qo-check / qo-setup / qo-reset aliases the sandbox uses.
7+
#
8+
# Usage:
9+
# curl -fsSL https://raw.githubusercontent.com/Open-Source-Community/qo2/ctf-improvements/setup.sh | bash
10+
#
11+
# Or from a checkout:
12+
# ./setup.sh
13+
#
14+
# Set QO_SKIP_DEPS=1 to skip the package-manager step (e.g. you already have go).
15+
set -e
16+
17+
APP_NAME="qo"
18+
INSTALL_DIR="/usr/local/bin"
19+
REPO_URL="https://github.com/Open-Source-Community/qo2.git"
20+
21+
log() { printf '\033[1;36m[qo]\033[0m %s\n' "$*"; }
22+
warn() { printf '\033[1;33m[qo]\033[0m %s\n' "$*"; }
23+
die() { printf '\033[1;31m[qo]\033[0m %s\n' "$*" >&2; exit 1; }
24+
25+
need() { command -v "$1" >/dev/null 2>&1; }
26+
27+
have() { command -v "$1" >/dev/null 2>&1; }
28+
29+
detect_pkg_mgr() {
30+
if have apt-get; then echo apt
31+
elif have dnf; then echo dnf
32+
elif have pacman; then echo pacman
33+
elif have zypper; then echo zypper
34+
elif have apk; then echo apk
35+
elif have microdnf; then echo microdnf
36+
else echo unknown; fi
37+
}
38+
39+
install_deps() {
40+
[ "${QO_SKIP_DEPS:-0}" = "1" ] && { log "skipping package install (QO_SKIP_DEPS=1)"; return; }
41+
42+
local mgr
43+
mgr=$(detect_pkg_mgr)
44+
log "detected package manager: $mgr"
45+
46+
case "$mgr" in
47+
apt)
48+
sudo apt-get update -y
49+
sudo apt-get install -y git golang-go gcc libc6-dev tar gzip curl ca-certificates
50+
;;
51+
dnf|microdnf)
52+
if command -v sudo >/dev/null 2>&1; then
53+
sudo dnf install -y git golang gcc glibc-devel tar gzip curl ca-certificates
54+
else
55+
dnf install -y git golang gcc glibc-devel tar gzip curl ca-certificates
56+
fi
57+
;;
58+
pacman)
59+
sudo pacman -Sy --noconfirm --needed git go gcc tar gzip curl ca-certificates
60+
;;
61+
zypper)
62+
sudo zypper install -y git go gcc glibc-devel tar gzip curl ca-certificates
63+
;;
64+
apk)
65+
apk add --no-cache git go gcc musl-dev tar gzip curl ca-certificates
66+
;;
67+
*)
68+
warn "unsupported package manager; assuming go is already installed"
69+
;;
70+
esac
71+
72+
if ! need go; then
73+
warn "go is not on PATH; install it from https://go.dev/dl/ or re-run with QO_SKIP_DEPS=0"
74+
fi
75+
}
76+
77+
build_and_install() {
78+
local srcdir="$1"
79+
80+
cd "$srcdir"
81+
82+
# Static build: the sandbox copies this binary into the chroot as /bin/qo-check,
83+
# so it must not depend on host glibc.
84+
log "building static binary (CGO_ENABLED=0)..."
85+
CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o "$APP_NAME" .
86+
87+
local dest="$INSTALL_DIR/$APP_NAME"
88+
if [ -w "$INSTALL_DIR" ]; then
89+
mv -f "$APP_NAME" "$dest"
90+
else
91+
sudo mv -f "$APP_NAME" "$dest"
92+
fi
93+
sudo chmod 755 "$dest"
94+
95+
# Aliases the sandbox expects; they share the same binary and dispatch on argv[0].
96+
for alias in qo-check qo-setup qo-reset; do
97+
sudo ln -sf "$dest" "$INSTALL_DIR/$alias"
98+
done
99+
100+
log "installed: $dest"
101+
}
102+
103+
if [ "$1" = "uninstall" ]; then
104+
for a in qo qo-check qo-setup qo-reset; do
105+
sudo rm -f "$INSTALL_DIR/$a"
106+
log "removed $INSTALL_DIR/$a"
107+
done
108+
exit 0
109+
fi
110+
111+
# Build from a local checkout if present, otherwise clone a fresh copy.
112+
if [ -f ./main.go ] && [ -d ./pkg ]; then
113+
log "building from local checkout: $PWD"
114+
install_deps
115+
build_and_install "$PWD"
116+
else
117+
install_deps
118+
TMP_DIR=$(mktemp -d)
119+
trap 'rm -rf "$TMP_DIR"' EXIT
120+
log "cloning $REPO_URL ..."
121+
git clone --depth=1 "$REPO_URL" "$TMP_DIR/qo2"
122+
build_and_install "$TMP_DIR/qo2"
123+
fi
124+
125+
log "done. Verify with: qo --help"

0 commit comments

Comments
 (0)