Three boards

All of them finished. One bulletin board, in three runtimes, carrying the same exercise ids wherever an exercise survives the move. What changes is the language, and in the third case the medium.

A finished repository is self-contained: the board, the work sheet, the answer key, the remediation notes, hardened counterparts in secure/, a payload suite, and the machine-checkable inventory that ties them together.

The original

insecurity-bbs-php

A deliberately vulnerable PHP bulletin board. Twenty-five PHP files and two stylesheets.

RequiresPHP 7.0 through 8.5 with pdo_sqlite and dom. mysqli only if you point it at MySQL.
Build stepNone. No Composer, no framework, no dependencies.
Run./run.sh — or run.cmd on Windows
Scoringphp tools/score.php

Every version in that range runs the full payload suite in CI on each push, so the supported range is a test result rather than a claim.

The board's index, as an anonymous visitor sees it
The board as an anonymous visitor sees it. Nothing on the page says what is wrong with it.

The Java twin

insecurity-bbs-jsp

The same board as JSPs and servlets on an embedded Tomcat. Seventeen JSPs, fourteen classes, two stylesheets.

RequiresJava 11 or newer, and Maven for the one build. Tomcat is embedded; SQLite arrives with the JDBC driver.
Build stepOne, on the first run — the only step that needs Maven. Later runs reuse the jar.
Run./run.sh — or run.cmd on Windows
Scoringjava tools/Score.java (single-file source launch)

Verified by running the board and the full payload suite on Java 11, 17, 21, 25 and 26, and CI covers the same range on every push.

DIFFERENCES.md walks what changed when the language did, and why.

The one without a browser Ready

insecurity-bbs-perl

A port by way of the JSP board that changes the medium as well as the language. Eleven modules, twenty-five screens, and no stylesheet at all.

There is no request here, no response and no document. A Perl program draws a screen on your terminal and reads your keystrokes, the way a bulletin board did before the web existed.

RequiresPerl 5.16 or newer with DBI and DBD::SQLite — one package on any distribution, already there on macOS. Term::ReadKey if you have it; stty if you do not.
Build stepNone. No web server, no framework, no container.
Run./run.sh — Unix only, and there is no run.cmd on purpose. ./run.sh --batch drives it from stdin without a pty; --serve runs the API listeners alone.
Scoringperl tools/score.pl

The port is finished. The repository carries the whole set the other two do: work sheet, answer key, remediation document, hardened counterparts in secure/, a payload suite and a scorer.

Unix only, and it means it. There is no run.cmd and there will not be. It also shares the host on purpose, so give it a machine or a container you can throw away.

Alongside the board

What each repository carries

All three carry the whole set.

 What it isphpjspperl
CHALLENGES.mdTargets and objectives, no payloads, the flags to collectyesyesyes
LAB.mdEvery payload worked, with the reasoning that found ityesyesyes
REMEDIATION.mdThe diffs against secure/, and why each fix is that fixyesyesyes
DIFFERENCES.mdOn the ports: what the change of language, or medium, forced—yesyes
SECURITY.mdWhat running it exposes, and how to keep it containedyesyesyes
VULNS.yamlThe inventory, in a form something other than prose can checkyesyesyes
secure/Hardened counterparts of the most instructive filesyesyesyes
tests/The payload suite, and a second suite that runs it against secure/yesyesyes
tools/Scorer, inventory checker, mutation run — and on jsp and perl the parity check against PHPyesyesyes

The mutation run is the one worth calling out: it asks whether the payload suite would actually notice a vulnerability being removed, which is a different question from whether the suite passes.

Isolated hosts only. The boards are insecure on purpose. The launchers bind loopback and refuse anything else unless you override it on purpose.