Docker Compose Provider Type Command Execution

SUMMARY
Docker Compose allows arbitrary command execution when processing compose files with a provider.type field. The vulnerability occurs because Docker Compose by design executes any Provider Type as a binary/script on the host without validation.
When it encounters a provider.type field in a service definition, it uses Go's exec.LookPath() function to find and execute the specified program. If the value is a relative path (like ./start), and that file exists and is executable in the current directory, Docker Compose will execute it on the host system before creating any containers.
An attacker can craft a malicious docker-compose.yml and script that executes arbitrary commands on the system running 'docker compose up'.
While this is considered by design and intended functionality by the maintainer, it could be a way to get code execution in local environments or platforms insecurely using docker compose during red team exercises.
HOW IT WORKS
Docker Compose reads
docker-compose.ymlService has
provider.type: ./start(relative path)Docker Compose calls
exec.LookPath("./start")If
./startexists and is executable, it executes it immediately
The script runs with the same privileges as the user running docker compose, with no additional checks to determine if this is a legitimate provider, nor isolation when running the script
Vulnerable Code Location
File:
pkg/compose/plugins.go(Docker Compose source code)Function:
getPluginBinaryPath()Line: ~167
Code:
func (s *composeService) getPluginBinaryPath(provider string) (path string, err error) {
// ... validation checks ...
if errdefs.IsNotFound(err) {
path, err = exec.LookPath(executable(provider)) // <- Executes relative paths
}
return path, err
}
Issue: exec.LookPath() accepts relative paths (like ./start) and will execute them if they exist and are executable. This is intended behavior per Docker, but users may not be aware that scripts in the current working directory will be executed.
Execution Flow
Docker Compose parses
docker-compose.ymlEncounters
provider.type: ./startCalls
getPluginBinaryPath("./start")exec.LookPath("./start")finds the fileDocker Compose executes
./start
Script runs arbitrary commands immediately and commands run with user privileges on host system
TESTING FILES
File: docker-compose.yml
services:
start:
provider:
type: ./start
File: start
#!/bin/sh
uname -a > /tmp/123
id >> /tmp/123
EXPLOITATION
Create a malicious script that:
- Executes arbitrary commands
Create a malicious compose file (
docker-compose.yml) that:Defines a service with
provider.type: ./startPoints to the malicious script
Triggering execution by:
Running
docker compose upon the malicious compose fileDocker Compose automatically executes
./startas a "provider"The script runs arbitrary commands on the host
POTENTIAL ATTACK SCENARIOS
Running
docker composeafter cloning a repoScenario: Developer clones a repository and runs
docker compose upgit clone [GIT REPO URL] cd repo docker compose up # <- command execution triggered hereRemote features / plugins for automatically/manually deploying a yaml compose file in IDEs
Scenario: IDE extensions (VSCode, JetBrains) may auto-detect and execute compose files
Attacker creates malicious repo with
docker-compose.ymlDeveloper opens repo in IDE
IDE extension detects compose file and suggests "Run Docker Compose"
Developer clicks button -> command execution triggered
Any services that allow arbitrary git URLs to deploy services without restricting config or providers
Scenario: Platforms that accept Git URLs and automatically deploy Docker Compose stacks
Attacker provides malicious Git URL in platform UI
Platform clones repo and runs
docker compose upCommand execution on platform host server
REPO SETUP
Create the repo and add files.
Note: Ensure 'start' file is executable (chmod +x start)
TESTING
Confirmed command execution on the following applications and platforms
Docker Compose v2.40.3 (Ubuntu 24.04)
Visual Studio Code with Docker from Microsoft Plugin (Mac OS X)
JetBrains IntelliJ IDE (Mac OS X)
DOCKER COMPOSE CLI
Step 1: Install docker compose
$ sudo apt install -y docker-compose-plugin
$ docker compose version
Docker Compose version v2.40.3
Step 2: Clone the repo and run docker compose
$ git clone [GIT REPO URL] && cd test
$ docker compose up
[+] Running 1/1
✔ start Created 0.0s
service "start" has no container to start
Step 3: Check for exploit artifact
$ cat /tmp/123
Linux 6.14.0 Ubuntu SMP x86_64 x86_64 x86_64 GNU/Linux
uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm),24(cdrom),27(sudo),30(dip),105(lxd),988(docker)
VSCODE IDE WITH DOCKER PLUGIN
Step 1: Install plugin
Extensions -> search Docker -> Install Docker (from Microsoft)
Explorer -> Clone Repository -> [GIT REPO URL] (Clone from URL)
Select as Repository Destination, Click Open
Click Yes, I trust the authors (clicking No = Restricted Mode with no Compose Up option)
Step 2: Run Compose from Repo
- Right click
compose.yaml-> Compose Up
[+] Running 1/1
✔ start Created 0.0s
service "start" has no container to start
Step 3: Check for exploit artifact
$ cat /tmp/123
Darwin Kernel Version 24.6.0 arm64
uid=501(test) gid=20(staff) groups=20(staff),12(everyone),61(localaccounts),79(_appserverusr),80(admin),81(_appserveradm),98(_lpadmin),204(_developer),33(_appstore),100(_lpoperator),250(_analyticsusers),395(com.apple.access_ftp),398(com.apple.access_screensharing),399(com.apple.access_ssh),400(com.apple.access_remote_ae)
INTELLIJ IDEA IDE
Similar to VSCode, but default can run docker compose on the repo
Step 1: Open IntellJ IDEA and clone repo
Step 2: 'Preview in Safe Mode' / Untrusted Mode worked (as well as Trusted Mode)
Step 3: Right click docker file and select Run 'docker-compose.yml: ...'
Mitigation
Maintainer considers custom providers an intended and documented feature and has released documentation update to clarify type field can execute scripts:
https://docs.docker.com/compose/how-tos/provider-services#provider-types
Additional information on how to mitigate risks
For Users:
Review compose files before execution, especially from untrusted sources
Be aware that
provider.typecan execute arbitrary scripts on the hostOnly run
docker compose upon compose files from trusted sourcesConsider using Docker Compose's schema validation (enabled by default in CLI) which may block some provider configurations
For Platforms:
Platforms that process untrusted compose files (CI/CD systems, container management platforms, IDEs) may consider implementing ideas that may mitigate impact or prevent exploitation:
Block
provider.typeField EntirelyReject any compose file containing
provider.typefieldSimplest mitigation if custom providers are not needed
Can be implemented via schema validation or pre-processing
Whitelist Allowed Providers
Maintain a list of approved provider names (e.g.,
["compose", "docker", "kubernetes"])Reject any
provider.typevalue not in the listValidate before passing compose file to Docker Compose
Restrict Relative Paths
Reject any
provider.typevalue containing relative paths (./,../, or no path prefix)Only allow absolute paths to known plugin directories (e.g.,
/usr/lib/docker/cli-plugins/)Prevents execution of scripts from untrusted repository directories
Validate Compose Files Before Execution
Parse compose files and check for
provider.typefieldsImplement custom validation logic before calling Docker Compose
Log all
provider.typeusage for security monitoring
Discovery
Jeremy Brown (jbrown3264/gmail)




