This repository contains a two‑container Docker lab for teaching common server misconfigurations and a safe, simulated Heartbleed‑style memory leak. It runs locally only; nothing is exposed to the internet beyond package downloads during image build.
Safety note: The “heartbleed” service is a tiny Python demo that returns bytes from a local file; it does not ship vulnerable OpenSSL. The SSH weaknesses are intentional, self‑contained misconfigurations inside the victim container.
- Docker Desktop installed and running
- Git Bash (Windows) or any terminal (macOS/Linux/WSL/PowerShell)
- Internet access once to build the images (package install), then everything runs offline
.
├─ docker-compose.yml
├─ attacker/
│ ├─ Dockerfile
│ └─ tools/
│ ├─ wordlist.txt
│ ├─ ssh-helper.sh
│ └─ bleed.py
└─ victim/
├─ Dockerfile
├─ entrypoint.sh
├─ services/
│ ├─ fake_heartbleed.py
│ └─ memory.bin
├─ ssh/
│ ├─ sshd_config.level1
│ ├─ sshd_config.level2
│ ├─ sshd_config.level3
│ ├─ sshd_config.level4
│ └─ sshd_config.level5
├─ banners/ # optional, can be empty
├─ motd/ # optional, can be empty
├─ users/
│ ├─ make_users_level1.sh
│ ├─ make_users_level2.sh
│ ├─ make_users_level3.sh
│ ├─ make_users_level4.sh
│ └─ make_users_level5.sh
└─ extras/ # optional keys/binaries/hints per level
If the folders don’t exist, create them as above. Ensure all
*.shfiles are executable on Linux/WSL:
chmod +x victim/entrypoint.sh victim/users/*.sh attacker/tools/*.sh
Open a terminal in the project root (where docker-compose.yml is).
DIFFICULTY=1 docker compose up --build -d$env:DIFFICULTY="1"
docker compose up --build -dThis builds and starts two containers: victim and attacker.
Default mapping (edit your docker-compose.yml if different):
ports:
- "2222:22" # SSH
- "8443:8443" # fake heartbleed server
# Optionally add: - "2223:2222" (if a level uses SSH port 2222 inside)Login (Level 1 example):
ssh -o StrictHostKeyChecking=no student@localhost -p 2222
# password: password# Linux/WSL/Git Bash:
docker exec -it attacker python3 /tools/bleed.py victim 8443 8000 | grep FLAG
# PowerShell equivalent (no grep):
docker exec -it attacker python3 /tools/bleed.py victim 8443 8000 | Select-String FLAGYou should see a “leak” containing FLAG{...} from a local file inside the victim service.
Start with a level by setting DIFFICULTY before docker compose up:
docker compose down
DIFFICULTY=3 docker compose up --build -dLevel overview (default users/ports):
| Level | SSH Port (inside) | Auth | Default user | Notes / Goal |
|---|---|---|---|---|
| 1 | 22 | Password | student:password |
Tutorial. Learn SSH, banners/MOTD, basic sudo usage. |
| 2 | 22 | Password | student:toor123 |
Weak crypto/algo hints in MOTD/Banner; learn enumeration. |
| 3 | 2222 | Password | admin:admin2024 |
Non‑standard port, leaked backup creds in /var/backups. |
| 4 | 22 | Key only | user dev |
Find leaked private key; pager escape via sudo less. |
| 5 | 2222 | Key only | user ops |
Weak key passphrase hint + SUID/path hijack demo. |
If Level 3/5 uses SSH port 2222 inside, either connect via the extra mapping (e.g.,
- "2223:2222") and usessh ... -p 2223, or change your compose ports to suit your host.
Check that the selected difficulty applied the expected SSH config:
docker exec -it victim bash -lc 'grep -E "PermitRootLogin|PasswordAuthentication|Port" /etc/ssh/sshd_config'See the active difficulty:
docker exec -it victim bash -lc 'echo $DIFFICULTY'View banners/MOTD (if present):
docker exec -it victim bash -lc 'cat /etc/issue.net || true; cat /etc/motd || true'Stop the stack:
docker compose downChange level (example to Level 4):
DIFFICULTY=4 docker compose up --build -dFull cleanup for this project only (containers, images, volumes):
docker compose down -v --rmi all-
“Permission denied” running entrypoint or user scripts
Make scripts executable (Linux/WSL/Git Bash):chmod +x victim/entrypoint.sh victim/users/*.sh attacker/tools/*.sh docker compose up --build -d
-
Port already in use
Editdocker-compose.ymland change the host port (left side of the mapping), e.g.2224:22, thendocker compose up -d. -
docker execsaysdocker: command not found
You’re inside the container. Rundocker exec ...on the host (in your terminal where you useddocker compose up). -
Attacker image failed installing netcat
Usenetcat-openbsdon Ubuntu 24.04, notnetcat-traditional.
- The lab is offline except for package downloads during build.
- The Heartbleed demo is a safe simulation (
fake_heartbleed.py), not a real CVE. - Use for education only. Do not expose these containers to the public internet.
# See running containers
docker ps
# Follow logs (victim)
docker logs -f victim
# Open a shell inside victim
docker exec -it victim bash
# Rebuild only one service
docker compose build victim
docker compose build attackerHave fun and hack responsibly!