Docker Compose Provider Type Command Execution Jeremy Brown (jbrown3264/gmail), Dec 2025 ======= 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'. ============ HOW IT WORKS ============ 1. Docker Compose reads docker-compose.yml 2. Service has provider.type: ./start (relative path) 3. Docker Compose calls exec.LookPath("./start") 4. If ./start exists 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: 1. Docker Compose parses docker-compose.yml 2. Encounters provider.type: ./start 3. Calls getPluginBinaryPath("./start") 4. exec.LookPath("./start") finds the file 5. Docker 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 ============ 1. Create a malicious script that: - Executes arbitrary commands 2. Create a malicious compose file (docker-compose.yml) that: - Defines a service with provider.type: ./start - Points to the malicious script 3. Triggering execution by: - Running docker compose up on the malicious compose file - Docker Compose automatically executes ./start as a "provider" - The script runs arbitrary commands on the host ========================== POTENTIAL ATTACK SCENARIOS ========================== 1) Running docker compose after cloning a repo Scenario: Developer clones a repository and runs docker compose up - git clone [GIT REPO URL] - cd repo - docker compose up # <- command execution triggered here 2) Remote 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.yml - Developer opens repo in IDE - IDE extension detects compose file and suggests "Run Docker Compose" - Developer clicks button -> command execution triggered 3) 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 up - Command 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 1. Extensions -> search Docker -> Install Docker (from Microsoft) 2. Explorer -> Clone Repository -> [GIT REPO URL] (Clone from URL) 3. Select as Repository Destination, Click Open 4. Click Yes, I trust the authors (clicking No = Restricted Mode with no Compose Up option) Step 2: Run Compose from Repo 1. 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 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.type can execute arbitrary scripts on the host - Only run docker compose up on compose files from trusted sources - Consider 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: 1. Block provider.type Field Entirely - Reject any compose file containing provider.type field - Simplest mitigation if custom providers are not needed - Can be implemented via schema validation or pre-processing 2. Whitelist Allowed Providers - Maintain a list of approved provider names (e.g., ["compose", "docker", "kubernetes"]) - Reject any provider.type value not in the list - Validate before passing compose file to Docker Compose 3. Restrict Relative Paths - Reject any provider.type value 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 4. Validate Compose Files Before Execution - Parse compose files and check for provider.type fields - Implement custom validation logic before calling Docker Compose - Log all provider.type usage for security monitoring