## https://sploitus.com/exploit?id=D3433272-C5D4-5413-81A8-0875D3EE2568
# CVE-2025-31133 Compose Build Lab
This lab is a small PaaS simulator for untrusted Compose projects.
The platform accepts a `project.zip` containing a Compose file, at least one Dockerfile, and app source. It validates the normalized Compose config, builds with either default Compose build or Bake/Buildx mode, starts the app, exposes one labelled service through `/apps//`, records logs, and cleans up by project.
The platform container mounts the Docker socket for the lab environment so it can create build and runtime containers. Run it only in an isolated disposable environment, not on a workstation Docker daemon that has host secrets, internal-network access, or important workloads.
## Build the Platform Image
From this directory:
```bash
docker build -t local/docker-cve-2025-31133-lab-platform:latest .
```
Or let Compose build it:
```bash
docker compose build
```
The image contains Python, Flask, PyYAML, Docker CLI, Compose plugin, Buildx plugin, and `runc` for health display. The runtime under observation is the Docker Engine runtime used by the lab, so also check the environment directly:
```bash
./scripts/host-health.sh
```
## Run
```bash
docker compose up -d --build
```
Open:
```text
http://localhost:8088/
```
The launcher mounts:
```yaml
/var/run/docker.sock:/var/run/docker.sock
./workspaces:/workspaces
```
Only mount the Docker socket from the isolated lab environment. Do not expose a real host Docker daemon to this lab.
The Docker socket is required because the platform acts as the lab controller. It uses the Docker API to run `docker compose config`, build uploaded projects, start project containers, collect Docker events, inspect exposed services, and clean up resources. Uploaded projects are not executed inside the platform container itself.
## Snapshot Gate
When a student clicks `Start Lab Run`, the UI shows a `CHECK ASK` dialog:
```text
Did you make a snapshot about this VM?
[No] [Yes]
[ ] don't ask again
```
`No` cancels the run. `Yes` submits the upload. If `don't ask again` is checked with `Yes`, the browser stores that choice in local storage and skips the dialog on later uploads.
The lab also captures a pre-flight and post-cleanup state record for:
```text
/dev/null
/proc/sys/kernel/core_pattern
```
The state files are written under:
```text
workspaces//lab-state/before.json
workspaces//lab-state/after.json
```
If post-cleanup checks detect that `/dev/null` is not character device `1:3` with mode `666`, or that `core_pattern` changed, the project is marked:
```text
LAB VM DIRTY: rollback required
```
Rollback the VM snapshot after any dirty result. Manual cleanup is useful for debugging, not as a safety guarantee.
## Make Sample Uploads
```bash
chmod +x scripts/make-sample-zips.sh
./scripts/make-sample-zips.sh
```
Upload:
```text
sample-zips/benign-web.zip
sample-zips/build-host-read-probe.zip
sample-zips/cve-2025-31133-safe-canary.zip
sample-zips/rejected-privileged.zip
```
`benign-web.zip` should build, run, and expose an app URL. `build-host-read-probe.zip` runs a read-only image-build probe that attempts to observe whether host-like paths are visible from the build container. `cve-2025-31133-safe-canary.zip` demonstrates the `/dev/null` redirection primitive against fake lab canaries under `/tmp`. `rejected-privileged.zip` should be blocked with explicit policy reasons.
For the host-read probe, choose Bake / Buildx mode when you want to observe the multi-target build path. The expected safe result is that `/proc/1/root/...` is denied or resolves to the build container namespace rather than the host.
## Upload Requirements
The zip must contain, at its root:
```text
docker-compose.yml, docker-compose.yaml, compose.yml, or compose.yaml
at least one Dockerfile
at least one service with build:
```
A service can request exposure through labels:
```yaml
labels:
lab.expose: "true"
lab.port: "8080"
```
The platform does not allow user-defined host port publishing.
## Build Modes
Default mode runs:
```bash
docker compose config
docker compose -p lab_ -f normalized-compose.yml -f platform.override.yml build
docker compose -p lab_ -f normalized-compose.yml -f platform.override.yml up -d --no-build
```
Bake mode runs:
```bash
docker buildx version
docker buildx bake -f normalized-compose.yml --print
COMPOSE_BAKE=true docker compose -p lab_ -f normalized-compose.yml -f platform.override.yml build
docker compose -p lab_ -f normalized-compose.yml -f platform.override.yml up -d --no-build
```
If Buildx is missing, Bake mode fails with `Bake unavailable: buildx plugin missing`. It does not silently fall back to default mode.
## Policy Checks
The platform first runs `docker compose config`, writes `normalized-compose.yml`, parses it with PyYAML, and then checks the normalized service runtime settings.
Rejected settings include:
```yaml
privileged: true
network_mode: host
pid: host
ipc: host
cgroup: host
cgroupns_mode: host
uts: host
userns_mode: host
cap_add: [...]
devices: [...]
device_cgroup_rules: [...]
security_opt: [...]
sysctls: ...
ports: ...
tmpfs: ...
```
It also rejects dangerous host path mounts, Docker socket mounts, external networks, `macvlan`, and `ipvlan`.
For allowed projects the platform applies `platform.override.yml`:
```yaml
services:
each-service:
privileged: false
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
read_only: true
pids_limit: 256
mem_limit: 512m
cpus: "1.0"
tmpfs:
- /tmp
- /run
labels:
lab.project:
lab.managed: "true"
networks:
default:
name: lab__net
```
## Workspace Layout
Each upload creates:
```text
workspaces//
โโโ uploaded.zip
โโโ src/
โโโ normalized-compose.yml
โโโ platform.override.yml
โโโ decision.log
โโโ build.log
โโโ bake-print.log
โโโ run.log
โโโ events.log
โโโ cleanup.log
โโโ lab-state/
โ โโโ before.json
โ โโโ after.json
โโโ report.json
```
The report has the teaching fields:
```json
{
"project_id": "...",
"build_mode": "default|bake",
"runc_version": "...",
"buildx_available": true,
"policy_result": "allowed|blocked",
"blocked_reasons": [],
"containers_created": 1,
"runtime_mode": "Lab Docker Engine via mounted Docker socket",
"cleanup_status": "done"
}
```
## Safety Boundary
Do not mount a real host Docker socket, home directory, cloud credentials, SSH keys, or sensitive internal networks. Use a disposable environment and roll it back after testing.
## CVE Demonstration Scope
This repository now implements the platform and observability path for CVE-2025-31133 education: untrusted Compose/Dockerfile input is handed to Docker/BuildKit/runc, with Default mode as baseline and Bake mode as the closer Buildx/concurrent-build observation mode.
It does not ship a host-root shell payload or a portable escape kit. Phase 5 should remain instructor-controlled and use local canaries or runtime observations rather than a reusable exploit chain.