The lab

A hardened range you spin up in one click.

The targets are deliberately vulnerable; the lab around them is hardened so it stays in the box. Loopback-only, non-root, no egress, no Docker socket. Everything runs on your machine, and the autograder runs in CI. Safe by design, not unhackable, and honest about the difference.

qwalora-ranger-lab // quick startLOOPBACK
# use the template, then
$ ./qrange doctor        ok
$ ./qrange up board2     127.0.0.1:7443
$ ./qrange exploit board2
# write your detection + patch
$ ./qrange check board2  score 100/100
no server to rungraded in GitHub Actions
Safe by design

The vulnerable things stay in the box.

Containment is enforced in the compose file and the seccomp profile, not left to the operator. We say safe by design, not unhackable: no containment is absolute, so treat the whole lab as hostile and never bridge it to a network you care about.

No egress

Every Docker network is internal, so the targets have no route to the internet.

networks: internal: true

Loopback only

Every UI and target port binds to 127.0.0.1, never a public interface.

127.0.0.1:7443 -> 8080

Least privilege

Containers run non-root and read-only, with cap_drop ALL, no-new-privileges and seccomp.

user 10001 · read_only

No Docker socket

The socket is never mounted into any container, so the usual escape is closed.

no /var/run/docker.sock

Pinned by digest

Images are pinned by digest so you run exactly the bytes you reviewed, every time.

qrange pin · cosign verify

Bounded resources

Memory, CPU and PID limits on every service keep a runaway target contained.

mem_limit · pids_limit
Automated, no server

The autograder, not a human, decides pass or fail.

You run the lab locally and open a pull request. GitHub Actions is the only compute: it re-runs the exact same checks and posts the result. Grading is deterministic because the attack is a recorded fixture and the patch is proven against your own code.

  • Exploit evidence: a well-formed, signed proof artifact is committed
  • Detection fires on the recorded attack and stays clean on benign traffic
  • The patched target re-runs the real exploit and refuses it
validate · board2GitHub Actions
exploit-evidencepass · 25
detection-firespass · 35
patch-blocks-exploitpass · 40
score100/100 · PASS
cohort-syncEd25519 signed
What is in the box

A full range, plus the proof it works.

The template ships every package: the hardened compose range, the one-click CLI, the autograder, the boards, exploit harnesses, detection templates and the signing pipeline.

qrangeCLI

One-click control

up, down, exploit, check, status, pack, pin, doctor. Grading needs only Python.

bash · python3
rangecompose

Hardened target + SOC

Vulnerable target, attacker, Suricata, Wazuh and Grafana on two no-egress networks.

docker compose
boardsgraded

Exploit, detect, patch

Each board is a manifest of deterministic checks the autograder runs end to end.

pass / fail
evidencesigned

Verifiable badges

The program re-measures and Ed25519-signs the result; the dossier verifies it in-browser.

attestation.json
# reproducible, verifiable builds
$ ./qrange pin     digests written
# air-gapped bundle: images + repo
$ ./qrange pack    qwalora-ranger-offline.zip
# on the offline host
$ docker load -i images.tar
$ ./qrange up board2
Offline capable

Runs air-gapped, pinned and verified.

Pin the image digests, pack the images and the repo into a single archive, and bring the whole range up on a host with no registry access. Every candidate runs identical, verified bytes.

Spin up your lab.

Use the template, bring up board two, and run the full loop: exploit, detect, patch, prove. The autograder does the rest.