One-click control
up, down, exploit, check, status, pack, pin, doctor. Grading needs only Python.
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.
# 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
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.
Every Docker network is internal, so the targets have no route to the internet.
Every UI and target port binds to 127.0.0.1, never a public interface.
Containers run non-root and read-only, with cap_drop ALL, no-new-privileges and seccomp.
The socket is never mounted into any container, so the usual escape is closed.
Images are pinned by digest so you run exactly the bytes you reviewed, every time.
Memory, CPU and PID limits on every service keep a runaway target contained.
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.
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.
up, down, exploit, check, status, pack, pin, doctor. Grading needs only Python.
Vulnerable target, attacker, Suricata, Wazuh and Grafana on two no-egress networks.
Each board is a manifest of deterministic checks the autograder runs end to end.
The program re-measures and Ed25519-signs the result; the dossier verifies it in-browser.
# 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
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.
Use the template, bring up board two, and run the full loop: exploit, detect, patch, prove. The autograder does the rest.