Skip to content

Docker label value used unescaped to build docker-compose commands (argument injection on shared hosts) #828

Description

@carfeii

Summary

lazydocker reads a container's com.docker.compose.project label directly
(pkg/commands/docker.go, container-to-service mapping) and, via a
project-name auto-detection heuristic in RefreshContainersAndServices, can
adopt that label's value from any container on the host, not only
containers belonging to the current user's project, as long as the
container's com.docker.compose.service label matches a service name
already defined in the local docker-compose.yml. That value is then
concatenated unescaped (fmt.Sprintf("%s -p %s", ...) in NewCommandObject)
into the docker-compose command prefix used to build every subsequent
command for that service.

A plain docker run --label invocation accepts an arbitrary string for
this label, with no character restriction (unlike a docker-compose-parsed
project name, which is restricted to a safe character class by the Compose
spec). This lets a local user on a shared Docker host create one container
with a crafted label to inject extra command-line arguments into another
user's lazydocker-issued docker-compose commands, requiring only that the
attacker's chosen service-name label matches a service name the victim
already uses (common names like "web" or "app" make this easy).

Validated against v0.25.2.

POC

(available upon request)

Impact

On a shared or multi-tenant Docker host, any user who can create a
container (no special privileges, no docker-compose required) can inject
arbitrary extra flags into another user's lazydocker-issued docker-compose
commands for a matching service name, causing those commands to silently
fail against the victim's own service. Depending on the injected flag, this
could extend beyond denial of service, for example toward causing
docker-compose to merge in an attacker-supplied compose file on a
subsequent up/recreate.

Suggested Fix

Validate any label-derived value against the character class
docker-compose itself requires for a project name before using it to build
a command line, and prefer the compose-file-derived project name over a
label match when both are available, rather than adopting the first
matching label found across the whole host.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions