CVEReports
Reports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Reports
  • Sitemap

Company

  • About
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Powered by Google Gemini & CVE Feed

|
•

CVE-2019-5736
CVSS 8.6|EPSS 55.56%

Breaking Out of the Box: The Runc Overwrite (CVE-2019-5736)

Amit Schendel
Amit Schendel
Senior Security Researcher•February 10, 2019•6 min read
WeaponizedNot in KEV

Executive Summary (TL;DR)

Containers aren't real. They are just processes lying to the kernel. CVE-2019-5736 exploits this lie by tricking the host's container runtime (`runc`) into exposing its own binary file descriptor to the container it is managing. An attacker can overwrite the `runc` binary on the host with a malicious payload, achieving root execution on the host system the next time `runc` is used.

A fundamental design flaw in how `runc` handles file descriptors allows a malicious container to overwrite the host `runc` binary, resulting in complete host compromise upon subsequent execution.

The Hook: Containers Are Just Chroot with Better Marketing

Let's dispel a myth before we start: a Docker container is not a Virtual Machine. It does not have its own kernel. It is simply a process on the host system that has been put in a "time-out" corner using Linux Namespaces and cgroups. The babysitter enforcing this time-out is runc.

runc is the low-level CLI tool that actually spawns and runs containers (used by Docker, Kubernetes, containerd, etc.). When you run docker exec, runc has to effectively dive into the shark tank (the container) to set up the environment for your command.

Here is the problem: when runc dives into the tank to check on the sharks, it brings its ID badge (its own binary executable) with it. CVE-2019-5736 is the story of how the sharks learned to steal that badge and walk out the front door.

The Flaw: The Magic of /proc/self/exe

The vulnerability relies on a quirky feature of the Linux kernel: /proc/self/exe. This is a symbolic link that points to the binary of the currently running process. If you are running /bin/ls, /proc/self/exe points to /bin/ls.

When you execute a command inside a container using docker exec, the runc process on the host starts up, enters the container's namespaces, and then executes the requested command. However, for a brief window after entering the namespace but before executing the target command, the runc process is susceptible to manipulation.

If the attacker inside the container places a symbolic link or a modified library that runc interacts with, the attacker can trick runc into executing itself (or rather, pointing /proc/self/exe back to the host runc binary). Once the attacker has a file descriptor (FD) pointing to the host's runc binary, they win. They can't overwrite it while it's running (Linux throws ETXTBSY - Text File Busy), but they can hold the door open and wait for it to leave.

The Code: Cloning the Babysitter

The fix for this issue was both elegant and slightly paranoid. The developers realized that they couldn't trust the container environment with the real runc binary. So, they decided to send in a clone.

In the patched version, before runc enters the container's namespace, it copies its own binary logic into a temporary, anonymous file in memory (using memfd_create if available). It then re-executes itself from this memory-resident clone.

Here is the logic flow in the patch:

// From libcontainer/nsenter/cloned_binary.c
 
// Create a memfd (anonymous file in RAM)
int fd = memfd_create("runc_cloned", MFD_CLOEXEC | MFD_ALLOW_SEALING);
 
// Copy the current runc binary into the memfd
sent = sendfile(fd, self_fd, NULL, sent);
 
// Seal the memfd so it cannot be modified
fcntl(fd, F_ADD_SEALS, F_SEAL_SEAL | F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE);
 
// Execute the clone
fexecve(fd, argv, envp);

By the time runc enters the hostile container environment, /proc/self/exe points to this throwaway memory file, not the critical binary on the host disk. If the attacker overwrites it, they are just vandalizing a temporary ghost in RAM.

The Exploit: How to Break Out

The exploitation process is a race against time and file locks. It requires a malicious container image or a compromised container where the attacker has root privileges (inside the container).

Here is the attack chain:

  1. The Setup: The attacker replaces the target binary (e.g., /bin/sh or the container entrypoint) with a symbolic link to /proc/self/exe.
  2. The Trigger: A victim (admin) runs docker exec -it malicious-container /bin/sh.
  3. The Trap: runc enters the container and tries to execute /bin/sh. Because of the symlink, runc actually re-executes itself inside the container context.
  4. The Hook: The malicious process inside the container (which was waiting for this) detects that runc has started. It opens /proc/[runc-pid]/exe for reading. This gives the attacker a file descriptor (FD) pointing to the real host runc binary.
  5. The Wait: The attacker tries to open the FD for writing, but fails with ETXTBSY because runc is still running. The attacker script sits in a loop, hammering the open() syscall.
  6. The Switch: As soon as the runc process exits, the write lock is released. The attacker's script successfully opens the FD for writing and overwrites the host runc binary with a malicious payload (e.g., a reverse shell wrapper).

The Impact: Total Host Takeover

This isn't a simple data leak; this is infrastructure destruction. By overwriting runc, the attacker has effectively backdoored the entire container host.

The next time any container is started, stopped, or exec'd into on that machine—whether by an admin or an automated orchestration tool like Kubernetes—the malicious runc binary will execute.

Typically, the malicious payload will spawn a reverse shell to the attacker and then execute the original runc logic so nobody notices anything is wrong. In a Kubernetes cluster, compromising one node often leads to compromising the orchestration credentials, allowing the attacker to pivot to every other node in the fleet.

The Fix: Mitigation & Defense

The primary fix is to update runc (and by extension Docker/containerd). However, there are architectural defenses that render this class of bug irrelevant.

1. User Namespaces (userns-remap): This is the silver bullet. By mapping root inside the container to a non-privileged user (e.g., nobody) on the host, the attack fails immediately. Even if the attacker gets a file descriptor to the host binary, the kernel says, "Nice try, but you don't have write permissions to this file."

2. Read-Only Root Filesystem: Running containers with a read-only root filesystem prevents the initial symlink setup, though sophisticated attackers might find other writable locations to stage the attack.

3. SELinux: If you are on a Red Hat based system, SELinux policies for containers usually prevent the container process from writing to runc, effectively blocking the exploit.

Official Patches

DockerDocker Engine 18.09.2 Release Notes
OpenContainersOfficial fix commit in runc

Fix Analysis (1)

Technical Appendix

CVSS Score
8.6/ 10
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H
EPSS Probability
55.56%
Top 99% most exploited

Affected Systems

Docker (versions prior to 18.09.2)Kubernetes (nodes using affected container runtimes)containerd (prior to patched versions)CRI-O (prior to patched versions)runc (<= 1.0-rc6)

Affected Versions Detail

ProductAffected VersionsFixed Version
runc
OpenContainers
<= 1.0-rc61.0-rc7
Docker
Docker Inc.
< 18.09.218.09.2
AttributeDetail
CWE IDCWE-269
Attack VectorLocal (requires container execution)
CVSS8.6 (High)
EPSS Score55.56%
ImpactContainer Escape / Host Root Compromise
Exploit StatusWeaponized / PoC Available

MITRE ATT&CK Mapping

MITRE ATT&CK Mapping

T1611Escape to Host
Privilege Escalation
T1543Create or Modify System Process
Persistence
T1574Hijack Execution Flow
Persistence
CWE-269
Improper Privilege Management

Improper Privilege Management

Exploit Resources

Known Exploits & Detection

GitHubGo-based implementation of the container escape
ExploitDBPoC for runc container escape

Vulnerability Timeline

Vulnerability Timeline

Vulnerability discovered by Adam Iwaniuk and Borys Popławski
2019-01-01
CVE-2019-5736 Assigned and Patch Released
2019-02-11
Public Proof of Concepts released
2019-02-13

References & Sources

  • [1]Unit42 Analysis of CVE-2019-5736
  • [2]Dragonfly Research: Runc Escape
Related Intelligence
CVE-2024-21626CVE-2016-9962

Subscribe to updates

Get the latest CVE analysis reports delivered to your inbox.

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.