Share
## 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.